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