Update
[llpp.git] / main.ml
blob8d47a445e0ea72908905723143d95d3e8d06658b
1 open Utils;;
2 open Config;;
3 open Glutils;;
4 open Listview;;
6 let selfexec = ref E.s;;
7 let ignoredoctitlte = ref false;;
8 let layouth = ref ~-1;;
9 let checkerstexid = ref None;;
11 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
12 dolog {|rect {
13 x0,y0=(% f, % f)
14 x1,y1=(% f, % f)
15 x2,y2=(% f, % f)
16 x3,y3=(% f, % f)
17 }|} x0 y0 x1 y1 x2 y2 x3 y3;
20 let pgscale h = truncate (float h *. conf.pgscale);;
22 let hscrollh () =
23 if ((conf.scrollb land scrollbhv != 0) && (state.w > state.winw))
24 || state.uioh#alwaysscrolly
25 then conf.scrollbw
26 else 0
29 let setfontsize n =
30 fstate.fontsize <- n;
31 fstate.wwidth <- measurestr fstate.fontsize "w";
32 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
35 let launchpath () =
36 if emptystr conf.pathlauncher
37 then dolog "%s" state.path
38 else (
39 let command =
40 Str.global_replace Utils.Re.percent state.path conf.pathlauncher in
41 match spawn command [] with
42 | _pid -> ()
43 | exception exn -> dolog "failed to execute `%s': %s" command @@ exntos exn
47 let getopaque pageno =
48 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
49 with Not_found -> None
52 let pagetranslatepoint l x y =
53 let dy = y - l.pagedispy in
54 let y = dy + l.pagey in
55 let dx = x - l.pagedispx in
56 let x = dx + l.pagex in
57 (x, y);
60 let onppundermouse g x y d =
61 let rec f = function
62 | l :: rest ->
63 begin match getopaque l.pageno with
64 | Some opaque ->
65 let x0 = l.pagedispx in
66 let x1 = x0 + l.pagevw in
67 let y0 = l.pagedispy in
68 let y1 = y0 + l.pagevh in
69 if y >= y0 && y <= y1 && x >= x0 && x <= x1
70 then
71 let px, py = pagetranslatepoint l x y in
72 match g opaque l px py with
73 | Some res -> res
74 | None -> f rest
75 else f rest
76 | _ -> f rest
77 end
78 | [] -> d
80 f state.layout
83 let getunder x y =
84 let g opaque l px py =
85 if state.bzoom
86 then (
87 match Ffi.rectofblock opaque px py with
88 | Some [|x0;x1;y0;y1|] ->
89 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
90 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
91 state.rects <- [l.pageno, color, rect];
92 postRedisplay "getunder";
93 | _ -> ()
95 let under = Ffi.whatsunder opaque px py in
96 if under = Unone then None else Some under
98 onppundermouse g x y Unone
101 let unproject x y =
102 let g opaque l x y =
103 match Ffi.unproject opaque x y with
104 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
105 | None -> None
107 onppundermouse g x y None;
110 let showtext c s =
111 state.text <- Printf.sprintf "%c%s" c s;
112 postRedisplay "showtext";
115 let impmsg fmt = Format.ksprintf (fun s -> showtext '!' s) fmt;;
117 let pipesel opaque cmd =
118 if Ffi.hassel opaque
119 then pipef ~closew:false "pipesel"
120 (fun w ->
121 Ffi.copysel w opaque;
122 postRedisplay "pipesel"
123 ) cmd
126 let paxunder x y =
127 let g opaque l px py =
128 if Ffi.markunder opaque px py conf.paxmark
129 then
130 Some (fun () ->
131 match getopaque l.pageno with
132 | None -> ()
133 | Some opaque -> pipesel opaque conf.paxcmd
135 else None
137 postRedisplay "paxunder";
138 if conf.paxmark = Mark_page
139 then
140 List.iter (fun l ->
141 match getopaque l.pageno with
142 | None -> ()
143 | Some opaque -> Ffi.clearmark opaque) state.layout;
144 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
147 let undertext = function
148 | Unone -> "none"
149 | Ulinkuri s -> s
150 | Utext s -> "font: " ^ s
151 | Uannotation (opaque, slinkindex) ->
152 "annotation: " ^ Ffi.getannotcontents opaque slinkindex
155 let updateunder x y =
156 match getunder x y with
157 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
158 | Ulinkuri uri ->
159 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
160 Wsi.setcursor Wsi.CURSOR_INFO
161 | Utext s ->
162 if conf.underinfo then showtext 'f' ("ont: " ^ s);
163 Wsi.setcursor Wsi.CURSOR_TEXT
164 | Uannotation _ ->
165 if conf.underinfo then showtext 'a' "nnotation";
166 Wsi.setcursor Wsi.CURSOR_INFO
169 let showlinktype under =
170 if conf.underinfo && under != Unone
171 then showtext ' ' @@ undertext under
174 let intentry_with_suffix text key =
175 let text =
176 match [@warning "-4"] key with
177 | Keys.Ascii ('0'..'9' as c) -> addchar text c
178 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
179 addchar text @@ asciilower c
180 | _ ->
181 state.text <- "invalid key";
182 text
184 TEcont text
187 let wcmd fmt =
188 let b = Buffer.create 16 in
189 Printf.kbprintf
190 (fun b ->
191 let b = Buffer.to_bytes b in
192 Ffi.wcmd state.ss b @@ Bytes.length b
193 ) b fmt
196 let nogeomcmds = function
197 | s, [] -> emptystr s
198 | _ -> false
201 let layoutN ((columns, coverA, coverB), b) x y sw sh =
202 let rec fold accu n =
203 if n = Array.length b
204 then accu
205 else
206 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
207 if (vy - y) > sh &&
208 (n = coverA - 1
209 || n = state.pagecount - coverB
210 || (n - coverA) mod columns = columns - 1)
211 then accu
212 else
213 let accu =
214 if vy + h > y
215 then
216 let pagey = max 0 (y - vy) in
217 let pagedispy = if pagey > 0 then 0 else vy - y in
218 let pagedispx, pagex =
219 let pdx =
220 if n = coverA - 1 || n = state.pagecount - coverB
221 then x + (sw - w) / 2
222 else dx + xoff + x
224 if pdx < 0
225 then 0, -pdx
226 else pdx, 0
228 let pagevw =
229 let vw = sw - pagedispx in
230 let pw = w - pagex in
231 min vw pw
233 let pagevh = min (h - pagey) (sh - pagedispy) in
234 if pagevw > 0 && pagevh > 0
235 then
236 { pageno = n
237 ; pagedimno = pdimno
238 ; pagew = w
239 ; pageh = h
240 ; pagex = pagex
241 ; pagey = pagey
242 ; pagevw = pagevw
243 ; pagevh = pagevh
244 ; pagedispx = pagedispx
245 ; pagedispy = pagedispy
246 ; pagecol = 0
247 } :: accu
248 else accu
249 else accu
251 fold accu (n+1)
253 if Array.length b = 0
254 then []
255 else List.rev (fold [] (page_of_y y))
258 let layoutS (columns, b) x y sw sh =
259 let rec fold accu n =
260 if n = Array.length b
261 then accu
262 else
263 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
264 if (vy - y) > sh
265 then accu
266 else
267 let accu =
268 if vy + pageh > y
269 then
270 let x = xoff + x in
271 let pagey = max 0 (y - vy) in
272 let pagedispy = if pagey > 0 then 0 else vy - y in
273 let pagedispx, pagex =
274 if px = 0
275 then (
276 if x < 0
277 then 0, -x
278 else x, 0
280 else (
281 let px = px - x in
282 if px < 0
283 then -px, 0
284 else 0, px
287 let pagecolw = pagew/columns in
288 let pagedispx =
289 if pagecolw < sw
290 then pagedispx + ((sw - pagecolw) / 2)
291 else pagedispx
293 let pagevw =
294 let vw = sw - pagedispx in
295 let pw = pagew - pagex in
296 min vw pw
298 let pagevw = min pagevw pagecolw in
299 let pagevh = min (pageh - pagey) (sh - pagedispy) in
300 if pagevw > 0 && pagevh > 0
301 then
302 { pageno = n/columns
303 ; pagedimno = pdimno
304 ; pagew = pagew
305 ; pageh = pageh
306 ; pagex = pagex
307 ; pagey = pagey
308 ; pagevw = pagevw
309 ; pagevh = pagevh
310 ; pagedispx = pagedispx
311 ; pagedispy = pagedispy
312 ; pagecol = n mod columns
313 } :: accu
314 else accu
315 else accu
317 fold accu (n+1)
319 List.rev (fold [] 0)
322 let layout x y sw sh =
323 if nogeomcmds state.geomcmds
324 then
325 match conf.columns with
326 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
327 | Cmulti c -> layoutN c x y sw sh
328 | Csplit s -> layoutS s x y sw sh
329 else []
332 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
333 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
335 let itertiles l f =
336 let tilex = l.pagex mod conf.tilew in
337 let tiley = l.pagey mod conf.tileh in
339 let col = l.pagex / conf.tilew in
340 let row = l.pagey / conf.tileh in
342 let rec rowloop row y0 dispy h =
343 if h != 0
344 then
345 let dh = conf.tileh - y0 in
346 let dh = min h dh in
347 let rec colloop col x0 dispx w =
348 if w != 0
349 then
350 let dw = conf.tilew - x0 in
351 let dw = min w dw in
352 f col row dispx dispy x0 y0 dw dh;
353 colloop (col+1) 0 (dispx+dw) (w-dw)
355 colloop col tilex l.pagedispx l.pagevw;
356 rowloop (row+1) 0 (dispy+dh) (h-dh)
358 if l.pagevw > 0 && l.pagevh > 0
359 then rowloop row tiley l.pagedispy l.pagevh;
362 let gettileopaque l col row =
363 let key = l.pageno, state.gen, conf.colorspace,
364 conf.angle, l.pagew, l.pageh, col, row in
365 try Some (Hashtbl.find state.tilemap key)
366 with Not_found -> None
369 let puttileopaque l col row gen colorspace angle opaque size elapsed =
370 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
371 Hashtbl.add state.tilemap key (opaque, size, elapsed)
374 let drawtiles l color =
375 GlDraw.color color;
376 Ffi.begintiles ();
377 let f col row x y tilex tiley w h =
378 match gettileopaque l col row with
379 | Some (opaque, _, t) ->
380 let params = x, y, w, h, tilex, tiley in
381 if conf.invert
382 then GlTex.env (`mode `blend);
383 Ffi.drawtile params opaque;
384 if conf.invert
385 then GlTex.env (`mode `modulate);
386 if conf.debug
387 then (
388 Ffi.endtiles ();
389 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
390 let w = measurestr fstate.fontsize s in
391 GlDraw.color (0.0, 0.0, 0.0);
392 filledrect
393 (float (x-2))
394 (float (y-2))
395 (float (x+2) +. w)
396 (float (y + fstate.fontsize + 2));
397 GlDraw.color color;
398 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
399 Ffi.begintiles ();
402 | None ->
403 Ffi.endtiles ();
404 let w = let lw = state.winw - x in min lw w
405 and h = let lh = state.winh - y in min lh h
407 if conf.invert
408 then GlTex.env (`mode `blend);
409 begin match !checkerstexid with
410 | Some id ->
411 Gl.enable `texture_2d;
412 GlTex.bind_texture ~target:`texture_2d id;
413 let x0 = float x
414 and y0 = float y
415 and x1 = float (x+w)
416 and y1 = float (y+h) in
418 let tw = float w /. 16.0
419 and th = float h /. 16.0 in
420 let tx0 = float tilex /. 16.0
421 and ty0 = float tiley /. 16.0 in
422 let tx1 = tx0 +. tw
423 and ty1 = ty0 +. th in
424 Raw.sets_float Glutils.vraw ~pos:0
425 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
426 Raw.sets_float Glutils.traw ~pos:0
427 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
428 GlArray.vertex `two Glutils.vraw;
429 GlArray.tex_coord `two Glutils.traw;
430 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
431 Gl.disable `texture_2d;
433 | None ->
434 GlDraw.color (1.0, 1.0, 1.0);
435 filledrect (float x) (float y) (float (x+w)) (float (y+h));
436 end;
437 if conf.invert
438 then GlTex.env (`mode `modulate);
439 if w > 128 && h > fstate.fontsize + 10
440 then (
441 let c = if conf.invert then 1.0 else 0.0 in
442 GlDraw.color (c, c, c);
443 let c, r =
444 if conf.verbose
445 then (col*conf.tilew, row*conf.tileh)
446 else col, row
448 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
450 GlDraw.color color;
451 Ffi.begintiles ();
453 itertiles l f;
454 Ffi.endtiles ();
457 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
459 let tilevisible1 l x y =
460 let ax0 = l.pagex
461 and ax1 = l.pagex + l.pagevw
462 and ay0 = l.pagey
463 and ay1 = l.pagey + l.pagevh in
465 let bx0 = x
466 and by0 = y in
467 let bx1 = min (bx0 + conf.tilew) l.pagew
468 and by1 = min (by0 + conf.tileh) l.pageh in
470 let rx0 = max ax0 bx0
471 and ry0 = max ay0 by0
472 and rx1 = min ax1 bx1
473 and ry1 = min ay1 by1 in
475 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
476 nonemptyintersection
479 let tilevisible layout n x y =
480 let rec findpageinlayout m = function
481 | l :: rest when l.pageno = n ->
482 tilevisible1 l x y || (
483 match conf.columns with
484 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
485 | Csplit _ | Csingle _ | Cmulti _ -> false
487 | _ :: rest -> findpageinlayout 0 rest
488 | [] -> false
490 findpageinlayout 0 layout;
493 let tileready l x y =
494 tilevisible1 l x y &&
495 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
498 let tilepage n p layout =
499 let rec loop = function
500 | l :: rest ->
501 if l.pageno = n
502 then
503 let f col row _ _ _ _ _ _ =
504 if state.currently = Idle
505 then
506 match gettileopaque l col row with
507 | Some _ -> ()
508 | None ->
509 let x = col*conf.tilew
510 and y = row*conf.tileh in
511 let w =
512 let w = l.pagew - x in
513 min w conf.tilew
515 let h =
516 let h = l.pageh - y in
517 min h conf.tileh
519 let pbo =
520 if conf.usepbo
521 then Ffi.getpbo w h conf.colorspace
522 else ~< "0"
524 wcmd "tile %s %d %d %d %d %s" (~> p) x y w h (~> pbo);
525 state.currently <-
526 Tiling (
527 l, p, conf.colorspace, conf.angle,
528 state.gen, col, row, conf.tilew, conf.tileh
531 itertiles l f;
532 else
533 loop rest
535 | [] -> ()
537 if nogeomcmds state.geomcmds
538 then loop layout;
541 let preloadlayout x y sw sh =
542 let y = if y < sh then 0 else y - sh in
543 let x = min 0 (x + sw) in
544 let h = sh*3 in
545 let w = sw*3 in
546 layout x y w h;
549 let load pages =
550 let rec loop pages =
551 if state.currently = Idle
552 then
553 match pages with
554 | l :: rest ->
555 begin match getopaque l.pageno with
556 | None ->
557 wcmd "page %d %d" l.pageno l.pagedimno;
558 state.currently <- Loading (l, state.gen);
559 | Some opaque ->
560 tilepage l.pageno opaque pages;
561 loop rest
562 end;
563 | _ -> ()
565 if nogeomcmds state.geomcmds
566 then loop pages
569 let preload pages =
570 load pages;
571 if conf.preload && state.currently = Idle
572 then load (preloadlayout state.x state.y state.winw state.winh);
575 let layoutready layout =
576 let rec fold all ls =
577 all && match ls with
578 | l :: rest ->
579 let seen = ref false in
580 let allvisible = ref true in
581 let foo col row _ _ _ _ _ _ =
582 seen := true;
583 allvisible := !allvisible &&
584 begin match gettileopaque l col row with
585 | Some _ -> true
586 | None -> false
589 itertiles l foo;
590 fold (!seen && !allvisible) rest
591 | [] -> true
593 let alltilesvisible = fold true layout in
594 alltilesvisible;
597 let gotoxy x y =
598 let y = bound y 0 state.maxy in
599 let y, layout =
600 let layout = layout x y state.winw state.winh in
601 postRedisplay "gotoxy ready";
602 y, layout
604 state.x <- x;
605 state.y <- y;
606 state.layout <- layout;
607 begin match state.mode with
608 | LinkNav ln ->
609 begin match ln with
610 | Ltexact (pageno, linkno) ->
611 let rec loop = function
612 | [] ->
613 state.lnava <- Some (pageno, linkno);
614 state.mode <- LinkNav (Ltgendir 0)
615 | l :: _ when l.pageno = pageno ->
616 begin match getopaque pageno with
617 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
618 | Some opaque ->
619 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
620 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
621 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
622 then state.mode <- LinkNav (Ltgendir 0)
624 | _ :: rest -> loop rest
626 loop layout
627 | Ltnotready _ | Ltgendir _ -> ()
629 | Birdseye _ | Textentry _ | View -> ()
630 end;
631 begin match state.mode with
632 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
633 if not (pagevisible layout pageno)
634 then (
635 match state.layout with
636 | [] -> ()
637 | l :: _ ->
638 state.mode <- Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
640 | LinkNav lt ->
641 begin match lt with
642 | Ltnotready (_, dir)
643 | Ltgendir dir ->
644 let linknav =
645 let rec loop = function
646 | [] -> lt
647 | l :: rest ->
648 match getopaque l.pageno with
649 | None -> Ltnotready (l.pageno, dir)
650 | Some opaque ->
651 let link =
652 let ld =
653 if dir = 0
654 then LDfirstvisible (l.pagex, l.pagey, dir)
655 else if dir > 0 then LDfirst else LDlast
657 Ffi.findlink opaque ld
659 match link with
660 | Lnotfound -> loop rest
661 | Lfound n ->
662 showlinktype (Ffi.getlink opaque n);
663 Ltexact (l.pageno, n)
665 loop state.layout
667 state.mode <- LinkNav linknav
668 | Ltexact _ -> ()
670 | Textentry _ | View -> ()
671 end;
672 preload layout;
673 if conf.updatecurs
674 then (
675 let mx, my = state.mpos in
676 updateunder mx my;
680 let conttiling pageno opaque =
681 tilepage pageno opaque
682 (if conf.preload
683 then preloadlayout state.x state.y state.winw state.winh
684 else state.layout)
687 let gotoxy x y =
688 if not conf.verbose then state.text <- E.s;
689 gotoxy x y;
692 let getanchory (n, top, dtop) =
693 let y, h = getpageyh n in
694 if conf.presentation
695 then
696 let ips = calcips h in
697 y + truncate (top*.float h -. dtop*.float ips) + ips;
698 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
701 let addnav () =
702 state.nav <- { past = getanchor () :: state.nav.past; future = []; }
705 let gotopage n top =
706 let y, h = getpageyh n in
707 let y = y + (truncate (top *. float h)) in
708 gotoxy state.x y
711 let gotopage1 n top =
712 let y = getpagey n in
713 let y = y + top in
714 gotoxy state.x y
717 let invalidate s f =
718 Glutils.redisplay := false;
719 state.layout <- [];
720 state.pdims <- [];
721 state.rects <- [];
722 state.rects1 <- [];
723 match state.geomcmds with
724 | ps, [] when emptystr ps ->
725 f ();
726 state.geomcmds <- s, [];
727 | ps, [] -> state.geomcmds <- ps, [s, f];
728 | ps, (s', _) :: rest when s' = s -> state.geomcmds <- ps, ((s, f) :: rest);
729 | ps, cmds -> state.geomcmds <- ps, ((s, f) :: cmds);
732 let flushpages () =
733 Hashtbl.iter (fun _ opaque -> wcmd "freepage %s" (~> opaque)) state.pagemap;
734 Hashtbl.clear state.pagemap;
737 let flushtiles () =
738 if not (Queue.is_empty state.tilelru)
739 then (
740 Queue.iter (fun (k, p, s) ->
741 wcmd "freetile %s" (~> p);
742 state.memused <- state.memused - s;
743 Hashtbl.remove state.tilemap k;
744 ) state.tilelru;
745 state.uioh#infochanged Memused;
746 Queue.clear state.tilelru;
748 load state.layout;
751 let stateh h =
752 let h = truncate (float h*.conf.zoom) in
753 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
754 h - d
757 let fillhelp () =
758 state.help <-
759 let sl = keystostrlist conf in
760 let rec loop accu =
761 function | [] -> accu
762 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
763 in Help.makehelp conf.urilauncher
764 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
767 let opendoc path password =
768 state.path <- path;
769 state.password <- password;
770 state.gen <- state.gen + 1;
771 state.docinfo <- [];
772 state.outlines <- [||];
774 flushpages ();
775 Ffi.setaalevel conf.aalevel;
776 Ffi.setpapercolor conf.papercolor;
777 let titlepath =
778 if emptystr state.origin
779 then path
780 else state.origin
782 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
783 wcmd "open %d %d %s\000%s\000%s\000"
784 (btod conf.usedoccss) !layouth
785 path password conf.css;
786 invalidate "reqlayout"
787 (fun () ->
788 wcmd "reqlayout %d %d %d %s\000"
789 conf.angle (FMTE.to_int conf.fitmodel)
790 (stateh state.winh) state.nameddest
792 fillhelp ();
795 let reload () =
796 state.anchor <- getanchor ();
797 state.reload <- Some (state.x, state.y, now ());
798 opendoc state.path state.password;
801 let scalecolor c = let c = c *. conf.colorscale in (c, c, c);;
802 let scalecolor2 (r, g, b) =
803 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
806 let docolumns columns =
807 match columns with
808 | Csingle _ ->
809 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
810 let rec loop pageno pdimno pdim y ph pdims =
811 if pageno != state.pagecount
812 then
813 let pdimno, ((_, w, h, xoff) as pdim), pdims =
814 match pdims with
815 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
816 pdimno+1, pdim, rest
817 | _ ->
818 pdimno, pdim, pdims
820 let x = max 0 (((state.winw - w) / 2) - xoff) in
821 let y =
822 y + (if conf.presentation
823 then (if pageno = 0 then calcips h else calcips ph + calcips h)
824 else (if pageno = 0 then 0 else conf.interpagespace))
826 a.(pageno) <- (pdimno, x, y, pdim);
827 loop (pageno+1) pdimno pdim (y + h) h pdims
829 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
830 conf.columns <- Csingle a;
832 | Cmulti ((columns, coverA, coverB), _) ->
833 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
834 let rec loop pageno pdimno pdim x y rowh pdims =
835 let rec fixrow m =
836 if m = pageno then () else
837 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
838 if h < rowh
839 then (
840 let y = y + (rowh - h) / 2 in
841 a.(m) <- (pdimno, x, y, pdim);
843 fixrow (m+1)
845 if pageno = state.pagecount
846 then fixrow (((pageno - 1) / columns) * columns)
847 else
848 let pdimno, ((_, w, h, xoff) as pdim), pdims =
849 match pdims with
850 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
851 pdimno+1, pdim, rest
852 | _ -> pdimno, pdim, pdims
854 let x, y, rowh' =
855 if pageno = coverA - 1 || pageno = state.pagecount - coverB
856 then (
857 let x = (state.winw - w) / 2 in
858 let ips =
859 if conf.presentation then calcips h else conf.interpagespace in
860 x, y + ips + rowh, h
862 else (
863 if (pageno - coverA) mod columns = 0
864 then (
865 let x = max 0 (state.winw - state.w) / 2 in
866 let y =
867 if conf.presentation
868 then
869 let ips = calcips h in
870 y + (if pageno = 0 then 0 else calcips rowh + ips)
871 else
872 y + (if pageno = 0 then 0 else conf.interpagespace)
874 x, y + rowh, h
876 else x, y, max rowh h
879 let y =
880 if pageno > 1 && (pageno - coverA) mod columns = 0
881 then (
882 let y =
883 if pageno = columns && conf.presentation
884 then (
885 let ips = calcips rowh in
886 for i = 0 to pred columns
888 let (pdimno, x, y, pdim) = a.(i) in
889 a.(i) <- (pdimno, x, y+ips, pdim)
890 done;
891 y+ips;
893 else y
895 fixrow (pageno - columns);
898 else y
900 a.(pageno) <- (pdimno, x, y, pdim);
901 let x = x + w + xoff*2 + conf.interpagespace in
902 loop (pageno+1) pdimno pdim x y rowh' pdims
904 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
905 conf.columns <- Cmulti ((columns, coverA, coverB), a);
907 | Csplit (c, _) ->
908 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
909 let rec loop pageno pdimno pdim y pdims =
910 if pageno != state.pagecount
911 then
912 let pdimno, ((_, w, h, _) as pdim), pdims =
913 match pdims with
914 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
915 pdimno+1, pdim, rest
916 | _ -> pdimno, pdim, pdims
918 let cw = w / c in
919 let rec loop1 n x y =
920 if n = c then y else (
921 a.(pageno*c + n) <- (pdimno, x, y, pdim);
922 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
925 let y = loop1 0 0 y in
926 loop (pageno+1) pdimno pdim y pdims
928 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
929 conf.columns <- Csplit (c, a);
932 let represent () =
933 docolumns conf.columns;
934 state.maxy <- calcheight ();
935 if state.reprf == noreprf
936 then (
937 match state.mode with
938 | Birdseye (_, _, pageno, _, _) ->
939 let y, h = getpageyh pageno in
940 let top = (state.winh - h) / 2 in
941 gotoxy state.x (max 0 (y - top))
942 | Textentry _ | View | LinkNav _ ->
943 let y = getanchory state.anchor in
944 let y = min y (state.maxy - state.winh) in
945 gotoxy state.x y;
947 else (
948 state.reprf ();
949 state.reprf <- noreprf;
953 let reshape ?(firsttime=false) w h =
954 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
955 if not firsttime && nogeomcmds state.geomcmds
956 then state.anchor <- getanchor ();
958 state.winw <- w;
959 let w = truncate (float w *. conf.zoom) in
960 let w = max w 2 in
961 state.winh <- h;
962 setfontsize fstate.fontsize;
963 GlMat.mode `modelview;
964 GlMat.load_identity ();
966 GlMat.mode `projection;
967 GlMat.load_identity ();
968 GlMat.rotate ~x:1.0 ~angle:180.0 ();
969 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
970 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
972 let relx =
973 if conf.zoom <= 1.0
974 then 0.0
975 else float state.x /. float state.w
977 invalidate "geometry"
978 (fun () ->
979 state.w <- w;
980 if not firsttime
981 then state.x <- truncate (relx *. float w);
982 let w =
983 match conf.columns with
984 | Csingle _ -> w
985 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
986 | Csplit (c, _) -> w * c
988 wcmd "geometry %d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
992 let gctiles () =
993 let len = Queue.length state.tilelru in
994 let layout = lazy (if conf.preload
995 then preloadlayout state.x state.y state.winw state.winh
996 else state.layout) in
997 let rec loop qpos =
998 if state.memused > conf.memlimit
999 then (
1000 if qpos < len
1001 then
1002 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1003 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1004 let (_, pw, ph, _) = getpagedim n in
1005 if gen = state.gen
1006 && colorspace = conf.colorspace
1007 && angle = conf.angle
1008 && pagew = pw
1009 && pageh = ph
1010 && (
1011 let x = col*conf.tilew and y = row*conf.tileh in
1012 tilevisible (Lazy.force_val layout) n x y
1014 then Queue.push lruitem state.tilelru
1015 else (
1016 Ffi.freepbo p;
1017 wcmd "freetile %s" (~> p);
1018 state.memused <- state.memused - s;
1019 state.uioh#infochanged Memused;
1020 Hashtbl.remove state.tilemap k;
1022 loop (qpos+1)
1025 loop 0
1028 let onpagerect pageno f =
1029 let b =
1030 match conf.columns with
1031 | Cmulti (_, b) -> b
1032 | Csingle b -> b
1033 | Csplit (_, b) -> b
1035 if pageno >= 0 && pageno < Array.length b
1036 then
1037 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1038 f w h
1041 let gotopagexy1 pageno x y =
1042 let _,w1,h1,leftx = getpagedim pageno in
1043 let top = y /. (float h1) in
1044 let left = x /. (float w1) in
1045 let py, w, h = getpageywh pageno in
1046 let wh = state.winh in
1047 let x = left *. (float w) in
1048 let x = leftx + state.x + truncate x in
1049 let sx =
1050 if x < 0 || x >= state.winw
1051 then state.x - x
1052 else state.x
1054 let pdy = truncate (top *. float h) in
1055 let y' = py + pdy in
1056 let dy = y' - state.y in
1057 let sy =
1058 if x != state.x || not (dy > 0 && dy < wh)
1059 then (
1060 if conf.presentation
1061 then
1062 if abs (py - y') > wh
1063 then y'
1064 else py
1065 else y';
1067 else state.y
1069 if state.x != sx || state.y != sy
1070 then gotoxy sx sy
1071 else gotoxy state.x state.y;
1074 let gotopagexy pageno x y =
1075 match state.mode with
1076 | Birdseye _ -> gotopage pageno 0.0
1077 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1080 let getpassword () =
1081 let passcmd = getenvdef "LLPP_ASKPASS" conf.passcmd in
1082 if emptystr passcmd
1083 then E.s
1084 else getcmdoutput (fun s ->
1085 impmsg "error getting password: %s" s;
1086 dolog "%s" s) passcmd;
1089 let pgoto opaque pageno x y =
1090 let pdimno = getpdimno pageno in
1091 let x, y = Ffi.project opaque pageno pdimno x y in
1092 gotopagexy pageno x y;
1095 let act cmds =
1096 (* dolog "%S" cmds; *)
1097 let spl = splitatchar cmds ' ' in
1098 let scan s fmt f =
1099 try Scanf.sscanf s fmt f
1100 with exn ->
1101 dolog "error processing '%S': %s" cmds @@ exntos exn;
1102 exit 1
1104 let addoutline outline =
1105 match state.currently with
1106 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1107 | Idle -> state.currently <- Outlining [outline]
1108 | Loading _ | Tiling _ ->
1109 dolog "invalid outlining state";
1110 logcurrently state.currently
1112 match spl with
1113 | "clear", "" ->
1114 state.pdims <- [];
1115 state.uioh#infochanged Pdim;
1117 | "clearrects", "" ->
1118 state.rects <- state.rects1;
1119 postRedisplay "clearrects";
1121 | "continue", args ->
1122 let n = scan args "%u" (fun n -> n) in
1123 state.pagecount <- n;
1124 begin match state.currently with
1125 | Outlining l ->
1126 state.currently <- Idle;
1127 state.outlines <- Array.of_list (List.rev l)
1128 | Idle | Loading _ | Tiling _ -> ()
1129 end;
1131 let cur, cmds = state.geomcmds in
1132 if emptystr cur then error "empty geomcmd";
1134 begin match List.rev cmds with
1135 | [] ->
1136 state.geomcmds <- E.s, [];
1137 represent ();
1138 | (s, f) :: rest ->
1139 f ();
1140 state.geomcmds <- s, List.rev rest;
1141 end;
1142 postRedisplay "continue";
1144 | "msg", args ->
1145 showtext ' ' args
1147 | "vmsg", args ->
1148 if conf.verbose then showtext ' ' args
1150 | "emsg", args ->
1151 Buffer.add_string state.errmsgs args;
1152 Buffer.add_char state.errmsgs '\n';
1153 if not state.newerrmsgs
1154 then (
1155 state.newerrmsgs <- true;
1156 postRedisplay "error message";
1159 | "progress", args ->
1160 let progress, text =
1161 scan args "%f %n"
1162 (fun f pos -> f, String.sub args pos (String.length args - pos))
1164 state.text <- text;
1165 state.progress <- progress;
1166 postRedisplay "progress"
1168 | "firstmatch", args ->
1169 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1170 scan args "%u %d %f %f %f %f %f %f %f %f"
1171 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1172 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1174 let y = (getpagey pageno) + truncate y0 in
1175 let x =
1176 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1177 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1178 else state.x
1180 addnav ();
1181 gotoxy x y;
1182 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1183 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1185 | "match", args ->
1186 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1187 scan args "%u %d %f %f %f %f %f %f %f %f"
1188 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1189 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1191 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1192 state.rects1 <-
1193 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1195 | "page", args ->
1196 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1197 let pageopaque = ~< pageopaques in
1198 begin match state.currently with
1199 | Loading (l, gen) ->
1200 vlog "page %d took %f sec" l.pageno t;
1201 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1202 let preloadedpages =
1203 if conf.preload
1204 then preloadlayout state.x state.y state.winw state.winh
1205 else state.layout
1207 let evict () =
1208 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1209 IntSet.empty preloadedpages
1211 let evictedpages =
1212 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1213 if not (IntSet.mem pageno set)
1214 then (
1215 wcmd "freepage %s" (~> opaque);
1216 key :: accu
1218 else accu
1219 ) state.pagemap []
1221 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1223 evict ();
1224 state.currently <- Idle;
1225 if gen = state.gen
1226 then (
1227 tilepage l.pageno pageopaque state.layout;
1228 load state.layout;
1229 load preloadedpages;
1230 let visible = pagevisible state.layout l.pageno in
1231 if visible
1232 then (
1233 match state.mode with
1234 | LinkNav (Ltnotready (pageno, dir)) ->
1235 if pageno = l.pageno
1236 then (
1237 let link =
1238 let ld =
1239 if dir = 0
1240 then LDfirstvisible (l.pagex, l.pagey, dir)
1241 else if dir > 0 then LDfirst else LDlast
1243 Ffi.findlink pageopaque ld
1245 match link with
1246 | Lnotfound -> ()
1247 | Lfound n ->
1248 showlinktype (Ffi.getlink pageopaque n);
1249 state.mode <- LinkNav (Ltexact (l.pageno, n))
1251 | LinkNav (Ltgendir _)
1252 | LinkNav (Ltexact _)
1253 | View
1254 | Birdseye _
1255 | Textentry _ -> ()
1258 if visible && layoutready state.layout
1259 then postRedisplay "page";
1262 | Idle | Tiling _ | Outlining _ ->
1263 dolog "Inconsistent loading state";
1264 logcurrently state.currently;
1265 exit 1
1268 | "tile" , args ->
1269 let (x, y, opaques, size, t) =
1270 scan args "%u %u %s %u %f" (fun x y p size t -> (x, y, p, size, t))
1272 let opaque = ~< opaques in
1273 begin match state.currently with
1274 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1275 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1277 Ffi.unmappbo opaque;
1278 if tilew != conf.tilew || tileh != conf.tileh
1279 then (
1280 wcmd "freetile %s" (~> opaque);
1281 state.currently <- Idle;
1282 load state.layout;
1284 else (
1285 puttileopaque l col row gen cs angle opaque size t;
1286 state.memused <- state.memused + size;
1287 state.uioh#infochanged Memused;
1288 gctiles ();
1289 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1290 opaque, size) state.tilelru;
1292 state.currently <- Idle;
1293 if gen = state.gen
1294 && conf.colorspace = cs
1295 && conf.angle = angle
1296 && tilevisible state.layout l.pageno x y
1297 then conttiling l.pageno pageopaque;
1299 preload state.layout;
1300 if gen = state.gen
1301 && conf.colorspace = cs
1302 && conf.angle = angle
1303 && tilevisible state.layout l.pageno x y
1304 && layoutready state.layout
1305 then postRedisplay "tile nothrottle";
1308 | Idle | Loading _ | Outlining _ ->
1309 dolog "Inconsistent tiling state";
1310 logcurrently state.currently;
1311 exit 1
1314 | "pdim", args ->
1315 let (n, w, h, _) as pdim =
1316 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1318 let pdim =
1319 match conf.fitmodel with
1320 | FitWidth -> pdim
1321 | FitPage | FitProportional ->
1322 match conf.columns with
1323 | Csplit _ -> (n, w, h, 0)
1324 | Csingle _ | Cmulti _ -> pdim
1326 state.pdims <- pdim :: state.pdims;
1327 state.uioh#infochanged Pdim
1329 | "o", args ->
1330 let (l, n, t, h, pos) =
1331 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1333 let s = String.sub args pos (String.length args - pos) in
1334 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1336 | "ou", args ->
1337 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1338 let s = String.sub args pos len in
1339 let pos2 = pos + len + 1 in
1340 let uri = String.sub args pos2 (String.length args - pos2) in
1341 addoutline (s, l, Ouri uri)
1343 | "on", args ->
1344 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1345 let s = String.sub args pos (String.length args - pos) in
1346 addoutline (s, l, Onone)
1348 | "a", args ->
1349 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1350 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1352 | "info", args ->
1353 let c, v = splitatchar args '\t' in
1354 let s =
1355 if nonemptystr v
1356 then
1357 if c = "Title"
1358 then (
1359 conf.title <- v;
1360 if not !ignoredoctitlte then Wsi.settitle v;
1361 args
1363 else
1364 if let len = String.length c in
1365 len > 6 && ((String.sub c (len-4) 4) = "date")
1366 then (
1367 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1368 then
1369 let b = Buffer.create 10 in
1370 Printf.bprintf b "%s\t" c;
1371 let sub p l c =
1373 Buffer.add_substring b v p l;
1374 Buffer.add_char b c;
1375 with exn -> Buffer.add_string b @@ exntos exn
1377 sub 2 4 '/';
1378 sub 6 2 '/';
1379 sub 8 2 ' ';
1380 sub 10 2 ':';
1381 sub 12 2 ':';
1382 sub 14 2 ' ';
1383 Printf.bprintf b "[%s]" v;
1384 Buffer.contents b
1385 else args
1387 else args
1388 else args
1390 state.docinfo <- (1, s) :: state.docinfo
1392 | "infoend", "" ->
1393 state.docinfo <- List.rev state.docinfo;
1394 state.uioh#infochanged Docinfo
1396 | "pass", args ->
1397 if args = "fail"
1398 then Wsi.settitle "Wrong password";
1399 let password = getpassword () in
1400 if emptystr password
1401 then error "document is password protected"
1402 else opendoc state.path password
1404 | _ -> error "unknown cmd `%S'" cmds
1407 let onhist cb =
1408 let rc = cb.rc in
1409 let action = function
1410 | HCprev -> cbget cb ~-1
1411 | HCnext -> cbget cb 1
1412 | HCfirst -> cbget cb ~-(cb.rc)
1413 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1414 and cancel () = cb.rc <- rc
1415 in (action, cancel)
1418 let search pattern forward =
1419 match conf.columns with
1420 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1421 | Csingle _ | Cmulti _ ->
1422 if nonemptystr pattern
1423 then
1424 let pn, py =
1425 match state.layout with
1426 | [] -> 0, 0
1427 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1429 wcmd "search %d %d %d %d,%s\000"
1430 (btod conf.icase) pn py (btod forward) pattern;
1433 let intentry text key =
1434 let text =
1435 if emptystr text && key = Keys.Ascii '-'
1436 then addchar text '-'
1437 else
1438 match [@warning "-4"] key with
1439 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1440 | _ ->
1441 state.text <- "invalid key";
1442 text
1444 TEcont text
1447 let linknact f s =
1448 if nonemptystr s
1449 then
1450 let n =
1451 let l = String.length s in
1452 let rec loop pos n =
1453 if pos = l
1454 then n
1455 else
1456 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1457 loop (pos+1) (n*26 + m)
1458 in loop 0 0
1460 let rec loop n = function
1461 | [] -> ()
1462 | l :: rest ->
1463 match getopaque l.pageno with
1464 | None -> loop n rest
1465 | Some opaque ->
1466 let m = Ffi.getlinkcount opaque in
1467 if n < m
1468 then
1469 let under = Ffi.getlink opaque n in
1470 f under
1471 else loop (n-m) rest
1473 loop n state.layout;
1476 let linknentry text key = match [@warning "-4"] key with
1477 | Keys.Ascii ('a' .. 'z' as c) ->
1478 let text = addchar text c in
1479 linknact (fun under -> state.text <- undertext under) text;
1480 TEcont text
1481 | _ ->
1482 state.text <- Printf.sprintf "invalid key %s" @@ Keys.to_string key;
1483 TEcont text
1486 let textentry text key = match [@warning "-4"] key with
1487 | Keys.Ascii c -> TEcont (addchar text c)
1488 | Keys.Code c -> TEcont (text ^ toutf8 c)
1489 | _ -> TEcont text
1492 let reqlayout angle fitmodel =
1493 if nogeomcmds state.geomcmds
1494 then state.anchor <- getanchor ();
1495 conf.angle <- angle mod 360;
1496 if conf.angle != 0
1497 then (
1498 match state.mode with
1499 | LinkNav _ -> state.mode <- View
1500 | Birdseye _ | Textentry _ | View -> ()
1502 conf.fitmodel <- fitmodel;
1503 invalidate "reqlayout"
1504 (fun () -> wcmd "reqlayout %d %d %d"
1505 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh));
1508 let settrim trimmargins trimfuzz =
1509 if nogeomcmds state.geomcmds
1510 then state.anchor <- getanchor ();
1511 conf.trimmargins <- trimmargins;
1512 conf.trimfuzz <- trimfuzz;
1513 let x0, y0, x1, y1 = trimfuzz in
1514 invalidate "settrim"
1515 (fun () -> wcmd "settrim %d %d %d %d %d"
1516 (btod conf.trimmargins) x0 y0 x1 y1);
1517 flushpages ();
1520 let setzoom zoom =
1521 let zoom = max 0.0001 zoom in
1522 if zoom <> conf.zoom
1523 then (
1524 state.prevzoom <- (conf.zoom, state.x);
1525 conf.zoom <- zoom;
1526 reshape state.winw state.winh;
1527 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1531 let pivotzoom ?(vw=min state.w state.winw)
1532 ?(vh=min (state.maxy-state.y) state.winh)
1533 ?(x=vw/2) ?(y=vh/2) zoom =
1534 let w = float state.w /. zoom in
1535 let hw = w /. 2.0 in
1536 let ratio = float vh /. float vw in
1537 let hh = hw *. ratio in
1538 let x0 = float x -. hw
1539 and y0 = float y -. hh in
1540 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1541 setzoom zoom;
1544 let pivotzoom ?vw ?vh ?x ?y zoom =
1545 if nogeomcmds state.geomcmds
1546 then
1547 if zoom > 1.0
1548 then pivotzoom ?vw ?vh ?x ?y zoom
1549 else setzoom zoom
1552 let setcolumns mode columns coverA coverB =
1553 state.prevcolumns <- Some (conf.columns, conf.zoom);
1554 if columns < 0
1555 then (
1556 if isbirdseye mode
1557 then impmsg "split mode doesn't work in bird's eye"
1558 else (
1559 conf.columns <- Csplit (-columns, E.a);
1560 state.x <- 0;
1561 conf.zoom <- 1.0;
1564 else (
1565 if columns < 2
1566 then (
1567 conf.columns <- Csingle E.a;
1568 state.x <- 0;
1569 setzoom 1.0;
1571 else (
1572 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1573 conf.zoom <- 1.0;
1576 reshape state.winw state.winh;
1579 let resetmstate () =
1580 state.mstate <- Mnone;
1581 Wsi.setcursor Wsi.CURSOR_INHERIT;
1584 let enterbirdseye () =
1585 let zoom = float conf.thumbw /. float state.winw in
1586 let birdseyepageno =
1587 let cy = state.winh / 2 in
1588 let fold = function
1589 | [] -> 0
1590 | l :: rest ->
1591 let rec fold best = function
1592 | [] -> best.pageno
1593 | l :: rest ->
1594 let d = cy - (l.pagedispy + l.pagevh/2)
1595 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1596 if abs d < abs dbest
1597 then fold l rest
1598 else best.pageno
1599 in fold l rest
1601 fold state.layout
1603 state.mode <-
1604 Birdseye (
1605 { conf with zoom = conf.zoom },
1606 state.x, birdseyepageno, -1, getanchor ()
1608 resetmstate ();
1609 conf.zoom <- zoom;
1610 conf.presentation <- false;
1611 conf.interpagespace <- 10;
1612 conf.hlinks <- false;
1613 conf.fitmodel <- FitPage;
1614 state.x <- 0;
1615 conf.columns <- (
1616 match conf.beyecolumns with
1617 | Some c ->
1618 conf.zoom <- 1.0;
1619 Cmulti ((c, 0, 0), E.a)
1620 | None -> Csingle E.a
1622 if conf.verbose
1623 then state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1624 (100.0*.zoom)
1625 else state.text <- E.s;
1626 reshape state.winw state.winh;
1629 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1630 state.mode <- View;
1631 conf.zoom <- c.zoom;
1632 conf.presentation <- c.presentation;
1633 conf.interpagespace <- c.interpagespace;
1634 conf.hlinks <- c.hlinks;
1635 conf.fitmodel <- c.fitmodel;
1636 conf.beyecolumns <- (
1637 match conf.columns with
1638 | Cmulti ((c, _, _), _) -> Some c
1639 | Csingle _ -> None
1640 | Csplit _ -> error "leaving bird's eye split mode"
1642 conf.columns <- (
1643 match c.columns with
1644 | Cmulti (c, _) -> Cmulti (c, E.a)
1645 | Csingle _ -> Csingle E.a
1646 | Csplit (c, _) -> Csplit (c, E.a)
1648 if conf.verbose
1649 then state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1650 (100.0*.conf.zoom);
1651 reshape state.winw state.winh;
1652 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1653 state.x <- leftx;
1656 let togglebirdseye () =
1657 match state.mode with
1658 | Birdseye vals -> leavebirdseye vals true
1659 | View -> enterbirdseye ()
1660 | Textentry _ | LinkNav _ -> ()
1663 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1664 let pageno = max 0 (pageno - incr) in
1665 let rec loop = function
1666 | [] -> gotopage1 pageno 0
1667 | l :: _ when l.pageno = pageno ->
1668 if l.pagedispy >= 0 && l.pagey = 0
1669 then postRedisplay "upbirdseye"
1670 else gotopage1 pageno 0
1671 | _ :: rest -> loop rest
1673 loop state.layout;
1674 state.text <- E.s;
1675 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1678 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1679 let pageno = min (state.pagecount - 1) (pageno + incr) in
1680 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1681 let rec loop = function
1682 | [] ->
1683 let y, h = getpageyh pageno in
1684 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1685 gotoxy state.x (clamp dy)
1686 | l :: _ when l.pageno = pageno ->
1687 if l.pagevh != l.pageh
1688 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1689 else postRedisplay "downbirdseye"
1690 | _ :: rest -> loop rest
1692 loop state.layout;
1693 state.text <- E.s;
1696 let optentry mode _ key =
1697 let btos b = if b then "on" else "off" in
1698 match [@warning "-4"] key with
1699 | Keys.Ascii 'C' ->
1700 let ondone s =
1702 let n, a, b = multicolumns_of_string s in
1703 setcolumns mode n a b;
1704 with exn ->
1705 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1707 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1709 | Keys.Ascii 'Z' ->
1710 let ondone s =
1712 let zoom = float (int_of_string s) /. 100.0 in
1713 pivotzoom zoom
1714 with exn ->
1715 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1717 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1719 | Keys.Ascii 'i' ->
1720 conf.icase <- not conf.icase;
1721 TEdone ("case insensitive search " ^ (btos conf.icase))
1723 | Keys.Ascii 'v' ->
1724 conf.verbose <- not conf.verbose;
1725 TEdone ("verbose " ^ (btos conf.verbose))
1727 | Keys.Ascii 'd' ->
1728 conf.debug <- not conf.debug;
1729 TEdone ("debug " ^ (btos conf.debug))
1731 | Keys.Ascii 'f' ->
1732 conf.underinfo <- not conf.underinfo;
1733 TEdone ("underinfo " ^ btos conf.underinfo)
1735 | Keys.Ascii 'T' ->
1736 settrim (not conf.trimmargins) conf.trimfuzz;
1737 TEdone ("trim margins " ^ btos conf.trimmargins)
1739 | Keys.Ascii 'I' ->
1740 conf.invert <- not conf.invert;
1741 TEdone ("invert colors " ^ btos conf.invert)
1743 | Keys.Ascii 'x' ->
1744 let ondone s =
1745 cbput state.hists.sel s;
1746 conf.selcmd <- s;
1748 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1749 textentry, ondone, true)
1751 | Keys.Ascii 'M' ->
1752 if conf.pax == None
1753 then conf.pax <- Some 0.0
1754 else conf.pax <- None;
1755 TEdone ("PAX " ^ btos (conf.pax != None))
1757 | (Keys.Ascii c) ->
1758 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1759 TEstop
1761 | _ -> TEcont state.text
1764 let adderrmsg src msg =
1765 Buffer.add_string state.errmsgs msg;
1766 state.newerrmsgs <- true;
1767 postRedisplay src
1770 let adderrfmt src fmt = Format.ksprintf (fun s -> adderrmsg src s) fmt;;
1772 class outlinelistview ~zebra ~source =
1773 let settext autonarrow s =
1774 if autonarrow
1775 then
1776 let ss = source#statestr in
1777 state.text <- if emptystr ss
1778 then "[" ^ s ^ "]"
1779 else "{" ^ ss ^ "} [" ^ s ^ "]"
1780 else state.text <- s
1782 object (self)
1783 inherit listview
1784 ~zebra
1785 ~helpmode:false
1786 ~source:(source :> lvsource)
1787 ~trusted:false
1788 ~modehash:(findkeyhash conf "outline")
1789 as super
1791 val m_autonarrow = false
1793 method! key key mask =
1794 let maxrows =
1795 if emptystr state.text
1796 then fstate.maxrows
1797 else fstate.maxrows - 2
1799 let calcfirst first active =
1800 if active > first
1801 then
1802 let rows = active - first in
1803 if rows > maxrows then active - maxrows else first
1804 else active
1806 let navigate incr =
1807 let active = m_active + incr in
1808 let active = bound active 0 (source#getitemcount - 1) in
1809 let first = calcfirst m_first active in
1810 postRedisplay "outline navigate";
1811 coe {< m_active = active; m_first = first >}
1813 let navscroll first =
1814 let active =
1815 let dist = m_active - first in
1816 if dist < 0
1817 then first
1818 else (
1819 if dist < maxrows
1820 then m_active
1821 else first + maxrows
1824 postRedisplay "outline navscroll";
1825 coe {< m_first = first; m_active = active >}
1827 let ctrl = Wsi.withctrl mask in
1828 let open Keys in
1829 match Wsi.kc2kt key with
1830 | Ascii 'a' when ctrl ->
1831 let text =
1832 if m_autonarrow
1833 then (
1834 source#denarrow;
1837 else (
1838 let pattern = source#renarrow in
1839 if nonemptystr m_qsearch
1840 then (source#narrow m_qsearch; m_qsearch)
1841 else pattern
1844 settext (not m_autonarrow) text;
1845 postRedisplay "toggle auto narrowing";
1846 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1848 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1849 settext true E.s;
1850 postRedisplay "toggle auto narrowing";
1851 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1853 | Ascii 'n' when ctrl ->
1854 source#narrow m_qsearch;
1855 if not m_autonarrow
1856 then source#add_narrow_pattern m_qsearch;
1857 postRedisplay "outline ctrl-n";
1858 coe {< m_first = 0; m_active = 0 >}
1860 | Ascii 'S' when ctrl ->
1861 let active = source#calcactive (getanchor ()) in
1862 let first = firstof m_first active in
1863 postRedisplay "outline ctrl-s";
1864 coe {< m_first = first; m_active = active >}
1866 | Ascii 'u' when ctrl ->
1867 postRedisplay "outline ctrl-u";
1868 if m_autonarrow && nonemptystr m_qsearch
1869 then (
1870 ignore (source#renarrow);
1871 settext m_autonarrow E.s;
1872 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1874 else (
1875 source#del_narrow_pattern;
1876 let pattern = source#renarrow in
1877 let text =
1878 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1880 settext m_autonarrow text;
1881 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1884 | Ascii 'l' when ctrl ->
1885 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1886 postRedisplay "outline ctrl-l";
1887 coe {< m_first = first >}
1889 | Ascii '\t' when m_autonarrow ->
1890 if nonemptystr m_qsearch
1891 then (
1892 postRedisplay "outline list view tab";
1893 source#add_narrow_pattern m_qsearch;
1894 settext true E.s;
1895 coe {< m_qsearch = E.s >}
1897 else coe self
1899 | Escape when m_autonarrow ->
1900 if nonemptystr m_qsearch
1901 then source#add_narrow_pattern m_qsearch;
1902 super#key key mask
1904 | Enter when m_autonarrow ->
1905 if nonemptystr m_qsearch
1906 then source#add_narrow_pattern m_qsearch;
1907 super#key key mask
1909 | (Ascii _ | Code _) when m_autonarrow ->
1910 let pattern = m_qsearch ^ toutf8 key in
1911 postRedisplay "outlinelistview autonarrow add";
1912 source#narrow pattern;
1913 settext true pattern;
1914 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1916 | Backspace when m_autonarrow ->
1917 if emptystr m_qsearch
1918 then coe self
1919 else
1920 let pattern = withoutlastutf8 m_qsearch in
1921 postRedisplay "outlinelistview autonarrow backspace";
1922 ignore (source#renarrow);
1923 source#narrow pattern;
1924 settext true pattern;
1925 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1927 | Up when ctrl -> navscroll (max 0 (m_first - 1))
1929 | Down when ctrl ->
1930 navscroll (min (source#getitemcount - 1) (m_first + 1))
1932 | Up -> navigate ~-1
1933 | Down -> navigate 1
1934 | Prior -> navigate ~-(fstate.maxrows)
1935 | Next -> navigate fstate.maxrows
1937 | Right ->
1938 let o =
1939 if ctrl
1940 then (
1941 postRedisplay "outline ctrl right";
1942 {< m_pan = m_pan + 1 >}
1944 else (
1945 if Wsi.withshift mask
1946 then self#nextcurlevel 1
1947 else self#updownlevel 1
1950 coe o
1952 | Left ->
1953 let o =
1954 if ctrl
1955 then (
1956 postRedisplay "outline ctrl left";
1957 {< m_pan = m_pan - 1 >}
1959 else (
1960 if Wsi.withshift mask
1961 then self#nextcurlevel ~-1
1962 else self#updownlevel ~-1
1965 coe o
1967 | Home ->
1968 postRedisplay "outline home";
1969 coe {< m_first = 0; m_active = 0 >}
1971 | End ->
1972 let active = source#getitemcount - 1 in
1973 let first = max 0 (active - fstate.maxrows) in
1974 postRedisplay "outline end";
1975 coe {< m_active = active; m_first = first >}
1977 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
1978 super#key key mask
1979 end;;
1981 let genhistoutlines () =
1982 Config.gethist ()
1983 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
1984 compare c2.lastvisit c1.lastvisit)
1985 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
1986 let path = if nonemptystr origin then origin else path in
1987 let base = mbtoutf8 @@ Filename.basename path in
1988 (base ^ "\000" ^ c.title, 1, Ohistory hist)
1992 let gotohist (path, c, bookmarks, x, anchor, origin) =
1993 Config.save leavebirdseye;
1994 state.anchor <- anchor;
1995 state.bookmarks <- bookmarks;
1996 state.origin <- origin;
1997 state.x <- x;
1998 setconf conf c;
1999 Ffi.settrimcachepath conf.trimcachepath;
2000 let x0, y0, x1, y1 = conf.trimfuzz in
2001 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2002 Wsi.reshape c.cwinw c.cwinh;
2003 opendoc path origin;
2004 setzoom c.zoom;
2007 let setcheckers enabled =
2008 match !checkerstexid with
2009 | None -> if enabled then checkerstexid := Some (makecheckers ())
2010 | Some id ->
2011 if not enabled
2012 then (
2013 GlTex.delete_texture id;
2014 checkerstexid := None;
2018 let describe_layout layout =
2019 let d =
2020 match layout with
2021 | [] -> "Page 0"
2022 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2023 | l :: rest ->
2024 let rangestr a b =
2025 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2026 else Printf.sprintf "%d%s%d" (a.pageno+1)
2027 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
2028 (b.pageno+1)
2030 let rec fold s la lb = function
2031 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2032 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2033 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2035 fold "Pages" l l rest
2037 let percent =
2038 let maxy = maxy () in
2039 if maxy <= 0
2040 then 100.
2041 else 100. *. (float state.y /. float maxy)
2043 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2046 let setpresentationmode v =
2047 let n = page_of_y state.y in
2048 state.anchor <- (n, 0.0, 1.0);
2049 conf.presentation <- v;
2050 if conf.fitmodel = FitPage
2051 then reqlayout conf.angle conf.fitmodel;
2052 represent ();
2055 let enterinfomode =
2056 let btos b = if b then Utf8syms.radical else E.s in
2057 let showextended = ref false in
2058 let showcolors = ref false in
2059 let leave mode _ = state.mode <- mode in
2060 let src =
2061 (object
2062 val mutable m_l = []
2063 val mutable m_a = E.a
2064 val mutable m_prev_uioh = nouioh
2065 val mutable m_prev_mode = View
2067 inherit lvsourcebase
2069 method reset prev_mode prev_uioh =
2070 m_a <- Array.of_list (List.rev m_l);
2071 m_l <- [];
2072 m_prev_mode <- prev_mode;
2073 m_prev_uioh <- prev_uioh;
2075 method int name get set =
2076 m_l <-
2077 (name, `int get, 1,
2078 Action (
2079 fun u ->
2080 let ondone s =
2081 try set (int_of_string s)
2082 with exn ->
2083 state.text <- Printf.sprintf "bad integer `%s': %s"
2084 s @@ exntos exn
2086 state.text <- E.s;
2087 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2088 state.mode <- Textentry (te, leave m_prev_mode);
2090 )) :: m_l
2092 method int_with_suffix name get set =
2093 m_l <-
2094 (name, `intws get, 1,
2095 Action (
2096 fun u ->
2097 let ondone s =
2098 try set (int_of_string_with_suffix s)
2099 with exn ->
2100 state.text <- Printf.sprintf "bad integer `%s': %s"
2101 s @@ exntos exn
2103 state.text <- E.s;
2104 let te =
2105 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2107 state.mode <- Textentry (te, leave m_prev_mode);
2109 )) :: m_l
2111 method bool ?(offset=1) ?(btos=btos) name get set =
2112 m_l <-
2113 (name, `bool (btos, get), offset, Action (
2114 fun u ->
2115 let v = get () in
2116 set (not v);
2118 )) :: m_l
2120 method color name get set =
2121 m_l <-
2122 (name, `color get, 1,
2123 Action (
2124 fun u ->
2125 let invalid = (nan, nan, nan) in
2126 let ondone s =
2127 let c =
2128 try color_of_string s
2129 with exn ->
2130 state.text <- Printf.sprintf "bad color `%s': %s"
2131 s @@ exntos exn;
2132 invalid
2134 if c <> invalid
2135 then set c;
2137 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2138 state.text <- color_to_string (get ());
2139 state.mode <- Textentry (te, leave m_prev_mode);
2141 )) :: m_l
2143 method string name get set =
2144 m_l <-
2145 (name, `string get, 1,
2146 Action (
2147 fun u ->
2148 let ondone s = set s in
2149 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2150 state.mode <- Textentry (te, leave m_prev_mode);
2152 )) :: m_l
2154 method colorspace name get set =
2155 m_l <-
2156 (name, `string get, 1,
2157 Action (
2158 fun _ ->
2159 let source =
2160 (object
2161 inherit lvsourcebase
2163 initializer
2164 m_active <- CSTE.to_int conf.colorspace;
2165 m_first <- 0;
2167 method getitemcount =
2168 Array.length CSTE.names
2169 method getitem n =
2170 (CSTE.names.(n), 0)
2171 method exit ~uioh ~cancel ~active ~first ~pan =
2172 ignore (uioh, first, pan);
2173 if not cancel then set active;
2174 None
2175 method hasaction _ = true
2176 end)
2178 state.text <- E.s;
2179 let modehash = findkeyhash conf "info" in
2180 coe (new listview ~zebra:false ~helpmode:false
2181 ~source ~trusted:true ~modehash)
2182 )) :: m_l
2184 method paxmark name get set =
2185 m_l <-
2186 (name, `string get, 1,
2187 Action (
2188 fun _ ->
2189 let source =
2190 (object
2191 inherit lvsourcebase
2193 initializer
2194 m_active <- MTE.to_int conf.paxmark;
2195 m_first <- 0;
2197 method getitemcount = Array.length MTE.names
2198 method getitem n = (MTE.names.(n), 0)
2199 method exit ~uioh ~cancel ~active ~first ~pan =
2200 ignore (uioh, first, pan);
2201 if not cancel then set active;
2202 None
2203 method hasaction _ = true
2204 end)
2206 state.text <- E.s;
2207 let modehash = findkeyhash conf "info" in
2208 coe (new listview ~zebra:false ~helpmode:false
2209 ~source ~trusted:true ~modehash)
2210 )) :: m_l
2212 method fitmodel name get set =
2213 m_l <-
2214 (name, `string get, 1,
2215 Action (
2216 fun _ ->
2217 let source =
2218 (object
2219 inherit lvsourcebase
2221 initializer
2222 m_active <- FMTE.to_int conf.fitmodel;
2223 m_first <- 0;
2225 method getitemcount = Array.length FMTE.names
2226 method getitem n = (FMTE.names.(n), 0)
2227 method exit ~uioh ~cancel ~active ~first ~pan =
2228 ignore (uioh, first, pan);
2229 if not cancel then set active;
2230 None
2231 method hasaction _ = true
2232 end)
2234 state.text <- E.s;
2235 let modehash = findkeyhash conf "info" in
2236 coe (new listview ~zebra:false ~helpmode:false
2237 ~source ~trusted:true ~modehash)
2238 )) :: m_l
2240 method caption s offset =
2241 m_l <- (s, `empty, offset, Noaction) :: m_l
2243 method caption2 s f offset =
2244 m_l <- (s, `string f, offset, Noaction) :: m_l
2246 method getitemcount = Array.length m_a
2248 method getitem n =
2249 let tostr = function
2250 | `int f -> string_of_int (f ())
2251 | `intws f -> string_with_suffix_of_int (f ())
2252 | `string f -> f ()
2253 | `color f -> color_to_string (f ())
2254 | `bool (btos, f) -> btos (f ())
2255 | `empty -> E.s
2257 let name, t, offset, _ = m_a.(n) in
2258 ((let s = tostr t in
2259 if nonemptystr s
2260 then Printf.sprintf "%s\t%s" name s
2261 else name),
2262 offset)
2264 method exit ~uioh ~cancel ~active ~first ~pan =
2265 let uiohopt =
2266 if not cancel
2267 then (
2268 let uioh =
2269 match m_a.(active) with
2270 | _, _, _, Action f -> f uioh
2271 | _, _, _, Noaction -> uioh
2273 Some uioh
2275 else None
2277 m_active <- active;
2278 m_first <- first;
2279 m_pan <- pan;
2280 uiohopt
2282 method hasaction n =
2283 match m_a.(n) with
2284 | _, _, _, Action _ -> true
2285 | _, _, _, Noaction -> false
2287 initializer m_active <- 1
2288 end)
2290 let rec fillsrc prevmode prevuioh =
2291 let sep () = src#caption E.s 0 in
2292 let colorp name get set =
2293 src#string name
2294 (fun () -> color_to_string (get ()))
2295 (fun v ->
2296 try set @@ color_of_string v
2297 with exn ->
2298 state.text <-
2299 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2302 let rgba name get set =
2303 src#string name
2304 (fun () -> get () |> rgba_to_string)
2305 (fun v ->
2306 try set @@ rgba_of_string v
2307 with exn ->
2308 state.text <-
2309 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2312 let oldmode = state.mode in
2313 let birdseye = isbirdseye state.mode in
2315 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2317 src#bool "presentation mode"
2318 (fun () -> conf.presentation)
2319 (fun v -> setpresentationmode v);
2321 src#bool "ignore case in searches"
2322 (fun () -> conf.icase)
2323 (fun v -> conf.icase <- v);
2325 src#bool "preload"
2326 (fun () -> conf.preload)
2327 (fun v -> conf.preload <- v);
2329 src#bool "highlight links"
2330 (fun () -> conf.hlinks)
2331 (fun v -> conf.hlinks <- v);
2333 src#bool "under info"
2334 (fun () -> conf.underinfo)
2335 (fun v -> conf.underinfo <- v);
2337 src#fitmodel "fit model"
2338 (fun () -> FMTE.to_string conf.fitmodel)
2339 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2341 src#bool "trim margins"
2342 (fun () -> conf.trimmargins)
2343 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2345 sep ();
2346 src#int "inter-page space"
2347 (fun () -> conf.interpagespace)
2348 (fun n ->
2349 conf.interpagespace <- n;
2350 docolumns conf.columns;
2351 let pageno, py =
2352 match state.layout with
2353 | [] -> 0, 0
2354 | l :: _ -> l.pageno, l.pagey
2356 state.maxy <- calcheight ();
2357 let y = getpagey pageno in
2358 gotoxy state.x (y + py)
2361 src#int "page bias"
2362 (fun () -> conf.pagebias)
2363 (fun v -> conf.pagebias <- v);
2365 src#int "scroll step"
2366 (fun () -> conf.scrollstep)
2367 (fun n -> conf.scrollstep <- n);
2369 src#int "horizontal scroll step"
2370 (fun () -> conf.hscrollstep)
2371 (fun v -> conf.hscrollstep <- v);
2373 src#int "auto scroll step"
2374 (fun () ->
2375 match state.autoscroll with
2376 | Some step -> step
2377 | _ -> conf.autoscrollstep)
2378 (fun n ->
2379 let n = boundastep state.winh n in
2380 if state.autoscroll <> None
2381 then state.autoscroll <- Some n;
2382 conf.autoscrollstep <- n);
2384 src#int "zoom"
2385 (fun () -> truncate (conf.zoom *. 100.))
2386 (fun v -> pivotzoom ((float v) /. 100.));
2388 src#int "rotation"
2389 (fun () -> conf.angle)
2390 (fun v -> reqlayout v conf.fitmodel);
2392 src#int "scroll bar width"
2393 (fun () -> conf.scrollbw)
2394 (fun v ->
2395 conf.scrollbw <- v;
2396 reshape state.winw state.winh;
2399 src#int "scroll handle height"
2400 (fun () -> conf.scrollh)
2401 (fun v -> conf.scrollh <- v;);
2403 src#int "thumbnail width"
2404 (fun () -> conf.thumbw)
2405 (fun v ->
2406 conf.thumbw <- min 4096 v;
2407 match oldmode with
2408 | Birdseye beye ->
2409 leavebirdseye beye false;
2410 enterbirdseye ()
2411 | Textentry _
2412 | View
2413 | LinkNav _ -> ()
2416 let mode = state.mode in
2417 src#string "columns"
2418 (fun () ->
2419 match conf.columns with
2420 | Csingle _ -> "1"
2421 | Cmulti (multi, _) -> multicolumns_to_string multi
2422 | Csplit (count, _) -> "-" ^ string_of_int count
2424 (fun v ->
2425 let n, a, b = multicolumns_of_string v in
2426 setcolumns mode n a b);
2428 sep ();
2429 src#caption "Pixmap cache" 0;
2430 src#int_with_suffix "size (advisory)"
2431 (fun () -> conf.memlimit)
2432 (fun v -> conf.memlimit <- v);
2434 src#caption2 "used"
2435 (fun () ->
2436 Printf.sprintf "%s bytes, %d tiles"
2437 (string_with_suffix_of_int state.memused)
2438 (Hashtbl.length state.tilemap)) 1;
2440 sep ();
2441 src#caption "Layout" 0;
2442 src#caption2 "Dimension"
2443 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2444 state.winw state.winh
2445 state.w state.maxy)
2447 if conf.debug
2448 then src#caption2 "Position" (fun () ->
2449 Printf.sprintf "%dx%d" state.x state.y
2451 else src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2453 sep ();
2454 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2455 "Save these parameters as global defaults at exit"
2456 (fun () -> conf.bedefault)
2457 (fun v -> conf.bedefault <- v);
2459 sep ();
2460 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2461 src#bool ~offset:0 ~btos "Extended parameters"
2462 (fun () -> !showextended)
2463 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2464 if !showextended
2465 then (
2466 src#bool "checkers"
2467 (fun () -> conf.checkers)
2468 (fun v -> conf.checkers <- v; setcheckers v);
2469 src#bool "update cursor"
2470 (fun () -> conf.updatecurs)
2471 (fun v -> conf.updatecurs <- v);
2472 src#bool "scroll-bar on the left"
2473 (fun () -> conf.leftscroll)
2474 (fun v -> conf.leftscroll <- v);
2475 src#bool "verbose"
2476 (fun () -> conf.verbose)
2477 (fun v -> conf.verbose <- v);
2478 src#bool "invert colors"
2479 (fun () -> conf.invert)
2480 (fun v -> conf.invert <- v);
2481 src#bool "max fit"
2482 (fun () -> conf.maxhfit)
2483 (fun v -> conf.maxhfit <- v);
2484 src#bool "pax mode"
2485 (fun () -> conf.pax != None)
2486 (fun v ->
2487 if v
2488 then conf.pax <- Some (now ())
2489 else conf.pax <- None);
2490 src#string "uri launcher"
2491 (fun () -> conf.urilauncher)
2492 (fun v -> conf.urilauncher <- v);
2493 src#string "path launcher"
2494 (fun () -> conf.pathlauncher)
2495 (fun v -> conf.pathlauncher <- v);
2496 src#string "tile size"
2497 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2498 (fun v ->
2500 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2501 conf.tilew <- max 64 w;
2502 conf.tileh <- max 64 h;
2503 flushtiles ();
2504 with exn ->
2505 state.text <- Printf.sprintf "bad tile size `%s': %s"
2506 v @@ exntos exn
2508 src#int "texture count"
2509 (fun () -> conf.texcount)
2510 (fun v ->
2511 if Ffi.realloctexts v
2512 then conf.texcount <- v
2513 else impmsg "failed to set texture count please retry later"
2515 src#int "slice height"
2516 (fun () -> conf.sliceheight)
2517 (fun v ->
2518 conf.sliceheight <- v;
2519 wcmd "sliceh %d" conf.sliceheight;
2521 src#int "anti-aliasing level"
2522 (fun () -> conf.aalevel)
2523 (fun v ->
2524 conf.aalevel <- bound v 0 8;
2525 state.anchor <- getanchor ();
2526 opendoc state.path state.password;
2528 src#string "page scroll scaling factor"
2529 (fun () -> string_of_float conf.pgscale)
2530 (fun v ->
2531 try conf.pgscale <- float_of_string v
2532 with exn ->
2533 state.text <-
2534 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2535 @@ exntos exn
2537 src#int "ui font size"
2538 (fun () -> fstate.fontsize)
2539 (fun v -> setfontsize (bound v 5 100));
2540 src#int "hint font size"
2541 (fun () -> conf.hfsize)
2542 (fun v -> conf.hfsize <- bound v 5 100);
2543 src#string "trim fuzz"
2544 (fun () -> irect_to_string conf.trimfuzz)
2545 (fun v ->
2547 conf.trimfuzz <- irect_of_string v;
2548 if conf.trimmargins
2549 then settrim true conf.trimfuzz;
2550 with exn ->
2551 state.text <- Printf.sprintf "bad irect `%s': %s" v
2552 @@ exntos exn
2554 src#string "selection command"
2555 (fun () -> conf.selcmd)
2556 (fun v -> conf.selcmd <- v);
2557 src#string "synctex command"
2558 (fun () -> conf.stcmd)
2559 (fun v -> conf.stcmd <- v);
2560 src#string "pax command"
2561 (fun () -> conf.paxcmd)
2562 (fun v -> conf.paxcmd <- v);
2563 src#string "ask password command"
2564 (fun () -> conf.passcmd)
2565 (fun v -> conf.passcmd <- v);
2566 src#string "save path command"
2567 (fun () -> conf.savecmd)
2568 (fun v -> conf.savecmd <- v);
2569 src#colorspace "color space"
2570 (fun () -> CSTE.to_string conf.colorspace)
2571 (fun v ->
2572 conf.colorspace <- CSTE.of_int v;
2573 wcmd "cs %d" v;
2574 load state.layout;
2576 src#paxmark "pax mark method"
2577 (fun () -> MTE.to_string conf.paxmark)
2578 (fun v -> conf.paxmark <- MTE.of_int v);
2579 if Ffi.bousable ()
2580 then
2581 src#bool "use PBO"
2582 (fun () -> conf.usepbo)
2583 (fun v -> conf.usepbo <- v);
2584 src#bool "mouse wheel scrolls pages"
2585 (fun () -> conf.wheelbypage)
2586 (fun v -> conf.wheelbypage <- v);
2587 src#bool "open remote links in a new instance"
2588 (fun () -> conf.riani)
2589 (fun v -> conf.riani <- v);
2590 src#bool "edit annotations inline"
2591 (fun () -> conf.annotinline)
2592 (fun v -> conf.annotinline <- v);
2593 src#bool "coarse positioning in presentation mode"
2594 (fun () -> conf.coarseprespos)
2595 (fun v -> conf.coarseprespos <- v);
2596 src#bool "use document CSS"
2597 (fun () -> conf.usedoccss)
2598 (fun v ->
2599 conf.usedoccss <- v;
2600 state.anchor <- getanchor ();
2601 opendoc state.path state.password;
2603 src#bool ~btos "colors"
2604 (fun () -> !showcolors)
2605 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2606 if !showcolors
2607 then (
2608 colorp " background"
2609 (fun () -> conf.bgcolor)
2610 (fun v -> conf.bgcolor <- v);
2612 rgba " paper color"
2613 (fun () -> conf.papercolor)
2614 (fun v ->
2615 conf.papercolor <- v;
2616 Ffi.setpapercolor conf.papercolor;
2617 flushtiles ();
2619 rgba " scrollbar"
2620 (fun () -> conf.sbarcolor)
2621 (fun v -> conf.sbarcolor <- v);
2622 rgba " scrollbar handle"
2623 (fun () -> conf.sbarhndlcolor)
2624 (fun v -> conf.sbarhndlcolor <- v);
2625 rgba " texture color"
2626 (fun () -> conf.texturecolor)
2627 (fun v ->
2628 GlTex.env (`color v);
2629 conf.texturecolor <- v;
2634 sep ();
2635 src#caption "Document" 0;
2636 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2637 src#caption2 "Pages" (fun () -> string_of_int state.pagecount) 1;
2638 src#caption2 "Dimensions"
2639 (fun () -> string_of_int (List.length state.pdims)) 1;
2640 if nonemptystr conf.css
2641 then src#caption2 "CSS" (fun () -> conf.css) 1;
2642 if conf.trimmargins
2643 then (
2644 sep ();
2645 src#caption "Trimmed margins" 0;
2646 src#caption2 "Dimensions"
2647 (fun () -> string_of_int (List.length state.pdims)) 1;
2650 sep ();
2651 src#caption "OpenGL" 0;
2652 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2653 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2655 sep ();
2656 src#caption "Location" 0;
2657 if nonemptystr state.origin
2658 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2659 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2661 src#reset prevmode prevuioh;
2663 fun () -> (
2664 state.text <- E.s;
2665 resetmstate ();
2666 let prevmode = state.mode
2667 and prevuioh = state.uioh in
2668 fillsrc prevmode prevuioh;
2669 let source = (src :> lvsource) in
2670 let modehash = findkeyhash conf "info" in
2671 state.uioh <-
2672 coe (object (self)
2673 inherit listview ~zebra:false ~helpmode:false
2674 ~source ~trusted:true ~modehash as super
2675 val mutable m_prevmemused = 0
2676 method! infochanged = function
2677 | Memused ->
2678 if m_prevmemused != state.memused
2679 then (
2680 m_prevmemused <- state.memused;
2681 postRedisplay "memusedchanged";
2683 | Pdim -> postRedisplay "pdimchanged"
2684 | Docinfo -> fillsrc prevmode prevuioh
2686 method! key key mask =
2687 if not (Wsi.withctrl mask)
2688 then
2689 match [@warning "-4"] Wsi.kc2kt key with
2690 | Keys.Left -> coe (self#updownlevel ~-1)
2691 | Keys.Right -> coe (self#updownlevel 1)
2692 | _ -> super#key key mask
2693 else super#key key mask
2694 end);
2695 postRedisplay "info";
2699 let enterhelpmode =
2700 let source =
2701 (object
2702 inherit lvsourcebase
2703 method getitemcount = Array.length state.help
2704 method getitem n =
2705 let s, l, _ = state.help.(n) in
2706 (s, l)
2708 method exit ~uioh ~cancel ~active ~first ~pan =
2709 let optuioh =
2710 if not cancel
2711 then (
2712 match state.help.(active) with
2713 | _, _, Action f -> Some (f uioh)
2714 | _, _, Noaction -> Some uioh
2716 else None
2718 m_active <- active;
2719 m_first <- first;
2720 m_pan <- pan;
2721 optuioh
2723 method hasaction n =
2724 match state.help.(n) with
2725 | _, _, Action _ -> true
2726 | _, _, Noaction -> false
2728 initializer
2729 m_active <- -1
2730 end)
2732 fun () ->
2733 let modehash = findkeyhash conf "help" in
2734 resetmstate ();
2735 state.uioh <- coe (new listview
2736 ~zebra:false ~helpmode:true
2737 ~source ~trusted:true ~modehash);
2738 postRedisplay "help";
2741 let entermsgsmode =
2742 let msgsource =
2743 (object
2744 inherit lvsourcebase
2745 val mutable m_items = E.a
2747 method getitemcount = 1 + Array.length m_items
2749 method getitem n =
2750 if n = 0
2751 then "[Clear]", 0
2752 else m_items.(n-1), 0
2754 method exit ~uioh ~cancel ~active ~first ~pan =
2755 ignore uioh;
2756 if not cancel
2757 then (
2758 if active = 0
2759 then Buffer.clear state.errmsgs;
2761 m_active <- active;
2762 m_first <- first;
2763 m_pan <- pan;
2764 None
2766 method hasaction n =
2767 n = 0
2769 method reset =
2770 state.newerrmsgs <- false;
2771 let l = Str.split Utils.Re.crlf (Buffer.contents state.errmsgs) in
2772 m_items <- Array.of_list l
2774 initializer
2775 m_active <- 0
2776 end)
2777 in fun () ->
2778 state.text <- E.s;
2779 resetmstate ();
2780 msgsource#reset;
2781 let source = (msgsource :> lvsource) in
2782 let modehash = findkeyhash conf "listview" in
2783 state.uioh <-
2784 coe (object
2785 inherit listview ~zebra:false ~helpmode:false
2786 ~source ~trusted:false ~modehash as super
2787 method! display =
2788 if state.newerrmsgs
2789 then msgsource#reset;
2790 super#display
2791 end);
2792 postRedisplay "msgs";
2795 let getusertext s =
2796 let editor = getenvdef "EDITOR" E.s in
2797 if emptystr editor
2798 then E.s
2799 else
2800 let tmppath = Filename.temp_file "llpp" "note" in
2801 if nonemptystr s
2802 then (
2803 let oc = open_out tmppath in
2804 output_string oc s;
2805 close_out oc;
2807 let execstr = editor ^ " " ^ tmppath in
2808 let s =
2809 match spawn execstr [] with
2810 | exception exn ->
2811 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
2813 | pid ->
2814 match Unix.waitpid [] pid with
2815 | exception exn ->
2816 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
2818 | (_pid, status) ->
2819 match status with
2820 | Unix.WEXITED 0 -> filecontents tmppath
2821 | Unix.WEXITED n ->
2822 impmsg "editor process(%s) exited abnormally: %d" execstr n;
2824 | Unix.WSIGNALED n ->
2825 impmsg "editor process(%s) was killed by signal %d" execstr n;
2827 | Unix.WSTOPPED n ->
2828 impmsg "editor(%s) process was stopped by signal %d" execstr n;
2831 match Unix.unlink tmppath with
2832 | exception exn ->
2833 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
2835 | () -> s
2838 let enterannotmode opaque slinkindex =
2839 let msgsource =
2840 (object
2841 inherit lvsourcebase
2842 val mutable m_text = E.s
2843 val mutable m_items = E.a
2845 method getitemcount = Array.length m_items
2847 method getitem n =
2848 let label, _func = m_items.(n) in
2849 label, 0
2851 method exit ~uioh ~cancel ~active ~first ~pan =
2852 ignore (uioh, first, pan);
2853 if not cancel
2854 then (
2855 let _label, func = m_items.(active) in
2856 func ()
2858 None
2860 method hasaction n = nonemptystr @@ fst m_items.(n)
2862 method reset s =
2863 let rec split accu b i =
2864 let p = b+i in
2865 if p = String.length s
2866 then (String.sub s b (p-b), fun () -> ()) :: accu
2867 else
2868 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2869 then
2870 let ss = if i = 0 then E.s else String.sub s b i in
2871 split ((ss, fun () -> ())::accu) (p+1) 0
2872 else split accu b (i+1)
2874 let cleanup () =
2875 wcmd "freepage %s" (~> opaque);
2876 let keys =
2877 Hashtbl.fold (fun key opaque' accu ->
2878 if opaque' = opaque'
2879 then key :: accu else accu) state.pagemap []
2881 List.iter (Hashtbl.remove state.pagemap) keys;
2882 flushtiles ();
2883 gotoxy state.x state.y
2885 let dele () =
2886 Ffi.delannot opaque slinkindex;
2887 cleanup ();
2889 let edit inline () =
2890 let update s =
2891 if emptystr s
2892 then dele ()
2893 else (
2894 Ffi.modannot opaque slinkindex s;
2895 cleanup ();
2898 if inline
2899 then
2900 let mode = state.mode in
2901 state.mode <-
2902 Textentry (
2903 ("annotation: ", m_text, None, textentry, update, true),
2904 fun _ -> state.mode <- mode
2906 state.text <- E.s;
2907 enttext ();
2908 else
2909 let s = getusertext m_text in
2910 update s
2912 m_text <- s;
2913 m_items <-
2914 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
2915 :: ("[Delete]", dele)
2916 :: ("[Edit]", edit conf.annotinline)
2917 :: (E.s, fun () -> ())
2918 :: split [] 0 0 |> List.rev |> Array.of_list
2920 initializer
2921 m_active <- 0
2922 end)
2924 state.text <- E.s;
2925 let s = Ffi.getannotcontents opaque slinkindex in
2926 resetmstate ();
2927 msgsource#reset s;
2928 let source = (msgsource :> lvsource) in
2929 let modehash = findkeyhash conf "listview" in
2930 state.uioh <- coe (object
2931 inherit listview ~zebra:false ~helpmode:false
2932 ~source ~trusted:false ~modehash
2933 end);
2934 postRedisplay "enterannotmode";
2937 let gotoremote spec =
2938 let filename, dest = splitatchar spec '#' in
2939 let getpath filename =
2940 let path =
2941 if nonemptystr filename
2942 then
2943 if Filename.is_relative filename
2944 then
2945 let dir = Filename.dirname state.path in
2946 let dir =
2947 if Filename.is_implicit dir
2948 then Filename.concat (Sys.getcwd ()) dir
2949 else dir
2951 Filename.concat dir filename
2952 else filename
2953 else E.s
2955 if Sys.file_exists path
2956 then path
2957 else E.s
2959 let path = getpath filename in
2960 let dospawn lcmd =
2961 if conf.riani
2962 then
2963 let cmd = Lazy.force_val lcmd in
2964 match spawn cmd with
2965 | _pid -> ()
2966 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
2967 else
2968 let anchor = getanchor () in
2969 let ranchor = state.path, state.password, anchor, state.origin in
2970 state.origin <- E.s;
2971 state.ranchors <- ranchor :: state.ranchors;
2972 opendoc path E.s;
2974 if substratis spec 0 "page="
2975 then
2976 match Scanf.sscanf spec "page=%d" (fun n -> n) with
2977 | pageno ->
2978 state.anchor <- (pageno, 0.0, 0.0);
2979 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
2980 | exception exn ->
2981 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
2982 else (
2983 state.nameddest <- dest;
2984 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
2988 let gotounder = function
2989 | Ulinkuri s when Ffi.isexternallink s ->
2990 if substratis s 0 "file://"
2991 then gotoremote @@ String.sub s 7 (String.length s - 7)
2992 else Help.gotouri conf.urilauncher s
2993 | Ulinkuri s ->
2994 let pageno, x, y = Ffi.uritolocation s in
2995 addnav ();
2996 gotopagexy pageno x y
2997 | Utext _ | Unone -> ()
2998 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
3001 let gotooutline (_, _, kind) =
3002 match kind with
3003 | Onone -> ()
3004 | Oanchor ((pageno, y, _) as anchor) ->
3005 addnav ();
3006 gotoxy state.x @@
3007 getanchory (if conf.presentation then (pageno, y, 1.0) else anchor)
3008 | Ouri uri -> gotounder (Ulinkuri uri)
3009 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
3010 | Oremote (remote, pageno) ->
3011 error "gotounder (Uremote (%S,%d) )" remote pageno
3012 | Ohistory hist -> gotohist hist
3013 | Oremotedest (path, dest) ->
3014 error "gotounder (Uremotedest (%S, %S))" path dest
3017 class outlinesoucebase fetchoutlines = object (self)
3018 inherit lvsourcebase
3019 val mutable m_items = E.a
3020 val mutable m_minfo = E.a
3021 val mutable m_orig_items = E.a
3022 val mutable m_orig_minfo = E.a
3023 val mutable m_narrow_patterns = []
3024 val mutable m_gen = -1
3026 method getitemcount = Array.length m_items
3028 method getitem n =
3029 let s, n, _ = m_items.(n) in
3030 (s, n+0)
3032 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3033 ignore (uioh, first);
3034 let items, minfo =
3035 if m_narrow_patterns = []
3036 then m_orig_items, m_orig_minfo
3037 else m_items, m_minfo
3039 m_pan <- pan;
3040 if not cancel
3041 then (
3042 m_items <- items;
3043 m_minfo <- minfo;
3044 gotooutline m_items.(active);
3046 else (
3047 m_items <- items;
3048 m_minfo <- minfo;
3050 None
3052 method hasaction (_:int) = true
3054 method greetmsg =
3055 if Array.length m_items != Array.length m_orig_items
3056 then
3057 let s =
3058 match m_narrow_patterns with
3059 | one :: [] -> one
3060 | many -> String.concat Utf8syms.ellipsis (List.rev many)
3062 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3063 else E.s
3065 method statestr =
3066 match m_narrow_patterns with
3067 | [] -> E.s
3068 | one :: [] -> one
3069 | head :: _ -> Utf8syms.ellipsis ^ head
3071 method narrow pattern =
3072 match Str.regexp_case_fold pattern with
3073 | exception _ -> ()
3074 | re ->
3075 let rec loop accu minfo n =
3076 if n = -1
3077 then (
3078 m_items <- Array.of_list accu;
3079 m_minfo <- Array.of_list minfo;
3081 else
3082 let (s, _, _) as o = m_items.(n) in
3083 let accu, minfo =
3084 match Str.search_forward re s 0 with
3085 | exception Not_found -> accu, minfo
3086 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3088 loop accu minfo (n-1)
3090 loop [] [] (Array.length m_items - 1)
3092 method! getminfo = m_minfo
3094 method denarrow =
3095 m_orig_items <- fetchoutlines ();
3096 m_minfo <- m_orig_minfo;
3097 m_items <- m_orig_items
3099 method add_narrow_pattern pattern =
3100 m_narrow_patterns <- pattern :: m_narrow_patterns
3102 method del_narrow_pattern =
3103 match m_narrow_patterns with
3104 | _ :: rest -> m_narrow_patterns <- rest
3105 | [] -> ()
3107 method renarrow =
3108 self#denarrow;
3109 match m_narrow_patterns with
3110 | pattern :: [] -> self#narrow pattern; pattern
3111 | list ->
3112 List.fold_left (fun accu pattern ->
3113 self#narrow pattern;
3114 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
3116 method calcactive (_:anchor) = 0
3118 method reset anchor items =
3119 if state.gen != m_gen
3120 then (
3121 m_orig_items <- items;
3122 m_items <- items;
3123 m_narrow_patterns <- [];
3124 m_minfo <- E.a;
3125 m_orig_minfo <- E.a;
3126 m_gen <- state.gen;
3128 else (
3129 if items != m_orig_items
3130 then (
3131 m_orig_items <- items;
3132 if m_narrow_patterns == []
3133 then m_items <- items;
3136 let active = self#calcactive anchor in
3137 m_active <- active;
3138 m_first <- firstof m_first active
3142 let outlinesource fetchoutlines =
3143 (object
3144 inherit outlinesoucebase fetchoutlines
3145 method! calcactive anchor =
3146 let rely = getanchory anchor in
3147 let rec loop n best bestd =
3148 if n = Array.length m_items
3149 then best
3150 else
3151 let _, _, kind = m_items.(n) in
3152 match kind with
3153 | Oanchor anchor ->
3154 let orely = getanchory anchor in
3155 let d = abs (orely - rely) in
3156 if d < bestd
3157 then loop (n+1) n d
3158 else loop (n+1) best bestd
3159 | Onone | Oremote _ | Olaunch _
3160 | Oremotedest _ | Ouri _ | Ohistory _ ->
3161 loop (n+1) best bestd
3163 loop 0 ~-1 max_int
3164 end)
3167 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3168 let fetchoutlines sourcetype () =
3169 match sourcetype with
3170 | `bookmarks -> Array.of_list state.bookmarks
3171 | `outlines -> state.outlines
3172 | `history -> genhistoutlines () |> Array.of_list
3174 let so = outlinesource (fetchoutlines `outlines) in
3175 let sb = outlinesource (fetchoutlines `bookmarks) in
3176 let sh = outlinesource (fetchoutlines `history) in
3177 let mkselector sourcetype source =
3178 (fun errmsg ->
3179 let outlines = fetchoutlines sourcetype () in
3180 if Array.length outlines = 0
3181 then showtext ' ' errmsg
3182 else (
3183 resetmstate ();
3184 Wsi.setcursor Wsi.CURSOR_INHERIT;
3185 let anchor = getanchor () in
3186 source#reset anchor outlines;
3187 state.text <- source#greetmsg;
3188 state.uioh <-
3189 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3190 postRedisplay "enter selector";
3194 let mkenter sourcetype errmsg s = fun () -> mkselector sourcetype s errmsg in
3195 ( mkenter `outlines "document has no outline" so
3196 , mkenter `bookmarks "document has no bookmarks (yet)" sb
3197 , mkenter `history "history is empty" sh )
3201 let addbookmark title a =
3202 let b = List.filter (fun (title', _, _) -> title <> title') state.bookmarks in
3203 state.bookmarks <- (title, 0, Oanchor a) :: b;;
3205 let quickbookmark ?title () =
3206 match state.layout with
3207 | [] -> ()
3208 | l :: _ ->
3209 let title =
3210 match title with
3211 | None ->
3212 Unix.(
3213 let tm = localtime (now ()) in
3214 Printf.sprintf
3215 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3216 (l.pageno+1)
3217 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3219 | Some title -> title
3221 addbookmark title (getanchor1 l)
3224 let setautoscrollspeed step goingdown =
3225 let incr = max 1 ((abs step) / 2) in
3226 let incr = if goingdown then incr else -incr in
3227 let astep = boundastep state.winh (step + incr) in
3228 state.autoscroll <- Some astep;
3231 let canpan () =
3232 match conf.columns with
3233 | Csplit _ -> true
3234 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3237 let panbound x = bound x (-state.w) state.winw;;
3239 let existsinrow pageno (columns, coverA, coverB) p =
3240 let last = ((pageno - coverA) mod columns) + columns in
3241 let rec any = function
3242 | [] -> false
3243 | l :: rest ->
3244 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3245 then p l
3246 else (
3247 if not (p l)
3248 then (if l.pageno = last then false else any rest)
3249 else true
3252 any state.layout
3255 let nextpage () =
3256 match state.layout with
3257 | [] ->
3258 let pageno = page_of_y state.y in
3259 gotoxy state.x (getpagey (pageno+1))
3260 | l :: rest ->
3261 match conf.columns with
3262 | Csingle _ ->
3263 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3264 then
3265 let y = clamp (pgscale state.winh) in
3266 gotoxy state.x y
3267 else
3268 let pageno = min (l.pageno+1) (state.pagecount-1) in
3269 gotoxy state.x (getpagey pageno)
3270 | Cmulti ((c, _, _) as cl, _) ->
3271 if conf.presentation
3272 && (existsinrow l.pageno cl
3273 (fun l -> l.pageh > l.pagey + l.pagevh))
3274 then
3275 let y = clamp (pgscale state.winh) in
3276 gotoxy state.x y
3277 else
3278 let pageno = min (l.pageno+c) (state.pagecount-1) in
3279 gotoxy state.x (getpagey pageno)
3280 | Csplit (n, _) ->
3281 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3282 then
3283 let pagey, pageh = getpageyh l.pageno in
3284 let pagey = pagey + pageh * l.pagecol in
3285 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3286 gotoxy state.x (pagey + pageh + ips)
3289 let prevpage () =
3290 match state.layout with
3291 | [] ->
3292 let pageno = page_of_y state.y in
3293 gotoxy state.x (getpagey (pageno-1))
3294 | l :: _ ->
3295 match conf.columns with
3296 | Csingle _ ->
3297 if conf.presentation && l.pagey != 0
3298 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3299 else
3300 let pageno = max 0 (l.pageno-1) in
3301 gotoxy state.x (getpagey pageno)
3302 | Cmulti ((c, _, coverB) as cl, _) ->
3303 if conf.presentation &&
3304 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3305 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3306 else
3307 let decr =
3308 if l.pageno = state.pagecount - coverB
3309 then 1
3310 else c
3312 let pageno = max 0 (l.pageno-decr) in
3313 gotoxy state.x (getpagey pageno)
3314 | Csplit (n, _) ->
3315 let y =
3316 if l.pagecol = 0
3317 then
3318 if l.pageno = 0
3319 then l.pagey
3320 else
3321 let pageno = max 0 (l.pageno-1) in
3322 let pagey, pageh = getpageyh pageno in
3323 pagey + (n-1)*pageh
3324 else
3325 let pagey, pageh = getpageyh l.pageno in
3326 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3328 gotoxy state.x y
3331 let save () =
3332 if emptystr conf.savecmd
3333 then adderrmsg "savepath-command is empty"
3334 "don't know where to save modified document"
3335 else
3336 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3337 let path =
3338 getcmdoutput
3339 (fun exn ->
3340 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3341 savecmd
3343 if nonemptystr path
3344 then
3345 let tmp = path ^ ".tmp" in
3346 Ffi.savedoc tmp;
3347 Unix.rename tmp path;
3350 let viewkeyboard key mask =
3351 let enttext te =
3352 let mode = state.mode in
3353 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3354 state.text <- E.s;
3355 enttext ();
3356 postRedisplay "view:enttext"
3357 and histback () =
3358 match state.nav.past with
3359 | [] -> ()
3360 | prev :: prest ->
3361 state.nav <- { past = prest
3362 ; future = getanchor () :: state.nav.future; };
3363 gotoxy state.x (getanchory prev)
3365 let ctrl = Wsi.withctrl mask in
3366 let open Keys in
3367 match Wsi.kc2kt key with
3368 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3370 | Ascii 'Q' -> exit 0
3372 | Ascii 'z' ->
3373 let yloc f =
3374 match List.rev state.rects with
3375 | [] -> ()
3376 | (pageno, _, (_, y0, _, y1, _, y2, _, y3)) :: _ ->
3377 f pageno (y0, y1, y2, y3)
3378 and yminmax (y0, y1, y2, y3) =
3379 let ym = min y0 y1 |> min y2 |> min y3 |> truncate in
3380 let yM = max y0 y1 |> max y2 |> max y3 |> truncate in
3381 ym, yM
3383 let ondone msg = state.text <- msg
3384 and zmod _ _ k =
3385 match [@warning "-4"] k with
3386 | Keys.Ascii 'z' ->
3387 let f pageno ys =
3388 let ym, yM = yminmax ys in
3389 let hh = (yM - ym)/2 in
3390 gotopage1 pageno (ym + hh - state.winh/2)
3392 yloc f;
3393 TEdone "center"
3394 | Keys.Ascii 't' ->
3395 let f pageno ys =
3396 let ym, _ = yminmax ys in
3397 gotopage1 pageno ym
3399 yloc f;
3400 TEdone "top"
3401 | Keys.Ascii 'b' ->
3402 let f pageno ys =
3403 let _, yM = yminmax ys in
3404 gotopage1 pageno (yM - state.winh)
3406 yloc f;
3407 TEdone "bottom"
3408 | _ -> TEstop
3410 enttext (": ", E.s, None, zmod state.mode, ondone, true)
3412 | Ascii 'W' ->
3413 if Ffi.hasunsavedchanges ()
3414 then save ()
3416 | Insert ->
3417 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3418 then (
3419 state.mode <- (
3420 match state.lnava with
3421 | None -> LinkNav (Ltgendir 0)
3422 | Some pn -> LinkNav (Ltexact pn)
3424 gotoxy state.x state.y;
3426 else impmsg "keyboard link navigation does not work under rotation"
3428 | Escape | Ascii 'q' ->
3429 begin match state.mstate with
3430 | Mzoomrect _ ->
3431 resetmstate ();
3432 postRedisplay "kill rect";
3433 | Msel _
3434 | Mpan _
3435 | Mscrolly | Mscrollx
3436 | Mzoom _
3437 | Mnone ->
3438 begin match state.mode with
3439 | LinkNav ln ->
3440 begin match ln with
3441 | Ltexact pl -> state.lnava <- Some pl
3442 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3443 end;
3444 state.mode <- View;
3445 postRedisplay "esc leave linknav"
3446 | Birdseye _ | Textentry _ | View ->
3447 match state.ranchors with
3448 | [] -> raise Quit
3449 | (path, password, anchor, origin) :: rest ->
3450 state.ranchors <- rest;
3451 state.anchor <- anchor;
3452 state.origin <- origin;
3453 state.nameddest <- E.s;
3454 opendoc path password
3455 end;
3456 end;
3458 | Ascii 'o' -> enteroutlinemode ()
3459 | Ascii 'H' -> enterhistmode ()
3461 | Ascii 'u' ->
3462 state.rects <- [];
3463 state.text <- E.s;
3464 Hashtbl.iter (fun _ opaque ->
3465 Ffi.clearmark opaque;
3466 Hashtbl.clear state.prects) state.pagemap;
3467 postRedisplay "dehighlight";
3469 | Ascii (('/' | '?') as c) ->
3470 let ondone isforw s =
3471 cbput state.hists.pat s;
3472 state.searchpattern <- s;
3473 search s isforw
3475 let s = String.make 1 c in
3476 enttext (s, E.s, Some (onhist state.hists.pat),
3477 textentry, ondone (c = '/'), true)
3479 | Ascii '+' | Ascii '=' when ctrl ->
3480 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3481 pivotzoom (conf.zoom +. incr)
3483 | Ascii '+' ->
3484 let ondone s =
3485 let n =
3486 try int_of_string s with exn ->
3487 state.text <-
3488 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3489 max_int
3491 if n != max_int
3492 then (
3493 conf.pagebias <- n;
3494 state.text <- "page bias is now " ^ string_of_int n;
3497 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3499 | Ascii '-' when ctrl ->
3500 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3501 pivotzoom (max 0.01 (conf.zoom -. decr))
3503 | Ascii '-' ->
3504 let ondone msg = state.text <- msg in
3505 enttext ("option: ", E.s, None,
3506 optentry state.mode, ondone, true)
3508 | Ascii '0' when ctrl ->
3509 if conf.zoom = 1.0
3510 then gotoxy 0 state.y
3511 else setzoom 1.0
3513 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3514 let cols =
3515 match conf.columns with
3516 | Csingle _ | Cmulti _ -> 1
3517 | Csplit (n, _) -> n
3519 let h = state.winh -
3520 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3522 let zoom = Ffi.zoomforh state.winw h 0 cols in
3523 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3524 then setzoom zoom
3526 | Ascii '3' when ctrl ->
3527 let fm =
3528 match conf.fitmodel with
3529 | FitWidth -> FitProportional
3530 | FitProportional -> FitPage
3531 | FitPage -> FitWidth
3533 state.text <- "fit model: " ^ FMTE.to_string fm;
3534 reqlayout conf.angle fm
3536 | Ascii '4' when ctrl ->
3537 let zoom = Ffi.getmaxw () /. float state.winw in
3538 if zoom > 0.0 then setzoom zoom
3540 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3542 | Ascii ('0'..'9' as c) when not ctrl ->
3543 let ondone s =
3544 let n =
3545 try int_of_string s with exn ->
3546 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3549 if n >= 0
3550 then (
3551 addnav ();
3552 cbput state.hists.pag (string_of_int n);
3553 gotopage1 (n + conf.pagebias - 1) 0;
3556 let pageentry text = function [@warning "-4"]
3557 | Keys.Ascii 'g' -> TEdone text
3558 | key -> intentry text key
3560 let text = String.make 1 c in
3561 enttext (":", text, Some (onhist state.hists.pag),
3562 pageentry, ondone, true)
3564 | Ascii 'b' ->
3565 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3566 postRedisplay "toggle scrollbar";
3568 | Ascii 'B' ->
3569 state.bzoom <- not state.bzoom;
3570 state.rects <- [];
3571 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3573 | Ascii 'l' ->
3574 conf.hlinks <- not conf.hlinks;
3575 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3576 postRedisplay "toggle highlightlinks";
3578 | Ascii 'F' ->
3579 if conf.angle mod 360 = 0
3580 then (
3581 state.glinks <- true;
3582 let mode = state.mode in
3583 state.mode <-
3584 Textentry (
3585 ("goto: ", E.s, None, linknentry, linknact gotounder, false),
3586 (fun _ ->
3587 state.glinks <- false;
3588 state.mode <- mode)
3590 state.text <- E.s;
3591 postRedisplay "view:linkent(F)"
3593 else impmsg "hint mode does not work under rotation"
3595 | Ascii 'y' ->
3596 state.glinks <- true;
3597 let mode = state.mode in
3598 state.mode <-
3599 Textentry (
3600 ("copy: ", E.s, None, linknentry,
3601 linknact (fun under ->
3602 selstring conf.selcmd (undertext under)), false),
3603 (fun _ ->
3604 state.glinks <- false;
3605 state.mode <- mode)
3607 state.text <- E.s;
3608 postRedisplay "view:linkent"
3610 | Ascii 'a' ->
3611 begin match state.autoscroll with
3612 | Some step ->
3613 conf.autoscrollstep <- step;
3614 state.autoscroll <- None
3615 | None ->
3616 state.autoscroll <- Some conf.autoscrollstep;
3617 state.slideshow <- state.slideshow land lnot 2
3620 | Ascii 'p' when ctrl ->
3621 launchpath () (* XXX where do error messages go? *)
3623 | Ascii 'P' ->
3624 setpresentationmode (not conf.presentation);
3625 showtext ' ' ("presentation mode " ^
3626 if conf.presentation then "on" else "off");
3628 | Ascii 'f' ->
3629 if List.mem Wsi.Fullscreen state.winstate
3630 then Wsi.reshape conf.cwinw conf.cwinh
3631 else Wsi.fullscreen ()
3633 | Ascii ('p'|'N') -> search state.searchpattern false
3634 | Ascii 'n' | Fn 3 -> search state.searchpattern true
3636 | Ascii 't' ->
3637 begin match state.layout with
3638 | [] -> ()
3639 | l :: _ -> gotoxy state.x (getpagey l.pageno)
3642 | Ascii ' ' -> nextpage ()
3643 | Delete -> prevpage ()
3644 | Ascii '=' -> showtext ' ' (describe_layout state.layout);
3646 | Ascii 'w' ->
3647 begin match state.layout with
3648 | [] -> ()
3649 | l :: _ ->
3650 Wsi.reshape l.pagew l.pageh;
3651 postRedisplay "w"
3654 | Ascii '\'' -> enterbookmarkmode ()
3655 | Ascii 'h' | Fn 1 -> enterhelpmode ()
3656 | Ascii 'i' -> enterinfomode ()
3657 | Ascii 'e' when Buffer.length state.errmsgs > 0 -> entermsgsmode ()
3659 | Ascii 'm' ->
3660 let ondone s =
3661 match state.layout with
3662 | l :: _ when nonemptystr s -> addbookmark s @@ getanchor1 l
3663 | _ -> ()
3665 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3667 | Ascii '~' ->
3668 quickbookmark ();
3669 showtext ' ' "Quick bookmark added";
3671 | Ascii 'x' -> state.roam ()
3673 | Ascii ('<'|'>' as c) ->
3674 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3676 | Ascii ('['|']' as c) ->
3677 conf.colorscale <-
3678 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3679 postRedisplay "brightness";
3681 | Ascii 'c' when state.mode = View ->
3682 if Wsi.withalt mask
3683 then (
3684 if conf.zoom > 1.0
3685 then
3686 let m = (state.winw - state.w) / 2 in
3687 gotoxy m state.y
3689 else
3690 let (c, a, b), z =
3691 match state.prevcolumns with
3692 | None -> (1, 0, 0), 1.0
3693 | Some (columns, z) ->
3694 let cab =
3695 match columns with
3696 | Csplit (c, _) -> -c, 0, 0
3697 | Cmulti ((c, a, b), _) -> c, a, b
3698 | Csingle _ -> 1, 0, 0
3700 cab, z
3702 setcolumns View c a b;
3703 setzoom z
3705 | Down | Up when ctrl && Wsi.withshift mask ->
3706 let zoom, x = state.prevzoom in
3707 setzoom zoom;
3708 state.x <- x;
3710 | Up ->
3711 begin match state.autoscroll with
3712 | None ->
3713 begin match state.mode with
3714 | Birdseye beye -> upbirdseye 1 beye
3715 | Textentry _ | View | LinkNav _ ->
3716 if ctrl
3717 then gotoxy state.x (clamp ~-(state.winh/2))
3718 else (
3719 if not (Wsi.withshift mask) && conf.presentation
3720 then prevpage ()
3721 else gotoxy state.x (clamp (-conf.scrollstep))
3724 | Some n -> setautoscrollspeed n false
3727 | Down ->
3728 begin match state.autoscroll with
3729 | None ->
3730 begin match state.mode with
3731 | Birdseye beye -> downbirdseye 1 beye
3732 | Textentry _ | View | LinkNav _ ->
3733 if ctrl
3734 then gotoxy state.x (clamp (state.winh/2))
3735 else (
3736 if not (Wsi.withshift mask) && conf.presentation
3737 then nextpage ()
3738 else gotoxy state.x (clamp (conf.scrollstep))
3741 | Some n -> setautoscrollspeed n true
3744 | Left | Right when not (Wsi.withalt mask) ->
3745 if canpan ()
3746 then
3747 let dx =
3748 if ctrl
3749 then state.winw / 2
3750 else conf.hscrollstep
3752 let dx =
3753 let pv = Wsi.kc2kt key in
3754 if pv = Keys.Left then dx else -dx
3756 gotoxy (panbound (state.x + dx)) state.y
3757 else (
3758 state.text <- E.s;
3759 postRedisplay "left/right"
3762 | Prior ->
3763 let y =
3764 if ctrl
3765 then
3766 match state.layout with
3767 | [] -> state.y
3768 | l :: _ -> state.y - l.pagey
3769 else clamp (pgscale (-state.winh))
3771 gotoxy state.x y
3773 | Next ->
3774 let y =
3775 if ctrl
3776 then
3777 match List.rev state.layout with
3778 | [] -> state.y
3779 | l :: _ -> getpagey l.pageno
3780 else clamp (pgscale state.winh)
3782 gotoxy state.x y
3784 | Ascii 'g' | Home ->
3785 addnav ();
3786 gotoxy 0 0
3787 | Ascii 'G' | End ->
3788 addnav ();
3789 gotoxy 0 (clamp state.maxy)
3791 | Right when Wsi.withalt mask ->
3792 (match state.nav.future with
3793 | [] -> ()
3794 | next :: frest ->
3795 state.nav <- { past = getanchor () :: state.nav.past; future = frest; };
3796 gotoxy state.x (getanchory next)
3798 | Left when Wsi.withalt mask -> histback ()
3799 | Backspace -> histback ()
3801 | Ascii 'r' ->
3802 reload ()
3804 | Ascii 'v' when conf.debug ->
3805 state.rects <- [];
3806 List.iter (fun l ->
3807 match getopaque l.pageno with
3808 | None -> ()
3809 | Some opaque ->
3810 let x0, y0, x1, y1 = Ffi.pagebbox opaque in
3811 let rect = (float x0, float y0,
3812 float x1, float y0,
3813 float x1, float y1,
3814 float x0, float y1) in
3815 debugrect rect;
3816 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3817 state.rects <- (l.pageno, color, rect) :: state.rects;
3818 ) state.layout;
3819 postRedisplay "v";
3821 | Ascii '|' ->
3822 let mode = state.mode in
3823 let cmd = ref E.s in
3824 let onleave = function
3825 | Cancel -> state.mode <- mode
3826 | Confirm ->
3827 List.iter (fun l ->
3828 match getopaque l.pageno with
3829 | Some opaque -> pipesel opaque !cmd
3830 | None -> ()) state.layout;
3831 state.mode <- mode
3833 let ondone s =
3834 cbput state.hists.sel s;
3835 cmd := s
3837 let te =
3838 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3840 postRedisplay "|";
3841 state.mode <- Textentry (te, onleave);
3843 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3844 vlog "huh? %s" (Wsi.keyname key)
3847 let linknavkeyboard key mask linknav =
3848 let pv = Wsi.kc2kt key in
3849 let getpage pageno =
3850 let rec loop = function
3851 | [] -> None
3852 | l :: _ when l.pageno = pageno -> Some l
3853 | _ :: rest -> loop rest
3854 in loop state.layout
3856 let doexact (pageno, n) =
3857 match getopaque pageno, getpage pageno with
3858 | Some opaque, Some l ->
3859 if pv = Keys.Enter
3860 then
3861 let under = Ffi.getlink opaque n in
3862 postRedisplay "link gotounder";
3863 gotounder under;
3864 state.mode <- View;
3865 else
3866 let opt, dir =
3867 let open Keys in
3868 match pv with
3869 | Home -> Some (Ffi.findlink opaque LDfirst), -1
3870 | End -> Some (Ffi.findlink opaque LDlast), 1
3871 | Left -> Some (Ffi.findlink opaque (LDleft n)), -1
3872 | Right -> Some (Ffi.findlink opaque (LDright n)), 1
3873 | Up -> Some (Ffi.findlink opaque (LDup n)), -1
3874 | Down -> Some (Ffi.findlink opaque (LDdown n)), 1
3875 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3876 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3878 let pwl l dir =
3879 begin match Ffi.findpwl l.pageno dir with
3880 | Pwlnotfound -> ()
3881 | Pwl pageno ->
3882 let notfound dir =
3883 state.mode <- LinkNav (Ltgendir dir);
3884 let y, h = getpageyh pageno in
3885 let y =
3886 if dir < 0
3887 then y + h - state.winh
3888 else y
3890 gotoxy state.x y
3892 begin match getopaque pageno, getpage pageno with
3893 | Some opaque, Some _ ->
3894 let link =
3895 let ld = if dir > 0 then LDfirst else LDlast in
3896 Ffi.findlink opaque ld
3898 begin match link with
3899 | Lfound m ->
3900 showlinktype (Ffi.getlink opaque m);
3901 state.mode <- LinkNav (Ltexact (pageno, m));
3902 postRedisplay "linknav jpage";
3903 | Lnotfound -> notfound dir
3904 end;
3905 | _ -> notfound dir
3906 end;
3907 end;
3909 begin match opt with
3910 | Some Lnotfound -> pwl l dir;
3911 | Some (Lfound m) ->
3912 if m = n
3913 then pwl l dir
3914 else (
3915 let _, y0, _, y1 = Ffi.getlinkrect opaque m in
3916 if y0 < l.pagey
3917 then gotopage1 l.pageno y0
3918 else (
3919 let d = fstate.fontsize + 1 in
3920 if y1 - l.pagey > l.pagevh - d
3921 then gotopage1 l.pageno (y1 - state.winh + d)
3922 else postRedisplay "linknav";
3924 showlinktype (Ffi.getlink opaque m);
3925 state.mode <- LinkNav (Ltexact (l.pageno, m));
3928 | None -> viewkeyboard key mask
3929 end;
3930 | _ -> viewkeyboard key mask
3932 if pv = Keys.Insert
3933 then (
3934 begin match linknav with
3935 | Ltexact pa -> state.lnava <- Some pa
3936 | Ltgendir _ | Ltnotready _ -> ()
3937 end;
3938 state.mode <- View;
3939 postRedisplay "leave linknav"
3941 else
3942 match linknav with
3943 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3944 | Ltexact exact -> doexact exact
3947 let keyboard key mask =
3948 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
3949 then wcmd "interrupt"
3950 else state.uioh <- state.uioh#key key mask
3953 let birdseyekeyboard key mask
3954 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3955 let incr =
3956 match conf.columns with
3957 | Csingle _ -> 1
3958 | Cmulti ((c, _, _), _) -> c
3959 | Csplit _ -> error "bird's eye split mode"
3961 let pgh layout = List.fold_left
3962 (fun m l -> max l.pageh m) state.winh layout in
3963 let open Keys in
3964 match Wsi.kc2kt key with
3965 | Ascii 'l' when Wsi.withctrl mask ->
3966 let y, h = getpageyh pageno in
3967 let top = (state.winh - h) / 2 in
3968 gotoxy state.x (max 0 (y - top))
3969 | Enter -> leavebirdseye beye false
3970 | Escape -> leavebirdseye beye true
3971 | Up -> upbirdseye incr beye
3972 | Down -> downbirdseye incr beye
3973 | Left -> upbirdseye 1 beye
3974 | Right -> downbirdseye 1 beye
3976 | Prior ->
3977 begin match state.layout with
3978 | l :: _ ->
3979 if l.pagey != 0
3980 then (
3981 state.mode <- Birdseye (
3982 oconf, leftx, l.pageno, hooverpageno, anchor
3984 gotopage1 l.pageno 0;
3986 else (
3987 let layout = layout state.x (state.y-state.winh)
3988 state.winw
3989 (pgh state.layout) in
3990 match layout with
3991 | [] -> gotoxy state.x (clamp (-state.winh))
3992 | l :: _ ->
3993 state.mode <- Birdseye (
3994 oconf, leftx, l.pageno, hooverpageno, anchor
3996 gotopage1 l.pageno 0
3999 | [] -> gotoxy state.x (clamp (-state.winh))
4000 end;
4002 | Next ->
4003 begin match List.rev state.layout with
4004 | l :: _ ->
4005 let layout = layout state.x
4006 (state.y + (pgh state.layout))
4007 state.winw state.winh in
4008 begin match layout with
4009 | [] ->
4010 let incr = l.pageh - l.pagevh in
4011 if incr = 0
4012 then (
4013 state.mode <-
4014 Birdseye (
4015 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4017 postRedisplay "birdseye pagedown";
4019 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4021 | l :: _ ->
4022 state.mode <-
4023 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4024 gotopage1 l.pageno 0;
4027 | [] -> gotoxy state.x (clamp state.winh)
4028 end;
4030 | Home ->
4031 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4032 gotopage1 0 0
4034 | End ->
4035 let pageno = state.pagecount - 1 in
4036 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4037 if not (pagevisible state.layout pageno)
4038 then
4039 let h =
4040 match List.rev state.pdims with
4041 | [] -> state.winh
4042 | (_, _, h, _) :: _ -> h
4044 gotoxy
4045 state.x
4046 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4047 else postRedisplay "birdseye end";
4049 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4052 let drawpage l =
4053 let color =
4054 match state.mode with
4055 | Textentry _ -> scalecolor 0.4
4056 | LinkNav _ | View -> scalecolor 1.0
4057 | Birdseye (_, _, pageno, hooverpageno, _) ->
4058 if l.pageno = hooverpageno
4059 then scalecolor 0.9
4060 else (
4061 if l.pageno = pageno
4062 then (
4063 let c = scalecolor 1.0 in
4064 GlDraw.color c;
4065 GlDraw.line_width 3.0;
4066 let dispx = l.pagedispx in
4067 linerect
4068 (float (dispx-1)) (float (l.pagedispy-1))
4069 (float (dispx+l.pagevw+1))
4070 (float (l.pagedispy+l.pagevh+1));
4071 GlDraw.line_width 1.0;
4074 else scalecolor 0.8
4077 drawtiles l color;
4080 let postdrawpage l linkindexbase =
4081 match getopaque l.pageno with
4082 | Some opaque ->
4083 if tileready l l.pagex l.pagey
4084 then
4085 let x = l.pagedispx - l.pagex
4086 and y = l.pagedispy - l.pagey in
4087 let hlmask =
4088 match conf.columns with
4089 | Csingle _ | Cmulti _ ->
4090 (if conf.hlinks then 1 else 0)
4091 + (if state.glinks
4092 && not (isbirdseye state.mode) then 2 else 0)
4093 | Csplit _ -> 0
4095 let s =
4096 match state.mode with
4097 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4098 | Textentry _
4099 | Birdseye _
4100 | View
4101 | LinkNav _ -> E.s
4103 Hashtbl.find_all state.prects l.pageno |>
4104 List.iter (fun vals -> Ffi.drawprect opaque x y vals);
4105 let n =
4106 Ffi.postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4107 if n < 0
4108 then (Glutils.redisplay := true; 0)
4109 else n
4110 else 0
4111 | _ -> 0
4114 let scrollindicator () =
4115 let sbw, ph, sh = state.uioh#scrollph in
4116 let sbh, pw, sw = state.uioh#scrollpw in
4118 let x0,x1,hx0 =
4119 if conf.leftscroll
4120 then (0, sbw, sbw)
4121 else ((state.winw - sbw), state.winw, 0)
4124 Gl.enable `blend;
4125 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4126 let (r, g, b, alpha) = conf.sbarcolor in
4127 GlDraw.color (r, g, b) ~alpha;
4128 filledrect (float x0) 0. (float x1) (float state.winh);
4129 filledrect
4130 (float hx0) (float (state.winh - sbh))
4131 (float (hx0 + state.winw)) (float state.winh);
4132 let (r, g, b, alpha) = conf.sbarhndlcolor in
4133 GlDraw.color (r, g, b) ~alpha;
4135 filledrect (float x0) ph (float x1) (ph +. sh);
4136 let pw = pw +. float hx0 in
4137 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4138 Gl.disable `blend;
4141 let showsel () =
4142 match state.mstate with
4143 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
4144 | Msel ((x0, y0), (x1, y1)) ->
4145 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4146 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4147 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4148 if n0 != -1 && n0 = n1 then Ffi.seltext o0 (px0, py0, px1, py1);
4151 let showrects = function
4152 | [] -> ()
4153 | rects ->
4154 Gl.enable `blend;
4155 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4156 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4157 List.iter
4158 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4159 List.iter (fun l ->
4160 if l.pageno = pageno
4161 then
4162 let dx = float (l.pagedispx - l.pagex) in
4163 let dy = float (l.pagedispy - l.pagey) in
4164 let r, g, b, alpha = c in
4165 GlDraw.color (r, g, b) ~alpha;
4166 filledrect2
4167 (x0+.dx) (y0+.dy)
4168 (x1+.dx) (y1+.dy)
4169 (x3+.dx) (y3+.dy)
4170 (x2+.dx) (y2+.dy);
4171 ) state.layout
4172 ) rects;
4173 Gl.disable `blend;
4176 let display () =
4177 GlDraw.color (scalecolor2 conf.bgcolor);
4178 GlClear.color (scalecolor2 conf.bgcolor);
4179 GlClear.clear [`color];
4180 List.iter drawpage state.layout;
4181 let rects =
4182 match state.mode with
4183 | LinkNav (Ltexact (pageno, linkno)) ->
4184 begin match getopaque pageno with
4185 | Some opaque ->
4186 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
4187 let color =
4188 if conf.invert
4189 then (1.0, 1.0, 1.0, 0.5)
4190 else (0.0, 0.0, 0.5, 0.5)
4192 (pageno, color,
4193 (float x0, float y0,
4194 float x1, float y0,
4195 float x1, float y1,
4196 float x0, float y1)
4197 ) :: state.rects
4198 | None -> state.rects
4200 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4201 | Birdseye _
4202 | Textentry _
4203 | View -> state.rects
4205 showrects rects;
4206 let rec postloop linkindexbase = function
4207 | l :: rest ->
4208 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4209 postloop linkindexbase rest
4210 | [] -> ()
4212 showsel ();
4213 postloop 0 state.layout;
4214 state.uioh#display;
4215 begin match state.mstate with
4216 | Mzoomrect ((x0, y0), (x1, y1)) ->
4217 Gl.enable `blend;
4218 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4219 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4220 filledrect (float x0) (float y0) (float x1) (float y1);
4221 Gl.disable `blend;
4222 | Msel _
4223 | Mpan _
4224 | Mscrolly | Mscrollx
4225 | Mzoom _
4226 | Mnone -> ()
4227 end;
4228 enttext ();
4229 scrollindicator ();
4230 Wsi.swapb ();
4233 let display () =
4234 match state.reload with
4235 | Some (x, y, t) ->
4236 if x != state.x || y != state.y || abs_float @@ now () -. t > 0.5
4237 || (state.layout != [] && layoutready state.layout)
4238 then (
4239 state.reload <- None;
4240 display ()
4242 | None -> display ()
4245 let zoomrect x y x1 y1 =
4246 let x0 = min x x1
4247 and x1 = max x x1
4248 and y0 = min y y1 in
4249 let zoom = (float state.w) /. float (x1 - x0) in
4250 let margin =
4251 let simple () =
4252 if state.w < state.winw
4253 then (state.winw - state.w) / 2
4254 else 0
4256 match conf.fitmodel with
4257 | FitWidth | FitProportional -> simple ()
4258 | FitPage ->
4259 match conf.columns with
4260 | Csplit _ ->
4261 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4262 | Cmulti _ | Csingle _ -> simple ()
4264 gotoxy ((state.x + margin) - x0) (state.y + y0);
4265 state.anchor <- getanchor ();
4266 setzoom zoom;
4267 resetmstate ();
4270 let annot inline x y =
4271 match unproject x y with
4272 | Some (opaque, n, ux, uy) ->
4273 let add text =
4274 Ffi.addannot opaque ux uy text;
4275 wcmd "freepage %s" (~> opaque);
4276 Hashtbl.remove state.pagemap (n, state.gen);
4277 flushtiles ();
4278 gotoxy state.x state.y
4280 if inline
4281 then
4282 let ondone s = add s in
4283 let mode = state.mode in
4284 state.mode <- Textentry (
4285 ("annotation: ", E.s, None, textentry, ondone, true),
4286 fun _ -> state.mode <- mode);
4287 state.text <- E.s;
4288 enttext ();
4289 postRedisplay "annot"
4290 else add @@ getusertext E.s
4291 | _ -> ()
4294 let zoomblock x y =
4295 let g opaque l px py =
4296 match Ffi.rectofblock opaque px py with
4297 | Some a ->
4298 let x0 = a.(0) -. 20. in
4299 let x1 = a.(1) +. 20. in
4300 let y0 = a.(2) -. 20. in
4301 let zoom = (float state.w) /. (x1 -. x0) in
4302 let pagey = getpagey l.pageno in
4303 let margin = (state.w - l.pagew)/2 in
4304 let nx = -truncate x0 - margin in
4305 gotoxy nx (pagey + truncate y0);
4306 state.anchor <- getanchor ();
4307 setzoom zoom;
4308 None
4309 | None -> None
4311 match conf.columns with
4312 | Csplit _ ->
4313 impmsg "block zooming does not work properly in split columns mode"
4314 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4317 let scrollx x =
4318 let winw = state.winw - 1 in
4319 let s = float x /. float winw in
4320 let destx = truncate (float (state.w + winw) *. s) in
4321 gotoxy (winw - destx) state.y;
4322 state.mstate <- Mscrollx;
4325 let scrolly y =
4326 let s = float y /. float state.winh in
4327 let desty = truncate (s *. float (maxy ())) in
4328 gotoxy state.x desty;
4329 state.mstate <- Mscrolly;
4332 let viewmulticlick clicks x y mask =
4333 let g opaque l px py =
4334 let mark =
4335 match clicks with
4336 | 2 -> Mark_word
4337 | 3 -> Mark_line
4338 | 4 -> Mark_block
4339 | _ -> Mark_page
4341 if Ffi.markunder opaque px py mark
4342 then (
4343 Some (fun () ->
4344 let dopipe cmd =
4345 match getopaque l.pageno with
4346 | None -> ()
4347 | Some opaque -> pipesel opaque cmd
4349 state.roam <- (fun () -> dopipe conf.paxcmd);
4350 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4353 else None
4355 postRedisplay "viewmulticlick";
4356 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4359 let canselect () =
4360 match conf.columns with
4361 | Csplit _ -> false
4362 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4365 let viewmouse button down x y mask =
4366 match button with
4367 | n when (n == 4 || n == 5) && not down ->
4368 if Wsi.withctrl mask
4369 then (
4370 let incr =
4371 if n = 5
4372 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4373 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4375 let fx, fy =
4376 match state.mstate with
4377 | Mzoom (oldn, _, pos) when n = oldn -> pos
4378 | Mzoomrect _ | Mnone | Mpan _
4379 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4381 let zoom = conf.zoom -. incr in
4382 state.mstate <- Mzoom (n, 0, (x, y));
4383 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4384 then pivotzoom ~x ~y zoom
4385 else pivotzoom zoom
4387 else (
4388 match state.autoscroll with
4389 | Some step -> setautoscrollspeed step (n=4)
4390 | None ->
4391 if conf.wheelbypage || conf.presentation
4392 then (
4393 if n = 4
4394 then prevpage ()
4395 else nextpage ()
4397 else
4398 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4399 let incr = incr * 2 in
4400 let y = clamp incr in
4401 gotoxy state.x y
4404 | n when (n = 6 || n = 7) && not down && canpan () ->
4405 let x =
4406 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4407 gotoxy x state.y
4409 | 1 when Wsi.withshift mask ->
4410 state.mstate <- Mnone;
4411 if not down
4412 then (
4413 match unproject x y with
4414 | None -> ()
4415 | Some (_, pageno, ux, uy) ->
4416 let cmd = Printf.sprintf
4417 "%s %s %d %d %d"
4418 conf.stcmd state.path pageno ux uy
4420 match spawn cmd [] with
4421 | exception exn ->
4422 impmsg "execution of synctex command(%S) failed: %S"
4423 conf.stcmd @@ exntos exn
4424 | _pid -> ()
4427 | 1 when Wsi.withctrl mask ->
4428 if down
4429 then (
4430 Wsi.setcursor Wsi.CURSOR_FLEUR;
4431 state.mstate <- Mpan (x, y)
4433 else state.mstate <- Mnone
4435 | 3 ->
4436 if down
4437 then (
4438 if Wsi.withshift mask
4439 then (
4440 annot conf.annotinline x y;
4441 postRedisplay "addannot"
4443 else
4444 let p = (x, y) in
4445 Wsi.setcursor Wsi.CURSOR_CYCLE;
4446 state.mstate <- Mzoomrect (p, p)
4448 else (
4449 match state.mstate with
4450 | Mzoomrect ((x0, y0), _) ->
4451 if abs (x-x0) > 10 && abs (y - y0) > 10
4452 then zoomrect x0 y0 x y
4453 else (
4454 resetmstate ();
4455 postRedisplay "kill accidental zoom rect";
4457 | Msel _
4458 | Mpan _
4459 | Mscrolly | Mscrollx
4460 | Mzoom _
4461 | Mnone -> resetmstate ()
4464 | 1 when vscrollhit x ->
4465 if down
4466 then
4467 let _, position, sh = state.uioh#scrollph in
4468 if y > truncate position && y < truncate (position +. sh)
4469 then state.mstate <- Mscrolly
4470 else scrolly y
4471 else state.mstate <- Mnone
4473 | 1 when y > state.winh - hscrollh () ->
4474 if down
4475 then
4476 let _, position, sw = state.uioh#scrollpw in
4477 if x > truncate position && x < truncate (position +. sw)
4478 then state.mstate <- Mscrollx
4479 else scrollx x
4480 else state.mstate <- Mnone
4482 | 1 when state.bzoom -> if not down then zoomblock x y
4484 | 1 ->
4485 let dest = if down then getunder x y else Unone in
4486 begin match dest with
4487 | Ulinkuri _ -> gotounder dest
4488 | Unone when down ->
4489 Wsi.setcursor Wsi.CURSOR_FLEUR;
4490 state.mstate <- Mpan (x, y);
4491 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4492 | Unone | Utext _ ->
4493 if down
4494 then (
4495 if canselect ()
4496 then (
4497 state.mstate <- Msel ((x, y), (x, y));
4498 postRedisplay "mouse select";
4501 else (
4502 match state.mstate with
4503 | Mnone -> ()
4504 | Mzoom _ | Mscrollx | Mscrolly -> state.mstate <- Mnone
4505 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4506 | Mpan _ ->
4507 Wsi.setcursor Wsi.CURSOR_INHERIT;
4508 state.mstate <- Mnone
4509 | Msel ((x0, y0), (x1, y1)) ->
4510 let rec loop = function
4511 | [] -> ()
4512 | l :: rest ->
4513 let inside =
4514 let a0 = l.pagedispy in
4515 let a1 = a0 + l.pagevh in
4516 let b0 = l.pagedispx in
4517 let b1 = b0 + l.pagevw in
4518 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4519 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4521 if inside
4522 then
4523 match getopaque l.pageno with
4524 | Some opaque ->
4525 let dosel cmd () =
4526 pipef ~closew:false "Msel"
4527 (fun w ->
4528 Ffi.copysel w opaque;
4529 postRedisplay "Msel") cmd
4531 dosel conf.selcmd ();
4532 state.roam <- dosel conf.paxcmd;
4533 | None -> ()
4534 else loop rest
4536 loop state.layout;
4537 resetmstate ();
4540 | _ -> ()
4543 let birdseyemouse button down x y mask
4544 (conf, leftx, _, hooverpageno, anchor) =
4545 match button with
4546 | 1 when down ->
4547 let rec loop = function
4548 | [] -> ()
4549 | l :: rest ->
4550 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4551 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4552 then
4553 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4554 else loop rest
4556 loop state.layout
4557 | 3 -> ()
4558 | _ -> viewmouse button down x y mask
4561 let uioh = object
4562 method display = ()
4564 method key key mask =
4565 begin match state.mode with
4566 | Textentry textentry -> textentrykeyboard key mask textentry
4567 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4568 | View -> viewkeyboard key mask
4569 | LinkNav linknav -> linknavkeyboard key mask linknav
4570 end;
4571 state.uioh
4573 method button button bstate x y mask =
4574 begin match state.mode with
4575 | LinkNav _ | View -> viewmouse button bstate x y mask
4576 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4577 | Textentry _ -> ()
4578 end;
4579 state.uioh
4581 method multiclick clicks x y mask =
4582 begin match state.mode with
4583 | LinkNav _ | View -> viewmulticlick clicks x y mask
4584 | Birdseye _ | Textentry _ -> ()
4585 end;
4586 state.uioh
4588 method motion x y =
4589 begin match state.mode with
4590 | Textentry _ -> ()
4591 | View | Birdseye _ | LinkNav _ ->
4592 match state.mstate with
4593 | Mzoom _ | Mnone -> ()
4594 | Mpan (x0, y0) ->
4595 let dx = x - x0
4596 and dy = y0 - y in
4597 state.mstate <- Mpan (x, y);
4598 let x = if canpan () then panbound (state.x + dx) else state.x in
4599 let y = clamp dy in
4600 gotoxy x y
4602 | Msel (a, _) ->
4603 state.mstate <- Msel (a, (x, y));
4604 postRedisplay "motion select";
4606 | Mscrolly ->
4607 let y = min state.winh (max 0 y) in
4608 scrolly y
4610 | Mscrollx ->
4611 let x = min state.winw (max 0 x) in
4612 scrollx x
4614 | Mzoomrect (p0, _) ->
4615 state.mstate <- Mzoomrect (p0, (x, y));
4616 postRedisplay "motion zoomrect";
4617 end;
4618 state.uioh
4620 method pmotion x y =
4621 begin match state.mode with
4622 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4623 let rec loop = function
4624 | [] ->
4625 if hooverpageno != -1
4626 then (
4627 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4628 postRedisplay "pmotion birdseye no hoover";
4630 | l :: rest ->
4631 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4632 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4633 then (
4634 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4635 postRedisplay "pmotion birdseye hoover";
4637 else loop rest
4639 loop state.layout
4641 | Textentry _ -> ()
4643 | LinkNav _ | View ->
4644 match state.mstate with
4645 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4646 | Mnone ->
4647 updateunder x y;
4648 if canselect ()
4649 then
4650 match conf.pax with
4651 | None -> ()
4652 | Some past ->
4653 let now = now () in
4654 let delta = now -. past in
4655 if delta > 0.01
4656 then paxunder x y
4657 else conf.pax <- Some now
4658 end;
4659 state.uioh
4661 method infochanged _ = ()
4663 method scrollph =
4664 let maxy = maxy () in
4665 let p, h =
4666 if maxy = 0
4667 then 0.0, float state.winh
4668 else scrollph state.y maxy
4670 vscrollw (), p, h
4672 method scrollpw =
4673 let fwinw = float (state.winw - vscrollw ()) in
4674 let sw =
4675 let sw = fwinw /. float state.w in
4676 let sw = fwinw *. sw in
4677 max sw (float conf.scrollh)
4679 let position =
4680 let maxx = state.w + state.winw in
4681 let x = state.winw - state.x in
4682 let percent = float x /. float maxx in
4683 (fwinw -. sw) *. percent
4685 hscrollh (), position, sw
4687 method modehash =
4688 let modename =
4689 match state.mode with
4690 | LinkNav _ -> "links"
4691 | Textentry _ -> "textentry"
4692 | Birdseye _ -> "birdseye"
4693 | View -> "view"
4695 findkeyhash conf modename
4697 method eformsgs = true
4698 method alwaysscrolly = false
4699 method scroll dx dy =
4700 let x = if canpan () then panbound (state.x + dx) else state.x in
4701 gotoxy x (clamp (2 * dy));
4702 state.uioh
4703 method zoom z x y =
4704 pivotzoom ~x ~y (conf.zoom *. exp z);
4705 end;;
4707 let addrect pageno r g b a x0 y0 x1 y1 =
4708 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4711 let ract cmds =
4712 let cl = splitatchar cmds ' ' in
4713 let scan s fmt f =
4714 try Scanf.sscanf s fmt f
4715 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4716 cmds @@ exntos exn
4718 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4719 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4720 s pageno r g b a x0 y0 x1 y1;
4721 onpagerect
4722 pageno
4723 (fun w h ->
4724 let _,w1,h1,_ = getpagedim pageno in
4725 let sw = float w1 /. float w
4726 and sh = float h1 /. float h in
4727 let x0s = x0 *. sw
4728 and x1s = x1 *. sw
4729 and y0s = y0 *. sh
4730 and y1s = y1 *. sh in
4731 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4732 let color = (r, g, b, a) in
4733 if conf.verbose then debugrect rect;
4734 state.rects <- (pageno, color, rect) :: state.rects;
4735 postRedisplay s;
4738 match cl with
4739 | "reload", "" -> reload ()
4740 | "goto", args ->
4741 scan args "%u %f %f"
4742 (fun pageno x y ->
4743 let cmd, _ = state.geomcmds in
4744 if emptystr cmd
4745 then gotopagexy pageno x y
4746 else
4747 let f prevf () =
4748 gotopagexy pageno x y;
4749 prevf ()
4751 state.reprf <- f state.reprf
4753 | "goto1", args -> scan args "%u %f" gotopage
4754 | "gotor", args -> scan args "%S" gotoremote
4755 | "rect", args ->
4756 scan args "%u %u %f %f %f %f"
4757 (fun pageno c x0 y0 x1 y1 ->
4758 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4759 rectx "rect" pageno color x0 y0 x1 y1;
4761 | "prect", args ->
4762 scan args "%u %f %f %f %f %f %f %f %f"
4763 (fun pageno r g b alpha x0 y0 x1 y1 ->
4764 addrect pageno r g b alpha x0 y0 x1 y1;
4765 postRedisplay "prect"
4767 | "pgoto", args ->
4768 scan args "%u %f %f"
4769 (fun pageno x y ->
4770 let optopaque =
4771 match getopaque pageno with
4772 | Some opaque -> opaque
4773 | None -> ~< E.s
4775 pgoto optopaque pageno x y;
4776 let rec fixx = function
4777 | [] -> ()
4778 | l :: rest ->
4779 if l.pageno = pageno
4780 then gotoxy (state.x - l.pagedispx) state.y
4781 else fixx rest
4783 let layout =
4784 let mult =
4785 match conf.columns with
4786 | Csingle _ | Csplit _ -> 1
4787 | Cmulti ((n, _, _), _) -> n
4789 layout 0 state.y (state.winw * mult) state.winh
4791 fixx layout
4793 | "activatewin", "" -> Wsi.activatewin ()
4794 | "quit", "" -> raise Quit
4795 | "keys", keys ->
4796 begin try
4797 let l = Config.keys_of_string keys in
4798 List.iter (fun (k, m) -> keyboard k m) l
4799 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4800 cmds @@ exntos exn
4802 | "clearrects", "" ->
4803 Hashtbl.clear state.prects;
4804 postRedisplay "clearrects"
4805 | _ ->
4806 adderrfmt "remote command"
4807 "error processing remote command: %S\n" cmds;
4810 let remote =
4811 let scratch = Bytes.create 80 in
4812 let buf = Buffer.create 80 in
4813 fun fd ->
4814 match tempfailureretry (Unix.read fd scratch 0) 80 with
4815 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4816 | 0 ->
4817 Unix.close fd;
4818 if Buffer.length buf > 0
4819 then (
4820 let s = Buffer.contents buf in
4821 Buffer.clear buf;
4822 ract s;
4824 None
4825 | n ->
4826 let rec eat ppos =
4827 let nlpos =
4828 match Bytes.index_from scratch ppos '\n' with
4829 | pos -> if pos >= n then -1 else pos
4830 | exception Not_found -> -1
4832 if nlpos >= 0
4833 then (
4834 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4835 let s = Buffer.contents buf in
4836 Buffer.clear buf;
4837 ract s;
4838 eat (nlpos+1);
4840 else (
4841 Buffer.add_subbytes buf scratch ppos (n-ppos);
4842 Some fd
4844 in eat 0
4847 let remoteopen path =
4848 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4849 with exn ->
4850 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4851 None
4854 let () =
4855 Utils.vlogf := (fun s -> if conf.verbose then prerr_endline s else ignore s);
4856 let gcconfig = ref false in
4857 let rcmdpath = ref E.s in
4858 let pageno = ref None in
4859 let openlast = ref false in
4860 let doreap = ref false in
4861 let csspath = ref None in
4862 selfexec := Sys.executable_name;
4863 Arg.parse
4864 (Arg.align
4865 [("-p", Arg.String (fun s -> state.password <- s),
4866 "<password> Set password");
4868 ("-f", Arg.String
4869 (fun s ->
4870 Config.fontpath := s;
4871 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4873 "<path> Set path to the user interface font");
4875 ("-c", Arg.String
4876 (fun s ->
4877 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4878 Config.confpath := s),
4879 "<path> Set path to the configuration file");
4881 ("-last", Arg.Set openlast, " Open last document");
4883 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4884 "<page-number> Jump to page");
4886 ("-tcf", Arg.String (fun s -> Config.tcfpath := s),
4887 "<path> Set path to the trim cache file");
4889 ("-dest", Arg.String (fun s -> state.nameddest <- s),
4890 "<named-destination> Set named destination");
4892 ("-remote", Arg.String (fun s -> rcmdpath := s),
4893 "<path> Set path to the source of remote commands");
4895 ("-gc", Arg.Set gcconfig, " Collect config garbage");
4897 ("-v", Arg.Unit (fun () ->
4898 Printf.printf
4899 "%s\nconfiguration file: %s\n"
4900 (Help.version ())
4901 Config.defconfpath;
4902 exit 0), " Print version and exit");
4904 ("-css", Arg.String (fun s -> csspath := Some s),
4905 "<path> Set path to the style sheet to use with EPUB/HTML");
4907 ("-origin", Arg.String (fun s -> state.origin <- s),
4908 "<origin> <undocumented>");
4910 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
4911 ("-layout-height", Arg.Set_int layouth,
4912 "<height> layout height html/epub/etc (-1, 0, N)");
4915 (fun s -> state.path <- s)
4916 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4918 let histmode = emptystr state.path && not !openlast in
4920 if not (Config.load !openlast)
4921 then dolog "failed to load configuration";
4923 begin match !pageno with
4924 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
4925 | None -> ()
4926 end;
4928 fillhelp ();
4929 if !gcconfig
4930 then (
4931 Config.gc ();
4932 exit 0
4935 let mu =
4936 object (self)
4937 val mutable m_clicks = 0
4938 val mutable m_click_x = 0
4939 val mutable m_click_y = 0
4940 val mutable m_lastclicktime = infinity
4942 method private cleanup =
4943 state.roam <- noroam;
4944 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) state.pagemap
4945 method expose = postRedisplay "expose"
4946 method visible v =
4947 let name =
4948 match v with
4949 | Wsi.Unobscured -> "unobscured"
4950 | Wsi.PartiallyObscured -> "partiallyobscured"
4951 | Wsi.FullyObscured -> "fullyobscured"
4953 vlog "visibility change %s" name
4954 method display = display ()
4955 method map mapped = vlog "mapped %b" mapped
4956 method reshape w h =
4957 self#cleanup;
4958 reshape w h
4959 method mouse b d x y m =
4960 if d && canselect ()
4961 then (
4963 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
4965 m_click_x <- x;
4966 m_click_y <- y;
4967 if b = 1
4968 then (
4969 let t = now () in
4970 if abs x - m_click_x > 10
4971 || abs y - m_click_y > 10
4972 || abs_float (t -. m_lastclicktime) > 0.3
4973 then m_clicks <- 0;
4974 m_clicks <- m_clicks + 1;
4975 m_lastclicktime <- t;
4976 if m_clicks = 1
4977 then (
4978 self#cleanup;
4979 postRedisplay "cleanup";
4980 state.uioh <- state.uioh#button b d x y m;
4982 else state.uioh <- state.uioh#multiclick m_clicks x y m
4984 else (
4985 self#cleanup;
4986 m_clicks <- 0;
4987 m_lastclicktime <- infinity;
4988 state.uioh <- state.uioh#button b d x y m
4991 else state.uioh <- state.uioh#button b d x y m
4992 method motion x y =
4993 state.mpos <- (x, y);
4994 state.uioh <- state.uioh#motion x y
4995 method pmotion x y =
4996 state.mpos <- (x, y);
4997 state.uioh <- state.uioh#pmotion x y
4998 method key k m =
4999 vlog "k=%#x m=%#x" k m;
5000 let mascm = m land (
5001 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
5002 ) in
5003 let keyboard k m =
5004 let x = state.x and y = state.y in
5005 keyboard k m;
5006 if x != state.x || y != state.y then self#cleanup
5008 match state.keystate with
5009 | KSnone ->
5010 let km = k, mascm in
5011 begin
5012 match
5013 let modehash = state.uioh#modehash in
5014 try Hashtbl.find modehash km
5015 with Not_found ->
5016 try Hashtbl.find (findkeyhash conf "global") km
5017 with Not_found -> KMinsrt (k, m)
5018 with
5019 | KMinsrt (k, m) -> keyboard k m
5020 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5021 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5023 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5024 List.iter (fun (k, m) -> keyboard k m) insrt;
5025 state.keystate <- KSnone
5026 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5027 state.keystate <- KSinto (keys, insrt)
5028 | KSinto _ -> state.keystate <- KSnone
5030 method enter x y =
5031 state.mpos <- (x, y);
5032 state.uioh <- state.uioh#pmotion x y
5033 method leave = state.mpos <- (-1, -1)
5034 method winstate wsl = state.winstate <- wsl
5035 method quit : 'a. 'a = raise Quit
5036 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5037 method zoom z x y = state.uioh#zoom z x y
5038 method opendoc path =
5039 state.mode <- View;
5040 state.uioh <- uioh;
5041 postRedisplay "opendoc";
5042 opendoc path state.password
5045 if !Config.tcfpath == E.s
5046 then Config.tcfpath := conf.trimcachepath;
5047 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5048 state.wsfd <- wsfd;
5050 if not @@ List.exists GlMisc.check_extension
5051 [ "GL_ARB_texture_rectangle"
5052 ; "GL_EXT_texture_recangle"
5053 ; "GL_NV_texture_rectangle" ]
5054 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5056 let cs, ss =
5057 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5058 | exception exn ->
5059 dolog "socketpair failed: %s" @@ exntos exn;
5060 exit 1
5061 | (r, w) ->
5062 cloexec r;
5063 cloexec w;
5064 r, w
5067 setcheckers conf.checkers;
5068 begin match !csspath with
5069 | None -> ()
5070 | Some "" -> conf.css <- E.s
5071 | Some path ->
5072 let css = filecontents path in
5073 let l = String.length css in
5074 conf.css <-
5075 if substratis css (l-2) "\r\n"
5076 then String.sub css 0 (l-2)
5077 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
5078 end;
5079 Ffi.settrimcachepath !Config.tcfpath;
5080 conf.trimcachepath <- !Config.tcfpath;
5081 Ffi.init cs (
5082 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5083 conf.texcount, conf.sliceheight, conf.mustoresize,
5084 conf.colorspace, !Config.fontpath
5086 List.iter GlArray.enable [`texture_coord; `vertex];
5087 GlTex.env (`color conf.texturecolor);
5088 state.ss <- ss;
5089 reshape ~firsttime:true winw winh;
5090 state.uioh <- uioh;
5091 if histmode
5092 then (
5093 Wsi.settitle "llpp (history)";
5094 enterhistmode ();
5096 else (
5097 state.text <- "Opening " ^ (mbtoutf8 state.path);
5098 opendoc state.path state.password;
5100 display ();
5101 Wsi.mapwin ();
5102 Wsi.setcursor Wsi.CURSOR_INHERIT;
5103 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5105 let rec reap () =
5106 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5107 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5108 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5109 | 0, _ -> ()
5110 | _pid, _status -> reap ()
5112 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5114 let optrfd =
5115 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
5118 let rec loop deadline =
5119 if !doreap
5120 then (
5121 doreap := false;
5122 reap ()
5124 let r = [state.ss; state.wsfd] in
5125 let r =
5126 match !optrfd with
5127 | None -> r
5128 | Some fd -> fd :: r
5130 if !redisplay
5131 then (
5132 Glutils.redisplay := false;
5133 display ();
5135 let timeout =
5136 let now = now () in
5137 if deadline > now
5138 then (
5139 if deadline = infinity
5140 then ~-.1.0
5141 else max 0.0 (deadline -. now)
5143 else 0.0
5145 let r, _, _ =
5146 try Unix.select r [] [] timeout
5147 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5149 begin match r with
5150 | [] ->
5151 let newdeadline =
5152 match state.autoscroll with
5153 | Some step when step != 0 ->
5154 if state.slideshow land 1 = 1
5155 then (
5156 if state.slideshow land 2 = 0
5157 then state.slideshow <- state.slideshow lor 2
5158 else if step < 0 then prevpage () else nextpage ();
5159 deadline +. (float (abs step))
5161 else
5162 let y = state.y + step in
5163 let fy = if conf.maxhfit then state.winh else 0 in
5164 let y =
5165 if y < 0
5166 then state.maxy - fy
5167 else if y >= state.maxy - fy then 0 else y
5169 gotoxy state.x y;
5170 deadline +. 0.01
5171 | _ -> infinity
5173 loop newdeadline
5175 | l ->
5176 let rec checkfds = function
5177 | [] -> ()
5178 | fd :: rest when fd = state.ss ->
5179 let cmd = Ffi.rcmd state.ss in
5180 act cmd;
5181 checkfds rest
5183 | fd :: rest when fd = state.wsfd ->
5184 Wsi.readresp fd;
5185 checkfds rest
5187 | fd :: rest when Some fd = !optrfd ->
5188 begin match remote fd with
5189 | None -> optrfd := remoteopen !rcmdpath;
5190 | opt -> optrfd := opt
5191 end;
5192 checkfds rest
5194 | _ :: rest ->
5195 dolog "select returned unknown descriptor";
5196 checkfds rest
5198 checkfds l;
5199 let newdeadline =
5200 let deadline1 =
5201 if deadline = infinity
5202 then now () +. 0.01
5203 else deadline
5205 match state.autoscroll with
5206 | Some step when step != 0 -> deadline1
5207 | _ -> infinity
5209 loop newdeadline
5210 end;
5212 match loop infinity with
5213 | exception Quit ->
5214 Config.save leavebirdseye;
5215 if Ffi.hasunsavedchanges ()
5216 then save ()
5217 | _ -> error "umpossible - infinity reached"