Update todo
[llpp.git] / main.ml
blob8c02ce74f63e015aa594439fec7621a83a694bc0
1 open Utils;;
2 open Config;;
3 open Glutils;;
4 open Listview;;
6 let selfexec = ref E.s;;
7 let ignoredoctitlte = ref false;;
8 let layouth = ref ~-1;;
9 let checkerstexid = ref None;;
11 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
12 dolog {|rect {
13 x0,y0=(% f, % f)
14 x1,y1=(% f, % f)
15 x2,y2=(% f, % f)
16 x3,y3=(% f, % f)
17 }|} x0 y0 x1 y1 x2 y2 x3 y3;
20 let pgscale h = truncate (float h *. conf.pgscale);;
22 let hscrollh () =
23 if ((conf.scrollb land scrollbhv != 0) && (state.w > state.winw))
24 || state.uioh#alwaysscrolly
25 then conf.scrollbw
26 else 0
29 let setfontsize n =
30 fstate.fontsize <- n;
31 fstate.wwidth <- measurestr fstate.fontsize "w";
32 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
35 let launchpath () =
36 if emptystr conf.pathlauncher
37 then dolog "%s" state.path
38 else (
39 let command =
40 Str.global_replace Utils.Re.percent state.path conf.pathlauncher in
41 match spawn command [] with
42 | _pid -> ()
43 | exception exn -> dolog "failed to execute `%s': %s" command @@ exntos exn
47 let getopaque pageno =
48 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
49 with Not_found -> None
52 let pagetranslatepoint l x y =
53 let dy = y - l.pagedispy in
54 let y = dy + l.pagey in
55 let dx = x - l.pagedispx in
56 let x = dx + l.pagex in
57 (x, y);
60 let onppundermouse g x y d =
61 let rec f = function
62 | l :: rest ->
63 begin match getopaque l.pageno with
64 | Some opaque ->
65 let x0 = l.pagedispx in
66 let x1 = x0 + l.pagevw in
67 let y0 = l.pagedispy in
68 let y1 = y0 + l.pagevh in
69 if y >= y0 && y <= y1 && x >= x0 && x <= x1
70 then
71 let px, py = pagetranslatepoint l x y in
72 match g opaque l px py with
73 | Some res -> res
74 | None -> f rest
75 else f rest
76 | _ -> f rest
77 end
78 | [] -> d
80 f state.layout
83 let getunder x y =
84 let g opaque l px py =
85 if state.bzoom
86 then (
87 match Ffi.rectofblock opaque px py with
88 | Some [|x0;x1;y0;y1|] ->
89 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
90 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
91 state.rects <- [l.pageno, color, rect];
92 postRedisplay "getunder";
93 | _ -> ()
95 let under = Ffi.whatsunder opaque px py in
96 if under = Unone then None else Some under
98 onppundermouse g x y Unone
101 let unproject x y =
102 let g opaque l x y =
103 match Ffi.unproject opaque x y with
104 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
105 | None -> None
107 onppundermouse g x y None;
110 let showtext c s =
111 state.text <- Printf.sprintf "%c%s" c s;
112 postRedisplay "showtext";
115 let impmsg fmt = Format.ksprintf (fun s -> showtext '!' s) fmt;;
117 let pipesel opaque cmd =
118 if Ffi.hassel opaque
119 then pipef ~closew:false "pipesel"
120 (fun w ->
121 Ffi.copysel w opaque;
122 postRedisplay "pipesel"
123 ) cmd
126 let paxunder x y =
127 let g opaque l px py =
128 if Ffi.markunder opaque px py conf.paxmark
129 then
130 Some (fun () ->
131 match getopaque l.pageno with
132 | None -> ()
133 | Some opaque -> pipesel opaque conf.paxcmd
135 else None
137 postRedisplay "paxunder";
138 if conf.paxmark = Mark_page
139 then
140 List.iter (fun l ->
141 match getopaque l.pageno with
142 | None -> ()
143 | Some opaque -> Ffi.clearmark opaque) state.layout;
144 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
147 let undertext = function
148 | Unone -> "none"
149 | Ulinkuri s -> s
150 | Utext s -> "font: " ^ s
151 | Uannotation (opaque, slinkindex) ->
152 "annotation: " ^ Ffi.getannotcontents opaque slinkindex
155 let updateunder x y =
156 match getunder x y with
157 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
158 | Ulinkuri uri ->
159 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
160 Wsi.setcursor Wsi.CURSOR_INFO
161 | Utext s ->
162 if conf.underinfo then showtext 'f' ("ont: " ^ s);
163 Wsi.setcursor Wsi.CURSOR_TEXT
164 | Uannotation _ ->
165 if conf.underinfo then showtext 'a' "nnotation";
166 Wsi.setcursor Wsi.CURSOR_INFO
169 let showlinktype under =
170 if conf.underinfo && under != Unone
171 then showtext ' ' @@ undertext under
174 let intentry_with_suffix text key =
175 let text =
176 match [@warning "-4"] key with
177 | Keys.Ascii ('0'..'9' as c) -> addchar text c
178 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
179 addchar text @@ asciilower c
180 | _ ->
181 state.text <- "invalid key";
182 text
184 TEcont text
187 let wcmd fmt =
188 let b = Buffer.create 16 in
189 Printf.kbprintf
190 (fun b ->
191 let b = Buffer.to_bytes b in
192 Ffi.wcmd state.ss b @@ Bytes.length b
193 ) b fmt
196 let nogeomcmds = function
197 | s, [] -> emptystr s
198 | _ -> false
201 let layoutN ((columns, coverA, coverB), b) x y sw sh =
202 let rec fold accu n =
203 if n = Array.length b
204 then accu
205 else
206 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
207 if (vy - y) > sh &&
208 (n = coverA - 1
209 || n = state.pagecount - coverB
210 || (n - coverA) mod columns = columns - 1)
211 then accu
212 else
213 let accu =
214 if vy + h > y
215 then
216 let pagey = max 0 (y - vy) in
217 let pagedispy = if pagey > 0 then 0 else vy - y in
218 let pagedispx, pagex =
219 let pdx =
220 if n = coverA - 1 || n = state.pagecount - coverB
221 then x + (sw - w) / 2
222 else dx + xoff + x
224 if pdx < 0
225 then 0, -pdx
226 else pdx, 0
228 let pagevw =
229 let vw = sw - pagedispx in
230 let pw = w - pagex in
231 min vw pw
233 let pagevh = min (h - pagey) (sh - pagedispy) in
234 if pagevw > 0 && pagevh > 0
235 then
236 { pageno = n
237 ; pagedimno = pdimno
238 ; pagew = w
239 ; pageh = h
240 ; pagex = pagex
241 ; pagey = pagey
242 ; pagevw = pagevw
243 ; pagevh = pagevh
244 ; pagedispx = pagedispx
245 ; pagedispy = pagedispy
246 ; pagecol = 0
247 } :: accu
248 else accu
249 else accu
251 fold accu (n+1)
253 if Array.length b = 0
254 then []
255 else List.rev (fold [] (page_of_y y))
258 let layoutS (columns, b) x y sw sh =
259 let rec fold accu n =
260 if n = Array.length b
261 then accu
262 else
263 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
264 if (vy - y) > sh
265 then accu
266 else
267 let accu =
268 if vy + pageh > y
269 then
270 let x = xoff + x in
271 let pagey = max 0 (y - vy) in
272 let pagedispy = if pagey > 0 then 0 else vy - y in
273 let pagedispx, pagex =
274 if px = 0
275 then (
276 if x < 0
277 then 0, -x
278 else x, 0
280 else (
281 let px = px - x in
282 if px < 0
283 then -px, 0
284 else 0, px
287 let pagecolw = pagew/columns in
288 let pagedispx =
289 if pagecolw < sw
290 then pagedispx + ((sw - pagecolw) / 2)
291 else pagedispx
293 let pagevw =
294 let vw = sw - pagedispx in
295 let pw = pagew - pagex in
296 min vw pw
298 let pagevw = min pagevw pagecolw in
299 let pagevh = min (pageh - pagey) (sh - pagedispy) in
300 if pagevw > 0 && pagevh > 0
301 then
302 { pageno = n/columns
303 ; pagedimno = pdimno
304 ; pagew = pagew
305 ; pageh = pageh
306 ; pagex = pagex
307 ; pagey = pagey
308 ; pagevw = pagevw
309 ; pagevh = pagevh
310 ; pagedispx = pagedispx
311 ; pagedispy = pagedispy
312 ; pagecol = n mod columns
313 } :: accu
314 else accu
315 else accu
317 fold accu (n+1)
319 List.rev (fold [] 0)
322 let layout x y sw sh =
323 if nogeomcmds state.geomcmds
324 then
325 match conf.columns with
326 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
327 | Cmulti c -> layoutN c x y sw sh
328 | Csplit s -> layoutS s x y sw sh
329 else []
332 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
333 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
335 let itertiles l f =
336 let tilex = l.pagex mod conf.tilew in
337 let tiley = l.pagey mod conf.tileh in
339 let col = l.pagex / conf.tilew in
340 let row = l.pagey / conf.tileh in
342 let rec rowloop row y0 dispy h =
343 if h != 0
344 then
345 let dh = conf.tileh - y0 in
346 let dh = min h dh in
347 let rec colloop col x0 dispx w =
348 if w != 0
349 then
350 let dw = conf.tilew - x0 in
351 let dw = min w dw in
352 f col row dispx dispy x0 y0 dw dh;
353 colloop (col+1) 0 (dispx+dw) (w-dw)
355 colloop col tilex l.pagedispx l.pagevw;
356 rowloop (row+1) 0 (dispy+dh) (h-dh)
358 if l.pagevw > 0 && l.pagevh > 0
359 then rowloop row tiley l.pagedispy l.pagevh;
362 let gettileopaque l col row =
363 let key = l.pageno, state.gen, conf.colorspace,
364 conf.angle, l.pagew, l.pageh, col, row in
365 try Some (Hashtbl.find state.tilemap key)
366 with Not_found -> None
369 let puttileopaque l col row gen colorspace angle opaque size elapsed =
370 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
371 Hashtbl.add state.tilemap key (opaque, size, elapsed)
374 let drawtiles l color =
375 GlDraw.color color;
376 Ffi.begintiles ();
377 let f col row x y tilex tiley w h =
378 match gettileopaque l col row with
379 | Some (opaque, _, t) ->
380 let params = x, y, w, h, tilex, tiley in
381 if conf.invert
382 then GlTex.env (`mode `blend);
383 Ffi.drawtile params opaque;
384 if conf.invert
385 then GlTex.env (`mode `modulate);
386 if conf.debug
387 then (
388 Ffi.endtiles ();
389 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
390 let w = measurestr fstate.fontsize s in
391 GlDraw.color (0.0, 0.0, 0.0);
392 filledrect
393 (float (x-2))
394 (float (y-2))
395 (float (x+2) +. w)
396 (float (y + fstate.fontsize + 2));
397 GlDraw.color color;
398 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
399 Ffi.begintiles ();
402 | None ->
403 Ffi.endtiles ();
404 let w = let lw = state.winw - x in min lw w
405 and h = let lh = state.winh - y in min lh h
407 if conf.invert
408 then GlTex.env (`mode `blend);
409 begin match !checkerstexid with
410 | Some id ->
411 Gl.enable `texture_2d;
412 GlTex.bind_texture ~target:`texture_2d id;
413 let x0 = float x
414 and y0 = float y
415 and x1 = float (x+w)
416 and y1 = float (y+h) in
418 let tw = float w /. 16.0
419 and th = float h /. 16.0 in
420 let tx0 = float tilex /. 16.0
421 and ty0 = float tiley /. 16.0 in
422 let tx1 = tx0 +. tw
423 and ty1 = ty0 +. th in
424 Raw.sets_float Glutils.vraw ~pos:0
425 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
426 Raw.sets_float Glutils.traw ~pos:0
427 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
428 GlArray.vertex `two Glutils.vraw;
429 GlArray.tex_coord `two Glutils.traw;
430 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
431 Gl.disable `texture_2d;
433 | None ->
434 GlDraw.color (1.0, 1.0, 1.0);
435 filledrect (float x) (float y) (float (x+w)) (float (y+h));
436 end;
437 if conf.invert
438 then GlTex.env (`mode `modulate);
439 if w > 128 && h > fstate.fontsize + 10
440 then (
441 let c = if conf.invert then 1.0 else 0.0 in
442 GlDraw.color (c, c, c);
443 let c, r =
444 if conf.verbose
445 then (col*conf.tilew, row*conf.tileh)
446 else col, row
448 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
450 GlDraw.color color;
451 Ffi.begintiles ();
453 itertiles l f;
454 Ffi.endtiles ();
457 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
459 let tilevisible1 l x y =
460 let ax0 = l.pagex
461 and ax1 = l.pagex + l.pagevw
462 and ay0 = l.pagey
463 and ay1 = l.pagey + l.pagevh in
465 let bx0 = x
466 and by0 = y in
467 let bx1 = min (bx0 + conf.tilew) l.pagew
468 and by1 = min (by0 + conf.tileh) l.pageh in
470 let rx0 = max ax0 bx0
471 and ry0 = max ay0 by0
472 and rx1 = min ax1 bx1
473 and ry1 = min ay1 by1 in
475 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
476 nonemptyintersection
479 let tilevisible layout n x y =
480 let rec findpageinlayout m = function
481 | l :: rest when l.pageno = n ->
482 tilevisible1 l x y || (
483 match conf.columns with
484 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
485 | Csplit _ | Csingle _ | Cmulti _ -> false
487 | _ :: rest -> findpageinlayout 0 rest
488 | [] -> false
490 findpageinlayout 0 layout;
493 let tileready l x y =
494 tilevisible1 l x y &&
495 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
498 let tilepage n p layout =
499 let rec loop = function
500 | l :: rest ->
501 if l.pageno = n
502 then
503 let f col row _ _ _ _ _ _ =
504 if state.currently = Idle
505 then
506 match gettileopaque l col row with
507 | Some _ -> ()
508 | None ->
509 let x = col*conf.tilew
510 and y = row*conf.tileh in
511 let w =
512 let w = l.pagew - x in
513 min w conf.tilew
515 let h =
516 let h = l.pageh - y in
517 min h conf.tileh
519 let pbo =
520 if conf.usepbo
521 then Ffi.getpbo w h conf.colorspace
522 else ~< "0"
524 wcmd "tile %s %d %d %d %d %s" (~> p) x y w h (~> pbo);
525 state.currently <-
526 Tiling (
527 l, p, conf.colorspace, conf.angle,
528 state.gen, col, row, conf.tilew, conf.tileh
531 itertiles l f;
532 else
533 loop rest
535 | [] -> ()
537 if nogeomcmds state.geomcmds
538 then loop layout;
541 let preloadlayout x y sw sh =
542 let y = if y < sh then 0 else y - sh in
543 let x = min 0 (x + sw) in
544 let h = sh*3 in
545 let w = sw*3 in
546 layout x y w h;
549 let load pages =
550 let rec loop pages =
551 if state.currently = Idle
552 then
553 match pages with
554 | l :: rest ->
555 begin match getopaque l.pageno with
556 | None ->
557 wcmd "page %d %d" l.pageno l.pagedimno;
558 state.currently <- Loading (l, state.gen);
559 | Some opaque ->
560 tilepage l.pageno opaque pages;
561 loop rest
562 end;
563 | _ -> ()
565 if nogeomcmds state.geomcmds
566 then loop pages
569 let preload pages =
570 load pages;
571 if conf.preload && state.currently = Idle
572 then load (preloadlayout state.x state.y state.winw state.winh);
575 let layoutready layout =
576 let rec fold all ls =
577 all && match ls with
578 | l :: rest ->
579 let seen = ref false in
580 let allvisible = ref true in
581 let foo col row _ _ _ _ _ _ =
582 seen := true;
583 allvisible := !allvisible &&
584 begin match gettileopaque l col row with
585 | Some _ -> true
586 | None -> false
589 itertiles l foo;
590 fold (!seen && !allvisible) rest
591 | [] -> true
593 let alltilesvisible = fold true layout in
594 alltilesvisible;
597 let gotoxy x y =
598 let y = bound y 0 state.maxy in
599 let y, layout =
600 let layout = layout x y state.winw state.winh in
601 postRedisplay "gotoxy ready";
602 y, layout
604 state.x <- x;
605 state.y <- y;
606 state.layout <- layout;
607 begin match state.mode with
608 | LinkNav ln ->
609 begin match ln with
610 | Ltexact (pageno, linkno) ->
611 let rec loop = function
612 | [] ->
613 state.lnava <- Some (pageno, linkno);
614 state.mode <- LinkNav (Ltgendir 0)
615 | l :: _ when l.pageno = pageno ->
616 begin match getopaque pageno with
617 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
618 | Some opaque ->
619 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
620 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
621 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
622 then state.mode <- LinkNav (Ltgendir 0)
624 | _ :: rest -> loop rest
626 loop layout
627 | Ltnotready _ | Ltgendir _ -> ()
629 | Birdseye _ | Textentry _ | View -> ()
630 end;
631 begin match state.mode with
632 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
633 if not (pagevisible layout pageno)
634 then (
635 match state.layout with
636 | [] -> ()
637 | l :: _ ->
638 state.mode <- Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
640 | LinkNav lt ->
641 begin match lt with
642 | Ltnotready (_, dir)
643 | Ltgendir dir ->
644 let linknav =
645 let rec loop = function
646 | [] -> lt
647 | l :: rest ->
648 match getopaque l.pageno with
649 | None -> Ltnotready (l.pageno, dir)
650 | Some opaque ->
651 let link =
652 let ld =
653 if dir = 0
654 then LDfirstvisible (l.pagex, l.pagey, dir)
655 else if dir > 0 then LDfirst else LDlast
657 Ffi.findlink opaque ld
659 match link with
660 | Lnotfound -> loop rest
661 | Lfound n ->
662 showlinktype (Ffi.getlink opaque n);
663 Ltexact (l.pageno, n)
665 loop state.layout
667 state.mode <- LinkNav linknav
668 | Ltexact _ -> ()
670 | Textentry _ | View -> ()
671 end;
672 preload layout;
673 if conf.updatecurs
674 then (
675 let mx, my = state.mpos in
676 updateunder mx my;
680 let conttiling pageno opaque =
681 tilepage pageno opaque
682 (if conf.preload
683 then preloadlayout state.x state.y state.winw state.winh
684 else state.layout)
687 let gotoxy x y =
688 if not conf.verbose then state.text <- E.s;
689 gotoxy x y;
692 let getanchory (n, top, dtop) =
693 let y, h = getpageyh n in
694 if conf.presentation
695 then
696 let ips = calcips h in
697 y + truncate (top*.float h -. dtop*.float ips) + ips;
698 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
701 let addnav () =
702 state.nav <- { past = getanchor () :: state.nav.past; future = []; }
705 let gotopage n top =
706 let y, h = getpageyh n in
707 let y = y + (truncate (top *. float h)) in
708 gotoxy state.x y
711 let gotopage1 n top =
712 let y = getpagey n in
713 let y = y + top in
714 gotoxy state.x y
717 let invalidate s f =
718 Glutils.redisplay := false;
719 state.layout <- [];
720 state.pdims <- [];
721 state.rects <- [];
722 state.rects1 <- [];
723 match state.geomcmds with
724 | ps, [] when emptystr ps ->
725 f ();
726 state.geomcmds <- s, [];
727 | ps, [] -> state.geomcmds <- ps, [s, f];
728 | ps, (s', _) :: rest when s' = s -> state.geomcmds <- ps, ((s, f) :: rest);
729 | ps, cmds -> state.geomcmds <- ps, ((s, f) :: cmds);
732 let flushpages () =
733 Hashtbl.iter (fun _ opaque -> wcmd "freepage %s" (~> opaque)) state.pagemap;
734 Hashtbl.clear state.pagemap;
737 let flushtiles () =
738 if not (Queue.is_empty state.tilelru)
739 then (
740 Queue.iter (fun (k, p, s) ->
741 wcmd "freetile %s" (~> p);
742 state.memused <- state.memused - s;
743 Hashtbl.remove state.tilemap k;
744 ) state.tilelru;
745 state.uioh#infochanged Memused;
746 Queue.clear state.tilelru;
748 load state.layout;
751 let stateh h =
752 let h = truncate (float h*.conf.zoom) in
753 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
754 h - d
757 let fillhelp () =
758 state.help <-
759 let sl = keystostrlist conf in
760 let rec loop accu =
761 function | [] -> accu
762 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
763 in Help.makehelp conf.urilauncher
764 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
767 let opendoc path password =
768 state.path <- path;
769 state.password <- password;
770 state.gen <- state.gen + 1;
771 state.docinfo <- [];
772 state.outlines <- [||];
774 flushpages ();
775 Ffi.setaalevel conf.aalevel;
776 Ffi.setpapercolor conf.papercolor;
777 Ffi.setdcf conf.dcf;
779 let titlepath =
780 if emptystr state.origin
781 then path
782 else state.origin
784 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
785 wcmd "open %d %d %s\000%s\000%s\000"
786 (btod conf.usedoccss) !layouth
787 path password conf.css;
788 invalidate "reqlayout"
789 (fun () ->
790 wcmd "reqlayout %d %d %d %s\000"
791 conf.angle (FMTE.to_int conf.fitmodel)
792 (stateh state.winh) state.nameddest
794 fillhelp ();
797 let reload () =
798 state.anchor <- getanchor ();
799 state.reload <- Some (state.x, state.y, now ());
800 opendoc state.path state.password;
803 let scalecolor c = let c = c *. conf.colorscale in (c, c, c);;
804 let scalecolor2 (r, g, b) =
805 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
808 let docolumns columns =
809 match columns with
810 | Csingle _ ->
811 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
812 let rec loop pageno pdimno pdim y ph pdims =
813 if pageno != state.pagecount
814 then
815 let pdimno, ((_, w, h, xoff) as pdim), pdims =
816 match pdims with
817 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
818 pdimno+1, pdim, rest
819 | _ ->
820 pdimno, pdim, pdims
822 let x = max 0 (((state.winw - w) / 2) - xoff) in
823 let y =
824 y + (if conf.presentation
825 then (if pageno = 0 then calcips h else calcips ph + calcips h)
826 else (if pageno = 0 then 0 else conf.interpagespace))
828 a.(pageno) <- (pdimno, x, y, pdim);
829 loop (pageno+1) pdimno pdim (y + h) h pdims
831 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
832 conf.columns <- Csingle a;
834 | Cmulti ((columns, coverA, coverB), _) ->
835 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
836 let rec loop pageno pdimno pdim x y rowh pdims =
837 let rec fixrow m =
838 if m = pageno then () else
839 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
840 if h < rowh
841 then (
842 let y = y + (rowh - h) / 2 in
843 a.(m) <- (pdimno, x, y, pdim);
845 fixrow (m+1)
847 if pageno = state.pagecount
848 then fixrow (((pageno - 1) / columns) * columns)
849 else
850 let pdimno, ((_, w, h, xoff) as pdim), pdims =
851 match pdims with
852 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
853 pdimno+1, pdim, rest
854 | _ -> pdimno, pdim, pdims
856 let x, y, rowh' =
857 if pageno = coverA - 1 || pageno = state.pagecount - coverB
858 then (
859 let x = (state.winw - w) / 2 in
860 let ips =
861 if conf.presentation then calcips h else conf.interpagespace in
862 x, y + ips + rowh, h
864 else (
865 if (pageno - coverA) mod columns = 0
866 then (
867 let x = max 0 (state.winw - state.w) / 2 in
868 let y =
869 if conf.presentation
870 then
871 let ips = calcips h in
872 y + (if pageno = 0 then 0 else calcips rowh + ips)
873 else
874 y + (if pageno = 0 then 0 else conf.interpagespace)
876 x, y + rowh, h
878 else x, y, max rowh h
881 let y =
882 if pageno > 1 && (pageno - coverA) mod columns = 0
883 then (
884 let y =
885 if pageno = columns && conf.presentation
886 then (
887 let ips = calcips rowh in
888 for i = 0 to pred columns
890 let (pdimno, x, y, pdim) = a.(i) in
891 a.(i) <- (pdimno, x, y+ips, pdim)
892 done;
893 y+ips;
895 else y
897 fixrow (pageno - columns);
900 else y
902 a.(pageno) <- (pdimno, x, y, pdim);
903 let x = x + w + xoff*2 + conf.interpagespace in
904 loop (pageno+1) pdimno pdim x y rowh' pdims
906 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
907 conf.columns <- Cmulti ((columns, coverA, coverB), a);
909 | Csplit (c, _) ->
910 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
911 let rec loop pageno pdimno pdim y pdims =
912 if pageno != state.pagecount
913 then
914 let pdimno, ((_, w, h, _) as pdim), pdims =
915 match pdims with
916 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
917 pdimno+1, pdim, rest
918 | _ -> pdimno, pdim, pdims
920 let cw = w / c in
921 let rec loop1 n x y =
922 if n = c then y else (
923 a.(pageno*c + n) <- (pdimno, x, y, pdim);
924 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
927 let y = loop1 0 0 y in
928 loop (pageno+1) pdimno pdim y pdims
930 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
931 conf.columns <- Csplit (c, a);
934 let represent () =
935 docolumns conf.columns;
936 state.maxy <- calcheight ();
937 if state.reprf == noreprf
938 then (
939 match state.mode with
940 | Birdseye (_, _, pageno, _, _) ->
941 let y, h = getpageyh pageno in
942 let top = (state.winh - h) / 2 in
943 gotoxy state.x (max 0 (y - top))
944 | Textentry _ | View | LinkNav _ ->
945 let y = getanchory state.anchor in
946 let y = min y (state.maxy - state.winh) in
947 gotoxy state.x y;
949 else (
950 state.reprf ();
951 state.reprf <- noreprf;
955 let reshape ?(firsttime=false) w h =
956 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
957 if not firsttime && nogeomcmds state.geomcmds
958 then state.anchor <- getanchor ();
960 state.winw <- w;
961 let w = truncate (float w *. conf.zoom) in
962 let w = max w 2 in
963 state.winh <- h;
964 setfontsize fstate.fontsize;
965 GlMat.mode `modelview;
966 GlMat.load_identity ();
968 GlMat.mode `projection;
969 GlMat.load_identity ();
970 GlMat.rotate ~x:1.0 ~angle:180.0 ();
971 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
972 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
974 let relx =
975 if conf.zoom <= 1.0
976 then 0.0
977 else float state.x /. float state.w
979 invalidate "geometry"
980 (fun () ->
981 state.w <- w;
982 if not firsttime
983 then state.x <- truncate (relx *. float w);
984 let w =
985 match conf.columns with
986 | Csingle _ -> w
987 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
988 | Csplit (c, _) -> w * c
990 wcmd "geometry %d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
994 let gctiles () =
995 let len = Queue.length state.tilelru in
996 let layout = lazy (if conf.preload
997 then preloadlayout state.x state.y state.winw state.winh
998 else state.layout) in
999 let rec loop qpos =
1000 if state.memused > conf.memlimit
1001 then (
1002 if qpos < len
1003 then
1004 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1005 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1006 let (_, pw, ph, _) = getpagedim n in
1007 if gen = state.gen
1008 && colorspace = conf.colorspace
1009 && angle = conf.angle
1010 && pagew = pw
1011 && pageh = ph
1012 && (
1013 let x = col*conf.tilew and y = row*conf.tileh in
1014 tilevisible (Lazy.force_val layout) n x y
1016 then Queue.push lruitem state.tilelru
1017 else (
1018 Ffi.freepbo p;
1019 wcmd "freetile %s" (~> p);
1020 state.memused <- state.memused - s;
1021 state.uioh#infochanged Memused;
1022 Hashtbl.remove state.tilemap k;
1024 loop (qpos+1)
1027 loop 0
1030 let onpagerect pageno f =
1031 let b =
1032 match conf.columns with
1033 | Cmulti (_, b) -> b
1034 | Csingle b -> b
1035 | Csplit (_, b) -> b
1037 if pageno >= 0 && pageno < Array.length b
1038 then
1039 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1040 f w h
1043 let gotopagexy1 pageno x y =
1044 let _,w1,h1,leftx = getpagedim pageno in
1045 let top = y /. (float h1) in
1046 let left = x /. (float w1) in
1047 let py, w, h = getpageywh pageno in
1048 let wh = state.winh in
1049 let x = left *. (float w) in
1050 let x = leftx + state.x + truncate x in
1051 let sx =
1052 if x < 0 || x >= state.winw
1053 then state.x - x
1054 else state.x
1056 let pdy = truncate (top *. float h) in
1057 let y' = py + pdy in
1058 let dy = y' - state.y in
1059 let sy =
1060 if x != state.x || not (dy > 0 && dy < wh)
1061 then (
1062 if conf.presentation
1063 then
1064 if abs (py - y') > wh
1065 then y'
1066 else py
1067 else y';
1069 else state.y
1071 if state.x != sx || state.y != sy
1072 then gotoxy sx sy
1073 else gotoxy state.x state.y;
1076 let gotopagexy pageno x y =
1077 match state.mode with
1078 | Birdseye _ -> gotopage pageno 0.0
1079 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1082 let getpassword () =
1083 let passcmd = getenvdef "LLPP_ASKPASS" conf.passcmd in
1084 if emptystr passcmd
1085 then E.s
1086 else getcmdoutput (fun s ->
1087 impmsg "error getting password: %s" s;
1088 dolog "%s" s) passcmd;
1091 let pgoto opaque pageno x y =
1092 let pdimno = getpdimno pageno in
1093 let x, y = Ffi.project opaque pageno pdimno x y in
1094 gotopagexy pageno x y;
1097 let act cmds =
1098 (* dolog "%S" cmds; *)
1099 let spl = splitatchar cmds ' ' in
1100 let scan s fmt f =
1101 try Scanf.sscanf s fmt f
1102 with exn ->
1103 dolog "error processing '%S': %s" cmds @@ exntos exn;
1104 exit 1
1106 let addoutline outline =
1107 match state.currently with
1108 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1109 | Idle -> state.currently <- Outlining [outline]
1110 | Loading _ | Tiling _ ->
1111 dolog "invalid outlining state";
1112 logcurrently state.currently
1114 match spl with
1115 | "clear", "" ->
1116 state.pdims <- [];
1117 state.uioh#infochanged Pdim;
1119 | "clearrects", "" ->
1120 state.rects <- state.rects1;
1121 postRedisplay "clearrects";
1123 | "continue", args ->
1124 let n = scan args "%u" (fun n -> n) in
1125 state.pagecount <- n;
1126 begin match state.currently with
1127 | Outlining l ->
1128 state.currently <- Idle;
1129 state.outlines <- Array.of_list (List.rev l)
1130 | Idle | Loading _ | Tiling _ -> ()
1131 end;
1133 let cur, cmds = state.geomcmds in
1134 if emptystr cur then error "empty geomcmd";
1136 begin match List.rev cmds with
1137 | [] ->
1138 state.geomcmds <- E.s, [];
1139 represent ();
1140 | (s, f) :: rest ->
1141 f ();
1142 state.geomcmds <- s, List.rev rest;
1143 end;
1144 postRedisplay "continue";
1146 | "msg", args ->
1147 showtext ' ' args
1149 | "vmsg", args ->
1150 if conf.verbose then showtext ' ' args
1152 | "emsg", args ->
1153 Buffer.add_string state.errmsgs args;
1154 Buffer.add_char state.errmsgs '\n';
1155 if not state.newerrmsgs
1156 then (
1157 state.newerrmsgs <- true;
1158 postRedisplay "error message";
1161 | "progress", args ->
1162 let progress, text =
1163 scan args "%f %n"
1164 (fun f pos -> f, String.sub args pos (String.length args - pos))
1166 state.text <- text;
1167 state.progress <- progress;
1168 postRedisplay "progress"
1170 | "firstmatch", args ->
1171 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1172 scan args "%u %d %f %f %f %f %f %f %f %f"
1173 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1174 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1176 let y = (getpagey pageno) + truncate y0 in
1177 let x =
1178 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1179 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1180 else state.x
1182 addnav ();
1183 gotoxy x y;
1184 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1185 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1187 | "match", args ->
1188 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1189 scan args "%u %d %f %f %f %f %f %f %f %f"
1190 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1191 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1193 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1194 state.rects1 <-
1195 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1197 | "page", args ->
1198 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1199 let pageopaque = ~< pageopaques in
1200 begin match state.currently with
1201 | Loading (l, gen) ->
1202 vlog "page %d took %f sec" l.pageno t;
1203 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1204 let preloadedpages =
1205 if conf.preload
1206 then preloadlayout state.x state.y state.winw state.winh
1207 else state.layout
1209 let evict () =
1210 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1211 IntSet.empty preloadedpages
1213 let evictedpages =
1214 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1215 if not (IntSet.mem pageno set)
1216 then (
1217 wcmd "freepage %s" (~> opaque);
1218 key :: accu
1220 else accu
1221 ) state.pagemap []
1223 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1225 evict ();
1226 state.currently <- Idle;
1227 if gen = state.gen
1228 then (
1229 tilepage l.pageno pageopaque state.layout;
1230 load state.layout;
1231 load preloadedpages;
1232 let visible = pagevisible state.layout l.pageno in
1233 if visible
1234 then (
1235 match state.mode with
1236 | LinkNav (Ltnotready (pageno, dir)) ->
1237 if pageno = l.pageno
1238 then (
1239 let link =
1240 let ld =
1241 if dir = 0
1242 then LDfirstvisible (l.pagex, l.pagey, dir)
1243 else if dir > 0 then LDfirst else LDlast
1245 Ffi.findlink pageopaque ld
1247 match link with
1248 | Lnotfound -> ()
1249 | Lfound n ->
1250 showlinktype (Ffi.getlink pageopaque n);
1251 state.mode <- LinkNav (Ltexact (l.pageno, n))
1253 | LinkNav (Ltgendir _)
1254 | LinkNav (Ltexact _)
1255 | View
1256 | Birdseye _
1257 | Textentry _ -> ()
1260 if visible && layoutready state.layout
1261 then postRedisplay "page";
1264 | Idle | Tiling _ | Outlining _ ->
1265 dolog "Inconsistent loading state";
1266 logcurrently state.currently;
1267 exit 1
1270 | "tile" , args ->
1271 let (x, y, opaques, size, t) =
1272 scan args "%u %u %s %u %f" (fun x y p size t -> (x, y, p, size, t))
1274 let opaque = ~< opaques in
1275 begin match state.currently with
1276 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1277 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1279 Ffi.unmappbo opaque;
1280 if tilew != conf.tilew || tileh != conf.tileh
1281 then (
1282 wcmd "freetile %s" (~> opaque);
1283 state.currently <- Idle;
1284 load state.layout;
1286 else (
1287 puttileopaque l col row gen cs angle opaque size t;
1288 state.memused <- state.memused + size;
1289 state.uioh#infochanged Memused;
1290 gctiles ();
1291 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1292 opaque, size) state.tilelru;
1294 state.currently <- Idle;
1295 if gen = state.gen
1296 && conf.colorspace = cs
1297 && conf.angle = angle
1298 && tilevisible state.layout l.pageno x y
1299 then conttiling l.pageno pageopaque;
1301 preload state.layout;
1302 if gen = state.gen
1303 && conf.colorspace = cs
1304 && conf.angle = angle
1305 && tilevisible state.layout l.pageno x y
1306 && layoutready state.layout
1307 then postRedisplay "tile nothrottle";
1310 | Idle | Loading _ | Outlining _ ->
1311 dolog "Inconsistent tiling state";
1312 logcurrently state.currently;
1313 exit 1
1316 | "pdim", args ->
1317 let (n, w, h, _) as pdim =
1318 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1320 let pdim =
1321 match conf.fitmodel with
1322 | FitWidth -> pdim
1323 | FitPage | FitProportional ->
1324 match conf.columns with
1325 | Csplit _ -> (n, w, h, 0)
1326 | Csingle _ | Cmulti _ -> pdim
1328 state.pdims <- pdim :: state.pdims;
1329 state.uioh#infochanged Pdim
1331 | "o", args ->
1332 let (l, n, t, h, pos) =
1333 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1335 let s = String.sub args pos (String.length args - pos) in
1336 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1338 | "ou", args ->
1339 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1340 let s = String.sub args pos len in
1341 let pos2 = pos + len + 1 in
1342 let uri = String.sub args pos2 (String.length args - pos2) in
1343 addoutline (s, l, Ouri uri)
1345 | "on", args ->
1346 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1347 let s = String.sub args pos (String.length args - pos) in
1348 addoutline (s, l, Onone)
1350 | "a", args ->
1351 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1352 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1354 | "info", args ->
1355 let c, v = splitatchar args '\t' in
1356 let s =
1357 if nonemptystr v
1358 then
1359 if c = "Title"
1360 then (
1361 conf.title <- v;
1362 if not !ignoredoctitlte then Wsi.settitle v;
1363 args
1365 else
1366 if let len = String.length c in
1367 len > 6 && ((String.sub c (len-4) 4) = "date")
1368 then (
1369 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1370 then
1371 let b = Buffer.create 10 in
1372 Printf.bprintf b "%s\t" c;
1373 let sub p l c =
1375 Buffer.add_substring b v p l;
1376 Buffer.add_char b c;
1377 with exn -> Buffer.add_string b @@ exntos exn
1379 sub 2 4 '/';
1380 sub 6 2 '/';
1381 sub 8 2 ' ';
1382 sub 10 2 ':';
1383 sub 12 2 ':';
1384 sub 14 2 ' ';
1385 Printf.bprintf b "[%s]" v;
1386 Buffer.contents b
1387 else args
1389 else args
1390 else args
1392 state.docinfo <- (1, s) :: state.docinfo
1394 | "infoend", "" ->
1395 state.docinfo <- List.rev state.docinfo;
1396 state.uioh#infochanged Docinfo
1398 | "pass", args ->
1399 if args = "fail"
1400 then Wsi.settitle "Wrong password";
1401 let password = getpassword () in
1402 if emptystr password
1403 then error "document is password protected"
1404 else opendoc state.path password
1406 | _ -> error "unknown cmd `%S'" cmds
1409 let onhist cb =
1410 let rc = cb.rc in
1411 let action = function
1412 | HCprev -> cbget cb ~-1
1413 | HCnext -> cbget cb 1
1414 | HCfirst -> cbget cb ~-(cb.rc)
1415 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1416 and cancel () = cb.rc <- rc
1417 in (action, cancel)
1420 let search pattern forward =
1421 match conf.columns with
1422 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1423 | Csingle _ | Cmulti _ ->
1424 if nonemptystr pattern
1425 then
1426 let pn, py =
1427 match state.layout with
1428 | [] -> 0, 0
1429 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1431 wcmd "search %d %d %d %d,%s\000"
1432 (btod conf.icase) pn py (btod forward) pattern;
1435 let intentry text key =
1436 let text =
1437 if emptystr text && key = Keys.Ascii '-'
1438 then addchar text '-'
1439 else
1440 match [@warning "-4"] key with
1441 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1442 | _ ->
1443 state.text <- "invalid key";
1444 text
1446 TEcont text
1449 let linknact f s =
1450 if nonemptystr s
1451 then
1452 let n =
1453 let l = String.length s in
1454 let rec loop pos n =
1455 if pos = l
1456 then n
1457 else
1458 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1459 loop (pos+1) (n*26 + m)
1460 in loop 0 0
1462 let rec loop n = function
1463 | [] -> ()
1464 | l :: rest ->
1465 match getopaque l.pageno with
1466 | None -> loop n rest
1467 | Some opaque ->
1468 let m = Ffi.getlinkcount opaque in
1469 if n < m
1470 then
1471 let under = Ffi.getlink opaque n in
1472 f under
1473 else loop (n-m) rest
1475 loop n state.layout;
1478 let linknentry text key = match [@warning "-4"] key with
1479 | Keys.Ascii ('a' .. 'z' as c) ->
1480 let text = addchar text c in
1481 linknact (fun under -> state.text <- undertext under) text;
1482 TEcont text
1483 | _ ->
1484 state.text <- Printf.sprintf "invalid key %s" @@ Keys.to_string key;
1485 TEcont text
1488 let textentry text key = match [@warning "-4"] key with
1489 | Keys.Ascii c -> TEcont (addchar text c)
1490 | Keys.Code c -> TEcont (text ^ toutf8 c)
1491 | _ -> TEcont text
1494 let reqlayout angle fitmodel =
1495 if nogeomcmds state.geomcmds
1496 then state.anchor <- getanchor ();
1497 conf.angle <- angle mod 360;
1498 if conf.angle != 0
1499 then (
1500 match state.mode with
1501 | LinkNav _ -> state.mode <- View
1502 | Birdseye _ | Textentry _ | View -> ()
1504 conf.fitmodel <- fitmodel;
1505 invalidate "reqlayout"
1506 (fun () -> wcmd "reqlayout %d %d %d"
1507 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh));
1510 let settrim trimmargins trimfuzz =
1511 if nogeomcmds state.geomcmds
1512 then state.anchor <- getanchor ();
1513 conf.trimmargins <- trimmargins;
1514 conf.trimfuzz <- trimfuzz;
1515 let x0, y0, x1, y1 = trimfuzz in
1516 invalidate "settrim"
1517 (fun () -> wcmd "settrim %d %d %d %d %d"
1518 (btod conf.trimmargins) x0 y0 x1 y1);
1519 flushpages ();
1522 let setzoom zoom =
1523 let zoom = max 0.0001 zoom in
1524 if zoom <> conf.zoom
1525 then (
1526 state.prevzoom <- (conf.zoom, state.x);
1527 conf.zoom <- zoom;
1528 reshape state.winw state.winh;
1529 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1533 let pivotzoom ?(vw=min state.w state.winw)
1534 ?(vh=min (state.maxy-state.y) state.winh)
1535 ?(x=vw/2) ?(y=vh/2) zoom =
1536 let w = float state.w /. zoom in
1537 let hw = w /. 2.0 in
1538 let ratio = float vh /. float vw in
1539 let hh = hw *. ratio in
1540 let x0 = float x -. hw
1541 and y0 = float y -. hh in
1542 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1543 setzoom zoom;
1546 let pivotzoom ?vw ?vh ?x ?y zoom =
1547 if nogeomcmds state.geomcmds
1548 then
1549 if zoom > 1.0
1550 then pivotzoom ?vw ?vh ?x ?y zoom
1551 else setzoom zoom
1554 let setcolumns mode columns coverA coverB =
1555 state.prevcolumns <- Some (conf.columns, conf.zoom);
1556 if columns < 0
1557 then (
1558 if isbirdseye mode
1559 then impmsg "split mode doesn't work in bird's eye"
1560 else (
1561 conf.columns <- Csplit (-columns, E.a);
1562 state.x <- 0;
1563 conf.zoom <- 1.0;
1566 else (
1567 if columns < 2
1568 then (
1569 conf.columns <- Csingle E.a;
1570 state.x <- 0;
1571 setzoom 1.0;
1573 else (
1574 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1575 conf.zoom <- 1.0;
1578 reshape state.winw state.winh;
1581 let resetmstate () =
1582 state.mstate <- Mnone;
1583 Wsi.setcursor Wsi.CURSOR_INHERIT;
1586 let enterbirdseye () =
1587 let zoom = float conf.thumbw /. float state.winw in
1588 let birdseyepageno =
1589 let cy = state.winh / 2 in
1590 let fold = function
1591 | [] -> 0
1592 | l :: rest ->
1593 let rec fold best = function
1594 | [] -> best.pageno
1595 | l :: rest ->
1596 let d = cy - (l.pagedispy + l.pagevh/2)
1597 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1598 if abs d < abs dbest
1599 then fold l rest
1600 else best.pageno
1601 in fold l rest
1603 fold state.layout
1605 state.mode <-
1606 Birdseye (
1607 { conf with zoom = conf.zoom },
1608 state.x, birdseyepageno, -1, getanchor ()
1610 resetmstate ();
1611 conf.zoom <- zoom;
1612 conf.presentation <- false;
1613 conf.interpagespace <- 10;
1614 conf.hlinks <- false;
1615 conf.fitmodel <- FitPage;
1616 state.x <- 0;
1617 conf.columns <- (
1618 match conf.beyecolumns with
1619 | Some c ->
1620 conf.zoom <- 1.0;
1621 Cmulti ((c, 0, 0), E.a)
1622 | None -> Csingle E.a
1624 if conf.verbose
1625 then state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1626 (100.0*.zoom)
1627 else state.text <- E.s;
1628 reshape state.winw state.winh;
1631 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1632 state.mode <- View;
1633 conf.zoom <- c.zoom;
1634 conf.presentation <- c.presentation;
1635 conf.interpagespace <- c.interpagespace;
1636 conf.hlinks <- c.hlinks;
1637 conf.fitmodel <- c.fitmodel;
1638 conf.beyecolumns <- (
1639 match conf.columns with
1640 | Cmulti ((c, _, _), _) -> Some c
1641 | Csingle _ -> None
1642 | Csplit _ -> error "leaving bird's eye split mode"
1644 conf.columns <- (
1645 match c.columns with
1646 | Cmulti (c, _) -> Cmulti (c, E.a)
1647 | Csingle _ -> Csingle E.a
1648 | Csplit (c, _) -> Csplit (c, E.a)
1650 if conf.verbose
1651 then state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1652 (100.0*.conf.zoom);
1653 reshape state.winw state.winh;
1654 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1655 state.x <- leftx;
1658 let togglebirdseye () =
1659 match state.mode with
1660 | Birdseye vals -> leavebirdseye vals true
1661 | View -> enterbirdseye ()
1662 | Textentry _ | LinkNav _ -> ()
1665 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1666 let pageno = max 0 (pageno - incr) in
1667 let rec loop = function
1668 | [] -> gotopage1 pageno 0
1669 | l :: _ when l.pageno = pageno ->
1670 if l.pagedispy >= 0 && l.pagey = 0
1671 then postRedisplay "upbirdseye"
1672 else gotopage1 pageno 0
1673 | _ :: rest -> loop rest
1675 loop state.layout;
1676 state.text <- E.s;
1677 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1680 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1681 let pageno = min (state.pagecount - 1) (pageno + incr) in
1682 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1683 let rec loop = function
1684 | [] ->
1685 let y, h = getpageyh pageno in
1686 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1687 gotoxy state.x (clamp dy)
1688 | l :: _ when l.pageno = pageno ->
1689 if l.pagevh != l.pageh
1690 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1691 else postRedisplay "downbirdseye"
1692 | _ :: rest -> loop rest
1694 loop state.layout;
1695 state.text <- E.s;
1698 let optentry mode _ key =
1699 let btos b = if b then "on" else "off" in
1700 match [@warning "-4"] key with
1701 | Keys.Ascii 'C' ->
1702 let ondone s =
1704 let n, a, b = multicolumns_of_string s in
1705 setcolumns mode n a b;
1706 with exn ->
1707 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1709 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1711 | Keys.Ascii 'Z' ->
1712 let ondone s =
1714 let zoom = float (int_of_string s) /. 100.0 in
1715 pivotzoom zoom
1716 with exn ->
1717 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1719 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1721 | Keys.Ascii 'i' ->
1722 conf.icase <- not conf.icase;
1723 TEdone ("case insensitive search " ^ (btos conf.icase))
1725 | Keys.Ascii 'v' ->
1726 conf.verbose <- not conf.verbose;
1727 TEdone ("verbose " ^ (btos conf.verbose))
1729 | Keys.Ascii 'd' ->
1730 conf.debug <- not conf.debug;
1731 TEdone ("debug " ^ (btos conf.debug))
1733 | Keys.Ascii 'f' ->
1734 conf.underinfo <- not conf.underinfo;
1735 TEdone ("underinfo " ^ btos conf.underinfo)
1737 | Keys.Ascii 'T' ->
1738 settrim (not conf.trimmargins) conf.trimfuzz;
1739 TEdone ("trim margins " ^ btos conf.trimmargins)
1741 | Keys.Ascii 'I' ->
1742 conf.invert <- not conf.invert;
1743 TEdone ("invert colors " ^ btos conf.invert)
1745 | Keys.Ascii 'x' ->
1746 let ondone s =
1747 cbput state.hists.sel s;
1748 conf.selcmd <- s;
1750 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1751 textentry, ondone, true)
1753 | Keys.Ascii 'M' ->
1754 if conf.pax == None
1755 then conf.pax <- Some 0.0
1756 else conf.pax <- None;
1757 TEdone ("PAX " ^ btos (conf.pax != None))
1759 | (Keys.Ascii c) ->
1760 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1761 TEstop
1763 | _ -> TEcont state.text
1766 let adderrmsg src msg =
1767 Buffer.add_string state.errmsgs msg;
1768 state.newerrmsgs <- true;
1769 postRedisplay src
1772 let adderrfmt src fmt = Format.ksprintf (fun s -> adderrmsg src s) fmt;;
1774 class outlinelistview ~zebra ~source =
1775 let settext autonarrow s =
1776 if autonarrow
1777 then
1778 let ss = source#statestr in
1779 state.text <- if emptystr ss
1780 then "[" ^ s ^ "]"
1781 else "{" ^ ss ^ "} [" ^ s ^ "]"
1782 else state.text <- s
1784 object (self)
1785 inherit listview
1786 ~zebra
1787 ~helpmode:false
1788 ~source:(source :> lvsource)
1789 ~trusted:false
1790 ~modehash:(findkeyhash conf "outline")
1791 as super
1793 val m_autonarrow = false
1795 method! key key mask =
1796 let maxrows =
1797 if emptystr state.text
1798 then fstate.maxrows
1799 else fstate.maxrows - 2
1801 let calcfirst first active =
1802 if active > first
1803 then
1804 let rows = active - first in
1805 if rows > maxrows then active - maxrows else first
1806 else active
1808 let navigate incr =
1809 let active = m_active + incr in
1810 let active = bound active 0 (source#getitemcount - 1) in
1811 let first = calcfirst m_first active in
1812 postRedisplay "outline navigate";
1813 coe {< m_active = active; m_first = first >}
1815 let navscroll first =
1816 let active =
1817 let dist = m_active - first in
1818 if dist < 0
1819 then first
1820 else (
1821 if dist < maxrows
1822 then m_active
1823 else first + maxrows
1826 postRedisplay "outline navscroll";
1827 coe {< m_first = first; m_active = active >}
1829 let ctrl = Wsi.withctrl mask in
1830 let open Keys in
1831 match Wsi.kc2kt key with
1832 | Ascii 'a' when ctrl ->
1833 let text =
1834 if m_autonarrow
1835 then (
1836 source#denarrow;
1839 else (
1840 let pattern = source#renarrow in
1841 if nonemptystr m_qsearch
1842 then (source#narrow m_qsearch; m_qsearch)
1843 else pattern
1846 settext (not m_autonarrow) text;
1847 postRedisplay "toggle auto narrowing";
1848 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1850 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1851 settext true E.s;
1852 postRedisplay "toggle auto narrowing";
1853 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1855 | Ascii 'n' when ctrl ->
1856 source#narrow m_qsearch;
1857 if not m_autonarrow
1858 then source#add_narrow_pattern m_qsearch;
1859 postRedisplay "outline ctrl-n";
1860 coe {< m_first = 0; m_active = 0 >}
1862 | Ascii 'S' when ctrl ->
1863 let active = source#calcactive (getanchor ()) in
1864 let first = firstof m_first active in
1865 postRedisplay "outline ctrl-s";
1866 coe {< m_first = first; m_active = active >}
1868 | Ascii 'u' when ctrl ->
1869 postRedisplay "outline ctrl-u";
1870 if m_autonarrow && nonemptystr m_qsearch
1871 then (
1872 ignore (source#renarrow);
1873 settext m_autonarrow E.s;
1874 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1876 else (
1877 source#del_narrow_pattern;
1878 let pattern = source#renarrow in
1879 let text =
1880 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1882 settext m_autonarrow text;
1883 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1886 | Ascii 'l' when ctrl ->
1887 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1888 postRedisplay "outline ctrl-l";
1889 coe {< m_first = first >}
1891 | Ascii '\t' when m_autonarrow ->
1892 if nonemptystr m_qsearch
1893 then (
1894 postRedisplay "outline list view tab";
1895 source#add_narrow_pattern m_qsearch;
1896 settext true E.s;
1897 coe {< m_qsearch = E.s >}
1899 else coe self
1901 | Escape when m_autonarrow ->
1902 if nonemptystr m_qsearch
1903 then source#add_narrow_pattern m_qsearch;
1904 super#key key mask
1906 | Enter when m_autonarrow ->
1907 if nonemptystr m_qsearch
1908 then source#add_narrow_pattern m_qsearch;
1909 super#key key mask
1911 | (Ascii _ | Code _) when m_autonarrow ->
1912 let pattern = m_qsearch ^ toutf8 key in
1913 postRedisplay "outlinelistview autonarrow add";
1914 source#narrow pattern;
1915 settext true pattern;
1916 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1918 | Backspace when m_autonarrow ->
1919 if emptystr m_qsearch
1920 then coe self
1921 else
1922 let pattern = withoutlastutf8 m_qsearch in
1923 postRedisplay "outlinelistview autonarrow backspace";
1924 ignore (source#renarrow);
1925 source#narrow pattern;
1926 settext true pattern;
1927 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1929 | Up when ctrl -> navscroll (max 0 (m_first - 1))
1931 | Down when ctrl ->
1932 navscroll (min (source#getitemcount - 1) (m_first + 1))
1934 | Up -> navigate ~-1
1935 | Down -> navigate 1
1936 | Prior -> navigate ~-(fstate.maxrows)
1937 | Next -> navigate fstate.maxrows
1939 | Right ->
1940 let o =
1941 if ctrl
1942 then (
1943 postRedisplay "outline ctrl right";
1944 {< m_pan = m_pan + 1 >}
1946 else (
1947 if Wsi.withshift mask
1948 then self#nextcurlevel 1
1949 else self#updownlevel 1
1952 coe o
1954 | Left ->
1955 let o =
1956 if ctrl
1957 then (
1958 postRedisplay "outline ctrl left";
1959 {< m_pan = m_pan - 1 >}
1961 else (
1962 if Wsi.withshift mask
1963 then self#nextcurlevel ~-1
1964 else self#updownlevel ~-1
1967 coe o
1969 | Home ->
1970 postRedisplay "outline home";
1971 coe {< m_first = 0; m_active = 0 >}
1973 | End ->
1974 let active = source#getitemcount - 1 in
1975 let first = max 0 (active - fstate.maxrows) in
1976 postRedisplay "outline end";
1977 coe {< m_active = active; m_first = first >}
1979 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
1980 super#key key mask
1981 end;;
1983 let genhistoutlines () =
1984 Config.gethist ()
1985 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
1986 compare c2.lastvisit c1.lastvisit)
1987 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
1988 let path = if nonemptystr origin then origin else path in
1989 let base = mbtoutf8 @@ Filename.basename path in
1990 (base ^ "\000" ^ c.title, 1, Ohistory hist)
1994 let gotohist (path, c, bookmarks, x, anchor, origin) =
1995 Config.save leavebirdseye;
1996 state.anchor <- anchor;
1997 state.bookmarks <- bookmarks;
1998 state.origin <- origin;
1999 state.x <- x;
2000 setconf conf c;
2001 Ffi.setdcf conf.dcf;
2002 let x0, y0, x1, y1 = conf.trimfuzz in
2003 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2004 Wsi.reshape c.cwinw c.cwinh;
2005 opendoc path origin;
2006 setzoom c.zoom;
2009 let setcheckers enabled =
2010 match !checkerstexid with
2011 | None -> if enabled then checkerstexid := Some (makecheckers ())
2012 | Some id ->
2013 if not enabled
2014 then (
2015 GlTex.delete_texture id;
2016 checkerstexid := None;
2020 let describe_layout layout =
2021 let d =
2022 match layout with
2023 | [] -> "Page 0"
2024 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2025 | l :: rest ->
2026 let rangestr a b =
2027 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2028 else Printf.sprintf "%d%s%d" (a.pageno+1)
2029 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
2030 (b.pageno+1)
2032 let rec fold s la lb = function
2033 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2034 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2035 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2037 fold "Pages" l l rest
2039 let percent =
2040 let maxy = maxy () in
2041 if maxy <= 0
2042 then 100.
2043 else 100. *. (float state.y /. float maxy)
2045 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2048 let setpresentationmode v =
2049 let n = page_of_y state.y in
2050 state.anchor <- (n, 0.0, 1.0);
2051 conf.presentation <- v;
2052 if conf.fitmodel = FitPage
2053 then reqlayout conf.angle conf.fitmodel;
2054 represent ();
2057 let enterinfomode =
2058 let btos b = if b then Utf8syms.radical else E.s in
2059 let showextended = ref false in
2060 let showcolors = ref false in
2061 let leave mode _ = state.mode <- mode in
2062 let src =
2063 (object
2064 val mutable m_l = []
2065 val mutable m_a = E.a
2066 val mutable m_prev_uioh = nouioh
2067 val mutable m_prev_mode = View
2069 inherit lvsourcebase
2071 method reset prev_mode prev_uioh =
2072 m_a <- Array.of_list (List.rev m_l);
2073 m_l <- [];
2074 m_prev_mode <- prev_mode;
2075 m_prev_uioh <- prev_uioh;
2077 method int name get set =
2078 m_l <-
2079 (name, `int get, 1,
2080 Action (
2081 fun u ->
2082 let ondone s =
2083 try set (int_of_string s)
2084 with exn ->
2085 state.text <- Printf.sprintf "bad integer `%s': %s"
2086 s @@ exntos exn
2088 state.text <- E.s;
2089 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2090 state.mode <- Textentry (te, leave m_prev_mode);
2092 )) :: m_l
2094 method int_with_suffix name get set =
2095 m_l <-
2096 (name, `intws get, 1,
2097 Action (
2098 fun u ->
2099 let ondone s =
2100 try set (int_of_string_with_suffix s)
2101 with exn ->
2102 state.text <- Printf.sprintf "bad integer `%s': %s"
2103 s @@ exntos exn
2105 state.text <- E.s;
2106 let te =
2107 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2109 state.mode <- Textentry (te, leave m_prev_mode);
2111 )) :: m_l
2113 method bool ?(offset=1) ?(btos=btos) name get set =
2114 m_l <-
2115 (name, `bool (btos, get), offset, Action (
2116 fun u ->
2117 let v = get () in
2118 set (not v);
2120 )) :: m_l
2122 method color name get set =
2123 m_l <-
2124 (name, `color get, 1,
2125 Action (
2126 fun u ->
2127 let invalid = (nan, nan, nan) in
2128 let ondone s =
2129 let c =
2130 try color_of_string s
2131 with exn ->
2132 state.text <- Printf.sprintf "bad color `%s': %s"
2133 s @@ exntos exn;
2134 invalid
2136 if c <> invalid
2137 then set c;
2139 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2140 state.text <- color_to_string (get ());
2141 state.mode <- Textentry (te, leave m_prev_mode);
2143 )) :: m_l
2145 method string name get set =
2146 m_l <-
2147 (name, `string get, 1,
2148 Action (
2149 fun u ->
2150 let ondone s = set s in
2151 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2152 state.mode <- Textentry (te, leave m_prev_mode);
2154 )) :: m_l
2156 method colorspace name get set =
2157 m_l <-
2158 (name, `string get, 1,
2159 Action (
2160 fun _ ->
2161 let source =
2162 (object
2163 inherit lvsourcebase
2165 initializer
2166 m_active <- CSTE.to_int conf.colorspace;
2167 m_first <- 0;
2169 method getitemcount =
2170 Array.length CSTE.names
2171 method getitem n =
2172 (CSTE.names.(n), 0)
2173 method exit ~uioh ~cancel ~active ~first ~pan =
2174 ignore (uioh, first, pan);
2175 if not cancel then set active;
2176 None
2177 method hasaction _ = true
2178 end)
2180 state.text <- E.s;
2181 let modehash = findkeyhash conf "info" in
2182 coe (new listview ~zebra:false ~helpmode:false
2183 ~source ~trusted:true ~modehash)
2184 )) :: m_l
2186 method paxmark name get set =
2187 m_l <-
2188 (name, `string get, 1,
2189 Action (
2190 fun _ ->
2191 let source =
2192 (object
2193 inherit lvsourcebase
2195 initializer
2196 m_active <- MTE.to_int conf.paxmark;
2197 m_first <- 0;
2199 method getitemcount = Array.length MTE.names
2200 method getitem n = (MTE.names.(n), 0)
2201 method exit ~uioh ~cancel ~active ~first ~pan =
2202 ignore (uioh, first, pan);
2203 if not cancel then set active;
2204 None
2205 method hasaction _ = true
2206 end)
2208 state.text <- E.s;
2209 let modehash = findkeyhash conf "info" in
2210 coe (new listview ~zebra:false ~helpmode:false
2211 ~source ~trusted:true ~modehash)
2212 )) :: m_l
2214 method fitmodel name get set =
2215 m_l <-
2216 (name, `string get, 1,
2217 Action (
2218 fun _ ->
2219 let source =
2220 (object
2221 inherit lvsourcebase
2223 initializer
2224 m_active <- FMTE.to_int conf.fitmodel;
2225 m_first <- 0;
2227 method getitemcount = Array.length FMTE.names
2228 method getitem n = (FMTE.names.(n), 0)
2229 method exit ~uioh ~cancel ~active ~first ~pan =
2230 ignore (uioh, first, pan);
2231 if not cancel then set active;
2232 None
2233 method hasaction _ = true
2234 end)
2236 state.text <- E.s;
2237 let modehash = findkeyhash conf "info" in
2238 coe (new listview ~zebra:false ~helpmode:false
2239 ~source ~trusted:true ~modehash)
2240 )) :: m_l
2242 method caption s offset =
2243 m_l <- (s, `empty, offset, Noaction) :: m_l
2245 method caption2 s f offset =
2246 m_l <- (s, `string f, offset, Noaction) :: m_l
2248 method getitemcount = Array.length m_a
2250 method getitem n =
2251 let tostr = function
2252 | `int f -> string_of_int (f ())
2253 | `intws f -> string_with_suffix_of_int (f ())
2254 | `string f -> f ()
2255 | `color f -> color_to_string (f ())
2256 | `bool (btos, f) -> btos (f ())
2257 | `empty -> E.s
2259 let name, t, offset, _ = m_a.(n) in
2260 ((let s = tostr t in
2261 if nonemptystr s
2262 then Printf.sprintf "%s\t%s" name s
2263 else name),
2264 offset)
2266 method exit ~uioh ~cancel ~active ~first ~pan =
2267 let uiohopt =
2268 if not cancel
2269 then (
2270 let uioh =
2271 match m_a.(active) with
2272 | _, _, _, Action f -> f uioh
2273 | _, _, _, Noaction -> uioh
2275 Some uioh
2277 else None
2279 m_active <- active;
2280 m_first <- first;
2281 m_pan <- pan;
2282 uiohopt
2284 method hasaction n =
2285 match m_a.(n) with
2286 | _, _, _, Action _ -> true
2287 | _, _, _, Noaction -> false
2289 initializer m_active <- 1
2290 end)
2292 let rec fillsrc prevmode prevuioh =
2293 let sep () = src#caption E.s 0 in
2294 let colorp name get set =
2295 src#string name
2296 (fun () -> color_to_string (get ()))
2297 (fun v ->
2298 try set @@ color_of_string v
2299 with exn ->
2300 state.text <-
2301 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2304 let rgba name get set =
2305 src#string name
2306 (fun () -> get () |> rgba_to_string)
2307 (fun v ->
2308 try set @@ rgba_of_string v
2309 with exn ->
2310 state.text <-
2311 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2314 let oldmode = state.mode in
2315 let birdseye = isbirdseye state.mode in
2317 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2319 src#bool "presentation mode"
2320 (fun () -> conf.presentation)
2321 (fun v -> setpresentationmode v);
2323 src#bool "ignore case in searches"
2324 (fun () -> conf.icase)
2325 (fun v -> conf.icase <- v);
2327 src#bool "preload"
2328 (fun () -> conf.preload)
2329 (fun v -> conf.preload <- v);
2331 src#bool "highlight links"
2332 (fun () -> conf.hlinks)
2333 (fun v -> conf.hlinks <- v);
2335 src#bool "under info"
2336 (fun () -> conf.underinfo)
2337 (fun v -> conf.underinfo <- v);
2339 src#fitmodel "fit model"
2340 (fun () -> FMTE.to_string conf.fitmodel)
2341 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2343 src#bool "trim margins"
2344 (fun () -> conf.trimmargins)
2345 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2347 sep ();
2348 src#int "inter-page space"
2349 (fun () -> conf.interpagespace)
2350 (fun n ->
2351 conf.interpagespace <- n;
2352 docolumns conf.columns;
2353 let pageno, py =
2354 match state.layout with
2355 | [] -> 0, 0
2356 | l :: _ -> l.pageno, l.pagey
2358 state.maxy <- calcheight ();
2359 let y = getpagey pageno in
2360 gotoxy state.x (y + py)
2363 src#int "page bias"
2364 (fun () -> conf.pagebias)
2365 (fun v -> conf.pagebias <- v);
2367 src#int "scroll step"
2368 (fun () -> conf.scrollstep)
2369 (fun n -> conf.scrollstep <- n);
2371 src#int "horizontal scroll step"
2372 (fun () -> conf.hscrollstep)
2373 (fun v -> conf.hscrollstep <- v);
2375 src#int "auto scroll step"
2376 (fun () ->
2377 match state.autoscroll with
2378 | Some step -> step
2379 | _ -> conf.autoscrollstep)
2380 (fun n ->
2381 let n = boundastep state.winh n in
2382 if state.autoscroll <> None
2383 then state.autoscroll <- Some n;
2384 conf.autoscrollstep <- n);
2386 src#int "zoom"
2387 (fun () -> truncate (conf.zoom *. 100.))
2388 (fun v -> pivotzoom ((float v) /. 100.));
2390 src#int "rotation"
2391 (fun () -> conf.angle)
2392 (fun v -> reqlayout v conf.fitmodel);
2394 src#int "scroll bar width"
2395 (fun () -> conf.scrollbw)
2396 (fun v ->
2397 conf.scrollbw <- v;
2398 reshape state.winw state.winh;
2401 src#int "scroll handle height"
2402 (fun () -> conf.scrollh)
2403 (fun v -> conf.scrollh <- v;);
2405 src#int "thumbnail width"
2406 (fun () -> conf.thumbw)
2407 (fun v ->
2408 conf.thumbw <- min 4096 v;
2409 match oldmode with
2410 | Birdseye beye ->
2411 leavebirdseye beye false;
2412 enterbirdseye ()
2413 | Textentry _
2414 | View
2415 | LinkNav _ -> ()
2418 let mode = state.mode in
2419 src#string "columns"
2420 (fun () ->
2421 match conf.columns with
2422 | Csingle _ -> "1"
2423 | Cmulti (multi, _) -> multicolumns_to_string multi
2424 | Csplit (count, _) -> "-" ^ string_of_int count
2426 (fun v ->
2427 let n, a, b = multicolumns_of_string v in
2428 setcolumns mode n a b);
2430 sep ();
2431 src#caption "Pixmap cache" 0;
2432 src#int_with_suffix "size (advisory)"
2433 (fun () -> conf.memlimit)
2434 (fun v -> conf.memlimit <- v);
2436 src#caption2 "used"
2437 (fun () ->
2438 Printf.sprintf "%s bytes, %d tiles"
2439 (string_with_suffix_of_int state.memused)
2440 (Hashtbl.length state.tilemap)) 1;
2442 sep ();
2443 src#caption "Layout" 0;
2444 src#caption2 "Dimension"
2445 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2446 state.winw state.winh
2447 state.w state.maxy)
2449 if conf.debug
2450 then src#caption2 "Position" (fun () ->
2451 Printf.sprintf "%dx%d" state.x state.y
2453 else src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2455 sep ();
2456 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2457 "Save these parameters as global defaults at exit"
2458 (fun () -> !Config.bedefault)
2459 (fun v -> Config.bedefault := v);
2461 sep ();
2462 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2463 src#bool ~offset:0 ~btos "Extended parameters"
2464 (fun () -> !showextended)
2465 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2466 if !showextended
2467 then (
2468 src#bool "checkers"
2469 (fun () -> conf.checkers)
2470 (fun v -> conf.checkers <- v; setcheckers v);
2471 src#bool "update cursor"
2472 (fun () -> conf.updatecurs)
2473 (fun v -> conf.updatecurs <- v);
2474 src#bool "scroll-bar on the left"
2475 (fun () -> conf.leftscroll)
2476 (fun v -> conf.leftscroll <- v);
2477 src#bool "verbose"
2478 (fun () -> conf.verbose)
2479 (fun v -> conf.verbose <- v);
2480 src#bool "invert colors"
2481 (fun () -> conf.invert)
2482 (fun v -> conf.invert <- v);
2483 src#bool "max fit"
2484 (fun () -> conf.maxhfit)
2485 (fun v -> conf.maxhfit <- v);
2486 src#bool "pax mode"
2487 (fun () -> conf.pax != None)
2488 (fun v ->
2489 if v
2490 then conf.pax <- Some (now ())
2491 else conf.pax <- None);
2492 src#string "uri launcher"
2493 (fun () -> conf.urilauncher)
2494 (fun v -> conf.urilauncher <- v);
2495 src#string "path launcher"
2496 (fun () -> conf.pathlauncher)
2497 (fun v -> conf.pathlauncher <- v);
2498 src#string "tile size"
2499 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2500 (fun v ->
2502 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2503 conf.tilew <- max 64 w;
2504 conf.tileh <- max 64 h;
2505 flushtiles ();
2506 with exn ->
2507 state.text <- Printf.sprintf "bad tile size `%s': %s"
2508 v @@ exntos exn
2510 src#int "texture count"
2511 (fun () -> conf.texcount)
2512 (fun v ->
2513 if Ffi.realloctexts v
2514 then conf.texcount <- v
2515 else impmsg "failed to set texture count please retry later"
2517 src#int "slice height"
2518 (fun () -> conf.sliceheight)
2519 (fun v ->
2520 conf.sliceheight <- v;
2521 wcmd "sliceh %d" conf.sliceheight;
2523 src#int "anti-aliasing level"
2524 (fun () -> conf.aalevel)
2525 (fun v ->
2526 conf.aalevel <- bound v 0 8;
2527 state.anchor <- getanchor ();
2528 opendoc state.path state.password;
2530 src#string "page scroll scaling factor"
2531 (fun () -> string_of_float conf.pgscale)
2532 (fun v ->
2533 try conf.pgscale <- float_of_string v
2534 with exn ->
2535 state.text <-
2536 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2537 @@ exntos exn
2539 src#int "ui font size"
2540 (fun () -> fstate.fontsize)
2541 (fun v -> setfontsize (bound v 5 100));
2542 src#int "hint font size"
2543 (fun () -> conf.hfsize)
2544 (fun v -> conf.hfsize <- bound v 5 100);
2545 src#string "trim fuzz"
2546 (fun () -> irect_to_string conf.trimfuzz)
2547 (fun v ->
2549 conf.trimfuzz <- irect_of_string v;
2550 if conf.trimmargins
2551 then settrim true conf.trimfuzz;
2552 with exn ->
2553 state.text <- Printf.sprintf "bad irect `%s': %s" v
2554 @@ exntos exn
2556 src#string "selection command"
2557 (fun () -> conf.selcmd)
2558 (fun v -> conf.selcmd <- v);
2559 src#string "synctex command"
2560 (fun () -> conf.stcmd)
2561 (fun v -> conf.stcmd <- v);
2562 src#string "pax command"
2563 (fun () -> conf.paxcmd)
2564 (fun v -> conf.paxcmd <- v);
2565 src#string "ask password command"
2566 (fun () -> conf.passcmd)
2567 (fun v -> conf.passcmd <- v);
2568 src#string "save path command"
2569 (fun () -> conf.savecmd)
2570 (fun v -> conf.savecmd <- v);
2571 src#colorspace "color space"
2572 (fun () -> CSTE.to_string conf.colorspace)
2573 (fun v ->
2574 conf.colorspace <- CSTE.of_int v;
2575 wcmd "cs %d" v;
2576 load state.layout;
2578 src#paxmark "pax mark method"
2579 (fun () -> MTE.to_string conf.paxmark)
2580 (fun v -> conf.paxmark <- MTE.of_int v);
2581 if Ffi.bousable ()
2582 then
2583 src#bool "use PBO"
2584 (fun () -> conf.usepbo)
2585 (fun v -> conf.usepbo <- v);
2586 src#bool "mouse wheel scrolls pages"
2587 (fun () -> conf.wheelbypage)
2588 (fun v -> conf.wheelbypage <- v);
2589 src#bool "open remote links in a new instance"
2590 (fun () -> conf.riani)
2591 (fun v -> conf.riani <- v);
2592 src#bool "edit annotations inline"
2593 (fun () -> conf.annotinline)
2594 (fun v -> conf.annotinline <- v);
2595 src#bool "coarse positioning in presentation mode"
2596 (fun () -> conf.coarseprespos)
2597 (fun v -> conf.coarseprespos <- v);
2598 src#bool "use document CSS"
2599 (fun () -> conf.usedoccss)
2600 (fun v ->
2601 conf.usedoccss <- v;
2602 state.anchor <- getanchor ();
2603 opendoc state.path state.password;
2605 src#bool ~btos "colors"
2606 (fun () -> !showcolors)
2607 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2608 if !showcolors
2609 then (
2610 colorp " background"
2611 (fun () -> conf.bgcolor)
2612 (fun v -> conf.bgcolor <- v);
2614 rgba " paper color"
2615 (fun () -> conf.papercolor)
2616 (fun v ->
2617 conf.papercolor <- v;
2618 Ffi.setpapercolor conf.papercolor;
2619 flushtiles ();
2621 rgba " scrollbar"
2622 (fun () -> conf.sbarcolor)
2623 (fun v -> conf.sbarcolor <- v);
2624 rgba " scrollbar handle"
2625 (fun () -> conf.sbarhndlcolor)
2626 (fun v -> conf.sbarhndlcolor <- v);
2627 rgba " texture color"
2628 (fun () -> conf.texturecolor)
2629 (fun v ->
2630 GlTex.env (`color v);
2631 conf.texturecolor <- v;
2636 sep ();
2637 src#caption "Document" 0;
2638 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2639 src#caption2 "Pages" (fun () -> string_of_int state.pagecount) 1;
2640 src#caption2 "Dimensions"
2641 (fun () -> string_of_int (List.length state.pdims)) 1;
2642 if nonemptystr conf.css
2643 then src#caption2 "CSS" (fun () -> conf.css) 1;
2644 if conf.trimmargins
2645 then (
2646 sep ();
2647 src#caption "Trimmed margins" 0;
2648 src#caption2 "Dimensions"
2649 (fun () -> string_of_int (List.length state.pdims)) 1;
2652 sep ();
2653 src#caption "OpenGL" 0;
2654 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2655 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2657 sep ();
2658 src#caption "Location" 0;
2659 if nonemptystr state.origin
2660 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2661 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2662 if nonemptystr conf.dcf
2663 then src#caption ("DCF\t" ^ mbtoutf8 conf.dcf) 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 if emptystr path
2965 then adderrfmt "gotoremote/getpath" "failed getpath for %S\n" filename
2966 else
2967 let dospawn lcmd =
2968 if conf.riani
2969 then
2970 let cmd = Lazy.force_val lcmd in
2971 match spawn cmd with
2972 | _pid -> ()
2973 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
2974 else
2975 let anchor = getanchor () in
2976 let ranchor = state.path, state.password, anchor, state.origin in
2977 state.origin <- E.s;
2978 state.ranchors <- ranchor :: state.ranchors;
2979 opendoc path E.s;
2981 if substratis spec 0 "page="
2982 then
2983 match Scanf.sscanf spec "page=%d" (fun n -> n) with
2984 | pageno ->
2985 state.anchor <- (pageno, 0.0, 0.0);
2986 dospawn @@ lazy (Printf.sprintf "%s -page %d %S"
2987 !selfexec pageno path);
2988 | exception exn ->
2989 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
2990 else (
2991 state.nameddest <- dest;
2992 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
2996 let gotounder = function
2997 | Ulinkuri s when Ffi.isexternallink s ->
2998 if substratis s 0 "file://"
2999 then gotoremote @@ String.sub s 7 (String.length s - 7)
3000 else Help.gotouri conf.urilauncher s
3001 | Ulinkuri s ->
3002 let pageno, x, y = Ffi.uritolocation s in
3003 addnav ();
3004 gotopagexy pageno x y
3005 | Utext _ | Unone -> ()
3006 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
3009 let gotooutline (_, _, kind) =
3010 match kind with
3011 | Onone -> ()
3012 | Oanchor ((pageno, y, _) as anchor) ->
3013 addnav ();
3014 gotoxy state.x @@
3015 getanchory (if conf.presentation then (pageno, y, 1.0) else anchor)
3016 | Ouri uri -> gotounder (Ulinkuri uri)
3017 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
3018 | Oremote (remote, pageno) ->
3019 error "gotounder (Uremote (%S,%d) )" remote pageno
3020 | Ohistory hist -> gotohist hist
3021 | Oremotedest (path, dest) ->
3022 error "gotounder (Uremotedest (%S, %S))" path dest
3025 class outlinesoucebase fetchoutlines = object (self)
3026 inherit lvsourcebase
3027 val mutable m_items = E.a
3028 val mutable m_minfo = E.a
3029 val mutable m_orig_items = E.a
3030 val mutable m_orig_minfo = E.a
3031 val mutable m_narrow_patterns = []
3032 val mutable m_gen = -1
3034 method getitemcount = Array.length m_items
3036 method getitem n =
3037 let s, n, _ = m_items.(n) in
3038 (s, n+0)
3040 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3041 ignore (uioh, first);
3042 let items, minfo =
3043 if m_narrow_patterns = []
3044 then m_orig_items, m_orig_minfo
3045 else m_items, m_minfo
3047 m_pan <- pan;
3048 if not cancel
3049 then (
3050 m_items <- items;
3051 m_minfo <- minfo;
3052 gotooutline m_items.(active);
3054 else (
3055 m_items <- items;
3056 m_minfo <- minfo;
3058 None
3060 method hasaction (_:int) = true
3062 method greetmsg =
3063 if Array.length m_items != Array.length m_orig_items
3064 then
3065 let s =
3066 match m_narrow_patterns with
3067 | one :: [] -> one
3068 | many -> String.concat Utf8syms.ellipsis (List.rev many)
3070 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3071 else E.s
3073 method statestr =
3074 match m_narrow_patterns with
3075 | [] -> E.s
3076 | one :: [] -> one
3077 | head :: _ -> Utf8syms.ellipsis ^ head
3079 method narrow pattern =
3080 match Str.regexp_case_fold pattern with
3081 | exception _ -> ()
3082 | re ->
3083 let rec loop accu minfo n =
3084 if n = -1
3085 then (
3086 m_items <- Array.of_list accu;
3087 m_minfo <- Array.of_list minfo;
3089 else
3090 let (s, _, _) as o = m_items.(n) in
3091 let accu, minfo =
3092 match Str.search_forward re s 0 with
3093 | exception Not_found -> accu, minfo
3094 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3096 loop accu minfo (n-1)
3098 loop [] [] (Array.length m_items - 1)
3100 method! getminfo = m_minfo
3102 method denarrow =
3103 m_orig_items <- fetchoutlines ();
3104 m_minfo <- m_orig_minfo;
3105 m_items <- m_orig_items
3107 method add_narrow_pattern pattern =
3108 m_narrow_patterns <- pattern :: m_narrow_patterns
3110 method del_narrow_pattern =
3111 match m_narrow_patterns with
3112 | _ :: rest -> m_narrow_patterns <- rest
3113 | [] -> ()
3115 method renarrow =
3116 self#denarrow;
3117 match m_narrow_patterns with
3118 | pattern :: [] -> self#narrow pattern; pattern
3119 | list ->
3120 List.fold_left (fun accu pattern ->
3121 self#narrow pattern;
3122 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
3124 method calcactive (_:anchor) = 0
3126 method reset anchor items =
3127 if state.gen != m_gen
3128 then (
3129 m_orig_items <- items;
3130 m_items <- items;
3131 m_narrow_patterns <- [];
3132 m_minfo <- E.a;
3133 m_orig_minfo <- E.a;
3134 m_gen <- state.gen;
3136 else (
3137 if items != m_orig_items
3138 then (
3139 m_orig_items <- items;
3140 if m_narrow_patterns == []
3141 then m_items <- items;
3144 let active = self#calcactive anchor in
3145 m_active <- active;
3146 m_first <- firstof m_first active
3150 let outlinesource fetchoutlines =
3151 (object
3152 inherit outlinesoucebase fetchoutlines
3153 method! calcactive anchor =
3154 let rely = getanchory anchor in
3155 let rec loop n best bestd =
3156 if n = Array.length m_items
3157 then best
3158 else
3159 let _, _, kind = m_items.(n) in
3160 match kind with
3161 | Oanchor anchor ->
3162 let orely = getanchory anchor in
3163 let d = abs (orely - rely) in
3164 if d < bestd
3165 then loop (n+1) n d
3166 else loop (n+1) best bestd
3167 | Onone | Oremote _ | Olaunch _
3168 | Oremotedest _ | Ouri _ | Ohistory _ ->
3169 loop (n+1) best bestd
3171 loop 0 ~-1 max_int
3172 end)
3175 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3176 let fetchoutlines sourcetype () =
3177 match sourcetype with
3178 | `bookmarks -> Array.of_list state.bookmarks
3179 | `outlines -> state.outlines
3180 | `history -> genhistoutlines () |> Array.of_list
3182 let so = outlinesource (fetchoutlines `outlines) in
3183 let sb = outlinesource (fetchoutlines `bookmarks) in
3184 let sh = outlinesource (fetchoutlines `history) in
3185 let mkselector sourcetype source =
3186 (fun errmsg ->
3187 let outlines = fetchoutlines sourcetype () in
3188 if Array.length outlines = 0
3189 then showtext ' ' errmsg
3190 else (
3191 resetmstate ();
3192 Wsi.setcursor Wsi.CURSOR_INHERIT;
3193 let anchor = getanchor () in
3194 source#reset anchor outlines;
3195 state.text <- source#greetmsg;
3196 state.uioh <-
3197 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3198 postRedisplay "enter selector";
3202 let mkenter sourcetype errmsg s = fun () -> mkselector sourcetype s errmsg in
3203 ( mkenter `outlines "document has no outline" so
3204 , mkenter `bookmarks "document has no bookmarks (yet)" sb
3205 , mkenter `history "history is empty" sh )
3209 let addbookmark title a =
3210 let b = List.filter (fun (title', _, _) -> title <> title') state.bookmarks in
3211 state.bookmarks <- (title, 0, Oanchor a) :: b;;
3213 let quickbookmark ?title () =
3214 match state.layout with
3215 | [] -> ()
3216 | l :: _ ->
3217 let title =
3218 match title with
3219 | None ->
3220 Unix.(
3221 let tm = localtime (now ()) in
3222 Printf.sprintf
3223 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3224 (l.pageno+1)
3225 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3227 | Some title -> title
3229 addbookmark title (getanchor1 l)
3232 let setautoscrollspeed step goingdown =
3233 let incr = max 1 ((abs step) / 2) in
3234 let incr = if goingdown then incr else -incr in
3235 let astep = boundastep state.winh (step + incr) in
3236 state.autoscroll <- Some astep;
3239 let canpan () =
3240 match conf.columns with
3241 | Csplit _ -> true
3242 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3245 let panbound x = bound x (-state.w) state.winw;;
3247 let existsinrow pageno (columns, coverA, coverB) p =
3248 let last = ((pageno - coverA) mod columns) + columns in
3249 let rec any = function
3250 | [] -> false
3251 | l :: rest ->
3252 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3253 then p l
3254 else (
3255 if not (p l)
3256 then (if l.pageno = last then false else any rest)
3257 else true
3260 any state.layout
3263 let nextpage () =
3264 match state.layout with
3265 | [] ->
3266 let pageno = page_of_y state.y in
3267 gotoxy state.x (getpagey (pageno+1))
3268 | l :: rest ->
3269 match conf.columns with
3270 | Csingle _ ->
3271 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3272 then
3273 let y = clamp (pgscale state.winh) in
3274 gotoxy state.x y
3275 else
3276 let pageno = min (l.pageno+1) (state.pagecount-1) in
3277 gotoxy state.x (getpagey pageno)
3278 | Cmulti ((c, _, _) as cl, _) ->
3279 if conf.presentation
3280 && (existsinrow l.pageno cl
3281 (fun l -> l.pageh > l.pagey + l.pagevh))
3282 then
3283 let y = clamp (pgscale state.winh) in
3284 gotoxy state.x y
3285 else
3286 let pageno = min (l.pageno+c) (state.pagecount-1) in
3287 gotoxy state.x (getpagey pageno)
3288 | Csplit (n, _) ->
3289 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3290 then
3291 let pagey, pageh = getpageyh l.pageno in
3292 let pagey = pagey + pageh * l.pagecol in
3293 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3294 gotoxy state.x (pagey + pageh + ips)
3297 let prevpage () =
3298 match state.layout with
3299 | [] ->
3300 let pageno = page_of_y state.y in
3301 gotoxy state.x (getpagey (pageno-1))
3302 | l :: _ ->
3303 match conf.columns with
3304 | Csingle _ ->
3305 if conf.presentation && l.pagey != 0
3306 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3307 else
3308 let pageno = max 0 (l.pageno-1) in
3309 gotoxy state.x (getpagey pageno)
3310 | Cmulti ((c, _, coverB) as cl, _) ->
3311 if conf.presentation &&
3312 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3313 then gotoxy state.x (clamp (pgscale ~-(state.winh)))
3314 else
3315 let decr =
3316 if l.pageno = state.pagecount - coverB
3317 then 1
3318 else c
3320 let pageno = max 0 (l.pageno-decr) in
3321 gotoxy state.x (getpagey pageno)
3322 | Csplit (n, _) ->
3323 let y =
3324 if l.pagecol = 0
3325 then
3326 if l.pageno = 0
3327 then l.pagey
3328 else
3329 let pageno = max 0 (l.pageno-1) in
3330 let pagey, pageh = getpageyh pageno in
3331 pagey + (n-1)*pageh
3332 else
3333 let pagey, pageh = getpageyh l.pageno in
3334 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3336 gotoxy state.x y
3339 let save () =
3340 if emptystr conf.savecmd
3341 then adderrmsg "savepath-command is empty"
3342 "don't know where to save modified document"
3343 else
3344 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3345 let path =
3346 getcmdoutput
3347 (fun exn ->
3348 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3349 savecmd
3351 if nonemptystr path
3352 then
3353 let tmp = path ^ ".tmp" in
3354 Ffi.savedoc tmp;
3355 Unix.rename tmp path;
3358 let viewkeyboard key mask =
3359 let enttext te =
3360 let mode = state.mode in
3361 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3362 state.text <- E.s;
3363 enttext ();
3364 postRedisplay "view:enttext"
3365 and histback () =
3366 match state.nav.past with
3367 | [] -> ()
3368 | prev :: prest ->
3369 state.nav <- { past = prest
3370 ; future = getanchor () :: state.nav.future; };
3371 gotoxy state.x (getanchory prev)
3373 let ctrl = Wsi.withctrl mask in
3374 let open Keys in
3375 match Wsi.kc2kt key with
3376 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3378 | Ascii 'Q' -> exit 0
3380 | Ascii 'z' ->
3381 let yloc f =
3382 match List.rev state.rects with
3383 | [] -> ()
3384 | (pageno, _, (_, y0, _, y1, _, y2, _, y3)) :: _ ->
3385 f pageno (y0, y1, y2, y3)
3386 and yminmax (y0, y1, y2, y3) =
3387 let ym = min y0 y1 |> min y2 |> min y3 |> truncate in
3388 let yM = max y0 y1 |> max y2 |> max y3 |> truncate in
3389 ym, yM
3391 let ondone msg = state.text <- msg
3392 and zmod _ _ k =
3393 match [@warning "-4"] k with
3394 | Keys.Ascii 'z' ->
3395 let f pageno ys =
3396 let ym, yM = yminmax ys in
3397 let hh = (yM - ym)/2 in
3398 gotopage1 pageno (ym + hh - state.winh/2)
3400 yloc f;
3401 TEdone "center"
3402 | Keys.Ascii 't' ->
3403 let f pageno ys =
3404 let ym, _ = yminmax ys in
3405 gotopage1 pageno ym
3407 yloc f;
3408 TEdone "top"
3409 | Keys.Ascii 'b' ->
3410 let f pageno ys =
3411 let _, yM = yminmax ys in
3412 gotopage1 pageno (yM - state.winh)
3414 yloc f;
3415 TEdone "bottom"
3416 | _ -> TEstop
3418 enttext (": ", E.s, None, zmod state.mode, ondone, true)
3420 | Ascii 'W' ->
3421 if Ffi.hasunsavedchanges ()
3422 then save ()
3424 | Insert ->
3425 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3426 then (
3427 state.mode <- (
3428 match state.lnava with
3429 | None -> LinkNav (Ltgendir 0)
3430 | Some pn -> LinkNav (Ltexact pn)
3432 gotoxy state.x state.y;
3434 else impmsg "keyboard link navigation does not work under rotation"
3436 | Escape | Ascii 'q' ->
3437 begin match state.mstate with
3438 | Mzoomrect _ ->
3439 resetmstate ();
3440 postRedisplay "kill rect";
3441 | Msel _
3442 | Mpan _
3443 | Mscrolly | Mscrollx
3444 | Mzoom _
3445 | Mnone ->
3446 begin match state.mode with
3447 | LinkNav ln ->
3448 begin match ln with
3449 | Ltexact pl -> state.lnava <- Some pl
3450 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3451 end;
3452 state.mode <- View;
3453 postRedisplay "esc leave linknav"
3454 | Birdseye _ | Textentry _ | View ->
3455 match state.ranchors with
3456 | [] -> raise Quit
3457 | (path, password, anchor, origin) :: rest ->
3458 state.ranchors <- rest;
3459 state.anchor <- anchor;
3460 state.origin <- origin;
3461 state.nameddest <- E.s;
3462 opendoc path password
3463 end;
3464 end;
3466 | Ascii 'o' -> enteroutlinemode ()
3467 | Ascii 'H' -> enterhistmode ()
3469 | Ascii 'u' ->
3470 state.rects <- [];
3471 state.text <- E.s;
3472 Hashtbl.iter (fun _ opaque ->
3473 Ffi.clearmark opaque;
3474 Hashtbl.clear state.prects) state.pagemap;
3475 postRedisplay "dehighlight";
3477 | Ascii (('/' | '?') as c) ->
3478 let ondone isforw s =
3479 cbput state.hists.pat s;
3480 state.searchpattern <- s;
3481 search s isforw
3483 let s = String.make 1 c in
3484 enttext (s, E.s, Some (onhist state.hists.pat),
3485 textentry, ondone (c = '/'), true)
3487 | Ascii '+' | Ascii '=' when ctrl ->
3488 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3489 pivotzoom (conf.zoom +. incr)
3491 | Ascii '+' ->
3492 let ondone s =
3493 let n =
3494 try int_of_string s with exn ->
3495 state.text <-
3496 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3497 max_int
3499 if n != max_int
3500 then (
3501 conf.pagebias <- n;
3502 state.text <- "page bias is now " ^ string_of_int n;
3505 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3507 | Ascii '-' when ctrl ->
3508 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3509 pivotzoom (max 0.01 (conf.zoom -. decr))
3511 | Ascii '-' ->
3512 let ondone msg = state.text <- msg in
3513 enttext ("option: ", E.s, None,
3514 optentry state.mode, ondone, true)
3516 | Ascii '0' when ctrl ->
3517 if conf.zoom = 1.0
3518 then gotoxy 0 state.y
3519 else setzoom 1.0
3521 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3522 let cols =
3523 match conf.columns with
3524 | Csingle _ | Cmulti _ -> 1
3525 | Csplit (n, _) -> n
3527 let h = state.winh -
3528 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3530 let zoom = Ffi.zoomforh state.winw h 0 cols in
3531 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3532 then setzoom zoom
3534 | Ascii '3' when ctrl ->
3535 let fm =
3536 match conf.fitmodel with
3537 | FitWidth -> FitProportional
3538 | FitProportional -> FitPage
3539 | FitPage -> FitWidth
3541 state.text <- "fit model: " ^ FMTE.to_string fm;
3542 reqlayout conf.angle fm
3544 | Ascii '4' when ctrl ->
3545 let zoom = Ffi.getmaxw () /. float state.winw in
3546 if zoom > 0.0 then setzoom zoom
3548 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3550 | Ascii ('0'..'9' as c) when not ctrl ->
3551 let ondone s =
3552 let n =
3553 try int_of_string s with exn ->
3554 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3557 if n >= 0
3558 then (
3559 addnav ();
3560 cbput state.hists.pag (string_of_int n);
3561 gotopage1 (n + conf.pagebias - 1) 0;
3564 let pageentry text = function [@warning "-4"]
3565 | Keys.Ascii 'g' -> TEdone text
3566 | key -> intentry text key
3568 let text = String.make 1 c in
3569 enttext (":", text, Some (onhist state.hists.pag),
3570 pageentry, ondone, true)
3572 | Ascii 'b' ->
3573 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3574 postRedisplay "toggle scrollbar";
3576 | Ascii 'B' ->
3577 state.bzoom <- not state.bzoom;
3578 state.rects <- [];
3579 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3581 | Ascii 'l' ->
3582 conf.hlinks <- not conf.hlinks;
3583 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3584 postRedisplay "toggle highlightlinks";
3586 | Ascii 'F' ->
3587 if conf.angle mod 360 = 0
3588 then (
3589 state.glinks <- true;
3590 let mode = state.mode in
3591 state.mode <-
3592 Textentry (
3593 ("goto: ", E.s, None, linknentry, linknact gotounder, false),
3594 (fun _ ->
3595 state.glinks <- false;
3596 state.mode <- mode)
3598 state.text <- E.s;
3599 postRedisplay "view:linkent(F)"
3601 else impmsg "hint mode does not work under rotation"
3603 | Ascii 'y' ->
3604 state.glinks <- true;
3605 let mode = state.mode in
3606 state.mode <-
3607 Textentry (
3608 ("copy: ", E.s, None, linknentry,
3609 linknact (fun under ->
3610 selstring conf.selcmd (undertext under)), false),
3611 (fun _ ->
3612 state.glinks <- false;
3613 state.mode <- mode)
3615 state.text <- E.s;
3616 postRedisplay "view:linkent"
3618 | Ascii 'a' ->
3619 begin match state.autoscroll with
3620 | Some step ->
3621 conf.autoscrollstep <- step;
3622 state.autoscroll <- None
3623 | None ->
3624 state.autoscroll <- Some conf.autoscrollstep;
3625 state.slideshow <- state.slideshow land lnot 2
3628 | Ascii 'p' when ctrl ->
3629 launchpath () (* XXX where do error messages go? *)
3631 | Ascii 'P' ->
3632 setpresentationmode (not conf.presentation);
3633 showtext ' ' ("presentation mode " ^
3634 if conf.presentation then "on" else "off");
3636 | Ascii 'f' ->
3637 if List.mem Wsi.Fullscreen state.winstate
3638 then Wsi.reshape conf.cwinw conf.cwinh
3639 else Wsi.fullscreen ()
3641 | Ascii ('p'|'N') -> search state.searchpattern false
3642 | Ascii 'n' | Fn 3 -> search state.searchpattern true
3644 | Ascii 't' ->
3645 begin match state.layout with
3646 | [] -> ()
3647 | l :: _ -> gotoxy state.x (getpagey l.pageno)
3650 | Ascii ' ' -> nextpage ()
3651 | Delete -> prevpage ()
3652 | Ascii '=' -> showtext ' ' (describe_layout state.layout);
3654 | Ascii 'w' ->
3655 begin match state.layout with
3656 | [] -> ()
3657 | l :: _ ->
3658 Wsi.reshape l.pagew l.pageh;
3659 postRedisplay "w"
3662 | Ascii '\'' -> enterbookmarkmode ()
3663 | Ascii 'h' | Fn 1 -> enterhelpmode ()
3664 | Ascii 'i' -> enterinfomode ()
3665 | Ascii 'e' when Buffer.length state.errmsgs > 0 -> entermsgsmode ()
3667 | Ascii 'm' ->
3668 let ondone s =
3669 match state.layout with
3670 | l :: _ when nonemptystr s -> addbookmark s @@ getanchor1 l
3671 | _ -> ()
3673 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3675 | Ascii '~' ->
3676 quickbookmark ();
3677 showtext ' ' "Quick bookmark added";
3679 | Ascii 'x' -> state.roam ()
3681 | Ascii ('<'|'>' as c) ->
3682 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3684 | Ascii ('['|']' as c) ->
3685 conf.colorscale <-
3686 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3687 postRedisplay "brightness";
3689 | Ascii 'c' when state.mode = View ->
3690 if Wsi.withalt mask
3691 then (
3692 if conf.zoom > 1.0
3693 then
3694 let m = (state.winw - state.w) / 2 in
3695 gotoxy m state.y
3697 else
3698 let (c, a, b), z =
3699 match state.prevcolumns with
3700 | None -> (1, 0, 0), 1.0
3701 | Some (columns, z) ->
3702 let cab =
3703 match columns with
3704 | Csplit (c, _) -> -c, 0, 0
3705 | Cmulti ((c, a, b), _) -> c, a, b
3706 | Csingle _ -> 1, 0, 0
3708 cab, z
3710 setcolumns View c a b;
3711 setzoom z
3713 | Down | Up when ctrl && Wsi.withshift mask ->
3714 let zoom, x = state.prevzoom in
3715 setzoom zoom;
3716 state.x <- x;
3718 | Up ->
3719 begin match state.autoscroll with
3720 | None ->
3721 begin match state.mode with
3722 | Birdseye beye -> upbirdseye 1 beye
3723 | Textentry _ | View | LinkNav _ ->
3724 if ctrl
3725 then gotoxy state.x (clamp ~-(state.winh/2))
3726 else (
3727 if not (Wsi.withshift mask) && conf.presentation
3728 then prevpage ()
3729 else gotoxy state.x (clamp (-conf.scrollstep))
3732 | Some n -> setautoscrollspeed n false
3735 | Down ->
3736 begin match state.autoscroll with
3737 | None ->
3738 begin match state.mode with
3739 | Birdseye beye -> downbirdseye 1 beye
3740 | Textentry _ | View | LinkNav _ ->
3741 if ctrl
3742 then gotoxy state.x (clamp (state.winh/2))
3743 else (
3744 if not (Wsi.withshift mask) && conf.presentation
3745 then nextpage ()
3746 else gotoxy state.x (clamp (conf.scrollstep))
3749 | Some n -> setautoscrollspeed n true
3752 | Left | Right when not (Wsi.withalt mask) ->
3753 if canpan ()
3754 then
3755 let dx =
3756 if ctrl
3757 then state.winw / 2
3758 else conf.hscrollstep
3760 let dx =
3761 let pv = Wsi.kc2kt key in
3762 if pv = Keys.Left then dx else -dx
3764 gotoxy (panbound (state.x + dx)) state.y
3765 else (
3766 state.text <- E.s;
3767 postRedisplay "left/right"
3770 | Prior ->
3771 let y =
3772 if ctrl
3773 then
3774 match state.layout with
3775 | [] -> state.y
3776 | l :: _ -> state.y - l.pagey
3777 else clamp (pgscale (-state.winh))
3779 gotoxy state.x y
3781 | Next ->
3782 let y =
3783 if ctrl
3784 then
3785 match List.rev state.layout with
3786 | [] -> state.y
3787 | l :: _ -> getpagey l.pageno
3788 else clamp (pgscale state.winh)
3790 gotoxy state.x y
3792 | Ascii 'g' | Home ->
3793 addnav ();
3794 gotoxy 0 0
3795 | Ascii 'G' | End ->
3796 addnav ();
3797 gotoxy 0 (clamp state.maxy)
3799 | Right when Wsi.withalt mask ->
3800 (match state.nav.future with
3801 | [] -> ()
3802 | next :: frest ->
3803 state.nav <- { past = getanchor () :: state.nav.past; future = frest; };
3804 gotoxy state.x (getanchory next)
3806 | Left when Wsi.withalt mask -> histback ()
3807 | Backspace -> histback ()
3809 | Ascii 'r' ->
3810 reload ()
3812 | Ascii 'v' when conf.debug ->
3813 state.rects <- [];
3814 List.iter (fun l ->
3815 match getopaque l.pageno with
3816 | None -> ()
3817 | Some opaque ->
3818 let x0, y0, x1, y1 = Ffi.pagebbox opaque in
3819 let rect = (float x0, float y0,
3820 float x1, float y0,
3821 float x1, float y1,
3822 float x0, float y1) in
3823 debugrect rect;
3824 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3825 state.rects <- (l.pageno, color, rect) :: state.rects;
3826 ) state.layout;
3827 postRedisplay "v";
3829 | Ascii '|' ->
3830 let mode = state.mode in
3831 let cmd = ref E.s in
3832 let onleave = function
3833 | Cancel -> state.mode <- mode
3834 | Confirm ->
3835 List.iter (fun l ->
3836 match getopaque l.pageno with
3837 | Some opaque -> pipesel opaque !cmd
3838 | None -> ()) state.layout;
3839 state.mode <- mode
3841 let ondone s =
3842 cbput state.hists.sel s;
3843 cmd := s
3845 let te =
3846 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3848 postRedisplay "|";
3849 state.mode <- Textentry (te, onleave);
3851 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3852 vlog "huh? %s" (Wsi.keyname key)
3855 let linknavkeyboard key mask linknav =
3856 let pv = Wsi.kc2kt key in
3857 let getpage pageno =
3858 let rec loop = function
3859 | [] -> None
3860 | l :: _ when l.pageno = pageno -> Some l
3861 | _ :: rest -> loop rest
3862 in loop state.layout
3864 let doexact (pageno, n) =
3865 match getopaque pageno, getpage pageno with
3866 | Some opaque, Some l ->
3867 if pv = Keys.Enter
3868 then
3869 let under = Ffi.getlink opaque n in
3870 postRedisplay "link gotounder";
3871 gotounder under;
3872 state.mode <- View;
3873 else
3874 let opt, dir =
3875 let open Keys in
3876 match pv with
3877 | Home -> Some (Ffi.findlink opaque LDfirst), -1
3878 | End -> Some (Ffi.findlink opaque LDlast), 1
3879 | Left -> Some (Ffi.findlink opaque (LDleft n)), -1
3880 | Right -> Some (Ffi.findlink opaque (LDright n)), 1
3881 | Up -> Some (Ffi.findlink opaque (LDup n)), -1
3882 | Down -> Some (Ffi.findlink opaque (LDdown n)), 1
3883 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3884 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3886 let pwl l dir =
3887 begin match Ffi.findpwl l.pageno dir with
3888 | Pwlnotfound -> ()
3889 | Pwl pageno ->
3890 let notfound dir =
3891 state.mode <- LinkNav (Ltgendir dir);
3892 let y, h = getpageyh pageno in
3893 let y =
3894 if dir < 0
3895 then y + h - state.winh
3896 else y
3898 gotoxy state.x y
3900 begin match getopaque pageno, getpage pageno with
3901 | Some opaque, Some _ ->
3902 let link =
3903 let ld = if dir > 0 then LDfirst else LDlast in
3904 Ffi.findlink opaque ld
3906 begin match link with
3907 | Lfound m ->
3908 showlinktype (Ffi.getlink opaque m);
3909 state.mode <- LinkNav (Ltexact (pageno, m));
3910 postRedisplay "linknav jpage";
3911 | Lnotfound -> notfound dir
3912 end;
3913 | _ -> notfound dir
3914 end;
3915 end;
3917 begin match opt with
3918 | Some Lnotfound -> pwl l dir;
3919 | Some (Lfound m) ->
3920 if m = n
3921 then pwl l dir
3922 else (
3923 let _, y0, _, y1 = Ffi.getlinkrect opaque m in
3924 if y0 < l.pagey
3925 then gotopage1 l.pageno y0
3926 else (
3927 let d = fstate.fontsize + 1 in
3928 if y1 - l.pagey > l.pagevh - d
3929 then gotopage1 l.pageno (y1 - state.winh + d)
3930 else postRedisplay "linknav";
3932 showlinktype (Ffi.getlink opaque m);
3933 state.mode <- LinkNav (Ltexact (l.pageno, m));
3936 | None -> viewkeyboard key mask
3937 end;
3938 | _ -> viewkeyboard key mask
3940 if pv = Keys.Insert
3941 then (
3942 begin match linknav with
3943 | Ltexact pa -> state.lnava <- Some pa
3944 | Ltgendir _ | Ltnotready _ -> ()
3945 end;
3946 state.mode <- View;
3947 postRedisplay "leave linknav"
3949 else
3950 match linknav with
3951 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3952 | Ltexact exact -> doexact exact
3955 let keyboard key mask =
3956 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
3957 then wcmd "interrupt"
3958 else state.uioh <- state.uioh#key key mask
3961 let birdseyekeyboard key mask
3962 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3963 let incr =
3964 match conf.columns with
3965 | Csingle _ -> 1
3966 | Cmulti ((c, _, _), _) -> c
3967 | Csplit _ -> error "bird's eye split mode"
3969 let pgh layout = List.fold_left
3970 (fun m l -> max l.pageh m) state.winh layout in
3971 let open Keys in
3972 match Wsi.kc2kt key with
3973 | Ascii 'l' when Wsi.withctrl mask ->
3974 let y, h = getpageyh pageno in
3975 let top = (state.winh - h) / 2 in
3976 gotoxy state.x (max 0 (y - top))
3977 | Enter -> leavebirdseye beye false
3978 | Escape -> leavebirdseye beye true
3979 | Up -> upbirdseye incr beye
3980 | Down -> downbirdseye incr beye
3981 | Left -> upbirdseye 1 beye
3982 | Right -> downbirdseye 1 beye
3984 | Prior ->
3985 begin match state.layout with
3986 | l :: _ ->
3987 if l.pagey != 0
3988 then (
3989 state.mode <- Birdseye (
3990 oconf, leftx, l.pageno, hooverpageno, anchor
3992 gotopage1 l.pageno 0;
3994 else (
3995 let layout = layout state.x (state.y-state.winh)
3996 state.winw
3997 (pgh state.layout) in
3998 match layout with
3999 | [] -> gotoxy state.x (clamp (-state.winh))
4000 | l :: _ ->
4001 state.mode <- Birdseye (
4002 oconf, leftx, l.pageno, hooverpageno, anchor
4004 gotopage1 l.pageno 0
4007 | [] -> gotoxy state.x (clamp (-state.winh))
4008 end;
4010 | Next ->
4011 begin match List.rev state.layout with
4012 | l :: _ ->
4013 let layout = layout state.x
4014 (state.y + (pgh state.layout))
4015 state.winw state.winh in
4016 begin match layout with
4017 | [] ->
4018 let incr = l.pageh - l.pagevh in
4019 if incr = 0
4020 then (
4021 state.mode <-
4022 Birdseye (
4023 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4025 postRedisplay "birdseye pagedown";
4027 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4029 | l :: _ ->
4030 state.mode <-
4031 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4032 gotopage1 l.pageno 0;
4035 | [] -> gotoxy state.x (clamp state.winh)
4036 end;
4038 | Home ->
4039 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4040 gotopage1 0 0
4042 | End ->
4043 let pageno = state.pagecount - 1 in
4044 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4045 if not (pagevisible state.layout pageno)
4046 then
4047 let h =
4048 match List.rev state.pdims with
4049 | [] -> state.winh
4050 | (_, _, h, _) :: _ -> h
4052 gotoxy
4053 state.x
4054 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4055 else postRedisplay "birdseye end";
4057 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4060 let drawpage l =
4061 let color =
4062 match state.mode with
4063 | Textentry _ -> scalecolor 0.4
4064 | LinkNav _ | View -> scalecolor 1.0
4065 | Birdseye (_, _, pageno, hooverpageno, _) ->
4066 if l.pageno = hooverpageno
4067 then scalecolor 0.9
4068 else (
4069 if l.pageno = pageno
4070 then (
4071 let c = scalecolor 1.0 in
4072 GlDraw.color c;
4073 GlDraw.line_width 3.0;
4074 let dispx = l.pagedispx in
4075 linerect
4076 (float (dispx-1)) (float (l.pagedispy-1))
4077 (float (dispx+l.pagevw+1))
4078 (float (l.pagedispy+l.pagevh+1));
4079 GlDraw.line_width 1.0;
4082 else scalecolor 0.8
4085 drawtiles l color;
4088 let postdrawpage l linkindexbase =
4089 match getopaque l.pageno with
4090 | Some opaque ->
4091 if tileready l l.pagex l.pagey
4092 then
4093 let x = l.pagedispx - l.pagex
4094 and y = l.pagedispy - l.pagey in
4095 let hlmask =
4096 match conf.columns with
4097 | Csingle _ | Cmulti _ ->
4098 (if conf.hlinks then 1 else 0)
4099 + (if state.glinks
4100 && not (isbirdseye state.mode) then 2 else 0)
4101 | Csplit _ -> 0
4103 let s =
4104 match state.mode with
4105 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4106 | Textentry _
4107 | Birdseye _
4108 | View
4109 | LinkNav _ -> E.s
4111 Hashtbl.find_all state.prects l.pageno |>
4112 List.iter (fun vals -> Ffi.drawprect opaque x y vals);
4113 let n =
4114 Ffi.postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4115 if n < 0
4116 then (Glutils.redisplay := true; 0)
4117 else n
4118 else 0
4119 | _ -> 0
4122 let scrollindicator () =
4123 let sbw, ph, sh = state.uioh#scrollph in
4124 let sbh, pw, sw = state.uioh#scrollpw in
4126 let x0,x1,hx0 =
4127 if conf.leftscroll
4128 then (0, sbw, sbw)
4129 else ((state.winw - sbw), state.winw, 0)
4132 Gl.enable `blend;
4133 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4134 let (r, g, b, alpha) = conf.sbarcolor in
4135 GlDraw.color (r, g, b) ~alpha;
4136 filledrect (float x0) 0. (float x1) (float state.winh);
4137 filledrect
4138 (float hx0) (float (state.winh - sbh))
4139 (float (hx0 + state.winw)) (float state.winh);
4140 let (r, g, b, alpha) = conf.sbarhndlcolor in
4141 GlDraw.color (r, g, b) ~alpha;
4143 filledrect (float x0) ph (float x1) (ph +. sh);
4144 let pw = pw +. float hx0 in
4145 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4146 Gl.disable `blend;
4149 let showsel () =
4150 match state.mstate with
4151 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
4152 | Msel ((x0, y0), (x1, y1)) ->
4153 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4154 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4155 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4156 if n0 != -1 && n0 = n1 then Ffi.seltext o0 (px0, py0, px1, py1);
4159 let showrects = function
4160 | [] -> ()
4161 | rects ->
4162 Gl.enable `blend;
4163 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4164 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4165 List.iter
4166 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4167 List.iter (fun l ->
4168 if l.pageno = pageno
4169 then
4170 let dx = float (l.pagedispx - l.pagex) in
4171 let dy = float (l.pagedispy - l.pagey) in
4172 let r, g, b, alpha = c in
4173 GlDraw.color (r, g, b) ~alpha;
4174 filledrect2
4175 (x0+.dx) (y0+.dy)
4176 (x1+.dx) (y1+.dy)
4177 (x3+.dx) (y3+.dy)
4178 (x2+.dx) (y2+.dy);
4179 ) state.layout
4180 ) rects;
4181 Gl.disable `blend;
4184 let display () =
4185 GlDraw.color (scalecolor2 conf.bgcolor);
4186 GlClear.color (scalecolor2 conf.bgcolor);
4187 GlClear.clear [`color];
4188 List.iter drawpage state.layout;
4189 let rects =
4190 match state.mode with
4191 | LinkNav (Ltexact (pageno, linkno)) ->
4192 begin match getopaque pageno with
4193 | Some opaque ->
4194 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
4195 let color =
4196 if conf.invert
4197 then (1.0, 1.0, 1.0, 0.5)
4198 else (0.0, 0.0, 0.5, 0.5)
4200 (pageno, color,
4201 (float x0, float y0,
4202 float x1, float y0,
4203 float x1, float y1,
4204 float x0, float y1)
4205 ) :: state.rects
4206 | None -> state.rects
4208 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4209 | Birdseye _
4210 | Textentry _
4211 | View -> state.rects
4213 showrects rects;
4214 let rec postloop linkindexbase = function
4215 | l :: rest ->
4216 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4217 postloop linkindexbase rest
4218 | [] -> ()
4220 showsel ();
4221 postloop 0 state.layout;
4222 state.uioh#display;
4223 begin match state.mstate with
4224 | Mzoomrect ((x0, y0), (x1, y1)) ->
4225 Gl.enable `blend;
4226 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4227 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4228 filledrect (float x0) (float y0) (float x1) (float y1);
4229 Gl.disable `blend;
4230 | Msel _
4231 | Mpan _
4232 | Mscrolly | Mscrollx
4233 | Mzoom _
4234 | Mnone -> ()
4235 end;
4236 enttext ();
4237 scrollindicator ();
4238 Wsi.swapb ();
4241 let display () =
4242 match state.reload with
4243 | Some (x, y, t) ->
4244 if x != state.x || y != state.y || abs_float @@ now () -. t > 0.5
4245 || (state.layout != [] && layoutready state.layout)
4246 then (
4247 state.reload <- None;
4248 display ()
4250 | None -> display ()
4253 let zoomrect x y x1 y1 =
4254 let x0 = min x x1
4255 and x1 = max x x1
4256 and y0 = min y y1 in
4257 let zoom = (float state.w) /. float (x1 - x0) in
4258 let margin =
4259 let simple () =
4260 if state.w < state.winw
4261 then (state.winw - state.w) / 2
4262 else 0
4264 match conf.fitmodel with
4265 | FitWidth | FitProportional -> simple ()
4266 | FitPage ->
4267 match conf.columns with
4268 | Csplit _ ->
4269 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4270 | Cmulti _ | Csingle _ -> simple ()
4272 gotoxy ((state.x + margin) - x0) (state.y + y0);
4273 state.anchor <- getanchor ();
4274 setzoom zoom;
4275 resetmstate ();
4278 let annot inline x y =
4279 match unproject x y with
4280 | Some (opaque, n, ux, uy) ->
4281 let add text =
4282 Ffi.addannot opaque ux uy text;
4283 wcmd "freepage %s" (~> opaque);
4284 Hashtbl.remove state.pagemap (n, state.gen);
4285 flushtiles ();
4286 gotoxy state.x state.y
4288 if inline
4289 then
4290 let ondone s = add s in
4291 let mode = state.mode in
4292 state.mode <- Textentry (
4293 ("annotation: ", E.s, None, textentry, ondone, true),
4294 fun _ -> state.mode <- mode);
4295 state.text <- E.s;
4296 enttext ();
4297 postRedisplay "annot"
4298 else add @@ getusertext E.s
4299 | _ -> ()
4302 let zoomblock x y =
4303 let g opaque l px py =
4304 match Ffi.rectofblock opaque px py with
4305 | Some a ->
4306 let x0 = a.(0) -. 20. in
4307 let x1 = a.(1) +. 20. in
4308 let y0 = a.(2) -. 20. in
4309 let zoom = (float state.w) /. (x1 -. x0) in
4310 let pagey = getpagey l.pageno in
4311 let margin = (state.w - l.pagew)/2 in
4312 let nx = -truncate x0 - margin in
4313 gotoxy nx (pagey + truncate y0);
4314 state.anchor <- getanchor ();
4315 setzoom zoom;
4316 None
4317 | None -> None
4319 match conf.columns with
4320 | Csplit _ ->
4321 impmsg "block zooming does not work properly in split columns mode"
4322 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4325 let scrollx x =
4326 let winw = state.winw - 1 in
4327 let s = float x /. float winw in
4328 let destx = truncate (float (state.w + winw) *. s) in
4329 gotoxy (winw - destx) state.y;
4330 state.mstate <- Mscrollx;
4333 let scrolly y =
4334 let s = float y /. float state.winh in
4335 let desty = truncate (s *. float (maxy ())) in
4336 gotoxy state.x desty;
4337 state.mstate <- Mscrolly;
4340 let viewmulticlick clicks x y mask =
4341 let g opaque l px py =
4342 let mark =
4343 match clicks with
4344 | 2 -> Mark_word
4345 | 3 -> Mark_line
4346 | 4 -> Mark_block
4347 | _ -> Mark_page
4349 if Ffi.markunder opaque px py mark
4350 then (
4351 Some (fun () ->
4352 let dopipe cmd =
4353 match getopaque l.pageno with
4354 | None -> ()
4355 | Some opaque -> pipesel opaque cmd
4357 state.roam <- (fun () -> dopipe conf.paxcmd);
4358 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4361 else None
4363 postRedisplay "viewmulticlick";
4364 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4367 let canselect () =
4368 match conf.columns with
4369 | Csplit _ -> false
4370 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4373 let viewmouse button down x y mask =
4374 match button with
4375 | n when (n == 4 || n == 5) && not down ->
4376 if Wsi.withctrl mask
4377 then (
4378 let incr =
4379 if n = 5
4380 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4381 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4383 let fx, fy =
4384 match state.mstate with
4385 | Mzoom (oldn, _, pos) when n = oldn -> pos
4386 | Mzoomrect _ | Mnone | Mpan _
4387 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4389 let zoom = conf.zoom -. incr in
4390 state.mstate <- Mzoom (n, 0, (x, y));
4391 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4392 then pivotzoom ~x ~y zoom
4393 else pivotzoom zoom
4395 else (
4396 match state.autoscroll with
4397 | Some step -> setautoscrollspeed step (n=4)
4398 | None ->
4399 if conf.wheelbypage || conf.presentation
4400 then (
4401 if n = 4
4402 then prevpage ()
4403 else nextpage ()
4405 else
4406 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4407 let incr = incr * 2 in
4408 let y = clamp incr in
4409 gotoxy state.x y
4412 | n when (n = 6 || n = 7) && not down && canpan () ->
4413 let x =
4414 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4415 gotoxy x state.y
4417 | 1 when Wsi.withshift mask ->
4418 state.mstate <- Mnone;
4419 if not down
4420 then (
4421 match unproject x y with
4422 | None -> ()
4423 | Some (_, pageno, ux, uy) ->
4424 let cmd = Printf.sprintf
4425 "%s %s %d %d %d"
4426 conf.stcmd state.path pageno ux uy
4428 match spawn cmd [] with
4429 | exception exn ->
4430 impmsg "execution of synctex command(%S) failed: %S"
4431 conf.stcmd @@ exntos exn
4432 | _pid -> ()
4435 | 1 when Wsi.withctrl mask ->
4436 if down
4437 then (
4438 Wsi.setcursor Wsi.CURSOR_FLEUR;
4439 state.mstate <- Mpan (x, y)
4441 else state.mstate <- Mnone
4443 | 3 ->
4444 if down
4445 then (
4446 if Wsi.withshift mask
4447 then (
4448 annot conf.annotinline x y;
4449 postRedisplay "addannot"
4451 else
4452 let p = (x, y) in
4453 Wsi.setcursor Wsi.CURSOR_CYCLE;
4454 state.mstate <- Mzoomrect (p, p)
4456 else (
4457 match state.mstate with
4458 | Mzoomrect ((x0, y0), _) ->
4459 if abs (x-x0) > 10 && abs (y - y0) > 10
4460 then zoomrect x0 y0 x y
4461 else (
4462 resetmstate ();
4463 postRedisplay "kill accidental zoom rect";
4465 | Msel _
4466 | Mpan _
4467 | Mscrolly | Mscrollx
4468 | Mzoom _
4469 | Mnone -> resetmstate ()
4472 | 1 when vscrollhit x ->
4473 if down
4474 then
4475 let _, position, sh = state.uioh#scrollph in
4476 if y > truncate position && y < truncate (position +. sh)
4477 then state.mstate <- Mscrolly
4478 else scrolly y
4479 else state.mstate <- Mnone
4481 | 1 when y > state.winh - hscrollh () ->
4482 if down
4483 then
4484 let _, position, sw = state.uioh#scrollpw in
4485 if x > truncate position && x < truncate (position +. sw)
4486 then state.mstate <- Mscrollx
4487 else scrollx x
4488 else state.mstate <- Mnone
4490 | 1 when state.bzoom -> if not down then zoomblock x y
4492 | 1 ->
4493 let dest = if down then getunder x y else Unone in
4494 begin match dest with
4495 | Ulinkuri _ -> gotounder dest
4496 | Unone when down ->
4497 Wsi.setcursor Wsi.CURSOR_FLEUR;
4498 state.mstate <- Mpan (x, y);
4499 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4500 | Unone | Utext _ ->
4501 if down
4502 then (
4503 if canselect ()
4504 then (
4505 state.mstate <- Msel ((x, y), (x, y));
4506 postRedisplay "mouse select";
4509 else (
4510 match state.mstate with
4511 | Mnone -> ()
4512 | Mzoom _ | Mscrollx | Mscrolly -> state.mstate <- Mnone
4513 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4514 | Mpan _ ->
4515 Wsi.setcursor Wsi.CURSOR_INHERIT;
4516 state.mstate <- Mnone
4517 | Msel ((x0, y0), (x1, y1)) ->
4518 let rec loop = function
4519 | [] -> ()
4520 | l :: rest ->
4521 let inside =
4522 let a0 = l.pagedispy in
4523 let a1 = a0 + l.pagevh in
4524 let b0 = l.pagedispx in
4525 let b1 = b0 + l.pagevw in
4526 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4527 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4529 if inside
4530 then
4531 match getopaque l.pageno with
4532 | Some opaque ->
4533 let dosel cmd () =
4534 pipef ~closew:false "Msel"
4535 (fun w ->
4536 Ffi.copysel w opaque;
4537 postRedisplay "Msel") cmd
4539 dosel conf.selcmd ();
4540 state.roam <- dosel conf.paxcmd;
4541 | None -> ()
4542 else loop rest
4544 loop state.layout;
4545 resetmstate ();
4548 | _ -> ()
4551 let birdseyemouse button down x y mask
4552 (conf, leftx, _, hooverpageno, anchor) =
4553 match button with
4554 | 1 when down ->
4555 let rec loop = function
4556 | [] -> ()
4557 | l :: rest ->
4558 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4559 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4560 then
4561 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4562 else loop rest
4564 loop state.layout
4565 | 3 -> ()
4566 | _ -> viewmouse button down x y mask
4569 let uioh = object
4570 method display = ()
4572 method key key mask =
4573 begin match state.mode with
4574 | Textentry textentry -> textentrykeyboard key mask textentry
4575 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4576 | View -> viewkeyboard key mask
4577 | LinkNav linknav -> linknavkeyboard key mask linknav
4578 end;
4579 state.uioh
4581 method button button bstate x y mask =
4582 begin match state.mode with
4583 | LinkNav _ | View -> viewmouse button bstate x y mask
4584 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4585 | Textentry _ -> ()
4586 end;
4587 state.uioh
4589 method multiclick clicks x y mask =
4590 begin match state.mode with
4591 | LinkNav _ | View -> viewmulticlick clicks x y mask
4592 | Birdseye _ | Textentry _ -> ()
4593 end;
4594 state.uioh
4596 method motion x y =
4597 begin match state.mode with
4598 | Textentry _ -> ()
4599 | View | Birdseye _ | LinkNav _ ->
4600 match state.mstate with
4601 | Mzoom _ | Mnone -> ()
4602 | Mpan (x0, y0) ->
4603 let dx = x - x0
4604 and dy = y0 - y in
4605 state.mstate <- Mpan (x, y);
4606 let x = if canpan () then panbound (state.x + dx) else state.x in
4607 let y = clamp dy in
4608 gotoxy x y
4610 | Msel (a, _) ->
4611 state.mstate <- Msel (a, (x, y));
4612 postRedisplay "motion select";
4614 | Mscrolly ->
4615 let y = min state.winh (max 0 y) in
4616 scrolly y
4618 | Mscrollx ->
4619 let x = min state.winw (max 0 x) in
4620 scrollx x
4622 | Mzoomrect (p0, _) ->
4623 state.mstate <- Mzoomrect (p0, (x, y));
4624 postRedisplay "motion zoomrect";
4625 end;
4626 state.uioh
4628 method pmotion x y =
4629 begin match state.mode with
4630 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4631 let rec loop = function
4632 | [] ->
4633 if hooverpageno != -1
4634 then (
4635 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4636 postRedisplay "pmotion birdseye no hoover";
4638 | l :: rest ->
4639 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4640 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4641 then (
4642 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4643 postRedisplay "pmotion birdseye hoover";
4645 else loop rest
4647 loop state.layout
4649 | Textentry _ -> ()
4651 | LinkNav _ | View ->
4652 match state.mstate with
4653 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4654 | Mnone ->
4655 updateunder x y;
4656 if canselect ()
4657 then
4658 match conf.pax with
4659 | None -> ()
4660 | Some past ->
4661 let now = now () in
4662 let delta = now -. past in
4663 if delta > 0.01
4664 then paxunder x y
4665 else conf.pax <- Some now
4666 end;
4667 state.uioh
4669 method infochanged _ = ()
4671 method scrollph =
4672 let maxy = maxy () in
4673 let p, h =
4674 if maxy = 0
4675 then 0.0, float state.winh
4676 else scrollph state.y maxy
4678 vscrollw (), p, h
4680 method scrollpw =
4681 let fwinw = float (state.winw - vscrollw ()) in
4682 let sw =
4683 let sw = fwinw /. float state.w in
4684 let sw = fwinw *. sw in
4685 max sw (float conf.scrollh)
4687 let position =
4688 let maxx = state.w + state.winw in
4689 let x = state.winw - state.x in
4690 let percent = float x /. float maxx in
4691 (fwinw -. sw) *. percent
4693 hscrollh (), position, sw
4695 method modehash =
4696 let modename =
4697 match state.mode with
4698 | LinkNav _ -> "links"
4699 | Textentry _ -> "textentry"
4700 | Birdseye _ -> "birdseye"
4701 | View -> "view"
4703 findkeyhash conf modename
4705 method eformsgs = true
4706 method alwaysscrolly = false
4707 method scroll dx dy =
4708 let x = if canpan () then panbound (state.x + dx) else state.x in
4709 gotoxy x (clamp (2 * dy));
4710 state.uioh
4711 method zoom z x y =
4712 pivotzoom ~x ~y (conf.zoom *. exp z);
4713 end;;
4715 let addrect pageno r g b a x0 y0 x1 y1 =
4716 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4719 let ract cmds =
4720 let cl = splitatchar cmds ' ' in
4721 let scan s fmt f =
4722 try Scanf.sscanf s fmt f
4723 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4724 cmds @@ exntos exn
4726 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4727 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4728 s pageno r g b a x0 y0 x1 y1;
4729 onpagerect
4730 pageno
4731 (fun w h ->
4732 let _,w1,h1,_ = getpagedim pageno in
4733 let sw = float w1 /. float w
4734 and sh = float h1 /. float h in
4735 let x0s = x0 *. sw
4736 and x1s = x1 *. sw
4737 and y0s = y0 *. sh
4738 and y1s = y1 *. sh in
4739 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4740 let color = (r, g, b, a) in
4741 if conf.verbose then debugrect rect;
4742 state.rects <- (pageno, color, rect) :: state.rects;
4743 postRedisplay s;
4746 match cl with
4747 | "reload", "" -> reload ()
4748 | "goto", args ->
4749 scan args "%u %f %f"
4750 (fun pageno x y ->
4751 let cmd, _ = state.geomcmds in
4752 if emptystr cmd
4753 then gotopagexy pageno x y
4754 else
4755 let f prevf () =
4756 gotopagexy pageno x y;
4757 prevf ()
4759 state.reprf <- f state.reprf
4761 | "goto1", args -> scan args "%u %f" gotopage
4762 | "gotor", args -> scan args "%S" gotoremote
4763 | "rect", args ->
4764 scan args "%u %u %f %f %f %f"
4765 (fun pageno c x0 y0 x1 y1 ->
4766 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4767 rectx "rect" pageno color x0 y0 x1 y1;
4769 | "prect", args ->
4770 scan args "%u %f %f %f %f %f %f %f %f"
4771 (fun pageno r g b alpha x0 y0 x1 y1 ->
4772 addrect pageno r g b alpha x0 y0 x1 y1;
4773 postRedisplay "prect"
4775 | "pgoto", args ->
4776 scan args "%u %f %f"
4777 (fun pageno x y ->
4778 let optopaque =
4779 match getopaque pageno with
4780 | Some opaque -> opaque
4781 | None -> ~< E.s
4783 pgoto optopaque pageno x y;
4784 let rec fixx = function
4785 | [] -> ()
4786 | l :: rest ->
4787 if l.pageno = pageno
4788 then gotoxy (state.x - l.pagedispx) state.y
4789 else fixx rest
4791 let layout =
4792 let mult =
4793 match conf.columns with
4794 | Csingle _ | Csplit _ -> 1
4795 | Cmulti ((n, _, _), _) -> n
4797 layout 0 state.y (state.winw * mult) state.winh
4799 fixx layout
4801 | "activatewin", "" -> Wsi.activatewin ()
4802 | "quit", "" -> raise Quit
4803 | "keys", keys ->
4804 begin try
4805 let l = Config.keys_of_string keys in
4806 List.iter (fun (k, m) -> keyboard k m) l
4807 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4808 cmds @@ exntos exn
4810 | "clearrects", "" ->
4811 Hashtbl.clear state.prects;
4812 postRedisplay "clearrects"
4813 | _ ->
4814 adderrfmt "remote command"
4815 "error processing remote command: %S\n" cmds;
4818 let remote =
4819 let scratch = Bytes.create 80 in
4820 let buf = Buffer.create 80 in
4821 fun fd ->
4822 match tempfailureretry (Unix.read fd scratch 0) 80 with
4823 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4824 | 0 ->
4825 Unix.close fd;
4826 if Buffer.length buf > 0
4827 then (
4828 let s = Buffer.contents buf in
4829 Buffer.clear buf;
4830 ract s;
4832 None
4833 | n ->
4834 let rec eat ppos =
4835 let nlpos =
4836 match Bytes.index_from scratch ppos '\n' with
4837 | pos -> if pos >= n then -1 else pos
4838 | exception Not_found -> -1
4840 if nlpos >= 0
4841 then (
4842 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4843 let s = Buffer.contents buf in
4844 Buffer.clear buf;
4845 ract s;
4846 eat (nlpos+1);
4848 else (
4849 Buffer.add_subbytes buf scratch ppos (n-ppos);
4850 Some fd
4852 in eat 0
4855 let remoteopen path =
4856 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4857 with exn ->
4858 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4859 None
4862 let () =
4863 Utils.vlogf := (fun s -> if conf.verbose then prerr_endline s else ignore s);
4864 let gcconfig = ref false in
4865 let rcmdpath = ref E.s in
4866 let dcfpath = ref None in
4867 let pageno = ref None in
4868 let openlast = ref false in
4869 let doreap = ref false in
4870 let csspath = ref None in
4871 selfexec := Sys.executable_name;
4872 Arg.parse
4873 (Arg.align
4874 [("-p", Arg.String (fun s -> state.password <- s),
4875 "<password> Set password");
4877 ("-f", Arg.String
4878 (fun s ->
4879 Config.fontpath := s;
4880 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4882 "<path> Set path to the user interface font");
4884 ("-c", Arg.String
4885 (fun s ->
4886 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4887 Config.confpath := s),
4888 "<path> Set path to the configuration file");
4890 ("-last", Arg.Set openlast, " Open last document");
4892 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4893 "<page-number> Jump to page");
4895 ("-dest", Arg.String (fun s -> state.nameddest <- s),
4896 "<named-destination> Set named destination");
4898 ("-remote", Arg.String (fun s -> rcmdpath := s),
4899 "<path> Set path to the source of remote commands");
4901 ("-gc", Arg.Set gcconfig, " Collect config garbage");
4903 ("-v", Arg.Unit (fun () ->
4904 Printf.printf
4905 "%s\nconfiguration file: %s\n"
4906 (Help.version ())
4907 Config.defconfpath;
4908 exit 0), " Print version and exit");
4910 ("-css", Arg.String (fun s -> csspath := Some s),
4911 "<path> Set path to the style sheet to use with EPUB/HTML");
4913 ("-origin", Arg.String (fun s -> state.origin <- s),
4914 "<origin> <undocumented>");
4916 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
4918 ("-dcf", Arg.String (fun s -> dcfpath := Some s), " <undocumented>");
4920 ("-layout-height", Arg.Set_int layouth,
4921 "<height> layout height html/epub/etc (-1, 0, N)");
4924 (fun s -> state.path <- s)
4925 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4927 let histmode = emptystr state.path && not !openlast in
4929 if not (Config.load !openlast)
4930 then dolog "failed to load configuration";
4932 begin match !dcfpath with
4933 | Some path -> conf.dcf <- path
4934 | None -> ()
4935 end;
4937 begin match !pageno with
4938 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
4939 | None -> ()
4940 end;
4942 fillhelp ();
4943 if !gcconfig
4944 then (
4945 Config.gc ();
4946 exit 0
4949 let mu =
4950 object (self)
4951 val mutable m_clicks = 0
4952 val mutable m_click_x = 0
4953 val mutable m_click_y = 0
4954 val mutable m_lastclicktime = infinity
4956 method private cleanup =
4957 state.roam <- noroam;
4958 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) state.pagemap
4959 method expose = postRedisplay "expose"
4960 method visible v =
4961 let name =
4962 match v with
4963 | Wsi.Unobscured -> "unobscured"
4964 | Wsi.PartiallyObscured -> "partiallyobscured"
4965 | Wsi.FullyObscured -> "fullyobscured"
4967 vlog "visibility change %s" name
4968 method display = display ()
4969 method map mapped = vlog "mapped %b" mapped
4970 method reshape w h =
4971 self#cleanup;
4972 reshape w h
4973 method mouse b d x y m =
4974 if d && canselect ()
4975 then (
4977 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
4979 m_click_x <- x;
4980 m_click_y <- y;
4981 if b = 1
4982 then (
4983 let t = now () in
4984 if abs x - m_click_x > 10
4985 || abs y - m_click_y > 10
4986 || abs_float (t -. m_lastclicktime) > 0.3
4987 then m_clicks <- 0;
4988 m_clicks <- m_clicks + 1;
4989 m_lastclicktime <- t;
4990 if m_clicks = 1
4991 then (
4992 self#cleanup;
4993 postRedisplay "cleanup";
4994 state.uioh <- state.uioh#button b d x y m;
4996 else state.uioh <- state.uioh#multiclick m_clicks x y m
4998 else (
4999 self#cleanup;
5000 m_clicks <- 0;
5001 m_lastclicktime <- infinity;
5002 state.uioh <- state.uioh#button b d x y m
5005 else state.uioh <- state.uioh#button b d x y m
5006 method motion x y =
5007 state.mpos <- (x, y);
5008 state.uioh <- state.uioh#motion x y
5009 method pmotion x y =
5010 state.mpos <- (x, y);
5011 state.uioh <- state.uioh#pmotion x y
5012 method key k m =
5013 vlog "k=%#x m=%#x" k m;
5014 let mascm = m land (
5015 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
5016 ) in
5017 let keyboard k m =
5018 let x = state.x and y = state.y in
5019 keyboard k m;
5020 if x != state.x || y != state.y then self#cleanup
5022 match state.keystate with
5023 | KSnone ->
5024 let km = k, mascm in
5025 begin
5026 match
5027 let modehash = state.uioh#modehash in
5028 try Hashtbl.find modehash km
5029 with Not_found ->
5030 try Hashtbl.find (findkeyhash conf "global") km
5031 with Not_found -> KMinsrt (k, m)
5032 with
5033 | KMinsrt (k, m) -> keyboard k m
5034 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5035 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5037 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5038 List.iter (fun (k, m) -> keyboard k m) insrt;
5039 state.keystate <- KSnone
5040 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5041 state.keystate <- KSinto (keys, insrt)
5042 | KSinto _ -> state.keystate <- KSnone
5044 method enter x y =
5045 state.mpos <- (x, y);
5046 state.uioh <- state.uioh#pmotion x y
5047 method leave = state.mpos <- (-1, -1)
5048 method winstate wsl = state.winstate <- wsl
5049 method quit : 'a. 'a = raise Quit
5050 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5051 method zoom z x y = state.uioh#zoom z x y
5052 method opendoc path =
5053 state.mode <- View;
5054 state.uioh <- uioh;
5055 postRedisplay "opendoc";
5056 opendoc path state.password
5059 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5060 state.wsfd <- wsfd;
5062 if not @@ List.exists GlMisc.check_extension
5063 [ "GL_ARB_texture_rectangle"
5064 ; "GL_EXT_texture_recangle"
5065 ; "GL_NV_texture_rectangle" ]
5066 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5068 let cs, ss =
5069 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5070 | exception exn ->
5071 dolog "socketpair failed: %s" @@ exntos exn;
5072 exit 1
5073 | (r, w) ->
5074 cloexec r;
5075 cloexec w;
5076 r, w
5079 setcheckers conf.checkers;
5080 begin match !csspath with
5081 | None -> ()
5082 | Some "" -> conf.css <- E.s
5083 | Some path ->
5084 let css = filecontents path in
5085 let l = String.length css in
5086 conf.css <-
5087 if substratis css (l-2) "\r\n"
5088 then String.sub css 0 (l-2)
5089 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
5090 end;
5091 Ffi.init cs (
5092 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5093 conf.texcount, conf.sliceheight, conf.mustoresize,
5094 conf.colorspace, !Config.fontpath
5096 List.iter GlArray.enable [`texture_coord; `vertex];
5097 GlTex.env (`color conf.texturecolor);
5098 state.ss <- ss;
5099 reshape ~firsttime:true winw winh;
5100 state.uioh <- uioh;
5101 if histmode
5102 then (
5103 Wsi.settitle "llpp (history)";
5104 enterhistmode ();
5106 else (
5107 state.text <- "Opening " ^ (mbtoutf8 state.path);
5108 opendoc state.path state.password;
5110 display ();
5111 Wsi.mapwin ();
5112 Wsi.setcursor Wsi.CURSOR_INHERIT;
5113 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5115 let rec reap () =
5116 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5117 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5118 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5119 | 0, _ -> ()
5120 | _pid, _status -> reap ()
5122 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5124 let optrfd =
5125 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
5128 let rec loop deadline =
5129 if !doreap
5130 then (
5131 doreap := false;
5132 reap ()
5134 let r = [state.ss; state.wsfd] in
5135 let r =
5136 match !optrfd with
5137 | None -> r
5138 | Some fd -> fd :: r
5140 if !redisplay
5141 then (
5142 Glutils.redisplay := false;
5143 display ();
5145 let timeout =
5146 let now = now () in
5147 if deadline > now
5148 then (
5149 if deadline = infinity
5150 then ~-.1.0
5151 else max 0.0 (deadline -. now)
5153 else 0.0
5155 let r, _, _ =
5156 try Unix.select r [] [] timeout
5157 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5159 begin match r with
5160 | [] ->
5161 let newdeadline =
5162 match state.autoscroll with
5163 | Some step when step != 0 ->
5164 if state.slideshow land 1 = 1
5165 then (
5166 if state.slideshow land 2 = 0
5167 then state.slideshow <- state.slideshow lor 2
5168 else if step < 0 then prevpage () else nextpage ();
5169 deadline +. (float (abs step))
5171 else
5172 let y = state.y + step in
5173 let fy = if conf.maxhfit then state.winh else 0 in
5174 let y =
5175 if y < 0
5176 then state.maxy - fy
5177 else if y >= state.maxy - fy then 0 else y
5179 gotoxy state.x y;
5180 deadline +. 0.01
5181 | _ -> infinity
5183 loop newdeadline
5185 | l ->
5186 let rec checkfds = function
5187 | [] -> ()
5188 | fd :: rest when fd = state.ss ->
5189 let cmd = Ffi.rcmd state.ss in
5190 act cmd;
5191 checkfds rest
5193 | fd :: rest when fd = state.wsfd ->
5194 Wsi.readresp fd;
5195 checkfds rest
5197 | fd :: rest when Some fd = !optrfd ->
5198 begin match remote fd with
5199 | None -> optrfd := remoteopen !rcmdpath;
5200 | opt -> optrfd := opt
5201 end;
5202 checkfds rest
5204 | _ :: rest ->
5205 dolog "select returned unknown descriptor";
5206 checkfds rest
5208 checkfds l;
5209 let newdeadline =
5210 let deadline1 =
5211 if deadline = infinity
5212 then now () +. 0.01
5213 else deadline
5215 match state.autoscroll with
5216 | Some step when step != 0 -> deadline1
5217 | _ -> infinity
5219 loop newdeadline
5220 end;
5222 match loop infinity with
5223 | exception Quit ->
5224 Config.save leavebirdseye;
5225 if Ffi.hasunsavedchanges ()
5226 then save ()
5227 | _ -> error "umpossible - infinity reached"