More braces
[llpp.git] / main.ml
blobd34c65c613329383f3422f71758a337066af9b50
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 )
3203 let quickbookmark ?title () =
3204 match state.layout with
3205 | [] -> ()
3206 | l :: _ ->
3207 let title =
3208 match title with
3209 | None ->
3210 Unix.(
3211 let tm = localtime (now ()) in
3212 Printf.sprintf
3213 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3214 (l.pageno+1)
3215 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3217 | Some title -> title
3219 state.bookmarks <- (title, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3222 let setautoscrollspeed step goingdown =
3223 let incr = max 1 ((abs step) / 2) in
3224 let incr = if goingdown then incr else -incr in
3225 let astep = boundastep state.winh (step + incr) in
3226 state.autoscroll <- Some astep;
3229 let canpan () =
3230 match conf.columns with
3231 | Csplit _ -> true
3232 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3235 let panbound x = bound x (-state.w) state.winw;;
3237 let existsinrow pageno (columns, coverA, coverB) p =
3238 let last = ((pageno - coverA) mod columns) + columns in
3239 let rec any = function
3240 | [] -> false
3241 | l :: rest ->
3242 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3243 then p l
3244 else (
3245 if not (p l)
3246 then (if l.pageno = last then false else any rest)
3247 else true
3250 any state.layout
3253 let nextpage () =
3254 match state.layout with
3255 | [] ->
3256 let pageno = page_of_y state.y in
3257 gotoxy state.x (getpagey (pageno+1))
3258 | l :: rest ->
3259 match conf.columns with
3260 | Csingle _ ->
3261 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3262 then
3263 let y = clamp (pgscale state.winh) in
3264 gotoxy state.x y
3265 else
3266 let pageno = min (l.pageno+1) (state.pagecount-1) in
3267 gotoxy state.x (getpagey pageno)
3268 | Cmulti ((c, _, _) as cl, _) ->
3269 if conf.presentation
3270 && (existsinrow l.pageno cl
3271 (fun l -> l.pageh > l.pagey + l.pagevh))
3272 then
3273 let y = clamp (pgscale state.winh) in
3274 gotoxy state.x y
3275 else
3276 let pageno = min (l.pageno+c) (state.pagecount-1) in
3277 gotoxy state.x (getpagey pageno)
3278 | Csplit (n, _) ->
3279 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3280 then
3281 let pagey, pageh = getpageyh l.pageno in
3282 let pagey = pagey + pageh * l.pagecol in
3283 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3284 gotoxy state.x (pagey + pageh + ips)
3287 let prevpage () =
3288 match state.layout with
3289 | [] ->
3290 let pageno = page_of_y state.y in
3291 gotoxy state.x (getpagey (pageno-1))
3292 | l :: _ ->
3293 match conf.columns with
3294 | Csingle _ ->
3295 if conf.presentation && l.pagey != 0
3296 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3297 else
3298 let pageno = max 0 (l.pageno-1) in
3299 gotoxy state.x (getpagey pageno)
3300 | Cmulti ((c, _, coverB) as cl, _) ->
3301 if conf.presentation &&
3302 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3303 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3304 else
3305 let decr =
3306 if l.pageno = state.pagecount - coverB
3307 then 1
3308 else c
3310 let pageno = max 0 (l.pageno-decr) in
3311 gotoxy state.x (getpagey pageno)
3312 | Csplit (n, _) ->
3313 let y =
3314 if l.pagecol = 0
3315 then
3316 if l.pageno = 0
3317 then l.pagey
3318 else
3319 let pageno = max 0 (l.pageno-1) in
3320 let pagey, pageh = getpageyh pageno in
3321 pagey + (n-1)*pageh
3322 else
3323 let pagey, pageh = getpageyh l.pageno in
3324 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3326 gotoxy state.x y
3329 let save () =
3330 if emptystr conf.savecmd
3331 then adderrmsg "savepath-command is empty"
3332 "don't know where to save modified document"
3333 else
3334 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3335 let path =
3336 getcmdoutput
3337 (fun exn ->
3338 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3339 savecmd
3341 if nonemptystr path
3342 then
3343 let tmp = path ^ ".tmp" in
3344 Ffi.savedoc tmp;
3345 Unix.rename tmp path;
3348 let viewkeyboard key mask =
3349 let enttext te =
3350 let mode = state.mode in
3351 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3352 state.text <- E.s;
3353 enttext ();
3354 postRedisplay "view:enttext"
3356 let ctrl = Wsi.withctrl mask in
3357 let open Keys in
3358 match Wsi.kc2kt key with
3359 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3361 | Ascii 'Q' -> exit 0
3363 | Ascii 'z' ->
3364 let yloc f =
3365 match List.rev state.rects with
3366 | [] -> ()
3367 | (pageno, _, (_, y0, _, y1, _, y2, _, y3)) :: _ ->
3368 f pageno (y0, y1, y2, y3)
3369 and yminmax (y0, y1, y2, y3) =
3370 let ym = min y0 y1 |> min y2 |> min y3 |> truncate in
3371 let yM = max y0 y1 |> max y2 |> max y3 |> truncate in
3372 ym, yM
3374 let ondone msg = state.text <- msg
3375 and zmod _ _ k =
3376 match [@warning "-4"] k with
3377 | Keys.Ascii 'z' ->
3378 let f pageno ys =
3379 let ym, yM = yminmax ys in
3380 let hh = (yM - ym)/2 in
3381 gotopage1 pageno (ym + hh - state.winh/2)
3383 yloc f;
3384 TEdone "center"
3385 | Keys.Ascii 't' ->
3386 let f pageno ys =
3387 let ym, _ = yminmax ys in
3388 gotopage1 pageno ym
3390 yloc f;
3391 TEdone "top"
3392 | Keys.Ascii 'b' ->
3393 let f pageno ys =
3394 let _, yM = yminmax ys in
3395 gotopage1 pageno (yM - state.winh)
3397 yloc f;
3398 TEdone "bottom"
3399 | _ -> TEstop
3401 enttext (": ", E.s, None, zmod state.mode, ondone, true)
3403 | Ascii 'W' ->
3404 if Ffi.hasunsavedchanges ()
3405 then save ()
3407 | Insert ->
3408 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3409 then (
3410 state.mode <- (
3411 match state.lnava with
3412 | None -> LinkNav (Ltgendir 0)
3413 | Some pn -> LinkNav (Ltexact pn)
3415 gotoxy state.x state.y;
3417 else impmsg "keyboard link navigation does not work under rotation"
3419 | Escape | Ascii 'q' ->
3420 begin match state.mstate with
3421 | Mzoomrect _ ->
3422 resetmstate ();
3423 postRedisplay "kill rect";
3424 | Msel _
3425 | Mpan _
3426 | Mscrolly | Mscrollx
3427 | Mzoom _
3428 | Mnone ->
3429 begin match state.mode with
3430 | LinkNav ln ->
3431 begin match ln with
3432 | Ltexact pl -> state.lnava <- Some pl
3433 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3434 end;
3435 state.mode <- View;
3436 postRedisplay "esc leave linknav"
3437 | Birdseye _ | Textentry _ | View ->
3438 match state.ranchors with
3439 | [] -> raise Quit
3440 | (path, password, anchor, origin) :: rest ->
3441 state.ranchors <- rest;
3442 state.anchor <- anchor;
3443 state.origin <- origin;
3444 state.nameddest <- E.s;
3445 opendoc path password
3446 end;
3447 end;
3449 | Backspace ->
3450 addnavnorc ();
3451 gotoxy state.x (getnav ~-1)
3453 | Ascii 'o' -> enteroutlinemode ()
3454 | Ascii 'H' -> enterhistmode ()
3456 | Ascii 'u' ->
3457 state.rects <- [];
3458 state.text <- E.s;
3459 Hashtbl.iter (fun _ opaque ->
3460 Ffi.clearmark opaque;
3461 Hashtbl.clear state.prects) state.pagemap;
3462 postRedisplay "dehighlight";
3464 | Ascii (('/' | '?') as c) ->
3465 let ondone isforw s =
3466 cbput state.hists.pat s;
3467 state.searchpattern <- s;
3468 search s isforw
3470 let s = String.make 1 c in
3471 enttext (s, E.s, Some (onhist state.hists.pat),
3472 textentry, ondone (c = '/'), true)
3474 | Ascii '+' | Ascii '=' when ctrl ->
3475 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3476 pivotzoom (conf.zoom +. incr)
3478 | Ascii '+' ->
3479 let ondone s =
3480 let n =
3481 try int_of_string s with exn ->
3482 state.text <-
3483 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3484 max_int
3486 if n != max_int
3487 then (
3488 conf.pagebias <- n;
3489 state.text <- "page bias is now " ^ string_of_int n;
3492 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3494 | Ascii '-' when ctrl ->
3495 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3496 pivotzoom (max 0.01 (conf.zoom -. decr))
3498 | Ascii '-' ->
3499 let ondone msg = state.text <- msg in
3500 enttext ("option: ", E.s, None,
3501 optentry state.mode, ondone, true)
3503 | Ascii '0' when ctrl ->
3504 if conf.zoom = 1.0
3505 then gotoxy 0 state.y
3506 else setzoom 1.0
3508 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3509 let cols =
3510 match conf.columns with
3511 | Csingle _ | Cmulti _ -> 1
3512 | Csplit (n, _) -> n
3514 let h = state.winh -
3515 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3517 let zoom = Ffi.zoomforh state.winw h 0 cols in
3518 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3519 then setzoom zoom
3521 | Ascii '3' when ctrl ->
3522 let fm =
3523 match conf.fitmodel with
3524 | FitWidth -> FitProportional
3525 | FitProportional -> FitPage
3526 | FitPage -> FitWidth
3528 state.text <- "fit model: " ^ FMTE.to_string fm;
3529 reqlayout conf.angle fm
3531 | Ascii '4' when ctrl ->
3532 let zoom = Ffi.getmaxw () /. float state.winw in
3533 if zoom > 0.0 then setzoom zoom
3535 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3537 | Ascii ('0'..'9' as c) when not ctrl ->
3538 let ondone s =
3539 let n =
3540 try int_of_string s with exn ->
3541 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3544 if n >= 0
3545 then (
3546 addnav ();
3547 cbput state.hists.pag (string_of_int n);
3548 gotopage1 (n + conf.pagebias - 1) 0;
3551 let pageentry text = function [@warning "-4"]
3552 | Keys.Ascii 'g' -> TEdone text
3553 | key -> intentry text key
3555 let text = String.make 1 c in
3556 enttext (":", text, Some (onhist state.hists.pag),
3557 pageentry, ondone, true)
3559 | Ascii 'b' ->
3560 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3561 postRedisplay "toggle scrollbar";
3563 | Ascii 'B' ->
3564 state.bzoom <- not state.bzoom;
3565 state.rects <- [];
3566 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3568 | Ascii 'l' ->
3569 conf.hlinks <- not conf.hlinks;
3570 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3571 postRedisplay "toggle highlightlinks";
3573 | Ascii 'F' ->
3574 if conf.angle mod 360 = 0
3575 then (
3576 state.glinks <- true;
3577 let mode = state.mode in
3578 state.mode <-
3579 Textentry (
3580 ("goto: ", E.s, None, linknentry, linknact gotounder, false),
3581 (fun _ ->
3582 state.glinks <- false;
3583 state.mode <- mode)
3585 state.text <- E.s;
3586 postRedisplay "view:linkent(F)"
3588 else impmsg "hint mode does not work under rotation"
3590 | Ascii 'y' ->
3591 state.glinks <- true;
3592 let mode = state.mode in
3593 state.mode <-
3594 Textentry (
3595 ("copy: ", E.s, None, linknentry,
3596 linknact (fun under ->
3597 selstring conf.selcmd (undertext under)), false),
3598 (fun _ ->
3599 state.glinks <- false;
3600 state.mode <- mode)
3602 state.text <- E.s;
3603 postRedisplay "view:linkent"
3605 | Ascii 'a' ->
3606 begin match state.autoscroll with
3607 | Some step ->
3608 conf.autoscrollstep <- step;
3609 state.autoscroll <- None
3610 | None ->
3611 state.autoscroll <- Some conf.autoscrollstep;
3612 state.slideshow <- state.slideshow land lnot 2
3615 | Ascii 'p' when ctrl ->
3616 launchpath () (* XXX where do error messages go? *)
3618 | Ascii 'P' ->
3619 setpresentationmode (not conf.presentation);
3620 showtext ' ' ("presentation mode " ^
3621 if conf.presentation then "on" else "off");
3623 | Ascii 'f' ->
3624 if List.mem Wsi.Fullscreen state.winstate
3625 then Wsi.reshape conf.cwinw conf.cwinh
3626 else Wsi.fullscreen ()
3628 | Ascii ('p'|'N') -> search state.searchpattern false
3629 | Ascii 'n' | Fn 3 -> search state.searchpattern true
3631 | Ascii 't' ->
3632 begin match state.layout with
3633 | [] -> ()
3634 | l :: _ -> gotoxy state.x (getpagey l.pageno)
3637 | Ascii ' ' -> nextpage ()
3638 | Delete -> prevpage ()
3639 | Ascii '=' -> showtext ' ' (describe_layout state.layout);
3641 | Ascii 'w' ->
3642 begin match state.layout with
3643 | [] -> ()
3644 | l :: _ ->
3645 Wsi.reshape l.pagew l.pageh;
3646 postRedisplay "w"
3649 | Ascii '\'' -> enterbookmarkmode ()
3650 | Ascii 'h' | Fn 1 -> enterhelpmode ()
3651 | Ascii 'i' -> enterinfomode ()
3652 | Ascii 'e' when Buffer.length state.errmsgs > 0 -> entermsgsmode ()
3654 | Ascii 'm' ->
3655 let ondone s =
3656 match state.layout with
3657 | l :: _ when nonemptystr s ->
3658 state.bookmarks <- (s, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3659 | _ -> ()
3661 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3663 | Ascii '~' ->
3664 quickbookmark ();
3665 showtext ' ' "Quick bookmark added";
3667 | Ascii 'x' -> state.roam ()
3669 | Ascii ('<'|'>' as c) ->
3670 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3672 | Ascii ('['|']' as c) ->
3673 conf.colorscale <-
3674 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3675 postRedisplay "brightness";
3677 | Ascii 'c' when state.mode = View ->
3678 if Wsi.withalt mask
3679 then (
3680 if conf.zoom > 1.0
3681 then
3682 let m = (state.winw - state.w) / 2 in
3683 gotoxy m state.y
3685 else
3686 let (c, a, b), z =
3687 match state.prevcolumns with
3688 | None -> (1, 0, 0), 1.0
3689 | Some (columns, z) ->
3690 let cab =
3691 match columns with
3692 | Csplit (c, _) -> -c, 0, 0
3693 | Cmulti ((c, a, b), _) -> c, a, b
3694 | Csingle _ -> 1, 0, 0
3696 cab, z
3698 setcolumns View c a b;
3699 setzoom z
3701 | Down | Up when ctrl && Wsi.withshift mask ->
3702 let zoom, x = state.prevzoom in
3703 setzoom zoom;
3704 state.x <- x;
3706 | Up ->
3707 begin match state.autoscroll with
3708 | None ->
3709 begin match state.mode with
3710 | Birdseye beye -> upbirdseye 1 beye
3711 | Textentry _ | View | LinkNav _ ->
3712 if ctrl
3713 then gotoxy state.x (clamp ~-(state.winh/2))
3714 else (
3715 if not (Wsi.withshift mask) && conf.presentation
3716 then prevpage ()
3717 else gotoxy state.x (clamp (-conf.scrollstep))
3720 | Some n -> setautoscrollspeed n false
3723 | Down ->
3724 begin match state.autoscroll with
3725 | None ->
3726 begin match state.mode with
3727 | Birdseye beye -> downbirdseye 1 beye
3728 | Textentry _ | View | LinkNav _ ->
3729 if ctrl
3730 then gotoxy state.x (clamp (state.winh/2))
3731 else (
3732 if not (Wsi.withshift mask) && conf.presentation
3733 then nextpage ()
3734 else gotoxy state.x (clamp (conf.scrollstep))
3737 | Some n -> setautoscrollspeed n true
3740 | Left | Right when not (Wsi.withalt mask) ->
3741 if canpan ()
3742 then
3743 let dx =
3744 if ctrl
3745 then state.winw / 2
3746 else conf.hscrollstep
3748 let dx =
3749 let pv = Wsi.kc2kt key in
3750 if pv = Keys.Left then dx else -dx
3752 gotoxy (panbound (state.x + dx)) state.y
3753 else (
3754 state.text <- E.s;
3755 postRedisplay "left/right"
3758 | Prior ->
3759 let y =
3760 if ctrl
3761 then
3762 match state.layout with
3763 | [] -> state.y
3764 | l :: _ -> state.y - l.pagey
3765 else clamp (pgscale (-state.winh))
3767 gotoxy state.x y
3769 | Next ->
3770 let y =
3771 if ctrl
3772 then
3773 match List.rev state.layout with
3774 | [] -> state.y
3775 | l :: _ -> getpagey l.pageno
3776 else clamp (pgscale state.winh)
3778 gotoxy state.x y
3780 | Ascii 'g' | Home ->
3781 addnav ();
3782 gotoxy 0 0
3783 | Ascii 'G' | End ->
3784 addnav ();
3785 gotoxy 0 (clamp state.maxy)
3787 | Right when Wsi.withalt mask ->
3788 addnavnorc ();
3789 gotoxy state.x (getnav 1)
3790 | Left when Wsi.withalt mask ->
3791 addnavnorc ();
3792 gotoxy state.x (getnav ~-1)
3794 | Ascii 'r' ->
3795 reload ()
3797 | Ascii 'v' when conf.debug ->
3798 state.rects <- [];
3799 List.iter (fun l ->
3800 match getopaque l.pageno with
3801 | None -> ()
3802 | Some opaque ->
3803 let x0, y0, x1, y1 = Ffi.pagebbox opaque in
3804 let rect = (float x0, float y0,
3805 float x1, float y0,
3806 float x1, float y1,
3807 float x0, float y1) in
3808 debugrect rect;
3809 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3810 state.rects <- (l.pageno, color, rect) :: state.rects;
3811 ) state.layout;
3812 postRedisplay "v";
3814 | Ascii '|' ->
3815 let mode = state.mode in
3816 let cmd = ref E.s in
3817 let onleave = function
3818 | Cancel -> state.mode <- mode
3819 | Confirm ->
3820 List.iter (fun l ->
3821 match getopaque l.pageno with
3822 | Some opaque -> pipesel opaque !cmd
3823 | None -> ()) state.layout;
3824 state.mode <- mode
3826 let ondone s =
3827 cbput state.hists.sel s;
3828 cmd := s
3830 let te =
3831 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3833 postRedisplay "|";
3834 state.mode <- Textentry (te, onleave);
3836 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3837 vlog "huh? %s" (Wsi.keyname key)
3840 let linknavkeyboard key mask linknav =
3841 let pv = Wsi.kc2kt key in
3842 let getpage pageno =
3843 let rec loop = function
3844 | [] -> None
3845 | l :: _ when l.pageno = pageno -> Some l
3846 | _ :: rest -> loop rest
3847 in loop state.layout
3849 let doexact (pageno, n) =
3850 match getopaque pageno, getpage pageno with
3851 | Some opaque, Some l ->
3852 if pv = Keys.Enter
3853 then
3854 let under = Ffi.getlink opaque n in
3855 postRedisplay "link gotounder";
3856 gotounder under;
3857 state.mode <- View;
3858 else
3859 let opt, dir =
3860 let open Keys in
3861 match pv with
3862 | Home -> Some (Ffi.findlink opaque LDfirst), -1
3863 | End -> Some (Ffi.findlink opaque LDlast), 1
3864 | Left -> Some (Ffi.findlink opaque (LDleft n)), -1
3865 | Right -> Some (Ffi.findlink opaque (LDright n)), 1
3866 | Up -> Some (Ffi.findlink opaque (LDup n)), -1
3867 | Down -> Some (Ffi.findlink opaque (LDdown n)), 1
3868 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3869 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3871 let pwl l dir =
3872 begin match Ffi.findpwl l.pageno dir with
3873 | Pwlnotfound -> ()
3874 | Pwl pageno ->
3875 let notfound dir =
3876 state.mode <- LinkNav (Ltgendir dir);
3877 let y, h = getpageyh pageno in
3878 let y =
3879 if dir < 0
3880 then y + h - state.winh
3881 else y
3883 gotoxy state.x y
3885 begin match getopaque pageno, getpage pageno with
3886 | Some opaque, Some _ ->
3887 let link =
3888 let ld = if dir > 0 then LDfirst else LDlast in
3889 Ffi.findlink opaque ld
3891 begin match link with
3892 | Lfound m ->
3893 showlinktype (Ffi.getlink opaque m);
3894 state.mode <- LinkNav (Ltexact (pageno, m));
3895 postRedisplay "linknav jpage";
3896 | Lnotfound -> notfound dir
3897 end;
3898 | _ -> notfound dir
3899 end;
3900 end;
3902 begin match opt with
3903 | Some Lnotfound -> pwl l dir;
3904 | Some (Lfound m) ->
3905 if m = n
3906 then pwl l dir
3907 else (
3908 let _, y0, _, y1 = Ffi.getlinkrect opaque m in
3909 if y0 < l.pagey
3910 then gotopage1 l.pageno y0
3911 else (
3912 let d = fstate.fontsize + 1 in
3913 if y1 - l.pagey > l.pagevh - d
3914 then gotopage1 l.pageno (y1 - state.winh + d)
3915 else postRedisplay "linknav";
3917 showlinktype (Ffi.getlink opaque m);
3918 state.mode <- LinkNav (Ltexact (l.pageno, m));
3921 | None -> viewkeyboard key mask
3922 end;
3923 | _ -> viewkeyboard key mask
3925 if pv = Keys.Insert
3926 then (
3927 begin match linknav with
3928 | Ltexact pa -> state.lnava <- Some pa
3929 | Ltgendir _ | Ltnotready _ -> ()
3930 end;
3931 state.mode <- View;
3932 postRedisplay "leave linknav"
3934 else
3935 match linknav with
3936 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3937 | Ltexact exact -> doexact exact
3940 let keyboard key mask =
3941 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
3942 then wcmd "interrupt"
3943 else state.uioh <- state.uioh#key key mask
3946 let birdseyekeyboard key mask
3947 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3948 let incr =
3949 match conf.columns with
3950 | Csingle _ -> 1
3951 | Cmulti ((c, _, _), _) -> c
3952 | Csplit _ -> error "bird's eye split mode"
3954 let pgh layout = List.fold_left
3955 (fun m l -> max l.pageh m) state.winh layout in
3956 let open Keys in
3957 match Wsi.kc2kt key with
3958 | Ascii 'l' when Wsi.withctrl mask ->
3959 let y, h = getpageyh pageno in
3960 let top = (state.winh - h) / 2 in
3961 gotoxy state.x (max 0 (y - top))
3962 | Enter -> leavebirdseye beye false
3963 | Escape -> leavebirdseye beye true
3964 | Up -> upbirdseye incr beye
3965 | Down -> downbirdseye incr beye
3966 | Left -> upbirdseye 1 beye
3967 | Right -> downbirdseye 1 beye
3969 | Prior ->
3970 begin match state.layout with
3971 | l :: _ ->
3972 if l.pagey != 0
3973 then (
3974 state.mode <- Birdseye (
3975 oconf, leftx, l.pageno, hooverpageno, anchor
3977 gotopage1 l.pageno 0;
3979 else (
3980 let layout = layout state.x (state.y-state.winh)
3981 state.winw
3982 (pgh state.layout) in
3983 match layout with
3984 | [] -> gotoxy state.x (clamp (-state.winh))
3985 | l :: _ ->
3986 state.mode <- Birdseye (
3987 oconf, leftx, l.pageno, hooverpageno, anchor
3989 gotopage1 l.pageno 0
3992 | [] -> gotoxy state.x (clamp (-state.winh))
3993 end;
3995 | Next ->
3996 begin match List.rev state.layout with
3997 | l :: _ ->
3998 let layout = layout state.x
3999 (state.y + (pgh state.layout))
4000 state.winw state.winh in
4001 begin match layout with
4002 | [] ->
4003 let incr = l.pageh - l.pagevh in
4004 if incr = 0
4005 then (
4006 state.mode <-
4007 Birdseye (
4008 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4010 postRedisplay "birdseye pagedown";
4012 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4014 | l :: _ ->
4015 state.mode <-
4016 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4017 gotopage1 l.pageno 0;
4020 | [] -> gotoxy state.x (clamp state.winh)
4021 end;
4023 | Home ->
4024 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4025 gotopage1 0 0
4027 | End ->
4028 let pageno = state.pagecount - 1 in
4029 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4030 if not (pagevisible state.layout pageno)
4031 then
4032 let h =
4033 match List.rev state.pdims with
4034 | [] -> state.winh
4035 | (_, _, h, _) :: _ -> h
4037 gotoxy
4038 state.x
4039 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4040 else postRedisplay "birdseye end";
4042 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4045 let drawpage l =
4046 let color =
4047 match state.mode with
4048 | Textentry _ -> scalecolor 0.4
4049 | LinkNav _ | View -> scalecolor 1.0
4050 | Birdseye (_, _, pageno, hooverpageno, _) ->
4051 if l.pageno = hooverpageno
4052 then scalecolor 0.9
4053 else (
4054 if l.pageno = pageno
4055 then (
4056 let c = scalecolor 1.0 in
4057 GlDraw.color c;
4058 GlDraw.line_width 3.0;
4059 let dispx = l.pagedispx in
4060 linerect
4061 (float (dispx-1)) (float (l.pagedispy-1))
4062 (float (dispx+l.pagevw+1))
4063 (float (l.pagedispy+l.pagevh+1));
4064 GlDraw.line_width 1.0;
4067 else scalecolor 0.8
4070 drawtiles l color;
4073 let postdrawpage l linkindexbase =
4074 match getopaque l.pageno with
4075 | Some opaque ->
4076 if tileready l l.pagex l.pagey
4077 then
4078 let x = l.pagedispx - l.pagex
4079 and y = l.pagedispy - l.pagey in
4080 let hlmask =
4081 match conf.columns with
4082 | Csingle _ | Cmulti _ ->
4083 (if conf.hlinks then 1 else 0)
4084 + (if state.glinks
4085 && not (isbirdseye state.mode) then 2 else 0)
4086 | Csplit _ -> 0
4088 let s =
4089 match state.mode with
4090 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4091 | Textentry _
4092 | Birdseye _
4093 | View
4094 | LinkNav _ -> E.s
4096 Hashtbl.find_all state.prects l.pageno |>
4097 List.iter (fun vals -> Ffi.drawprect opaque x y vals);
4098 let n =
4099 Ffi.postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4100 if n < 0
4101 then (Glutils.redisplay := true; 0)
4102 else n
4103 else 0
4104 | _ -> 0
4107 let scrollindicator () =
4108 let sbw, ph, sh = state.uioh#scrollph in
4109 let sbh, pw, sw = state.uioh#scrollpw in
4111 let x0,x1,hx0 =
4112 if conf.leftscroll
4113 then (0, sbw, sbw)
4114 else ((state.winw - sbw), state.winw, 0)
4117 Gl.enable `blend;
4118 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4119 let (r, g, b, alpha) = conf.sbarcolor in
4120 GlDraw.color (r, g, b) ~alpha;
4121 filledrect (float x0) 0. (float x1) (float state.winh);
4122 filledrect
4123 (float hx0) (float (state.winh - sbh))
4124 (float (hx0 + state.winw)) (float state.winh);
4125 let (r, g, b, alpha) = conf.sbarhndlcolor in
4126 GlDraw.color (r, g, b) ~alpha;
4128 filledrect (float x0) ph (float x1) (ph +. sh);
4129 let pw = pw +. float hx0 in
4130 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4131 Gl.disable `blend;
4134 let showsel () =
4135 match state.mstate with
4136 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
4137 | Msel ((x0, y0), (x1, y1)) ->
4138 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4139 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4140 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4141 if n0 != -1 && n0 = n1 then Ffi.seltext o0 (px0, py0, px1, py1);
4144 let showrects = function
4145 | [] -> ()
4146 | rects ->
4147 Gl.enable `blend;
4148 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4149 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4150 List.iter
4151 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4152 List.iter (fun l ->
4153 if l.pageno = pageno
4154 then
4155 let dx = float (l.pagedispx - l.pagex) in
4156 let dy = float (l.pagedispy - l.pagey) in
4157 let r, g, b, alpha = c in
4158 GlDraw.color (r, g, b) ~alpha;
4159 filledrect2
4160 (x0+.dx) (y0+.dy)
4161 (x1+.dx) (y1+.dy)
4162 (x3+.dx) (y3+.dy)
4163 (x2+.dx) (y2+.dy);
4164 ) state.layout
4165 ) rects;
4166 Gl.disable `blend;
4169 let display () =
4170 GlDraw.color (scalecolor2 conf.bgcolor);
4171 GlClear.color (scalecolor2 conf.bgcolor);
4172 GlClear.clear [`color];
4173 List.iter drawpage state.layout;
4174 let rects =
4175 match state.mode with
4176 | LinkNav (Ltexact (pageno, linkno)) ->
4177 begin match getopaque pageno with
4178 | Some opaque ->
4179 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
4180 let color = (0.0, 0.0, 0.5, 0.5) in
4181 (pageno, color,
4182 (float x0, float y0,
4183 float x1, float y0,
4184 float x1, float y1,
4185 float x0, float y1)
4186 ) :: state.rects
4187 | None -> state.rects
4189 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4190 | Birdseye _
4191 | Textentry _
4192 | View -> state.rects
4194 showrects rects;
4195 let rec postloop linkindexbase = function
4196 | l :: rest ->
4197 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4198 postloop linkindexbase rest
4199 | [] -> ()
4201 showsel ();
4202 postloop 0 state.layout;
4203 state.uioh#display;
4204 begin match state.mstate with
4205 | Mzoomrect ((x0, y0), (x1, y1)) ->
4206 Gl.enable `blend;
4207 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4208 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4209 filledrect (float x0) (float y0) (float x1) (float y1);
4210 Gl.disable `blend;
4211 | Msel _
4212 | Mpan _
4213 | Mscrolly | Mscrollx
4214 | Mzoom _
4215 | Mnone -> ()
4216 end;
4217 enttext ();
4218 scrollindicator ();
4219 Wsi.swapb ();
4222 let display () =
4223 match state.reload with
4224 | Some (x, y, t) ->
4225 if x != state.x || y != state.y || abs_float @@ now () -. t > 0.5
4226 || (state.layout != [] && layoutready state.layout)
4227 then (
4228 state.reload <- None;
4229 display ()
4231 | None -> display ()
4234 let zoomrect x y x1 y1 =
4235 let x0 = min x x1
4236 and x1 = max x x1
4237 and y0 = min y y1 in
4238 let zoom = (float state.w) /. float (x1 - x0) in
4239 let margin =
4240 let simple () =
4241 if state.w < state.winw
4242 then (state.winw - state.w) / 2
4243 else 0
4245 match conf.fitmodel with
4246 | FitWidth | FitProportional -> simple ()
4247 | FitPage ->
4248 match conf.columns with
4249 | Csplit _ ->
4250 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4251 | Cmulti _ | Csingle _ -> simple ()
4253 gotoxy ((state.x + margin) - x0) (state.y + y0);
4254 state.anchor <- getanchor ();
4255 setzoom zoom;
4256 resetmstate ();
4259 let annot inline x y =
4260 match unproject x y with
4261 | Some (opaque, n, ux, uy) ->
4262 let add text =
4263 Ffi.addannot opaque ux uy text;
4264 wcmd "freepage %s" (~> opaque);
4265 Hashtbl.remove state.pagemap (n, state.gen);
4266 flushtiles ();
4267 gotoxy state.x state.y
4269 if inline
4270 then
4271 let ondone s = add s in
4272 let mode = state.mode in
4273 state.mode <- Textentry (
4274 ("annotation: ", E.s, None, textentry, ondone, true),
4275 fun _ -> state.mode <- mode);
4276 state.text <- E.s;
4277 enttext ();
4278 postRedisplay "annot"
4279 else add @@ getusertext E.s
4280 | _ -> ()
4283 let zoomblock x y =
4284 let g opaque l px py =
4285 match Ffi.rectofblock opaque px py with
4286 | Some a ->
4287 let x0 = a.(0) -. 20. in
4288 let x1 = a.(1) +. 20. in
4289 let y0 = a.(2) -. 20. in
4290 let zoom = (float state.w) /. (x1 -. x0) in
4291 let pagey = getpagey l.pageno in
4292 let margin = (state.w - l.pagew)/2 in
4293 let nx = -truncate x0 - margin in
4294 gotoxy nx (pagey + truncate y0);
4295 state.anchor <- getanchor ();
4296 setzoom zoom;
4297 None
4298 | None -> None
4300 match conf.columns with
4301 | Csplit _ ->
4302 impmsg "block zooming does not work properly in split columns mode"
4303 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4306 let scrollx x =
4307 let winw = state.winw - 1 in
4308 let s = float x /. float winw in
4309 let destx = truncate (float (state.w + winw) *. s) in
4310 gotoxy (winw - destx) state.y;
4311 state.mstate <- Mscrollx;
4314 let scrolly y =
4315 let s = float y /. float state.winh in
4316 let desty = truncate (s *. float (maxy ())) in
4317 gotoxy state.x desty;
4318 state.mstate <- Mscrolly;
4321 let viewmulticlick clicks x y mask =
4322 let g opaque l px py =
4323 let mark =
4324 match clicks with
4325 | 2 -> Mark_word
4326 | 3 -> Mark_line
4327 | 4 -> Mark_block
4328 | _ -> Mark_page
4330 if Ffi.markunder opaque px py mark
4331 then (
4332 Some (fun () ->
4333 let dopipe cmd =
4334 match getopaque l.pageno with
4335 | None -> ()
4336 | Some opaque -> pipesel opaque cmd
4338 state.roam <- (fun () -> dopipe conf.paxcmd);
4339 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4342 else None
4344 postRedisplay "viewmulticlick";
4345 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4348 let canselect () =
4349 match conf.columns with
4350 | Csplit _ -> false
4351 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4354 let viewmouse button down x y mask =
4355 match button with
4356 | n when (n == 4 || n == 5) && not down ->
4357 if Wsi.withctrl mask
4358 then (
4359 let incr =
4360 if n = 5
4361 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4362 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4364 let fx, fy =
4365 match state.mstate with
4366 | Mzoom (oldn, _, pos) when n = oldn -> pos
4367 | Mzoomrect _ | Mnone | Mpan _
4368 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4370 let zoom = conf.zoom -. incr in
4371 state.mstate <- Mzoom (n, 0, (x, y));
4372 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4373 then pivotzoom ~x ~y zoom
4374 else pivotzoom zoom
4376 else (
4377 match state.autoscroll with
4378 | Some step -> setautoscrollspeed step (n=4)
4379 | None ->
4380 if conf.wheelbypage || conf.presentation
4381 then (
4382 if n = 4
4383 then prevpage ()
4384 else nextpage ()
4386 else
4387 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4388 let incr = incr * 2 in
4389 let y = clamp incr in
4390 gotoxy state.x y
4393 | n when (n = 6 || n = 7) && not down && canpan () ->
4394 let x =
4395 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4396 gotoxy x state.y
4398 | 1 when Wsi.withshift mask ->
4399 state.mstate <- Mnone;
4400 if not down
4401 then (
4402 match unproject x y with
4403 | None -> ()
4404 | Some (_, pageno, ux, uy) ->
4405 let cmd = Printf.sprintf
4406 "%s %s %d %d %d"
4407 conf.stcmd state.path pageno ux uy
4409 match spawn cmd [] with
4410 | exception exn ->
4411 impmsg "execution of synctex command(%S) failed: %S"
4412 conf.stcmd @@ exntos exn
4413 | _pid -> ()
4416 | 1 when Wsi.withctrl mask ->
4417 if down
4418 then (
4419 Wsi.setcursor Wsi.CURSOR_FLEUR;
4420 state.mstate <- Mpan (x, y)
4422 else state.mstate <- Mnone
4424 | 3 ->
4425 if down
4426 then (
4427 if Wsi.withshift mask
4428 then (
4429 annot conf.annotinline x y;
4430 postRedisplay "addannot"
4432 else
4433 let p = (x, y) in
4434 Wsi.setcursor Wsi.CURSOR_CYCLE;
4435 state.mstate <- Mzoomrect (p, p)
4437 else (
4438 match state.mstate with
4439 | Mzoomrect ((x0, y0), _) ->
4440 if abs (x-x0) > 10 && abs (y - y0) > 10
4441 then zoomrect x0 y0 x y
4442 else (
4443 resetmstate ();
4444 postRedisplay "kill accidental zoom rect";
4446 | Msel _
4447 | Mpan _
4448 | Mscrolly | Mscrollx
4449 | Mzoom _
4450 | Mnone -> resetmstate ()
4453 | 1 when vscrollhit x ->
4454 if down
4455 then
4456 let _, position, sh = state.uioh#scrollph in
4457 if y > truncate position && y < truncate (position +. sh)
4458 then state.mstate <- Mscrolly
4459 else scrolly y
4460 else state.mstate <- Mnone
4462 | 1 when y > state.winh - hscrollh () ->
4463 if down
4464 then
4465 let _, position, sw = state.uioh#scrollpw in
4466 if x > truncate position && x < truncate (position +. sw)
4467 then state.mstate <- Mscrollx
4468 else scrollx x
4469 else state.mstate <- Mnone
4471 | 1 when state.bzoom -> if not down then zoomblock x y
4473 | 1 ->
4474 let dest = if down then getunder x y else Unone in
4475 begin match dest with
4476 | Ulinkuri _ -> gotounder dest
4477 | Unone when down ->
4478 Wsi.setcursor Wsi.CURSOR_FLEUR;
4479 state.mstate <- Mpan (x, y);
4480 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4481 | Unone | Utext _ ->
4482 if down
4483 then (
4484 if canselect ()
4485 then (
4486 state.mstate <- Msel ((x, y), (x, y));
4487 postRedisplay "mouse select";
4490 else (
4491 match state.mstate with
4492 | Mnone -> ()
4493 | Mzoom _ | Mscrollx | Mscrolly -> state.mstate <- Mnone
4494 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4495 | Mpan _ ->
4496 Wsi.setcursor Wsi.CURSOR_INHERIT;
4497 state.mstate <- Mnone
4498 | Msel ((x0, y0), (x1, y1)) ->
4499 let rec loop = function
4500 | [] -> ()
4501 | l :: rest ->
4502 let inside =
4503 let a0 = l.pagedispy in
4504 let a1 = a0 + l.pagevh in
4505 let b0 = l.pagedispx in
4506 let b1 = b0 + l.pagevw in
4507 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4508 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4510 if inside
4511 then
4512 match getopaque l.pageno with
4513 | Some opaque ->
4514 let dosel cmd () =
4515 pipef ~closew:false "Msel"
4516 (fun w ->
4517 Ffi.copysel w opaque;
4518 postRedisplay "Msel") cmd
4520 dosel conf.selcmd ();
4521 state.roam <- dosel conf.paxcmd;
4522 | None -> ()
4523 else loop rest
4525 loop state.layout;
4526 resetmstate ();
4529 | _ -> ()
4532 let birdseyemouse button down x y mask
4533 (conf, leftx, _, hooverpageno, anchor) =
4534 match button with
4535 | 1 when down ->
4536 let rec loop = function
4537 | [] -> ()
4538 | l :: rest ->
4539 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4540 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4541 then
4542 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4543 else loop rest
4545 loop state.layout
4546 | 3 -> ()
4547 | _ -> viewmouse button down x y mask
4550 let uioh = object
4551 method display = ()
4553 method key key mask =
4554 begin match state.mode with
4555 | Textentry textentry -> textentrykeyboard key mask textentry
4556 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4557 | View -> viewkeyboard key mask
4558 | LinkNav linknav -> linknavkeyboard key mask linknav
4559 end;
4560 state.uioh
4562 method button button bstate x y mask =
4563 begin match state.mode with
4564 | LinkNav _ | View -> viewmouse button bstate x y mask
4565 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4566 | Textentry _ -> ()
4567 end;
4568 state.uioh
4570 method multiclick clicks x y mask =
4571 begin match state.mode with
4572 | LinkNav _ | View -> viewmulticlick clicks x y mask
4573 | Birdseye _ | Textentry _ -> ()
4574 end;
4575 state.uioh
4577 method motion x y =
4578 begin match state.mode with
4579 | Textentry _ -> ()
4580 | View | Birdseye _ | LinkNav _ ->
4581 match state.mstate with
4582 | Mzoom _ | Mnone -> ()
4583 | Mpan (x0, y0) ->
4584 let dx = x - x0
4585 and dy = y0 - y in
4586 state.mstate <- Mpan (x, y);
4587 let x = if canpan () then panbound (state.x + dx) else state.x in
4588 let y = clamp dy in
4589 gotoxy x y
4591 | Msel (a, _) ->
4592 state.mstate <- Msel (a, (x, y));
4593 postRedisplay "motion select";
4595 | Mscrolly ->
4596 let y = min state.winh (max 0 y) in
4597 scrolly y
4599 | Mscrollx ->
4600 let x = min state.winw (max 0 x) in
4601 scrollx x
4603 | Mzoomrect (p0, _) ->
4604 state.mstate <- Mzoomrect (p0, (x, y));
4605 postRedisplay "motion zoomrect";
4606 end;
4607 state.uioh
4609 method pmotion x y =
4610 begin match state.mode with
4611 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4612 let rec loop = function
4613 | [] ->
4614 if hooverpageno != -1
4615 then (
4616 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4617 postRedisplay "pmotion birdseye no hoover";
4619 | l :: rest ->
4620 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4621 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4622 then (
4623 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4624 postRedisplay "pmotion birdseye hoover";
4626 else loop rest
4628 loop state.layout
4630 | Textentry _ -> ()
4632 | LinkNav _ | View ->
4633 match state.mstate with
4634 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4635 | Mnone ->
4636 updateunder x y;
4637 if canselect ()
4638 then
4639 match conf.pax with
4640 | None -> ()
4641 | Some past ->
4642 let now = now () in
4643 let delta = now -. past in
4644 if delta > 0.01
4645 then paxunder x y
4646 else conf.pax <- Some now
4647 end;
4648 state.uioh
4650 method infochanged _ = ()
4652 method scrollph =
4653 let maxy = maxy () in
4654 let p, h =
4655 if maxy = 0
4656 then 0.0, float state.winh
4657 else scrollph state.y maxy
4659 vscrollw (), p, h
4661 method scrollpw =
4662 let fwinw = float (state.winw - vscrollw ()) in
4663 let sw =
4664 let sw = fwinw /. float state.w in
4665 let sw = fwinw *. sw in
4666 max sw (float conf.scrollh)
4668 let position =
4669 let maxx = state.w + state.winw in
4670 let x = state.winw - state.x in
4671 let percent = float x /. float maxx in
4672 (fwinw -. sw) *. percent
4674 hscrollh (), position, sw
4676 method modehash =
4677 let modename =
4678 match state.mode with
4679 | LinkNav _ -> "links"
4680 | Textentry _ -> "textentry"
4681 | Birdseye _ -> "birdseye"
4682 | View -> "view"
4684 findkeyhash conf modename
4686 method eformsgs = true
4687 method alwaysscrolly = false
4688 method scroll dx dy =
4689 let x = if canpan () then panbound (state.x + dx) else state.x in
4690 gotoxy x (clamp (2 * dy));
4691 state.uioh
4692 method zoom z x y =
4693 pivotzoom ~x ~y (conf.zoom *. exp z);
4694 end;;
4696 let addrect pageno r g b a x0 y0 x1 y1 =
4697 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4700 let ract cmds =
4701 let cl = splitatchar cmds ' ' in
4702 let scan s fmt f =
4703 try Scanf.sscanf s fmt f
4704 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4705 cmds @@ exntos exn
4707 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4708 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4709 s pageno r g b a x0 y0 x1 y1;
4710 onpagerect
4711 pageno
4712 (fun w h ->
4713 let _,w1,h1,_ = getpagedim pageno in
4714 let sw = float w1 /. float w
4715 and sh = float h1 /. float h in
4716 let x0s = x0 *. sw
4717 and x1s = x1 *. sw
4718 and y0s = y0 *. sh
4719 and y1s = y1 *. sh in
4720 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4721 let color = (r, g, b, a) in
4722 if conf.verbose then debugrect rect;
4723 state.rects <- (pageno, color, rect) :: state.rects;
4724 postRedisplay s;
4727 match cl with
4728 | "reload", "" -> reload ()
4729 | "goto", args ->
4730 scan args "%u %f %f"
4731 (fun pageno x y ->
4732 let cmd, _ = state.geomcmds in
4733 if emptystr cmd
4734 then gotopagexy pageno x y
4735 else
4736 let f prevf () =
4737 gotopagexy pageno x y;
4738 prevf ()
4740 state.reprf <- f state.reprf
4742 | "goto1", args -> scan args "%u %f" gotopage
4743 | "gotor", args -> scan args "%S" gotoremote
4744 | "rect", args ->
4745 scan args "%u %u %f %f %f %f"
4746 (fun pageno c x0 y0 x1 y1 ->
4747 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4748 rectx "rect" pageno color x0 y0 x1 y1;
4750 | "prect", args ->
4751 scan args "%u %f %f %f %f %f %f %f %f"
4752 (fun pageno r g b alpha x0 y0 x1 y1 ->
4753 addrect pageno r g b alpha x0 y0 x1 y1;
4754 postRedisplay "prect"
4756 | "pgoto", args ->
4757 scan args "%u %f %f"
4758 (fun pageno x y ->
4759 let optopaque =
4760 match getopaque pageno with
4761 | Some opaque -> opaque
4762 | None -> ~< E.s
4764 pgoto optopaque pageno x y;
4765 let rec fixx = function
4766 | [] -> ()
4767 | l :: rest ->
4768 if l.pageno = pageno
4769 then gotoxy (state.x - l.pagedispx) state.y
4770 else fixx rest
4772 let layout =
4773 let mult =
4774 match conf.columns with
4775 | Csingle _ | Csplit _ -> 1
4776 | Cmulti ((n, _, _), _) -> n
4778 layout 0 state.y (state.winw * mult) state.winh
4780 fixx layout
4782 | "activatewin", "" -> Wsi.activatewin ()
4783 | "quit", "" -> raise Quit
4784 | "keys", keys ->
4785 begin try
4786 let l = Config.keys_of_string keys in
4787 List.iter (fun (k, m) -> keyboard k m) l
4788 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4789 cmds @@ exntos exn
4791 | "clearrects", "" ->
4792 Hashtbl.clear state.prects;
4793 postRedisplay "clearrects"
4794 | _ ->
4795 adderrfmt "remote command"
4796 "error processing remote command: %S\n" cmds;
4799 let remote =
4800 let scratch = Bytes.create 80 in
4801 let buf = Buffer.create 80 in
4802 fun fd ->
4803 match tempfailureretry (Unix.read fd scratch 0) 80 with
4804 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4805 | 0 ->
4806 Unix.close fd;
4807 if Buffer.length buf > 0
4808 then (
4809 let s = Buffer.contents buf in
4810 Buffer.clear buf;
4811 ract s;
4813 None
4814 | n ->
4815 let rec eat ppos =
4816 let nlpos =
4817 match Bytes.index_from scratch ppos '\n' with
4818 | pos -> if pos >= n then -1 else pos
4819 | exception Not_found -> -1
4821 if nlpos >= 0
4822 then (
4823 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4824 let s = Buffer.contents buf in
4825 Buffer.clear buf;
4826 ract s;
4827 eat (nlpos+1);
4829 else (
4830 Buffer.add_subbytes buf scratch ppos (n-ppos);
4831 Some fd
4833 in eat 0
4836 let remoteopen path =
4837 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4838 with exn ->
4839 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4840 None
4843 let () =
4844 Utils.vlogf := (fun s -> if conf.verbose then prerr_endline s else ignore s);
4845 let gcconfig = ref false in
4846 let trimcachepath = ref E.s in
4847 let rcmdpath = ref E.s in
4848 let pageno = ref None in
4849 let openlast = ref false in
4850 let doreap = ref false in
4851 let csspath = ref None in
4852 selfexec := Sys.executable_name;
4853 Arg.parse
4854 (Arg.align
4855 [("-p", Arg.String (fun s -> state.password <- s),
4856 "<password> Set password");
4858 ("-f", Arg.String
4859 (fun s ->
4860 Config.fontpath := s;
4861 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4863 "<path> Set path to the user interface font");
4865 ("-c", Arg.String
4866 (fun s ->
4867 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4868 Config.confpath := s),
4869 "<path> Set path to the configuration file");
4871 ("-last", Arg.Set openlast, " Open last document");
4873 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4874 "<page-number> Jump to page");
4876 ("-tcf", Arg.String (fun s -> trimcachepath := s),
4877 "<path> Set path to the trim cache file");
4879 ("-dest", Arg.String (fun s -> state.nameddest <- s),
4880 "<named-destination> Set named destination");
4882 ("-remote", Arg.String (fun s -> rcmdpath := s),
4883 "<path> Set path to the source of remote commands");
4885 ("-gc", Arg.Set gcconfig, " Collect config garbage");
4887 ("-v", Arg.Unit (fun () ->
4888 Printf.printf
4889 "%s\nconfiguration file: %s\n"
4890 (Help.version ())
4891 Config.defconfpath;
4892 exit 0), " Print version and exit");
4894 ("-css", Arg.String (fun s -> csspath := Some s),
4895 "<path> Set path to the style sheet to use with EPUB/HTML");
4897 ("-origin", Arg.String (fun s -> state.origin <- s),
4898 "<origin> <undocumented>");
4900 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
4901 ("-layout-height", Arg.Set_int layouth,
4902 "<height> layout height html/epub/etc (-1, 0, N)");
4905 (fun s -> state.path <- s)
4906 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4908 let histmode = emptystr state.path && not !openlast in
4910 if not (Config.load !openlast)
4911 then dolog "failed to load configuration";
4913 begin match !pageno with
4914 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
4915 | None -> ()
4916 end;
4918 fillhelp ();
4919 if !gcconfig
4920 then (
4921 Config.gc ();
4922 exit 0
4925 let mu =
4926 object (self)
4927 val mutable m_clicks = 0
4928 val mutable m_click_x = 0
4929 val mutable m_click_y = 0
4930 val mutable m_lastclicktime = infinity
4932 method private cleanup =
4933 state.roam <- noroam;
4934 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) state.pagemap
4935 method expose = postRedisplay "expose"
4936 method visible v =
4937 let name =
4938 match v with
4939 | Wsi.Unobscured -> "unobscured"
4940 | Wsi.PartiallyObscured -> "partiallyobscured"
4941 | Wsi.FullyObscured -> "fullyobscured"
4943 vlog "visibility change %s" name
4944 method display = display ()
4945 method map mapped = vlog "mapped %b" mapped
4946 method reshape w h =
4947 self#cleanup;
4948 reshape w h
4949 method mouse b d x y m =
4950 if d && canselect ()
4951 then (
4953 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
4955 m_click_x <- x;
4956 m_click_y <- y;
4957 if b = 1
4958 then (
4959 let t = now () in
4960 if abs x - m_click_x > 10
4961 || abs y - m_click_y > 10
4962 || abs_float (t -. m_lastclicktime) > 0.3
4963 then m_clicks <- 0;
4964 m_clicks <- m_clicks + 1;
4965 m_lastclicktime <- t;
4966 if m_clicks = 1
4967 then (
4968 self#cleanup;
4969 postRedisplay "cleanup";
4970 state.uioh <- state.uioh#button b d x y m;
4972 else state.uioh <- state.uioh#multiclick m_clicks x y m
4974 else (
4975 self#cleanup;
4976 m_clicks <- 0;
4977 m_lastclicktime <- infinity;
4978 state.uioh <- state.uioh#button b d x y m
4981 else state.uioh <- state.uioh#button b d x y m
4982 method motion x y =
4983 state.mpos <- (x, y);
4984 state.uioh <- state.uioh#motion x y
4985 method pmotion x y =
4986 state.mpos <- (x, y);
4987 state.uioh <- state.uioh#pmotion x y
4988 method key k m =
4989 vlog "k=%#x m=%#x" k m;
4990 let mascm = m land (
4991 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
4992 ) in
4993 let keyboard k m =
4994 let x = state.x and y = state.y in
4995 keyboard k m;
4996 if x != state.x || y != state.y then self#cleanup
4998 match state.keystate with
4999 | KSnone ->
5000 let km = k, mascm in
5001 begin
5002 match
5003 let modehash = state.uioh#modehash in
5004 try Hashtbl.find modehash km
5005 with Not_found ->
5006 try Hashtbl.find (findkeyhash conf "global") km
5007 with Not_found -> KMinsrt (k, m)
5008 with
5009 | KMinsrt (k, m) -> keyboard k m
5010 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5011 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5013 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5014 List.iter (fun (k, m) -> keyboard k m) insrt;
5015 state.keystate <- KSnone
5016 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5017 state.keystate <- KSinto (keys, insrt)
5018 | KSinto _ -> state.keystate <- KSnone
5020 method enter x y =
5021 state.mpos <- (x, y);
5022 state.uioh <- state.uioh#pmotion x y
5023 method leave = state.mpos <- (-1, -1)
5024 method winstate wsl = state.winstate <- wsl
5025 method quit : 'a. 'a = raise Quit
5026 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5027 method zoom z x y = state.uioh#zoom z x y
5028 method opendoc path =
5029 state.mode <- View;
5030 state.uioh <- uioh;
5031 postRedisplay "opendoc";
5032 opendoc path state.password
5035 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5036 state.wsfd <- wsfd;
5038 if not @@ List.exists GlMisc.check_extension
5039 [ "GL_ARB_texture_rectangle"
5040 ; "GL_EXT_texture_recangle"
5041 ; "GL_NV_texture_rectangle" ]
5042 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5044 let cs, ss =
5045 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5046 | exception exn ->
5047 dolog "socketpair failed: %s" @@ exntos exn;
5048 exit 1
5049 | (r, w) ->
5050 cloexec r;
5051 cloexec w;
5052 r, w
5055 setcheckers conf.checkers;
5056 begin match !csspath with
5057 | None -> ()
5058 | Some "" -> conf.css <- E.s
5059 | Some path ->
5060 let css = filecontents path in
5061 let l = String.length css in
5062 conf.css <-
5063 if substratis css (l-2) "\r\n"
5064 then String.sub css 0 (l-2)
5065 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
5066 end;
5067 Ffi.init cs (
5068 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5069 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5070 !Config.fontpath, !trimcachepath
5072 List.iter GlArray.enable [`texture_coord; `vertex];
5073 GlTex.env (`color conf.texturecolor);
5074 state.ss <- ss;
5075 reshape ~firsttime:true winw winh;
5076 state.uioh <- uioh;
5077 if histmode
5078 then (
5079 Wsi.settitle "llpp (history)";
5080 enterhistmode ();
5082 else (
5083 state.text <- "Opening " ^ (mbtoutf8 state.path);
5084 opendoc state.path state.password;
5086 display ();
5087 Wsi.mapwin ();
5088 Wsi.setcursor Wsi.CURSOR_INHERIT;
5089 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5091 let rec reap () =
5092 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5093 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5094 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5095 | 0, _ -> ()
5096 | _pid, _status -> reap ()
5098 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5100 let optrfd =
5101 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
5104 let rec loop deadline =
5105 if !doreap
5106 then (
5107 doreap := false;
5108 reap ()
5110 let r = [state.ss; state.wsfd] in
5111 let r =
5112 match !optrfd with
5113 | None -> r
5114 | Some fd -> fd :: r
5116 if !redisplay
5117 then (
5118 Glutils.redisplay := false;
5119 display ();
5121 let timeout =
5122 let now = now () in
5123 if deadline > now
5124 then (
5125 if deadline = infinity
5126 then ~-.1.0
5127 else max 0.0 (deadline -. now)
5129 else 0.0
5131 let r, _, _ =
5132 try Unix.select r [] [] timeout
5133 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5135 begin match r with
5136 | [] ->
5137 let newdeadline =
5138 match state.autoscroll with
5139 | Some step when step != 0 ->
5140 if state.slideshow land 1 = 1
5141 then (
5142 if state.slideshow land 2 = 0
5143 then state.slideshow <- state.slideshow lor 2
5144 else if step < 0 then prevpage () else nextpage ();
5145 deadline +. (float (abs step))
5147 else
5148 let y = state.y + step in
5149 let fy = if conf.maxhfit then state.winh else 0 in
5150 let y =
5151 if y < 0
5152 then state.maxy - fy
5153 else if y >= state.maxy - fy then 0 else y
5155 gotoxy state.x y;
5156 deadline +. 0.01
5157 | _ -> infinity
5159 loop newdeadline
5161 | l ->
5162 let rec checkfds = function
5163 | [] -> ()
5164 | fd :: rest when fd = state.ss ->
5165 let cmd = Ffi.rcmd state.ss in
5166 act cmd;
5167 checkfds rest
5169 | fd :: rest when fd = state.wsfd ->
5170 Wsi.readresp fd;
5171 checkfds rest
5173 | fd :: rest when Some fd = !optrfd ->
5174 begin match remote fd with
5175 | None -> optrfd := remoteopen !rcmdpath;
5176 | opt -> optrfd := opt
5177 end;
5178 checkfds rest
5180 | _ :: rest ->
5181 dolog "select returned unknown descriptor";
5182 checkfds rest
5184 checkfds l;
5185 let newdeadline =
5186 let deadline1 =
5187 if deadline = infinity
5188 then now () +. 0.01
5189 else deadline
5191 match state.autoscroll with
5192 | Some step when step != 0 -> deadline1
5193 | _ -> infinity
5195 loop newdeadline
5196 end;
5198 match loop infinity with
5199 | exception Quit ->
5200 Config.save leavebirdseye;
5201 if Ffi.hasunsavedchanges ()
5202 then save ()
5203 | _ -> error "umpossible - infinity reached"