Rename getenvwithdef to getenvdef
[llpp.git] / main.ml
blobc583d69aeeb5751d060647cc8e6f4722008ab5a8
1 open Utils;;
2 open Config;;
3 open Glutils;;
4 open Listview;;
5 open Ffi;;
7 let selfexec = ref E.s;;
8 let ignoredoctitlte = ref false;;
9 let layouth = ref ~-1;;
10 let checkerstexid = ref None;;
12 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
13 dolog {|rect {
14 x0,y0=(% f, % f)
15 x1,y1=(% f, % f)
16 x2,y2=(% f, % f)
17 x3,y3=(% f, % f)
18 }|} x0 y0 x1 y1 x2 y2 x3 y3;
21 let pgscale h = truncate (float h *. conf.pgscale);;
23 let hscrollh () =
24 if state.uioh#alwaysscrolly || ((conf.scrollb land scrollbhv != 0)
25 && (state.w > state.winw))
26 then conf.scrollbw
27 else 0
30 let setfontsize n =
31 fstate.fontsize <- n;
32 fstate.wwidth <- measurestr fstate.fontsize "w";
33 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
36 let vlog fmt = if conf.verbose then dolog fmt else Printf.kprintf ignore fmt;;
38 let launchpath () =
39 if emptystr conf.pathlauncher
40 then dolog "%s" state.path
41 else (
42 let command =
43 Str.global_replace Utils.Re.percent state.path conf.pathlauncher in
44 match spawn command [] with
45 | _pid -> ()
46 | exception exn -> dolog "failed to execute `%s': %s" command @@ exntos exn
50 let getopaque pageno =
51 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
52 with Not_found -> None
55 let pagetranslatepoint l x y =
56 let dy = y - l.pagedispy in
57 let y = dy + l.pagey in
58 let dx = x - l.pagedispx in
59 let x = dx + l.pagex in
60 (x, y);
63 let onppundermouse g x y d =
64 let rec f = function
65 | l :: rest ->
66 begin match getopaque l.pageno with
67 | Some opaque ->
68 let x0 = l.pagedispx in
69 let x1 = x0 + l.pagevw in
70 let y0 = l.pagedispy in
71 let y1 = y0 + l.pagevh in
72 if y >= y0 && y <= y1 && x >= x0 && x <= x1
73 then
74 let px, py = pagetranslatepoint l x y in
75 match g opaque l px py with
76 | Some res -> res
77 | None -> f rest
78 else f rest
79 | _ -> f rest
80 end
81 | [] -> d
83 f state.layout
86 let getunder x y =
87 let g opaque l px py =
88 if state.bzoom
89 then (
90 match rectofblock opaque px py with
91 | Some [|x0;x1;y0;y1|] ->
92 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
93 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
94 state.rects <- [l.pageno, color, rect];
95 postRedisplay "getunder";
96 | _ -> ()
98 let under = whatsunder opaque px py in
99 if under = Unone then None else Some under
101 onppundermouse g x y Unone
104 let unproject x y =
105 let g opaque l x y =
106 match unproject opaque x y with
107 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
108 | None -> None
110 onppundermouse g x y None;
113 let showtext c s =
114 state.text <- Printf.sprintf "%c%s" c s;
115 postRedisplay "showtext";
118 let impmsg fmt = Format.ksprintf (fun s -> showtext '!' s) fmt;;
120 let pipesel opaque cmd =
121 if hassel opaque
122 then pipef ~closew:false "pipesel"
123 (fun w ->
124 copysel w opaque;
125 postRedisplay "pipesel"
126 ) cmd
129 let paxunder x y =
130 let g opaque l px py =
131 if markunder opaque px py conf.paxmark
132 then
133 Some (fun () ->
134 match getopaque l.pageno with
135 | None -> ()
136 | Some opaque -> pipesel opaque conf.paxcmd
138 else None
140 postRedisplay "paxunder";
141 if conf.paxmark = Mark_page
142 then
143 List.iter (fun l ->
144 match getopaque l.pageno with
145 | None -> ()
146 | Some opaque -> clearmark opaque) state.layout;
147 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
150 let undertext = function
151 | Unone -> "none"
152 | Ulinkuri s -> s
153 | Utext s -> "font: " ^ s
154 | Uannotation (opaque, slinkindex) ->
155 "annotation: " ^ getannotcontents opaque slinkindex
158 let updateunder x y =
159 match getunder x y with
160 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
161 | Ulinkuri uri ->
162 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
163 Wsi.setcursor Wsi.CURSOR_INFO
164 | Utext s ->
165 if conf.underinfo then showtext 'f' ("ont: " ^ s);
166 Wsi.setcursor Wsi.CURSOR_TEXT
167 | Uannotation _ ->
168 if conf.underinfo then showtext 'a' "nnotation";
169 Wsi.setcursor Wsi.CURSOR_INFO
172 let showlinktype under =
173 if conf.underinfo && under != Unone
174 then showtext ' ' @@ undertext under
177 let intentry_with_suffix text key =
178 let text =
179 match [@warning "-4"] key with
180 | Keys.Ascii ('0'..'9' as c) -> addchar text c
181 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
182 addchar text @@ asciilower c
183 | _ ->
184 state.text <- "invalid key";
185 text
187 TEcont text
190 let wcmd fmt =
191 let b = Buffer.create 16 in
192 Printf.kbprintf
193 (fun b ->
194 let b = Buffer.to_bytes b in
195 wcmd state.ss b @@ Bytes.length b
196 ) b fmt
199 let nogeomcmds = function
200 | s, [] -> emptystr s
201 | _ -> false
204 let layoutN ((columns, coverA, coverB), b) x y sw sh =
205 let rec fold accu n =
206 if n = Array.length b
207 then accu
208 else
209 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
210 if (vy - y) > sh &&
211 (n = coverA - 1
212 || n = state.pagecount - coverB
213 || (n - coverA) mod columns = columns - 1)
214 then accu
215 else
216 let accu =
217 if vy + h > y
218 then
219 let pagey = max 0 (y - vy) in
220 let pagedispy = if pagey > 0 then 0 else vy - y in
221 let pagedispx, pagex =
222 let pdx =
223 if n = coverA - 1 || n = state.pagecount - coverB
224 then x + (sw - w) / 2
225 else dx + xoff + x
227 if pdx < 0
228 then 0, -pdx
229 else pdx, 0
231 let pagevw =
232 let vw = sw - pagedispx in
233 let pw = w - pagex in
234 min vw pw
236 let pagevh = min (h - pagey) (sh - pagedispy) in
237 if pagevw > 0 && pagevh > 0
238 then
239 let e =
240 { pageno = n
241 ; pagedimno = pdimno
242 ; pagew = w
243 ; pageh = h
244 ; pagex = pagex
245 ; pagey = pagey
246 ; pagevw = pagevw
247 ; pagevh = pagevh
248 ; pagedispx = pagedispx
249 ; pagedispy = pagedispy
250 ; pagecol = 0
253 e :: accu
254 else accu
255 else accu
257 fold accu (n+1)
259 if Array.length b = 0
260 then []
261 else List.rev (fold [] (page_of_y y))
264 let layoutS (columns, b) x y sw sh =
265 let rec fold accu n =
266 if n = Array.length b
267 then accu
268 else
269 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
270 if (vy - y) > sh
271 then accu
272 else
273 let accu =
274 if vy + pageh > y
275 then
276 let x = xoff + x in
277 let pagey = max 0 (y - vy) in
278 let pagedispy = if pagey > 0 then 0 else vy - y in
279 let pagedispx, pagex =
280 if px = 0
281 then (
282 if x < 0
283 then 0, -x
284 else x, 0
286 else (
287 let px = px - x in
288 if px < 0
289 then -px, 0
290 else 0, px
293 let pagecolw = pagew/columns in
294 let pagedispx =
295 if pagecolw < sw
296 then pagedispx + ((sw - pagecolw) / 2)
297 else pagedispx
299 let pagevw =
300 let vw = sw - pagedispx in
301 let pw = pagew - pagex in
302 min vw pw
304 let pagevw = min pagevw pagecolw in
305 let pagevh = min (pageh - pagey) (sh - pagedispy) in
306 if pagevw > 0 && pagevh > 0
307 then
308 let e =
309 { pageno = n/columns
310 ; pagedimno = pdimno
311 ; pagew = pagew
312 ; pageh = pageh
313 ; pagex = pagex
314 ; pagey = pagey
315 ; pagevw = pagevw
316 ; pagevh = pagevh
317 ; pagedispx = pagedispx
318 ; pagedispy = pagedispy
319 ; pagecol = n mod columns
322 e :: accu
323 else accu
324 else accu
326 fold accu (n+1)
328 List.rev (fold [] 0)
331 let layout x y sw sh =
332 if nogeomcmds state.geomcmds
333 then
334 match conf.columns with
335 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
336 | Cmulti c -> layoutN c x y sw sh
337 | Csplit s -> layoutS s x y sw sh
338 else []
341 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
342 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
344 let itertiles l f =
345 let tilex = l.pagex mod conf.tilew in
346 let tiley = l.pagey mod conf.tileh in
348 let col = l.pagex / conf.tilew in
349 let row = l.pagey / conf.tileh in
351 let rec rowloop row y0 dispy h =
352 if h != 0
353 then
354 let dh = conf.tileh - y0 in
355 let dh = min h dh in
356 let rec colloop col x0 dispx w =
357 if w != 0
358 then
359 let dw = conf.tilew - x0 in
360 let dw = min w dw in
361 f col row dispx dispy x0 y0 dw dh;
362 colloop (col+1) 0 (dispx+dw) (w-dw)
364 colloop col tilex l.pagedispx l.pagevw;
365 rowloop (row+1) 0 (dispy+dh) (h-dh)
367 if l.pagevw > 0 && l.pagevh > 0
368 then rowloop row tiley l.pagedispy l.pagevh;
371 let gettileopaque l col row =
372 let key = l.pageno, state.gen, conf.colorspace,
373 conf.angle, l.pagew, l.pageh, col, row in
374 try Some (Hashtbl.find state.tilemap key)
375 with Not_found -> None
378 let puttileopaque l col row gen colorspace angle opaque size elapsed =
379 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
380 Hashtbl.add state.tilemap key (opaque, size, elapsed)
383 let drawtiles l color =
384 GlDraw.color color;
385 begintiles ();
386 let f col row x y tilex tiley w h =
387 match gettileopaque l col row with
388 | Some (opaque, _, t) ->
389 let params = x, y, w, h, tilex, tiley in
390 if conf.invert
391 then GlTex.env (`mode `blend);
392 drawtile params opaque;
393 if conf.invert
394 then GlTex.env (`mode `modulate);
395 if conf.debug
396 then (
397 endtiles ();
398 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
399 let w = measurestr fstate.fontsize s in
400 GlDraw.color (0.0, 0.0, 0.0);
401 filledrect
402 (float (x-2))
403 (float (y-2))
404 (float (x+2) +. w)
405 (float (y + fstate.fontsize + 2));
406 GlDraw.color color;
407 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
408 begintiles ();
411 | None ->
412 endtiles ();
413 let w = let lw = state.winw - x in min lw w
414 and h = let lh = state.winh - y in min lh h
416 if conf.invert
417 then GlTex.env (`mode `blend);
418 begin match !checkerstexid with
419 | Some id ->
420 Gl.enable `texture_2d;
421 GlTex.bind_texture ~target:`texture_2d id;
422 let x0 = float x
423 and y0 = float y
424 and x1 = float (x+w)
425 and y1 = float (y+h) in
427 let tw = float w /. 16.0
428 and th = float h /. 16.0 in
429 let tx0 = float tilex /. 16.0
430 and ty0 = float tiley /. 16.0 in
431 let tx1 = tx0 +. tw
432 and ty1 = ty0 +. th in
433 Raw.sets_float Glutils.vraw ~pos:0
434 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
435 Raw.sets_float Glutils.traw ~pos:0
436 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
437 GlArray.vertex `two Glutils.vraw;
438 GlArray.tex_coord `two Glutils.traw;
439 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
440 Gl.disable `texture_2d;
442 | None ->
443 GlDraw.color (1.0, 1.0, 1.0);
444 filledrect (float x) (float y) (float (x+w)) (float (y+h));
445 end;
446 if conf.invert
447 then GlTex.env (`mode `modulate);
448 if w > 128 && h > fstate.fontsize + 10
449 then (
450 let c = if conf.invert then 1.0 else 0.0 in
451 GlDraw.color (c, c, c);
452 let c, r =
453 if conf.verbose
454 then (col*conf.tilew, row*conf.tileh)
455 else col, row
457 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
459 GlDraw.color color;
460 begintiles ();
462 itertiles l f;
463 endtiles ();
466 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
468 let tilevisible1 l x y =
469 let ax0 = l.pagex
470 and ax1 = l.pagex + l.pagevw
471 and ay0 = l.pagey
472 and ay1 = l.pagey + l.pagevh in
474 let bx0 = x
475 and by0 = y in
476 let bx1 = min (bx0 + conf.tilew) l.pagew
477 and by1 = min (by0 + conf.tileh) l.pageh in
479 let rx0 = max ax0 bx0
480 and ry0 = max ay0 by0
481 and rx1 = min ax1 bx1
482 and ry1 = min ay1 by1 in
484 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
485 nonemptyintersection
488 let tilevisible layout n x y =
489 let rec findpageinlayout m = function
490 | l :: rest when l.pageno = n ->
491 tilevisible1 l x y || (
492 match conf.columns with
493 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
494 | Csplit _ | Csingle _ | Cmulti _ -> false
496 | _ :: rest -> findpageinlayout 0 rest
497 | [] -> false
499 findpageinlayout 0 layout;
502 let tileready l x y =
503 tilevisible1 l x y &&
504 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
507 let tilepage n p layout =
508 let rec loop = function
509 | l :: rest ->
510 if l.pageno = n
511 then
512 let f col row _ _ _ _ _ _ =
513 if state.currently = Idle
514 then
515 match gettileopaque l col row with
516 | Some _ -> ()
517 | None ->
518 let x = col*conf.tilew
519 and y = row*conf.tileh in
520 let w =
521 let w = l.pagew - x in
522 min w conf.tilew
524 let h =
525 let h = l.pageh - y in
526 min h conf.tileh
528 let pbo =
529 if conf.usepbo
530 then getpbo w h conf.colorspace
531 else ~< "0"
533 wcmd "tile %s %d %d %d %d %s" (~> p) x y w h (~> pbo);
534 state.currently <-
535 Tiling (
536 l, p, conf.colorspace, conf.angle,
537 state.gen, col, row, conf.tilew, conf.tileh
540 itertiles l f;
541 else
542 loop rest
544 | [] -> ()
546 if nogeomcmds state.geomcmds
547 then loop layout;
550 let preloadlayout x y sw sh =
551 let y = if y < sh then 0 else y - sh in
552 let x = min 0 (x + sw) in
553 let h = sh*3 in
554 let w = sw*3 in
555 layout x y w h;
558 let load pages =
559 let rec loop pages =
560 if state.currently = Idle
561 then
562 match pages with
563 | l :: rest ->
564 begin match getopaque l.pageno with
565 | None ->
566 wcmd "page %d %d" l.pageno l.pagedimno;
567 state.currently <- Loading (l, state.gen);
568 | Some opaque ->
569 tilepage l.pageno opaque pages;
570 loop rest
571 end;
572 | _ -> ()
574 if nogeomcmds state.geomcmds
575 then loop pages
578 let preload pages =
579 load pages;
580 if conf.preload && state.currently = Idle
581 then load (preloadlayout state.x state.y state.winw state.winh);
584 let layoutready layout =
585 let rec fold all ls =
586 all && match ls with
587 | l :: rest ->
588 let seen = ref false in
589 let allvisible = ref true in
590 let foo col row _ _ _ _ _ _ =
591 seen := true;
592 allvisible := !allvisible &&
593 begin match gettileopaque l col row with
594 | Some _ -> true
595 | None -> false
598 itertiles l foo;
599 fold (!seen && !allvisible) rest
600 | [] -> true
602 let alltilesvisible = fold true layout in
603 alltilesvisible;
606 let gotoxy x y =
607 let y = bound y 0 state.maxy in
608 let y, layout =
609 let layout = layout x y state.winw state.winh in
610 postRedisplay "gotoxy ready";
611 y, layout
613 state.x <- x;
614 state.y <- y;
615 state.layout <- layout;
616 begin match state.mode with
617 | LinkNav ln ->
618 begin match ln with
619 | Ltexact (pageno, linkno) ->
620 let rec loop = function
621 | [] ->
622 state.lnava <- Some (pageno, linkno);
623 state.mode <- LinkNav (Ltgendir 0)
624 | l :: _ when l.pageno = pageno ->
625 begin match getopaque pageno with
626 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
627 | Some opaque ->
628 let x0, y0, x1, y1 = getlinkrect opaque linkno in
629 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
630 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
631 then state.mode <- LinkNav (Ltgendir 0)
633 | _ :: rest -> loop rest
635 loop layout
636 | Ltnotready _ | Ltgendir _ -> ()
638 | Birdseye _ | Textentry _ | View -> ()
639 end;
640 begin match state.mode with
641 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
642 if not (pagevisible layout pageno)
643 then (
644 match state.layout with
645 | [] -> ()
646 | l :: _ ->
647 state.mode <- Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
649 | LinkNav lt ->
650 begin match lt with
651 | Ltnotready (_, dir)
652 | Ltgendir dir ->
653 let linknav =
654 let rec loop = function
655 | [] -> lt
656 | l :: rest ->
657 match getopaque l.pageno with
658 | None -> Ltnotready (l.pageno, dir)
659 | Some opaque ->
660 let link =
661 let ld =
662 if dir = 0
663 then LDfirstvisible (l.pagex, l.pagey, dir)
664 else if dir > 0 then LDfirst else LDlast
666 findlink opaque ld
668 match link with
669 | Lnotfound -> loop rest
670 | Lfound n ->
671 showlinktype (getlink opaque n);
672 Ltexact (l.pageno, n)
674 loop state.layout
676 state.mode <- LinkNav linknav
677 | Ltexact _ -> ()
679 | Textentry _ | View -> ()
680 end;
681 preload layout;
682 if conf.updatecurs
683 then (
684 let mx, my = state.mpos in
685 updateunder mx my;
689 let conttiling pageno opaque =
690 tilepage pageno opaque
691 (if conf.preload
692 then preloadlayout state.x state.y state.winw state.winh
693 else state.layout)
696 let gotoxy x y =
697 if not conf.verbose then state.text <- E.s;
698 gotoxy x y;
701 let getanchory (n, top, dtop) =
702 let y, h = getpageyh n in
703 if conf.presentation
704 then
705 let ips = calcips h in
706 y + truncate (top*.float h -. dtop*.float ips) + ips;
707 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
710 let gotoanchor anchor = gotoxy state.x (getanchory anchor);;
711 let addnav () = getanchor () |> cbput state.hists.nav;;
712 let addnavnorc () = getanchor () |> cbput_dont_update_rc state.hists.nav;;
714 let getnav dir =
715 let anchor = cbgetc state.hists.nav dir in
716 getanchory anchor;
719 let gotopage n top =
720 let y, h = getpageyh n in
721 let y = y + (truncate (top *. float h)) in
722 gotoxy state.x y
725 let gotopage1 n top =
726 let y = getpagey n in
727 let y = y + top in
728 gotoxy state.x y
731 let invalidate s f =
732 Glutils.redisplay := false;
733 state.layout <- [];
734 state.pdims <- [];
735 state.rects <- [];
736 state.rects1 <- [];
737 match state.geomcmds with
738 | ps, [] when emptystr ps ->
739 f ();
740 state.geomcmds <- s, [];
741 | ps, [] -> state.geomcmds <- ps, [s, f];
742 | ps, (s', _) :: rest when s' = s -> state.geomcmds <- ps, ((s, f) :: rest);
743 | ps, cmds -> state.geomcmds <- ps, ((s, f) :: cmds);
746 let flushpages () =
747 Hashtbl.iter (fun _ opaque -> wcmd "freepage %s" (~> opaque)) state.pagemap;
748 Hashtbl.clear state.pagemap;
751 let flushtiles () =
752 if not (Queue.is_empty state.tilelru)
753 then (
754 Queue.iter (fun (k, p, s) ->
755 wcmd "freetile %s" (~> p);
756 state.memused <- state.memused - s;
757 Hashtbl.remove state.tilemap k;
758 ) state.tilelru;
759 state.uioh#infochanged Memused;
760 Queue.clear state.tilelru;
762 load state.layout;
765 let stateh h =
766 let h = truncate (float h*.conf.zoom) in
767 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
768 h - d
771 let fillhelp () =
772 state.help <-
773 let sl = keystostrlist conf in
774 let rec loop accu =
775 function | [] -> accu
776 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
777 in Help.makehelp conf.urilauncher
778 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
781 let opendoc path password =
782 state.path <- path;
783 state.password <- password;
784 state.gen <- state.gen + 1;
785 state.docinfo <- [];
786 state.outlines <- [||];
788 flushpages ();
789 setaalevel conf.aalevel;
790 let titlepath =
791 if emptystr state.origin
792 then path
793 else state.origin
795 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
796 wcmd "open %d %d %s\000%s\000%s\000"
797 (btod conf.usedoccss) !layouth
798 path password conf.css;
799 invalidate "reqlayout"
800 (fun () ->
801 wcmd "reqlayout %d %d %d %s\000"
802 conf.angle (FMTE.to_int conf.fitmodel)
803 (stateh state.winh) state.nameddest
805 fillhelp ();
808 let reload () =
809 state.anchor <- getanchor ();
810 opendoc state.path state.password;
813 let scalecolor c = let c = c *. conf.colorscale in (c, c, c);;
814 let scalecolor2 (r, g, b) =
815 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
818 let docolumns columns =
819 match columns with
820 | Csingle _ ->
821 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
822 let rec loop pageno pdimno pdim y ph pdims =
823 if pageno != state.pagecount
824 then
825 let pdimno, ((_, w, h, xoff) as pdim), pdims =
826 match pdims with
827 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
828 pdimno+1, pdim, rest
829 | _ ->
830 pdimno, pdim, pdims
832 let x = max 0 (((state.winw - w) / 2) - xoff) in
833 let y =
834 y + (if conf.presentation
835 then (if pageno = 0 then calcips h else calcips ph + calcips h)
836 else (if pageno = 0 then 0 else conf.interpagespace))
838 a.(pageno) <- (pdimno, x, y, pdim);
839 loop (pageno+1) pdimno pdim (y + h) h pdims
841 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
842 conf.columns <- Csingle a;
844 | Cmulti ((columns, coverA, coverB), _) ->
845 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
846 let rec loop pageno pdimno pdim x y rowh pdims =
847 let rec fixrow m =
848 if m = pageno then () else
849 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
850 if h < rowh
851 then (
852 let y = y + (rowh - h) / 2 in
853 a.(m) <- (pdimno, x, y, pdim);
855 fixrow (m+1)
857 if pageno = state.pagecount
858 then fixrow (((pageno - 1) / columns) * columns)
859 else
860 let pdimno, ((_, w, h, xoff) as pdim), pdims =
861 match pdims with
862 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
863 pdimno+1, pdim, rest
864 | _ -> pdimno, pdim, pdims
866 let x, y, rowh' =
867 if pageno = coverA - 1 || pageno = state.pagecount - coverB
868 then (
869 let x = (state.winw - w) / 2 in
870 let ips =
871 if conf.presentation then calcips h else conf.interpagespace in
872 x, y + ips + rowh, h
874 else (
875 if (pageno - coverA) mod columns = 0
876 then (
877 let x = max 0 (state.winw - state.w) / 2 in
878 let y =
879 if conf.presentation
880 then
881 let ips = calcips h in
882 y + (if pageno = 0 then 0 else calcips rowh + ips)
883 else
884 y + (if pageno = 0 then 0 else conf.interpagespace)
886 x, y + rowh, h
888 else x, y, max rowh h
891 let y =
892 if pageno > 1 && (pageno - coverA) mod columns = 0
893 then (
894 let y =
895 if pageno = columns && conf.presentation
896 then (
897 let ips = calcips rowh in
898 for i = 0 to pred columns
900 let (pdimno, x, y, pdim) = a.(i) in
901 a.(i) <- (pdimno, x, y+ips, pdim)
902 done;
903 y+ips;
905 else y
907 fixrow (pageno - columns);
910 else y
912 a.(pageno) <- (pdimno, x, y, pdim);
913 let x = x + w + xoff*2 + conf.interpagespace in
914 loop (pageno+1) pdimno pdim x y rowh' pdims
916 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
917 conf.columns <- Cmulti ((columns, coverA, coverB), a);
919 | Csplit (c, _) ->
920 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
921 let rec loop pageno pdimno pdim y pdims =
922 if pageno != state.pagecount
923 then
924 let pdimno, ((_, w, h, _) as pdim), pdims =
925 match pdims with
926 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
927 pdimno+1, pdim, rest
928 | _ -> pdimno, pdim, pdims
930 let cw = w / c in
931 let rec loop1 n x y =
932 if n = c then y else (
933 a.(pageno*c + n) <- (pdimno, x, y, pdim);
934 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
937 let y = loop1 0 0 y in
938 loop (pageno+1) pdimno pdim y pdims
940 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
941 conf.columns <- Csplit (c, a);
944 let represent () =
945 docolumns conf.columns;
946 state.maxy <- calcheight ();
947 if state.reprf == noreprf
948 then (
949 match state.mode with
950 | Birdseye (_, _, pageno, _, _) ->
951 let y, h = getpageyh pageno in
952 let top = (state.winh - h) / 2 in
953 gotoxy state.x (max 0 (y - top))
954 | Textentry _ | View | LinkNav _ ->
955 let y = getanchory state.anchor in
956 let y = min y (state.maxy - state.winh) in
957 gotoxy state.x y;
959 else (
960 state.reprf ();
961 state.reprf <- noreprf;
965 let reshape ?(firsttime=false) w h =
966 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
967 if not firsttime && nogeomcmds state.geomcmds
968 then state.anchor <- getanchor ();
970 state.winw <- w;
971 let w = truncate (float w *. conf.zoom) in
972 let w = max w 2 in
973 state.winh <- h;
974 setfontsize fstate.fontsize;
975 GlMat.mode `modelview;
976 GlMat.load_identity ();
978 GlMat.mode `projection;
979 GlMat.load_identity ();
980 GlMat.rotate ~x:1.0 ~angle:180.0 ();
981 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
982 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
984 let relx =
985 if conf.zoom <= 1.0
986 then 0.0
987 else float state.x /. float state.w
989 invalidate "geometry"
990 (fun () ->
991 state.w <- w;
992 if not firsttime
993 then state.x <- truncate (relx *. float w);
994 let w =
995 match conf.columns with
996 | Csingle _ -> w
997 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
998 | Csplit (c, _) -> w * c
1000 wcmd "geometry %d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
1004 let gctiles () =
1005 let len = Queue.length state.tilelru in
1006 let layout = lazy (if conf.preload
1007 then preloadlayout state.x state.y state.winw state.winh
1008 else state.layout) in
1009 let rec loop qpos =
1010 if state.memused > conf.memlimit
1011 then (
1012 if qpos < len
1013 then
1014 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1015 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1016 let (_, pw, ph, _) = getpagedim n in
1017 if gen = state.gen
1018 && colorspace = conf.colorspace
1019 && angle = conf.angle
1020 && pagew = pw
1021 && pageh = ph
1022 && (
1023 let x = col*conf.tilew and y = row*conf.tileh in
1024 tilevisible (Lazy.force_val layout) n x y
1026 then Queue.push lruitem state.tilelru
1027 else (
1028 freepbo p;
1029 wcmd "freetile %s" (~> p);
1030 state.memused <- state.memused - s;
1031 state.uioh#infochanged Memused;
1032 Hashtbl.remove state.tilemap k;
1034 loop (qpos+1)
1037 loop 0
1040 let onpagerect pageno f =
1041 let b =
1042 match conf.columns with
1043 | Cmulti (_, b) -> b
1044 | Csingle b -> b
1045 | Csplit (_, b) -> b
1047 if pageno >= 0 && pageno < Array.length b
1048 then
1049 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1050 f w h
1053 let gotopagexy1 pageno x y =
1054 let _,w1,h1,leftx = getpagedim pageno in
1055 let top = y /. (float h1) in
1056 let left = x /. (float w1) in
1057 let py, w, h = getpageywh pageno in
1058 let wh = state.winh in
1059 let x = left *. (float w) in
1060 let x = leftx + state.x + truncate x in
1061 let sx =
1062 if x < 0 || x >= state.winw
1063 then state.x - x
1064 else state.x
1066 let pdy = truncate (top *. float h) in
1067 let y' = py + pdy in
1068 let dy = y' - state.y in
1069 let sy =
1070 if x != state.x || not (dy > 0 && dy < wh)
1071 then (
1072 if conf.presentation
1073 then
1074 if abs (py - y') > wh
1075 then y'
1076 else py
1077 else y';
1079 else state.y
1081 if state.x != sx || state.y != sy
1082 then gotoxy sx sy
1083 else gotoxy state.x state.y;
1086 let gotopagexy pageno x y =
1087 match state.mode with
1088 | Birdseye _ -> gotopage pageno 0.0
1089 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1092 let getpassword () =
1093 let passcmd = getenvdef "LLPP_ASKPASS" conf.passcmd in
1094 if emptystr passcmd
1095 then E.s
1096 else getcmdoutput (fun s ->
1097 impmsg "error getting password: %s" s;
1098 dolog "%s" s) passcmd;
1101 let pgoto opaque pageno x y =
1102 let pdimno = getpdimno pageno in
1103 let x, y = project opaque pageno pdimno x y in
1104 gotopagexy pageno x y;
1107 let act cmds =
1108 (* dolog "%S" cmds; *)
1109 let spl = splitatchar cmds ' ' in
1110 let scan s fmt f =
1111 try Scanf.sscanf s fmt f
1112 with exn ->
1113 dolog "error processing '%S': %s" cmds @@ exntos exn;
1114 exit 1
1116 let addoutline outline =
1117 match state.currently with
1118 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1119 | Idle -> state.currently <- Outlining [outline]
1120 | Loading _ | Tiling _ ->
1121 dolog "invalid outlining state";
1122 logcurrently state.currently
1124 match spl with
1125 | "clear", "" ->
1126 state.pdims <- [];
1127 state.uioh#infochanged Pdim;
1129 | "clearrects", "" ->
1130 state.rects <- state.rects1;
1131 postRedisplay "clearrects";
1133 | "continue", args ->
1134 let n = scan args "%u" (fun n -> n) in
1135 state.pagecount <- n;
1136 begin match state.currently with
1137 | Outlining l ->
1138 state.currently <- Idle;
1139 state.outlines <- Array.of_list (List.rev l)
1140 | Idle | Loading _ | Tiling _ -> ()
1141 end;
1143 let cur, cmds = state.geomcmds in
1144 if emptystr cur then error "empty geomcmd";
1146 begin match List.rev cmds with
1147 | [] ->
1148 state.geomcmds <- E.s, [];
1149 represent ();
1150 | (s, f) :: rest ->
1151 f ();
1152 state.geomcmds <- s, List.rev rest;
1153 end;
1154 postRedisplay "continue";
1156 | "msg", args ->
1157 showtext ' ' args
1159 | "vmsg", args ->
1160 if conf.verbose then showtext ' ' args
1162 | "emsg", args ->
1163 Buffer.add_string state.errmsgs args;
1164 state.newerrmsgs <- true;
1165 postRedisplay "error message"
1167 | "progress", args ->
1168 let progress, text =
1169 scan args "%f %n"
1170 (fun f pos -> f, String.sub args pos (String.length args - pos))
1172 state.text <- text;
1173 state.progress <- progress;
1174 postRedisplay "progress"
1176 | "firstmatch", args ->
1177 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1178 scan args "%u %d %f %f %f %f %f %f %f %f"
1179 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1180 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1182 let y = (getpagey pageno) + truncate y0 in
1183 let x =
1184 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1185 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1186 else state.x
1188 addnav ();
1189 gotoxy x y;
1190 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1191 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1193 | "match", args ->
1194 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1195 scan args "%u %d %f %f %f %f %f %f %f %f"
1196 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1197 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1199 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1200 state.rects1 <-
1201 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1203 | "page", args ->
1204 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1205 let pageopaque = ~< pageopaques in
1206 begin match state.currently with
1207 | Loading (l, gen) ->
1208 vlog "page %d took %f sec" l.pageno t;
1209 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1210 let preloadedpages =
1211 if conf.preload
1212 then preloadlayout state.x state.y state.winw state.winh
1213 else state.layout
1215 let evict () =
1216 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1217 IntSet.empty preloadedpages
1219 let evictedpages =
1220 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1221 if not (IntSet.mem pageno set)
1222 then (
1223 wcmd "freepage %s" (~> opaque);
1224 key :: accu
1226 else accu
1227 ) state.pagemap []
1229 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1231 evict ();
1232 state.currently <- Idle;
1233 if gen = state.gen
1234 then (
1235 tilepage l.pageno pageopaque state.layout;
1236 load state.layout;
1237 load preloadedpages;
1238 let visible = pagevisible state.layout l.pageno in
1239 if visible
1240 then (
1241 match state.mode with
1242 | LinkNav (Ltnotready (pageno, dir)) ->
1243 if pageno = l.pageno
1244 then (
1245 let link =
1246 let ld =
1247 if dir = 0
1248 then LDfirstvisible (l.pagex, l.pagey, dir)
1249 else if dir > 0 then LDfirst else LDlast
1251 findlink pageopaque ld
1253 match link with
1254 | Lnotfound -> ()
1255 | Lfound n ->
1256 showlinktype (getlink pageopaque n);
1257 state.mode <- LinkNav (Ltexact (l.pageno, n))
1259 | LinkNav (Ltgendir _)
1260 | LinkNav (Ltexact _)
1261 | View
1262 | Birdseye _
1263 | Textentry _ -> ()
1266 if visible && layoutready state.layout
1267 then postRedisplay "page";
1270 | Idle | Tiling _ | Outlining _ ->
1271 dolog "Inconsistent loading state";
1272 logcurrently state.currently;
1273 exit 1
1276 | "tile" , args ->
1277 let (x, y, opaques, size, t) =
1278 scan args "%u %u %s %u %f" (fun x y p size t -> (x, y, p, size, t))
1280 let opaque = ~< opaques in
1281 begin match state.currently with
1282 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1283 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1285 unmappbo opaque;
1286 if tilew != conf.tilew || tileh != conf.tileh
1287 then (
1288 wcmd "freetile %s" (~> opaque);
1289 state.currently <- Idle;
1290 load state.layout;
1292 else (
1293 puttileopaque l col row gen cs angle opaque size t;
1294 state.memused <- state.memused + size;
1295 state.uioh#infochanged Memused;
1296 gctiles ();
1297 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1298 opaque, size) state.tilelru;
1300 state.currently <- Idle;
1301 if gen = state.gen
1302 && conf.colorspace = cs
1303 && conf.angle = angle
1304 && tilevisible state.layout l.pageno x y
1305 then conttiling l.pageno pageopaque;
1307 preload state.layout;
1308 if gen = state.gen
1309 && conf.colorspace = cs
1310 && conf.angle = angle
1311 && tilevisible state.layout l.pageno x y
1312 && layoutready state.layout
1313 then postRedisplay "tile nothrottle";
1316 | Idle | Loading _ | Outlining _ ->
1317 dolog "Inconsistent tiling state";
1318 logcurrently state.currently;
1319 exit 1
1322 | "pdim", args ->
1323 let (n, w, h, _) as pdim =
1324 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1326 let pdim =
1327 match conf.fitmodel with
1328 | FitWidth -> pdim
1329 | FitPage | FitProportional ->
1330 match conf.columns with
1331 | Csplit _ -> (n, w, h, 0)
1332 | Csingle _ | Cmulti _ -> pdim
1334 state.pdims <- pdim :: state.pdims;
1335 state.uioh#infochanged Pdim
1337 | "o", args ->
1338 let (l, n, t, h, pos) =
1339 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1341 let s = String.sub args pos (String.length args - pos) in
1342 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1344 | "ou", args ->
1345 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1346 let s = String.sub args pos len in
1347 let pos2 = pos + len + 1 in
1348 let uri = String.sub args pos2 (String.length args - pos2) in
1349 addoutline (s, l, Ouri uri)
1351 | "on", args ->
1352 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1353 let s = String.sub args pos (String.length args - pos) in
1354 addoutline (s, l, Onone)
1356 | "a", args ->
1357 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1358 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1360 | "info", args ->
1361 let c, v = splitatchar args '\t' in
1362 let s =
1363 if nonemptystr v
1364 then
1365 if c = "Title"
1366 then (
1367 conf.title <- v;
1368 if not !ignoredoctitlte then Wsi.settitle v;
1369 args
1371 else
1372 if let len = String.length c in
1373 len > 6 && ((String.sub c (len-4) 4) = "date")
1374 then (
1375 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1376 then
1377 let b = Buffer.create 10 in
1378 Printf.bprintf b "%s\t" c;
1379 let sub p l c =
1381 Buffer.add_substring b v p l;
1382 Buffer.add_char b c;
1383 with exn -> Buffer.add_string b @@ exntos exn
1385 sub 2 4 '/';
1386 sub 6 2 '/';
1387 sub 8 2 ' ';
1388 sub 10 2 ':';
1389 sub 12 2 ':';
1390 sub 14 2 ' ';
1391 Buffer.add_char b '[';
1392 Buffer.add_string b v;
1393 Buffer.add_char b ']';
1394 Buffer.contents b
1395 else args
1397 else args
1398 else args
1400 state.docinfo <- (1, s) :: state.docinfo
1402 | "infoend", "" ->
1403 state.docinfo <- List.rev state.docinfo;
1404 state.uioh#infochanged Docinfo
1406 | "pass", args ->
1407 if args = "fail"
1408 then Wsi.settitle "Wrong password";
1409 let password = getpassword () in
1410 if emptystr password
1411 then error "document is password protected"
1412 else opendoc state.path password
1414 | _ -> error "unknown cmd `%S'" cmds
1417 let onhist cb =
1418 let rc = cb.rc in
1419 let action = function
1420 | HCprev -> cbget cb ~-1
1421 | HCnext -> cbget cb 1
1422 | HCfirst -> cbget cb ~-(cb.rc)
1423 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1424 and cancel () = cb.rc <- rc
1425 in (action, cancel)
1428 let search pattern forward =
1429 match conf.columns with
1430 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1431 | Csingle _ | Cmulti _ ->
1432 if nonemptystr pattern
1433 then
1434 let pn, py =
1435 match state.layout with
1436 | [] -> 0, 0
1437 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1439 wcmd "search %d %d %d %d,%s\000"
1440 (btod conf.icase) pn py (btod forward) pattern;
1443 let intentry text key =
1444 let text =
1445 if emptystr text && key = Keys.Ascii '-'
1446 then addchar text '-'
1447 else
1448 match [@warning "-4"] key with
1449 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1450 | _ ->
1451 state.text <- "invalid key";
1452 text
1454 TEcont text
1457 let linknact f s =
1458 if nonemptystr s
1459 then
1460 let n =
1461 let l = String.length s in
1462 let rec loop pos n =
1463 if pos = l
1464 then n
1465 else
1466 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1467 loop (pos+1) (n*26 + m)
1468 in loop 0 0
1470 let rec loop n = function
1471 | [] -> ()
1472 | l :: rest ->
1473 match getopaque l.pageno with
1474 | None -> loop n rest
1475 | Some opaque ->
1476 let m = getlinkcount opaque in
1477 if n < m
1478 then
1479 let under = getlink opaque n in
1480 f under
1481 else loop (n-m) rest
1483 loop n state.layout;
1486 let linknentry text key = match [@warning "-4"] key with
1487 | Keys.Ascii c ->
1488 let text = addchar text c in
1489 linknact (fun under -> state.text <- undertext under) text;
1490 TEcont text
1491 | _ ->
1492 state.text <- Printf.sprintf "invalid key";
1493 TEcont text
1496 let textentry text key = match [@warning "-4"] key with
1497 | Keys.Ascii c -> TEcont (addchar text c)
1498 | Keys.Code c -> TEcont (text ^ toutf8 c)
1499 | _ -> TEcont text
1502 let reqlayout angle fitmodel =
1503 if nogeomcmds state.geomcmds
1504 then state.anchor <- getanchor ();
1505 conf.angle <- angle mod 360;
1506 if conf.angle != 0
1507 then (
1508 match state.mode with
1509 | LinkNav _ -> state.mode <- View
1510 | Birdseye _ | Textentry _ | View -> ()
1512 conf.fitmodel <- fitmodel;
1513 invalidate "reqlayout"
1514 (fun () -> wcmd "reqlayout %d %d %d"
1515 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh));
1518 let settrim trimmargins trimfuzz =
1519 if nogeomcmds state.geomcmds
1520 then state.anchor <- getanchor ();
1521 conf.trimmargins <- trimmargins;
1522 conf.trimfuzz <- trimfuzz;
1523 let x0, y0, x1, y1 = trimfuzz in
1524 invalidate "settrim"
1525 (fun () -> wcmd "settrim %d %d %d %d %d"
1526 (btod conf.trimmargins) x0 y0 x1 y1);
1527 flushpages ();
1530 let setzoom zoom =
1531 let zoom = max 0.0001 zoom in
1532 if zoom <> conf.zoom
1533 then (
1534 state.prevzoom <- (conf.zoom, state.x);
1535 conf.zoom <- zoom;
1536 reshape state.winw state.winh;
1537 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1541 let pivotzoom ?(vw=min state.w state.winw)
1542 ?(vh=min (state.maxy-state.y) state.winh)
1543 ?(x=vw/2) ?(y=vh/2) zoom =
1544 let w = float state.w /. zoom in
1545 let hw = w /. 2.0 in
1546 let ratio = float vh /. float vw in
1547 let hh = hw *. ratio in
1548 let x0 = float x -. hw
1549 and y0 = float y -. hh in
1550 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1551 setzoom zoom;
1554 let pivotzoom ?vw ?vh ?x ?y zoom =
1555 if nogeomcmds state.geomcmds
1556 then
1557 if zoom > 1.0
1558 then pivotzoom ?vw ?vh ?x ?y zoom
1559 else setzoom zoom
1562 let setcolumns mode columns coverA coverB =
1563 state.prevcolumns <- Some (conf.columns, conf.zoom);
1564 if columns < 0
1565 then (
1566 if isbirdseye mode
1567 then impmsg "split mode doesn't work in bird's eye"
1568 else (
1569 conf.columns <- Csplit (-columns, E.a);
1570 state.x <- 0;
1571 conf.zoom <- 1.0;
1574 else (
1575 if columns < 2
1576 then (
1577 conf.columns <- Csingle E.a;
1578 state.x <- 0;
1579 setzoom 1.0;
1581 else (
1582 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1583 conf.zoom <- 1.0;
1586 reshape state.winw state.winh;
1589 let resetmstate () =
1590 state.mstate <- Mnone;
1591 Wsi.setcursor Wsi.CURSOR_INHERIT;
1594 let enterbirdseye () =
1595 let zoom = float conf.thumbw /. float state.winw in
1596 let birdseyepageno =
1597 let cy = state.winh / 2 in
1598 let fold = function
1599 | [] -> 0
1600 | l :: rest ->
1601 let rec fold best = function
1602 | [] -> best.pageno
1603 | l :: rest ->
1604 let d = cy - (l.pagedispy + l.pagevh/2)
1605 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1606 if abs d < abs dbest
1607 then fold l rest
1608 else best.pageno
1609 in fold l rest
1611 fold state.layout
1613 state.mode <-
1614 Birdseye (
1615 { conf with zoom = conf.zoom },
1616 state.x, birdseyepageno, -1, getanchor ()
1618 resetmstate ();
1619 conf.zoom <- zoom;
1620 conf.presentation <- false;
1621 conf.interpagespace <- 10;
1622 conf.hlinks <- false;
1623 conf.fitmodel <- FitPage;
1624 state.x <- 0;
1625 conf.columns <- (
1626 match conf.beyecolumns with
1627 | Some c ->
1628 conf.zoom <- 1.0;
1629 Cmulti ((c, 0, 0), E.a)
1630 | None -> Csingle E.a
1632 if conf.verbose
1633 then state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1634 (100.0*.zoom)
1635 else state.text <- E.s;
1636 reshape state.winw state.winh;
1639 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1640 state.mode <- View;
1641 conf.zoom <- c.zoom;
1642 conf.presentation <- c.presentation;
1643 conf.interpagespace <- c.interpagespace;
1644 conf.hlinks <- c.hlinks;
1645 conf.fitmodel <- c.fitmodel;
1646 conf.beyecolumns <- (
1647 match conf.columns with
1648 | Cmulti ((c, _, _), _) -> Some c
1649 | Csingle _ -> None
1650 | Csplit _ -> error "leaving bird's eye split mode"
1652 conf.columns <- (
1653 match c.columns with
1654 | Cmulti (c, _) -> Cmulti (c, E.a)
1655 | Csingle _ -> Csingle E.a
1656 | Csplit (c, _) -> Csplit (c, E.a)
1658 if conf.verbose
1659 then state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1660 (100.0*.conf.zoom);
1661 reshape state.winw state.winh;
1662 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1663 state.x <- leftx;
1666 let togglebirdseye () =
1667 match state.mode with
1668 | Birdseye vals -> leavebirdseye vals true
1669 | View -> enterbirdseye ()
1670 | Textentry _ | LinkNav _ -> ()
1673 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1674 let pageno = max 0 (pageno - incr) in
1675 let rec loop = function
1676 | [] -> gotopage1 pageno 0
1677 | l :: _ when l.pageno = pageno ->
1678 if l.pagedispy >= 0 && l.pagey = 0
1679 then postRedisplay "upbirdseye"
1680 else gotopage1 pageno 0
1681 | _ :: rest -> loop rest
1683 loop state.layout;
1684 state.text <- E.s;
1685 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1688 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1689 let pageno = min (state.pagecount - 1) (pageno + incr) in
1690 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1691 let rec loop = function
1692 | [] ->
1693 let y, h = getpageyh pageno in
1694 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1695 gotoxy state.x (clamp dy)
1696 | l :: _ when l.pageno = pageno ->
1697 if l.pagevh != l.pageh
1698 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1699 else postRedisplay "downbirdseye"
1700 | _ :: rest -> loop rest
1702 loop state.layout;
1703 state.text <- E.s;
1706 let optentry mode _ key =
1707 let btos b = if b then "on" else "off" in
1708 match [@warning "-4"] key with
1709 | Keys.Ascii 'C' ->
1710 let ondone s =
1712 let n, a, b = multicolumns_of_string s in
1713 setcolumns mode n a b;
1714 with exn ->
1715 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1717 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1719 | Keys.Ascii 'Z' ->
1720 let ondone s =
1722 let zoom = float (int_of_string s) /. 100.0 in
1723 pivotzoom zoom
1724 with exn ->
1725 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1727 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1729 | Keys.Ascii 'i' ->
1730 conf.icase <- not conf.icase;
1731 TEdone ("case insensitive search " ^ (btos conf.icase))
1733 | Keys.Ascii 'v' ->
1734 conf.verbose <- not conf.verbose;
1735 TEdone ("verbose " ^ (btos conf.verbose))
1737 | Keys.Ascii 'd' ->
1738 conf.debug <- not conf.debug;
1739 TEdone ("debug " ^ (btos conf.debug))
1741 | Keys.Ascii 'f' ->
1742 conf.underinfo <- not conf.underinfo;
1743 TEdone ("underinfo " ^ btos conf.underinfo)
1745 | Keys.Ascii 'T' ->
1746 settrim (not conf.trimmargins) conf.trimfuzz;
1747 TEdone ("trim margins " ^ btos conf.trimmargins)
1749 | Keys.Ascii 'I' ->
1750 conf.invert <- not conf.invert;
1751 TEdone ("invert colors " ^ btos conf.invert)
1753 | Keys.Ascii 'x' ->
1754 let ondone s =
1755 cbput state.hists.sel s;
1756 conf.selcmd <- s;
1758 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1759 textentry, ondone, true)
1761 | Keys.Ascii 'M' ->
1762 if conf.pax == None
1763 then conf.pax <- Some 0.0
1764 else conf.pax <- None;
1765 TEdone ("PAX " ^ btos (conf.pax != None))
1767 | (Keys.Ascii c) ->
1768 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1769 TEstop
1771 | _ -> TEcont state.text
1774 let adderrmsg src msg =
1775 Buffer.add_string state.errmsgs msg;
1776 state.newerrmsgs <- true;
1777 postRedisplay src
1780 let adderrfmt src fmt = Format.ksprintf (fun s -> adderrmsg src s) fmt;;
1782 class outlinelistview ~zebra ~source =
1783 let settext autonarrow s =
1784 if autonarrow
1785 then
1786 let ss = source#statestr in
1787 state.text <- if emptystr ss
1788 then "[" ^ s ^ "]"
1789 else "{" ^ ss ^ "} [" ^ s ^ "]"
1790 else state.text <- s
1792 object (self)
1793 inherit listview
1794 ~zebra
1795 ~helpmode:false
1796 ~source:(source :> lvsource)
1797 ~trusted:false
1798 ~modehash:(findkeyhash conf "outline")
1799 as super
1801 val m_autonarrow = false
1803 method! key key mask =
1804 let maxrows =
1805 if emptystr state.text
1806 then fstate.maxrows
1807 else fstate.maxrows - 2
1809 let calcfirst first active =
1810 if active > first
1811 then
1812 let rows = active - first in
1813 if rows > maxrows then active - maxrows else first
1814 else active
1816 let navigate incr =
1817 let active = m_active + incr in
1818 let active = bound active 0 (source#getitemcount - 1) in
1819 let first = calcfirst m_first active in
1820 postRedisplay "outline navigate";
1821 coe {< m_active = active; m_first = first >}
1823 let navscroll first =
1824 let active =
1825 let dist = m_active - first in
1826 if dist < 0
1827 then first
1828 else (
1829 if dist < maxrows
1830 then m_active
1831 else first + maxrows
1834 postRedisplay "outline navscroll";
1835 coe {< m_first = first; m_active = active >}
1837 let ctrl = Wsi.withctrl mask in
1838 let open Keys in
1839 match Wsi.kc2kt key with
1840 | Ascii 'a' when ctrl ->
1841 let text =
1842 if m_autonarrow
1843 then (
1844 source#denarrow;
1847 else (
1848 let pattern = source#renarrow in
1849 if nonemptystr m_qsearch
1850 then (source#narrow m_qsearch; m_qsearch)
1851 else pattern
1854 settext (not m_autonarrow) text;
1855 postRedisplay "toggle auto narrowing";
1856 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1858 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1859 settext true E.s;
1860 postRedisplay "toggle auto narrowing";
1861 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1863 | Ascii 'n' when ctrl ->
1864 source#narrow m_qsearch;
1865 if not m_autonarrow
1866 then source#add_narrow_pattern m_qsearch;
1867 postRedisplay "outline ctrl-n";
1868 coe {< m_first = 0; m_active = 0 >}
1870 | Ascii 'S' when ctrl ->
1871 let active = source#calcactive (getanchor ()) in
1872 let first = firstof m_first active in
1873 postRedisplay "outline ctrl-s";
1874 coe {< m_first = first; m_active = active >}
1876 | Ascii 'u' when ctrl ->
1877 postRedisplay "outline ctrl-u";
1878 if m_autonarrow && nonemptystr m_qsearch
1879 then (
1880 ignore (source#renarrow);
1881 settext m_autonarrow E.s;
1882 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1884 else (
1885 source#del_narrow_pattern;
1886 let pattern = source#renarrow in
1887 let text =
1888 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1890 settext m_autonarrow text;
1891 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1894 | Ascii 'l' when ctrl ->
1895 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1896 postRedisplay "outline ctrl-l";
1897 coe {< m_first = first >}
1899 | Ascii '\t' when m_autonarrow ->
1900 if nonemptystr m_qsearch
1901 then (
1902 postRedisplay "outline list view tab";
1903 source#add_narrow_pattern m_qsearch;
1904 settext true E.s;
1905 coe {< m_qsearch = E.s >}
1907 else coe self
1909 | Escape when m_autonarrow ->
1910 if nonemptystr m_qsearch
1911 then source#add_narrow_pattern m_qsearch;
1912 super#key key mask
1914 | Enter when m_autonarrow ->
1915 if nonemptystr m_qsearch
1916 then source#add_narrow_pattern m_qsearch;
1917 super#key key mask
1919 | (Ascii _ | Code _) when m_autonarrow ->
1920 let pattern = m_qsearch ^ toutf8 key in
1921 postRedisplay "outlinelistview autonarrow add";
1922 source#narrow pattern;
1923 settext true pattern;
1924 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1926 | Backspace when m_autonarrow ->
1927 if emptystr m_qsearch
1928 then coe self
1929 else
1930 let pattern = withoutlastutf8 m_qsearch in
1931 postRedisplay "outlinelistview autonarrow backspace";
1932 ignore (source#renarrow);
1933 source#narrow pattern;
1934 settext true pattern;
1935 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1937 | Up when ctrl -> navscroll (max 0 (m_first - 1))
1939 | Down when ctrl ->
1940 navscroll (min (source#getitemcount - 1) (m_first + 1))
1942 | Up -> navigate ~-1
1943 | Down -> navigate 1
1944 | Prior -> navigate ~-(fstate.maxrows)
1945 | Next -> navigate fstate.maxrows
1947 | Right ->
1948 let o =
1949 if ctrl
1950 then (
1951 postRedisplay "outline ctrl right";
1952 {< m_pan = m_pan + 1 >}
1954 else self#updownlevel 1
1956 coe o
1958 | Left ->
1959 let o =
1960 if ctrl
1961 then (
1962 postRedisplay "outline ctrl left";
1963 {< m_pan = m_pan - 1 >}
1965 else self#updownlevel ~-1
1967 coe o
1969 | Home ->
1970 postRedisplay "outline home";
1971 coe {< m_first = 0; m_active = 0 >}
1973 | End ->
1974 let active = source#getitemcount - 1 in
1975 let first = max 0 (active - fstate.maxrows) in
1976 postRedisplay "outline end";
1977 coe {< m_active = active; m_first = first >}
1979 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
1980 super#key key mask
1981 end;;
1983 let genhistoutlines () =
1984 Config.gethist ()
1985 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
1986 compare c2.lastvisit c1.lastvisit)
1987 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
1988 let path = if nonemptystr origin then origin else path in
1989 let base = mbtoutf8 @@ Filename.basename path in
1990 (base ^ "\000" ^ c.title, 1, Ohistory hist)
1994 let gotohist (path, c, bookmarks, x, anchor, origin) =
1995 Config.save leavebirdseye;
1996 state.anchor <- anchor;
1997 state.bookmarks <- bookmarks;
1998 state.origin <- origin;
1999 state.x <- x;
2000 setconf conf c;
2001 let x0, y0, x1, y1 = conf.trimfuzz in
2002 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2003 reshape ~firsttime:true state.winw state.winh;
2004 opendoc path origin;
2005 setzoom c.zoom;
2008 let setcheckers enabled =
2009 match !checkerstexid with
2010 | None -> if enabled then checkerstexid := Some (makecheckers ())
2011 | Some id ->
2012 if not enabled
2013 then (
2014 GlTex.delete_texture id;
2015 checkerstexid := None;
2019 let describe_layout layout =
2020 let d =
2021 match layout with
2022 | [] -> "Page 0"
2023 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2024 | l :: rest ->
2025 let rangestr a b =
2026 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2027 else Printf.sprintf "%d%s%d" (a.pageno+1)
2028 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
2029 (b.pageno+1)
2031 let rec fold s la lb = function
2032 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2033 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2034 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2036 fold "Pages" l l rest
2038 let percent =
2039 let maxy = maxy () in
2040 if maxy <= 0
2041 then 100.
2042 else 100. *. (float state.y /. float maxy)
2044 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2047 let setpresentationmode v =
2048 let n = page_of_y state.y in
2049 state.anchor <- (n, 0.0, 1.0);
2050 conf.presentation <- v;
2051 if conf.fitmodel = FitPage
2052 then reqlayout conf.angle conf.fitmodel;
2053 represent ();
2056 let enterinfomode =
2057 let btos b = if b then Utf8syms.radical else E.s in
2058 let showextended = ref false in
2059 let showcolors = ref false in
2060 let leave mode _ = state.mode <- mode in
2061 let src =
2062 (object
2063 val mutable m_l = []
2064 val mutable m_a = E.a
2065 val mutable m_prev_uioh = nouioh
2066 val mutable m_prev_mode = View
2068 inherit lvsourcebase
2070 method reset prev_mode prev_uioh =
2071 m_a <- Array.of_list (List.rev m_l);
2072 m_l <- [];
2073 m_prev_mode <- prev_mode;
2074 m_prev_uioh <- prev_uioh;
2076 method int name get set =
2077 m_l <-
2078 (name, `int get, 1,
2079 Action (
2080 fun u ->
2081 let ondone s =
2082 try set (int_of_string s)
2083 with exn ->
2084 state.text <- Printf.sprintf "bad integer `%s': %s"
2085 s @@ exntos exn
2087 state.text <- E.s;
2088 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2089 state.mode <- Textentry (te, leave m_prev_mode);
2091 )) :: m_l
2093 method int_with_suffix name get set =
2094 m_l <-
2095 (name, `intws get, 1,
2096 Action (
2097 fun u ->
2098 let ondone s =
2099 try set (int_of_string_with_suffix s)
2100 with exn ->
2101 state.text <- Printf.sprintf "bad integer `%s': %s"
2102 s @@ exntos exn
2104 state.text <- E.s;
2105 let te =
2106 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2108 state.mode <- Textentry (te, leave m_prev_mode);
2110 )) :: m_l
2112 method bool ?(offset=1) ?(btos=btos) name get set =
2113 m_l <-
2114 (name, `bool (btos, get), offset, Action (
2115 fun u ->
2116 let v = get () in
2117 set (not v);
2119 )) :: m_l
2121 method color name get set =
2122 m_l <-
2123 (name, `color get, 1,
2124 Action (
2125 fun u ->
2126 let invalid = (nan, nan, nan) in
2127 let ondone s =
2128 let c =
2129 try color_of_string s
2130 with exn ->
2131 state.text <- Printf.sprintf "bad color `%s': %s"
2132 s @@ exntos exn;
2133 invalid
2135 if c <> invalid
2136 then set c;
2138 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2139 state.text <- color_to_string (get ());
2140 state.mode <- Textentry (te, leave m_prev_mode);
2142 )) :: m_l
2144 method string name get set =
2145 m_l <-
2146 (name, `string get, 1,
2147 Action (
2148 fun u ->
2149 let ondone s = set s in
2150 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2151 state.mode <- Textentry (te, leave m_prev_mode);
2153 )) :: m_l
2155 method colorspace name get set =
2156 m_l <-
2157 (name, `string get, 1,
2158 Action (
2159 fun _ ->
2160 let source =
2161 (object
2162 inherit lvsourcebase
2164 initializer
2165 m_active <- CSTE.to_int conf.colorspace;
2166 m_first <- 0;
2168 method getitemcount =
2169 Array.length CSTE.names
2170 method getitem n =
2171 (CSTE.names.(n), 0)
2172 method exit ~uioh ~cancel ~active ~first ~pan =
2173 ignore (uioh, first, pan);
2174 if not cancel then set active;
2175 None
2176 method hasaction _ = true
2177 end)
2179 state.text <- E.s;
2180 let modehash = findkeyhash conf "info" in
2181 coe (new listview ~zebra:false ~helpmode:false
2182 ~source ~trusted:true ~modehash)
2183 )) :: m_l
2185 method paxmark name get set =
2186 m_l <-
2187 (name, `string get, 1,
2188 Action (
2189 fun _ ->
2190 let source =
2191 (object
2192 inherit lvsourcebase
2194 initializer
2195 m_active <- MTE.to_int conf.paxmark;
2196 m_first <- 0;
2198 method getitemcount = Array.length MTE.names
2199 method getitem n = (MTE.names.(n), 0)
2200 method exit ~uioh ~cancel ~active ~first ~pan =
2201 ignore (uioh, first, pan);
2202 if not cancel then set active;
2203 None
2204 method hasaction _ = true
2205 end)
2207 state.text <- E.s;
2208 let modehash = findkeyhash conf "info" in
2209 coe (new listview ~zebra:false ~helpmode:false
2210 ~source ~trusted:true ~modehash)
2211 )) :: m_l
2213 method fitmodel name get set =
2214 m_l <-
2215 (name, `string get, 1,
2216 Action (
2217 fun _ ->
2218 let source =
2219 (object
2220 inherit lvsourcebase
2222 initializer
2223 m_active <- FMTE.to_int conf.fitmodel;
2224 m_first <- 0;
2226 method getitemcount = Array.length FMTE.names
2227 method getitem n = (FMTE.names.(n), 0)
2228 method exit ~uioh ~cancel ~active ~first ~pan =
2229 ignore (uioh, first, pan);
2230 if not cancel then set active;
2231 None
2232 method hasaction _ = true
2233 end)
2235 state.text <- E.s;
2236 let modehash = findkeyhash conf "info" in
2237 coe (new listview ~zebra:false ~helpmode:false
2238 ~source ~trusted:true ~modehash)
2239 )) :: m_l
2241 method caption s offset =
2242 m_l <- (s, `empty, offset, Noaction) :: m_l
2244 method caption2 s f offset =
2245 m_l <- (s, `string f, offset, Noaction) :: m_l
2247 method getitemcount = Array.length m_a
2249 method getitem n =
2250 let tostr = function
2251 | `int f -> string_of_int (f ())
2252 | `intws f -> string_with_suffix_of_int (f ())
2253 | `string f -> f ()
2254 | `color f -> color_to_string (f ())
2255 | `bool (btos, f) -> btos (f ())
2256 | `empty -> E.s
2258 let name, t, offset, _ = m_a.(n) in
2259 ((let s = tostr t in
2260 if nonemptystr s
2261 then Printf.sprintf "%s\t%s" name s
2262 else name),
2263 offset)
2265 method exit ~uioh ~cancel ~active ~first ~pan =
2266 let uiohopt =
2267 if not cancel
2268 then (
2269 let uioh =
2270 match m_a.(active) with
2271 | _, _, _, Action f -> f uioh
2272 | _, _, _, Noaction -> uioh
2274 Some uioh
2276 else None
2278 m_active <- active;
2279 m_first <- first;
2280 m_pan <- pan;
2281 uiohopt
2283 method hasaction n =
2284 match m_a.(n) with
2285 | _, _, _, Action _ -> true
2286 | _, _, _, Noaction -> false
2288 initializer m_active <- 1
2289 end)
2291 let rec fillsrc prevmode prevuioh =
2292 let sep () = src#caption E.s 0 in
2293 let colorp name get set =
2294 src#string name
2295 (fun () -> color_to_string (get ()))
2296 (fun v ->
2297 try set @@ color_of_string v
2298 with exn ->
2299 state.text <-
2300 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2303 let rgba name get set =
2304 src#string name
2305 (fun () -> get () |> rgba_to_string)
2306 (fun v ->
2307 try set @@ rgba_of_string v
2308 with exn ->
2309 state.text <-
2310 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2313 let oldmode = state.mode in
2314 let birdseye = isbirdseye state.mode in
2316 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2318 src#bool "presentation mode"
2319 (fun () -> conf.presentation)
2320 (fun v -> setpresentationmode v);
2322 src#bool "ignore case in searches"
2323 (fun () -> conf.icase)
2324 (fun v -> conf.icase <- v);
2326 src#bool "preload"
2327 (fun () -> conf.preload)
2328 (fun v -> conf.preload <- v);
2330 src#bool "highlight links"
2331 (fun () -> conf.hlinks)
2332 (fun v -> conf.hlinks <- v);
2334 src#bool "under info"
2335 (fun () -> conf.underinfo)
2336 (fun v -> conf.underinfo <- v);
2338 src#fitmodel "fit model"
2339 (fun () -> FMTE.to_string conf.fitmodel)
2340 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2342 src#bool "trim margins"
2343 (fun () -> conf.trimmargins)
2344 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2346 sep ();
2347 src#int "inter-page space"
2348 (fun () -> conf.interpagespace)
2349 (fun n ->
2350 conf.interpagespace <- n;
2351 docolumns conf.columns;
2352 let pageno, py =
2353 match state.layout with
2354 | [] -> 0, 0
2355 | l :: _ -> l.pageno, l.pagey
2357 state.maxy <- calcheight ();
2358 let y = getpagey pageno in
2359 gotoxy state.x (y + py)
2362 src#int "page bias"
2363 (fun () -> conf.pagebias)
2364 (fun v -> conf.pagebias <- v);
2366 src#int "scroll step"
2367 (fun () -> conf.scrollstep)
2368 (fun n -> conf.scrollstep <- n);
2370 src#int "horizontal scroll step"
2371 (fun () -> conf.hscrollstep)
2372 (fun v -> conf.hscrollstep <- v);
2374 src#int "auto scroll step"
2375 (fun () ->
2376 match state.autoscroll with
2377 | Some step -> step
2378 | _ -> conf.autoscrollstep)
2379 (fun n ->
2380 let n = boundastep state.winh n in
2381 if state.autoscroll <> None
2382 then state.autoscroll <- Some n;
2383 conf.autoscrollstep <- n);
2385 src#int "zoom"
2386 (fun () -> truncate (conf.zoom *. 100.))
2387 (fun v -> pivotzoom ((float v) /. 100.));
2389 src#int "rotation"
2390 (fun () -> conf.angle)
2391 (fun v -> reqlayout v conf.fitmodel);
2393 src#int "scroll bar width"
2394 (fun () -> conf.scrollbw)
2395 (fun v ->
2396 conf.scrollbw <- v;
2397 reshape state.winw state.winh;
2400 src#int "scroll handle height"
2401 (fun () -> conf.scrollh)
2402 (fun v -> conf.scrollh <- v;);
2404 src#int "thumbnail width"
2405 (fun () -> conf.thumbw)
2406 (fun v ->
2407 conf.thumbw <- min 4096 v;
2408 match oldmode with
2409 | Birdseye beye ->
2410 leavebirdseye beye false;
2411 enterbirdseye ()
2412 | Textentry _
2413 | View
2414 | LinkNav _ -> ()
2417 let mode = state.mode in
2418 src#string "columns"
2419 (fun () ->
2420 match conf.columns with
2421 | Csingle _ -> "1"
2422 | Cmulti (multi, _) -> multicolumns_to_string multi
2423 | Csplit (count, _) -> "-" ^ string_of_int count
2425 (fun v ->
2426 let n, a, b = multicolumns_of_string v in
2427 setcolumns mode n a b);
2429 sep ();
2430 src#caption "Pixmap cache" 0;
2431 src#int_with_suffix "size (advisory)"
2432 (fun () -> conf.memlimit)
2433 (fun v -> conf.memlimit <- v);
2435 src#caption2 "used"
2436 (fun () ->
2437 Printf.sprintf "%s bytes, %d tiles"
2438 (string_with_suffix_of_int state.memused)
2439 (Hashtbl.length state.tilemap)) 1;
2441 sep ();
2442 src#caption "Layout" 0;
2443 src#caption2 "Dimension"
2444 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2445 state.winw state.winh
2446 state.w state.maxy)
2448 if conf.debug
2449 then src#caption2 "Position" (fun () ->
2450 Printf.sprintf "%dx%d" state.x state.y
2452 else src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2454 sep ();
2455 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2456 "Save these parameters as global defaults at exit"
2457 (fun () -> conf.bedefault)
2458 (fun v -> conf.bedefault <- v);
2460 sep ();
2461 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2462 src#bool ~offset:0 ~btos "Extended parameters"
2463 (fun () -> !showextended)
2464 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2465 if !showextended
2466 then (
2467 src#bool "checkers"
2468 (fun () -> conf.checkers)
2469 (fun v -> conf.checkers <- v; setcheckers v);
2470 src#bool "update cursor"
2471 (fun () -> conf.updatecurs)
2472 (fun v -> conf.updatecurs <- v);
2473 src#bool "scroll-bar on the left"
2474 (fun () -> conf.leftscroll)
2475 (fun v -> conf.leftscroll <- v);
2476 src#bool "verbose"
2477 (fun () -> conf.verbose)
2478 (fun v -> conf.verbose <- v);
2479 src#bool "invert colors"
2480 (fun () -> conf.invert)
2481 (fun v -> conf.invert <- v);
2482 src#bool "max fit"
2483 (fun () -> conf.maxhfit)
2484 (fun v -> conf.maxhfit <- v);
2485 src#bool "pax mode"
2486 (fun () -> conf.pax != None)
2487 (fun v ->
2488 if v
2489 then conf.pax <- Some (now ())
2490 else conf.pax <- None);
2491 src#string "uri launcher"
2492 (fun () -> conf.urilauncher)
2493 (fun v -> conf.urilauncher <- v);
2494 src#string "path launcher"
2495 (fun () -> conf.pathlauncher)
2496 (fun v -> conf.pathlauncher <- v);
2497 src#string "tile size"
2498 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2499 (fun v ->
2501 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2502 conf.tilew <- max 64 w;
2503 conf.tileh <- max 64 h;
2504 flushtiles ();
2505 with exn ->
2506 state.text <- Printf.sprintf "bad tile size `%s': %s"
2507 v @@ exntos exn
2509 src#int "texture count"
2510 (fun () -> conf.texcount)
2511 (fun v ->
2512 if realloctexts v
2513 then conf.texcount <- v
2514 else impmsg "failed to set texture count please retry later"
2516 src#int "slice height"
2517 (fun () -> conf.sliceheight)
2518 (fun v ->
2519 conf.sliceheight <- v;
2520 wcmd "sliceh %d" conf.sliceheight;
2522 src#int "anti-aliasing level"
2523 (fun () -> conf.aalevel)
2524 (fun v ->
2525 conf.aalevel <- bound v 0 8;
2526 state.anchor <- getanchor ();
2527 opendoc state.path state.password;
2529 src#string "page scroll scaling factor"
2530 (fun () -> string_of_float conf.pgscale)
2531 (fun v ->
2532 try conf.pgscale <- float_of_string v
2533 with exn ->
2534 state.text <-
2535 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2536 @@ exntos exn
2538 src#int "ui font size"
2539 (fun () -> fstate.fontsize)
2540 (fun v -> setfontsize (bound v 5 100));
2541 src#int "hint font size"
2542 (fun () -> conf.hfsize)
2543 (fun v -> conf.hfsize <- bound v 5 100);
2544 src#string "trim fuzz"
2545 (fun () -> irect_to_string conf.trimfuzz)
2546 (fun v ->
2548 conf.trimfuzz <- irect_of_string v;
2549 if conf.trimmargins
2550 then settrim true conf.trimfuzz;
2551 with exn ->
2552 state.text <- Printf.sprintf "bad irect `%s': %s" v
2553 @@ exntos exn
2555 src#string "selection command"
2556 (fun () -> conf.selcmd)
2557 (fun v -> conf.selcmd <- v);
2558 src#string "synctex command"
2559 (fun () -> conf.stcmd)
2560 (fun v -> conf.stcmd <- v);
2561 src#string "pax command"
2562 (fun () -> conf.paxcmd)
2563 (fun v -> conf.paxcmd <- v);
2564 src#string "ask password command"
2565 (fun () -> conf.passcmd)
2566 (fun v -> conf.passcmd <- v);
2567 src#string "save path command"
2568 (fun () -> conf.savecmd)
2569 (fun v -> conf.savecmd <- v);
2570 src#colorspace "color space"
2571 (fun () -> CSTE.to_string conf.colorspace)
2572 (fun v ->
2573 conf.colorspace <- CSTE.of_int v;
2574 wcmd "cs %d" v;
2575 load state.layout;
2577 src#paxmark "pax mark method"
2578 (fun () -> MTE.to_string conf.paxmark)
2579 (fun v -> conf.paxmark <- MTE.of_int v);
2580 if bousable ()
2581 then
2582 src#bool "use PBO"
2583 (fun () -> conf.usepbo)
2584 (fun v -> conf.usepbo <- v);
2585 src#bool "mouse wheel scrolls pages"
2586 (fun () -> conf.wheelbypage)
2587 (fun v -> conf.wheelbypage <- v);
2588 src#bool "open remote links in a new instance"
2589 (fun () -> conf.riani)
2590 (fun v -> conf.riani <- v);
2591 src#bool "edit annotations inline"
2592 (fun () -> conf.annotinline)
2593 (fun v -> conf.annotinline <- v);
2594 src#bool "coarse positioning in presentation mode"
2595 (fun () -> conf.coarseprespos)
2596 (fun v -> conf.coarseprespos <- v);
2597 src#bool "use document CSS"
2598 (fun () -> conf.usedoccss)
2599 (fun v ->
2600 conf.usedoccss <- v;
2601 state.anchor <- getanchor ();
2602 opendoc state.path state.password;
2604 src#bool ~btos "colors"
2605 (fun () -> !showcolors)
2606 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2607 if !showcolors
2608 then (
2609 colorp " background"
2610 (fun () -> conf.bgcolor)
2611 (fun v -> conf.bgcolor <- v);
2612 rgba " scrollbar"
2613 (fun () -> conf.sbarcolor)
2614 (fun v -> conf.sbarcolor <- v);
2615 rgba " scrollbar handle"
2616 (fun () -> conf.sbarhndlcolor)
2617 (fun v -> conf.sbarhndlcolor <- v);
2621 sep ();
2622 src#caption "Document" 0;
2623 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2624 src#caption2 "Pages" (fun () -> string_of_int state.pagecount) 1;
2625 src#caption2 "Dimensions"
2626 (fun () -> string_of_int (List.length state.pdims)) 1;
2627 if nonemptystr conf.css
2628 then src#caption2 "CSS" (fun () -> conf.css) 1;
2629 if conf.trimmargins
2630 then (
2631 sep ();
2632 src#caption "Trimmed margins" 0;
2633 src#caption2 "Dimensions"
2634 (fun () -> string_of_int (List.length state.pdims)) 1;
2637 sep ();
2638 src#caption "OpenGL" 0;
2639 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2640 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2642 sep ();
2643 src#caption "Location" 0;
2644 if nonemptystr state.origin
2645 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2646 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2648 src#reset prevmode prevuioh;
2650 fun () -> (
2651 state.text <- E.s;
2652 resetmstate ();
2653 let prevmode = state.mode
2654 and prevuioh = state.uioh in
2655 fillsrc prevmode prevuioh;
2656 let source = (src :> lvsource) in
2657 let modehash = findkeyhash conf "info" in
2658 state.uioh <-
2659 coe (object (self)
2660 inherit listview ~zebra:false ~helpmode:false
2661 ~source ~trusted:true ~modehash as super
2662 val mutable m_prevmemused = 0
2663 method! infochanged = function
2664 | Memused ->
2665 if m_prevmemused != state.memused
2666 then (
2667 m_prevmemused <- state.memused;
2668 postRedisplay "memusedchanged";
2670 | Pdim -> postRedisplay "pdimchanged"
2671 | Docinfo -> fillsrc prevmode prevuioh
2673 method! key key mask =
2674 if not (Wsi.withctrl mask)
2675 then
2676 match [@warning "-4"] Wsi.kc2kt key with
2677 | Keys.Left -> coe (self#updownlevel ~-1)
2678 | Keys.Right -> coe (self#updownlevel 1)
2679 | _ -> super#key key mask
2680 else super#key key mask
2681 end);
2682 postRedisplay "info";
2686 let enterhelpmode =
2687 let source =
2688 (object
2689 inherit lvsourcebase
2690 method getitemcount = Array.length state.help
2691 method getitem n =
2692 let s, l, _ = state.help.(n) in
2693 (s, l)
2695 method exit ~uioh ~cancel ~active ~first ~pan =
2696 let optuioh =
2697 if not cancel
2698 then (
2699 match state.help.(active) with
2700 | _, _, Action f -> Some (f uioh)
2701 | _, _, Noaction -> Some uioh
2703 else None
2705 m_active <- active;
2706 m_first <- first;
2707 m_pan <- pan;
2708 optuioh
2710 method hasaction n =
2711 match state.help.(n) with
2712 | _, _, Action _ -> true
2713 | _, _, Noaction -> false
2715 initializer
2716 m_active <- -1
2717 end)
2719 fun () ->
2720 let modehash = findkeyhash conf "help" in
2721 resetmstate ();
2722 state.uioh <- coe (new listview
2723 ~zebra:false ~helpmode:true
2724 ~source ~trusted:true ~modehash);
2725 postRedisplay "help";
2728 let entermsgsmode =
2729 let msgsource =
2730 (object
2731 inherit lvsourcebase
2732 val mutable m_items = E.a
2734 method getitemcount = 1 + Array.length m_items
2736 method getitem n =
2737 if n = 0
2738 then "[Clear]", 0
2739 else m_items.(n-1), 0
2741 method exit ~uioh ~cancel ~active ~first ~pan =
2742 ignore uioh;
2743 if not cancel
2744 then (
2745 if active = 0
2746 then Buffer.clear state.errmsgs;
2748 m_active <- active;
2749 m_first <- first;
2750 m_pan <- pan;
2751 None
2753 method hasaction n =
2754 n = 0
2756 method reset =
2757 state.newerrmsgs <- false;
2758 let l = Str.split Utils.Re.crlf (Buffer.contents state.errmsgs) in
2759 m_items <- Array.of_list l
2761 initializer
2762 m_active <- 0
2763 end)
2764 in fun () ->
2765 state.text <- E.s;
2766 resetmstate ();
2767 msgsource#reset;
2768 let source = (msgsource :> lvsource) in
2769 let modehash = findkeyhash conf "listview" in
2770 state.uioh <-
2771 coe (object
2772 inherit listview ~zebra:false ~helpmode:false
2773 ~source ~trusted:false ~modehash as super
2774 method! display =
2775 if state.newerrmsgs
2776 then msgsource#reset;
2777 super#display
2778 end);
2779 postRedisplay "msgs";
2782 let getusertext s =
2783 let editor = getenvdef "EDITOR" E.s in
2784 if emptystr editor
2785 then E.s
2786 else
2787 let tmppath = Filename.temp_file "llpp" "note" in
2788 if nonemptystr s
2789 then (
2790 let oc = open_out tmppath in
2791 output_string oc s;
2792 close_out oc;
2794 let execstr = editor ^ " " ^ tmppath in
2795 let s =
2796 match spawn execstr [] with
2797 | exception exn ->
2798 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
2800 | pid ->
2801 match Unix.waitpid [] pid with
2802 | exception exn ->
2803 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
2805 | (_pid, status) ->
2806 match status with
2807 | Unix.WEXITED 0 -> filecontents tmppath
2808 | Unix.WEXITED n ->
2809 impmsg "editor process(%s) exited abnormally: %d" execstr n;
2811 | Unix.WSIGNALED n ->
2812 impmsg "editor process(%s) was killed by signal %d" execstr n;
2814 | Unix.WSTOPPED n ->
2815 impmsg "editor(%s) process was stopped by signal %d" execstr n;
2818 match Unix.unlink tmppath with
2819 | exception exn ->
2820 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
2822 | () -> s
2825 let enterannotmode opaque slinkindex =
2826 let msgsource =
2827 (object
2828 inherit lvsourcebase
2829 val mutable m_text = E.s
2830 val mutable m_items = E.a
2832 method getitemcount = Array.length m_items
2834 method getitem n =
2835 let label, _func = m_items.(n) in
2836 label, 0
2838 method exit ~uioh ~cancel ~active ~first ~pan =
2839 ignore (uioh, first, pan);
2840 if not cancel
2841 then (
2842 let _label, func = m_items.(active) in
2843 func ()
2845 None
2847 method hasaction n = nonemptystr @@ fst m_items.(n)
2849 method reset s =
2850 let rec split accu b i =
2851 let p = b+i in
2852 if p = String.length s
2853 then (String.sub s b (p-b), unit) :: accu
2854 else
2855 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2856 then
2857 let ss = if i = 0 then E.s else String.sub s b i in
2858 split ((ss, unit)::accu) (p+1) 0
2859 else split accu b (i+1)
2861 let cleanup () =
2862 wcmd "freepage %s" (~> opaque);
2863 let keys =
2864 Hashtbl.fold (fun key opaque' accu ->
2865 if opaque' = opaque'
2866 then key :: accu else accu) state.pagemap []
2868 List.iter (Hashtbl.remove state.pagemap) keys;
2869 flushtiles ();
2870 gotoxy state.x state.y
2872 let dele () =
2873 delannot opaque slinkindex;
2874 cleanup ();
2876 let edit inline () =
2877 let update s =
2878 if emptystr s
2879 then dele ()
2880 else (
2881 modannot opaque slinkindex s;
2882 cleanup ();
2885 if inline
2886 then
2887 let mode = state.mode in
2888 state.mode <-
2889 Textentry (
2890 ("annotation: ", m_text, None, textentry, update, true),
2891 fun _ -> state.mode <- mode
2893 state.text <- E.s;
2894 enttext ();
2895 else
2896 let s = getusertext m_text in
2897 update s
2899 m_text <- s;
2900 m_items <-
2901 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
2902 :: ("[Delete]", dele)
2903 :: ("[Edit]", edit conf.annotinline)
2904 :: (E.s, unit)
2905 :: split [] 0 0 |> List.rev |> Array.of_list
2907 initializer
2908 m_active <- 0
2909 end)
2911 state.text <- E.s;
2912 let s = getannotcontents opaque slinkindex in
2913 resetmstate ();
2914 msgsource#reset s;
2915 let source = (msgsource :> lvsource) in
2916 let modehash = findkeyhash conf "listview" in
2917 state.uioh <- coe (object
2918 inherit listview ~zebra:false ~helpmode:false
2919 ~source ~trusted:false ~modehash
2920 end);
2921 postRedisplay "enterannotmode";
2924 let gotoremote spec =
2925 let filename, dest = splitatchar spec '#' in
2926 let getpath filename =
2927 let path =
2928 if nonemptystr filename
2929 then
2930 if Filename.is_relative filename
2931 then
2932 let dir = Filename.dirname state.path in
2933 let dir =
2934 if Filename.is_implicit dir
2935 then Filename.concat (Sys.getcwd ()) dir
2936 else dir
2938 Filename.concat dir filename
2939 else filename
2940 else E.s
2942 if Sys.file_exists path
2943 then path
2944 else E.s
2946 let path = getpath filename in
2947 let dospawn lcmd =
2948 if conf.riani
2949 then
2950 let cmd = Lazy.force_val lcmd in
2951 match spawn cmd with
2952 | _pid -> ()
2953 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
2954 else
2955 let anchor = getanchor () in
2956 let ranchor = state.path, state.password, anchor, state.origin in
2957 state.origin <- E.s;
2958 state.ranchors <- ranchor :: state.ranchors;
2959 opendoc path E.s;
2961 if substratis spec 0 "page="
2962 then
2963 match Scanf.sscanf spec "page=%d" (fun n -> n) with
2964 | pageno ->
2965 state.anchor <- (pageno, 0.0, 0.0);
2966 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
2967 | exception exn ->
2968 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
2969 else (
2970 state.nameddest <- dest;
2971 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
2975 let gotounder = function
2976 | Ulinkuri s when isexternallink s ->
2977 if substratis s 0 "file://"
2978 then gotoremote @@ String.sub s 7 (String.length s - 7)
2979 else Help.gotouri conf.urilauncher s
2980 | Ulinkuri s ->
2981 let pageno, x, y = uritolocation s in
2982 addnav ();
2983 gotopagexy pageno x y
2984 | Utext _ | Unone -> ()
2985 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
2988 let gotooutline (_, _, kind) =
2989 match kind with
2990 | Onone -> ()
2991 | Oanchor anchor ->
2992 let (pageno, y, _) = anchor in
2993 let y = getanchory
2994 (if conf.presentation then (pageno, y, 1.0) else anchor)
2996 addnav ();
2997 gotoxy state.x y
2998 | Ouri uri -> gotounder (Ulinkuri uri)
2999 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
3000 | Oremote (remote, pageno) ->
3001 error "gotounder (Uremote (%S,%d) )" remote pageno
3002 | Ohistory hist -> gotohist hist
3003 | Oremotedest (path, dest) ->
3004 error "gotounder (Uremotedest (%S, %S))" path dest
3007 class outlinesoucebase fetchoutlines = object (self)
3008 inherit lvsourcebase
3009 val mutable m_items = E.a
3010 val mutable m_minfo = E.a
3011 val mutable m_orig_items = E.a
3012 val mutable m_orig_minfo = E.a
3013 val mutable m_narrow_patterns = []
3014 val mutable m_gen = -1
3016 method getitemcount = Array.length m_items
3018 method getitem n =
3019 let s, n, _ = m_items.(n) in
3020 (s, n+0)
3022 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3023 ignore (uioh, first);
3024 let items, minfo =
3025 if m_narrow_patterns = []
3026 then m_orig_items, m_orig_minfo
3027 else m_items, m_minfo
3029 m_pan <- pan;
3030 if not cancel
3031 then (
3032 m_items <- items;
3033 m_minfo <- minfo;
3034 gotooutline m_items.(active);
3036 else (
3037 m_items <- items;
3038 m_minfo <- minfo;
3040 None
3042 method hasaction (_:int) = true
3044 method greetmsg =
3045 if Array.length m_items != Array.length m_orig_items
3046 then
3047 let s =
3048 match m_narrow_patterns with
3049 | one :: [] -> one
3050 | many -> String.concat Utf8syms.ellipsis (List.rev many)
3052 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3053 else E.s
3055 method statestr =
3056 match m_narrow_patterns with
3057 | [] -> E.s
3058 | one :: [] -> one
3059 | head :: _ -> Utf8syms.ellipsis ^ head
3061 method narrow pattern =
3062 match Str.regexp_case_fold pattern with
3063 | exception _ -> ()
3064 | re ->
3065 let rec loop accu minfo n =
3066 if n = -1
3067 then (
3068 m_items <- Array.of_list accu;
3069 m_minfo <- Array.of_list minfo;
3071 else
3072 let (s, _, _) as o = m_items.(n) in
3073 let accu, minfo =
3074 match Str.search_forward re s 0 with
3075 | exception Not_found -> accu, minfo
3076 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3078 loop accu minfo (n-1)
3080 loop [] [] (Array.length m_items - 1)
3082 method! getminfo = m_minfo
3084 method denarrow =
3085 m_orig_items <- fetchoutlines ();
3086 m_minfo <- m_orig_minfo;
3087 m_items <- m_orig_items
3089 method add_narrow_pattern pattern =
3090 m_narrow_patterns <- pattern :: m_narrow_patterns
3092 method del_narrow_pattern =
3093 match m_narrow_patterns with
3094 | _ :: rest -> m_narrow_patterns <- rest
3095 | [] -> ()
3097 method renarrow =
3098 self#denarrow;
3099 match m_narrow_patterns with
3100 | pattern :: [] -> self#narrow pattern; pattern
3101 | list ->
3102 List.fold_left (fun accu pattern ->
3103 self#narrow pattern;
3104 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
3106 method calcactive (_:anchor) = 0
3108 method reset anchor items =
3109 if state.gen != m_gen
3110 then (
3111 m_orig_items <- items;
3112 m_items <- items;
3113 m_narrow_patterns <- [];
3114 m_minfo <- E.a;
3115 m_orig_minfo <- E.a;
3116 m_gen <- state.gen;
3118 else (
3119 if items != m_orig_items
3120 then (
3121 m_orig_items <- items;
3122 if m_narrow_patterns == []
3123 then m_items <- items;
3126 let active = self#calcactive anchor in
3127 m_active <- active;
3128 m_first <- firstof m_first active
3132 let outlinesource fetchoutlines =
3133 (object
3134 inherit outlinesoucebase fetchoutlines
3135 method! calcactive anchor =
3136 let rely = getanchory anchor in
3137 let rec loop n best bestd =
3138 if n = Array.length m_items
3139 then best
3140 else
3141 let _, _, kind = m_items.(n) in
3142 match kind with
3143 | Oanchor anchor ->
3144 let orely = getanchory anchor in
3145 let d = abs (orely - rely) in
3146 if d < bestd
3147 then loop (n+1) n d
3148 else loop (n+1) best bestd
3149 | Onone | Oremote _ | Olaunch _
3150 | Oremotedest _ | Ouri _ | Ohistory _ ->
3151 loop (n+1) best bestd
3153 loop 0 ~-1 max_int
3154 end)
3157 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3158 let mkselector sourcetype =
3159 let fetchoutlines () =
3160 match sourcetype with
3161 | `bookmarks -> Array.of_list state.bookmarks
3162 | `outlines -> state.outlines
3163 | `history -> genhistoutlines () |> Array.of_list
3165 let source =
3166 if sourcetype = `history
3167 then new outlinesoucebase fetchoutlines
3168 else outlinesource fetchoutlines
3170 (fun errmsg ->
3171 let outlines = fetchoutlines () in
3172 if Array.length outlines = 0
3173 then showtext ' ' errmsg
3174 else (
3175 resetmstate ();
3176 Wsi.setcursor Wsi.CURSOR_INHERIT;
3177 let anchor = getanchor () in
3178 source#reset anchor outlines;
3179 state.text <- source#greetmsg;
3180 state.uioh <-
3181 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3182 postRedisplay "enter selector";
3186 let mkenter sourcetype errmsg = fun () -> mkselector sourcetype errmsg in
3187 ( mkenter `outlines "document has no outline"
3188 , mkenter `bookmarks "document has no bookmarks (yet)"
3189 , mkenter `history "history is empty" )
3192 let quickbookmark ?title () =
3193 match state.layout with
3194 | [] -> ()
3195 | l :: _ ->
3196 let title =
3197 match title with
3198 | None ->
3199 Unix.(
3200 let tm = localtime (now ()) in
3201 Printf.sprintf
3202 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3203 (l.pageno+1)
3204 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3206 | Some title -> title
3208 state.bookmarks <- (title, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3211 let setautoscrollspeed step goingdown =
3212 let incr = max 1 ((abs step) / 2) in
3213 let incr = if goingdown then incr else -incr in
3214 let astep = boundastep state.winh (step + incr) in
3215 state.autoscroll <- Some astep;
3218 let canpan () =
3219 match conf.columns with
3220 | Csplit _ -> true
3221 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3224 let panbound x = bound x (-state.w) state.winw;;
3226 let existsinrow pageno (columns, coverA, coverB) p =
3227 let last = ((pageno - coverA) mod columns) + columns in
3228 let rec any = function
3229 | [] -> false
3230 | l :: rest ->
3231 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3232 then p l
3233 else (
3234 if not (p l)
3235 then (if l.pageno = last then false else any rest)
3236 else true
3239 any state.layout
3242 let nextpage () =
3243 match state.layout with
3244 | [] ->
3245 let pageno = page_of_y state.y in
3246 gotoxy state.x (getpagey (pageno+1))
3247 | l :: rest ->
3248 match conf.columns with
3249 | Csingle _ ->
3250 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3251 then
3252 let y = clamp (pgscale state.winh) in
3253 gotoxy state.x y
3254 else
3255 let pageno = min (l.pageno+1) (state.pagecount-1) in
3256 gotoxy state.x (getpagey pageno)
3257 | Cmulti ((c, _, _) as cl, _) ->
3258 if conf.presentation
3259 && (existsinrow l.pageno cl
3260 (fun l -> l.pageh > l.pagey + l.pagevh))
3261 then
3262 let y = clamp (pgscale state.winh) in
3263 gotoxy state.x y
3264 else
3265 let pageno = min (l.pageno+c) (state.pagecount-1) in
3266 gotoxy state.x (getpagey pageno)
3267 | Csplit (n, _) ->
3268 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3269 then
3270 let pagey, pageh = getpageyh l.pageno in
3271 let pagey = pagey + pageh * l.pagecol in
3272 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3273 gotoxy state.x (pagey + pageh + ips)
3276 let prevpage () =
3277 match state.layout with
3278 | [] ->
3279 let pageno = page_of_y state.y in
3280 gotoxy state.x (getpagey (pageno-1))
3281 | l :: _ ->
3282 match conf.columns with
3283 | Csingle _ ->
3284 if conf.presentation && l.pagey != 0
3285 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3286 else
3287 let pageno = max 0 (l.pageno-1) in
3288 gotoxy state.x (getpagey pageno)
3289 | Cmulti ((c, _, coverB) as cl, _) ->
3290 if conf.presentation &&
3291 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3292 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3293 else
3294 let decr =
3295 if l.pageno = state.pagecount - coverB
3296 then 1
3297 else c
3299 let pageno = max 0 (l.pageno-decr) in
3300 gotoxy state.x (getpagey pageno)
3301 | Csplit (n, _) ->
3302 let y =
3303 if l.pagecol = 0
3304 then
3305 if l.pageno = 0
3306 then l.pagey
3307 else
3308 let pageno = max 0 (l.pageno-1) in
3309 let pagey, pageh = getpageyh pageno in
3310 pagey + (n-1)*pageh
3311 else
3312 let pagey, pageh = getpageyh l.pageno in
3313 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3315 gotoxy state.x y
3318 let save () =
3319 if emptystr conf.savecmd
3320 then adderrmsg "savepath-command is empty"
3321 "don't know where to save modified document"
3322 else
3323 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3324 let path =
3325 getcmdoutput
3326 (fun exn ->
3327 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3328 savecmd
3330 if nonemptystr path
3331 then
3332 let tmp = path ^ ".tmp" in
3333 savedoc tmp;
3334 Unix.rename tmp path;
3337 let viewkeyboard key mask =
3338 let enttext te =
3339 let mode = state.mode in
3340 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3341 state.text <- E.s;
3342 enttext ();
3343 postRedisplay "view:enttext"
3345 let ctrl = Wsi.withctrl mask in
3346 let open Keys in
3347 match Wsi.kc2kt key with
3348 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3350 | Ascii 'Q' -> exit 0
3352 | Ascii 'W' ->
3353 if hasunsavedchanges ()
3354 then save ()
3356 | Insert ->
3357 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3358 then (
3359 state.mode <- (
3360 match state.lnava with
3361 | None -> LinkNav (Ltgendir 0)
3362 | Some pn -> LinkNav (Ltexact pn)
3364 gotoxy state.x state.y;
3366 else impmsg "keyboard link navigation does not work under rotation"
3368 | Escape | Ascii 'q' ->
3369 begin match state.mstate with
3370 | Mzoomrect _ ->
3371 resetmstate ();
3372 postRedisplay "kill rect";
3373 | Msel _
3374 | Mpan _
3375 | Mscrolly | Mscrollx
3376 | Mzoom _
3377 | Mnone ->
3378 begin match state.mode with
3379 | LinkNav ln ->
3380 begin match ln with
3381 | Ltexact pl -> state.lnava <- Some pl
3382 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3383 end;
3384 state.mode <- View;
3385 postRedisplay "esc leave linknav"
3386 | Birdseye _ | Textentry _ | View ->
3387 match state.ranchors with
3388 | [] -> raise Quit
3389 | (path, password, anchor, origin) :: rest ->
3390 state.ranchors <- rest;
3391 state.anchor <- anchor;
3392 state.origin <- origin;
3393 state.nameddest <- E.s;
3394 opendoc path password
3395 end;
3396 end;
3398 | Backspace ->
3399 addnavnorc ();
3400 gotoxy state.x (getnav ~-1)
3402 | Ascii 'o' -> enteroutlinemode ()
3403 | Ascii 'H' -> enterhistmode ()
3405 | Ascii 'u' ->
3406 state.rects <- [];
3407 state.text <- E.s;
3408 Hashtbl.iter (fun _ opaque ->
3409 clearmark opaque;
3410 Hashtbl.clear state.prects) state.pagemap;
3411 postRedisplay "dehighlight";
3413 | Ascii (('/' | '?') as c) ->
3414 let ondone isforw s =
3415 cbput state.hists.pat s;
3416 state.searchpattern <- s;
3417 search s isforw
3419 let s = String.make 1 c in
3420 enttext (s, E.s, Some (onhist state.hists.pat),
3421 textentry, ondone (c = '/'), true)
3423 | Ascii '+' | Ascii '=' when ctrl ->
3424 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3425 pivotzoom (conf.zoom +. incr)
3427 | Ascii '+' ->
3428 let ondone s =
3429 let n =
3430 try int_of_string s with exn ->
3431 state.text <-
3432 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3433 max_int
3435 if n != max_int
3436 then (
3437 conf.pagebias <- n;
3438 state.text <- "page bias is now " ^ string_of_int n;
3441 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3443 | Ascii '-' when ctrl ->
3444 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3445 pivotzoom (max 0.01 (conf.zoom -. decr))
3447 | Ascii '-' ->
3448 let ondone msg = state.text <- msg in
3449 enttext ("option: ", E.s, None,
3450 optentry state.mode, ondone, true)
3452 | Ascii '0' when ctrl ->
3453 if conf.zoom = 1.0
3454 then gotoxy 0 state.y
3455 else setzoom 1.0
3457 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3458 let cols =
3459 match conf.columns with
3460 | Csingle _ | Cmulti _ -> 1
3461 | Csplit (n, _) -> n
3463 let h = state.winh -
3464 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3466 let zoom = zoomforh state.winw h 0 cols in
3467 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3468 then setzoom zoom
3470 | Ascii '3' when ctrl ->
3471 let fm =
3472 match conf.fitmodel with
3473 | FitWidth -> FitProportional
3474 | FitProportional -> FitPage
3475 | FitPage -> FitWidth
3477 state.text <- "fit model: " ^ FMTE.to_string fm;
3478 reqlayout conf.angle fm
3480 | Ascii '4' when ctrl ->
3481 let zoom = getmaxw () /. float state.winw in
3482 if zoom > 0.0 then setzoom zoom
3484 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3486 | Ascii ('0'..'9' as c) when not ctrl ->
3487 let ondone s =
3488 let n =
3489 try int_of_string s with exn ->
3490 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3493 if n >= 0
3494 then (
3495 addnav ();
3496 cbput state.hists.pag (string_of_int n);
3497 gotopage1 (n + conf.pagebias - 1) 0;
3500 let pageentry text = function [@warning "-4"]
3501 | Keys.Ascii 'g' -> TEdone text
3502 | key -> intentry text key
3504 let text = String.make 1 c in
3505 enttext (":", text, Some (onhist state.hists.pag),
3506 pageentry, ondone, true)
3508 | Ascii 'b' ->
3509 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3510 postRedisplay "toggle scrollbar";
3512 | Ascii 'B' ->
3513 state.bzoom <- not state.bzoom;
3514 state.rects <- [];
3515 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3517 | Ascii 'l' ->
3518 conf.hlinks <- not conf.hlinks;
3519 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3520 postRedisplay "toggle highlightlinks";
3522 | Ascii 'F' ->
3523 if conf.angle mod 360 = 0
3524 then (
3525 state.glinks <- true;
3526 let mode = state.mode in
3527 state.mode <-
3528 Textentry (
3529 (":", E.s, None, linknentry, linknact gotounder, false),
3530 (fun _ ->
3531 state.glinks <- false;
3532 state.mode <- mode)
3534 state.text <- E.s;
3535 postRedisplay "view:linkent(F)"
3537 else impmsg "hint mode does not work under rotation"
3539 | Ascii 'y' ->
3540 state.glinks <- true;
3541 let mode = state.mode in
3542 state.mode <-
3543 Textentry (
3544 (":", E.s, None, linknentry,
3545 linknact (fun under ->
3546 selstring conf.selcmd (undertext under)), false),
3547 (fun _ ->
3548 state.glinks <- false;
3549 state.mode <- mode)
3551 state.text <- E.s;
3552 postRedisplay "view:linkent"
3554 | Ascii 'a' ->
3555 begin match state.autoscroll with
3556 | Some step ->
3557 conf.autoscrollstep <- step;
3558 state.autoscroll <- None
3559 | None ->
3560 state.autoscroll <- Some conf.autoscrollstep;
3561 state.slideshow <- state.slideshow land lnot 2
3564 | Ascii 'p' when ctrl ->
3565 launchpath () (* XXX where do error messages go? *)
3567 | Ascii 'P' ->
3568 setpresentationmode (not conf.presentation);
3569 showtext ' ' ("presentation mode " ^
3570 if conf.presentation then "on" else "off");
3572 | Ascii 'f' ->
3573 if List.mem Wsi.Fullscreen state.winstate
3574 then Wsi.reshape conf.cwinw conf.cwinh
3575 else Wsi.fullscreen ()
3577 | Ascii ('p'|'N') -> search state.searchpattern false
3578 | Ascii 'n' | Fn 3 -> search state.searchpattern true
3580 | Ascii 't' ->
3581 begin match state.layout with
3582 | [] -> ()
3583 | l :: _ -> gotoxy state.x (getpagey l.pageno)
3586 | Ascii ' ' -> nextpage ()
3587 | Delete -> prevpage ()
3588 | Ascii '=' -> showtext ' ' (describe_layout state.layout);
3590 | Ascii 'w' ->
3591 begin match state.layout with
3592 | [] -> ()
3593 | l :: _ ->
3594 Wsi.reshape l.pagew l.pageh;
3595 postRedisplay "w"
3598 | Ascii '\'' -> enterbookmarkmode ()
3599 | Ascii 'h' | Fn 1 -> enterhelpmode ()
3600 | Ascii 'i' -> enterinfomode ()
3601 | Ascii 'e' when Buffer.length state.errmsgs > 0 -> entermsgsmode ()
3603 | Ascii 'm' ->
3604 let ondone s =
3605 match state.layout with
3606 | l :: _ when nonemptystr s ->
3607 state.bookmarks <- (s, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3608 | _ -> ()
3610 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3612 | Ascii '~' ->
3613 quickbookmark ();
3614 showtext ' ' "Quick bookmark added";
3616 | Ascii 'x' -> state.roam ()
3618 | Ascii ('<'|'>' as c) ->
3619 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3621 | Ascii ('['|']' as c) ->
3622 conf.colorscale <-
3623 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3624 postRedisplay "brightness";
3626 | Ascii 'c' when state.mode = View ->
3627 if Wsi.withalt mask
3628 then (
3629 if conf.zoom > 1.0
3630 then
3631 let m = (state.winw - state.w) / 2 in
3632 gotoxy m state.y
3634 else
3635 let (c, a, b), z =
3636 match state.prevcolumns with
3637 | None -> (1, 0, 0), 1.0
3638 | Some (columns, z) ->
3639 let cab =
3640 match columns with
3641 | Csplit (c, _) -> -c, 0, 0
3642 | Cmulti ((c, a, b), _) -> c, a, b
3643 | Csingle _ -> 1, 0, 0
3645 cab, z
3647 setcolumns View c a b;
3648 setzoom z
3650 | Down | Up when ctrl && Wsi.withshift mask ->
3651 let zoom, x = state.prevzoom in
3652 setzoom zoom;
3653 state.x <- x;
3655 | Ascii 'k' | Up ->
3656 begin match state.autoscroll with
3657 | None ->
3658 begin match state.mode with
3659 | Birdseye beye -> upbirdseye 1 beye
3660 | Textentry _ | View | LinkNav _ ->
3661 if ctrl
3662 then gotoxy state.x (clamp ~-(state.winh/2))
3663 else (
3664 if not (Wsi.withshift mask) && conf.presentation
3665 then prevpage ()
3666 else gotoxy state.x (clamp (-conf.scrollstep))
3669 | Some n -> setautoscrollspeed n false
3672 | Ascii 'j' | Down ->
3673 begin match state.autoscroll with
3674 | None ->
3675 begin match state.mode with
3676 | Birdseye beye -> downbirdseye 1 beye
3677 | Textentry _ | View | LinkNav _ ->
3678 if ctrl
3679 then gotoxy state.x (clamp (state.winh/2))
3680 else (
3681 if not (Wsi.withshift mask) && conf.presentation
3682 then nextpage ()
3683 else gotoxy state.x (clamp (conf.scrollstep))
3686 | Some n -> setautoscrollspeed n true
3689 | Left | Right when not (Wsi.withalt mask) ->
3690 if canpan ()
3691 then
3692 let dx =
3693 if ctrl
3694 then state.winw / 2
3695 else conf.hscrollstep
3697 let dx =
3698 let pv = Wsi.kc2kt key in
3699 if pv = Keys.Left then dx else -dx
3701 gotoxy (panbound (state.x + dx)) state.y
3702 else (
3703 state.text <- E.s;
3704 postRedisplay "left/right"
3707 | Prior ->
3708 let y =
3709 if ctrl
3710 then
3711 match state.layout with
3712 | [] -> state.y
3713 | l :: _ -> state.y - l.pagey
3714 else clamp (pgscale (-state.winh))
3716 gotoxy state.x y
3718 | Next ->
3719 let y =
3720 if ctrl
3721 then
3722 match List.rev state.layout with
3723 | [] -> state.y
3724 | l :: _ -> getpagey l.pageno
3725 else clamp (pgscale state.winh)
3727 gotoxy state.x y
3729 | Ascii 'g' | Home ->
3730 addnav ();
3731 gotoxy 0 0
3732 | Ascii 'G' | End ->
3733 addnav ();
3734 gotoxy 0 (clamp state.maxy)
3736 | Right when Wsi.withalt mask ->
3737 addnavnorc ();
3738 gotoxy state.x (getnav 1)
3739 | Left when Wsi.withalt mask ->
3740 addnavnorc ();
3741 gotoxy state.x (getnav ~-1)
3743 | Ascii 'r' ->
3744 reload ()
3746 | Ascii 'v' when conf.debug ->
3747 state.rects <- [];
3748 List.iter (fun l ->
3749 match getopaque l.pageno with
3750 | None -> ()
3751 | Some opaque ->
3752 let x0, y0, x1, y1 = pagebbox opaque in
3753 let rect = (float x0, float y0,
3754 float x1, float y0,
3755 float x1, float y1,
3756 float x0, float y1) in
3757 debugrect rect;
3758 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3759 state.rects <- (l.pageno, color, rect) :: state.rects;
3760 ) state.layout;
3761 postRedisplay "v";
3763 | Ascii '|' ->
3764 let mode = state.mode in
3765 let cmd = ref E.s in
3766 let onleave = function
3767 | Cancel -> state.mode <- mode
3768 | Confirm ->
3769 List.iter (fun l ->
3770 match getopaque l.pageno with
3771 | Some opaque -> pipesel opaque !cmd
3772 | None -> ()) state.layout;
3773 state.mode <- mode
3775 let ondone s =
3776 cbput state.hists.sel s;
3777 cmd := s
3779 let te =
3780 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3782 postRedisplay "|";
3783 state.mode <- Textentry (te, onleave);
3785 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3786 vlog "huh? %s" (Wsi.keyname key)
3789 let linknavkeyboard key mask linknav =
3790 let pv = Wsi.kc2kt key in
3791 let getpage pageno =
3792 let rec loop = function
3793 | [] -> None
3794 | l :: _ when l.pageno = pageno -> Some l
3795 | _ :: rest -> loop rest
3796 in loop state.layout
3798 let doexact (pageno, n) =
3799 match getopaque pageno, getpage pageno with
3800 | Some opaque, Some l ->
3801 if pv = Keys.Enter
3802 then
3803 let under = getlink opaque n in
3804 postRedisplay "link gotounder";
3805 gotounder under;
3806 state.mode <- View;
3807 else
3808 let opt, dir =
3809 let open Keys in
3810 match pv with
3811 | Home -> Some (findlink opaque LDfirst), -1
3812 | End -> Some (findlink opaque LDlast), 1
3813 | Left -> Some (findlink opaque (LDleft n)), -1
3814 | Right -> Some (findlink opaque (LDright n)), 1
3815 | Up -> Some (findlink opaque (LDup n)), -1
3816 | Down -> Some (findlink opaque (LDdown n)), 1
3817 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3818 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3820 let pwl l dir =
3821 begin match findpwl l.pageno dir with
3822 | Pwlnotfound -> ()
3823 | Pwl pageno ->
3824 let notfound dir =
3825 state.mode <- LinkNav (Ltgendir dir);
3826 let y, h = getpageyh pageno in
3827 let y =
3828 if dir < 0
3829 then y + h - state.winh
3830 else y
3832 gotoxy state.x y
3834 begin match getopaque pageno, getpage pageno with
3835 | Some opaque, Some _ ->
3836 let link =
3837 let ld = if dir > 0 then LDfirst else LDlast in
3838 findlink opaque ld
3840 begin match link with
3841 | Lfound m ->
3842 showlinktype (getlink opaque m);
3843 state.mode <- LinkNav (Ltexact (pageno, m));
3844 postRedisplay "linknav jpage";
3845 | Lnotfound -> notfound dir
3846 end;
3847 | _ -> notfound dir
3848 end;
3849 end;
3851 begin match opt with
3852 | Some Lnotfound -> pwl l dir;
3853 | Some (Lfound m) ->
3854 if m = n
3855 then pwl l dir
3856 else (
3857 let _, y0, _, y1 = getlinkrect opaque m in
3858 if y0 < l.pagey
3859 then gotopage1 l.pageno y0
3860 else (
3861 let d = fstate.fontsize + 1 in
3862 if y1 - l.pagey > l.pagevh - d
3863 then gotopage1 l.pageno (y1 - state.winh + d)
3864 else postRedisplay "linknav";
3866 showlinktype (getlink opaque m);
3867 state.mode <- LinkNav (Ltexact (l.pageno, m));
3870 | None -> viewkeyboard key mask
3871 end;
3872 | _ -> viewkeyboard key mask
3874 if pv = Keys.Insert
3875 then (
3876 begin match linknav with
3877 | Ltexact pa -> state.lnava <- Some pa
3878 | Ltgendir _ | Ltnotready _ -> ()
3879 end;
3880 state.mode <- View;
3881 postRedisplay "leave linknav"
3883 else
3884 match linknav with
3885 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3886 | Ltexact exact -> doexact exact
3889 let keyboard key mask =
3890 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
3891 then wcmd "interrupt"
3892 else state.uioh <- state.uioh#key key mask
3895 let birdseyekeyboard key mask
3896 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3897 let incr =
3898 match conf.columns with
3899 | Csingle _ -> 1
3900 | Cmulti ((c, _, _), _) -> c
3901 | Csplit _ -> error "bird's eye split mode"
3903 let pgh layout = List.fold_left
3904 (fun m l -> max l.pageh m) state.winh layout in
3905 let open Keys in
3906 match Wsi.kc2kt key with
3907 | Ascii 'l' when Wsi.withctrl mask ->
3908 let y, h = getpageyh pageno in
3909 let top = (state.winh - h) / 2 in
3910 gotoxy state.x (max 0 (y - top))
3911 | Enter -> leavebirdseye beye false
3912 | Escape -> leavebirdseye beye true
3913 | Up -> upbirdseye incr beye
3914 | Down -> downbirdseye incr beye
3915 | Left -> upbirdseye 1 beye
3916 | Right -> downbirdseye 1 beye
3918 | Prior ->
3919 begin match state.layout with
3920 | l :: _ ->
3921 if l.pagey != 0
3922 then (
3923 state.mode <- Birdseye (
3924 oconf, leftx, l.pageno, hooverpageno, anchor
3926 gotopage1 l.pageno 0;
3928 else (
3929 let layout = layout state.x (state.y-state.winh)
3930 state.winw
3931 (pgh state.layout) in
3932 match layout with
3933 | [] -> gotoxy state.x (clamp (-state.winh))
3934 | l :: _ ->
3935 state.mode <- Birdseye (
3936 oconf, leftx, l.pageno, hooverpageno, anchor
3938 gotopage1 l.pageno 0
3941 | [] -> gotoxy state.x (clamp (-state.winh))
3942 end;
3944 | Next ->
3945 begin match List.rev state.layout with
3946 | l :: _ ->
3947 let layout = layout state.x
3948 (state.y + (pgh state.layout))
3949 state.winw state.winh in
3950 begin match layout with
3951 | [] ->
3952 let incr = l.pageh - l.pagevh in
3953 if incr = 0
3954 then (
3955 state.mode <-
3956 Birdseye (
3957 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
3959 postRedisplay "birdseye pagedown";
3961 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
3963 | l :: _ ->
3964 state.mode <-
3965 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3966 gotopage1 l.pageno 0;
3969 | [] -> gotoxy state.x (clamp state.winh)
3970 end;
3972 | Home ->
3973 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
3974 gotopage1 0 0
3976 | End ->
3977 let pageno = state.pagecount - 1 in
3978 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
3979 if not (pagevisible state.layout pageno)
3980 then
3981 let h =
3982 match List.rev state.pdims with
3983 | [] -> state.winh
3984 | (_, _, h, _) :: _ -> h
3986 gotoxy
3987 state.x
3988 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
3989 else postRedisplay "birdseye end";
3991 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
3994 let drawpage l =
3995 let color =
3996 match state.mode with
3997 | Textentry _ -> scalecolor 0.4
3998 | LinkNav _ | View -> scalecolor 1.0
3999 | Birdseye (_, _, pageno, hooverpageno, _) ->
4000 if l.pageno = hooverpageno
4001 then scalecolor 0.9
4002 else (
4003 if l.pageno = pageno
4004 then (
4005 let c = scalecolor 1.0 in
4006 GlDraw.color c;
4007 GlDraw.line_width 3.0;
4008 let dispx = l.pagedispx in
4009 linerect
4010 (float (dispx-1)) (float (l.pagedispy-1))
4011 (float (dispx+l.pagevw+1))
4012 (float (l.pagedispy+l.pagevh+1));
4013 GlDraw.line_width 1.0;
4016 else scalecolor 0.8
4019 drawtiles l color;
4022 let postdrawpage l linkindexbase =
4023 match getopaque l.pageno with
4024 | Some opaque ->
4025 if tileready l l.pagex l.pagey
4026 then
4027 let x = l.pagedispx - l.pagex
4028 and y = l.pagedispy - l.pagey in
4029 let hlmask =
4030 match conf.columns with
4031 | Csingle _ | Cmulti _ ->
4032 (if conf.hlinks then 1 else 0)
4033 + (if state.glinks
4034 && not (isbirdseye state.mode) then 2 else 0)
4035 | Csplit _ -> 0
4037 let s =
4038 match state.mode with
4039 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4040 | Textentry _
4041 | Birdseye _
4042 | View
4043 | LinkNav _ -> E.s
4045 Hashtbl.find_all state.prects l.pageno |>
4046 List.iter (fun vals -> drawprect opaque x y vals);
4047 let n = postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4048 if n < 0
4049 then (Glutils.redisplay := true; 0)
4050 else n
4051 else 0
4052 | _ -> 0
4055 let scrollindicator () =
4056 let sbw, ph, sh = state.uioh#scrollph in
4057 let sbh, pw, sw = state.uioh#scrollpw in
4059 let x0,x1,hx0 =
4060 if conf.leftscroll
4061 then (0, sbw, sbw)
4062 else ((state.winw - sbw), state.winw, 0)
4065 Gl.enable `blend;
4066 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4067 let (r, g, b, alpha) = conf.sbarcolor in
4068 GlDraw.color (r, g, b) ~alpha;
4069 filledrect (float x0) 0. (float x1) (float state.winh);
4070 filledrect
4071 (float hx0) (float (state.winh - sbh))
4072 (float (hx0 + state.winw)) (float state.winh);
4073 let (r, g, b, alpha) = conf.sbarhndlcolor in
4074 GlDraw.color (r, g, b) ~alpha;
4076 filledrect (float x0) ph (float x1) (ph +. sh);
4077 let pw = pw +. float hx0 in
4078 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4079 Gl.disable `blend;
4082 let showsel () =
4083 match state.mstate with
4084 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
4085 | Msel ((x0, y0), (x1, y1)) ->
4086 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4087 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4088 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4089 if n0 != -1 && n0 = n1 then seltext o0 (px0, py0, px1, py1);
4092 let showrects = function
4093 | [] -> ()
4094 | rects ->
4095 Gl.enable `blend;
4096 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4097 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4098 List.iter
4099 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4100 List.iter (fun l ->
4101 if l.pageno = pageno
4102 then
4103 let dx = float (l.pagedispx - l.pagex) in
4104 let dy = float (l.pagedispy - l.pagey) in
4105 let r, g, b, alpha = c in
4106 GlDraw.color (r, g, b) ~alpha;
4107 filledrect2
4108 (x0+.dx) (y0+.dy)
4109 (x1+.dx) (y1+.dy)
4110 (x3+.dx) (y3+.dy)
4111 (x2+.dx) (y2+.dy);
4112 ) state.layout
4113 ) rects;
4114 Gl.disable `blend;
4117 let display () =
4118 GlDraw.color (scalecolor2 conf.bgcolor);
4119 GlClear.color (scalecolor2 conf.bgcolor);
4120 GlClear.clear [`color];
4121 List.iter drawpage state.layout;
4122 let rects =
4123 match state.mode with
4124 | LinkNav (Ltexact (pageno, linkno)) ->
4125 begin match getopaque pageno with
4126 | Some opaque ->
4127 let x0, y0, x1, y1 = getlinkrect opaque linkno in
4128 let color = (0.0, 0.0, 0.5, 0.5) in
4129 (pageno, color,
4130 (float x0, float y0,
4131 float x1, float y0,
4132 float x1, float y1,
4133 float x0, float y1)
4134 ) :: state.rects
4135 | None -> state.rects
4137 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4138 | Birdseye _
4139 | Textentry _
4140 | View -> state.rects
4142 showrects rects;
4143 let rec postloop linkindexbase = function
4144 | l :: rest ->
4145 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4146 postloop linkindexbase rest
4147 | [] -> ()
4149 showsel ();
4150 postloop 0 state.layout;
4151 state.uioh#display;
4152 begin match state.mstate with
4153 | Mzoomrect ((x0, y0), (x1, y1)) ->
4154 Gl.enable `blend;
4155 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4156 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4157 filledrect (float x0) (float y0) (float x1) (float y1);
4158 Gl.disable `blend;
4159 | Msel _
4160 | Mpan _
4161 | Mscrolly | Mscrollx
4162 | Mzoom _
4163 | Mnone -> ()
4164 end;
4165 enttext ();
4166 scrollindicator ();
4167 Wsi.swapb ();
4170 let zoomrect x y x1 y1 =
4171 let x0 = min x x1
4172 and x1 = max x x1
4173 and y0 = min y y1 in
4174 let zoom = (float state.w) /. float (x1 - x0) in
4175 let margin =
4176 let simple () =
4177 if state.w < state.winw
4178 then (state.winw - state.w) / 2
4179 else 0
4181 match conf.fitmodel with
4182 | FitWidth | FitProportional -> simple ()
4183 | FitPage ->
4184 match conf.columns with
4185 | Csplit _ ->
4186 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4187 | Cmulti _ | Csingle _ -> simple ()
4189 gotoxy ((state.x + margin) - x0) (state.y + y0);
4190 state.anchor <- getanchor ();
4191 setzoom zoom;
4192 resetmstate ();
4195 let annot inline x y =
4196 match unproject x y with
4197 | Some (opaque, n, ux, uy) ->
4198 let add text =
4199 addannot opaque ux uy text;
4200 wcmd "freepage %s" (~> opaque);
4201 Hashtbl.remove state.pagemap (n, state.gen);
4202 flushtiles ();
4203 gotoxy state.x state.y
4205 if inline
4206 then
4207 let ondone s = add s in
4208 let mode = state.mode in
4209 state.mode <- Textentry (
4210 ("annotation: ", E.s, None, textentry, ondone, true),
4211 fun _ -> state.mode <- mode);
4212 state.text <- E.s;
4213 enttext ();
4214 postRedisplay "annot"
4215 else add @@ getusertext E.s
4216 | _ -> ()
4219 let zoomblock x y =
4220 let g opaque l px py =
4221 match rectofblock opaque px py with
4222 | Some a ->
4223 let x0 = a.(0) -. 20. in
4224 let x1 = a.(1) +. 20. in
4225 let y0 = a.(2) -. 20. in
4226 let zoom = (float state.w) /. (x1 -. x0) in
4227 let pagey = getpagey l.pageno in
4228 let margin = (state.w - l.pagew)/2 in
4229 let nx = -truncate x0 - margin in
4230 gotoxy nx (pagey + truncate y0);
4231 state.anchor <- getanchor ();
4232 setzoom zoom;
4233 None
4234 | None -> None
4236 match conf.columns with
4237 | Csplit _ ->
4238 impmsg "block zooming does not work properly in split columns mode"
4239 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4242 let scrollx x =
4243 let winw = state.winw - 1 in
4244 let s = float x /. float winw in
4245 let destx = truncate (float (state.w + winw) *. s) in
4246 gotoxy (winw - destx) state.y;
4247 state.mstate <- Mscrollx;
4250 let scrolly y =
4251 let s = float y /. float state.winh in
4252 let desty = truncate (s *. float (maxy ())) in
4253 gotoxy state.x desty;
4254 state.mstate <- Mscrolly;
4257 let viewmulticlick clicks x y mask =
4258 let g opaque l px py =
4259 let mark =
4260 match clicks with
4261 | 2 -> Mark_word
4262 | 3 -> Mark_line
4263 | 4 -> Mark_block
4264 | _ -> Mark_page
4266 if markunder opaque px py mark
4267 then (
4268 Some (fun () ->
4269 let dopipe cmd =
4270 match getopaque l.pageno with
4271 | None -> ()
4272 | Some opaque -> pipesel opaque cmd
4274 state.roam <- (fun () -> dopipe conf.paxcmd);
4275 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4278 else None
4280 postRedisplay "viewmulticlick";
4281 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4284 let canselect () =
4285 match conf.columns with
4286 | Csplit _ -> false
4287 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4290 let viewmouse button down x y mask =
4291 match button with
4292 | n when (n == 4 || n == 5) && not down ->
4293 if Wsi.withctrl mask
4294 then (
4295 let incr =
4296 if n = 5
4297 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4298 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4300 let fx, fy =
4301 match state.mstate with
4302 | Mzoom (oldn, _, pos) when n = oldn -> pos
4303 | Mzoomrect _ | Mnone | Mpan _
4304 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4306 let zoom = conf.zoom -. incr in
4307 state.mstate <- Mzoom (n, 0, (x, y));
4308 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4309 then pivotzoom ~x ~y zoom
4310 else pivotzoom zoom
4312 else (
4313 match state.autoscroll with
4314 | Some step -> setautoscrollspeed step (n=4)
4315 | None ->
4316 if conf.wheelbypage || conf.presentation
4317 then (
4318 if n = 4
4319 then prevpage ()
4320 else nextpage ()
4322 else
4323 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4324 let incr = incr * 2 in
4325 let y = clamp incr in
4326 gotoxy state.x y
4329 | n when (n = 6 || n = 7) && not down && canpan () ->
4330 let x =
4331 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4332 gotoxy x state.y
4334 | 1 when Wsi.withshift mask ->
4335 state.mstate <- Mnone;
4336 if not down
4337 then (
4338 match unproject x y with
4339 | None -> ()
4340 | Some (_, pageno, ux, uy) ->
4341 let cmd = Printf.sprintf
4342 "%s %s %d %d %d"
4343 conf.stcmd state.path pageno ux uy
4345 match spawn cmd [] with
4346 | exception exn ->
4347 impmsg "execution of synctex command(%S) failed: %S"
4348 conf.stcmd @@ exntos exn
4349 | _pid -> ()
4352 | 1 when Wsi.withctrl mask ->
4353 if down
4354 then (
4355 Wsi.setcursor Wsi.CURSOR_FLEUR;
4356 state.mstate <- Mpan (x, y)
4358 else state.mstate <- Mnone
4360 | 3 ->
4361 if down
4362 then (
4363 if Wsi.withshift mask
4364 then (
4365 annot conf.annotinline x y;
4366 postRedisplay "addannot"
4368 else
4369 let p = (x, y) in
4370 Wsi.setcursor Wsi.CURSOR_CYCLE;
4371 state.mstate <- Mzoomrect (p, p)
4373 else (
4374 match state.mstate with
4375 | Mzoomrect ((x0, y0), _) ->
4376 if abs (x-x0) > 10 && abs (y - y0) > 10
4377 then zoomrect x0 y0 x y
4378 else (
4379 resetmstate ();
4380 postRedisplay "kill accidental zoom rect";
4382 | Msel _
4383 | Mpan _
4384 | Mscrolly | Mscrollx
4385 | Mzoom _
4386 | Mnone -> resetmstate ()
4389 | 1 when vscrollhit x ->
4390 if down
4391 then
4392 let _, position, sh = state.uioh#scrollph in
4393 if y > truncate position && y < truncate (position +. sh)
4394 then state.mstate <- Mscrolly
4395 else scrolly y
4396 else state.mstate <- Mnone
4398 | 1 when y > state.winh - hscrollh () ->
4399 if down
4400 then
4401 let _, position, sw = state.uioh#scrollpw in
4402 if x > truncate position && x < truncate (position +. sw)
4403 then state.mstate <- Mscrollx
4404 else scrollx x
4405 else state.mstate <- Mnone
4407 | 1 when state.bzoom -> if not down then zoomblock x y
4409 | 1 ->
4410 let dest = if down then getunder x y else Unone in
4411 begin match dest with
4412 | Ulinkuri _ -> gotounder dest
4413 | Unone when down ->
4414 Wsi.setcursor Wsi.CURSOR_FLEUR;
4415 state.mstate <- Mpan (x, y);
4416 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4417 | Unone | Utext _ ->
4418 if down
4419 then (
4420 if canselect ()
4421 then (
4422 state.mstate <- Msel ((x, y), (x, y));
4423 postRedisplay "mouse select";
4426 else (
4427 match state.mstate with
4428 | Mnone -> ()
4429 | Mzoom _ | Mscrollx | Mscrolly -> state.mstate <- Mnone
4430 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4431 | Mpan _ ->
4432 Wsi.setcursor Wsi.CURSOR_INHERIT;
4433 state.mstate <- Mnone
4434 | Msel ((x0, y0), (x1, y1)) ->
4435 let rec loop = function
4436 | [] -> ()
4437 | l :: rest ->
4438 let inside =
4439 let a0 = l.pagedispy in
4440 let a1 = a0 + l.pagevh in
4441 let b0 = l.pagedispx in
4442 let b1 = b0 + l.pagevw in
4443 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4444 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4446 if inside
4447 then
4448 match getopaque l.pageno with
4449 | Some opaque ->
4450 let dosel cmd () =
4451 pipef ~closew:false "Msel"
4452 (fun w ->
4453 copysel w opaque;
4454 postRedisplay "Msel") cmd
4456 dosel conf.selcmd ();
4457 state.roam <- dosel conf.paxcmd;
4458 | None -> ()
4459 else loop rest
4461 loop state.layout;
4462 resetmstate ();
4465 | _ -> ()
4468 let birdseyemouse button down x y mask
4469 (conf, leftx, _, hooverpageno, anchor) =
4470 match button with
4471 | 1 when down ->
4472 let rec loop = function
4473 | [] -> ()
4474 | l :: rest ->
4475 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4476 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4477 then
4478 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4479 else loop rest
4481 loop state.layout
4482 | 3 -> ()
4483 | _ -> viewmouse button down x y mask
4486 let uioh = object
4487 method display = ()
4489 method key key mask =
4490 begin match state.mode with
4491 | Textentry textentry -> textentrykeyboard key mask textentry
4492 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4493 | View -> viewkeyboard key mask
4494 | LinkNav linknav -> linknavkeyboard key mask linknav
4495 end;
4496 state.uioh
4498 method button button bstate x y mask =
4499 begin match state.mode with
4500 | LinkNav _ | View -> viewmouse button bstate x y mask
4501 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4502 | Textentry _ -> ()
4503 end;
4504 state.uioh
4506 method multiclick clicks x y mask =
4507 begin match state.mode with
4508 | LinkNav _ | View -> viewmulticlick clicks x y mask
4509 | Birdseye _ | Textentry _ -> ()
4510 end;
4511 state.uioh
4513 method motion x y =
4514 begin match state.mode with
4515 | Textentry _ -> ()
4516 | View | Birdseye _ | LinkNav _ ->
4517 match state.mstate with
4518 | Mzoom _ | Mnone -> ()
4519 | Mpan (x0, y0) ->
4520 let dx = x - x0
4521 and dy = y0 - y in
4522 state.mstate <- Mpan (x, y);
4523 let x = if canpan () then panbound (state.x + dx) else state.x in
4524 let y = clamp dy in
4525 gotoxy x y
4527 | Msel (a, _) ->
4528 state.mstate <- Msel (a, (x, y));
4529 postRedisplay "motion select";
4531 | Mscrolly ->
4532 let y = min state.winh (max 0 y) in
4533 scrolly y
4535 | Mscrollx ->
4536 let x = min state.winw (max 0 x) in
4537 scrollx x
4539 | Mzoomrect (p0, _) ->
4540 state.mstate <- Mzoomrect (p0, (x, y));
4541 postRedisplay "motion zoomrect";
4542 end;
4543 state.uioh
4545 method pmotion x y =
4546 begin match state.mode with
4547 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4548 let rec loop = function
4549 | [] ->
4550 if hooverpageno != -1
4551 then (
4552 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4553 postRedisplay "pmotion birdseye no hoover";
4555 | l :: rest ->
4556 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4557 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4558 then (
4559 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4560 postRedisplay "pmotion birdseye hoover";
4562 else loop rest
4564 loop state.layout
4566 | Textentry _ -> ()
4568 | LinkNav _ | View ->
4569 match state.mstate with
4570 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4571 | Mnone ->
4572 updateunder x y;
4573 if canselect ()
4574 then
4575 match conf.pax with
4576 | None -> ()
4577 | Some past ->
4578 let now = now () in
4579 let delta = now -. past in
4580 if delta > 0.01
4581 then paxunder x y
4582 else conf.pax <- Some now
4583 end;
4584 state.uioh
4586 method infochanged _ = ()
4588 method scrollph =
4589 let maxy = maxy () in
4590 let p, h =
4591 if maxy = 0
4592 then 0.0, float state.winh
4593 else scrollph state.y maxy
4595 vscrollw (), p, h
4597 method scrollpw =
4598 let fwinw = float (state.winw - vscrollw ()) in
4599 let sw =
4600 let sw = fwinw /. float state.w in
4601 let sw = fwinw *. sw in
4602 max sw (float conf.scrollh)
4604 let position =
4605 let maxx = state.w + state.winw in
4606 let x = state.winw - state.x in
4607 let percent = float x /. float maxx in
4608 (fwinw -. sw) *. percent
4610 hscrollh (), position, sw
4612 method modehash =
4613 let modename =
4614 match state.mode with
4615 | LinkNav _ -> "links"
4616 | Textentry _ -> "textentry"
4617 | Birdseye _ -> "birdseye"
4618 | View -> "view"
4620 findkeyhash conf modename
4622 method eformsgs = true
4623 method alwaysscrolly = false
4624 method scroll dx dy =
4625 let x = if canpan () then panbound (state.x + dx) else state.x in
4626 gotoxy x (clamp (2 * dy));
4627 state.uioh
4628 method zoom z x y =
4629 pivotzoom ~x ~y (conf.zoom *. exp z);
4630 end;;
4632 let addrect pageno r g b a x0 y0 x1 y1 =
4633 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4636 let ract cmds =
4637 let cl = splitatchar cmds ' ' in
4638 let scan s fmt f =
4639 try Scanf.sscanf s fmt f
4640 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4641 cmds @@ exntos exn
4643 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4644 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4645 s pageno r g b a x0 y0 x1 y1;
4646 onpagerect
4647 pageno
4648 (fun w h ->
4649 let _,w1,h1,_ = getpagedim pageno in
4650 let sw = float w1 /. float w
4651 and sh = float h1 /. float h in
4652 let x0s = x0 *. sw
4653 and x1s = x1 *. sw
4654 and y0s = y0 *. sh
4655 and y1s = y1 *. sh in
4656 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4657 let color = (r, g, b, a) in
4658 if conf.verbose then debugrect rect;
4659 state.rects <- (pageno, color, rect) :: state.rects;
4660 postRedisplay s;
4663 match cl with
4664 | "reload", "" -> reload ()
4665 | "goto", args ->
4666 scan args "%u %f %f"
4667 (fun pageno x y ->
4668 let cmd, _ = state.geomcmds in
4669 if emptystr cmd
4670 then gotopagexy pageno x y
4671 else
4672 let f prevf () =
4673 gotopagexy pageno x y;
4674 prevf ()
4676 state.reprf <- f state.reprf
4678 | "goto1", args -> scan args "%u %f" gotopage
4679 | "gotor", args -> scan args "%S" gotoremote
4680 | "rect", args ->
4681 scan args "%u %u %f %f %f %f"
4682 (fun pageno c x0 y0 x1 y1 ->
4683 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4684 rectx "rect" pageno color x0 y0 x1 y1;
4686 | "prect", args ->
4687 scan args "%u %f %f %f %f %f %f %f %f"
4688 (fun pageno r g b alpha x0 y0 x1 y1 ->
4689 addrect pageno r g b alpha x0 y0 x1 y1;
4690 postRedisplay "prect"
4692 | "pgoto", args ->
4693 scan args "%u %f %f"
4694 (fun pageno x y ->
4695 let optopaque =
4696 match getopaque pageno with
4697 | Some opaque -> opaque
4698 | None -> ~< E.s
4700 pgoto optopaque pageno x y;
4701 let rec fixx = function
4702 | [] -> ()
4703 | l :: rest ->
4704 if l.pageno = pageno
4705 then gotoxy (state.x - l.pagedispx) state.y
4706 else fixx rest
4708 let layout =
4709 let mult =
4710 match conf.columns with
4711 | Csingle _ | Csplit _ -> 1
4712 | Cmulti ((n, _, _), _) -> n
4714 layout 0 state.y (state.winw * mult) state.winh
4716 fixx layout
4718 | "activatewin", "" -> Wsi.activatewin ()
4719 | "quit", "" -> raise Quit
4720 | "keys", keys ->
4721 begin try
4722 let l = Config.keys_of_string keys in
4723 List.iter (fun (k, m) -> keyboard k m) l
4724 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4725 cmds @@ exntos exn
4727 | "clearrects", "" ->
4728 Hashtbl.clear state.prects;
4729 postRedisplay "clearrects"
4730 | _ ->
4731 adderrfmt "remote command"
4732 "error processing remote command: %S\n" cmds;
4735 let remote =
4736 let scratch = Bytes.create 80 in
4737 let buf = Buffer.create 80 in
4738 fun fd ->
4739 match tempfailureretry (Unix.read fd scratch 0) 80 with
4740 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4741 | 0 ->
4742 Unix.close fd;
4743 if Buffer.length buf > 0
4744 then (
4745 let s = Buffer.contents buf in
4746 Buffer.clear buf;
4747 ract s;
4749 None
4750 | n ->
4751 let rec eat ppos =
4752 let nlpos =
4753 match Bytes.index_from scratch ppos '\n' with
4754 | pos -> if pos >= n then -1 else pos
4755 | exception Not_found -> -1
4757 if nlpos >= 0
4758 then (
4759 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4760 let s = Buffer.contents buf in
4761 Buffer.clear buf;
4762 ract s;
4763 eat (nlpos+1);
4765 else (
4766 Buffer.add_subbytes buf scratch ppos (n-ppos);
4767 Some fd
4769 in eat 0
4772 let remoteopen path =
4773 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4774 with exn ->
4775 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4776 None
4779 let () =
4780 let gcconfig = ref false in
4781 let trimcachepath = ref E.s in
4782 let rcmdpath = ref E.s in
4783 let pageno = ref None in
4784 let openlast = ref false in
4785 let doreap = ref false in
4786 let csspath = ref None in
4787 selfexec := Sys.executable_name;
4788 Arg.parse
4789 (Arg.align
4790 [("-p", Arg.String (fun s -> state.password <- s),
4791 "<password> Set password");
4793 ("-f", Arg.String
4794 (fun s ->
4795 Config.fontpath := s;
4796 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4798 "<path> Set path to the user interface font");
4800 ("-c", Arg.String
4801 (fun s ->
4802 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4803 Config.confpath := s),
4804 "<path> Set path to the configuration file");
4806 ("-last", Arg.Set openlast, " Open last document");
4808 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4809 "<page-number> Jump to page");
4811 ("-tcf", Arg.String (fun s -> trimcachepath := s),
4812 "<path> Set path to the trim cache file");
4814 ("-dest", Arg.String (fun s -> state.nameddest <- s),
4815 "<named-destination> Set named destination");
4817 ("-remote", Arg.String (fun s -> rcmdpath := s),
4818 "<path> Set path to the source of remote commands");
4820 ("-gc", Arg.Set gcconfig, " Collect config garbage");
4822 ("-v", Arg.Unit (fun () ->
4823 Printf.printf
4824 "%s\nconfiguration file: %s\n"
4825 (Help.version ())
4826 Config.defconfpath;
4827 exit 0), " Print version and exit");
4829 ("-css", Arg.String (fun s -> csspath := Some s),
4830 "<path> Set path to the style sheet to use with EPUB/HTML");
4832 ("-origin", Arg.String (fun s -> state.origin <- s),
4833 "<origin> <undocumented>");
4835 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
4836 ("-layout-height", Arg.Set_int layouth,
4837 "<height> layout height html/epub/etc (-1, 0, N)");
4840 (fun s -> state.path <- s)
4841 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4843 let histmode = emptystr state.path && not !openlast in
4845 if not (Config.load !openlast)
4846 then dolog "failed to load configuration";
4848 begin match !pageno with
4849 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
4850 | None -> ()
4851 end;
4853 fillhelp ();
4854 if !gcconfig
4855 then (
4856 Config.gc ();
4857 exit 0
4860 let mu =
4861 object (self)
4862 val mutable m_clicks = 0
4863 val mutable m_click_x = 0
4864 val mutable m_click_y = 0
4865 val mutable m_lastclicktime = infinity
4867 method private cleanup =
4868 state.roam <- noroam;
4869 Hashtbl.iter (fun _ opaque -> clearmark opaque) state.pagemap
4870 method expose = postRedisplay "expose"
4871 method visible v =
4872 let name =
4873 match v with
4874 | Wsi.Unobscured -> "unobscured"
4875 | Wsi.PartiallyObscured -> "partiallyobscured"
4876 | Wsi.FullyObscured -> "fullyobscured"
4878 vlog "visibility change %s" name
4879 method display = display ()
4880 method map mapped = vlog "mapped %b" mapped
4881 method reshape w h =
4882 self#cleanup;
4883 reshape w h
4884 method mouse b d x y m =
4885 if d && canselect ()
4886 then (
4888 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
4890 m_click_x <- x;
4891 m_click_y <- y;
4892 if b = 1
4893 then (
4894 let t = now () in
4895 if abs x - m_click_x > 10
4896 || abs y - m_click_y > 10
4897 || abs_float (t -. m_lastclicktime) > 0.3
4898 then m_clicks <- 0;
4899 m_clicks <- m_clicks + 1;
4900 m_lastclicktime <- t;
4901 if m_clicks = 1
4902 then (
4903 self#cleanup;
4904 postRedisplay "cleanup";
4905 state.uioh <- state.uioh#button b d x y m;
4907 else state.uioh <- state.uioh#multiclick m_clicks x y m
4909 else (
4910 self#cleanup;
4911 m_clicks <- 0;
4912 m_lastclicktime <- infinity;
4913 state.uioh <- state.uioh#button b d x y m
4916 else state.uioh <- state.uioh#button b d x y m
4917 method motion x y =
4918 state.mpos <- (x, y);
4919 state.uioh <- state.uioh#motion x y
4920 method pmotion x y =
4921 state.mpos <- (x, y);
4922 state.uioh <- state.uioh#pmotion x y
4923 method key k m =
4924 vlog "k=%#x m=%#x" k m;
4925 let mascm = m land (
4926 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
4927 ) in
4928 let keyboard k m =
4929 let x = state.x and y = state.y in
4930 keyboard k m;
4931 if x != state.x || y != state.y then self#cleanup
4933 match state.keystate with
4934 | KSnone ->
4935 let km = k, mascm in
4936 begin
4937 match
4938 let modehash = state.uioh#modehash in
4939 try Hashtbl.find modehash km
4940 with Not_found ->
4941 try Hashtbl.find (findkeyhash conf "global") km
4942 with Not_found -> KMinsrt (k, m)
4943 with
4944 | KMinsrt (k, m) -> keyboard k m
4945 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
4946 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
4948 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
4949 List.iter (fun (k, m) -> keyboard k m) insrt;
4950 state.keystate <- KSnone
4951 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
4952 state.keystate <- KSinto (keys, insrt)
4953 | KSinto _ -> state.keystate <- KSnone
4955 method enter x y =
4956 state.mpos <- (x, y);
4957 state.uioh <- state.uioh#pmotion x y
4958 method leave = state.mpos <- (-1, -1)
4959 method winstate wsl = state.winstate <- wsl
4960 method quit : 'a. 'a = raise Quit
4961 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
4962 method zoom z x y = state.uioh#zoom z x y
4963 method opendoc path =
4964 state.mode <- View;
4965 state.uioh <- uioh;
4966 postRedisplay "opendoc";
4967 opendoc path state.password
4970 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
4971 state.wsfd <- wsfd;
4973 if not @@ List.exists GlMisc.check_extension
4974 [ "GL_ARB_texture_rectangle"
4975 ; "GL_EXT_texture_recangle"
4976 ; "GL_NV_texture_rectangle" ]
4977 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
4979 let cs, ss =
4980 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
4981 | exception exn ->
4982 dolog "socketpair failed: %s" @@ exntos exn;
4983 exit 1
4984 | (r, w) ->
4985 cloexec r;
4986 cloexec w;
4987 r, w
4990 setcheckers conf.checkers;
4991 begin match !csspath with
4992 | None -> ()
4993 | Some "" -> conf.css <- E.s
4994 | Some path ->
4995 let css = filecontents path in
4996 let l = String.length css in
4997 conf.css <-
4998 if substratis css (l-2) "\r\n"
4999 then String.sub css 0 (l-2)
5000 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
5001 end;
5002 init cs (
5003 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5004 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5005 !Config.fontpath, !trimcachepath
5007 List.iter GlArray.enable [`texture_coord; `vertex];
5008 state.ss <- ss;
5009 reshape ~firsttime:true winw winh;
5010 state.uioh <- uioh;
5011 if histmode
5012 then (
5013 Wsi.settitle "llpp (history)";
5014 enterhistmode ();
5016 else (
5017 state.text <- "Opening " ^ (mbtoutf8 state.path);
5018 opendoc state.path state.password;
5020 display ();
5021 Wsi.mapwin ();
5022 Wsi.setcursor Wsi.CURSOR_INHERIT;
5023 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5025 let rec reap () =
5026 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5027 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5028 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5029 | 0, _ -> ()
5030 | _pid, _status -> reap ()
5032 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5034 let optrfd =
5035 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
5038 let rec loop deadline =
5039 if !doreap
5040 then (
5041 doreap := false;
5042 reap ()
5044 let r = [state.ss; state.wsfd] in
5045 let r =
5046 match !optrfd with
5047 | None -> r
5048 | Some fd -> fd :: r
5050 if !redisplay
5051 then (
5052 Glutils.redisplay := false;
5053 display ();
5055 let timeout =
5056 let now = now () in
5057 if deadline > now
5058 then (
5059 if deadline = infinity
5060 then ~-.1.0
5061 else max 0.0 (deadline -. now)
5063 else 0.0
5065 let r, _, _ =
5066 try Unix.select r [] [] timeout
5067 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5069 begin match r with
5070 | [] ->
5071 let newdeadline =
5072 match state.autoscroll with
5073 | Some step when step != 0 ->
5074 if state.slideshow land 1 = 1
5075 then (
5076 if state.slideshow land 2 = 0
5077 then state.slideshow <- state.slideshow lor 2
5078 else if step < 0 then prevpage () else nextpage ();
5079 deadline +. (float (abs step))
5081 else
5082 let y = state.y + step in
5083 let fy = if conf.maxhfit then state.winh else 0 in
5084 let y =
5085 if y < 0
5086 then state.maxy - fy
5087 else if y >= state.maxy - fy then 0 else y
5089 gotoxy state.x y;
5090 deadline +. 0.01
5091 | _ -> infinity
5093 loop newdeadline
5095 | l ->
5096 let rec checkfds = function
5097 | [] -> ()
5098 | fd :: rest when fd = state.ss ->
5099 let cmd = rcmd state.ss in
5100 act cmd;
5101 checkfds rest
5103 | fd :: rest when fd = state.wsfd ->
5104 Wsi.readresp fd;
5105 checkfds rest
5107 | fd :: rest when Some fd = !optrfd ->
5108 begin match remote fd with
5109 | None -> optrfd := remoteopen !rcmdpath;
5110 | opt -> optrfd := opt
5111 end;
5112 checkfds rest
5114 | _ :: rest ->
5115 dolog "select returned unknown descriptor";
5116 checkfds rest
5118 checkfds l;
5119 let newdeadline =
5120 let deadline1 =
5121 if deadline = infinity
5122 then now () +. 0.01
5123 else deadline
5125 match state.autoscroll with
5126 | Some step when step != 0 -> deadline1
5127 | _ -> infinity
5129 loop newdeadline
5130 end;
5132 match loop infinity with
5133 | exception Quit ->
5134 Config.save leavebirdseye;
5135 if hasunsavedchanges ()
5136 then save ()
5137 | _ -> error "umpossible - infinity reached"