Update
[llpp.git] / main.ml
blobfccda1db16739151fee06e04801d053243a06d81
1 open Utils;;
2 open Config;;
3 open Glutils;;
4 open Listview;;
6 let selfexec = ref E.s;;
7 let ignoredoctitlte = ref false;;
8 let layouth = ref ~-1;;
9 let checkerstexid = ref None;;
11 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
12 dolog {|rect {
13 x0,y0=(% f, % f)
14 x1,y1=(% f, % f)
15 x2,y2=(% f, % f)
16 x3,y3=(% f, % f)
17 }|} x0 y0 x1 y1 x2 y2 x3 y3;
20 let pgscale h = truncate (float h *. conf.pgscale);;
22 let hscrollh () =
23 if ((conf.scrollb land scrollbhv != 0) && (state.w > state.winw))
24 || state.uioh#alwaysscrolly
25 then conf.scrollbw
26 else 0
29 let setfontsize n =
30 fstate.fontsize <- n;
31 fstate.wwidth <- measurestr fstate.fontsize "w";
32 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
35 let launchpath () =
36 if emptystr conf.pathlauncher
37 then dolog "%s" state.path
38 else (
39 let command =
40 Str.global_replace Utils.Re.percent state.path conf.pathlauncher in
41 match spawn command [] with
42 | _pid -> ()
43 | exception exn -> dolog "failed to execute `%s': %s" command @@ exntos exn
47 let getopaque pageno =
48 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
49 with Not_found -> None
52 let pagetranslatepoint l x y =
53 let dy = y - l.pagedispy in
54 let y = dy + l.pagey in
55 let dx = x - l.pagedispx in
56 let x = dx + l.pagex in
57 (x, y);
60 let onppundermouse g x y d =
61 let rec f = function
62 | l :: rest ->
63 begin match getopaque l.pageno with
64 | Some opaque ->
65 let x0 = l.pagedispx in
66 let x1 = x0 + l.pagevw in
67 let y0 = l.pagedispy in
68 let y1 = y0 + l.pagevh in
69 if y >= y0 && y <= y1 && x >= x0 && x <= x1
70 then
71 let px, py = pagetranslatepoint l x y in
72 match g opaque l px py with
73 | Some res -> res
74 | None -> f rest
75 else f rest
76 | _ -> f rest
77 end
78 | [] -> d
80 f state.layout
83 let getunder x y =
84 let g opaque l px py =
85 if state.bzoom
86 then (
87 match Ffi.rectofblock opaque px py with
88 | Some [|x0;x1;y0;y1|] ->
89 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
90 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
91 state.rects <- [l.pageno, color, rect];
92 postRedisplay "getunder";
93 | _ -> ()
95 let under = Ffi.whatsunder opaque px py in
96 if under = Unone then None else Some under
98 onppundermouse g x y Unone
101 let unproject x y =
102 let g opaque l x y =
103 match Ffi.unproject opaque x y with
104 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
105 | None -> None
107 onppundermouse g x y None;
110 let showtext c s =
111 state.text <- Printf.sprintf "%c%s" c s;
112 postRedisplay "showtext";
115 let impmsg fmt = Format.ksprintf (fun s -> showtext '!' s) fmt;;
117 let pipesel opaque cmd =
118 if Ffi.hassel opaque
119 then pipef ~closew:false "pipesel"
120 (fun w ->
121 Ffi.copysel w opaque;
122 postRedisplay "pipesel"
123 ) cmd
126 let paxunder x y =
127 let g opaque l px py =
128 if Ffi.markunder opaque px py conf.paxmark
129 then
130 Some (fun () ->
131 match getopaque l.pageno with
132 | None -> ()
133 | Some opaque -> pipesel opaque conf.paxcmd
135 else None
137 postRedisplay "paxunder";
138 if conf.paxmark = Mark_page
139 then
140 List.iter (fun l ->
141 match getopaque l.pageno with
142 | None -> ()
143 | Some opaque -> Ffi.clearmark opaque) state.layout;
144 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
147 let undertext = function
148 | Unone -> "none"
149 | Ulinkuri s -> s
150 | Utext s -> "font: " ^ s
151 | Uannotation (opaque, slinkindex) ->
152 "annotation: " ^ Ffi.getannotcontents opaque slinkindex
155 let updateunder x y =
156 match getunder x y with
157 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
158 | Ulinkuri uri ->
159 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
160 Wsi.setcursor Wsi.CURSOR_INFO
161 | Utext s ->
162 if conf.underinfo then showtext 'f' ("ont: " ^ s);
163 Wsi.setcursor Wsi.CURSOR_TEXT
164 | Uannotation _ ->
165 if conf.underinfo then showtext 'a' "nnotation";
166 Wsi.setcursor Wsi.CURSOR_INFO
169 let showlinktype under =
170 if conf.underinfo && under != Unone
171 then showtext ' ' @@ undertext under
174 let intentry_with_suffix text key =
175 let text =
176 match [@warning "-4"] key with
177 | Keys.Ascii ('0'..'9' as c) -> addchar text c
178 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
179 addchar text @@ asciilower c
180 | _ ->
181 state.text <- "invalid key";
182 text
184 TEcont text
187 let wcmd fmt =
188 let b = Buffer.create 16 in
189 Printf.kbprintf
190 (fun b ->
191 let b = Buffer.to_bytes b in
192 Ffi.wcmd state.ss b @@ Bytes.length b
193 ) b fmt
196 let nogeomcmds = function
197 | s, [] -> emptystr s
198 | _ -> false
201 let layoutN ((columns, coverA, coverB), b) x y sw sh =
202 let rec fold accu n =
203 if n = Array.length b
204 then accu
205 else
206 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
207 if (vy - y) > sh &&
208 (n = coverA - 1
209 || n = state.pagecount - coverB
210 || (n - coverA) mod columns = columns - 1)
211 then accu
212 else
213 let accu =
214 if vy + h > y
215 then
216 let pagey = max 0 (y - vy) in
217 let pagedispy = if pagey > 0 then 0 else vy - y in
218 let pagedispx, pagex =
219 let pdx =
220 if n = coverA - 1 || n = state.pagecount - coverB
221 then x + (sw - w) / 2
222 else dx + xoff + x
224 if pdx < 0
225 then 0, -pdx
226 else pdx, 0
228 let pagevw =
229 let vw = sw - pagedispx in
230 let pw = w - pagex in
231 min vw pw
233 let pagevh = min (h - pagey) (sh - pagedispy) in
234 if pagevw > 0 && pagevh > 0
235 then
236 { pageno = n
237 ; pagedimno = pdimno
238 ; pagew = w
239 ; pageh = h
240 ; pagex = pagex
241 ; pagey = pagey
242 ; pagevw = pagevw
243 ; pagevh = pagevh
244 ; pagedispx = pagedispx
245 ; pagedispy = pagedispy
246 ; pagecol = 0
247 } :: accu
248 else accu
249 else accu
251 fold accu (n+1)
253 if Array.length b = 0
254 then []
255 else List.rev (fold [] (page_of_y y))
258 let layoutS (columns, b) x y sw sh =
259 let rec fold accu n =
260 if n = Array.length b
261 then accu
262 else
263 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
264 if (vy - y) > sh
265 then accu
266 else
267 let accu =
268 if vy + pageh > y
269 then
270 let x = xoff + x in
271 let pagey = max 0 (y - vy) in
272 let pagedispy = if pagey > 0 then 0 else vy - y in
273 let pagedispx, pagex =
274 if px = 0
275 then (
276 if x < 0
277 then 0, -x
278 else x, 0
280 else (
281 let px = px - x in
282 if px < 0
283 then -px, 0
284 else 0, px
287 let pagecolw = pagew/columns in
288 let pagedispx =
289 if pagecolw < sw
290 then pagedispx + ((sw - pagecolw) / 2)
291 else pagedispx
293 let pagevw =
294 let vw = sw - pagedispx in
295 let pw = pagew - pagex in
296 min vw pw
298 let pagevw = min pagevw pagecolw in
299 let pagevh = min (pageh - pagey) (sh - pagedispy) in
300 if pagevw > 0 && pagevh > 0
301 then
302 { pageno = n/columns
303 ; pagedimno = pdimno
304 ; pagew = pagew
305 ; pageh = pageh
306 ; pagex = pagex
307 ; pagey = pagey
308 ; pagevw = pagevw
309 ; pagevh = pagevh
310 ; pagedispx = pagedispx
311 ; pagedispy = pagedispy
312 ; pagecol = n mod columns
313 } :: accu
314 else accu
315 else accu
317 fold accu (n+1)
319 List.rev (fold [] 0)
322 let layout x y sw sh =
323 if nogeomcmds state.geomcmds
324 then
325 match conf.columns with
326 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
327 | Cmulti c -> layoutN c x y sw sh
328 | Csplit s -> layoutS s x y sw sh
329 else []
332 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
333 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
335 let itertiles l f =
336 let tilex = l.pagex mod conf.tilew in
337 let tiley = l.pagey mod conf.tileh in
339 let col = l.pagex / conf.tilew in
340 let row = l.pagey / conf.tileh in
342 let rec rowloop row y0 dispy h =
343 if h != 0
344 then
345 let dh = conf.tileh - y0 in
346 let dh = min h dh in
347 let rec colloop col x0 dispx w =
348 if w != 0
349 then
350 let dw = conf.tilew - x0 in
351 let dw = min w dw in
352 f col row dispx dispy x0 y0 dw dh;
353 colloop (col+1) 0 (dispx+dw) (w-dw)
355 colloop col tilex l.pagedispx l.pagevw;
356 rowloop (row+1) 0 (dispy+dh) (h-dh)
358 if l.pagevw > 0 && l.pagevh > 0
359 then rowloop row tiley l.pagedispy l.pagevh;
362 let gettileopaque l col row =
363 let key = l.pageno, state.gen, conf.colorspace,
364 conf.angle, l.pagew, l.pageh, col, row in
365 try Some (Hashtbl.find state.tilemap key)
366 with Not_found -> None
369 let puttileopaque l col row gen colorspace angle opaque size elapsed =
370 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
371 Hashtbl.add state.tilemap key (opaque, size, elapsed)
374 let drawtiles l color =
375 GlDraw.color color;
376 Ffi.begintiles ();
377 let f col row x y tilex tiley w h =
378 match gettileopaque l col row with
379 | Some (opaque, _, t) ->
380 let params = x, y, w, h, tilex, tiley in
381 if conf.invert
382 then GlTex.env (`mode `blend);
383 Ffi.drawtile params opaque;
384 if conf.invert
385 then GlTex.env (`mode `modulate);
386 if conf.debug
387 then (
388 Ffi.endtiles ();
389 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
390 let w = measurestr fstate.fontsize s in
391 GlDraw.color (0.0, 0.0, 0.0);
392 filledrect
393 (float (x-2))
394 (float (y-2))
395 (float (x+2) +. w)
396 (float (y + fstate.fontsize + 2));
397 GlDraw.color color;
398 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
399 Ffi.begintiles ();
402 | None ->
403 Ffi.endtiles ();
404 let w = let lw = state.winw - x in min lw w
405 and h = let lh = state.winh - y in min lh h
407 if conf.invert
408 then GlTex.env (`mode `blend);
409 begin match !checkerstexid with
410 | Some id ->
411 Gl.enable `texture_2d;
412 GlTex.bind_texture ~target:`texture_2d id;
413 let x0 = float x
414 and y0 = float y
415 and x1 = float (x+w)
416 and y1 = float (y+h) in
418 let tw = float w /. 16.0
419 and th = float h /. 16.0 in
420 let tx0 = float tilex /. 16.0
421 and ty0 = float tiley /. 16.0 in
422 let tx1 = tx0 +. tw
423 and ty1 = ty0 +. th in
424 Raw.sets_float Glutils.vraw ~pos:0
425 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
426 Raw.sets_float Glutils.traw ~pos:0
427 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
428 GlArray.vertex `two Glutils.vraw;
429 GlArray.tex_coord `two Glutils.traw;
430 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
431 Gl.disable `texture_2d;
433 | None ->
434 GlDraw.color (1.0, 1.0, 1.0);
435 filledrect (float x) (float y) (float (x+w)) (float (y+h));
436 end;
437 if conf.invert
438 then GlTex.env (`mode `modulate);
439 if w > 128 && h > fstate.fontsize + 10
440 then (
441 let c = if conf.invert then 1.0 else 0.0 in
442 GlDraw.color (c, c, c);
443 let c, r =
444 if conf.verbose
445 then (col*conf.tilew, row*conf.tileh)
446 else col, row
448 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
450 GlDraw.color color;
451 Ffi.begintiles ();
453 itertiles l f;
454 Ffi.endtiles ();
457 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
459 let tilevisible1 l x y =
460 let ax0 = l.pagex
461 and ax1 = l.pagex + l.pagevw
462 and ay0 = l.pagey
463 and ay1 = l.pagey + l.pagevh in
465 let bx0 = x
466 and by0 = y in
467 let bx1 = min (bx0 + conf.tilew) l.pagew
468 and by1 = min (by0 + conf.tileh) l.pageh in
470 let rx0 = max ax0 bx0
471 and ry0 = max ay0 by0
472 and rx1 = min ax1 bx1
473 and ry1 = min ay1 by1 in
475 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
476 nonemptyintersection
479 let tilevisible layout n x y =
480 let rec findpageinlayout m = function
481 | l :: rest when l.pageno = n ->
482 tilevisible1 l x y || (
483 match conf.columns with
484 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
485 | Csplit _ | Csingle _ | Cmulti _ -> false
487 | _ :: rest -> findpageinlayout 0 rest
488 | [] -> false
490 findpageinlayout 0 layout;
493 let tileready l x y =
494 tilevisible1 l x y &&
495 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
498 let tilepage n p layout =
499 let rec loop = function
500 | l :: rest ->
501 if l.pageno = n
502 then
503 let f col row _ _ _ _ _ _ =
504 if state.currently = Idle
505 then
506 match gettileopaque l col row with
507 | Some _ -> ()
508 | None ->
509 let x = col*conf.tilew
510 and y = row*conf.tileh in
511 let w =
512 let w = l.pagew - x in
513 min w conf.tilew
515 let h =
516 let h = l.pageh - y in
517 min h conf.tileh
519 let pbo =
520 if conf.usepbo
521 then Ffi.getpbo w h conf.colorspace
522 else ~< "0"
524 wcmd "tile %s %d %d %d %d %s" (~> p) x y w h (~> pbo);
525 state.currently <-
526 Tiling (
527 l, p, conf.colorspace, conf.angle,
528 state.gen, col, row, conf.tilew, conf.tileh
531 itertiles l f;
532 else
533 loop rest
535 | [] -> ()
537 if nogeomcmds state.geomcmds
538 then loop layout;
541 let preloadlayout x y sw sh =
542 let y = if y < sh then 0 else y - sh in
543 let x = min 0 (x + sw) in
544 let h = sh*3 in
545 let w = sw*3 in
546 layout x y w h;
549 let load pages =
550 let rec loop pages =
551 if state.currently = Idle
552 then
553 match pages with
554 | l :: rest ->
555 begin match getopaque l.pageno with
556 | None ->
557 wcmd "page %d %d" l.pageno l.pagedimno;
558 state.currently <- Loading (l, state.gen);
559 | Some opaque ->
560 tilepage l.pageno opaque pages;
561 loop rest
562 end;
563 | _ -> ()
565 if nogeomcmds state.geomcmds
566 then loop pages
569 let preload pages =
570 load pages;
571 if conf.preload && state.currently = Idle
572 then load (preloadlayout state.x state.y state.winw state.winh);
575 let layoutready layout =
576 let rec fold all ls =
577 all && match ls with
578 | l :: rest ->
579 let seen = ref false in
580 let allvisible = ref true in
581 let foo col row _ _ _ _ _ _ =
582 seen := true;
583 allvisible := !allvisible &&
584 begin match gettileopaque l col row with
585 | Some _ -> true
586 | None -> false
589 itertiles l foo;
590 fold (!seen && !allvisible) rest
591 | [] -> true
593 let alltilesvisible = fold true layout in
594 alltilesvisible;
597 let gotoxy x y =
598 let y = bound y 0 state.maxy in
599 let y, layout =
600 let layout = layout x y state.winw state.winh in
601 postRedisplay "gotoxy ready";
602 y, layout
604 state.x <- x;
605 state.y <- y;
606 state.layout <- layout;
607 begin match state.mode with
608 | LinkNav ln ->
609 begin match ln with
610 | Ltexact (pageno, linkno) ->
611 let rec loop = function
612 | [] ->
613 state.lnava <- Some (pageno, linkno);
614 state.mode <- LinkNav (Ltgendir 0)
615 | l :: _ when l.pageno = pageno ->
616 begin match getopaque pageno with
617 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
618 | Some opaque ->
619 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
620 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
621 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
622 then state.mode <- LinkNav (Ltgendir 0)
624 | _ :: rest -> loop rest
626 loop layout
627 | Ltnotready _ | Ltgendir _ -> ()
629 | Birdseye _ | Textentry _ | View -> ()
630 end;
631 begin match state.mode with
632 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
633 if not (pagevisible layout pageno)
634 then (
635 match state.layout with
636 | [] -> ()
637 | l :: _ ->
638 state.mode <- Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
640 | LinkNav lt ->
641 begin match lt with
642 | Ltnotready (_, dir)
643 | Ltgendir dir ->
644 let linknav =
645 let rec loop = function
646 | [] -> lt
647 | l :: rest ->
648 match getopaque l.pageno with
649 | None -> Ltnotready (l.pageno, dir)
650 | Some opaque ->
651 let link =
652 let ld =
653 if dir = 0
654 then LDfirstvisible (l.pagex, l.pagey, dir)
655 else if dir > 0 then LDfirst else LDlast
657 Ffi.findlink opaque ld
659 match link with
660 | Lnotfound -> loop rest
661 | Lfound n ->
662 showlinktype (Ffi.getlink opaque n);
663 Ltexact (l.pageno, n)
665 loop state.layout
667 state.mode <- LinkNav linknav
668 | Ltexact _ -> ()
670 | Textentry _ | View -> ()
671 end;
672 preload layout;
673 if conf.updatecurs
674 then (
675 let mx, my = state.mpos in
676 updateunder mx my;
680 let conttiling pageno opaque =
681 tilepage pageno opaque
682 (if conf.preload
683 then preloadlayout state.x state.y state.winw state.winh
684 else state.layout)
687 let gotoxy x y =
688 if not conf.verbose then state.text <- E.s;
689 gotoxy x y;
692 let getanchory (n, top, dtop) =
693 let y, h = getpageyh n in
694 if conf.presentation
695 then
696 let ips = calcips h in
697 y + truncate (top*.float h -. dtop*.float ips) + ips;
698 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
701 let addnav () = getanchor () |> cbput state.hists.nav;;
702 let addnavnorc () = getanchor () |> cbput_dont_update_rc state.hists.nav;;
704 let getnav dir =
705 let anchor = cbgetc state.hists.nav dir in
706 getanchory anchor;
709 let gotopage n top =
710 let y, h = getpageyh n in
711 let y = y + (truncate (top *. float h)) in
712 gotoxy state.x y
715 let gotopage1 n top =
716 let y = getpagey n in
717 let y = y + top in
718 gotoxy state.x y
721 let invalidate s f =
722 Glutils.redisplay := false;
723 state.layout <- [];
724 state.pdims <- [];
725 state.rects <- [];
726 state.rects1 <- [];
727 match state.geomcmds with
728 | ps, [] when emptystr ps ->
729 f ();
730 state.geomcmds <- s, [];
731 | ps, [] -> state.geomcmds <- ps, [s, f];
732 | ps, (s', _) :: rest when s' = s -> state.geomcmds <- ps, ((s, f) :: rest);
733 | ps, cmds -> state.geomcmds <- ps, ((s, f) :: cmds);
736 let flushpages () =
737 Hashtbl.iter (fun _ opaque -> wcmd "freepage %s" (~> opaque)) state.pagemap;
738 Hashtbl.clear state.pagemap;
741 let flushtiles () =
742 if not (Queue.is_empty state.tilelru)
743 then (
744 Queue.iter (fun (k, p, s) ->
745 wcmd "freetile %s" (~> p);
746 state.memused <- state.memused - s;
747 Hashtbl.remove state.tilemap k;
748 ) state.tilelru;
749 state.uioh#infochanged Memused;
750 Queue.clear state.tilelru;
752 load state.layout;
755 let stateh h =
756 let h = truncate (float h*.conf.zoom) in
757 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
758 h - d
761 let fillhelp () =
762 state.help <-
763 let sl = keystostrlist conf in
764 let rec loop accu =
765 function | [] -> accu
766 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
767 in Help.makehelp conf.urilauncher
768 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
771 let opendoc path password =
772 state.path <- path;
773 state.password <- password;
774 state.gen <- state.gen + 1;
775 state.docinfo <- [];
776 state.outlines <- [||];
778 flushpages ();
779 Ffi.setaalevel conf.aalevel;
780 Ffi.setpapercolor conf.papercolor;
781 let titlepath =
782 if emptystr state.origin
783 then path
784 else state.origin
786 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
787 wcmd "open %d %d %s\000%s\000%s\000"
788 (btod conf.usedoccss) !layouth
789 path password conf.css;
790 invalidate "reqlayout"
791 (fun () ->
792 wcmd "reqlayout %d %d %d %s\000"
793 conf.angle (FMTE.to_int conf.fitmodel)
794 (stateh state.winh) state.nameddest
796 fillhelp ();
799 let reload () =
800 state.anchor <- getanchor ();
801 state.reload <- Some (state.x, state.y, now ());
802 opendoc state.path state.password;
805 let scalecolor c = let c = c *. conf.colorscale in (c, c, c);;
806 let scalecolor2 (r, g, b) =
807 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
810 let docolumns columns =
811 match columns with
812 | Csingle _ ->
813 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
814 let rec loop pageno pdimno pdim y ph pdims =
815 if pageno != state.pagecount
816 then
817 let pdimno, ((_, w, h, xoff) as pdim), pdims =
818 match pdims with
819 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
820 pdimno+1, pdim, rest
821 | _ ->
822 pdimno, pdim, pdims
824 let x = max 0 (((state.winw - w) / 2) - xoff) in
825 let y =
826 y + (if conf.presentation
827 then (if pageno = 0 then calcips h else calcips ph + calcips h)
828 else (if pageno = 0 then 0 else conf.interpagespace))
830 a.(pageno) <- (pdimno, x, y, pdim);
831 loop (pageno+1) pdimno pdim (y + h) h pdims
833 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
834 conf.columns <- Csingle a;
836 | Cmulti ((columns, coverA, coverB), _) ->
837 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
838 let rec loop pageno pdimno pdim x y rowh pdims =
839 let rec fixrow m =
840 if m = pageno then () else
841 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
842 if h < rowh
843 then (
844 let y = y + (rowh - h) / 2 in
845 a.(m) <- (pdimno, x, y, pdim);
847 fixrow (m+1)
849 if pageno = state.pagecount
850 then fixrow (((pageno - 1) / columns) * columns)
851 else
852 let pdimno, ((_, w, h, xoff) as pdim), pdims =
853 match pdims with
854 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
855 pdimno+1, pdim, rest
856 | _ -> pdimno, pdim, pdims
858 let x, y, rowh' =
859 if pageno = coverA - 1 || pageno = state.pagecount - coverB
860 then (
861 let x = (state.winw - w) / 2 in
862 let ips =
863 if conf.presentation then calcips h else conf.interpagespace in
864 x, y + ips + rowh, h
866 else (
867 if (pageno - coverA) mod columns = 0
868 then (
869 let x = max 0 (state.winw - state.w) / 2 in
870 let y =
871 if conf.presentation
872 then
873 let ips = calcips h in
874 y + (if pageno = 0 then 0 else calcips rowh + ips)
875 else
876 y + (if pageno = 0 then 0 else conf.interpagespace)
878 x, y + rowh, h
880 else x, y, max rowh h
883 let y =
884 if pageno > 1 && (pageno - coverA) mod columns = 0
885 then (
886 let y =
887 if pageno = columns && conf.presentation
888 then (
889 let ips = calcips rowh in
890 for i = 0 to pred columns
892 let (pdimno, x, y, pdim) = a.(i) in
893 a.(i) <- (pdimno, x, y+ips, pdim)
894 done;
895 y+ips;
897 else y
899 fixrow (pageno - columns);
902 else y
904 a.(pageno) <- (pdimno, x, y, pdim);
905 let x = x + w + xoff*2 + conf.interpagespace in
906 loop (pageno+1) pdimno pdim x y rowh' pdims
908 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
909 conf.columns <- Cmulti ((columns, coverA, coverB), a);
911 | Csplit (c, _) ->
912 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
913 let rec loop pageno pdimno pdim y pdims =
914 if pageno != state.pagecount
915 then
916 let pdimno, ((_, w, h, _) as pdim), pdims =
917 match pdims with
918 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
919 pdimno+1, pdim, rest
920 | _ -> pdimno, pdim, pdims
922 let cw = w / c in
923 let rec loop1 n x y =
924 if n = c then y else (
925 a.(pageno*c + n) <- (pdimno, x, y, pdim);
926 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
929 let y = loop1 0 0 y in
930 loop (pageno+1) pdimno pdim y pdims
932 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
933 conf.columns <- Csplit (c, a);
936 let represent () =
937 docolumns conf.columns;
938 state.maxy <- calcheight ();
939 if state.reprf == noreprf
940 then (
941 match state.mode with
942 | Birdseye (_, _, pageno, _, _) ->
943 let y, h = getpageyh pageno in
944 let top = (state.winh - h) / 2 in
945 gotoxy state.x (max 0 (y - top))
946 | Textentry _ | View | LinkNav _ ->
947 let y = getanchory state.anchor in
948 let y = min y (state.maxy - state.winh) in
949 gotoxy state.x y;
951 else (
952 state.reprf ();
953 state.reprf <- noreprf;
957 let reshape ?(firsttime=false) w h =
958 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
959 if not firsttime && nogeomcmds state.geomcmds
960 then state.anchor <- getanchor ();
962 state.winw <- w;
963 let w = truncate (float w *. conf.zoom) in
964 let w = max w 2 in
965 state.winh <- h;
966 setfontsize fstate.fontsize;
967 GlMat.mode `modelview;
968 GlMat.load_identity ();
970 GlMat.mode `projection;
971 GlMat.load_identity ();
972 GlMat.rotate ~x:1.0 ~angle:180.0 ();
973 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
974 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
976 let relx =
977 if conf.zoom <= 1.0
978 then 0.0
979 else float state.x /. float state.w
981 invalidate "geometry"
982 (fun () ->
983 state.w <- w;
984 if not firsttime
985 then state.x <- truncate (relx *. float w);
986 let w =
987 match conf.columns with
988 | Csingle _ -> w
989 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
990 | Csplit (c, _) -> w * c
992 wcmd "geometry %d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
996 let gctiles () =
997 let len = Queue.length state.tilelru in
998 let layout = lazy (if conf.preload
999 then preloadlayout state.x state.y state.winw state.winh
1000 else state.layout) in
1001 let rec loop qpos =
1002 if state.memused > conf.memlimit
1003 then (
1004 if qpos < len
1005 then
1006 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1007 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1008 let (_, pw, ph, _) = getpagedim n in
1009 if gen = state.gen
1010 && colorspace = conf.colorspace
1011 && angle = conf.angle
1012 && pagew = pw
1013 && pageh = ph
1014 && (
1015 let x = col*conf.tilew and y = row*conf.tileh in
1016 tilevisible (Lazy.force_val layout) n x y
1018 then Queue.push lruitem state.tilelru
1019 else (
1020 Ffi.freepbo p;
1021 wcmd "freetile %s" (~> p);
1022 state.memused <- state.memused - s;
1023 state.uioh#infochanged Memused;
1024 Hashtbl.remove state.tilemap k;
1026 loop (qpos+1)
1029 loop 0
1032 let onpagerect pageno f =
1033 let b =
1034 match conf.columns with
1035 | Cmulti (_, b) -> b
1036 | Csingle b -> b
1037 | Csplit (_, b) -> b
1039 if pageno >= 0 && pageno < Array.length b
1040 then
1041 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1042 f w h
1045 let gotopagexy1 pageno x y =
1046 let _,w1,h1,leftx = getpagedim pageno in
1047 let top = y /. (float h1) in
1048 let left = x /. (float w1) in
1049 let py, w, h = getpageywh pageno in
1050 let wh = state.winh in
1051 let x = left *. (float w) in
1052 let x = leftx + state.x + truncate x in
1053 let sx =
1054 if x < 0 || x >= state.winw
1055 then state.x - x
1056 else state.x
1058 let pdy = truncate (top *. float h) in
1059 let y' = py + pdy in
1060 let dy = y' - state.y in
1061 let sy =
1062 if x != state.x || not (dy > 0 && dy < wh)
1063 then (
1064 if conf.presentation
1065 then
1066 if abs (py - y') > wh
1067 then y'
1068 else py
1069 else y';
1071 else state.y
1073 if state.x != sx || state.y != sy
1074 then gotoxy sx sy
1075 else gotoxy state.x state.y;
1078 let gotopagexy pageno x y =
1079 match state.mode with
1080 | Birdseye _ -> gotopage pageno 0.0
1081 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1084 let getpassword () =
1085 let passcmd = getenvdef "LLPP_ASKPASS" conf.passcmd in
1086 if emptystr passcmd
1087 then E.s
1088 else getcmdoutput (fun s ->
1089 impmsg "error getting password: %s" s;
1090 dolog "%s" s) passcmd;
1093 let pgoto opaque pageno x y =
1094 let pdimno = getpdimno pageno in
1095 let x, y = Ffi.project opaque pageno pdimno x y in
1096 gotopagexy pageno x y;
1099 let act cmds =
1100 (* dolog "%S" cmds; *)
1101 let spl = splitatchar cmds ' ' in
1102 let scan s fmt f =
1103 try Scanf.sscanf s fmt f
1104 with exn ->
1105 dolog "error processing '%S': %s" cmds @@ exntos exn;
1106 exit 1
1108 let addoutline outline =
1109 match state.currently with
1110 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1111 | Idle -> state.currently <- Outlining [outline]
1112 | Loading _ | Tiling _ ->
1113 dolog "invalid outlining state";
1114 logcurrently state.currently
1116 match spl with
1117 | "clear", "" ->
1118 state.pdims <- [];
1119 state.uioh#infochanged Pdim;
1121 | "clearrects", "" ->
1122 state.rects <- state.rects1;
1123 postRedisplay "clearrects";
1125 | "continue", args ->
1126 let n = scan args "%u" (fun n -> n) in
1127 state.pagecount <- n;
1128 begin match state.currently with
1129 | Outlining l ->
1130 state.currently <- Idle;
1131 state.outlines <- Array.of_list (List.rev l)
1132 | Idle | Loading _ | Tiling _ -> ()
1133 end;
1135 let cur, cmds = state.geomcmds in
1136 if emptystr cur then error "empty geomcmd";
1138 begin match List.rev cmds with
1139 | [] ->
1140 state.geomcmds <- E.s, [];
1141 represent ();
1142 | (s, f) :: rest ->
1143 f ();
1144 state.geomcmds <- s, List.rev rest;
1145 end;
1146 postRedisplay "continue";
1148 | "msg", args ->
1149 showtext ' ' args
1151 | "vmsg", args ->
1152 if conf.verbose then showtext ' ' args
1154 | "emsg", args ->
1155 Buffer.add_string state.errmsgs args;
1156 Buffer.add_char state.errmsgs '\n';
1157 if not state.newerrmsgs
1158 then (
1159 state.newerrmsgs <- true;
1160 postRedisplay "error message";
1163 | "progress", args ->
1164 let progress, text =
1165 scan args "%f %n"
1166 (fun f pos -> f, String.sub args pos (String.length args - pos))
1168 state.text <- text;
1169 state.progress <- progress;
1170 postRedisplay "progress"
1172 | "firstmatch", args ->
1173 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1174 scan args "%u %d %f %f %f %f %f %f %f %f"
1175 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1176 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1178 let y = (getpagey pageno) + truncate y0 in
1179 let x =
1180 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1181 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1182 else state.x
1184 addnav ();
1185 gotoxy x y;
1186 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1187 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1189 | "match", args ->
1190 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1191 scan args "%u %d %f %f %f %f %f %f %f %f"
1192 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1193 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1195 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1196 state.rects1 <-
1197 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1199 | "page", args ->
1200 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1201 let pageopaque = ~< pageopaques in
1202 begin match state.currently with
1203 | Loading (l, gen) ->
1204 vlog "page %d took %f sec" l.pageno t;
1205 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1206 let preloadedpages =
1207 if conf.preload
1208 then preloadlayout state.x state.y state.winw state.winh
1209 else state.layout
1211 let evict () =
1212 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1213 IntSet.empty preloadedpages
1215 let evictedpages =
1216 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1217 if not (IntSet.mem pageno set)
1218 then (
1219 wcmd "freepage %s" (~> opaque);
1220 key :: accu
1222 else accu
1223 ) state.pagemap []
1225 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1227 evict ();
1228 state.currently <- Idle;
1229 if gen = state.gen
1230 then (
1231 tilepage l.pageno pageopaque state.layout;
1232 load state.layout;
1233 load preloadedpages;
1234 let visible = pagevisible state.layout l.pageno in
1235 if visible
1236 then (
1237 match state.mode with
1238 | LinkNav (Ltnotready (pageno, dir)) ->
1239 if pageno = l.pageno
1240 then (
1241 let link =
1242 let ld =
1243 if dir = 0
1244 then LDfirstvisible (l.pagex, l.pagey, dir)
1245 else if dir > 0 then LDfirst else LDlast
1247 Ffi.findlink pageopaque ld
1249 match link with
1250 | Lnotfound -> ()
1251 | Lfound n ->
1252 showlinktype (Ffi.getlink pageopaque n);
1253 state.mode <- LinkNav (Ltexact (l.pageno, n))
1255 | LinkNav (Ltgendir _)
1256 | LinkNav (Ltexact _)
1257 | View
1258 | Birdseye _
1259 | Textentry _ -> ()
1262 if visible && layoutready state.layout
1263 then postRedisplay "page";
1266 | Idle | Tiling _ | Outlining _ ->
1267 dolog "Inconsistent loading state";
1268 logcurrently state.currently;
1269 exit 1
1272 | "tile" , args ->
1273 let (x, y, opaques, size, t) =
1274 scan args "%u %u %s %u %f" (fun x y p size t -> (x, y, p, size, t))
1276 let opaque = ~< opaques in
1277 begin match state.currently with
1278 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1279 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1281 Ffi.unmappbo opaque;
1282 if tilew != conf.tilew || tileh != conf.tileh
1283 then (
1284 wcmd "freetile %s" (~> opaque);
1285 state.currently <- Idle;
1286 load state.layout;
1288 else (
1289 puttileopaque l col row gen cs angle opaque size t;
1290 state.memused <- state.memused + size;
1291 state.uioh#infochanged Memused;
1292 gctiles ();
1293 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1294 opaque, size) state.tilelru;
1296 state.currently <- Idle;
1297 if gen = state.gen
1298 && conf.colorspace = cs
1299 && conf.angle = angle
1300 && tilevisible state.layout l.pageno x y
1301 then conttiling l.pageno pageopaque;
1303 preload state.layout;
1304 if gen = state.gen
1305 && conf.colorspace = cs
1306 && conf.angle = angle
1307 && tilevisible state.layout l.pageno x y
1308 && layoutready state.layout
1309 then postRedisplay "tile nothrottle";
1312 | Idle | Loading _ | Outlining _ ->
1313 dolog "Inconsistent tiling state";
1314 logcurrently state.currently;
1315 exit 1
1318 | "pdim", args ->
1319 let (n, w, h, _) as pdim =
1320 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1322 let pdim =
1323 match conf.fitmodel with
1324 | FitWidth -> pdim
1325 | FitPage | FitProportional ->
1326 match conf.columns with
1327 | Csplit _ -> (n, w, h, 0)
1328 | Csingle _ | Cmulti _ -> pdim
1330 state.pdims <- pdim :: state.pdims;
1331 state.uioh#infochanged Pdim
1333 | "o", args ->
1334 let (l, n, t, h, pos) =
1335 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1337 let s = String.sub args pos (String.length args - pos) in
1338 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1340 | "ou", args ->
1341 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1342 let s = String.sub args pos len in
1343 let pos2 = pos + len + 1 in
1344 let uri = String.sub args pos2 (String.length args - pos2) in
1345 addoutline (s, l, Ouri uri)
1347 | "on", args ->
1348 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1349 let s = String.sub args pos (String.length args - pos) in
1350 addoutline (s, l, Onone)
1352 | "a", args ->
1353 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1354 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1356 | "info", args ->
1357 let c, v = splitatchar args '\t' in
1358 let s =
1359 if nonemptystr v
1360 then
1361 if c = "Title"
1362 then (
1363 conf.title <- v;
1364 if not !ignoredoctitlte then Wsi.settitle v;
1365 args
1367 else
1368 if let len = String.length c in
1369 len > 6 && ((String.sub c (len-4) 4) = "date")
1370 then (
1371 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1372 then
1373 let b = Buffer.create 10 in
1374 Printf.bprintf b "%s\t" c;
1375 let sub p l c =
1377 Buffer.add_substring b v p l;
1378 Buffer.add_char b c;
1379 with exn -> Buffer.add_string b @@ exntos exn
1381 sub 2 4 '/';
1382 sub 6 2 '/';
1383 sub 8 2 ' ';
1384 sub 10 2 ':';
1385 sub 12 2 ':';
1386 sub 14 2 ' ';
1387 Printf.bprintf b "[%s]" v;
1388 Buffer.contents b
1389 else args
1391 else args
1392 else args
1394 state.docinfo <- (1, s) :: state.docinfo
1396 | "infoend", "" ->
1397 state.docinfo <- List.rev state.docinfo;
1398 state.uioh#infochanged Docinfo
1400 | "pass", args ->
1401 if args = "fail"
1402 then Wsi.settitle "Wrong password";
1403 let password = getpassword () in
1404 if emptystr password
1405 then error "document is password protected"
1406 else opendoc state.path password
1408 | _ -> error "unknown cmd `%S'" cmds
1411 let onhist cb =
1412 let rc = cb.rc in
1413 let action = function
1414 | HCprev -> cbget cb ~-1
1415 | HCnext -> cbget cb 1
1416 | HCfirst -> cbget cb ~-(cb.rc)
1417 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1418 and cancel () = cb.rc <- rc
1419 in (action, cancel)
1422 let search pattern forward =
1423 match conf.columns with
1424 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1425 | Csingle _ | Cmulti _ ->
1426 if nonemptystr pattern
1427 then
1428 let pn, py =
1429 match state.layout with
1430 | [] -> 0, 0
1431 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1433 wcmd "search %d %d %d %d,%s\000"
1434 (btod conf.icase) pn py (btod forward) pattern;
1437 let intentry text key =
1438 let text =
1439 if emptystr text && key = Keys.Ascii '-'
1440 then addchar text '-'
1441 else
1442 match [@warning "-4"] key with
1443 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1444 | _ ->
1445 state.text <- "invalid key";
1446 text
1448 TEcont text
1451 let linknact f s =
1452 if nonemptystr s
1453 then
1454 let n =
1455 let l = String.length s in
1456 let rec loop pos n =
1457 if pos = l
1458 then n
1459 else
1460 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1461 loop (pos+1) (n*26 + m)
1462 in loop 0 0
1464 let rec loop n = function
1465 | [] -> ()
1466 | l :: rest ->
1467 match getopaque l.pageno with
1468 | None -> loop n rest
1469 | Some opaque ->
1470 let m = Ffi.getlinkcount opaque in
1471 if n < m
1472 then
1473 let under = Ffi.getlink opaque n in
1474 f under
1475 else loop (n-m) rest
1477 loop n state.layout;
1480 let linknentry text key = match [@warning "-4"] key with
1481 | Keys.Ascii ('a' .. 'z' as c) ->
1482 let text = addchar text c in
1483 linknact (fun under -> state.text <- undertext under) text;
1484 TEcont text
1485 | _ ->
1486 state.text <- Printf.sprintf "invalid key %s" @@ Keys.to_string key;
1487 TEcont text
1490 let textentry text key = match [@warning "-4"] key with
1491 | Keys.Ascii c -> TEcont (addchar text c)
1492 | Keys.Code c -> TEcont (text ^ toutf8 c)
1493 | _ -> TEcont text
1496 let reqlayout angle fitmodel =
1497 if nogeomcmds state.geomcmds
1498 then state.anchor <- getanchor ();
1499 conf.angle <- angle mod 360;
1500 if conf.angle != 0
1501 then (
1502 match state.mode with
1503 | LinkNav _ -> state.mode <- View
1504 | Birdseye _ | Textentry _ | View -> ()
1506 conf.fitmodel <- fitmodel;
1507 invalidate "reqlayout"
1508 (fun () -> wcmd "reqlayout %d %d %d"
1509 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh));
1512 let settrim trimmargins trimfuzz =
1513 if nogeomcmds state.geomcmds
1514 then state.anchor <- getanchor ();
1515 conf.trimmargins <- trimmargins;
1516 conf.trimfuzz <- trimfuzz;
1517 let x0, y0, x1, y1 = trimfuzz in
1518 invalidate "settrim"
1519 (fun () -> wcmd "settrim %d %d %d %d %d"
1520 (btod conf.trimmargins) x0 y0 x1 y1);
1521 flushpages ();
1524 let setzoom zoom =
1525 let zoom = max 0.0001 zoom in
1526 if zoom <> conf.zoom
1527 then (
1528 state.prevzoom <- (conf.zoom, state.x);
1529 conf.zoom <- zoom;
1530 reshape state.winw state.winh;
1531 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1535 let pivotzoom ?(vw=min state.w state.winw)
1536 ?(vh=min (state.maxy-state.y) state.winh)
1537 ?(x=vw/2) ?(y=vh/2) zoom =
1538 let w = float state.w /. zoom in
1539 let hw = w /. 2.0 in
1540 let ratio = float vh /. float vw in
1541 let hh = hw *. ratio in
1542 let x0 = float x -. hw
1543 and y0 = float y -. hh in
1544 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1545 setzoom zoom;
1548 let pivotzoom ?vw ?vh ?x ?y zoom =
1549 if nogeomcmds state.geomcmds
1550 then
1551 if zoom > 1.0
1552 then pivotzoom ?vw ?vh ?x ?y zoom
1553 else setzoom zoom
1556 let setcolumns mode columns coverA coverB =
1557 state.prevcolumns <- Some (conf.columns, conf.zoom);
1558 if columns < 0
1559 then (
1560 if isbirdseye mode
1561 then impmsg "split mode doesn't work in bird's eye"
1562 else (
1563 conf.columns <- Csplit (-columns, E.a);
1564 state.x <- 0;
1565 conf.zoom <- 1.0;
1568 else (
1569 if columns < 2
1570 then (
1571 conf.columns <- Csingle E.a;
1572 state.x <- 0;
1573 setzoom 1.0;
1575 else (
1576 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1577 conf.zoom <- 1.0;
1580 reshape state.winw state.winh;
1583 let resetmstate () =
1584 state.mstate <- Mnone;
1585 Wsi.setcursor Wsi.CURSOR_INHERIT;
1588 let enterbirdseye () =
1589 let zoom = float conf.thumbw /. float state.winw in
1590 let birdseyepageno =
1591 let cy = state.winh / 2 in
1592 let fold = function
1593 | [] -> 0
1594 | l :: rest ->
1595 let rec fold best = function
1596 | [] -> best.pageno
1597 | l :: rest ->
1598 let d = cy - (l.pagedispy + l.pagevh/2)
1599 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1600 if abs d < abs dbest
1601 then fold l rest
1602 else best.pageno
1603 in fold l rest
1605 fold state.layout
1607 state.mode <-
1608 Birdseye (
1609 { conf with zoom = conf.zoom },
1610 state.x, birdseyepageno, -1, getanchor ()
1612 resetmstate ();
1613 conf.zoom <- zoom;
1614 conf.presentation <- false;
1615 conf.interpagespace <- 10;
1616 conf.hlinks <- false;
1617 conf.fitmodel <- FitPage;
1618 state.x <- 0;
1619 conf.columns <- (
1620 match conf.beyecolumns with
1621 | Some c ->
1622 conf.zoom <- 1.0;
1623 Cmulti ((c, 0, 0), E.a)
1624 | None -> Csingle E.a
1626 if conf.verbose
1627 then state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1628 (100.0*.zoom)
1629 else state.text <- E.s;
1630 reshape state.winw state.winh;
1633 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1634 state.mode <- View;
1635 conf.zoom <- c.zoom;
1636 conf.presentation <- c.presentation;
1637 conf.interpagespace <- c.interpagespace;
1638 conf.hlinks <- c.hlinks;
1639 conf.fitmodel <- c.fitmodel;
1640 conf.beyecolumns <- (
1641 match conf.columns with
1642 | Cmulti ((c, _, _), _) -> Some c
1643 | Csingle _ -> None
1644 | Csplit _ -> error "leaving bird's eye split mode"
1646 conf.columns <- (
1647 match c.columns with
1648 | Cmulti (c, _) -> Cmulti (c, E.a)
1649 | Csingle _ -> Csingle E.a
1650 | Csplit (c, _) -> Csplit (c, E.a)
1652 if conf.verbose
1653 then state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1654 (100.0*.conf.zoom);
1655 reshape state.winw state.winh;
1656 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1657 state.x <- leftx;
1660 let togglebirdseye () =
1661 match state.mode with
1662 | Birdseye vals -> leavebirdseye vals true
1663 | View -> enterbirdseye ()
1664 | Textentry _ | LinkNav _ -> ()
1667 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1668 let pageno = max 0 (pageno - incr) in
1669 let rec loop = function
1670 | [] -> gotopage1 pageno 0
1671 | l :: _ when l.pageno = pageno ->
1672 if l.pagedispy >= 0 && l.pagey = 0
1673 then postRedisplay "upbirdseye"
1674 else gotopage1 pageno 0
1675 | _ :: rest -> loop rest
1677 loop state.layout;
1678 state.text <- E.s;
1679 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1682 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1683 let pageno = min (state.pagecount - 1) (pageno + incr) in
1684 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1685 let rec loop = function
1686 | [] ->
1687 let y, h = getpageyh pageno in
1688 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1689 gotoxy state.x (clamp dy)
1690 | l :: _ when l.pageno = pageno ->
1691 if l.pagevh != l.pageh
1692 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1693 else postRedisplay "downbirdseye"
1694 | _ :: rest -> loop rest
1696 loop state.layout;
1697 state.text <- E.s;
1700 let optentry mode _ key =
1701 let btos b = if b then "on" else "off" in
1702 match [@warning "-4"] key with
1703 | Keys.Ascii 'C' ->
1704 let ondone s =
1706 let n, a, b = multicolumns_of_string s in
1707 setcolumns mode n a b;
1708 with exn ->
1709 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1711 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1713 | Keys.Ascii 'Z' ->
1714 let ondone s =
1716 let zoom = float (int_of_string s) /. 100.0 in
1717 pivotzoom zoom
1718 with exn ->
1719 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1721 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1723 | Keys.Ascii 'i' ->
1724 conf.icase <- not conf.icase;
1725 TEdone ("case insensitive search " ^ (btos conf.icase))
1727 | Keys.Ascii 'v' ->
1728 conf.verbose <- not conf.verbose;
1729 TEdone ("verbose " ^ (btos conf.verbose))
1731 | Keys.Ascii 'd' ->
1732 conf.debug <- not conf.debug;
1733 TEdone ("debug " ^ (btos conf.debug))
1735 | Keys.Ascii 'f' ->
1736 conf.underinfo <- not conf.underinfo;
1737 TEdone ("underinfo " ^ btos conf.underinfo)
1739 | Keys.Ascii 'T' ->
1740 settrim (not conf.trimmargins) conf.trimfuzz;
1741 TEdone ("trim margins " ^ btos conf.trimmargins)
1743 | Keys.Ascii 'I' ->
1744 conf.invert <- not conf.invert;
1745 TEdone ("invert colors " ^ btos conf.invert)
1747 | Keys.Ascii 'x' ->
1748 let ondone s =
1749 cbput state.hists.sel s;
1750 conf.selcmd <- s;
1752 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1753 textentry, ondone, true)
1755 | Keys.Ascii 'M' ->
1756 if conf.pax == None
1757 then conf.pax <- Some 0.0
1758 else conf.pax <- None;
1759 TEdone ("PAX " ^ btos (conf.pax != None))
1761 | (Keys.Ascii c) ->
1762 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1763 TEstop
1765 | _ -> TEcont state.text
1768 let adderrmsg src msg =
1769 Buffer.add_string state.errmsgs msg;
1770 state.newerrmsgs <- true;
1771 postRedisplay src
1774 let adderrfmt src fmt = Format.ksprintf (fun s -> adderrmsg src s) fmt;;
1776 class outlinelistview ~zebra ~source =
1777 let settext autonarrow s =
1778 if autonarrow
1779 then
1780 let ss = source#statestr in
1781 state.text <- if emptystr ss
1782 then "[" ^ s ^ "]"
1783 else "{" ^ ss ^ "} [" ^ s ^ "]"
1784 else state.text <- s
1786 object (self)
1787 inherit listview
1788 ~zebra
1789 ~helpmode:false
1790 ~source:(source :> lvsource)
1791 ~trusted:false
1792 ~modehash:(findkeyhash conf "outline")
1793 as super
1795 val m_autonarrow = false
1797 method! key key mask =
1798 let maxrows =
1799 if emptystr state.text
1800 then fstate.maxrows
1801 else fstate.maxrows - 2
1803 let calcfirst first active =
1804 if active > first
1805 then
1806 let rows = active - first in
1807 if rows > maxrows then active - maxrows else first
1808 else active
1810 let navigate incr =
1811 let active = m_active + incr in
1812 let active = bound active 0 (source#getitemcount - 1) in
1813 let first = calcfirst m_first active in
1814 postRedisplay "outline navigate";
1815 coe {< m_active = active; m_first = first >}
1817 let navscroll first =
1818 let active =
1819 let dist = m_active - first in
1820 if dist < 0
1821 then first
1822 else (
1823 if dist < maxrows
1824 then m_active
1825 else first + maxrows
1828 postRedisplay "outline navscroll";
1829 coe {< m_first = first; m_active = active >}
1831 let ctrl = Wsi.withctrl mask in
1832 let open Keys in
1833 match Wsi.kc2kt key with
1834 | Ascii 'a' when ctrl ->
1835 let text =
1836 if m_autonarrow
1837 then (
1838 source#denarrow;
1841 else (
1842 let pattern = source#renarrow in
1843 if nonemptystr m_qsearch
1844 then (source#narrow m_qsearch; m_qsearch)
1845 else pattern
1848 settext (not m_autonarrow) text;
1849 postRedisplay "toggle auto narrowing";
1850 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1852 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1853 settext true E.s;
1854 postRedisplay "toggle auto narrowing";
1855 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1857 | Ascii 'n' when ctrl ->
1858 source#narrow m_qsearch;
1859 if not m_autonarrow
1860 then source#add_narrow_pattern m_qsearch;
1861 postRedisplay "outline ctrl-n";
1862 coe {< m_first = 0; m_active = 0 >}
1864 | Ascii 'S' when ctrl ->
1865 let active = source#calcactive (getanchor ()) in
1866 let first = firstof m_first active in
1867 postRedisplay "outline ctrl-s";
1868 coe {< m_first = first; m_active = active >}
1870 | Ascii 'u' when ctrl ->
1871 postRedisplay "outline ctrl-u";
1872 if m_autonarrow && nonemptystr m_qsearch
1873 then (
1874 ignore (source#renarrow);
1875 settext m_autonarrow E.s;
1876 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1878 else (
1879 source#del_narrow_pattern;
1880 let pattern = source#renarrow in
1881 let text =
1882 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1884 settext m_autonarrow text;
1885 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1888 | Ascii 'l' when ctrl ->
1889 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1890 postRedisplay "outline ctrl-l";
1891 coe {< m_first = first >}
1893 | Ascii '\t' when m_autonarrow ->
1894 if nonemptystr m_qsearch
1895 then (
1896 postRedisplay "outline list view tab";
1897 source#add_narrow_pattern m_qsearch;
1898 settext true E.s;
1899 coe {< m_qsearch = E.s >}
1901 else coe self
1903 | Escape when m_autonarrow ->
1904 if nonemptystr m_qsearch
1905 then source#add_narrow_pattern m_qsearch;
1906 super#key key mask
1908 | Enter when m_autonarrow ->
1909 if nonemptystr m_qsearch
1910 then source#add_narrow_pattern m_qsearch;
1911 super#key key mask
1913 | (Ascii _ | Code _) when m_autonarrow ->
1914 let pattern = m_qsearch ^ toutf8 key in
1915 postRedisplay "outlinelistview autonarrow add";
1916 source#narrow pattern;
1917 settext true pattern;
1918 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1920 | Backspace when m_autonarrow ->
1921 if emptystr m_qsearch
1922 then coe self
1923 else
1924 let pattern = withoutlastutf8 m_qsearch in
1925 postRedisplay "outlinelistview autonarrow backspace";
1926 ignore (source#renarrow);
1927 source#narrow pattern;
1928 settext true pattern;
1929 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1931 | Up when ctrl -> navscroll (max 0 (m_first - 1))
1933 | Down when ctrl ->
1934 navscroll (min (source#getitemcount - 1) (m_first + 1))
1936 | Up -> navigate ~-1
1937 | Down -> navigate 1
1938 | Prior -> navigate ~-(fstate.maxrows)
1939 | Next -> navigate fstate.maxrows
1941 | Right ->
1942 let o =
1943 if ctrl
1944 then (
1945 postRedisplay "outline ctrl right";
1946 {< m_pan = m_pan + 1 >}
1948 else (
1949 if Wsi.withshift mask
1950 then self#nextcurlevel 1
1951 else self#updownlevel 1
1954 coe o
1956 | Left ->
1957 let o =
1958 if ctrl
1959 then (
1960 postRedisplay "outline ctrl left";
1961 {< m_pan = m_pan - 1 >}
1963 else (
1964 if Wsi.withshift mask
1965 then self#nextcurlevel ~-1
1966 else self#updownlevel ~-1
1969 coe o
1971 | Home ->
1972 postRedisplay "outline home";
1973 coe {< m_first = 0; m_active = 0 >}
1975 | End ->
1976 let active = source#getitemcount - 1 in
1977 let first = max 0 (active - fstate.maxrows) in
1978 postRedisplay "outline end";
1979 coe {< m_active = active; m_first = first >}
1981 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
1982 super#key key mask
1983 end;;
1985 let genhistoutlines () =
1986 Config.gethist ()
1987 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
1988 compare c2.lastvisit c1.lastvisit)
1989 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
1990 let path = if nonemptystr origin then origin else path in
1991 let base = mbtoutf8 @@ Filename.basename path in
1992 (base ^ "\000" ^ c.title, 1, Ohistory hist)
1996 let gotohist (path, c, bookmarks, x, anchor, origin) =
1997 Config.save leavebirdseye;
1998 state.anchor <- anchor;
1999 state.bookmarks <- bookmarks;
2000 state.origin <- origin;
2001 state.x <- x;
2002 setconf conf c;
2003 let x0, y0, x1, y1 = conf.trimfuzz in
2004 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2005 reshape ~firsttime:true state.winw state.winh;
2006 opendoc path origin;
2007 setzoom c.zoom;
2010 let setcheckers enabled =
2011 match !checkerstexid with
2012 | None -> if enabled then checkerstexid := Some (makecheckers ())
2013 | Some id ->
2014 if not enabled
2015 then (
2016 GlTex.delete_texture id;
2017 checkerstexid := None;
2021 let describe_layout layout =
2022 let d =
2023 match layout with
2024 | [] -> "Page 0"
2025 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2026 | l :: rest ->
2027 let rangestr a b =
2028 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2029 else Printf.sprintf "%d%s%d" (a.pageno+1)
2030 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
2031 (b.pageno+1)
2033 let rec fold s la lb = function
2034 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2035 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2036 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2038 fold "Pages" l l rest
2040 let percent =
2041 let maxy = maxy () in
2042 if maxy <= 0
2043 then 100.
2044 else 100. *. (float state.y /. float maxy)
2046 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2049 let setpresentationmode v =
2050 let n = page_of_y state.y in
2051 state.anchor <- (n, 0.0, 1.0);
2052 conf.presentation <- v;
2053 if conf.fitmodel = FitPage
2054 then reqlayout conf.angle conf.fitmodel;
2055 represent ();
2058 let enterinfomode =
2059 let btos b = if b then Utf8syms.radical else E.s in
2060 let showextended = ref false in
2061 let showcolors = ref false in
2062 let leave mode _ = state.mode <- mode in
2063 let src =
2064 (object
2065 val mutable m_l = []
2066 val mutable m_a = E.a
2067 val mutable m_prev_uioh = nouioh
2068 val mutable m_prev_mode = View
2070 inherit lvsourcebase
2072 method reset prev_mode prev_uioh =
2073 m_a <- Array.of_list (List.rev m_l);
2074 m_l <- [];
2075 m_prev_mode <- prev_mode;
2076 m_prev_uioh <- prev_uioh;
2078 method int name get set =
2079 m_l <-
2080 (name, `int get, 1,
2081 Action (
2082 fun u ->
2083 let ondone s =
2084 try set (int_of_string s)
2085 with exn ->
2086 state.text <- Printf.sprintf "bad integer `%s': %s"
2087 s @@ exntos exn
2089 state.text <- E.s;
2090 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2091 state.mode <- Textentry (te, leave m_prev_mode);
2093 )) :: m_l
2095 method int_with_suffix name get set =
2096 m_l <-
2097 (name, `intws get, 1,
2098 Action (
2099 fun u ->
2100 let ondone s =
2101 try set (int_of_string_with_suffix s)
2102 with exn ->
2103 state.text <- Printf.sprintf "bad integer `%s': %s"
2104 s @@ exntos exn
2106 state.text <- E.s;
2107 let te =
2108 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2110 state.mode <- Textentry (te, leave m_prev_mode);
2112 )) :: m_l
2114 method bool ?(offset=1) ?(btos=btos) name get set =
2115 m_l <-
2116 (name, `bool (btos, get), offset, Action (
2117 fun u ->
2118 let v = get () in
2119 set (not v);
2121 )) :: m_l
2123 method color name get set =
2124 m_l <-
2125 (name, `color get, 1,
2126 Action (
2127 fun u ->
2128 let invalid = (nan, nan, nan) in
2129 let ondone s =
2130 let c =
2131 try color_of_string s
2132 with exn ->
2133 state.text <- Printf.sprintf "bad color `%s': %s"
2134 s @@ exntos exn;
2135 invalid
2137 if c <> invalid
2138 then set c;
2140 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2141 state.text <- color_to_string (get ());
2142 state.mode <- Textentry (te, leave m_prev_mode);
2144 )) :: m_l
2146 method string name get set =
2147 m_l <-
2148 (name, `string get, 1,
2149 Action (
2150 fun u ->
2151 let ondone s = set s in
2152 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2153 state.mode <- Textentry (te, leave m_prev_mode);
2155 )) :: m_l
2157 method colorspace name get set =
2158 m_l <-
2159 (name, `string get, 1,
2160 Action (
2161 fun _ ->
2162 let source =
2163 (object
2164 inherit lvsourcebase
2166 initializer
2167 m_active <- CSTE.to_int conf.colorspace;
2168 m_first <- 0;
2170 method getitemcount =
2171 Array.length CSTE.names
2172 method getitem n =
2173 (CSTE.names.(n), 0)
2174 method exit ~uioh ~cancel ~active ~first ~pan =
2175 ignore (uioh, first, pan);
2176 if not cancel then set active;
2177 None
2178 method hasaction _ = true
2179 end)
2181 state.text <- E.s;
2182 let modehash = findkeyhash conf "info" in
2183 coe (new listview ~zebra:false ~helpmode:false
2184 ~source ~trusted:true ~modehash)
2185 )) :: m_l
2187 method paxmark name get set =
2188 m_l <-
2189 (name, `string get, 1,
2190 Action (
2191 fun _ ->
2192 let source =
2193 (object
2194 inherit lvsourcebase
2196 initializer
2197 m_active <- MTE.to_int conf.paxmark;
2198 m_first <- 0;
2200 method getitemcount = Array.length MTE.names
2201 method getitem n = (MTE.names.(n), 0)
2202 method exit ~uioh ~cancel ~active ~first ~pan =
2203 ignore (uioh, first, pan);
2204 if not cancel then set active;
2205 None
2206 method hasaction _ = true
2207 end)
2209 state.text <- E.s;
2210 let modehash = findkeyhash conf "info" in
2211 coe (new listview ~zebra:false ~helpmode:false
2212 ~source ~trusted:true ~modehash)
2213 )) :: m_l
2215 method fitmodel name get set =
2216 m_l <-
2217 (name, `string get, 1,
2218 Action (
2219 fun _ ->
2220 let source =
2221 (object
2222 inherit lvsourcebase
2224 initializer
2225 m_active <- FMTE.to_int conf.fitmodel;
2226 m_first <- 0;
2228 method getitemcount = Array.length FMTE.names
2229 method getitem n = (FMTE.names.(n), 0)
2230 method exit ~uioh ~cancel ~active ~first ~pan =
2231 ignore (uioh, first, pan);
2232 if not cancel then set active;
2233 None
2234 method hasaction _ = true
2235 end)
2237 state.text <- E.s;
2238 let modehash = findkeyhash conf "info" in
2239 coe (new listview ~zebra:false ~helpmode:false
2240 ~source ~trusted:true ~modehash)
2241 )) :: m_l
2243 method caption s offset =
2244 m_l <- (s, `empty, offset, Noaction) :: m_l
2246 method caption2 s f offset =
2247 m_l <- (s, `string f, offset, Noaction) :: m_l
2249 method getitemcount = Array.length m_a
2251 method getitem n =
2252 let tostr = function
2253 | `int f -> string_of_int (f ())
2254 | `intws f -> string_with_suffix_of_int (f ())
2255 | `string f -> f ()
2256 | `color f -> color_to_string (f ())
2257 | `bool (btos, f) -> btos (f ())
2258 | `empty -> E.s
2260 let name, t, offset, _ = m_a.(n) in
2261 ((let s = tostr t in
2262 if nonemptystr s
2263 then Printf.sprintf "%s\t%s" name s
2264 else name),
2265 offset)
2267 method exit ~uioh ~cancel ~active ~first ~pan =
2268 let uiohopt =
2269 if not cancel
2270 then (
2271 let uioh =
2272 match m_a.(active) with
2273 | _, _, _, Action f -> f uioh
2274 | _, _, _, Noaction -> uioh
2276 Some uioh
2278 else None
2280 m_active <- active;
2281 m_first <- first;
2282 m_pan <- pan;
2283 uiohopt
2285 method hasaction n =
2286 match m_a.(n) with
2287 | _, _, _, Action _ -> true
2288 | _, _, _, Noaction -> false
2290 initializer m_active <- 1
2291 end)
2293 let rec fillsrc prevmode prevuioh =
2294 let sep () = src#caption E.s 0 in
2295 let colorp name get set =
2296 src#string name
2297 (fun () -> color_to_string (get ()))
2298 (fun v ->
2299 try set @@ color_of_string v
2300 with exn ->
2301 state.text <-
2302 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2305 let rgba name get set =
2306 src#string name
2307 (fun () -> get () |> rgba_to_string)
2308 (fun v ->
2309 try set @@ rgba_of_string v
2310 with exn ->
2311 state.text <-
2312 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2315 let oldmode = state.mode in
2316 let birdseye = isbirdseye state.mode in
2318 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2320 src#bool "presentation mode"
2321 (fun () -> conf.presentation)
2322 (fun v -> setpresentationmode v);
2324 src#bool "ignore case in searches"
2325 (fun () -> conf.icase)
2326 (fun v -> conf.icase <- v);
2328 src#bool "preload"
2329 (fun () -> conf.preload)
2330 (fun v -> conf.preload <- v);
2332 src#bool "highlight links"
2333 (fun () -> conf.hlinks)
2334 (fun v -> conf.hlinks <- v);
2336 src#bool "under info"
2337 (fun () -> conf.underinfo)
2338 (fun v -> conf.underinfo <- v);
2340 src#fitmodel "fit model"
2341 (fun () -> FMTE.to_string conf.fitmodel)
2342 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2344 src#bool "trim margins"
2345 (fun () -> conf.trimmargins)
2346 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2348 sep ();
2349 src#int "inter-page space"
2350 (fun () -> conf.interpagespace)
2351 (fun n ->
2352 conf.interpagespace <- n;
2353 docolumns conf.columns;
2354 let pageno, py =
2355 match state.layout with
2356 | [] -> 0, 0
2357 | l :: _ -> l.pageno, l.pagey
2359 state.maxy <- calcheight ();
2360 let y = getpagey pageno in
2361 gotoxy state.x (y + py)
2364 src#int "page bias"
2365 (fun () -> conf.pagebias)
2366 (fun v -> conf.pagebias <- v);
2368 src#int "scroll step"
2369 (fun () -> conf.scrollstep)
2370 (fun n -> conf.scrollstep <- n);
2372 src#int "horizontal scroll step"
2373 (fun () -> conf.hscrollstep)
2374 (fun v -> conf.hscrollstep <- v);
2376 src#int "auto scroll step"
2377 (fun () ->
2378 match state.autoscroll with
2379 | Some step -> step
2380 | _ -> conf.autoscrollstep)
2381 (fun n ->
2382 let n = boundastep state.winh n in
2383 if state.autoscroll <> None
2384 then state.autoscroll <- Some n;
2385 conf.autoscrollstep <- n);
2387 src#int "zoom"
2388 (fun () -> truncate (conf.zoom *. 100.))
2389 (fun v -> pivotzoom ((float v) /. 100.));
2391 src#int "rotation"
2392 (fun () -> conf.angle)
2393 (fun v -> reqlayout v conf.fitmodel);
2395 src#int "scroll bar width"
2396 (fun () -> conf.scrollbw)
2397 (fun v ->
2398 conf.scrollbw <- v;
2399 reshape state.winw state.winh;
2402 src#int "scroll handle height"
2403 (fun () -> conf.scrollh)
2404 (fun v -> conf.scrollh <- v;);
2406 src#int "thumbnail width"
2407 (fun () -> conf.thumbw)
2408 (fun v ->
2409 conf.thumbw <- min 4096 v;
2410 match oldmode with
2411 | Birdseye beye ->
2412 leavebirdseye beye false;
2413 enterbirdseye ()
2414 | Textentry _
2415 | View
2416 | LinkNav _ -> ()
2419 let mode = state.mode in
2420 src#string "columns"
2421 (fun () ->
2422 match conf.columns with
2423 | Csingle _ -> "1"
2424 | Cmulti (multi, _) -> multicolumns_to_string multi
2425 | Csplit (count, _) -> "-" ^ string_of_int count
2427 (fun v ->
2428 let n, a, b = multicolumns_of_string v in
2429 setcolumns mode n a b);
2431 sep ();
2432 src#caption "Pixmap cache" 0;
2433 src#int_with_suffix "size (advisory)"
2434 (fun () -> conf.memlimit)
2435 (fun v -> conf.memlimit <- v);
2437 src#caption2 "used"
2438 (fun () ->
2439 Printf.sprintf "%s bytes, %d tiles"
2440 (string_with_suffix_of_int state.memused)
2441 (Hashtbl.length state.tilemap)) 1;
2443 sep ();
2444 src#caption "Layout" 0;
2445 src#caption2 "Dimension"
2446 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2447 state.winw state.winh
2448 state.w state.maxy)
2450 if conf.debug
2451 then src#caption2 "Position" (fun () ->
2452 Printf.sprintf "%dx%d" state.x state.y
2454 else src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2456 sep ();
2457 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2458 "Save these parameters as global defaults at exit"
2459 (fun () -> conf.bedefault)
2460 (fun v -> conf.bedefault <- v);
2462 sep ();
2463 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2464 src#bool ~offset:0 ~btos "Extended parameters"
2465 (fun () -> !showextended)
2466 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2467 if !showextended
2468 then (
2469 src#bool "checkers"
2470 (fun () -> conf.checkers)
2471 (fun v -> conf.checkers <- v; setcheckers v);
2472 src#bool "update cursor"
2473 (fun () -> conf.updatecurs)
2474 (fun v -> conf.updatecurs <- v);
2475 src#bool "scroll-bar on the left"
2476 (fun () -> conf.leftscroll)
2477 (fun v -> conf.leftscroll <- v);
2478 src#bool "verbose"
2479 (fun () -> conf.verbose)
2480 (fun v -> conf.verbose <- v);
2481 src#bool "invert colors"
2482 (fun () -> conf.invert)
2483 (fun v -> conf.invert <- v);
2484 src#bool "max fit"
2485 (fun () -> conf.maxhfit)
2486 (fun v -> conf.maxhfit <- v);
2487 src#bool "pax mode"
2488 (fun () -> conf.pax != None)
2489 (fun v ->
2490 if v
2491 then conf.pax <- Some (now ())
2492 else conf.pax <- None);
2493 src#string "uri launcher"
2494 (fun () -> conf.urilauncher)
2495 (fun v -> conf.urilauncher <- v);
2496 src#string "path launcher"
2497 (fun () -> conf.pathlauncher)
2498 (fun v -> conf.pathlauncher <- v);
2499 src#string "tile size"
2500 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2501 (fun v ->
2503 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2504 conf.tilew <- max 64 w;
2505 conf.tileh <- max 64 h;
2506 flushtiles ();
2507 with exn ->
2508 state.text <- Printf.sprintf "bad tile size `%s': %s"
2509 v @@ exntos exn
2511 src#int "texture count"
2512 (fun () -> conf.texcount)
2513 (fun v ->
2514 if Ffi.realloctexts v
2515 then conf.texcount <- v
2516 else impmsg "failed to set texture count please retry later"
2518 src#int "slice height"
2519 (fun () -> conf.sliceheight)
2520 (fun v ->
2521 conf.sliceheight <- v;
2522 wcmd "sliceh %d" conf.sliceheight;
2524 src#int "anti-aliasing level"
2525 (fun () -> conf.aalevel)
2526 (fun v ->
2527 conf.aalevel <- bound v 0 8;
2528 state.anchor <- getanchor ();
2529 opendoc state.path state.password;
2531 src#string "page scroll scaling factor"
2532 (fun () -> string_of_float conf.pgscale)
2533 (fun v ->
2534 try conf.pgscale <- float_of_string v
2535 with exn ->
2536 state.text <-
2537 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2538 @@ exntos exn
2540 src#int "ui font size"
2541 (fun () -> fstate.fontsize)
2542 (fun v -> setfontsize (bound v 5 100));
2543 src#int "hint font size"
2544 (fun () -> conf.hfsize)
2545 (fun v -> conf.hfsize <- bound v 5 100);
2546 src#string "trim fuzz"
2547 (fun () -> irect_to_string conf.trimfuzz)
2548 (fun v ->
2550 conf.trimfuzz <- irect_of_string v;
2551 if conf.trimmargins
2552 then settrim true conf.trimfuzz;
2553 with exn ->
2554 state.text <- Printf.sprintf "bad irect `%s': %s" v
2555 @@ exntos exn
2557 src#string "selection command"
2558 (fun () -> conf.selcmd)
2559 (fun v -> conf.selcmd <- v);
2560 src#string "synctex command"
2561 (fun () -> conf.stcmd)
2562 (fun v -> conf.stcmd <- v);
2563 src#string "pax command"
2564 (fun () -> conf.paxcmd)
2565 (fun v -> conf.paxcmd <- v);
2566 src#string "ask password command"
2567 (fun () -> conf.passcmd)
2568 (fun v -> conf.passcmd <- v);
2569 src#string "save path command"
2570 (fun () -> conf.savecmd)
2571 (fun v -> conf.savecmd <- v);
2572 src#colorspace "color space"
2573 (fun () -> CSTE.to_string conf.colorspace)
2574 (fun v ->
2575 conf.colorspace <- CSTE.of_int v;
2576 wcmd "cs %d" v;
2577 load state.layout;
2579 src#paxmark "pax mark method"
2580 (fun () -> MTE.to_string conf.paxmark)
2581 (fun v -> conf.paxmark <- MTE.of_int v);
2582 if Ffi.bousable ()
2583 then
2584 src#bool "use PBO"
2585 (fun () -> conf.usepbo)
2586 (fun v -> conf.usepbo <- v);
2587 src#bool "mouse wheel scrolls pages"
2588 (fun () -> conf.wheelbypage)
2589 (fun v -> conf.wheelbypage <- v);
2590 src#bool "open remote links in a new instance"
2591 (fun () -> conf.riani)
2592 (fun v -> conf.riani <- v);
2593 src#bool "edit annotations inline"
2594 (fun () -> conf.annotinline)
2595 (fun v -> conf.annotinline <- v);
2596 src#bool "coarse positioning in presentation mode"
2597 (fun () -> conf.coarseprespos)
2598 (fun v -> conf.coarseprespos <- v);
2599 src#bool "use document CSS"
2600 (fun () -> conf.usedoccss)
2601 (fun v ->
2602 conf.usedoccss <- v;
2603 state.anchor <- getanchor ();
2604 opendoc state.path state.password;
2606 src#bool ~btos "colors"
2607 (fun () -> !showcolors)
2608 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2609 if !showcolors
2610 then (
2611 colorp " background"
2612 (fun () -> conf.bgcolor)
2613 (fun v -> conf.bgcolor <- v);
2615 rgba " paper color"
2616 (fun () -> conf.papercolor)
2617 (fun v ->
2618 conf.papercolor <- v;
2619 Ffi.setpapercolor conf.papercolor;
2620 flushtiles ();
2622 rgba " scrollbar"
2623 (fun () -> conf.sbarcolor)
2624 (fun v -> conf.sbarcolor <- v);
2625 rgba " scrollbar handle"
2626 (fun () -> conf.sbarhndlcolor)
2627 (fun v -> conf.sbarhndlcolor <- v);
2628 rgba " texture color"
2629 (fun () -> conf.texturecolor)
2630 (fun v ->
2631 GlTex.env (`color v);
2632 conf.texturecolor <- v;
2637 sep ();
2638 src#caption "Document" 0;
2639 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2640 src#caption2 "Pages" (fun () -> string_of_int state.pagecount) 1;
2641 src#caption2 "Dimensions"
2642 (fun () -> string_of_int (List.length state.pdims)) 1;
2643 if nonemptystr conf.css
2644 then src#caption2 "CSS" (fun () -> conf.css) 1;
2645 if conf.trimmargins
2646 then (
2647 sep ();
2648 src#caption "Trimmed margins" 0;
2649 src#caption2 "Dimensions"
2650 (fun () -> string_of_int (List.length state.pdims)) 1;
2653 sep ();
2654 src#caption "OpenGL" 0;
2655 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2656 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2658 sep ();
2659 src#caption "Location" 0;
2660 if nonemptystr state.origin
2661 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2662 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2664 src#reset prevmode prevuioh;
2666 fun () -> (
2667 state.text <- E.s;
2668 resetmstate ();
2669 let prevmode = state.mode
2670 and prevuioh = state.uioh in
2671 fillsrc prevmode prevuioh;
2672 let source = (src :> lvsource) in
2673 let modehash = findkeyhash conf "info" in
2674 state.uioh <-
2675 coe (object (self)
2676 inherit listview ~zebra:false ~helpmode:false
2677 ~source ~trusted:true ~modehash as super
2678 val mutable m_prevmemused = 0
2679 method! infochanged = function
2680 | Memused ->
2681 if m_prevmemused != state.memused
2682 then (
2683 m_prevmemused <- state.memused;
2684 postRedisplay "memusedchanged";
2686 | Pdim -> postRedisplay "pdimchanged"
2687 | Docinfo -> fillsrc prevmode prevuioh
2689 method! key key mask =
2690 if not (Wsi.withctrl mask)
2691 then
2692 match [@warning "-4"] Wsi.kc2kt key with
2693 | Keys.Left -> coe (self#updownlevel ~-1)
2694 | Keys.Right -> coe (self#updownlevel 1)
2695 | _ -> super#key key mask
2696 else super#key key mask
2697 end);
2698 postRedisplay "info";
2702 let enterhelpmode =
2703 let source =
2704 (object
2705 inherit lvsourcebase
2706 method getitemcount = Array.length state.help
2707 method getitem n =
2708 let s, l, _ = state.help.(n) in
2709 (s, l)
2711 method exit ~uioh ~cancel ~active ~first ~pan =
2712 let optuioh =
2713 if not cancel
2714 then (
2715 match state.help.(active) with
2716 | _, _, Action f -> Some (f uioh)
2717 | _, _, Noaction -> Some uioh
2719 else None
2721 m_active <- active;
2722 m_first <- first;
2723 m_pan <- pan;
2724 optuioh
2726 method hasaction n =
2727 match state.help.(n) with
2728 | _, _, Action _ -> true
2729 | _, _, Noaction -> false
2731 initializer
2732 m_active <- -1
2733 end)
2735 fun () ->
2736 let modehash = findkeyhash conf "help" in
2737 resetmstate ();
2738 state.uioh <- coe (new listview
2739 ~zebra:false ~helpmode:true
2740 ~source ~trusted:true ~modehash);
2741 postRedisplay "help";
2744 let entermsgsmode =
2745 let msgsource =
2746 (object
2747 inherit lvsourcebase
2748 val mutable m_items = E.a
2750 method getitemcount = 1 + Array.length m_items
2752 method getitem n =
2753 if n = 0
2754 then "[Clear]", 0
2755 else m_items.(n-1), 0
2757 method exit ~uioh ~cancel ~active ~first ~pan =
2758 ignore uioh;
2759 if not cancel
2760 then (
2761 if active = 0
2762 then Buffer.clear state.errmsgs;
2764 m_active <- active;
2765 m_first <- first;
2766 m_pan <- pan;
2767 None
2769 method hasaction n =
2770 n = 0
2772 method reset =
2773 state.newerrmsgs <- false;
2774 let l = Str.split Utils.Re.crlf (Buffer.contents state.errmsgs) in
2775 m_items <- Array.of_list l
2777 initializer
2778 m_active <- 0
2779 end)
2780 in fun () ->
2781 state.text <- E.s;
2782 resetmstate ();
2783 msgsource#reset;
2784 let source = (msgsource :> lvsource) in
2785 let modehash = findkeyhash conf "listview" in
2786 state.uioh <-
2787 coe (object
2788 inherit listview ~zebra:false ~helpmode:false
2789 ~source ~trusted:false ~modehash as super
2790 method! display =
2791 if state.newerrmsgs
2792 then msgsource#reset;
2793 super#display
2794 end);
2795 postRedisplay "msgs";
2798 let getusertext s =
2799 let editor = getenvdef "EDITOR" E.s in
2800 if emptystr editor
2801 then E.s
2802 else
2803 let tmppath = Filename.temp_file "llpp" "note" in
2804 if nonemptystr s
2805 then (
2806 let oc = open_out tmppath in
2807 output_string oc s;
2808 close_out oc;
2810 let execstr = editor ^ " " ^ tmppath in
2811 let s =
2812 match spawn execstr [] with
2813 | exception exn ->
2814 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
2816 | pid ->
2817 match Unix.waitpid [] pid with
2818 | exception exn ->
2819 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
2821 | (_pid, status) ->
2822 match status with
2823 | Unix.WEXITED 0 -> filecontents tmppath
2824 | Unix.WEXITED n ->
2825 impmsg "editor process(%s) exited abnormally: %d" execstr n;
2827 | Unix.WSIGNALED n ->
2828 impmsg "editor process(%s) was killed by signal %d" execstr n;
2830 | Unix.WSTOPPED n ->
2831 impmsg "editor(%s) process was stopped by signal %d" execstr n;
2834 match Unix.unlink tmppath with
2835 | exception exn ->
2836 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
2838 | () -> s
2841 let enterannotmode opaque slinkindex =
2842 let msgsource =
2843 (object
2844 inherit lvsourcebase
2845 val mutable m_text = E.s
2846 val mutable m_items = E.a
2848 method getitemcount = Array.length m_items
2850 method getitem n =
2851 let label, _func = m_items.(n) in
2852 label, 0
2854 method exit ~uioh ~cancel ~active ~first ~pan =
2855 ignore (uioh, first, pan);
2856 if not cancel
2857 then (
2858 let _label, func = m_items.(active) in
2859 func ()
2861 None
2863 method hasaction n = nonemptystr @@ fst m_items.(n)
2865 method reset s =
2866 let rec split accu b i =
2867 let p = b+i in
2868 if p = String.length s
2869 then (String.sub s b (p-b), fun () -> ()) :: accu
2870 else
2871 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2872 then
2873 let ss = if i = 0 then E.s else String.sub s b i in
2874 split ((ss, fun () -> ())::accu) (p+1) 0
2875 else split accu b (i+1)
2877 let cleanup () =
2878 wcmd "freepage %s" (~> opaque);
2879 let keys =
2880 Hashtbl.fold (fun key opaque' accu ->
2881 if opaque' = opaque'
2882 then key :: accu else accu) state.pagemap []
2884 List.iter (Hashtbl.remove state.pagemap) keys;
2885 flushtiles ();
2886 gotoxy state.x state.y
2888 let dele () =
2889 Ffi.delannot opaque slinkindex;
2890 cleanup ();
2892 let edit inline () =
2893 let update s =
2894 if emptystr s
2895 then dele ()
2896 else (
2897 Ffi.modannot opaque slinkindex s;
2898 cleanup ();
2901 if inline
2902 then
2903 let mode = state.mode in
2904 state.mode <-
2905 Textentry (
2906 ("annotation: ", m_text, None, textentry, update, true),
2907 fun _ -> state.mode <- mode
2909 state.text <- E.s;
2910 enttext ();
2911 else
2912 let s = getusertext m_text in
2913 update s
2915 m_text <- s;
2916 m_items <-
2917 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
2918 :: ("[Delete]", dele)
2919 :: ("[Edit]", edit conf.annotinline)
2920 :: (E.s, fun () -> ())
2921 :: split [] 0 0 |> List.rev |> Array.of_list
2923 initializer
2924 m_active <- 0
2925 end)
2927 state.text <- E.s;
2928 let s = Ffi.getannotcontents opaque slinkindex in
2929 resetmstate ();
2930 msgsource#reset s;
2931 let source = (msgsource :> lvsource) in
2932 let modehash = findkeyhash conf "listview" in
2933 state.uioh <- coe (object
2934 inherit listview ~zebra:false ~helpmode:false
2935 ~source ~trusted:false ~modehash
2936 end);
2937 postRedisplay "enterannotmode";
2940 let gotoremote spec =
2941 let filename, dest = splitatchar spec '#' in
2942 let getpath filename =
2943 let path =
2944 if nonemptystr filename
2945 then
2946 if Filename.is_relative filename
2947 then
2948 let dir = Filename.dirname state.path in
2949 let dir =
2950 if Filename.is_implicit dir
2951 then Filename.concat (Sys.getcwd ()) dir
2952 else dir
2954 Filename.concat dir filename
2955 else filename
2956 else E.s
2958 if Sys.file_exists path
2959 then path
2960 else E.s
2962 let path = getpath filename in
2963 let dospawn lcmd =
2964 if conf.riani
2965 then
2966 let cmd = Lazy.force_val lcmd in
2967 match spawn cmd with
2968 | _pid -> ()
2969 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
2970 else
2971 let anchor = getanchor () in
2972 let ranchor = state.path, state.password, anchor, state.origin in
2973 state.origin <- E.s;
2974 state.ranchors <- ranchor :: state.ranchors;
2975 opendoc path E.s;
2977 if substratis spec 0 "page="
2978 then
2979 match Scanf.sscanf spec "page=%d" (fun n -> n) with
2980 | pageno ->
2981 state.anchor <- (pageno, 0.0, 0.0);
2982 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
2983 | exception exn ->
2984 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
2985 else (
2986 state.nameddest <- dest;
2987 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
2991 let gotounder = function
2992 | Ulinkuri s when Ffi.isexternallink s ->
2993 if substratis s 0 "file://"
2994 then gotoremote @@ String.sub s 7 (String.length s - 7)
2995 else Help.gotouri conf.urilauncher s
2996 | Ulinkuri s ->
2997 let pageno, x, y = Ffi.uritolocation s in
2998 addnav ();
2999 gotopagexy pageno x y
3000 | Utext _ | Unone -> ()
3001 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
3004 let gotooutline (_, _, kind) =
3005 match kind with
3006 | Onone -> ()
3007 | Oanchor ((pageno, y, _) as anchor) ->
3008 addnav ();
3009 gotoxy state.x @@
3010 getanchory (if conf.presentation then (pageno, y, 1.0) else anchor)
3011 | Ouri uri -> gotounder (Ulinkuri uri)
3012 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
3013 | Oremote (remote, pageno) ->
3014 error "gotounder (Uremote (%S,%d) )" remote pageno
3015 | Ohistory hist -> gotohist hist
3016 | Oremotedest (path, dest) ->
3017 error "gotounder (Uremotedest (%S, %S))" path dest
3020 class outlinesoucebase fetchoutlines = object (self)
3021 inherit lvsourcebase
3022 val mutable m_items = E.a
3023 val mutable m_minfo = E.a
3024 val mutable m_orig_items = E.a
3025 val mutable m_orig_minfo = E.a
3026 val mutable m_narrow_patterns = []
3027 val mutable m_gen = -1
3029 method getitemcount = Array.length m_items
3031 method getitem n =
3032 let s, n, _ = m_items.(n) in
3033 (s, n+0)
3035 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3036 ignore (uioh, first);
3037 let items, minfo =
3038 if m_narrow_patterns = []
3039 then m_orig_items, m_orig_minfo
3040 else m_items, m_minfo
3042 m_pan <- pan;
3043 if not cancel
3044 then (
3045 m_items <- items;
3046 m_minfo <- minfo;
3047 gotooutline m_items.(active);
3049 else (
3050 m_items <- items;
3051 m_minfo <- minfo;
3053 None
3055 method hasaction (_:int) = true
3057 method greetmsg =
3058 if Array.length m_items != Array.length m_orig_items
3059 then
3060 let s =
3061 match m_narrow_patterns with
3062 | one :: [] -> one
3063 | many -> String.concat Utf8syms.ellipsis (List.rev many)
3065 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3066 else E.s
3068 method statestr =
3069 match m_narrow_patterns with
3070 | [] -> E.s
3071 | one :: [] -> one
3072 | head :: _ -> Utf8syms.ellipsis ^ head
3074 method narrow pattern =
3075 match Str.regexp_case_fold pattern with
3076 | exception _ -> ()
3077 | re ->
3078 let rec loop accu minfo n =
3079 if n = -1
3080 then (
3081 m_items <- Array.of_list accu;
3082 m_minfo <- Array.of_list minfo;
3084 else
3085 let (s, _, _) as o = m_items.(n) in
3086 let accu, minfo =
3087 match Str.search_forward re s 0 with
3088 | exception Not_found -> accu, minfo
3089 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3091 loop accu minfo (n-1)
3093 loop [] [] (Array.length m_items - 1)
3095 method! getminfo = m_minfo
3097 method denarrow =
3098 m_orig_items <- fetchoutlines ();
3099 m_minfo <- m_orig_minfo;
3100 m_items <- m_orig_items
3102 method add_narrow_pattern pattern =
3103 m_narrow_patterns <- pattern :: m_narrow_patterns
3105 method del_narrow_pattern =
3106 match m_narrow_patterns with
3107 | _ :: rest -> m_narrow_patterns <- rest
3108 | [] -> ()
3110 method renarrow =
3111 self#denarrow;
3112 match m_narrow_patterns with
3113 | pattern :: [] -> self#narrow pattern; pattern
3114 | list ->
3115 List.fold_left (fun accu pattern ->
3116 self#narrow pattern;
3117 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
3119 method calcactive (_:anchor) = 0
3121 method reset anchor items =
3122 if state.gen != m_gen
3123 then (
3124 m_orig_items <- items;
3125 m_items <- items;
3126 m_narrow_patterns <- [];
3127 m_minfo <- E.a;
3128 m_orig_minfo <- E.a;
3129 m_gen <- state.gen;
3131 else (
3132 if items != m_orig_items
3133 then (
3134 m_orig_items <- items;
3135 if m_narrow_patterns == []
3136 then m_items <- items;
3139 let active = self#calcactive anchor in
3140 m_active <- active;
3141 m_first <- firstof m_first active
3145 let outlinesource fetchoutlines =
3146 (object
3147 inherit outlinesoucebase fetchoutlines
3148 method! calcactive anchor =
3149 let rely = getanchory anchor in
3150 let rec loop n best bestd =
3151 if n = Array.length m_items
3152 then best
3153 else
3154 let _, _, kind = m_items.(n) in
3155 match kind with
3156 | Oanchor anchor ->
3157 let orely = getanchory anchor in
3158 let d = abs (orely - rely) in
3159 if d < bestd
3160 then loop (n+1) n d
3161 else loop (n+1) best bestd
3162 | Onone | Oremote _ | Olaunch _
3163 | Oremotedest _ | Ouri _ | Ohistory _ ->
3164 loop (n+1) best bestd
3166 loop 0 ~-1 max_int
3167 end)
3170 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3171 let fetchoutlines sourcetype () =
3172 match sourcetype with
3173 | `bookmarks -> Array.of_list state.bookmarks
3174 | `outlines -> state.outlines
3175 | `history -> genhistoutlines () |> Array.of_list
3177 let so = outlinesource (fetchoutlines `outlines) in
3178 let sb = outlinesource (fetchoutlines `bookmarks) in
3179 let sh = outlinesource (fetchoutlines `history) in
3180 let mkselector sourcetype source =
3181 (fun errmsg ->
3182 let outlines = fetchoutlines sourcetype () in
3183 if Array.length outlines = 0
3184 then showtext ' ' errmsg
3185 else (
3186 resetmstate ();
3187 Wsi.setcursor Wsi.CURSOR_INHERIT;
3188 let anchor = getanchor () in
3189 source#reset anchor outlines;
3190 state.text <- source#greetmsg;
3191 state.uioh <-
3192 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3193 postRedisplay "enter selector";
3197 let mkenter sourcetype errmsg s = fun () -> mkselector sourcetype s errmsg in
3198 ( mkenter `outlines "document has no outline" so
3199 , mkenter `bookmarks "document has no bookmarks (yet)" sb
3200 , mkenter `history "history is empty" sh )
3204 let addbookmark title a =
3205 let b = List.filter (fun (title', _, _) -> title <> title') state.bookmarks in
3206 state.bookmarks <- (title, 0, Oanchor a) :: b;;
3208 let quickbookmark ?title () =
3209 match state.layout with
3210 | [] -> ()
3211 | l :: _ ->
3212 let title =
3213 match title with
3214 | None ->
3215 Unix.(
3216 let tm = localtime (now ()) in
3217 Printf.sprintf
3218 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3219 (l.pageno+1)
3220 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3222 | Some title -> title
3224 addbookmark title (getanchor1 l)
3227 let setautoscrollspeed step goingdown =
3228 let incr = max 1 ((abs step) / 2) in
3229 let incr = if goingdown then incr else -incr in
3230 let astep = boundastep state.winh (step + incr) in
3231 state.autoscroll <- Some astep;
3234 let canpan () =
3235 match conf.columns with
3236 | Csplit _ -> true
3237 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3240 let panbound x = bound x (-state.w) state.winw;;
3242 let existsinrow pageno (columns, coverA, coverB) p =
3243 let last = ((pageno - coverA) mod columns) + columns in
3244 let rec any = function
3245 | [] -> false
3246 | l :: rest ->
3247 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3248 then p l
3249 else (
3250 if not (p l)
3251 then (if l.pageno = last then false else any rest)
3252 else true
3255 any state.layout
3258 let nextpage () =
3259 match state.layout with
3260 | [] ->
3261 let pageno = page_of_y state.y in
3262 gotoxy state.x (getpagey (pageno+1))
3263 | l :: rest ->
3264 match conf.columns with
3265 | Csingle _ ->
3266 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3267 then
3268 let y = clamp (pgscale state.winh) in
3269 gotoxy state.x y
3270 else
3271 let pageno = min (l.pageno+1) (state.pagecount-1) in
3272 gotoxy state.x (getpagey pageno)
3273 | Cmulti ((c, _, _) as cl, _) ->
3274 if conf.presentation
3275 && (existsinrow l.pageno cl
3276 (fun l -> l.pageh > l.pagey + l.pagevh))
3277 then
3278 let y = clamp (pgscale state.winh) in
3279 gotoxy state.x y
3280 else
3281 let pageno = min (l.pageno+c) (state.pagecount-1) in
3282 gotoxy state.x (getpagey pageno)
3283 | Csplit (n, _) ->
3284 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3285 then
3286 let pagey, pageh = getpageyh l.pageno in
3287 let pagey = pagey + pageh * l.pagecol in
3288 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3289 gotoxy state.x (pagey + pageh + ips)
3292 let prevpage () =
3293 match state.layout with
3294 | [] ->
3295 let pageno = page_of_y state.y in
3296 gotoxy state.x (getpagey (pageno-1))
3297 | l :: _ ->
3298 match conf.columns with
3299 | Csingle _ ->
3300 if conf.presentation && l.pagey != 0
3301 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3302 else
3303 let pageno = max 0 (l.pageno-1) in
3304 gotoxy state.x (getpagey pageno)
3305 | Cmulti ((c, _, coverB) as cl, _) ->
3306 if conf.presentation &&
3307 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3308 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3309 else
3310 let decr =
3311 if l.pageno = state.pagecount - coverB
3312 then 1
3313 else c
3315 let pageno = max 0 (l.pageno-decr) in
3316 gotoxy state.x (getpagey pageno)
3317 | Csplit (n, _) ->
3318 let y =
3319 if l.pagecol = 0
3320 then
3321 if l.pageno = 0
3322 then l.pagey
3323 else
3324 let pageno = max 0 (l.pageno-1) in
3325 let pagey, pageh = getpageyh pageno in
3326 pagey + (n-1)*pageh
3327 else
3328 let pagey, pageh = getpageyh l.pageno in
3329 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3331 gotoxy state.x y
3334 let save () =
3335 if emptystr conf.savecmd
3336 then adderrmsg "savepath-command is empty"
3337 "don't know where to save modified document"
3338 else
3339 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3340 let path =
3341 getcmdoutput
3342 (fun exn ->
3343 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3344 savecmd
3346 if nonemptystr path
3347 then
3348 let tmp = path ^ ".tmp" in
3349 Ffi.savedoc tmp;
3350 Unix.rename tmp path;
3353 let viewkeyboard key mask =
3354 let enttext te =
3355 let mode = state.mode in
3356 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3357 state.text <- E.s;
3358 enttext ();
3359 postRedisplay "view:enttext"
3361 let ctrl = Wsi.withctrl mask in
3362 let open Keys in
3363 match Wsi.kc2kt key with
3364 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3366 | Ascii 'Q' -> exit 0
3368 | Ascii 'z' ->
3369 let yloc f =
3370 match List.rev state.rects with
3371 | [] -> ()
3372 | (pageno, _, (_, y0, _, y1, _, y2, _, y3)) :: _ ->
3373 f pageno (y0, y1, y2, y3)
3374 and yminmax (y0, y1, y2, y3) =
3375 let ym = min y0 y1 |> min y2 |> min y3 |> truncate in
3376 let yM = max y0 y1 |> max y2 |> max y3 |> truncate in
3377 ym, yM
3379 let ondone msg = state.text <- msg
3380 and zmod _ _ k =
3381 match [@warning "-4"] k with
3382 | Keys.Ascii 'z' ->
3383 let f pageno ys =
3384 let ym, yM = yminmax ys in
3385 let hh = (yM - ym)/2 in
3386 gotopage1 pageno (ym + hh - state.winh/2)
3388 yloc f;
3389 TEdone "center"
3390 | Keys.Ascii 't' ->
3391 let f pageno ys =
3392 let ym, _ = yminmax ys in
3393 gotopage1 pageno ym
3395 yloc f;
3396 TEdone "top"
3397 | Keys.Ascii 'b' ->
3398 let f pageno ys =
3399 let _, yM = yminmax ys in
3400 gotopage1 pageno (yM - state.winh)
3402 yloc f;
3403 TEdone "bottom"
3404 | _ -> TEstop
3406 enttext (": ", E.s, None, zmod state.mode, ondone, true)
3408 | Ascii 'W' ->
3409 if Ffi.hasunsavedchanges ()
3410 then save ()
3412 | Insert ->
3413 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3414 then (
3415 state.mode <- (
3416 match state.lnava with
3417 | None -> LinkNav (Ltgendir 0)
3418 | Some pn -> LinkNav (Ltexact pn)
3420 gotoxy state.x state.y;
3422 else impmsg "keyboard link navigation does not work under rotation"
3424 | Escape | Ascii 'q' ->
3425 begin match state.mstate with
3426 | Mzoomrect _ ->
3427 resetmstate ();
3428 postRedisplay "kill rect";
3429 | Msel _
3430 | Mpan _
3431 | Mscrolly | Mscrollx
3432 | Mzoom _
3433 | Mnone ->
3434 begin match state.mode with
3435 | LinkNav ln ->
3436 begin match ln with
3437 | Ltexact pl -> state.lnava <- Some pl
3438 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3439 end;
3440 state.mode <- View;
3441 postRedisplay "esc leave linknav"
3442 | Birdseye _ | Textentry _ | View ->
3443 match state.ranchors with
3444 | [] -> raise Quit
3445 | (path, password, anchor, origin) :: rest ->
3446 state.ranchors <- rest;
3447 state.anchor <- anchor;
3448 state.origin <- origin;
3449 state.nameddest <- E.s;
3450 opendoc path password
3451 end;
3452 end;
3454 | Backspace ->
3455 addnavnorc ();
3456 gotoxy state.x (getnav ~-1)
3458 | Ascii 'o' -> enteroutlinemode ()
3459 | Ascii 'H' -> enterhistmode ()
3461 | Ascii 'u' ->
3462 state.rects <- [];
3463 state.text <- E.s;
3464 Hashtbl.iter (fun _ opaque ->
3465 Ffi.clearmark opaque;
3466 Hashtbl.clear state.prects) state.pagemap;
3467 postRedisplay "dehighlight";
3469 | Ascii (('/' | '?') as c) ->
3470 let ondone isforw s =
3471 cbput state.hists.pat s;
3472 state.searchpattern <- s;
3473 search s isforw
3475 let s = String.make 1 c in
3476 enttext (s, E.s, Some (onhist state.hists.pat),
3477 textentry, ondone (c = '/'), true)
3479 | Ascii '+' | Ascii '=' when ctrl ->
3480 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3481 pivotzoom (conf.zoom +. incr)
3483 | Ascii '+' ->
3484 let ondone s =
3485 let n =
3486 try int_of_string s with exn ->
3487 state.text <-
3488 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3489 max_int
3491 if n != max_int
3492 then (
3493 conf.pagebias <- n;
3494 state.text <- "page bias is now " ^ string_of_int n;
3497 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3499 | Ascii '-' when ctrl ->
3500 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3501 pivotzoom (max 0.01 (conf.zoom -. decr))
3503 | Ascii '-' ->
3504 let ondone msg = state.text <- msg in
3505 enttext ("option: ", E.s, None,
3506 optentry state.mode, ondone, true)
3508 | Ascii '0' when ctrl ->
3509 if conf.zoom = 1.0
3510 then gotoxy 0 state.y
3511 else setzoom 1.0
3513 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3514 let cols =
3515 match conf.columns with
3516 | Csingle _ | Cmulti _ -> 1
3517 | Csplit (n, _) -> n
3519 let h = state.winh -
3520 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3522 let zoom = Ffi.zoomforh state.winw h 0 cols in
3523 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3524 then setzoom zoom
3526 | Ascii '3' when ctrl ->
3527 let fm =
3528 match conf.fitmodel with
3529 | FitWidth -> FitProportional
3530 | FitProportional -> FitPage
3531 | FitPage -> FitWidth
3533 state.text <- "fit model: " ^ FMTE.to_string fm;
3534 reqlayout conf.angle fm
3536 | Ascii '4' when ctrl ->
3537 let zoom = Ffi.getmaxw () /. float state.winw in
3538 if zoom > 0.0 then setzoom zoom
3540 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3542 | Ascii ('0'..'9' as c) when not ctrl ->
3543 let ondone s =
3544 let n =
3545 try int_of_string s with exn ->
3546 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3549 if n >= 0
3550 then (
3551 addnav ();
3552 cbput state.hists.pag (string_of_int n);
3553 gotopage1 (n + conf.pagebias - 1) 0;
3556 let pageentry text = function [@warning "-4"]
3557 | Keys.Ascii 'g' -> TEdone text
3558 | key -> intentry text key
3560 let text = String.make 1 c in
3561 enttext (":", text, Some (onhist state.hists.pag),
3562 pageentry, ondone, true)
3564 | Ascii 'b' ->
3565 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3566 postRedisplay "toggle scrollbar";
3568 | Ascii 'B' ->
3569 state.bzoom <- not state.bzoom;
3570 state.rects <- [];
3571 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3573 | Ascii 'l' ->
3574 conf.hlinks <- not conf.hlinks;
3575 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3576 postRedisplay "toggle highlightlinks";
3578 | Ascii 'F' ->
3579 if conf.angle mod 360 = 0
3580 then (
3581 state.glinks <- true;
3582 let mode = state.mode in
3583 state.mode <-
3584 Textentry (
3585 ("goto: ", E.s, None, linknentry, linknact gotounder, false),
3586 (fun _ ->
3587 state.glinks <- false;
3588 state.mode <- mode)
3590 state.text <- E.s;
3591 postRedisplay "view:linkent(F)"
3593 else impmsg "hint mode does not work under rotation"
3595 | Ascii 'y' ->
3596 state.glinks <- true;
3597 let mode = state.mode in
3598 state.mode <-
3599 Textentry (
3600 ("copy: ", E.s, None, linknentry,
3601 linknact (fun under ->
3602 selstring conf.selcmd (undertext under)), false),
3603 (fun _ ->
3604 state.glinks <- false;
3605 state.mode <- mode)
3607 state.text <- E.s;
3608 postRedisplay "view:linkent"
3610 | Ascii 'a' ->
3611 begin match state.autoscroll with
3612 | Some step ->
3613 conf.autoscrollstep <- step;
3614 state.autoscroll <- None
3615 | None ->
3616 state.autoscroll <- Some conf.autoscrollstep;
3617 state.slideshow <- state.slideshow land lnot 2
3620 | Ascii 'p' when ctrl ->
3621 launchpath () (* XXX where do error messages go? *)
3623 | Ascii 'P' ->
3624 setpresentationmode (not conf.presentation);
3625 showtext ' ' ("presentation mode " ^
3626 if conf.presentation then "on" else "off");
3628 | Ascii 'f' ->
3629 if List.mem Wsi.Fullscreen state.winstate
3630 then Wsi.reshape conf.cwinw conf.cwinh
3631 else Wsi.fullscreen ()
3633 | Ascii ('p'|'N') -> search state.searchpattern false
3634 | Ascii 'n' | Fn 3 -> search state.searchpattern true
3636 | Ascii 't' ->
3637 begin match state.layout with
3638 | [] -> ()
3639 | l :: _ -> gotoxy state.x (getpagey l.pageno)
3642 | Ascii ' ' -> nextpage ()
3643 | Delete -> prevpage ()
3644 | Ascii '=' -> showtext ' ' (describe_layout state.layout);
3646 | Ascii 'w' ->
3647 begin match state.layout with
3648 | [] -> ()
3649 | l :: _ ->
3650 Wsi.reshape l.pagew l.pageh;
3651 postRedisplay "w"
3654 | Ascii '\'' -> enterbookmarkmode ()
3655 | Ascii 'h' | Fn 1 -> enterhelpmode ()
3656 | Ascii 'i' -> enterinfomode ()
3657 | Ascii 'e' when Buffer.length state.errmsgs > 0 -> entermsgsmode ()
3659 | Ascii 'm' ->
3660 let ondone s =
3661 match state.layout with
3662 | l :: _ when nonemptystr s -> addbookmark s @@ getanchor1 l
3663 | _ -> ()
3665 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3667 | Ascii '~' ->
3668 quickbookmark ();
3669 showtext ' ' "Quick bookmark added";
3671 | Ascii 'x' -> state.roam ()
3673 | Ascii ('<'|'>' as c) ->
3674 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3676 | Ascii ('['|']' as c) ->
3677 conf.colorscale <-
3678 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3679 postRedisplay "brightness";
3681 | Ascii 'c' when state.mode = View ->
3682 if Wsi.withalt mask
3683 then (
3684 if conf.zoom > 1.0
3685 then
3686 let m = (state.winw - state.w) / 2 in
3687 gotoxy m state.y
3689 else
3690 let (c, a, b), z =
3691 match state.prevcolumns with
3692 | None -> (1, 0, 0), 1.0
3693 | Some (columns, z) ->
3694 let cab =
3695 match columns with
3696 | Csplit (c, _) -> -c, 0, 0
3697 | Cmulti ((c, a, b), _) -> c, a, b
3698 | Csingle _ -> 1, 0, 0
3700 cab, z
3702 setcolumns View c a b;
3703 setzoom z
3705 | Down | Up when ctrl && Wsi.withshift mask ->
3706 let zoom, x = state.prevzoom in
3707 setzoom zoom;
3708 state.x <- x;
3710 | Up ->
3711 begin match state.autoscroll with
3712 | None ->
3713 begin match state.mode with
3714 | Birdseye beye -> upbirdseye 1 beye
3715 | Textentry _ | View | LinkNav _ ->
3716 if ctrl
3717 then gotoxy state.x (clamp ~-(state.winh/2))
3718 else (
3719 if not (Wsi.withshift mask) && conf.presentation
3720 then prevpage ()
3721 else gotoxy state.x (clamp (-conf.scrollstep))
3724 | Some n -> setautoscrollspeed n false
3727 | Down ->
3728 begin match state.autoscroll with
3729 | None ->
3730 begin match state.mode with
3731 | Birdseye beye -> downbirdseye 1 beye
3732 | Textentry _ | View | LinkNav _ ->
3733 if ctrl
3734 then gotoxy state.x (clamp (state.winh/2))
3735 else (
3736 if not (Wsi.withshift mask) && conf.presentation
3737 then nextpage ()
3738 else gotoxy state.x (clamp (conf.scrollstep))
3741 | Some n -> setautoscrollspeed n true
3744 | Left | Right when not (Wsi.withalt mask) ->
3745 if canpan ()
3746 then
3747 let dx =
3748 if ctrl
3749 then state.winw / 2
3750 else conf.hscrollstep
3752 let dx =
3753 let pv = Wsi.kc2kt key in
3754 if pv = Keys.Left then dx else -dx
3756 gotoxy (panbound (state.x + dx)) state.y
3757 else (
3758 state.text <- E.s;
3759 postRedisplay "left/right"
3762 | Prior ->
3763 let y =
3764 if ctrl
3765 then
3766 match state.layout with
3767 | [] -> state.y
3768 | l :: _ -> state.y - l.pagey
3769 else clamp (pgscale (-state.winh))
3771 gotoxy state.x y
3773 | Next ->
3774 let y =
3775 if ctrl
3776 then
3777 match List.rev state.layout with
3778 | [] -> state.y
3779 | l :: _ -> getpagey l.pageno
3780 else clamp (pgscale state.winh)
3782 gotoxy state.x y
3784 | Ascii 'g' | Home ->
3785 addnav ();
3786 gotoxy 0 0
3787 | Ascii 'G' | End ->
3788 addnav ();
3789 gotoxy 0 (clamp state.maxy)
3791 | Right when Wsi.withalt mask ->
3792 addnavnorc ();
3793 gotoxy state.x (getnav 1)
3794 | Left when Wsi.withalt mask ->
3795 addnavnorc ();
3796 gotoxy state.x (getnav ~-1)
3798 | Ascii 'r' ->
3799 reload ()
3801 | Ascii 'v' when conf.debug ->
3802 state.rects <- [];
3803 List.iter (fun l ->
3804 match getopaque l.pageno with
3805 | None -> ()
3806 | Some opaque ->
3807 let x0, y0, x1, y1 = Ffi.pagebbox opaque in
3808 let rect = (float x0, float y0,
3809 float x1, float y0,
3810 float x1, float y1,
3811 float x0, float y1) in
3812 debugrect rect;
3813 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3814 state.rects <- (l.pageno, color, rect) :: state.rects;
3815 ) state.layout;
3816 postRedisplay "v";
3818 | Ascii '|' ->
3819 let mode = state.mode in
3820 let cmd = ref E.s in
3821 let onleave = function
3822 | Cancel -> state.mode <- mode
3823 | Confirm ->
3824 List.iter (fun l ->
3825 match getopaque l.pageno with
3826 | Some opaque -> pipesel opaque !cmd
3827 | None -> ()) state.layout;
3828 state.mode <- mode
3830 let ondone s =
3831 cbput state.hists.sel s;
3832 cmd := s
3834 let te =
3835 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3837 postRedisplay "|";
3838 state.mode <- Textentry (te, onleave);
3840 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3841 vlog "huh? %s" (Wsi.keyname key)
3844 let linknavkeyboard key mask linknav =
3845 let pv = Wsi.kc2kt key in
3846 let getpage pageno =
3847 let rec loop = function
3848 | [] -> None
3849 | l :: _ when l.pageno = pageno -> Some l
3850 | _ :: rest -> loop rest
3851 in loop state.layout
3853 let doexact (pageno, n) =
3854 match getopaque pageno, getpage pageno with
3855 | Some opaque, Some l ->
3856 if pv = Keys.Enter
3857 then
3858 let under = Ffi.getlink opaque n in
3859 postRedisplay "link gotounder";
3860 gotounder under;
3861 state.mode <- View;
3862 else
3863 let opt, dir =
3864 let open Keys in
3865 match pv with
3866 | Home -> Some (Ffi.findlink opaque LDfirst), -1
3867 | End -> Some (Ffi.findlink opaque LDlast), 1
3868 | Left -> Some (Ffi.findlink opaque (LDleft n)), -1
3869 | Right -> Some (Ffi.findlink opaque (LDright n)), 1
3870 | Up -> Some (Ffi.findlink opaque (LDup n)), -1
3871 | Down -> Some (Ffi.findlink opaque (LDdown n)), 1
3872 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3873 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3875 let pwl l dir =
3876 begin match Ffi.findpwl l.pageno dir with
3877 | Pwlnotfound -> ()
3878 | Pwl pageno ->
3879 let notfound dir =
3880 state.mode <- LinkNav (Ltgendir dir);
3881 let y, h = getpageyh pageno in
3882 let y =
3883 if dir < 0
3884 then y + h - state.winh
3885 else y
3887 gotoxy state.x y
3889 begin match getopaque pageno, getpage pageno with
3890 | Some opaque, Some _ ->
3891 let link =
3892 let ld = if dir > 0 then LDfirst else LDlast in
3893 Ffi.findlink opaque ld
3895 begin match link with
3896 | Lfound m ->
3897 showlinktype (Ffi.getlink opaque m);
3898 state.mode <- LinkNav (Ltexact (pageno, m));
3899 postRedisplay "linknav jpage";
3900 | Lnotfound -> notfound dir
3901 end;
3902 | _ -> notfound dir
3903 end;
3904 end;
3906 begin match opt with
3907 | Some Lnotfound -> pwl l dir;
3908 | Some (Lfound m) ->
3909 if m = n
3910 then pwl l dir
3911 else (
3912 let _, y0, _, y1 = Ffi.getlinkrect opaque m in
3913 if y0 < l.pagey
3914 then gotopage1 l.pageno y0
3915 else (
3916 let d = fstate.fontsize + 1 in
3917 if y1 - l.pagey > l.pagevh - d
3918 then gotopage1 l.pageno (y1 - state.winh + d)
3919 else postRedisplay "linknav";
3921 showlinktype (Ffi.getlink opaque m);
3922 state.mode <- LinkNav (Ltexact (l.pageno, m));
3925 | None -> viewkeyboard key mask
3926 end;
3927 | _ -> viewkeyboard key mask
3929 if pv = Keys.Insert
3930 then (
3931 begin match linknav with
3932 | Ltexact pa -> state.lnava <- Some pa
3933 | Ltgendir _ | Ltnotready _ -> ()
3934 end;
3935 state.mode <- View;
3936 postRedisplay "leave linknav"
3938 else
3939 match linknav with
3940 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3941 | Ltexact exact -> doexact exact
3944 let keyboard key mask =
3945 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
3946 then wcmd "interrupt"
3947 else state.uioh <- state.uioh#key key mask
3950 let birdseyekeyboard key mask
3951 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3952 let incr =
3953 match conf.columns with
3954 | Csingle _ -> 1
3955 | Cmulti ((c, _, _), _) -> c
3956 | Csplit _ -> error "bird's eye split mode"
3958 let pgh layout = List.fold_left
3959 (fun m l -> max l.pageh m) state.winh layout in
3960 let open Keys in
3961 match Wsi.kc2kt key with
3962 | Ascii 'l' when Wsi.withctrl mask ->
3963 let y, h = getpageyh pageno in
3964 let top = (state.winh - h) / 2 in
3965 gotoxy state.x (max 0 (y - top))
3966 | Enter -> leavebirdseye beye false
3967 | Escape -> leavebirdseye beye true
3968 | Up -> upbirdseye incr beye
3969 | Down -> downbirdseye incr beye
3970 | Left -> upbirdseye 1 beye
3971 | Right -> downbirdseye 1 beye
3973 | Prior ->
3974 begin match state.layout with
3975 | l :: _ ->
3976 if l.pagey != 0
3977 then (
3978 state.mode <- Birdseye (
3979 oconf, leftx, l.pageno, hooverpageno, anchor
3981 gotopage1 l.pageno 0;
3983 else (
3984 let layout = layout state.x (state.y-state.winh)
3985 state.winw
3986 (pgh state.layout) in
3987 match layout with
3988 | [] -> gotoxy state.x (clamp (-state.winh))
3989 | l :: _ ->
3990 state.mode <- Birdseye (
3991 oconf, leftx, l.pageno, hooverpageno, anchor
3993 gotopage1 l.pageno 0
3996 | [] -> gotoxy state.x (clamp (-state.winh))
3997 end;
3999 | Next ->
4000 begin match List.rev state.layout with
4001 | l :: _ ->
4002 let layout = layout state.x
4003 (state.y + (pgh state.layout))
4004 state.winw state.winh in
4005 begin match layout with
4006 | [] ->
4007 let incr = l.pageh - l.pagevh in
4008 if incr = 0
4009 then (
4010 state.mode <-
4011 Birdseye (
4012 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4014 postRedisplay "birdseye pagedown";
4016 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4018 | l :: _ ->
4019 state.mode <-
4020 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4021 gotopage1 l.pageno 0;
4024 | [] -> gotoxy state.x (clamp state.winh)
4025 end;
4027 | Home ->
4028 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4029 gotopage1 0 0
4031 | End ->
4032 let pageno = state.pagecount - 1 in
4033 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4034 if not (pagevisible state.layout pageno)
4035 then
4036 let h =
4037 match List.rev state.pdims with
4038 | [] -> state.winh
4039 | (_, _, h, _) :: _ -> h
4041 gotoxy
4042 state.x
4043 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4044 else postRedisplay "birdseye end";
4046 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4049 let drawpage l =
4050 let color =
4051 match state.mode with
4052 | Textentry _ -> scalecolor 0.4
4053 | LinkNav _ | View -> scalecolor 1.0
4054 | Birdseye (_, _, pageno, hooverpageno, _) ->
4055 if l.pageno = hooverpageno
4056 then scalecolor 0.9
4057 else (
4058 if l.pageno = pageno
4059 then (
4060 let c = scalecolor 1.0 in
4061 GlDraw.color c;
4062 GlDraw.line_width 3.0;
4063 let dispx = l.pagedispx in
4064 linerect
4065 (float (dispx-1)) (float (l.pagedispy-1))
4066 (float (dispx+l.pagevw+1))
4067 (float (l.pagedispy+l.pagevh+1));
4068 GlDraw.line_width 1.0;
4071 else scalecolor 0.8
4074 drawtiles l color;
4077 let postdrawpage l linkindexbase =
4078 match getopaque l.pageno with
4079 | Some opaque ->
4080 if tileready l l.pagex l.pagey
4081 then
4082 let x = l.pagedispx - l.pagex
4083 and y = l.pagedispy - l.pagey in
4084 let hlmask =
4085 match conf.columns with
4086 | Csingle _ | Cmulti _ ->
4087 (if conf.hlinks then 1 else 0)
4088 + (if state.glinks
4089 && not (isbirdseye state.mode) then 2 else 0)
4090 | Csplit _ -> 0
4092 let s =
4093 match state.mode with
4094 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4095 | Textentry _
4096 | Birdseye _
4097 | View
4098 | LinkNav _ -> E.s
4100 Hashtbl.find_all state.prects l.pageno |>
4101 List.iter (fun vals -> Ffi.drawprect opaque x y vals);
4102 let n =
4103 Ffi.postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4104 if n < 0
4105 then (Glutils.redisplay := true; 0)
4106 else n
4107 else 0
4108 | _ -> 0
4111 let scrollindicator () =
4112 let sbw, ph, sh = state.uioh#scrollph in
4113 let sbh, pw, sw = state.uioh#scrollpw in
4115 let x0,x1,hx0 =
4116 if conf.leftscroll
4117 then (0, sbw, sbw)
4118 else ((state.winw - sbw), state.winw, 0)
4121 Gl.enable `blend;
4122 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4123 let (r, g, b, alpha) = conf.sbarcolor in
4124 GlDraw.color (r, g, b) ~alpha;
4125 filledrect (float x0) 0. (float x1) (float state.winh);
4126 filledrect
4127 (float hx0) (float (state.winh - sbh))
4128 (float (hx0 + state.winw)) (float state.winh);
4129 let (r, g, b, alpha) = conf.sbarhndlcolor in
4130 GlDraw.color (r, g, b) ~alpha;
4132 filledrect (float x0) ph (float x1) (ph +. sh);
4133 let pw = pw +. float hx0 in
4134 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4135 Gl.disable `blend;
4138 let showsel () =
4139 match state.mstate with
4140 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
4141 | Msel ((x0, y0), (x1, y1)) ->
4142 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4143 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4144 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4145 if n0 != -1 && n0 = n1 then Ffi.seltext o0 (px0, py0, px1, py1);
4148 let showrects = function
4149 | [] -> ()
4150 | rects ->
4151 Gl.enable `blend;
4152 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4153 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4154 List.iter
4155 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4156 List.iter (fun l ->
4157 if l.pageno = pageno
4158 then
4159 let dx = float (l.pagedispx - l.pagex) in
4160 let dy = float (l.pagedispy - l.pagey) in
4161 let r, g, b, alpha = c in
4162 GlDraw.color (r, g, b) ~alpha;
4163 filledrect2
4164 (x0+.dx) (y0+.dy)
4165 (x1+.dx) (y1+.dy)
4166 (x3+.dx) (y3+.dy)
4167 (x2+.dx) (y2+.dy);
4168 ) state.layout
4169 ) rects;
4170 Gl.disable `blend;
4173 let display () =
4174 GlDraw.color (scalecolor2 conf.bgcolor);
4175 GlClear.color (scalecolor2 conf.bgcolor);
4176 GlClear.clear [`color];
4177 List.iter drawpage state.layout;
4178 let rects =
4179 match state.mode with
4180 | LinkNav (Ltexact (pageno, linkno)) ->
4181 begin match getopaque pageno with
4182 | Some opaque ->
4183 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
4184 let color =
4185 if conf.invert
4186 then (1.0, 1.0, 1.0, 0.5)
4187 else (0.0, 0.0, 0.5, 0.5)
4189 (pageno, color,
4190 (float x0, float y0,
4191 float x1, float y0,
4192 float x1, float y1,
4193 float x0, float y1)
4194 ) :: state.rects
4195 | None -> state.rects
4197 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4198 | Birdseye _
4199 | Textentry _
4200 | View -> state.rects
4202 showrects rects;
4203 let rec postloop linkindexbase = function
4204 | l :: rest ->
4205 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4206 postloop linkindexbase rest
4207 | [] -> ()
4209 showsel ();
4210 postloop 0 state.layout;
4211 state.uioh#display;
4212 begin match state.mstate with
4213 | Mzoomrect ((x0, y0), (x1, y1)) ->
4214 Gl.enable `blend;
4215 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4216 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4217 filledrect (float x0) (float y0) (float x1) (float y1);
4218 Gl.disable `blend;
4219 | Msel _
4220 | Mpan _
4221 | Mscrolly | Mscrollx
4222 | Mzoom _
4223 | Mnone -> ()
4224 end;
4225 enttext ();
4226 scrollindicator ();
4227 Wsi.swapb ();
4230 let display () =
4231 match state.reload with
4232 | Some (x, y, t) ->
4233 if x != state.x || y != state.y || abs_float @@ now () -. t > 0.5
4234 || (state.layout != [] && layoutready state.layout)
4235 then (
4236 state.reload <- None;
4237 display ()
4239 | None -> display ()
4242 let zoomrect x y x1 y1 =
4243 let x0 = min x x1
4244 and x1 = max x x1
4245 and y0 = min y y1 in
4246 let zoom = (float state.w) /. float (x1 - x0) in
4247 let margin =
4248 let simple () =
4249 if state.w < state.winw
4250 then (state.winw - state.w) / 2
4251 else 0
4253 match conf.fitmodel with
4254 | FitWidth | FitProportional -> simple ()
4255 | FitPage ->
4256 match conf.columns with
4257 | Csplit _ ->
4258 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4259 | Cmulti _ | Csingle _ -> simple ()
4261 gotoxy ((state.x + margin) - x0) (state.y + y0);
4262 state.anchor <- getanchor ();
4263 setzoom zoom;
4264 resetmstate ();
4267 let annot inline x y =
4268 match unproject x y with
4269 | Some (opaque, n, ux, uy) ->
4270 let add text =
4271 Ffi.addannot opaque ux uy text;
4272 wcmd "freepage %s" (~> opaque);
4273 Hashtbl.remove state.pagemap (n, state.gen);
4274 flushtiles ();
4275 gotoxy state.x state.y
4277 if inline
4278 then
4279 let ondone s = add s in
4280 let mode = state.mode in
4281 state.mode <- Textentry (
4282 ("annotation: ", E.s, None, textentry, ondone, true),
4283 fun _ -> state.mode <- mode);
4284 state.text <- E.s;
4285 enttext ();
4286 postRedisplay "annot"
4287 else add @@ getusertext E.s
4288 | _ -> ()
4291 let zoomblock x y =
4292 let g opaque l px py =
4293 match Ffi.rectofblock opaque px py with
4294 | Some a ->
4295 let x0 = a.(0) -. 20. in
4296 let x1 = a.(1) +. 20. in
4297 let y0 = a.(2) -. 20. in
4298 let zoom = (float state.w) /. (x1 -. x0) in
4299 let pagey = getpagey l.pageno in
4300 let margin = (state.w - l.pagew)/2 in
4301 let nx = -truncate x0 - margin in
4302 gotoxy nx (pagey + truncate y0);
4303 state.anchor <- getanchor ();
4304 setzoom zoom;
4305 None
4306 | None -> None
4308 match conf.columns with
4309 | Csplit _ ->
4310 impmsg "block zooming does not work properly in split columns mode"
4311 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4314 let scrollx x =
4315 let winw = state.winw - 1 in
4316 let s = float x /. float winw in
4317 let destx = truncate (float (state.w + winw) *. s) in
4318 gotoxy (winw - destx) state.y;
4319 state.mstate <- Mscrollx;
4322 let scrolly y =
4323 let s = float y /. float state.winh in
4324 let desty = truncate (s *. float (maxy ())) in
4325 gotoxy state.x desty;
4326 state.mstate <- Mscrolly;
4329 let viewmulticlick clicks x y mask =
4330 let g opaque l px py =
4331 let mark =
4332 match clicks with
4333 | 2 -> Mark_word
4334 | 3 -> Mark_line
4335 | 4 -> Mark_block
4336 | _ -> Mark_page
4338 if Ffi.markunder opaque px py mark
4339 then (
4340 Some (fun () ->
4341 let dopipe cmd =
4342 match getopaque l.pageno with
4343 | None -> ()
4344 | Some opaque -> pipesel opaque cmd
4346 state.roam <- (fun () -> dopipe conf.paxcmd);
4347 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4350 else None
4352 postRedisplay "viewmulticlick";
4353 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4356 let canselect () =
4357 match conf.columns with
4358 | Csplit _ -> false
4359 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4362 let viewmouse button down x y mask =
4363 match button with
4364 | n when (n == 4 || n == 5) && not down ->
4365 if Wsi.withctrl mask
4366 then (
4367 let incr =
4368 if n = 5
4369 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4370 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4372 let fx, fy =
4373 match state.mstate with
4374 | Mzoom (oldn, _, pos) when n = oldn -> pos
4375 | Mzoomrect _ | Mnone | Mpan _
4376 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4378 let zoom = conf.zoom -. incr in
4379 state.mstate <- Mzoom (n, 0, (x, y));
4380 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4381 then pivotzoom ~x ~y zoom
4382 else pivotzoom zoom
4384 else (
4385 match state.autoscroll with
4386 | Some step -> setautoscrollspeed step (n=4)
4387 | None ->
4388 if conf.wheelbypage || conf.presentation
4389 then (
4390 if n = 4
4391 then prevpage ()
4392 else nextpage ()
4394 else
4395 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4396 let incr = incr * 2 in
4397 let y = clamp incr in
4398 gotoxy state.x y
4401 | n when (n = 6 || n = 7) && not down && canpan () ->
4402 let x =
4403 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4404 gotoxy x state.y
4406 | 1 when Wsi.withshift mask ->
4407 state.mstate <- Mnone;
4408 if not down
4409 then (
4410 match unproject x y with
4411 | None -> ()
4412 | Some (_, pageno, ux, uy) ->
4413 let cmd = Printf.sprintf
4414 "%s %s %d %d %d"
4415 conf.stcmd state.path pageno ux uy
4417 match spawn cmd [] with
4418 | exception exn ->
4419 impmsg "execution of synctex command(%S) failed: %S"
4420 conf.stcmd @@ exntos exn
4421 | _pid -> ()
4424 | 1 when Wsi.withctrl mask ->
4425 if down
4426 then (
4427 Wsi.setcursor Wsi.CURSOR_FLEUR;
4428 state.mstate <- Mpan (x, y)
4430 else state.mstate <- Mnone
4432 | 3 ->
4433 if down
4434 then (
4435 if Wsi.withshift mask
4436 then (
4437 annot conf.annotinline x y;
4438 postRedisplay "addannot"
4440 else
4441 let p = (x, y) in
4442 Wsi.setcursor Wsi.CURSOR_CYCLE;
4443 state.mstate <- Mzoomrect (p, p)
4445 else (
4446 match state.mstate with
4447 | Mzoomrect ((x0, y0), _) ->
4448 if abs (x-x0) > 10 && abs (y - y0) > 10
4449 then zoomrect x0 y0 x y
4450 else (
4451 resetmstate ();
4452 postRedisplay "kill accidental zoom rect";
4454 | Msel _
4455 | Mpan _
4456 | Mscrolly | Mscrollx
4457 | Mzoom _
4458 | Mnone -> resetmstate ()
4461 | 1 when vscrollhit x ->
4462 if down
4463 then
4464 let _, position, sh = state.uioh#scrollph in
4465 if y > truncate position && y < truncate (position +. sh)
4466 then state.mstate <- Mscrolly
4467 else scrolly y
4468 else state.mstate <- Mnone
4470 | 1 when y > state.winh - hscrollh () ->
4471 if down
4472 then
4473 let _, position, sw = state.uioh#scrollpw in
4474 if x > truncate position && x < truncate (position +. sw)
4475 then state.mstate <- Mscrollx
4476 else scrollx x
4477 else state.mstate <- Mnone
4479 | 1 when state.bzoom -> if not down then zoomblock x y
4481 | 1 ->
4482 let dest = if down then getunder x y else Unone in
4483 begin match dest with
4484 | Ulinkuri _ -> gotounder dest
4485 | Unone when down ->
4486 Wsi.setcursor Wsi.CURSOR_FLEUR;
4487 state.mstate <- Mpan (x, y);
4488 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4489 | Unone | Utext _ ->
4490 if down
4491 then (
4492 if canselect ()
4493 then (
4494 state.mstate <- Msel ((x, y), (x, y));
4495 postRedisplay "mouse select";
4498 else (
4499 match state.mstate with
4500 | Mnone -> ()
4501 | Mzoom _ | Mscrollx | Mscrolly -> state.mstate <- Mnone
4502 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4503 | Mpan _ ->
4504 Wsi.setcursor Wsi.CURSOR_INHERIT;
4505 state.mstate <- Mnone
4506 | Msel ((x0, y0), (x1, y1)) ->
4507 let rec loop = function
4508 | [] -> ()
4509 | l :: rest ->
4510 let inside =
4511 let a0 = l.pagedispy in
4512 let a1 = a0 + l.pagevh in
4513 let b0 = l.pagedispx in
4514 let b1 = b0 + l.pagevw in
4515 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4516 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4518 if inside
4519 then
4520 match getopaque l.pageno with
4521 | Some opaque ->
4522 let dosel cmd () =
4523 pipef ~closew:false "Msel"
4524 (fun w ->
4525 Ffi.copysel w opaque;
4526 postRedisplay "Msel") cmd
4528 dosel conf.selcmd ();
4529 state.roam <- dosel conf.paxcmd;
4530 | None -> ()
4531 else loop rest
4533 loop state.layout;
4534 resetmstate ();
4537 | _ -> ()
4540 let birdseyemouse button down x y mask
4541 (conf, leftx, _, hooverpageno, anchor) =
4542 match button with
4543 | 1 when down ->
4544 let rec loop = function
4545 | [] -> ()
4546 | l :: rest ->
4547 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4548 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4549 then
4550 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4551 else loop rest
4553 loop state.layout
4554 | 3 -> ()
4555 | _ -> viewmouse button down x y mask
4558 let uioh = object
4559 method display = ()
4561 method key key mask =
4562 begin match state.mode with
4563 | Textentry textentry -> textentrykeyboard key mask textentry
4564 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4565 | View -> viewkeyboard key mask
4566 | LinkNav linknav -> linknavkeyboard key mask linknav
4567 end;
4568 state.uioh
4570 method button button bstate x y mask =
4571 begin match state.mode with
4572 | LinkNav _ | View -> viewmouse button bstate x y mask
4573 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4574 | Textentry _ -> ()
4575 end;
4576 state.uioh
4578 method multiclick clicks x y mask =
4579 begin match state.mode with
4580 | LinkNav _ | View -> viewmulticlick clicks x y mask
4581 | Birdseye _ | Textentry _ -> ()
4582 end;
4583 state.uioh
4585 method motion x y =
4586 begin match state.mode with
4587 | Textentry _ -> ()
4588 | View | Birdseye _ | LinkNav _ ->
4589 match state.mstate with
4590 | Mzoom _ | Mnone -> ()
4591 | Mpan (x0, y0) ->
4592 let dx = x - x0
4593 and dy = y0 - y in
4594 state.mstate <- Mpan (x, y);
4595 let x = if canpan () then panbound (state.x + dx) else state.x in
4596 let y = clamp dy in
4597 gotoxy x y
4599 | Msel (a, _) ->
4600 state.mstate <- Msel (a, (x, y));
4601 postRedisplay "motion select";
4603 | Mscrolly ->
4604 let y = min state.winh (max 0 y) in
4605 scrolly y
4607 | Mscrollx ->
4608 let x = min state.winw (max 0 x) in
4609 scrollx x
4611 | Mzoomrect (p0, _) ->
4612 state.mstate <- Mzoomrect (p0, (x, y));
4613 postRedisplay "motion zoomrect";
4614 end;
4615 state.uioh
4617 method pmotion x y =
4618 begin match state.mode with
4619 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4620 let rec loop = function
4621 | [] ->
4622 if hooverpageno != -1
4623 then (
4624 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4625 postRedisplay "pmotion birdseye no hoover";
4627 | l :: rest ->
4628 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4629 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4630 then (
4631 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4632 postRedisplay "pmotion birdseye hoover";
4634 else loop rest
4636 loop state.layout
4638 | Textentry _ -> ()
4640 | LinkNav _ | View ->
4641 match state.mstate with
4642 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4643 | Mnone ->
4644 updateunder x y;
4645 if canselect ()
4646 then
4647 match conf.pax with
4648 | None -> ()
4649 | Some past ->
4650 let now = now () in
4651 let delta = now -. past in
4652 if delta > 0.01
4653 then paxunder x y
4654 else conf.pax <- Some now
4655 end;
4656 state.uioh
4658 method infochanged _ = ()
4660 method scrollph =
4661 let maxy = maxy () in
4662 let p, h =
4663 if maxy = 0
4664 then 0.0, float state.winh
4665 else scrollph state.y maxy
4667 vscrollw (), p, h
4669 method scrollpw =
4670 let fwinw = float (state.winw - vscrollw ()) in
4671 let sw =
4672 let sw = fwinw /. float state.w in
4673 let sw = fwinw *. sw in
4674 max sw (float conf.scrollh)
4676 let position =
4677 let maxx = state.w + state.winw in
4678 let x = state.winw - state.x in
4679 let percent = float x /. float maxx in
4680 (fwinw -. sw) *. percent
4682 hscrollh (), position, sw
4684 method modehash =
4685 let modename =
4686 match state.mode with
4687 | LinkNav _ -> "links"
4688 | Textentry _ -> "textentry"
4689 | Birdseye _ -> "birdseye"
4690 | View -> "view"
4692 findkeyhash conf modename
4694 method eformsgs = true
4695 method alwaysscrolly = false
4696 method scroll dx dy =
4697 let x = if canpan () then panbound (state.x + dx) else state.x in
4698 gotoxy x (clamp (2 * dy));
4699 state.uioh
4700 method zoom z x y =
4701 pivotzoom ~x ~y (conf.zoom *. exp z);
4702 end;;
4704 let addrect pageno r g b a x0 y0 x1 y1 =
4705 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4708 let ract cmds =
4709 let cl = splitatchar cmds ' ' in
4710 let scan s fmt f =
4711 try Scanf.sscanf s fmt f
4712 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4713 cmds @@ exntos exn
4715 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4716 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4717 s pageno r g b a x0 y0 x1 y1;
4718 onpagerect
4719 pageno
4720 (fun w h ->
4721 let _,w1,h1,_ = getpagedim pageno in
4722 let sw = float w1 /. float w
4723 and sh = float h1 /. float h in
4724 let x0s = x0 *. sw
4725 and x1s = x1 *. sw
4726 and y0s = y0 *. sh
4727 and y1s = y1 *. sh in
4728 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4729 let color = (r, g, b, a) in
4730 if conf.verbose then debugrect rect;
4731 state.rects <- (pageno, color, rect) :: state.rects;
4732 postRedisplay s;
4735 match cl with
4736 | "reload", "" -> reload ()
4737 | "goto", args ->
4738 scan args "%u %f %f"
4739 (fun pageno x y ->
4740 let cmd, _ = state.geomcmds in
4741 if emptystr cmd
4742 then gotopagexy pageno x y
4743 else
4744 let f prevf () =
4745 gotopagexy pageno x y;
4746 prevf ()
4748 state.reprf <- f state.reprf
4750 | "goto1", args -> scan args "%u %f" gotopage
4751 | "gotor", args -> scan args "%S" gotoremote
4752 | "rect", args ->
4753 scan args "%u %u %f %f %f %f"
4754 (fun pageno c x0 y0 x1 y1 ->
4755 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4756 rectx "rect" pageno color x0 y0 x1 y1;
4758 | "prect", args ->
4759 scan args "%u %f %f %f %f %f %f %f %f"
4760 (fun pageno r g b alpha x0 y0 x1 y1 ->
4761 addrect pageno r g b alpha x0 y0 x1 y1;
4762 postRedisplay "prect"
4764 | "pgoto", args ->
4765 scan args "%u %f %f"
4766 (fun pageno x y ->
4767 let optopaque =
4768 match getopaque pageno with
4769 | Some opaque -> opaque
4770 | None -> ~< E.s
4772 pgoto optopaque pageno x y;
4773 let rec fixx = function
4774 | [] -> ()
4775 | l :: rest ->
4776 if l.pageno = pageno
4777 then gotoxy (state.x - l.pagedispx) state.y
4778 else fixx rest
4780 let layout =
4781 let mult =
4782 match conf.columns with
4783 | Csingle _ | Csplit _ -> 1
4784 | Cmulti ((n, _, _), _) -> n
4786 layout 0 state.y (state.winw * mult) state.winh
4788 fixx layout
4790 | "activatewin", "" -> Wsi.activatewin ()
4791 | "quit", "" -> raise Quit
4792 | "keys", keys ->
4793 begin try
4794 let l = Config.keys_of_string keys in
4795 List.iter (fun (k, m) -> keyboard k m) l
4796 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4797 cmds @@ exntos exn
4799 | "clearrects", "" ->
4800 Hashtbl.clear state.prects;
4801 postRedisplay "clearrects"
4802 | _ ->
4803 adderrfmt "remote command"
4804 "error processing remote command: %S\n" cmds;
4807 let remote =
4808 let scratch = Bytes.create 80 in
4809 let buf = Buffer.create 80 in
4810 fun fd ->
4811 match tempfailureretry (Unix.read fd scratch 0) 80 with
4812 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4813 | 0 ->
4814 Unix.close fd;
4815 if Buffer.length buf > 0
4816 then (
4817 let s = Buffer.contents buf in
4818 Buffer.clear buf;
4819 ract s;
4821 None
4822 | n ->
4823 let rec eat ppos =
4824 let nlpos =
4825 match Bytes.index_from scratch ppos '\n' with
4826 | pos -> if pos >= n then -1 else pos
4827 | exception Not_found -> -1
4829 if nlpos >= 0
4830 then (
4831 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4832 let s = Buffer.contents buf in
4833 Buffer.clear buf;
4834 ract s;
4835 eat (nlpos+1);
4837 else (
4838 Buffer.add_subbytes buf scratch ppos (n-ppos);
4839 Some fd
4841 in eat 0
4844 let remoteopen path =
4845 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4846 with exn ->
4847 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4848 None
4851 let () =
4852 Utils.vlogf := (fun s -> if conf.verbose then prerr_endline s else ignore s);
4853 let gcconfig = ref false in
4854 let rcmdpath = ref E.s in
4855 let pageno = ref None in
4856 let openlast = ref false in
4857 let doreap = ref false in
4858 let csspath = ref None in
4859 selfexec := Sys.executable_name;
4860 Arg.parse
4861 (Arg.align
4862 [("-p", Arg.String (fun s -> state.password <- s),
4863 "<password> Set password");
4865 ("-f", Arg.String
4866 (fun s ->
4867 Config.fontpath := s;
4868 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4870 "<path> Set path to the user interface font");
4872 ("-c", Arg.String
4873 (fun s ->
4874 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4875 Config.confpath := s),
4876 "<path> Set path to the configuration file");
4878 ("-last", Arg.Set openlast, " Open last document");
4880 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4881 "<page-number> Jump to page");
4883 ("-tcf", Arg.String (fun s -> defconf.trimcachepath <- s),
4884 "<path> Set path to the trim cache file");
4886 ("-dest", Arg.String (fun s -> state.nameddest <- s),
4887 "<named-destination> Set named destination");
4889 ("-remote", Arg.String (fun s -> rcmdpath := s),
4890 "<path> Set path to the source of remote commands");
4892 ("-gc", Arg.Set gcconfig, " Collect config garbage");
4894 ("-v", Arg.Unit (fun () ->
4895 Printf.printf
4896 "%s\nconfiguration file: %s\n"
4897 (Help.version ())
4898 Config.defconfpath;
4899 exit 0), " Print version and exit");
4901 ("-css", Arg.String (fun s -> csspath := Some s),
4902 "<path> Set path to the style sheet to use with EPUB/HTML");
4904 ("-origin", Arg.String (fun s -> state.origin <- s),
4905 "<origin> <undocumented>");
4907 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
4908 ("-layout-height", Arg.Set_int layouth,
4909 "<height> layout height html/epub/etc (-1, 0, N)");
4912 (fun s -> state.path <- s)
4913 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4915 let histmode = emptystr state.path && not !openlast in
4917 if not (Config.load !openlast)
4918 then dolog "failed to load configuration";
4920 begin match !pageno with
4921 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
4922 | None -> ()
4923 end;
4925 fillhelp ();
4926 if !gcconfig
4927 then (
4928 Config.gc ();
4929 exit 0
4932 let mu =
4933 object (self)
4934 val mutable m_clicks = 0
4935 val mutable m_click_x = 0
4936 val mutable m_click_y = 0
4937 val mutable m_lastclicktime = infinity
4939 method private cleanup =
4940 state.roam <- noroam;
4941 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) state.pagemap
4942 method expose = postRedisplay "expose"
4943 method visible v =
4944 let name =
4945 match v with
4946 | Wsi.Unobscured -> "unobscured"
4947 | Wsi.PartiallyObscured -> "partiallyobscured"
4948 | Wsi.FullyObscured -> "fullyobscured"
4950 vlog "visibility change %s" name
4951 method display = display ()
4952 method map mapped = vlog "mapped %b" mapped
4953 method reshape w h =
4954 self#cleanup;
4955 reshape w h
4956 method mouse b d x y m =
4957 if d && canselect ()
4958 then (
4960 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
4962 m_click_x <- x;
4963 m_click_y <- y;
4964 if b = 1
4965 then (
4966 let t = now () in
4967 if abs x - m_click_x > 10
4968 || abs y - m_click_y > 10
4969 || abs_float (t -. m_lastclicktime) > 0.3
4970 then m_clicks <- 0;
4971 m_clicks <- m_clicks + 1;
4972 m_lastclicktime <- t;
4973 if m_clicks = 1
4974 then (
4975 self#cleanup;
4976 postRedisplay "cleanup";
4977 state.uioh <- state.uioh#button b d x y m;
4979 else state.uioh <- state.uioh#multiclick m_clicks x y m
4981 else (
4982 self#cleanup;
4983 m_clicks <- 0;
4984 m_lastclicktime <- infinity;
4985 state.uioh <- state.uioh#button b d x y m
4988 else state.uioh <- state.uioh#button b d x y m
4989 method motion x y =
4990 state.mpos <- (x, y);
4991 state.uioh <- state.uioh#motion x y
4992 method pmotion x y =
4993 state.mpos <- (x, y);
4994 state.uioh <- state.uioh#pmotion x y
4995 method key k m =
4996 vlog "k=%#x m=%#x" k m;
4997 let mascm = m land (
4998 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
4999 ) in
5000 let keyboard k m =
5001 let x = state.x and y = state.y in
5002 keyboard k m;
5003 if x != state.x || y != state.y then self#cleanup
5005 match state.keystate with
5006 | KSnone ->
5007 let km = k, mascm in
5008 begin
5009 match
5010 let modehash = state.uioh#modehash in
5011 try Hashtbl.find modehash km
5012 with Not_found ->
5013 try Hashtbl.find (findkeyhash conf "global") km
5014 with Not_found -> KMinsrt (k, m)
5015 with
5016 | KMinsrt (k, m) -> keyboard k m
5017 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5018 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5020 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5021 List.iter (fun (k, m) -> keyboard k m) insrt;
5022 state.keystate <- KSnone
5023 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5024 state.keystate <- KSinto (keys, insrt)
5025 | KSinto _ -> state.keystate <- KSnone
5027 method enter x y =
5028 state.mpos <- (x, y);
5029 state.uioh <- state.uioh#pmotion x y
5030 method leave = state.mpos <- (-1, -1)
5031 method winstate wsl = state.winstate <- wsl
5032 method quit : 'a. 'a = raise Quit
5033 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5034 method zoom z x y = state.uioh#zoom z x y
5035 method opendoc path =
5036 state.mode <- View;
5037 state.uioh <- uioh;
5038 postRedisplay "opendoc";
5039 opendoc path state.password
5042 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5043 state.wsfd <- wsfd;
5045 if not @@ List.exists GlMisc.check_extension
5046 [ "GL_ARB_texture_rectangle"
5047 ; "GL_EXT_texture_recangle"
5048 ; "GL_NV_texture_rectangle" ]
5049 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5051 let cs, ss =
5052 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5053 | exception exn ->
5054 dolog "socketpair failed: %s" @@ exntos exn;
5055 exit 1
5056 | (r, w) ->
5057 cloexec r;
5058 cloexec w;
5059 r, w
5062 setcheckers conf.checkers;
5063 begin match !csspath with
5064 | None -> ()
5065 | Some "" -> conf.css <- E.s
5066 | Some path ->
5067 let css = filecontents path in
5068 let l = String.length css in
5069 conf.css <-
5070 if substratis css (l-2) "\r\n"
5071 then String.sub css 0 (l-2)
5072 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
5073 end;
5074 Ffi.init cs (
5075 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5076 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5077 !Config.fontpath, conf.trimcachepath
5079 List.iter GlArray.enable [`texture_coord; `vertex];
5080 GlTex.env (`color conf.texturecolor);
5081 state.ss <- ss;
5082 reshape ~firsttime:true winw winh;
5083 state.uioh <- uioh;
5084 if histmode
5085 then (
5086 Wsi.settitle "llpp (history)";
5087 enterhistmode ();
5089 else (
5090 state.text <- "Opening " ^ (mbtoutf8 state.path);
5091 opendoc state.path state.password;
5093 display ();
5094 Wsi.mapwin ();
5095 Wsi.setcursor Wsi.CURSOR_INHERIT;
5096 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5098 let rec reap () =
5099 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5100 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5101 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5102 | 0, _ -> ()
5103 | _pid, _status -> reap ()
5105 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5107 let optrfd =
5108 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
5111 let rec loop deadline =
5112 if !doreap
5113 then (
5114 doreap := false;
5115 reap ()
5117 let r = [state.ss; state.wsfd] in
5118 let r =
5119 match !optrfd with
5120 | None -> r
5121 | Some fd -> fd :: r
5123 if !redisplay
5124 then (
5125 Glutils.redisplay := false;
5126 display ();
5128 let timeout =
5129 let now = now () in
5130 if deadline > now
5131 then (
5132 if deadline = infinity
5133 then ~-.1.0
5134 else max 0.0 (deadline -. now)
5136 else 0.0
5138 let r, _, _ =
5139 try Unix.select r [] [] timeout
5140 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5142 begin match r with
5143 | [] ->
5144 let newdeadline =
5145 match state.autoscroll with
5146 | Some step when step != 0 ->
5147 if state.slideshow land 1 = 1
5148 then (
5149 if state.slideshow land 2 = 0
5150 then state.slideshow <- state.slideshow lor 2
5151 else if step < 0 then prevpage () else nextpage ();
5152 deadline +. (float (abs step))
5154 else
5155 let y = state.y + step in
5156 let fy = if conf.maxhfit then state.winh else 0 in
5157 let y =
5158 if y < 0
5159 then state.maxy - fy
5160 else if y >= state.maxy - fy then 0 else y
5162 gotoxy state.x y;
5163 deadline +. 0.01
5164 | _ -> infinity
5166 loop newdeadline
5168 | l ->
5169 let rec checkfds = function
5170 | [] -> ()
5171 | fd :: rest when fd = state.ss ->
5172 let cmd = Ffi.rcmd state.ss in
5173 act cmd;
5174 checkfds rest
5176 | fd :: rest when fd = state.wsfd ->
5177 Wsi.readresp fd;
5178 checkfds rest
5180 | fd :: rest when Some fd = !optrfd ->
5181 begin match remote fd with
5182 | None -> optrfd := remoteopen !rcmdpath;
5183 | opt -> optrfd := opt
5184 end;
5185 checkfds rest
5187 | _ :: rest ->
5188 dolog "select returned unknown descriptor";
5189 checkfds rest
5191 checkfds l;
5192 let newdeadline =
5193 let deadline1 =
5194 if deadline = infinity
5195 then now () +. 0.01
5196 else deadline
5198 match state.autoscroll with
5199 | Some step when step != 0 -> deadline1
5200 | _ -> infinity
5202 loop newdeadline
5203 end;
5205 match loop infinity with
5206 | exception Quit ->
5207 Config.save leavebirdseye;
5208 if Ffi.hasunsavedchanges ()
5209 then save ()
5210 | _ -> error "umpossible - infinity reached"