Consistency
[llpp.git] / main.ml
blob70b3057f263630b1d3430cf03c0f0dd11917eeb3
1 open Utils;;
2 open Config;;
3 open Glutils;;
4 open Listview;;
6 external init : Unix.file_descr -> initparams -> unit = "ml_init";;
7 external seltext : opaque -> (int * int * int * int) -> unit = "ml_seltext";;
8 external hassel : opaque -> bool = "ml_hassel";;
9 external getpdimrect : int -> float array = "ml_getpdimrect";;
10 external whatsunder : opaque -> int -> int -> under = "ml_whatsunder";;
11 external markunder : opaque -> int -> int -> mark -> bool = "ml_markunder";;
12 external clearmark : opaque -> unit = "ml_clearmark";;
13 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
14 external getmaxw : unit -> float = "ml_getmaxw";;
15 external postprocess :
16 opaque -> int -> int -> int -> (int * string * int) -> int
17 = "ml_postprocess";;
18 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
19 external setaalevel : int -> unit = "ml_setaalevel";;
20 external realloctexts : int -> bool = "ml_realloctexts";;
21 external findlink : opaque -> linkdir -> link = "ml_findlink";;
22 external getlink : opaque -> int -> under = "ml_getlink";;
23 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
24 external getlinkcount : opaque -> int = "ml_getlinkcount";;
25 external findpwl : int -> int -> pagewithlinks = "ml_find_page_with_links";;
26 external getpbo : width -> height -> colorspace -> opaque = "ml_getpbo";;
27 external freepbo : opaque -> unit = "ml_freepbo";;
28 external unmappbo : opaque -> unit = "ml_unmappbo";;
29 external bousable : unit -> bool = "ml_bo_usable";;
30 external unproject :
31 opaque -> int -> int -> (int * int) option = "ml_unproject";;
32 external project :
33 opaque -> int -> int -> float -> float -> (float * float) = "ml_project";;
34 external drawtile :
35 tileparams -> opaque -> unit = "ml_drawtile";;
36 external rectofblock :
37 opaque -> int -> int -> float array option = "ml_rectofblock";;
38 external begintiles : unit -> unit = "ml_begintiles";;
39 external endtiles : unit -> unit = "ml_endtiles";;
40 external addannot : opaque -> int -> int -> string -> unit = "ml_addannot";;
41 external modannot : opaque -> slinkindex -> string -> unit = "ml_modannot";;
42 external delannot : opaque -> slinkindex -> unit = "ml_delannot";;
43 external hasunsavedchanges : unit -> bool = "ml_hasunsavedchanges";;
44 external savedoc : string -> unit = "ml_savedoc";;
45 external getannotcontents :
46 opaque -> slinkindex -> string = "ml_getannotcontents";;
47 external drawprect :
48 opaque -> int -> int -> float array -> unit = "ml_drawprect";;
49 external wcmd : Unix.file_descr -> bytes -> int -> unit = "ml_wcmd";;
50 external rcmd : Unix.file_descr -> string = "ml_rcmd";;
51 external uritolocation :
52 string -> (pageno * float * float) = "ml_uritolocation";;
53 external isexternallink : string -> bool = "ml_isexternallink";;
55 (* copysel _will_ close the supplied descriptor *)
56 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
58 let selfexec = ref E.s;;
59 let ignoredoctitlte = ref false;;
60 let opengl_has_pbo = ref false;;
61 let layouth = ref ~-1;;
63 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
64 dolog {|rect {
65 x0,y0=(% f, % f)
66 x1,y1=(% f, % f)
67 x2,y2=(% f, % f)
68 x3,y3=(% f, % f)
69 }|} x0 y0 x1 y1 x2 y2 x3 y3;
72 let pgscale h = truncate (float h *. conf.pgscale);;
74 let hscrollh () =
75 if state.uioh#alwaysscrolly || ((conf.scrollb land scrollbhv != 0)
76 && (state.w > state.winw))
77 then conf.scrollbw
78 else 0
81 let setfontsize n =
82 fstate.fontsize <- n;
83 fstate.wwidth <- measurestr fstate.fontsize "w";
84 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
87 let vlog fmt =
88 if conf.verbose
89 then dolog fmt
90 else Printf.kprintf ignore fmt
93 let launchpath () =
94 if emptystr conf.pathlauncher
95 then dolog "%s" state.path
96 else (
97 let command =
98 Str.global_replace Utils.Re.percent state.path conf.pathlauncher in
99 match spawn command [] with
100 | _pid -> ()
101 | exception exn -> dolog "failed to execute `%s': %s" command @@ exntos exn
105 let getopaque pageno =
106 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
107 with Not_found -> None
110 let pagetranslatepoint l x y =
111 let dy = y - l.pagedispy in
112 let y = dy + l.pagey in
113 let dx = x - l.pagedispx in
114 let x = dx + l.pagex in
115 (x, y);
118 let onppundermouse g x y d =
119 let rec f = function
120 | l :: rest ->
121 begin match getopaque l.pageno with
122 | Some opaque ->
123 let x0 = l.pagedispx in
124 let x1 = x0 + l.pagevw in
125 let y0 = l.pagedispy in
126 let y1 = y0 + l.pagevh in
127 if y >= y0 && y <= y1 && x >= x0 && x <= x1
128 then
129 let px, py = pagetranslatepoint l x y in
130 match g opaque l px py with
131 | Some res -> res
132 | None -> f rest
133 else f rest
134 | _ -> f rest
136 | [] -> d
138 f state.layout
141 let getunder x y =
142 let g opaque l px py =
143 if state.bzoom
144 then (
145 match rectofblock opaque px py with
146 | Some [|x0;x1;y0;y1|] ->
147 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
148 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
149 state.rects <- [l.pageno, color, rect];
150 postRedisplay "getunder";
151 | _ -> ()
153 let under = whatsunder opaque px py in
154 if under = Unone then None else Some under
156 onppundermouse g x y Unone
159 let unproject x y =
160 let g opaque l x y =
161 match unproject opaque x y with
162 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
163 | None -> None
165 onppundermouse g x y None;
168 let showtext c s =
169 state.text <- Printf.sprintf "%c%s" c s;
170 postRedisplay "showtext";
173 let impmsg fmt = Format.ksprintf (fun s -> showtext '!' s) fmt;;
175 let pipesel opaque cmd =
176 if hassel opaque
177 then pipef ~closew:false "pipesel"
178 (fun w ->
179 copysel w opaque;
180 postRedisplay "pipesel"
181 ) cmd
184 let paxunder x y =
185 let g opaque l px py =
186 if markunder opaque px py conf.paxmark
187 then
188 Some (fun () ->
189 match getopaque l.pageno with
190 | None -> ()
191 | Some opaque -> pipesel opaque conf.paxcmd
193 else None
195 postRedisplay "paxunder";
196 if conf.paxmark = Mark_page
197 then
198 List.iter (fun l ->
199 match getopaque l.pageno with
200 | None -> ()
201 | Some opaque -> clearmark opaque) state.layout;
202 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
205 let undertext = function
206 | Unone -> "none"
207 | Ulinkuri s -> s
208 | Utext s -> "font: " ^ s
209 | Uannotation (opaque, slinkindex) ->
210 "annotation: " ^ getannotcontents opaque slinkindex
213 let updateunder x y =
214 match getunder x y with
215 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
216 | Ulinkuri uri ->
217 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
218 Wsi.setcursor Wsi.CURSOR_INFO
219 | Utext s ->
220 if conf.underinfo then showtext 'f' ("ont: " ^ s);
221 Wsi.setcursor Wsi.CURSOR_TEXT
222 | Uannotation _ ->
223 if conf.underinfo then showtext 'a' "nnotation";
224 Wsi.setcursor Wsi.CURSOR_INFO
227 let showlinktype under =
228 if conf.underinfo && under != Unone
229 then showtext ' ' @@ undertext under
232 let intentry_with_suffix text key =
233 let text =
234 match [@warning "-4"] key with
235 | Keys.Ascii ('0'..'9' as c) -> addchar text c
236 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
237 addchar text @@ asciilower c
238 | _ ->
239 state.text <- "invalid key";
240 text
242 TEcont text
245 let wcmd fmt =
246 let b = Buffer.create 16 in
247 Printf.kbprintf
248 (fun b ->
249 let b = Buffer.to_bytes b in
250 wcmd state.ss b @@ Bytes.length b
251 ) b fmt
254 let nogeomcmds cmds =
255 match cmds with
256 | s, [] -> emptystr s
257 | _ -> false
260 let layoutN ((columns, coverA, coverB), b) x y sw sh =
261 let rec fold accu n =
262 if n = Array.length b
263 then accu
264 else
265 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
266 if (vy - y) > sh &&
267 (n = coverA - 1
268 || n = state.pagecount - coverB
269 || (n - coverA) mod columns = columns - 1)
270 then accu
271 else
272 let accu =
273 if vy + h > y
274 then
275 let pagey = max 0 (y - vy) in
276 let pagedispy = if pagey > 0 then 0 else vy - y in
277 let pagedispx, pagex =
278 let pdx =
279 if n = coverA - 1 || n = state.pagecount - coverB
280 then x + (sw - w) / 2
281 else dx + xoff + x
283 if pdx < 0
284 then 0, -pdx
285 else pdx, 0
287 let pagevw =
288 let vw = sw - pagedispx in
289 let pw = w - pagex in
290 min vw pw
292 let pagevh = min (h - pagey) (sh - pagedispy) in
293 if pagevw > 0 && pagevh > 0
294 then
295 let e =
296 { pageno = n
297 ; pagedimno = pdimno
298 ; pagew = w
299 ; pageh = h
300 ; pagex = pagex
301 ; pagey = pagey
302 ; pagevw = pagevw
303 ; pagevh = pagevh
304 ; pagedispx = pagedispx
305 ; pagedispy = pagedispy
306 ; pagecol = 0
309 e :: accu
310 else accu
311 else accu
313 fold accu (n+1)
315 if Array.length b = 0
316 then []
317 else List.rev (fold [] (page_of_y y))
320 let layoutS (columns, b) x y sw sh =
321 let rec fold accu n =
322 if n = Array.length b
323 then accu
324 else
325 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
326 if (vy - y) > sh
327 then accu
328 else
329 let accu =
330 if vy + pageh > y
331 then
332 let x = xoff + x in
333 let pagey = max 0 (y - vy) in
334 let pagedispy = if pagey > 0 then 0 else vy - y in
335 let pagedispx, pagex =
336 if px = 0
337 then (
338 if x < 0
339 then 0, -x
340 else x, 0
342 else (
343 let px = px - x in
344 if px < 0
345 then -px, 0
346 else 0, px
349 let pagecolw = pagew/columns in
350 let pagedispx =
351 if pagecolw < sw
352 then pagedispx + ((sw - pagecolw) / 2)
353 else pagedispx
355 let pagevw =
356 let vw = sw - pagedispx in
357 let pw = pagew - pagex in
358 min vw pw
360 let pagevw = min pagevw pagecolw in
361 let pagevh = min (pageh - pagey) (sh - pagedispy) in
362 if pagevw > 0 && pagevh > 0
363 then
364 let e =
365 { pageno = n/columns
366 ; pagedimno = pdimno
367 ; pagew = pagew
368 ; pageh = pageh
369 ; pagex = pagex
370 ; pagey = pagey
371 ; pagevw = pagevw
372 ; pagevh = pagevh
373 ; pagedispx = pagedispx
374 ; pagedispy = pagedispy
375 ; pagecol = n mod columns
378 e :: accu
379 else accu
380 else accu
382 fold accu (n+1)
384 List.rev (fold [] 0)
387 let layout x y sw sh =
388 if nogeomcmds state.geomcmds
389 then
390 match conf.columns with
391 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
392 | Cmulti c -> layoutN c x y sw sh
393 | Csplit s -> layoutS s x y sw sh
394 else []
397 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
398 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
400 let itertiles l f =
401 let tilex = l.pagex mod conf.tilew in
402 let tiley = l.pagey mod conf.tileh in
404 let col = l.pagex / conf.tilew in
405 let row = l.pagey / conf.tileh in
407 let rec rowloop row y0 dispy h =
408 if h = 0
409 then ()
410 else (
411 let dh = conf.tileh - y0 in
412 let dh = min h dh in
413 let rec colloop col x0 dispx w =
414 if w != 0
415 then
416 let dw = conf.tilew - x0 in
417 let dw = min w dw in
418 f col row dispx dispy x0 y0 dw dh;
419 colloop (col+1) 0 (dispx+dw) (w-dw)
421 colloop col tilex l.pagedispx l.pagevw;
422 rowloop (row+1) 0 (dispy+dh) (h-dh)
425 if l.pagevw > 0 && l.pagevh > 0
426 then rowloop row tiley l.pagedispy l.pagevh;
429 let gettileopaque l col row =
430 let key = l.pageno, state.gen, conf.colorspace,
431 conf.angle, l.pagew, l.pageh, col, row in
432 try Some (Hashtbl.find state.tilemap key)
433 with Not_found -> None
436 let puttileopaque l col row gen colorspace angle opaque size elapsed =
437 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
438 Hashtbl.add state.tilemap key (opaque, size, elapsed)
441 let drawtiles l color =
442 GlDraw.color color;
443 begintiles ();
444 let f col row x y tilex tiley w h =
445 match gettileopaque l col row with
446 | Some (opaque, _, t) ->
447 let params = x, y, w, h, tilex, tiley in
448 if conf.invert
449 then GlTex.env (`mode `blend);
450 drawtile params opaque;
451 if conf.invert
452 then GlTex.env (`mode `modulate);
453 if conf.debug
454 then (
455 endtiles ();
456 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
457 let w = measurestr fstate.fontsize s in
458 GlDraw.color (0.0, 0.0, 0.0);
459 filledrect
460 (float (x-2))
461 (float (y-2))
462 (float (x+2) +. w)
463 (float (y + fstate.fontsize + 2));
464 GlDraw.color color;
465 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
466 begintiles ();
469 | None ->
470 endtiles ();
471 let w = let lw = state.winw - x in min lw w
472 and h = let lh = state.winh - y in min lh h
474 if conf.invert
475 then GlTex.env (`mode `blend);
476 begin match state.checkerstexid with
477 | Some id ->
478 Gl.enable `texture_2d;
479 GlTex.bind_texture ~target:`texture_2d id;
480 let x0 = float x
481 and y0 = float y
482 and x1 = float (x+w)
483 and y1 = float (y+h) in
485 let tw = float w /. 16.0
486 and th = float h /. 16.0 in
487 let tx0 = float tilex /. 16.0
488 and ty0 = float tiley /. 16.0 in
489 let tx1 = tx0 +. tw
490 and ty1 = ty0 +. th in
491 Raw.sets_float Glutils.vraw ~pos:0
492 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
493 Raw.sets_float Glutils.traw ~pos:0
494 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
495 GlArray.vertex `two Glutils.vraw;
496 GlArray.tex_coord `two Glutils.traw;
497 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
498 Gl.disable `texture_2d;
500 | None ->
501 GlDraw.color (1.0, 1.0, 1.0);
502 filledrect (float x) (float y) (float (x+w)) (float (y+h));
503 end;
504 if conf.invert
505 then GlTex.env (`mode `modulate);
506 if w > 128 && h > fstate.fontsize + 10
507 then (
508 let c = if conf.invert then 1.0 else 0.0 in
509 GlDraw.color (c, c, c);
510 let c, r =
511 if conf.verbose
512 then (col*conf.tilew, row*conf.tileh)
513 else col, row
515 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
517 GlDraw.color color;
518 begintiles ();
520 itertiles l f;
521 endtiles ();
524 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
526 let tilevisible1 l x y =
527 let ax0 = l.pagex
528 and ax1 = l.pagex + l.pagevw
529 and ay0 = l.pagey
530 and ay1 = l.pagey + l.pagevh in
532 let bx0 = x
533 and by0 = y in
534 let bx1 = min (bx0 + conf.tilew) l.pagew
535 and by1 = min (by0 + conf.tileh) l.pageh in
537 let rx0 = max ax0 bx0
538 and ry0 = max ay0 by0
539 and rx1 = min ax1 bx1
540 and ry1 = min ay1 by1 in
542 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
543 nonemptyintersection
546 let tilevisible layout n x y =
547 let rec findpageinlayout m = function
548 | l :: rest when l.pageno = n ->
549 tilevisible1 l x y || (
550 match conf.columns with
551 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
552 | Csplit _ | Csingle _ | Cmulti _ -> false
554 | _ :: rest -> findpageinlayout 0 rest
555 | [] -> false
557 findpageinlayout 0 layout;
560 let tileready l x y =
561 tilevisible1 l x y &&
562 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
565 let tilepage n p layout =
566 let rec loop = function
567 | l :: rest ->
568 if l.pageno = n
569 then
570 let f col row _ _ _ _ _ _ =
571 if state.currently = Idle
572 then
573 match gettileopaque l col row with
574 | Some _ -> ()
575 | None ->
576 let x = col*conf.tilew
577 and y = row*conf.tileh in
578 let w =
579 let w = l.pagew - x in
580 min w conf.tilew
582 let h =
583 let h = l.pageh - y in
584 min h conf.tileh
586 let pbo =
587 if conf.usepbo
588 then getpbo w h conf.colorspace
589 else ~< "0"
591 wcmd "tile %s %d %d %d %d %s" (~> p) x y w h (~> pbo);
592 state.currently <-
593 Tiling (
594 l, p, conf.colorspace, conf.angle,
595 state.gen, col, row, conf.tilew, conf.tileh
598 itertiles l f;
599 else
600 loop rest
602 | [] -> ()
604 if nogeomcmds state.geomcmds
605 then loop layout;
608 let preloadlayout x y sw sh =
609 let y = if y < sh then 0 else y - sh in
610 let x = min 0 (x + sw) in
611 let h = sh*3 in
612 let w = sw*3 in
613 layout x y w h;
616 let load pages =
617 let rec loop pages =
618 if state.currently = Idle
619 then
620 match pages with
621 | l :: rest ->
622 begin match getopaque l.pageno with
623 | None ->
624 wcmd "page %d %d" l.pageno l.pagedimno;
625 state.currently <- Loading (l, state.gen);
626 | Some opaque ->
627 tilepage l.pageno opaque pages;
628 loop rest
629 end;
630 | _ -> ()
632 if nogeomcmds state.geomcmds
633 then loop pages
636 let preload pages =
637 load pages;
638 if conf.preload && state.currently = Idle
639 then load (preloadlayout state.x state.y state.winw state.winh);
642 let layoutready layout =
643 let rec fold all ls =
644 all && match ls with
645 | l :: rest ->
646 let seen = ref false in
647 let allvisible = ref true in
648 let foo col row _ _ _ _ _ _ =
649 seen := true;
650 allvisible := !allvisible &&
651 begin match gettileopaque l col row with
652 | Some _ -> true
653 | None -> false
656 itertiles l foo;
657 fold (!seen && !allvisible) rest
658 | [] -> true
660 let alltilesvisible = fold true layout in
661 alltilesvisible;
664 let gotoxy x y =
665 let y = bound y 0 state.maxy in
666 let y, layout =
667 let layout = layout x y state.winw state.winh in
668 postRedisplay "gotoxy ready";
669 y, layout
671 state.x <- x;
672 state.y <- y;
673 state.layout <- layout;
674 begin match state.mode with
675 | LinkNav ln ->
676 begin match ln with
677 | Ltexact (pageno, linkno) ->
678 let rec loop = function
679 | [] ->
680 state.lnava <- Some (pageno, linkno);
681 state.mode <- LinkNav (Ltgendir 0)
682 | l :: _ when l.pageno = pageno ->
683 begin match getopaque pageno with
684 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
685 | Some opaque ->
686 let x0, y0, x1, y1 = getlinkrect opaque linkno in
687 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
688 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
689 then state.mode <- LinkNav (Ltgendir 0)
691 | _ :: rest -> loop rest
693 loop layout
694 | Ltnotready _ | Ltgendir _ -> ()
696 | Birdseye _ | Textentry _ | View -> ()
697 end;
698 begin match state.mode with
699 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
700 if not (pagevisible layout pageno)
701 then (
702 match state.layout with
703 | [] -> ()
704 | l :: _ ->
705 state.mode <- Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
707 | LinkNav lt ->
708 begin match lt with
709 | Ltnotready (_, dir)
710 | Ltgendir dir ->
711 let linknav =
712 let rec loop = function
713 | [] -> lt
714 | l :: rest ->
715 match getopaque l.pageno with
716 | None -> Ltnotready (l.pageno, dir)
717 | Some opaque ->
718 let link =
719 let ld =
720 if dir = 0
721 then LDfirstvisible (l.pagex, l.pagey, dir)
722 else (
723 if dir > 0 then LDfirst else LDlast
726 findlink opaque ld
728 match link with
729 | Lnotfound -> loop rest
730 | Lfound n ->
731 showlinktype (getlink opaque n);
732 Ltexact (l.pageno, n)
734 loop state.layout
736 state.mode <- LinkNav linknav
737 | Ltexact _ -> ()
739 | Textentry _ | View -> ()
740 end;
741 preload layout;
742 if conf.updatecurs
743 then (
744 let mx, my = state.mpos in
745 updateunder mx my;
749 let conttiling pageno opaque =
750 tilepage pageno opaque
751 (if conf.preload
752 then preloadlayout state.x state.y state.winw state.winh
753 else state.layout)
756 let gotoxy x y =
757 if not conf.verbose then state.text <- E.s;
758 gotoxy x y;
761 let getanchory (n, top, dtop) =
762 let y, h = getpageyh n in
763 if conf.presentation
764 then
765 let ips = calcips h in
766 y + truncate (top*.float h -. dtop*.float ips) + ips;
767 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
770 let gotoanchor anchor = gotoxy state.x (getanchory anchor);;
771 let addnav () = getanchor () |> cbput state.hists.nav;;
772 let addnavnorc () = getanchor () |> cbput_dont_update_rc state.hists.nav;;
774 let getnav dir =
775 let anchor = cbgetc state.hists.nav dir in
776 getanchory anchor;
779 let gotopage n top =
780 let y, h = getpageyh n in
781 let y = y + (truncate (top *. float h)) in
782 gotoxy state.x y
785 let gotopage1 n top =
786 let y = getpagey n in
787 let y = y + top in
788 gotoxy state.x y
791 let invalidate s f =
792 Glutils.redisplay := false;
793 state.layout <- [];
794 state.pdims <- [];
795 state.rects <- [];
796 state.rects1 <- [];
797 match state.geomcmds with
798 | ps, [] when emptystr ps ->
799 f ();
800 state.geomcmds <- s, [];
801 | ps, [] -> state.geomcmds <- ps, [s, f];
802 | ps, (s', _) :: rest when s' = s -> state.geomcmds <- ps, ((s, f) :: rest);
803 | ps, cmds -> state.geomcmds <- ps, ((s, f) :: cmds);
806 let flushpages () =
807 Hashtbl.iter (fun _ opaque -> wcmd "freepage %s" (~> opaque)) state.pagemap;
808 Hashtbl.clear state.pagemap;
811 let flushtiles () =
812 if not (Queue.is_empty state.tilelru)
813 then (
814 Queue.iter (fun (k, p, s) ->
815 wcmd "freetile %s" (~> p);
816 state.memused <- state.memused - s;
817 Hashtbl.remove state.tilemap k;
818 ) state.tilelru;
819 state.uioh#infochanged Memused;
820 Queue.clear state.tilelru;
822 load state.layout;
825 let stateh h =
826 let h = truncate (float h*.conf.zoom) in
827 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
828 h - d
831 let fillhelp () =
832 state.help <-
833 let sl = keystostrlist conf in
834 let rec loop accu =
835 function | [] -> accu
836 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
837 in Help.makehelp conf.urilauncher
838 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
841 let opendoc path password =
842 state.path <- path;
843 state.password <- password;
844 state.gen <- state.gen + 1;
845 state.docinfo <- [];
846 state.outlines <- [||];
848 flushpages ();
849 setaalevel conf.aalevel;
850 let titlepath =
851 if emptystr state.origin
852 then path
853 else state.origin
855 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
856 wcmd "open %d %d %s\000%s\000%s\000"
857 (btod conf.usedoccss) !layouth
858 path password conf.css;
859 invalidate "reqlayout"
860 (fun () ->
861 wcmd "reqlayout %d %d %d %s\000"
862 conf.angle (FMTE.to_int conf.fitmodel)
863 (stateh state.winh) state.nameddest
865 fillhelp ();
868 let reload () =
869 state.anchor <- getanchor ();
870 opendoc state.path state.password;
873 let scalecolor c =
874 let c = c *. conf.colorscale in
875 (c, c, c);
878 let scalecolor2 (r, g, b) =
879 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
882 let docolumns columns =
883 match columns with
884 | Csingle _ ->
885 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
886 let rec loop pageno pdimno pdim y ph pdims =
887 if pageno = state.pagecount
888 then ()
889 else
890 let pdimno, ((_, w, h, xoff) as pdim), pdims =
891 match pdims with
892 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
893 pdimno+1, pdim, rest
894 | _ ->
895 pdimno, pdim, pdims
897 let x = max 0 (((state.winw - w) / 2) - xoff) in
898 let y =
899 y + (if conf.presentation
900 then (if pageno = 0 then calcips h else calcips ph + calcips h)
901 else (if pageno = 0 then 0 else conf.interpagespace))
903 a.(pageno) <- (pdimno, x, y, pdim);
904 loop (pageno+1) pdimno pdim (y + h) h pdims
906 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
907 conf.columns <- Csingle a;
909 | Cmulti ((columns, coverA, coverB), _) ->
910 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
911 let rec loop pageno pdimno pdim x y rowh pdims =
912 let rec fixrow m =
913 if m = pageno then () else
914 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
915 if h < rowh
916 then (
917 let y = y + (rowh - h) / 2 in
918 a.(m) <- (pdimno, x, y, pdim);
920 fixrow (m+1)
922 if pageno = state.pagecount
923 then fixrow (((pageno - 1) / columns) * columns)
924 else
925 let pdimno, ((_, w, h, xoff) as pdim), pdims =
926 match pdims with
927 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
928 pdimno+1, pdim, rest
929 | _ -> pdimno, pdim, pdims
931 let x, y, rowh' =
932 if pageno = coverA - 1 || pageno = state.pagecount - coverB
933 then (
934 let x = (state.winw - w) / 2 in
935 let ips =
936 if conf.presentation then calcips h else conf.interpagespace in
937 x, y + ips + rowh, h
939 else (
940 if (pageno - coverA) mod columns = 0
941 then (
942 let x = max 0 (state.winw - state.w) / 2 in
943 let y =
944 if conf.presentation
945 then
946 let ips = calcips h in
947 y + (if pageno = 0 then 0 else calcips rowh + ips)
948 else
949 y + (if pageno = 0 then 0 else conf.interpagespace)
951 x, y + rowh, h
953 else x, y, max rowh h
956 let y =
957 if pageno > 1 && (pageno - coverA) mod columns = 0
958 then (
959 let y =
960 if pageno = columns && conf.presentation
961 then (
962 let ips = calcips rowh in
963 for i = 0 to pred columns
965 let (pdimno, x, y, pdim) = a.(i) in
966 a.(i) <- (pdimno, x, y+ips, pdim)
967 done;
968 y+ips;
970 else y
972 fixrow (pageno - columns);
975 else y
977 a.(pageno) <- (pdimno, x, y, pdim);
978 let x = x + w + xoff*2 + conf.interpagespace in
979 loop (pageno+1) pdimno pdim x y rowh' pdims
981 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
982 conf.columns <- Cmulti ((columns, coverA, coverB), a);
984 | Csplit (c, _) ->
985 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
986 let rec loop pageno pdimno pdim y pdims =
987 if pageno != state.pagecount
988 then
989 let pdimno, ((_, w, h, _) as pdim), pdims =
990 match pdims with
991 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
992 pdimno+1, pdim, rest
993 | _ -> pdimno, pdim, pdims
995 let cw = w / c in
996 let rec loop1 n x y =
997 if n = c then y else (
998 a.(pageno*c + n) <- (pdimno, x, y, pdim);
999 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1002 let y = loop1 0 0 y in
1003 loop (pageno+1) pdimno pdim y pdims
1005 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1006 conf.columns <- Csplit (c, a);
1009 let represent () =
1010 docolumns conf.columns;
1011 state.maxy <- calcheight ();
1012 if state.reprf == noreprf
1013 then (
1014 match state.mode with
1015 | Birdseye (_, _, pageno, _, _) ->
1016 let y, h = getpageyh pageno in
1017 let top = (state.winh - h) / 2 in
1018 gotoxy state.x (max 0 (y - top))
1019 | Textentry _ | View | LinkNav _ ->
1020 let y = getanchory state.anchor in
1021 let y = min y (state.maxy - state.winh) in
1022 gotoxy state.x y;
1024 else (
1025 state.reprf ();
1026 state.reprf <- noreprf;
1030 let reshape ?(firsttime=false) w h =
1031 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
1032 if not firsttime && nogeomcmds state.geomcmds
1033 then state.anchor <- getanchor ();
1035 state.winw <- w;
1036 let w = truncate (float w *. conf.zoom) in
1037 let w = max w 2 in
1038 state.winh <- h;
1039 setfontsize fstate.fontsize;
1040 GlMat.mode `modelview;
1041 GlMat.load_identity ();
1043 GlMat.mode `projection;
1044 GlMat.load_identity ();
1045 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1046 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1047 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
1049 let relx =
1050 if conf.zoom <= 1.0
1051 then 0.0
1052 else float state.x /. float state.w
1054 invalidate "geometry"
1055 (fun () ->
1056 state.w <- w;
1057 if not firsttime
1058 then state.x <- truncate (relx *. float w);
1059 let w =
1060 match conf.columns with
1061 | Csingle _ -> w
1062 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1063 | Csplit (c, _) -> w * c
1065 wcmd "geometry %d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
1069 let gctiles () =
1070 let len = Queue.length state.tilelru in
1071 let layout = lazy (if conf.preload
1072 then preloadlayout state.x state.y state.winw state.winh
1073 else state.layout) in
1074 let rec loop qpos =
1075 if state.memused > conf.memlimit
1076 then (
1077 if qpos < len
1078 then
1079 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1080 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1081 let (_, pw, ph, _) = getpagedim n in
1082 if gen = state.gen
1083 && colorspace = conf.colorspace
1084 && angle = conf.angle
1085 && pagew = pw
1086 && pageh = ph
1087 && (
1088 let x = col*conf.tilew and y = row*conf.tileh in
1089 tilevisible (Lazy.force_val layout) n x y
1091 then Queue.push lruitem state.tilelru
1092 else (
1093 freepbo p;
1094 wcmd "freetile %s" (~> p);
1095 state.memused <- state.memused - s;
1096 state.uioh#infochanged Memused;
1097 Hashtbl.remove state.tilemap k;
1099 loop (qpos+1)
1102 loop 0
1105 let onpagerect pageno f =
1106 let b =
1107 match conf.columns with
1108 | Cmulti (_, b) -> b
1109 | Csingle b -> b
1110 | Csplit (_, b) -> b
1112 if pageno >= 0 && pageno < Array.length b
1113 then
1114 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1115 f w h
1118 let gotopagexy1 pageno x y =
1119 let _,w1,h1,leftx = getpagedim pageno in
1120 let top = y /. (float h1) in
1121 let left = x /. (float w1) in
1122 let py, w, h = getpageywh pageno in
1123 let wh = state.winh in
1124 let x = left *. (float w) in
1125 let x = leftx + state.x + truncate x in
1126 let sx =
1127 if x < 0 || x >= state.winw
1128 then state.x - x
1129 else state.x
1131 let pdy = truncate (top *. float h) in
1132 let y' = py + pdy in
1133 let dy = y' - state.y in
1134 let sy =
1135 if x != state.x || not (dy > 0 && dy < wh)
1136 then (
1137 if conf.presentation
1138 then
1139 if abs (py - y') > wh
1140 then y'
1141 else py
1142 else y';
1144 else state.y
1146 if state.x != sx || state.y != sy
1147 then gotoxy sx sy
1148 else gotoxy state.x state.y;
1151 let gotopagexy pageno x y =
1152 match state.mode with
1153 | Birdseye _ -> gotopage pageno 0.0
1154 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1157 let getpassword () =
1158 let passcmd = getenvwithdef "LLPP_ASKPASS" conf.passcmd in
1159 if emptystr passcmd
1160 then E.s
1161 else getcmdoutput
1162 (fun s ->
1163 impmsg "error getting password: %s" s;
1164 dolog "%s" s) passcmd;
1167 let pgoto opaque pageno x y =
1168 let pdimno = getpdimno pageno in
1169 let x, y = project opaque pageno pdimno x y in
1170 gotopagexy pageno x y;
1173 let act cmds =
1174 (* dolog "%S" cmds; *)
1175 let spl = splitatchar cmds ' ' in
1176 let scan s fmt f =
1177 try Scanf.sscanf s fmt f
1178 with exn ->
1179 dolog "error processing '%S': %s" cmds @@ exntos exn;
1180 exit 1
1182 let addoutline outline =
1183 match state.currently with
1184 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1185 | Idle -> state.currently <- Outlining [outline]
1186 | Loading _ | Tiling _ ->
1187 dolog "invalid outlining state";
1188 logcurrently state.currently
1190 match spl with
1191 | "clear", "" ->
1192 state.pdims <- [];
1193 state.uioh#infochanged Pdim;
1195 | "clearrects", "" ->
1196 state.rects <- state.rects1;
1197 postRedisplay "clearrects";
1199 | "continue", args ->
1200 let n = scan args "%u" (fun n -> n) in
1201 state.pagecount <- n;
1202 begin match state.currently with
1203 | Outlining l ->
1204 state.currently <- Idle;
1205 state.outlines <- Array.of_list (List.rev l)
1206 | Idle | Loading _ | Tiling _ -> ()
1207 end;
1209 let cur, cmds = state.geomcmds in
1210 if emptystr cur
1211 then error "empty geomcmd";
1213 begin match List.rev cmds with
1214 | [] ->
1215 state.geomcmds <- E.s, [];
1216 represent ();
1217 | (s, f) :: rest ->
1218 f ();
1219 state.geomcmds <- s, List.rev rest;
1220 end;
1221 postRedisplay "continue";
1223 | "msg", args ->
1224 showtext ' ' args
1226 | "vmsg", args ->
1227 if conf.verbose
1228 then showtext ' ' args
1230 | "emsg", args ->
1231 Buffer.add_string state.errmsgs args;
1232 state.newerrmsgs <- true;
1233 postRedisplay "error message"
1235 | "progress", args ->
1236 let progress, text =
1237 scan args "%f %n"
1238 (fun f pos ->
1239 f, String.sub args pos (String.length args - pos))
1241 state.text <- text;
1242 state.progress <- progress;
1243 postRedisplay "progress"
1245 | "firstmatch", args ->
1246 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1247 scan args "%u %d %f %f %f %f %f %f %f %f"
1248 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1249 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1251 let y = (getpagey pageno) + truncate y0 in
1252 let x =
1253 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1254 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1255 else state.x
1257 addnav ();
1258 gotoxy x y;
1259 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1260 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1262 | "match", args ->
1263 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1264 scan args "%u %d %f %f %f %f %f %f %f %f"
1265 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1266 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1268 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1269 state.rects1 <-
1270 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1272 | "page", args ->
1273 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1274 let pageopaque = ~< pageopaques in
1275 begin match state.currently with
1276 | Loading (l, gen) ->
1277 vlog "page %d took %f sec" l.pageno t;
1278 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1279 let preloadedpages =
1280 if conf.preload
1281 then preloadlayout state.x state.y state.winw state.winh
1282 else state.layout
1284 let evict () =
1285 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1286 IntSet.empty preloadedpages
1288 let evictedpages =
1289 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1290 if not (IntSet.mem pageno set)
1291 then (
1292 wcmd "freepage %s" (~> opaque);
1293 key :: accu
1295 else accu
1296 ) state.pagemap []
1298 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1300 evict ();
1301 state.currently <- Idle;
1302 if gen = state.gen
1303 then (
1304 tilepage l.pageno pageopaque state.layout;
1305 load state.layout;
1306 load preloadedpages;
1307 let visible = pagevisible state.layout l.pageno in
1308 if visible
1309 then (
1310 match state.mode with
1311 | LinkNav (Ltnotready (pageno, dir)) ->
1312 if pageno = l.pageno
1313 then (
1314 let link =
1315 let ld =
1316 if dir = 0
1317 then LDfirstvisible (l.pagex, l.pagey, dir)
1318 else (
1319 if dir > 0 then LDfirst else LDlast
1322 findlink pageopaque ld
1324 match link with
1325 | Lnotfound -> ()
1326 | Lfound n ->
1327 showlinktype (getlink pageopaque n);
1328 state.mode <- LinkNav (Ltexact (l.pageno, n))
1330 | LinkNav (Ltgendir _)
1331 | LinkNav (Ltexact _)
1332 | View
1333 | Birdseye _
1334 | Textentry _ -> ()
1337 if visible && layoutready state.layout
1338 then (
1339 postRedisplay "page";
1343 | Idle | Tiling _ | Outlining _ ->
1344 dolog "Inconsistent loading state";
1345 logcurrently state.currently;
1346 exit 1
1349 | "tile" , args ->
1350 let (x, y, opaques, size, t) =
1351 scan args "%u %u %s %u %f"
1352 (fun x y p size t -> (x, y, p, size, t))
1354 let opaque = ~< opaques in
1355 begin match state.currently with
1356 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1357 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1359 unmappbo opaque;
1360 if tilew != conf.tilew || tileh != conf.tileh
1361 then (
1362 wcmd "freetile %s" (~> opaque);
1363 state.currently <- Idle;
1364 load state.layout;
1366 else (
1367 puttileopaque l col row gen cs angle opaque size t;
1368 state.memused <- state.memused + size;
1369 state.uioh#infochanged Memused;
1370 gctiles ();
1371 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1372 opaque, size) state.tilelru;
1374 state.currently <- Idle;
1375 if gen = state.gen
1376 && conf.colorspace = cs
1377 && conf.angle = angle
1378 && tilevisible state.layout l.pageno x y
1379 then conttiling l.pageno pageopaque;
1381 preload state.layout;
1382 if gen = state.gen
1383 && conf.colorspace = cs
1384 && conf.angle = angle
1385 && tilevisible state.layout l.pageno x y
1386 && layoutready state.layout
1387 then postRedisplay "tile nothrottle";
1390 | Idle | Loading _ | Outlining _ ->
1391 dolog "Inconsistent tiling state";
1392 logcurrently state.currently;
1393 exit 1
1396 | "pdim", args ->
1397 let (n, w, h, _) as pdim =
1398 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1400 let pdim =
1401 match conf.fitmodel with
1402 | FitWidth -> pdim
1403 | FitPage | FitProportional ->
1404 match conf.columns with
1405 | Csplit _ -> (n, w, h, 0)
1406 | Csingle _ | Cmulti _ -> pdim
1408 state.pdims <- pdim :: state.pdims;
1409 state.uioh#infochanged Pdim
1411 | "o", args ->
1412 let (l, n, t, h, pos) =
1413 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1415 let s = String.sub args pos (String.length args - pos) in
1416 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1418 | "ou", args ->
1419 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1420 let s = String.sub args pos len in
1421 let pos2 = pos + len + 1 in
1422 let uri = String.sub args pos2 (String.length args - pos2) in
1423 addoutline (s, l, Ouri uri)
1425 | "on", args ->
1426 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1427 let s = String.sub args pos (String.length args - pos) in
1428 addoutline (s, l, Onone)
1430 | "a", args ->
1431 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1432 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1434 | "info", args ->
1435 let c, v = splitatchar args '\t' in
1436 let s =
1437 if nonemptystr v
1438 then
1439 if c = "Title"
1440 then (
1441 conf.title <- v;
1442 if not !ignoredoctitlte
1443 then Wsi.settitle v;
1444 args
1446 else
1447 if let len = String.length c in
1448 len > 6 && ((String.sub c (len-4) 4) = "date")
1449 then (
1450 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1451 then
1452 let b = Buffer.create 10 in
1453 Printf.bprintf b "%s\t" c;
1454 let sub p l c =
1456 Buffer.add_substring b v p l;
1457 Buffer.add_char b c;
1458 with exn -> Buffer.add_string b @@ exntos exn
1460 sub 2 4 '/';
1461 sub 6 2 '/';
1462 sub 8 2 ' ';
1463 sub 10 2 ':';
1464 sub 12 2 ':';
1465 sub 14 2 ' ';
1466 Buffer.add_char b '[';
1467 Buffer.add_string b v;
1468 Buffer.add_char b ']';
1469 Buffer.contents b
1470 else args
1472 else args
1473 else args
1475 state.docinfo <- (1, s) :: state.docinfo
1477 | "infoend", "" ->
1478 state.docinfo <- List.rev state.docinfo;
1479 state.uioh#infochanged Docinfo
1481 | "pass", args ->
1482 if args = "fail"
1483 then Wsi.settitle "Wrong password";
1484 let password = getpassword () in
1485 if emptystr password
1486 then error "document is password protected"
1487 else opendoc state.path password
1489 | _ -> error "unknown cmd `%S'" cmds
1492 let onhist cb =
1493 let rc = cb.rc in
1494 let action = function
1495 | HCprev -> cbget cb ~-1
1496 | HCnext -> cbget cb 1
1497 | HCfirst -> cbget cb ~-(cb.rc)
1498 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1499 and cancel () = cb.rc <- rc
1500 in (action, cancel)
1503 let search pattern forward =
1504 match conf.columns with
1505 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1506 | Csingle _ | Cmulti _ ->
1507 if nonemptystr pattern
1508 then
1509 let pn, py =
1510 match state.layout with
1511 | [] -> 0, 0
1512 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1514 wcmd "search %d %d %d %d,%s\000"
1515 (btod conf.icase) pn py (btod forward) pattern;
1518 let intentry text key =
1519 let text =
1520 if emptystr text && key = Keys.Ascii '-'
1521 then addchar text '-'
1522 else
1523 match [@warning "-4"] key with
1524 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1525 | _ ->
1526 state.text <- "invalid key";
1527 text
1529 TEcont text
1532 let linknact f s =
1533 if nonemptystr s
1534 then (
1535 let n =
1536 let l = String.length s in
1537 let rec loop pos n =
1538 if pos = l
1539 then n
1540 else
1541 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1542 loop (pos+1) (n*26 + m)
1543 in loop 0 0
1545 let rec loop n = function
1546 | [] -> ()
1547 | l :: rest ->
1548 match getopaque l.pageno with
1549 | None -> loop n rest
1550 | Some opaque ->
1551 let m = getlinkcount opaque in
1552 if n < m
1553 then (
1554 let under = getlink opaque n in
1555 f under
1557 else loop (n-m) rest
1559 loop n state.layout;
1563 let linknentry text key = match [@warning "-4"] key with
1564 | Keys.Ascii c ->
1565 let text = addchar text c in
1566 linknact (fun under -> state.text <- undertext under) text;
1567 TEcont text
1568 | _ ->
1569 state.text <- Printf.sprintf "invalid key";
1570 TEcont text
1573 let textentry text key = match [@warning "-4"] key with
1574 | Keys.Ascii c -> TEcont (addchar text c)
1575 | Keys.Code c -> TEcont (text ^ toutf8 c)
1576 | _ -> TEcont text
1579 let reqlayout angle fitmodel =
1580 if nogeomcmds state.geomcmds
1581 then state.anchor <- getanchor ();
1582 conf.angle <- angle mod 360;
1583 if conf.angle != 0
1584 then (
1585 match state.mode with
1586 | LinkNav _ -> state.mode <- View
1587 | Birdseye _ | Textentry _ | View -> ()
1589 conf.fitmodel <- fitmodel;
1590 invalidate "reqlayout"
1591 (fun () ->
1592 wcmd "reqlayout %d %d %d"
1593 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh)
1597 let settrim trimmargins trimfuzz =
1598 if nogeomcmds state.geomcmds
1599 then state.anchor <- getanchor ();
1600 conf.trimmargins <- trimmargins;
1601 conf.trimfuzz <- trimfuzz;
1602 let x0, y0, x1, y1 = trimfuzz in
1603 invalidate "settrim"
1604 (fun () -> wcmd "settrim %d %d %d %d %d"
1605 (btod conf.trimmargins) x0 y0 x1 y1);
1606 flushpages ();
1609 let setzoom zoom =
1610 let zoom = max 0.0001 zoom in
1611 if zoom <> conf.zoom
1612 then (
1613 state.prevzoom <- (conf.zoom, state.x);
1614 conf.zoom <- zoom;
1615 reshape state.winw state.winh;
1616 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1620 let pivotzoom ?(vw=min state.w state.winw)
1621 ?(vh=min (state.maxy-state.y) state.winh)
1622 ?(x=vw/2) ?(y=vh/2) zoom =
1623 let w = float state.w /. zoom in
1624 let hw = w /. 2.0 in
1625 let ratio = float vh /. float vw in
1626 let hh = hw *. ratio in
1627 let x0 = float x -. hw
1628 and y0 = float y -. hh in
1629 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1630 setzoom zoom;
1633 let pivotzoom ?vw ?vh ?x ?y zoom =
1634 if nogeomcmds state.geomcmds
1635 then
1636 if zoom > 1.0
1637 then pivotzoom ?vw ?vh ?x ?y zoom
1638 else setzoom zoom
1641 let setcolumns mode columns coverA coverB =
1642 state.prevcolumns <- Some (conf.columns, conf.zoom);
1643 if columns < 0
1644 then (
1645 if isbirdseye mode
1646 then impmsg "split mode doesn't work in bird's eye"
1647 else (
1648 conf.columns <- Csplit (-columns, E.a);
1649 state.x <- 0;
1650 conf.zoom <- 1.0;
1653 else (
1654 if columns < 2
1655 then (
1656 conf.columns <- Csingle E.a;
1657 state.x <- 0;
1658 setzoom 1.0;
1660 else (
1661 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1662 conf.zoom <- 1.0;
1665 reshape state.winw state.winh;
1668 let resetmstate () =
1669 state.mstate <- Mnone;
1670 Wsi.setcursor Wsi.CURSOR_INHERIT;
1673 let enterbirdseye () =
1674 let zoom = float conf.thumbw /. float state.winw in
1675 let birdseyepageno =
1676 let cy = state.winh / 2 in
1677 let fold = function
1678 | [] -> 0
1679 | l :: rest ->
1680 let rec fold best = function
1681 | [] -> best.pageno
1682 | l :: rest ->
1683 let d = cy - (l.pagedispy + l.pagevh/2)
1684 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1685 if abs d < abs dbest
1686 then fold l rest
1687 else best.pageno
1688 in fold l rest
1690 fold state.layout
1692 state.mode <-
1693 Birdseye (
1694 { conf with zoom = conf.zoom },
1695 state.x, birdseyepageno, -1, getanchor ()
1697 resetmstate ();
1698 conf.zoom <- zoom;
1699 conf.presentation <- false;
1700 conf.interpagespace <- 10;
1701 conf.hlinks <- false;
1702 conf.fitmodel <- FitPage;
1703 state.x <- 0;
1704 conf.columns <- (
1705 match conf.beyecolumns with
1706 | Some c ->
1707 conf.zoom <- 1.0;
1708 Cmulti ((c, 0, 0), E.a)
1709 | None -> Csingle E.a
1711 if conf.verbose
1712 then
1713 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1714 (100.0*.zoom)
1715 else state.text <- E.s;
1716 reshape state.winw state.winh;
1719 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1720 state.mode <- View;
1721 conf.zoom <- c.zoom;
1722 conf.presentation <- c.presentation;
1723 conf.interpagespace <- c.interpagespace;
1724 conf.hlinks <- c.hlinks;
1725 conf.fitmodel <- c.fitmodel;
1726 conf.beyecolumns <- (
1727 match conf.columns with
1728 | Cmulti ((c, _, _), _) -> Some c
1729 | Csingle _ -> None
1730 | Csplit _ -> error "leaving bird's eye split mode"
1732 conf.columns <- (
1733 match c.columns with
1734 | Cmulti (c, _) -> Cmulti (c, E.a)
1735 | Csingle _ -> Csingle E.a
1736 | Csplit (c, _) -> Csplit (c, E.a)
1738 if conf.verbose
1739 then
1740 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1741 (100.0*.conf.zoom);
1742 reshape state.winw state.winh;
1743 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1744 state.x <- leftx;
1747 let togglebirdseye () =
1748 match state.mode with
1749 | Birdseye vals -> leavebirdseye vals true
1750 | View -> enterbirdseye ()
1751 | Textentry _ | LinkNav _ -> ()
1754 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1755 let pageno = max 0 (pageno - incr) in
1756 let rec loop = function
1757 | [] -> gotopage1 pageno 0
1758 | l :: _ when l.pageno = pageno ->
1759 if l.pagedispy >= 0 && l.pagey = 0
1760 then postRedisplay "upbirdseye"
1761 else gotopage1 pageno 0
1762 | _ :: rest -> loop rest
1764 loop state.layout;
1765 state.text <- E.s;
1766 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1769 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1770 let pageno = min (state.pagecount - 1) (pageno + incr) in
1771 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1772 let rec loop = function
1773 | [] ->
1774 let y, h = getpageyh pageno in
1775 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1776 gotoxy state.x (clamp dy)
1777 | l :: _ when l.pageno = pageno ->
1778 if l.pagevh != l.pageh
1779 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1780 else postRedisplay "downbirdseye"
1781 | _ :: rest -> loop rest
1783 loop state.layout;
1784 state.text <- E.s;
1787 let optentry mode _ key =
1788 let btos b = if b then "on" else "off" in
1789 match [@warning "-4"] key with
1790 | Keys.Ascii 'C' ->
1791 let ondone s =
1793 let n, a, b = multicolumns_of_string s in
1794 setcolumns mode n a b;
1795 with exn ->
1796 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1798 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1800 | Keys.Ascii 'Z' ->
1801 let ondone s =
1803 let zoom = float (int_of_string s) /. 100.0 in
1804 pivotzoom zoom
1805 with exn ->
1806 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1808 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1810 | Keys.Ascii 'i' ->
1811 conf.icase <- not conf.icase;
1812 TEdone ("case insensitive search " ^ (btos conf.icase))
1814 | Keys.Ascii 'v' ->
1815 conf.verbose <- not conf.verbose;
1816 TEdone ("verbose " ^ (btos conf.verbose))
1818 | Keys.Ascii 'd' ->
1819 conf.debug <- not conf.debug;
1820 TEdone ("debug " ^ (btos conf.debug))
1822 | Keys.Ascii 'f' ->
1823 conf.underinfo <- not conf.underinfo;
1824 TEdone ("underinfo " ^ btos conf.underinfo)
1826 | Keys.Ascii 'T' ->
1827 settrim (not conf.trimmargins) conf.trimfuzz;
1828 TEdone ("trim margins " ^ btos conf.trimmargins)
1830 | Keys.Ascii 'I' ->
1831 conf.invert <- not conf.invert;
1832 TEdone ("invert colors " ^ btos conf.invert)
1834 | Keys.Ascii 'x' ->
1835 let ondone s =
1836 cbput state.hists.sel s;
1837 conf.selcmd <- s;
1839 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1840 textentry, ondone, true)
1842 | Keys.Ascii 'M' ->
1843 if conf.pax == None
1844 then conf.pax <- Some 0.0
1845 else conf.pax <- None;
1846 TEdone ("PAX " ^ btos (conf.pax != None))
1848 | (Keys.Ascii c) ->
1849 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1850 TEstop
1852 | _ -> TEcont state.text
1855 let adderrmsg src msg =
1856 Buffer.add_string state.errmsgs msg;
1857 state.newerrmsgs <- true;
1858 postRedisplay src
1861 let adderrfmt src fmt = Format.ksprintf (fun s -> adderrmsg src s) fmt;;
1863 class outlinelistview ~zebra ~source =
1864 let settext autonarrow s =
1865 if autonarrow
1866 then
1867 let ss = source#statestr in
1868 state.text <-
1869 if emptystr ss
1870 then "[" ^ s ^ "]"
1871 else "{" ^ ss ^ "} [" ^ s ^ "]"
1872 else state.text <- s
1874 object (self)
1875 inherit listview
1876 ~zebra
1877 ~helpmode:false
1878 ~source:(source :> lvsource)
1879 ~trusted:false
1880 ~modehash:(findkeyhash conf "outline")
1881 as super
1883 val m_autonarrow = false
1885 method! key key mask =
1886 let maxrows =
1887 if emptystr state.text
1888 then fstate.maxrows
1889 else fstate.maxrows - 2
1891 let calcfirst first active =
1892 if active > first
1893 then
1894 let rows = active - first in
1895 if rows > maxrows then active - maxrows else first
1896 else active
1898 let navigate incr =
1899 let active = m_active + incr in
1900 let active = bound active 0 (source#getitemcount - 1) in
1901 let first = calcfirst m_first active in
1902 postRedisplay "outline navigate";
1903 coe {< m_active = active; m_first = first >}
1905 let navscroll first =
1906 let active =
1907 let dist = m_active - first in
1908 if dist < 0
1909 then first
1910 else (
1911 if dist < maxrows
1912 then m_active
1913 else first + maxrows
1916 postRedisplay "outline navscroll";
1917 coe {< m_first = first; m_active = active >}
1919 let ctrl = Wsi.withctrl mask in
1920 let open Keys in
1921 match Wsi.kc2kt key with
1922 | Ascii 'a' when ctrl ->
1923 let text =
1924 if m_autonarrow
1925 then (
1926 source#denarrow;
1929 else (
1930 let pattern = source#renarrow in
1931 if nonemptystr m_qsearch
1932 then (source#narrow m_qsearch; m_qsearch)
1933 else pattern
1936 settext (not m_autonarrow) text;
1937 postRedisplay "toggle auto narrowing";
1938 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1940 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1941 settext true E.s;
1942 postRedisplay "toggle auto narrowing";
1943 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1945 | Ascii 'n' when ctrl ->
1946 source#narrow m_qsearch;
1947 if not m_autonarrow
1948 then source#add_narrow_pattern m_qsearch;
1949 postRedisplay "outline ctrl-n";
1950 coe {< m_first = 0; m_active = 0 >}
1952 | Ascii 'S' when ctrl ->
1953 let active = source#calcactive (getanchor ()) in
1954 let first = firstof m_first active in
1955 postRedisplay "outline ctrl-s";
1956 coe {< m_first = first; m_active = active >}
1958 | Ascii 'u' when ctrl ->
1959 postRedisplay "outline ctrl-u";
1960 if m_autonarrow && nonemptystr m_qsearch
1961 then (
1962 ignore (source#renarrow);
1963 settext m_autonarrow E.s;
1964 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1966 else (
1967 source#del_narrow_pattern;
1968 let pattern = source#renarrow in
1969 let text =
1970 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1972 settext m_autonarrow text;
1973 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1976 | Ascii 'l' when ctrl ->
1977 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1978 postRedisplay "outline ctrl-l";
1979 coe {< m_first = first >}
1981 | Ascii '\t' when m_autonarrow ->
1982 if nonemptystr m_qsearch
1983 then (
1984 postRedisplay "outline list view tab";
1985 source#add_narrow_pattern m_qsearch;
1986 settext true E.s;
1987 coe {< m_qsearch = E.s >}
1989 else coe self
1991 | Escape when m_autonarrow ->
1992 if nonemptystr m_qsearch
1993 then source#add_narrow_pattern m_qsearch;
1994 super#key key mask
1996 | Enter when m_autonarrow ->
1997 if nonemptystr m_qsearch
1998 then source#add_narrow_pattern m_qsearch;
1999 super#key key mask
2001 | (Ascii _ | Code _) when m_autonarrow ->
2002 let pattern = m_qsearch ^ toutf8 key in
2003 postRedisplay "outlinelistview autonarrow add";
2004 source#narrow pattern;
2005 settext true pattern;
2006 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
2008 | Backspace when m_autonarrow ->
2009 if emptystr m_qsearch
2010 then coe self
2011 else
2012 let pattern = withoutlastutf8 m_qsearch in
2013 postRedisplay "outlinelistview autonarrow backspace";
2014 ignore (source#renarrow);
2015 source#narrow pattern;
2016 settext true pattern;
2017 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
2019 | Up when ctrl -> navscroll (max 0 (m_first - 1))
2021 | Down when ctrl ->
2022 navscroll (min (source#getitemcount - 1) (m_first + 1))
2024 | Up -> navigate ~-1
2025 | Down -> navigate 1
2026 | Prior -> navigate ~-(fstate.maxrows)
2027 | Next -> navigate fstate.maxrows
2029 | Right ->
2030 let o =
2031 if ctrl
2032 then (
2033 postRedisplay "outline ctrl right";
2034 {< m_pan = m_pan + 1 >}
2036 else self#updownlevel 1
2038 coe o
2040 | Left ->
2041 let o =
2042 if ctrl
2043 then (
2044 postRedisplay "outline ctrl left";
2045 {< m_pan = m_pan - 1 >}
2047 else self#updownlevel ~-1
2049 coe o
2051 | Home ->
2052 postRedisplay "outline home";
2053 coe {< m_first = 0; m_active = 0 >}
2055 | End ->
2056 let active = source#getitemcount - 1 in
2057 let first = max 0 (active - fstate.maxrows) in
2058 postRedisplay "outline end";
2059 coe {< m_active = active; m_first = first >}
2061 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
2062 super#key key mask
2063 end;;
2065 let genhistoutlines () =
2066 Config.gethist ()
2067 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
2068 compare c2.lastvisit c1.lastvisit)
2069 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
2070 let path = if nonemptystr origin then origin else path in
2071 let base = mbtoutf8 @@ Filename.basename path in
2072 (base ^ "\000" ^ c.title, 1, Ohistory hist)
2076 let gotohist (path, c, bookmarks, x, anchor, origin) =
2077 Config.save leavebirdseye;
2078 state.anchor <- anchor;
2079 state.bookmarks <- bookmarks;
2080 state.origin <- origin;
2081 state.x <- x;
2082 setconf conf c;
2083 let x0, y0, x1, y1 = conf.trimfuzz in
2084 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2085 reshape ~firsttime:true state.winw state.winh;
2086 opendoc path origin;
2087 setzoom c.zoom;
2090 let setcheckers enabled =
2091 match state.checkerstexid with
2092 | None -> if enabled then state.checkerstexid <- Some (makecheckers ())
2094 | Some checkerstexid ->
2095 if not enabled
2096 then (
2097 GlTex.delete_texture checkerstexid;
2098 state.checkerstexid <- None;
2102 let describe_layout layout =
2103 let d =
2104 match layout with
2105 | [] -> "Page 0"
2106 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2107 | l :: rest ->
2108 let rangestr a b =
2109 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2110 else Printf.sprintf "%d%s%d" (a.pageno+1)
2111 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
2112 (b.pageno+1)
2114 let rec fold s la lb = function
2115 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2116 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2117 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2119 fold "Pages" l l rest
2121 let percent =
2122 let maxy = maxy () in
2123 if maxy <= 0
2124 then 100.
2125 else 100. *. (float state.y /. float maxy)
2127 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2130 let setpresentationmode v =
2131 let n = page_of_y state.y in
2132 state.anchor <- (n, 0.0, 1.0);
2133 conf.presentation <- v;
2134 if conf.fitmodel = FitPage
2135 then reqlayout conf.angle conf.fitmodel;
2136 represent ();
2139 let enterinfomode =
2140 let btos b = if b then Utf8syms.radical else E.s in
2141 let showextended = ref false in
2142 let showcolors = ref false in
2143 let leave mode _ = state.mode <- mode in
2144 let src =
2145 (object
2146 val mutable m_l = []
2147 val mutable m_a = E.a
2148 val mutable m_prev_uioh = nouioh
2149 val mutable m_prev_mode = View
2151 inherit lvsourcebase
2153 method reset prev_mode prev_uioh =
2154 m_a <- Array.of_list (List.rev m_l);
2155 m_l <- [];
2156 m_prev_mode <- prev_mode;
2157 m_prev_uioh <- prev_uioh;
2159 method int name get set =
2160 m_l <-
2161 (name, `int get, 1,
2162 Action (
2163 fun u ->
2164 let ondone s =
2165 try set (int_of_string s)
2166 with exn ->
2167 state.text <- Printf.sprintf "bad integer `%s': %s"
2168 s @@ exntos exn
2170 state.text <- E.s;
2171 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2172 state.mode <- Textentry (te, leave m_prev_mode);
2174 )) :: m_l
2176 method int_with_suffix name get set =
2177 m_l <-
2178 (name, `intws get, 1,
2179 Action (
2180 fun u ->
2181 let ondone s =
2182 try set (int_of_string_with_suffix s)
2183 with exn ->
2184 state.text <- Printf.sprintf "bad integer `%s': %s"
2185 s @@ exntos exn
2187 state.text <- E.s;
2188 let te =
2189 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2191 state.mode <- Textentry (te, leave m_prev_mode);
2193 )) :: m_l
2195 method bool ?(offset=1) ?(btos=btos) name get set =
2196 m_l <-
2197 (name, `bool (btos, get), offset, Action (
2198 fun u ->
2199 let v = get () in
2200 set (not v);
2202 )) :: m_l
2204 method color name get set =
2205 m_l <-
2206 (name, `color get, 1,
2207 Action (
2208 fun u ->
2209 let invalid = (nan, nan, nan) in
2210 let ondone s =
2211 let c =
2212 try color_of_string s
2213 with exn ->
2214 state.text <- Printf.sprintf "bad color `%s': %s"
2215 s @@ exntos exn;
2216 invalid
2218 if c <> invalid
2219 then set c;
2221 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2222 state.text <- color_to_string (get ());
2223 state.mode <- Textentry (te, leave m_prev_mode);
2225 )) :: m_l
2227 method string name get set =
2228 m_l <-
2229 (name, `string get, 1,
2230 Action (
2231 fun u ->
2232 let ondone s = set s in
2233 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2234 state.mode <- Textentry (te, leave m_prev_mode);
2236 )) :: m_l
2238 method colorspace name get set =
2239 m_l <-
2240 (name, `string get, 1,
2241 Action (
2242 fun _ ->
2243 let source =
2244 (object
2245 inherit lvsourcebase
2247 initializer
2248 m_active <- CSTE.to_int conf.colorspace;
2249 m_first <- 0;
2251 method getitemcount =
2252 Array.length CSTE.names
2253 method getitem n =
2254 (CSTE.names.(n), 0)
2255 method exit ~uioh ~cancel ~active ~first ~pan =
2256 ignore (uioh, first, pan);
2257 if not cancel then set active;
2258 None
2259 method hasaction _ = true
2260 end)
2262 state.text <- E.s;
2263 let modehash = findkeyhash conf "info" in
2264 coe (new listview ~zebra:false ~helpmode:false
2265 ~source ~trusted:true ~modehash)
2266 )) :: m_l
2268 method paxmark name get set =
2269 m_l <-
2270 (name, `string get, 1,
2271 Action (
2272 fun _ ->
2273 let source =
2274 (object
2275 inherit lvsourcebase
2277 initializer
2278 m_active <- MTE.to_int conf.paxmark;
2279 m_first <- 0;
2281 method getitemcount = Array.length MTE.names
2282 method getitem n = (MTE.names.(n), 0)
2283 method exit ~uioh ~cancel ~active ~first ~pan =
2284 ignore (uioh, first, pan);
2285 if not cancel then set active;
2286 None
2287 method hasaction _ = true
2288 end)
2290 state.text <- E.s;
2291 let modehash = findkeyhash conf "info" in
2292 coe (new listview ~zebra:false ~helpmode:false
2293 ~source ~trusted:true ~modehash)
2294 )) :: m_l
2296 method fitmodel name get set =
2297 m_l <-
2298 (name, `string get, 1,
2299 Action (
2300 fun _ ->
2301 let source =
2302 (object
2303 inherit lvsourcebase
2305 initializer
2306 m_active <- FMTE.to_int conf.fitmodel;
2307 m_first <- 0;
2309 method getitemcount = Array.length FMTE.names
2310 method getitem n = (FMTE.names.(n), 0)
2311 method exit ~uioh ~cancel ~active ~first ~pan =
2312 ignore (uioh, first, pan);
2313 if not cancel then set active;
2314 None
2315 method hasaction _ = true
2316 end)
2318 state.text <- E.s;
2319 let modehash = findkeyhash conf "info" in
2320 coe (new listview ~zebra:false ~helpmode:false
2321 ~source ~trusted:true ~modehash)
2322 )) :: m_l
2324 method caption s offset =
2325 m_l <- (s, `empty, offset, Noaction) :: m_l
2327 method caption2 s f offset =
2328 m_l <- (s, `string f, offset, Noaction) :: m_l
2330 method getitemcount = Array.length m_a
2332 method getitem n =
2333 let tostr = function
2334 | `int f -> string_of_int (f ())
2335 | `intws f -> string_with_suffix_of_int (f ())
2336 | `string f -> f ()
2337 | `color f -> color_to_string (f ())
2338 | `bool (btos, f) -> btos (f ())
2339 | `empty -> E.s
2341 let name, t, offset, _ = m_a.(n) in
2342 ((let s = tostr t in
2343 if nonemptystr s
2344 then Printf.sprintf "%s\t%s" name s
2345 else name),
2346 offset)
2348 method exit ~uioh ~cancel ~active ~first ~pan =
2349 let uiohopt =
2350 if not cancel
2351 then (
2352 let uioh =
2353 match m_a.(active) with
2354 | _, _, _, Action f -> f uioh
2355 | _, _, _, Noaction -> uioh
2357 Some uioh
2359 else None
2361 m_active <- active;
2362 m_first <- first;
2363 m_pan <- pan;
2364 uiohopt
2366 method hasaction n =
2367 match m_a.(n) with
2368 | _, _, _, Action _ -> true
2369 | _, _, _, Noaction -> false
2371 initializer m_active <- 1
2372 end)
2374 let rec fillsrc prevmode prevuioh =
2375 let sep () = src#caption E.s 0 in
2376 let colorp name get set =
2377 src#string name
2378 (fun () -> color_to_string (get ()))
2379 (fun v ->
2380 try set @@ color_of_string v
2381 with exn ->
2382 state.text <-
2383 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2386 let rgba name get set =
2387 src#string name
2388 (fun () -> get () |> rgba_to_string)
2389 (fun v ->
2390 try set @@ rgba_of_string v
2391 with exn ->
2392 state.text <-
2393 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2396 let oldmode = state.mode in
2397 let birdseye = isbirdseye state.mode in
2399 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2401 src#bool "presentation mode"
2402 (fun () -> conf.presentation)
2403 (fun v -> setpresentationmode v);
2405 src#bool "ignore case in searches"
2406 (fun () -> conf.icase)
2407 (fun v -> conf.icase <- v);
2409 src#bool "preload"
2410 (fun () -> conf.preload)
2411 (fun v -> conf.preload <- v);
2413 src#bool "highlight links"
2414 (fun () -> conf.hlinks)
2415 (fun v -> conf.hlinks <- v);
2417 src#bool "under info"
2418 (fun () -> conf.underinfo)
2419 (fun v -> conf.underinfo <- v);
2421 src#fitmodel "fit model"
2422 (fun () -> FMTE.to_string conf.fitmodel)
2423 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2425 src#bool "trim margins"
2426 (fun () -> conf.trimmargins)
2427 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2429 sep ();
2430 src#int "inter-page space"
2431 (fun () -> conf.interpagespace)
2432 (fun n ->
2433 conf.interpagespace <- n;
2434 docolumns conf.columns;
2435 let pageno, py =
2436 match state.layout with
2437 | [] -> 0, 0
2438 | l :: _ -> l.pageno, l.pagey
2440 state.maxy <- calcheight ();
2441 let y = getpagey pageno in
2442 gotoxy state.x (y + py)
2445 src#int "page bias"
2446 (fun () -> conf.pagebias)
2447 (fun v -> conf.pagebias <- v);
2449 src#int "scroll step"
2450 (fun () -> conf.scrollstep)
2451 (fun n -> conf.scrollstep <- n);
2453 src#int "horizontal scroll step"
2454 (fun () -> conf.hscrollstep)
2455 (fun v -> conf.hscrollstep <- v);
2457 src#int "auto scroll step"
2458 (fun () ->
2459 match state.autoscroll with
2460 | Some step -> step
2461 | _ -> conf.autoscrollstep)
2462 (fun n ->
2463 let n = boundastep state.winh n in
2464 if state.autoscroll <> None
2465 then state.autoscroll <- Some n;
2466 conf.autoscrollstep <- n);
2468 src#int "zoom"
2469 (fun () -> truncate (conf.zoom *. 100.))
2470 (fun v -> pivotzoom ((float v) /. 100.));
2472 src#int "rotation"
2473 (fun () -> conf.angle)
2474 (fun v -> reqlayout v conf.fitmodel);
2476 src#int "scroll bar width"
2477 (fun () -> conf.scrollbw)
2478 (fun v ->
2479 conf.scrollbw <- v;
2480 reshape state.winw state.winh;
2483 src#int "scroll handle height"
2484 (fun () -> conf.scrollh)
2485 (fun v -> conf.scrollh <- v;);
2487 src#int "thumbnail width"
2488 (fun () -> conf.thumbw)
2489 (fun v ->
2490 conf.thumbw <- min 4096 v;
2491 match oldmode with
2492 | Birdseye beye ->
2493 leavebirdseye beye false;
2494 enterbirdseye ()
2495 | Textentry _
2496 | View
2497 | LinkNav _ -> ()
2500 let mode = state.mode in
2501 src#string "columns"
2502 (fun () ->
2503 match conf.columns with
2504 | Csingle _ -> "1"
2505 | Cmulti (multi, _) -> multicolumns_to_string multi
2506 | Csplit (count, _) -> "-" ^ string_of_int count
2508 (fun v ->
2509 let n, a, b = multicolumns_of_string v in
2510 setcolumns mode n a b);
2512 sep ();
2513 src#caption "Pixmap cache" 0;
2514 src#int_with_suffix "size (advisory)"
2515 (fun () -> conf.memlimit)
2516 (fun v -> conf.memlimit <- v);
2518 src#caption2 "used"
2519 (fun () ->
2520 Printf.sprintf "%s bytes, %d tiles"
2521 (string_with_suffix_of_int state.memused)
2522 (Hashtbl.length state.tilemap)) 1;
2524 sep ();
2525 src#caption "Layout" 0;
2526 src#caption2 "Dimension"
2527 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2528 state.winw state.winh
2529 state.w state.maxy)
2531 if conf.debug
2532 then src#caption2 "Position" (fun () ->
2533 Printf.sprintf "%dx%d" state.x state.y
2535 else src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2537 sep ();
2538 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2539 "Save these parameters as global defaults at exit"
2540 (fun () -> conf.bedefault)
2541 (fun v -> conf.bedefault <- v);
2543 sep ();
2544 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2545 src#bool ~offset:0 ~btos "Extended parameters"
2546 (fun () -> !showextended)
2547 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2548 if !showextended
2549 then (
2550 src#bool "checkers"
2551 (fun () -> conf.checkers)
2552 (fun v -> conf.checkers <- v; setcheckers v);
2553 src#bool "update cursor"
2554 (fun () -> conf.updatecurs)
2555 (fun v -> conf.updatecurs <- v);
2556 src#bool "scroll-bar on the left"
2557 (fun () -> conf.leftscroll)
2558 (fun v -> conf.leftscroll <- v);
2559 src#bool "verbose"
2560 (fun () -> conf.verbose)
2561 (fun v -> conf.verbose <- v);
2562 src#bool "invert colors"
2563 (fun () -> conf.invert)
2564 (fun v -> conf.invert <- v);
2565 src#bool "max fit"
2566 (fun () -> conf.maxhfit)
2567 (fun v -> conf.maxhfit <- v);
2568 src#bool "pax mode"
2569 (fun () -> conf.pax != None)
2570 (fun v ->
2571 if v
2572 then conf.pax <- Some (now ())
2573 else conf.pax <- None);
2574 src#string "uri launcher"
2575 (fun () -> conf.urilauncher)
2576 (fun v -> conf.urilauncher <- v);
2577 src#string "path launcher"
2578 (fun () -> conf.pathlauncher)
2579 (fun v -> conf.pathlauncher <- v);
2580 src#string "tile size"
2581 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2582 (fun v ->
2584 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2585 conf.tilew <- max 64 w;
2586 conf.tileh <- max 64 h;
2587 flushtiles ();
2588 with exn ->
2589 state.text <- Printf.sprintf "bad tile size `%s': %s"
2590 v @@ exntos exn
2592 src#int "texture count"
2593 (fun () -> conf.texcount)
2594 (fun v ->
2595 if realloctexts v
2596 then conf.texcount <- v
2597 else impmsg "failed to set texture count please retry later"
2599 src#int "slice height"
2600 (fun () -> conf.sliceheight)
2601 (fun v ->
2602 conf.sliceheight <- v;
2603 wcmd "sliceh %d" conf.sliceheight;
2605 src#int "anti-aliasing level"
2606 (fun () -> conf.aalevel)
2607 (fun v ->
2608 conf.aalevel <- bound v 0 8;
2609 state.anchor <- getanchor ();
2610 opendoc state.path state.password;
2612 src#string "page scroll scaling factor"
2613 (fun () -> string_of_float conf.pgscale)
2614 (fun v ->
2615 try conf.pgscale <- float_of_string v
2616 with exn ->
2617 state.text <-
2618 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2619 @@ exntos exn
2621 src#int "ui font size"
2622 (fun () -> fstate.fontsize)
2623 (fun v -> setfontsize (bound v 5 100));
2624 src#int "hint font size"
2625 (fun () -> conf.hfsize)
2626 (fun v -> conf.hfsize <- bound v 5 100);
2627 src#string "trim fuzz"
2628 (fun () -> irect_to_string conf.trimfuzz)
2629 (fun v ->
2631 conf.trimfuzz <- irect_of_string v;
2632 if conf.trimmargins
2633 then settrim true conf.trimfuzz;
2634 with exn ->
2635 state.text <- Printf.sprintf "bad irect `%s': %s" v
2636 @@ exntos exn
2638 src#string "selection command"
2639 (fun () -> conf.selcmd)
2640 (fun v -> conf.selcmd <- v);
2641 src#string "synctex command"
2642 (fun () -> conf.stcmd)
2643 (fun v -> conf.stcmd <- v);
2644 src#string "pax command"
2645 (fun () -> conf.paxcmd)
2646 (fun v -> conf.paxcmd <- v);
2647 src#string "ask password command"
2648 (fun () -> conf.passcmd)
2649 (fun v -> conf.passcmd <- v);
2650 src#string "save path command"
2651 (fun () -> conf.savecmd)
2652 (fun v -> conf.savecmd <- v);
2653 src#colorspace "color space"
2654 (fun () -> CSTE.to_string conf.colorspace)
2655 (fun v ->
2656 conf.colorspace <- CSTE.of_int v;
2657 wcmd "cs %d" v;
2658 load state.layout;
2660 src#paxmark "pax mark method"
2661 (fun () -> MTE.to_string conf.paxmark)
2662 (fun v -> conf.paxmark <- MTE.of_int v);
2663 if bousable () && !opengl_has_pbo
2664 then
2665 src#bool "use PBO"
2666 (fun () -> conf.usepbo)
2667 (fun v -> conf.usepbo <- v);
2668 src#bool "mouse wheel scrolls pages"
2669 (fun () -> conf.wheelbypage)
2670 (fun v -> conf.wheelbypage <- v);
2671 src#bool "open remote links in a new instance"
2672 (fun () -> conf.riani)
2673 (fun v -> conf.riani <- v);
2674 src#bool "edit annotations inline"
2675 (fun () -> conf.annotinline)
2676 (fun v -> conf.annotinline <- v);
2677 src#bool "coarse positioning in presentation mode"
2678 (fun () -> conf.coarseprespos)
2679 (fun v -> conf.coarseprespos <- v);
2680 src#bool "use document CSS"
2681 (fun () -> conf.usedoccss)
2682 (fun v ->
2683 conf.usedoccss <- v;
2684 state.anchor <- getanchor ();
2685 opendoc state.path state.password;
2687 src#bool ~btos "colors"
2688 (fun () -> !showcolors)
2689 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2690 if !showcolors
2691 then (
2692 colorp " background"
2693 (fun () -> conf.bgcolor)
2694 (fun v -> conf.bgcolor <- v);
2695 rgba " scrollbar"
2696 (fun () -> conf.sbarcolor)
2697 (fun v -> conf.sbarcolor <- v);
2698 rgba " scrollbar handle"
2699 (fun () -> conf.sbarhndlcolor)
2700 (fun v -> conf.sbarhndlcolor <- v);
2704 sep ();
2705 src#caption "Document" 0;
2706 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2707 src#caption2 "Pages"
2708 (fun () -> string_of_int state.pagecount) 1;
2709 src#caption2 "Dimensions"
2710 (fun () -> string_of_int (List.length state.pdims)) 1;
2711 if nonemptystr conf.css
2712 then src#caption2 "CSS" (fun () -> conf.css) 1;
2713 if conf.trimmargins
2714 then (
2715 sep ();
2716 src#caption "Trimmed margins" 0;
2717 src#caption2 "Dimensions"
2718 (fun () -> string_of_int (List.length state.pdims)) 1;
2721 sep ();
2722 src#caption "OpenGL" 0;
2723 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2724 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2726 sep ();
2727 src#caption "Location" 0;
2728 if nonemptystr state.origin
2729 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2730 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2732 src#reset prevmode prevuioh;
2734 fun () ->
2735 state.text <- E.s;
2736 resetmstate ();
2737 let prevmode = state.mode
2738 and prevuioh = state.uioh in
2739 fillsrc prevmode prevuioh;
2740 let source = (src :> lvsource) in
2741 let modehash = findkeyhash conf "info" in
2742 state.uioh <-
2743 coe (object (self)
2744 inherit listview ~zebra:false ~helpmode:false
2745 ~source ~trusted:true ~modehash as super
2746 val mutable m_prevmemused = 0
2747 method! infochanged = function
2748 | Memused ->
2749 if m_prevmemused != state.memused
2750 then (
2751 m_prevmemused <- state.memused;
2752 postRedisplay "memusedchanged";
2754 | Pdim -> postRedisplay "pdimchanged"
2755 | Docinfo -> fillsrc prevmode prevuioh
2757 method! key key mask =
2758 if not (Wsi.withctrl mask)
2759 then
2760 match [@warning "-4"] Wsi.kc2kt key with
2761 | Keys.Left -> coe (self#updownlevel ~-1)
2762 | Keys.Right -> coe (self#updownlevel 1)
2763 | _ -> super#key key mask
2764 else super#key key mask
2765 end);
2766 postRedisplay "info";
2769 let enterhelpmode =
2770 let source =
2771 (object
2772 inherit lvsourcebase
2773 method getitemcount = Array.length state.help
2774 method getitem n =
2775 let s, l, _ = state.help.(n) in
2776 (s, l)
2778 method exit ~uioh ~cancel ~active ~first ~pan =
2779 let optuioh =
2780 if not cancel
2781 then (
2782 match state.help.(active) with
2783 | _, _, Action f -> Some (f uioh)
2784 | _, _, Noaction -> Some uioh
2786 else None
2788 m_active <- active;
2789 m_first <- first;
2790 m_pan <- pan;
2791 optuioh
2793 method hasaction n =
2794 match state.help.(n) with
2795 | _, _, Action _ -> true
2796 | _, _, Noaction -> false
2798 initializer
2799 m_active <- -1
2800 end)
2801 in fun () ->
2802 let modehash = findkeyhash conf "help" in
2803 resetmstate ();
2804 state.uioh <- coe (new listview
2805 ~zebra:false ~helpmode:true
2806 ~source ~trusted:true ~modehash);
2807 postRedisplay "help";
2810 let entermsgsmode =
2811 let msgsource =
2812 (object
2813 inherit lvsourcebase
2814 val mutable m_items = E.a
2816 method getitemcount = 1 + Array.length m_items
2818 method getitem n =
2819 if n = 0
2820 then "[Clear]", 0
2821 else m_items.(n-1), 0
2823 method exit ~uioh ~cancel ~active ~first ~pan =
2824 ignore uioh;
2825 if not cancel
2826 then (
2827 if active = 0
2828 then Buffer.clear state.errmsgs;
2830 m_active <- active;
2831 m_first <- first;
2832 m_pan <- pan;
2833 None
2835 method hasaction n =
2836 n = 0
2838 method reset =
2839 state.newerrmsgs <- false;
2840 let l = Str.split Utils.Re.crlf (Buffer.contents state.errmsgs) in
2841 m_items <- Array.of_list l
2843 initializer
2844 m_active <- 0
2845 end)
2846 in fun () ->
2847 state.text <- E.s;
2848 resetmstate ();
2849 msgsource#reset;
2850 let source = (msgsource :> lvsource) in
2851 let modehash = findkeyhash conf "listview" in
2852 state.uioh <-
2853 coe (object
2854 inherit listview ~zebra:false ~helpmode:false
2855 ~source ~trusted:false ~modehash as super
2856 method! display =
2857 if state.newerrmsgs
2858 then msgsource#reset;
2859 super#display
2860 end);
2861 postRedisplay "msgs";
2864 let getusertext s =
2865 let editor = getenvwithdef "EDITOR" E.s in
2866 if emptystr editor
2867 then E.s
2868 else
2869 let tmppath = Filename.temp_file "llpp" "note" in
2870 if nonemptystr s
2871 then (
2872 let oc = open_out tmppath in
2873 output_string oc s;
2874 close_out oc;
2876 let execstr = editor ^ " " ^ tmppath in
2877 let s =
2878 match spawn execstr [] with
2879 | exception exn ->
2880 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
2882 | pid ->
2883 match Unix.waitpid [] pid with
2884 | exception exn ->
2885 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
2887 | (_pid, status) ->
2888 match status with
2889 | Unix.WEXITED 0 -> filecontents tmppath
2890 | Unix.WEXITED n ->
2891 impmsg "editor process(%s) exited abnormally: %d" execstr n;
2893 | Unix.WSIGNALED n ->
2894 impmsg "editor process(%s) was killed by signal %d" execstr n;
2896 | Unix.WSTOPPED n ->
2897 impmsg "editor(%s) process was stopped by signal %d" execstr n;
2900 match Unix.unlink tmppath with
2901 | exception exn ->
2902 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
2904 | () -> s
2907 let enterannotmode opaque slinkindex =
2908 let msgsource =
2909 (object
2910 inherit lvsourcebase
2911 val mutable m_text = E.s
2912 val mutable m_items = E.a
2914 method getitemcount = Array.length m_items
2916 method getitem n =
2917 let label, _func = m_items.(n) in
2918 label, 0
2920 method exit ~uioh ~cancel ~active ~first ~pan =
2921 ignore (uioh, first, pan);
2922 if not cancel
2923 then (
2924 let _label, func = m_items.(active) in
2925 func ()
2927 None
2929 method hasaction n = nonemptystr @@ fst m_items.(n)
2931 method reset s =
2932 let rec split accu b i =
2933 let p = b+i in
2934 if p = String.length s
2935 then (String.sub s b (p-b), unit) :: accu
2936 else
2937 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2938 then
2939 let ss = if i = 0 then E.s else String.sub s b i in
2940 split ((ss, unit)::accu) (p+1) 0
2941 else split accu b (i+1)
2943 let cleanup () =
2944 wcmd "freepage %s" (~> opaque);
2945 let keys =
2946 Hashtbl.fold (fun key opaque' accu ->
2947 if opaque' = opaque'
2948 then key :: accu else accu) state.pagemap []
2950 List.iter (Hashtbl.remove state.pagemap) keys;
2951 flushtiles ();
2952 gotoxy state.x state.y
2954 let dele () =
2955 delannot opaque slinkindex;
2956 cleanup ();
2958 let edit inline () =
2959 let update s =
2960 if emptystr s
2961 then dele ()
2962 else (
2963 modannot opaque slinkindex s;
2964 cleanup ();
2967 if inline
2968 then
2969 let mode = state.mode in
2970 state.mode <-
2971 Textentry (
2972 ("annotation: ", m_text, None, textentry, update, true),
2973 fun _ -> state.mode <- mode
2975 state.text <- E.s;
2976 enttext ();
2977 else
2978 let s = getusertext m_text in
2979 update s
2981 m_text <- s;
2982 m_items <-
2983 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
2984 :: ("[Delete]", dele)
2985 :: ("[Edit]", edit conf.annotinline)
2986 :: (E.s, unit)
2987 :: split [] 0 0 |> List.rev |> Array.of_list
2989 initializer
2990 m_active <- 0
2991 end)
2993 state.text <- E.s;
2994 let s = getannotcontents opaque slinkindex in
2995 resetmstate ();
2996 msgsource#reset s;
2997 let source = (msgsource :> lvsource) in
2998 let modehash = findkeyhash conf "listview" in
2999 state.uioh <- coe (object
3000 inherit listview ~zebra:false ~helpmode:false
3001 ~source ~trusted:false ~modehash
3002 end);
3003 postRedisplay "enterannotmode";
3006 let gotoremote spec =
3007 let filename, dest = splitatchar spec '#' in
3008 let getpath filename =
3009 let path =
3010 if nonemptystr filename
3011 then
3012 if Filename.is_relative filename
3013 then
3014 let dir = Filename.dirname state.path in
3015 let dir =
3016 if Filename.is_implicit dir
3017 then Filename.concat (Sys.getcwd ()) dir
3018 else dir
3020 Filename.concat dir filename
3021 else filename
3022 else E.s
3024 if Sys.file_exists path
3025 then path
3026 else E.s
3028 let path = getpath filename in
3029 let dospawn lcmd =
3030 if conf.riani
3031 then
3032 let cmd = Lazy.force_val lcmd in
3033 match spawn cmd with
3034 | _pid -> ()
3035 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
3036 else
3037 let anchor = getanchor () in
3038 let ranchor = state.path, state.password, anchor, state.origin in
3039 state.origin <- E.s;
3040 state.ranchors <- ranchor :: state.ranchors;
3041 opendoc path E.s;
3043 if substratis spec 0 "page="
3044 then
3045 match Scanf.sscanf spec "page=%d" (fun n -> n) with
3046 | pageno ->
3047 state.anchor <- (pageno, 0.0, 0.0);
3048 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
3049 | exception exn ->
3050 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
3051 else (
3052 state.nameddest <- dest;
3053 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
3057 let gotounder = function
3058 | Ulinkuri s when isexternallink s ->
3059 if substratis s 0 "file://"
3060 then gotoremote @@ String.sub s 7 (String.length s - 7)
3061 else Help.gotouri conf.urilauncher s
3062 | Ulinkuri s ->
3063 let pageno, x, y = uritolocation s in
3064 addnav ();
3065 gotopagexy pageno x y
3066 | Utext _ | Unone -> ()
3067 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
3070 let gotooutline (_, _, kind) =
3071 match kind with
3072 | Onone -> ()
3073 | Oanchor anchor ->
3074 let (pageno, y, _) = anchor in
3075 let y = getanchory
3076 (if conf.presentation then (pageno, y, 1.0) else anchor)
3078 addnav ();
3079 gotoxy state.x y
3080 | Ouri uri -> gotounder (Ulinkuri uri)
3081 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
3082 | Oremote (remote, pageno) ->
3083 error "gotounder (Uremote (%S,%d) )" remote pageno
3084 | Ohistory hist -> gotohist hist
3085 | Oremotedest (path, dest) ->
3086 error "gotounder (Uremotedest (%S, %S))" path dest
3089 class outlinesoucebase fetchoutlines = object (self)
3090 inherit lvsourcebase
3091 val mutable m_items = E.a
3092 val mutable m_minfo = E.a
3093 val mutable m_orig_items = E.a
3094 val mutable m_orig_minfo = E.a
3095 val mutable m_narrow_patterns = []
3096 val mutable m_gen = -1
3098 method getitemcount = Array.length m_items
3100 method getitem n =
3101 let s, n, _ = m_items.(n) in
3102 (s, n+0)
3104 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3105 ignore (uioh, first);
3106 let items, minfo =
3107 if m_narrow_patterns = []
3108 then m_orig_items, m_orig_minfo
3109 else m_items, m_minfo
3111 m_pan <- pan;
3112 if not cancel
3113 then (
3114 m_items <- items;
3115 m_minfo <- minfo;
3116 gotooutline m_items.(active);
3118 else (
3119 m_items <- items;
3120 m_minfo <- minfo;
3122 None
3124 method hasaction (_:int) = true
3126 method greetmsg =
3127 if Array.length m_items != Array.length m_orig_items
3128 then
3129 let s =
3130 match m_narrow_patterns with
3131 | one :: [] -> one
3132 | many -> String.concat Utf8syms.ellipsis (List.rev many)
3134 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3135 else E.s
3137 method statestr =
3138 match m_narrow_patterns with
3139 | [] -> E.s
3140 | one :: [] -> one
3141 | head :: _ -> Utf8syms.ellipsis ^ head
3143 method narrow pattern =
3144 match Str.regexp_case_fold pattern with
3145 | exception _ -> ()
3146 | re ->
3147 let rec loop accu minfo n =
3148 if n = -1
3149 then (
3150 m_items <- Array.of_list accu;
3151 m_minfo <- Array.of_list minfo;
3153 else
3154 let (s, _, _) as o = m_items.(n) in
3155 let accu, minfo =
3156 match Str.search_forward re s 0 with
3157 | exception Not_found -> accu, minfo
3158 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3160 loop accu minfo (n-1)
3162 loop [] [] (Array.length m_items - 1)
3164 method! getminfo = m_minfo
3166 method denarrow =
3167 m_orig_items <- fetchoutlines ();
3168 m_minfo <- m_orig_minfo;
3169 m_items <- m_orig_items
3171 method add_narrow_pattern pattern =
3172 m_narrow_patterns <- pattern :: m_narrow_patterns
3174 method del_narrow_pattern =
3175 match m_narrow_patterns with
3176 | _ :: rest -> m_narrow_patterns <- rest
3177 | [] -> ()
3179 method renarrow =
3180 self#denarrow;
3181 match m_narrow_patterns with
3182 | pattern :: [] -> self#narrow pattern; pattern
3183 | list ->
3184 List.fold_left (fun accu pattern ->
3185 self#narrow pattern;
3186 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
3188 method calcactive (_:anchor) = 0
3190 method reset anchor items =
3191 if state.gen != m_gen
3192 then (
3193 m_orig_items <- items;
3194 m_items <- items;
3195 m_narrow_patterns <- [];
3196 m_minfo <- E.a;
3197 m_orig_minfo <- E.a;
3198 m_gen <- state.gen;
3200 else (
3201 if items != m_orig_items
3202 then (
3203 m_orig_items <- items;
3204 if m_narrow_patterns == []
3205 then m_items <- items;
3208 let active = self#calcactive anchor in
3209 m_active <- active;
3210 m_first <- firstof m_first active
3214 let outlinesource fetchoutlines =
3215 (object
3216 inherit outlinesoucebase fetchoutlines
3217 method! calcactive anchor =
3218 let rely = getanchory anchor in
3219 let rec loop n best bestd =
3220 if n = Array.length m_items
3221 then best
3222 else
3223 let _, _, kind = m_items.(n) in
3224 match kind with
3225 | Oanchor anchor ->
3226 let orely = getanchory anchor in
3227 let d = abs (orely - rely) in
3228 if d < bestd
3229 then loop (n+1) n d
3230 else loop (n+1) best bestd
3231 | Onone | Oremote _ | Olaunch _
3232 | Oremotedest _ | Ouri _ | Ohistory _ ->
3233 loop (n+1) best bestd
3235 loop 0 ~-1 max_int
3236 end)
3239 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3240 let mkselector sourcetype =
3241 let fetchoutlines () =
3242 match sourcetype with
3243 | `bookmarks -> Array.of_list state.bookmarks
3244 | `outlines -> state.outlines
3245 | `history -> genhistoutlines () |> Array.of_list
3247 let source =
3248 if sourcetype = `history
3249 then new outlinesoucebase fetchoutlines
3250 else outlinesource fetchoutlines
3252 (fun errmsg ->
3253 let outlines = fetchoutlines () in
3254 if Array.length outlines = 0
3255 then showtext ' ' errmsg
3256 else (
3257 resetmstate ();
3258 Wsi.setcursor Wsi.CURSOR_INHERIT;
3259 let anchor = getanchor () in
3260 source#reset anchor outlines;
3261 state.text <- source#greetmsg;
3262 state.uioh <-
3263 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3264 postRedisplay "enter selector";
3268 let mkenter sourcetype errmsg = fun () -> mkselector sourcetype errmsg in
3269 ( mkenter `outlines "document has no outline"
3270 , mkenter `bookmarks "document has no bookmarks (yet)"
3271 , mkenter `history "history is empty" )
3274 let quickbookmark ?title () =
3275 match state.layout with
3276 | [] -> ()
3277 | l :: _ ->
3278 let title =
3279 match title with
3280 | None ->
3281 Unix.(
3282 let tm = localtime (now ()) in
3283 Printf.sprintf
3284 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3285 (l.pageno+1)
3286 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3288 | Some title -> title
3290 state.bookmarks <- (title, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3293 let setautoscrollspeed step goingdown =
3294 let incr = max 1 ((abs step) / 2) in
3295 let incr = if goingdown then incr else -incr in
3296 let astep = boundastep state.winh (step + incr) in
3297 state.autoscroll <- Some astep;
3300 let canpan () =
3301 match conf.columns with
3302 | Csplit _ -> true
3303 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3306 let panbound x = bound x (-state.w) state.winw;;
3308 let existsinrow pageno (columns, coverA, coverB) p =
3309 let last = ((pageno - coverA) mod columns) + columns in
3310 let rec any = function
3311 | [] -> false
3312 | l :: rest ->
3313 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3314 then p l
3315 else (
3316 if not (p l)
3317 then (if l.pageno = last then false else any rest)
3318 else true
3321 any state.layout
3324 let nextpage () =
3325 match state.layout with
3326 | [] ->
3327 let pageno = page_of_y state.y in
3328 gotoxy state.x (getpagey (pageno+1))
3329 | l :: rest ->
3330 match conf.columns with
3331 | Csingle _ ->
3332 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3333 then
3334 let y = clamp (pgscale state.winh) in
3335 gotoxy state.x y
3336 else
3337 let pageno = min (l.pageno+1) (state.pagecount-1) in
3338 gotoxy state.x (getpagey pageno)
3339 | Cmulti ((c, _, _) as cl, _) ->
3340 if conf.presentation
3341 && (existsinrow l.pageno cl
3342 (fun l -> l.pageh > l.pagey + l.pagevh))
3343 then
3344 let y = clamp (pgscale state.winh) in
3345 gotoxy state.x y
3346 else
3347 let pageno = min (l.pageno+c) (state.pagecount-1) in
3348 gotoxy state.x (getpagey pageno)
3349 | Csplit (n, _) ->
3350 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3351 then
3352 let pagey, pageh = getpageyh l.pageno in
3353 let pagey = pagey + pageh * l.pagecol in
3354 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3355 gotoxy state.x (pagey + pageh + ips)
3358 let prevpage () =
3359 match state.layout with
3360 | [] ->
3361 let pageno = page_of_y state.y in
3362 gotoxy state.x (getpagey (pageno-1))
3363 | l :: _ ->
3364 match conf.columns with
3365 | Csingle _ ->
3366 if conf.presentation && l.pagey != 0
3367 then
3368 gotoxy state.x (clamp (pgscale ~-(state.winh)))
3369 else
3370 let pageno = max 0 (l.pageno-1) in
3371 gotoxy state.x (getpagey pageno)
3372 | Cmulti ((c, _, coverB) as cl, _) ->
3373 if conf.presentation &&
3374 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3375 then
3376 gotoxy state.x (clamp (pgscale ~-(state.winh)))
3377 else
3378 let decr =
3379 if l.pageno = state.pagecount - coverB
3380 then 1
3381 else c
3383 let pageno = max 0 (l.pageno-decr) in
3384 gotoxy state.x (getpagey pageno)
3385 | Csplit (n, _) ->
3386 let y =
3387 if l.pagecol = 0
3388 then
3389 if l.pageno = 0
3390 then l.pagey
3391 else
3392 let pageno = max 0 (l.pageno-1) in
3393 let pagey, pageh = getpageyh pageno in
3394 pagey + (n-1)*pageh
3395 else
3396 let pagey, pageh = getpageyh l.pageno in
3397 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3399 gotoxy state.x y
3402 let save () =
3403 if emptystr conf.savecmd
3404 then adderrmsg "savepath-command is empty"
3405 "don't know where to save modified document"
3406 else
3407 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3408 let path =
3409 getcmdoutput
3410 (fun exn ->
3411 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3412 savecmd
3414 if nonemptystr path
3415 then
3416 let tmp = path ^ ".tmp" in
3417 savedoc tmp;
3418 Unix.rename tmp path;
3421 let viewkeyboard key mask =
3422 let enttext te =
3423 let mode = state.mode in
3424 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3425 state.text <- E.s;
3426 enttext ();
3427 postRedisplay "view:enttext"
3429 let ctrl = Wsi.withctrl mask in
3430 let open Keys in
3431 match Wsi.kc2kt key with
3432 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3434 | Ascii 'Q' -> exit 0
3436 | Ascii 'W' ->
3437 if hasunsavedchanges ()
3438 then save ()
3440 | Insert ->
3441 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3442 then (
3443 state.mode <- (
3444 match state.lnava with
3445 | None -> LinkNav (Ltgendir 0)
3446 | Some pn -> LinkNav (Ltexact pn)
3448 gotoxy state.x state.y;
3450 else impmsg "keyboard link navigation does not work under rotation"
3452 | Escape | Ascii 'q' ->
3453 begin match state.mstate with
3454 | Mzoomrect _ ->
3455 resetmstate ();
3456 postRedisplay "kill rect";
3457 | Msel _
3458 | Mpan _
3459 | Mscrolly | Mscrollx
3460 | Mzoom _
3461 | Mnone ->
3462 begin match state.mode with
3463 | LinkNav ln ->
3464 begin match ln with
3465 | Ltexact pl -> state.lnava <- Some pl
3466 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3467 end;
3468 state.mode <- View;
3469 postRedisplay "esc leave linknav"
3470 | Birdseye _ | Textentry _ | View ->
3471 match state.ranchors with
3472 | [] -> raise Quit
3473 | (path, password, anchor, origin) :: rest ->
3474 state.ranchors <- rest;
3475 state.anchor <- anchor;
3476 state.origin <- origin;
3477 state.nameddest <- E.s;
3478 opendoc path password
3479 end;
3480 end;
3482 | Backspace ->
3483 addnavnorc ();
3484 gotoxy state.x (getnav ~-1)
3486 | Ascii 'o' ->
3487 enteroutlinemode ()
3489 | Ascii 'H' ->
3490 enterhistmode ()
3492 | Ascii 'u' ->
3493 state.rects <- [];
3494 state.text <- E.s;
3495 Hashtbl.iter (fun _ opaque ->
3496 clearmark opaque;
3497 Hashtbl.clear state.prects) state.pagemap;
3498 postRedisplay "dehighlight";
3500 | Ascii (('/' | '?') as c) ->
3501 let ondone isforw s =
3502 cbput state.hists.pat s;
3503 state.searchpattern <- s;
3504 search s isforw
3506 let s = String.make 1 c in
3507 enttext (s, E.s, Some (onhist state.hists.pat),
3508 textentry, ondone (c = '/'), true)
3510 | Ascii '+' | Ascii '=' when ctrl ->
3511 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3512 pivotzoom (conf.zoom +. incr)
3514 | Ascii '+' ->
3515 let ondone s =
3516 let n =
3517 try int_of_string s with exn ->
3518 state.text <-
3519 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3520 max_int
3522 if n != max_int
3523 then (
3524 conf.pagebias <- n;
3525 state.text <- "page bias is now " ^ string_of_int n;
3528 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3530 | Ascii '-' when ctrl ->
3531 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3532 pivotzoom (max 0.01 (conf.zoom -. decr))
3534 | Ascii '-' ->
3535 let ondone msg = state.text <- msg in
3536 enttext ("option: ", E.s, None,
3537 optentry state.mode, ondone, true)
3539 | Ascii '0' when ctrl ->
3540 if conf.zoom = 1.0
3541 then gotoxy 0 state.y
3542 else setzoom 1.0
3544 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3545 let cols =
3546 match conf.columns with
3547 | Csingle _ | Cmulti _ -> 1
3548 | Csplit (n, _) -> n
3550 let h = state.winh -
3551 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3553 let zoom = zoomforh state.winw h 0 cols in
3554 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3555 then setzoom zoom
3557 | Ascii '3' when ctrl ->
3558 let fm =
3559 match conf.fitmodel with
3560 | FitWidth -> FitProportional
3561 | FitProportional -> FitPage
3562 | FitPage -> FitWidth
3564 state.text <- "fit model: " ^ FMTE.to_string fm;
3565 reqlayout conf.angle fm
3567 | Ascii '4' when ctrl ->
3568 let zoom = getmaxw () /. float state.winw in
3569 if zoom > 0.0 then setzoom zoom
3571 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3573 | Ascii ('0'..'9' as c) when not ctrl ->
3574 let ondone s =
3575 let n =
3576 try int_of_string s with exn ->
3577 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3580 if n >= 0
3581 then (
3582 addnav ();
3583 cbput state.hists.pag (string_of_int n);
3584 gotopage1 (n + conf.pagebias - 1) 0;
3587 let pageentry text = function [@warning "-4"]
3588 | Keys.Ascii 'g' -> TEdone text
3589 | key -> intentry text key
3591 let text = String.make 1 c in
3592 enttext (":", text, Some (onhist state.hists.pag),
3593 pageentry, ondone, true)
3595 | Ascii 'b' ->
3596 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3597 postRedisplay "toggle scrollbar";
3599 | Ascii 'B' ->
3600 state.bzoom <- not state.bzoom;
3601 state.rects <- [];
3602 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3604 | Ascii 'l' ->
3605 conf.hlinks <- not conf.hlinks;
3606 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3607 postRedisplay "toggle highlightlinks";
3609 | Ascii 'F' ->
3610 if conf.angle mod 360 = 0
3611 then (
3612 state.glinks <- true;
3613 let mode = state.mode in
3614 state.mode <-
3615 Textentry (
3616 (":", E.s, None, linknentry, linknact gotounder, false),
3617 (fun _ ->
3618 state.glinks <- false;
3619 state.mode <- mode)
3621 state.text <- E.s;
3622 postRedisplay "view:linkent(F)"
3624 else impmsg "hint mode does not work under rotation"
3626 | Ascii 'y' ->
3627 state.glinks <- true;
3628 let mode = state.mode in
3629 state.mode <-
3630 Textentry (
3631 (":", E.s, None, linknentry,
3632 linknact (fun under ->
3633 selstring conf.selcmd (undertext under)), false),
3634 (fun _ ->
3635 state.glinks <- false;
3636 state.mode <- mode)
3638 state.text <- E.s;
3639 postRedisplay "view:linkent"
3641 | Ascii 'a' ->
3642 begin match state.autoscroll with
3643 | Some step ->
3644 conf.autoscrollstep <- step;
3645 state.autoscroll <- None
3646 | None ->
3647 state.autoscroll <- Some conf.autoscrollstep;
3648 state.slideshow <- state.slideshow land lnot 2
3651 | Ascii 'p' when ctrl ->
3652 launchpath () (* XXX where do error messages go? *)
3654 | Ascii 'P' ->
3655 setpresentationmode (not conf.presentation);
3656 showtext ' ' ("presentation mode " ^
3657 if conf.presentation then "on" else "off");
3659 | Ascii 'f' ->
3660 if List.mem Wsi.Fullscreen state.winstate
3661 then Wsi.reshape conf.cwinw conf.cwinh
3662 else Wsi.fullscreen ()
3664 | Ascii ('p'|'N') ->
3665 search state.searchpattern false
3667 | Ascii 'n' | Fn 3 ->
3668 search state.searchpattern true
3670 | Ascii 't' ->
3671 begin match state.layout with
3672 | [] -> ()
3673 | l :: _ -> gotoxy state.x (getpagey l.pageno)
3676 | Ascii ' ' -> nextpage ()
3677 | Delete -> prevpage ()
3678 | Ascii '=' -> showtext ' ' (describe_layout state.layout);
3680 | Ascii 'w' ->
3681 begin match state.layout with
3682 | [] -> ()
3683 | l :: _ ->
3684 Wsi.reshape l.pagew l.pageh;
3685 postRedisplay "w"
3688 | Ascii '\'' -> enterbookmarkmode ()
3689 | Ascii 'h' | Fn 1 -> enterhelpmode ()
3690 | Ascii 'i' -> enterinfomode ()
3691 | Ascii 'e' when Buffer.length state.errmsgs > 0 -> entermsgsmode ()
3693 | Ascii 'm' ->
3694 let ondone s =
3695 match state.layout with
3696 | l :: _ when nonemptystr s ->
3697 state.bookmarks <- (s, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3698 | _ -> ()
3700 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3702 | Ascii '~' ->
3703 quickbookmark ();
3704 showtext ' ' "Quick bookmark added";
3706 | Ascii 'x' -> state.roam ()
3708 | Ascii ('<'|'>' as c) ->
3709 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3711 | Ascii ('['|']' as c) ->
3712 conf.colorscale <-
3713 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3714 postRedisplay "brightness";
3716 | Ascii 'c' when state.mode = View ->
3717 if Wsi.withalt mask
3718 then (
3719 if conf.zoom > 1.0
3720 then
3721 let m = (state.winw - state.w) / 2 in
3722 gotoxy m state.y
3724 else
3725 let (c, a, b), z =
3726 match state.prevcolumns with
3727 | None -> (1, 0, 0), 1.0
3728 | Some (columns, z) ->
3729 let cab =
3730 match columns with
3731 | Csplit (c, _) -> -c, 0, 0
3732 | Cmulti ((c, a, b), _) -> c, a, b
3733 | Csingle _ -> 1, 0, 0
3735 cab, z
3737 setcolumns View c a b;
3738 setzoom z
3740 | Down | Up when ctrl && Wsi.withshift mask ->
3741 let zoom, x = state.prevzoom in
3742 setzoom zoom;
3743 state.x <- x;
3745 | Ascii 'k' | Up ->
3746 begin match state.autoscroll with
3747 | None ->
3748 begin match state.mode with
3749 | Birdseye beye -> upbirdseye 1 beye
3750 | Textentry _ | View | LinkNav _ ->
3751 if ctrl
3752 then gotoxy state.x (clamp ~-(state.winh/2))
3753 else (
3754 if not (Wsi.withshift mask) && conf.presentation
3755 then prevpage ()
3756 else gotoxy state.x (clamp (-conf.scrollstep))
3759 | Some n -> setautoscrollspeed n false
3762 | Ascii 'j' | Down ->
3763 begin match state.autoscroll with
3764 | None ->
3765 begin match state.mode with
3766 | Birdseye beye -> downbirdseye 1 beye
3767 | Textentry _ | View | LinkNav _ ->
3768 if ctrl
3769 then gotoxy state.x (clamp (state.winh/2))
3770 else (
3771 if not (Wsi.withshift mask) && conf.presentation
3772 then nextpage ()
3773 else gotoxy state.x (clamp (conf.scrollstep))
3776 | Some n -> setautoscrollspeed n true
3779 | Left | Right when not (Wsi.withalt mask) ->
3780 if canpan ()
3781 then
3782 let dx =
3783 if ctrl
3784 then state.winw / 2
3785 else conf.hscrollstep
3787 let dx =
3788 let pv = Wsi.kc2kt key in
3789 if pv = Keys.Left then dx else -dx
3791 gotoxy (panbound (state.x + dx)) state.y
3792 else (
3793 state.text <- E.s;
3794 postRedisplay "left/right"
3797 | Prior ->
3798 let y =
3799 if ctrl
3800 then
3801 match state.layout with
3802 | [] -> state.y
3803 | l :: _ -> state.y - l.pagey
3804 else clamp (pgscale (-state.winh))
3806 gotoxy state.x y
3808 | Next ->
3809 let y =
3810 if ctrl
3811 then
3812 match List.rev state.layout with
3813 | [] -> state.y
3814 | l :: _ -> getpagey l.pageno
3815 else clamp (pgscale state.winh)
3817 gotoxy state.x y
3819 | Ascii 'g' | Home ->
3820 addnav ();
3821 gotoxy 0 0
3822 | Ascii 'G' | End ->
3823 addnav ();
3824 gotoxy 0 (clamp state.maxy)
3826 | Right when Wsi.withalt mask ->
3827 addnavnorc ();
3828 gotoxy state.x (getnav 1)
3829 | Left when Wsi.withalt mask ->
3830 addnavnorc ();
3831 gotoxy state.x (getnav ~-1)
3833 | Ascii 'r' ->
3834 reload ()
3836 | Ascii 'v' when conf.debug ->
3837 state.rects <- [];
3838 List.iter (fun l ->
3839 match getopaque l.pageno with
3840 | None -> ()
3841 | Some opaque ->
3842 let x0, y0, x1, y1 = pagebbox opaque in
3843 let rect = (float x0, float y0,
3844 float x1, float y0,
3845 float x1, float y1,
3846 float x0, float y1) in
3847 debugrect rect;
3848 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3849 state.rects <- (l.pageno, color, rect) :: state.rects;
3850 ) state.layout;
3851 postRedisplay "v";
3853 | Ascii '|' ->
3854 let mode = state.mode in
3855 let cmd = ref E.s in
3856 let onleave = function
3857 | Cancel -> state.mode <- mode
3858 | Confirm ->
3859 List.iter (fun l ->
3860 match getopaque l.pageno with
3861 | Some opaque -> pipesel opaque !cmd
3862 | None -> ()) state.layout;
3863 state.mode <- mode
3865 let ondone s =
3866 cbput state.hists.sel s;
3867 cmd := s
3869 let te =
3870 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3872 postRedisplay "|";
3873 state.mode <- Textentry (te, onleave);
3875 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3876 vlog "huh? %s" (Wsi.keyname key)
3879 let linknavkeyboard key mask linknav =
3880 let pv = Wsi.kc2kt key in
3881 let getpage pageno =
3882 let rec loop = function
3883 | [] -> None
3884 | l :: _ when l.pageno = pageno -> Some l
3885 | _ :: rest -> loop rest
3886 in loop state.layout
3888 let doexact (pageno, n) =
3889 match getopaque pageno, getpage pageno with
3890 | Some opaque, Some l ->
3891 if pv = Keys.Enter
3892 then
3893 let under = getlink opaque n in
3894 postRedisplay "link gotounder";
3895 gotounder under;
3896 state.mode <- View;
3897 else
3898 let opt, dir =
3899 let open Keys in
3900 match pv with
3901 | Home -> Some (findlink opaque LDfirst), -1
3902 | End -> Some (findlink opaque LDlast), 1
3903 | Left -> Some (findlink opaque (LDleft n)), -1
3904 | Right -> Some (findlink opaque (LDright n)), 1
3905 | Up -> Some (findlink opaque (LDup n)), -1
3906 | Down -> Some (findlink opaque (LDdown n)), 1
3907 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3908 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3910 let pwl l dir =
3911 begin match findpwl l.pageno dir with
3912 | Pwlnotfound -> ()
3913 | Pwl pageno ->
3914 let notfound dir =
3915 state.mode <- LinkNav (Ltgendir dir);
3916 let y, h = getpageyh pageno in
3917 let y =
3918 if dir < 0
3919 then y + h - state.winh
3920 else y
3922 gotoxy state.x y
3924 begin match getopaque pageno, getpage pageno with
3925 | Some opaque, Some _ ->
3926 let link =
3927 let ld = if dir > 0 then LDfirst else LDlast in
3928 findlink opaque ld
3930 begin match link with
3931 | Lfound m ->
3932 showlinktype (getlink opaque m);
3933 state.mode <- LinkNav (Ltexact (pageno, m));
3934 postRedisplay "linknav jpage";
3935 | Lnotfound -> notfound dir
3936 end;
3937 | _ -> notfound dir
3938 end;
3939 end;
3941 begin match opt with
3942 | Some Lnotfound -> pwl l dir;
3943 | Some (Lfound m) ->
3944 if m = n
3945 then pwl l dir
3946 else (
3947 let _, y0, _, y1 = getlinkrect opaque m in
3948 if y0 < l.pagey
3949 then gotopage1 l.pageno y0
3950 else (
3951 let d = fstate.fontsize + 1 in
3952 if y1 - l.pagey > l.pagevh - d
3953 then gotopage1 l.pageno (y1 - state.winh + d)
3954 else postRedisplay "linknav";
3956 showlinktype (getlink opaque m);
3957 state.mode <- LinkNav (Ltexact (l.pageno, m));
3960 | None -> viewkeyboard key mask
3961 end;
3962 | _ -> viewkeyboard key mask
3964 if pv = Keys.Insert
3965 then (
3966 begin match linknav with
3967 | Ltexact pa -> state.lnava <- Some pa
3968 | Ltgendir _ | Ltnotready _ -> ()
3969 end;
3970 state.mode <- View;
3971 postRedisplay "leave linknav"
3973 else
3974 match linknav with
3975 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3976 | Ltexact exact -> doexact exact
3979 let keyboard key mask =
3980 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
3981 then wcmd "interrupt"
3982 else state.uioh <- state.uioh#key key mask
3985 let birdseyekeyboard key mask
3986 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3987 let incr =
3988 match conf.columns with
3989 | Csingle _ -> 1
3990 | Cmulti ((c, _, _), _) -> c
3991 | Csplit _ -> error "bird's eye split mode"
3993 let pgh layout = List.fold_left
3994 (fun m l -> max l.pageh m) state.winh layout in
3995 let open Keys in
3996 match Wsi.kc2kt key with
3997 | Ascii 'l' when Wsi.withctrl mask ->
3998 let y, h = getpageyh pageno in
3999 let top = (state.winh - h) / 2 in
4000 gotoxy state.x (max 0 (y - top))
4001 | Enter -> leavebirdseye beye false
4002 | Escape -> leavebirdseye beye true
4003 | Up -> upbirdseye incr beye
4004 | Down -> downbirdseye incr beye
4005 | Left -> upbirdseye 1 beye
4006 | Right -> downbirdseye 1 beye
4008 | Prior ->
4009 begin match state.layout with
4010 | l :: _ ->
4011 if l.pagey != 0
4012 then (
4013 state.mode <- Birdseye (
4014 oconf, leftx, l.pageno, hooverpageno, anchor
4016 gotopage1 l.pageno 0;
4018 else (
4019 let layout = layout state.x (state.y-state.winh)
4020 state.winw
4021 (pgh state.layout) in
4022 match layout with
4023 | [] -> gotoxy state.x (clamp (-state.winh))
4024 | l :: _ ->
4025 state.mode <- Birdseye (
4026 oconf, leftx, l.pageno, hooverpageno, anchor
4028 gotopage1 l.pageno 0
4031 | [] -> gotoxy state.x (clamp (-state.winh))
4032 end;
4034 | Next ->
4035 begin match List.rev state.layout with
4036 | l :: _ ->
4037 let layout = layout state.x
4038 (state.y + (pgh state.layout))
4039 state.winw state.winh in
4040 begin match layout with
4041 | [] ->
4042 let incr = l.pageh - l.pagevh in
4043 if incr = 0
4044 then (
4045 state.mode <-
4046 Birdseye (
4047 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4049 postRedisplay "birdseye pagedown";
4051 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4053 | l :: _ ->
4054 state.mode <-
4055 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4056 gotopage1 l.pageno 0;
4059 | [] -> gotoxy state.x (clamp state.winh)
4060 end;
4062 | Home ->
4063 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4064 gotopage1 0 0
4066 | End ->
4067 let pageno = state.pagecount - 1 in
4068 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4069 if not (pagevisible state.layout pageno)
4070 then
4071 let h =
4072 match List.rev state.pdims with
4073 | [] -> state.winh
4074 | (_, _, h, _) :: _ -> h
4076 gotoxy
4077 state.x
4078 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4079 else postRedisplay "birdseye end";
4081 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4084 let drawpage l =
4085 let color =
4086 match state.mode with
4087 | Textentry _ -> scalecolor 0.4
4088 | LinkNav _ | View -> scalecolor 1.0
4089 | Birdseye (_, _, pageno, hooverpageno, _) ->
4090 if l.pageno = hooverpageno
4091 then scalecolor 0.9
4092 else (
4093 if l.pageno = pageno
4094 then (
4095 let c = scalecolor 1.0 in
4096 GlDraw.color c;
4097 GlDraw.line_width 3.0;
4098 let dispx = l.pagedispx in
4099 linerect
4100 (float (dispx-1)) (float (l.pagedispy-1))
4101 (float (dispx+l.pagevw+1))
4102 (float (l.pagedispy+l.pagevh+1));
4103 GlDraw.line_width 1.0;
4106 else scalecolor 0.8
4109 drawtiles l color;
4112 let postdrawpage l linkindexbase =
4113 match getopaque l.pageno with
4114 | Some opaque ->
4115 if tileready l l.pagex l.pagey
4116 then
4117 let x = l.pagedispx - l.pagex
4118 and y = l.pagedispy - l.pagey in
4119 let hlmask =
4120 match conf.columns with
4121 | Csingle _ | Cmulti _ ->
4122 (if conf.hlinks then 1 else 0)
4123 + (if state.glinks
4124 && not (isbirdseye state.mode) then 2 else 0)
4125 | Csplit _ -> 0
4127 let s =
4128 match state.mode with
4129 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4130 | Textentry _
4131 | Birdseye _
4132 | View
4133 | LinkNav _ -> E.s
4135 Hashtbl.find_all state.prects l.pageno |>
4136 List.iter (fun vals -> drawprect opaque x y vals);
4137 let n = postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4138 if n < 0
4139 then (Glutils.redisplay := true; 0)
4140 else n
4141 else 0
4142 | _ -> 0
4145 let scrollindicator () =
4146 let sbw, ph, sh = state.uioh#scrollph in
4147 let sbh, pw, sw = state.uioh#scrollpw in
4149 let x0,x1,hx0 =
4150 if conf.leftscroll
4151 then (0, sbw, sbw)
4152 else ((state.winw - sbw), state.winw, 0)
4155 Gl.enable `blend;
4156 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4157 let (r, g, b, alpha) = conf.sbarcolor in
4158 GlDraw.color (r, g, b) ~alpha;
4159 filledrect (float x0) 0. (float x1) (float state.winh);
4160 filledrect
4161 (float hx0) (float (state.winh - sbh))
4162 (float (hx0 + state.winw)) (float state.winh);
4163 let (r, g, b, alpha) = conf.sbarhndlcolor in
4164 GlDraw.color (r, g, b) ~alpha;
4166 filledrect (float x0) ph (float x1) (ph +. sh);
4167 let pw = pw +. float hx0 in
4168 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4169 Gl.disable `blend;
4172 let showsel () =
4173 match state.mstate with
4174 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
4175 | Msel ((x0, y0), (x1, y1)) ->
4176 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4177 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4178 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4179 if n0 != -1 && n0 = n1 then seltext o0 (px0, py0, px1, py1);
4182 let showrects = function
4183 | [] -> ()
4184 | rects ->
4185 Gl.enable `blend;
4186 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4187 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4188 List.iter
4189 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4190 List.iter (fun l ->
4191 if l.pageno = pageno
4192 then (
4193 let dx = float (l.pagedispx - l.pagex) in
4194 let dy = float (l.pagedispy - l.pagey) in
4195 let r, g, b, alpha = c in
4196 GlDraw.color (r, g, b) ~alpha;
4197 filledrect2
4198 (x0+.dx) (y0+.dy)
4199 (x1+.dx) (y1+.dy)
4200 (x3+.dx) (y3+.dy)
4201 (x2+.dx) (y2+.dy);
4203 ) state.layout
4204 ) rects;
4205 Gl.disable `blend;
4208 let display () =
4209 GlDraw.color (scalecolor2 conf.bgcolor);
4210 GlClear.color (scalecolor2 conf.bgcolor);
4211 GlClear.clear [`color];
4212 List.iter drawpage state.layout;
4213 let rects =
4214 match state.mode with
4215 | LinkNav (Ltexact (pageno, linkno)) ->
4216 begin match getopaque pageno with
4217 | Some opaque ->
4218 let x0, y0, x1, y1 = getlinkrect opaque linkno in
4219 let color = (0.0, 0.0, 0.5, 0.5) in
4220 (pageno, color,
4221 (float x0, float y0,
4222 float x1, float y0,
4223 float x1, float y1,
4224 float x0, float y1)
4225 ) :: state.rects
4226 | None -> state.rects
4228 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4229 | Birdseye _
4230 | Textentry _
4231 | View -> state.rects
4233 showrects rects;
4234 let rec postloop linkindexbase = function
4235 | l :: rest ->
4236 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4237 postloop linkindexbase rest
4238 | [] -> ()
4240 showsel ();
4241 postloop 0 state.layout;
4242 state.uioh#display;
4243 begin match state.mstate with
4244 | Mzoomrect ((x0, y0), (x1, y1)) ->
4245 Gl.enable `blend;
4246 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4247 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4248 filledrect (float x0) (float y0) (float x1) (float y1);
4249 Gl.disable `blend;
4250 | Msel _
4251 | Mpan _
4252 | Mscrolly | Mscrollx
4253 | Mzoom _
4254 | Mnone -> ()
4255 end;
4256 enttext ();
4257 scrollindicator ();
4258 Wsi.swapb ();
4261 let zoomrect x y x1 y1 =
4262 let x0 = min x x1
4263 and x1 = max x x1
4264 and y0 = min y y1 in
4265 let zoom = (float state.w) /. float (x1 - x0) in
4266 let margin =
4267 let simple () =
4268 if state.w < state.winw
4269 then (state.winw - state.w) / 2
4270 else 0
4272 match conf.fitmodel with
4273 | FitWidth | FitProportional -> simple ()
4274 | FitPage ->
4275 match conf.columns with
4276 | Csplit _ ->
4277 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4278 | Cmulti _ | Csingle _ -> simple ()
4280 gotoxy ((state.x + margin) - x0) (state.y + y0);
4281 state.anchor <- getanchor ();
4282 setzoom zoom;
4283 resetmstate ();
4286 let annot inline x y =
4287 match unproject x y with
4288 | Some (opaque, n, ux, uy) ->
4289 let add text =
4290 addannot opaque ux uy text;
4291 wcmd "freepage %s" (~> opaque);
4292 Hashtbl.remove state.pagemap (n, state.gen);
4293 flushtiles ();
4294 gotoxy state.x state.y
4296 if inline
4297 then
4298 let ondone s = add s in
4299 let mode = state.mode in
4300 state.mode <- Textentry (
4301 ("annotation: ", E.s, None, textentry, ondone, true),
4302 fun _ -> state.mode <- mode);
4303 state.text <- E.s;
4304 enttext ();
4305 postRedisplay "annot"
4306 else add @@ getusertext E.s
4307 | _ -> ()
4310 let zoomblock x y =
4311 let g opaque l px py =
4312 match rectofblock opaque px py with
4313 | Some a ->
4314 let x0 = a.(0) -. 20. in
4315 let x1 = a.(1) +. 20. in
4316 let y0 = a.(2) -. 20. in
4317 let zoom = (float state.w) /. (x1 -. x0) in
4318 let pagey = getpagey l.pageno in
4319 let margin = (state.w - l.pagew)/2 in
4320 let nx = -truncate x0 - margin in
4321 gotoxy nx (pagey + truncate y0);
4322 state.anchor <- getanchor ();
4323 setzoom zoom;
4324 None
4325 | None -> None
4327 match conf.columns with
4328 | Csplit _ ->
4329 impmsg "block zooming does not work properly in split columns mode"
4330 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4333 let scrollx x =
4334 let winw = state.winw - 1 in
4335 let s = float x /. float winw in
4336 let destx = truncate (float (state.w + winw) *. s) in
4337 gotoxy (winw - destx) state.y;
4338 state.mstate <- Mscrollx;
4341 let scrolly y =
4342 let s = float y /. float state.winh in
4343 let desty = truncate (s *. float (maxy ())) in
4344 gotoxy state.x desty;
4345 state.mstate <- Mscrolly;
4348 let viewmulticlick clicks x y mask =
4349 let g opaque l px py =
4350 let mark =
4351 match clicks with
4352 | 2 -> Mark_word
4353 | 3 -> Mark_line
4354 | 4 -> Mark_block
4355 | _ -> Mark_page
4357 if markunder opaque px py mark
4358 then (
4359 Some (fun () ->
4360 let dopipe cmd =
4361 match getopaque l.pageno with
4362 | None -> ()
4363 | Some opaque -> pipesel opaque cmd
4365 state.roam <- (fun () -> dopipe conf.paxcmd);
4366 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4369 else None
4371 postRedisplay "viewmulticlick";
4372 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4375 let canselect () =
4376 match conf.columns with
4377 | Csplit _ -> false
4378 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4381 let viewmouse button down x y mask =
4382 match button with
4383 | n when (n == 4 || n == 5) && not down ->
4384 if Wsi.withctrl mask
4385 then (
4386 let incr =
4387 if n = 5
4388 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4389 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4391 let fx, fy =
4392 match state.mstate with
4393 | Mzoom (oldn, _, pos) when n = oldn -> pos
4394 | Mzoomrect _ | Mnone | Mpan _
4395 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4397 let zoom = conf.zoom -. incr in
4398 state.mstate <- Mzoom (n, 0, (x, y));
4399 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4400 then pivotzoom ~x ~y zoom
4401 else pivotzoom zoom
4403 else (
4404 match state.autoscroll with
4405 | Some step -> setautoscrollspeed step (n=4)
4406 | None ->
4407 if conf.wheelbypage || conf.presentation
4408 then (
4409 if n = 4
4410 then prevpage ()
4411 else nextpage ()
4413 else
4414 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4415 let incr = incr * 2 in
4416 let y = clamp incr in
4417 gotoxy state.x y
4420 | n when (n = 6 || n = 7) && not down && canpan () ->
4421 let x =
4422 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4423 gotoxy x state.y
4425 | 1 when Wsi.withshift mask ->
4426 state.mstate <- Mnone;
4427 if not down
4428 then (
4429 match unproject x y with
4430 | None -> ()
4431 | Some (_, pageno, ux, uy) ->
4432 let cmd = Printf.sprintf
4433 "%s %s %d %d %d"
4434 conf.stcmd state.path pageno ux uy
4436 match spawn cmd [] with
4437 | exception exn ->
4438 impmsg "execution of synctex command(%S) failed: %S"
4439 conf.stcmd @@ exntos exn
4440 | _pid -> ()
4443 | 1 when Wsi.withctrl mask ->
4444 if down
4445 then (
4446 Wsi.setcursor Wsi.CURSOR_FLEUR;
4447 state.mstate <- Mpan (x, y)
4449 else state.mstate <- Mnone
4451 | 3 ->
4452 if down
4453 then (
4454 if Wsi.withshift mask
4455 then (
4456 annot conf.annotinline x y;
4457 postRedisplay "addannot"
4459 else
4460 let p = (x, y) in
4461 Wsi.setcursor Wsi.CURSOR_CYCLE;
4462 state.mstate <- Mzoomrect (p, p)
4464 else (
4465 match state.mstate with
4466 | Mzoomrect ((x0, y0), _) ->
4467 if abs (x-x0) > 10 && abs (y - y0) > 10
4468 then zoomrect x0 y0 x y
4469 else (
4470 resetmstate ();
4471 postRedisplay "kill accidental zoom rect";
4473 | Msel _
4474 | Mpan _
4475 | Mscrolly | Mscrollx
4476 | Mzoom _
4477 | Mnone -> resetmstate ()
4480 | 1 when vscrollhit x ->
4481 if down
4482 then
4483 let _, position, sh = state.uioh#scrollph in
4484 if y > truncate position && y < truncate (position +. sh)
4485 then state.mstate <- Mscrolly
4486 else scrolly y
4487 else state.mstate <- Mnone
4489 | 1 when y > state.winh - hscrollh () ->
4490 if down
4491 then
4492 let _, position, sw = state.uioh#scrollpw in
4493 if x > truncate position && x < truncate (position +. sw)
4494 then state.mstate <- Mscrollx
4495 else scrollx x
4496 else state.mstate <- Mnone
4498 | 1 when state.bzoom -> if not down then zoomblock x y
4500 | 1 ->
4501 let dest = if down then getunder x y else Unone in
4502 begin match dest with
4503 | Ulinkuri _ -> gotounder dest
4504 | Unone when down ->
4505 Wsi.setcursor Wsi.CURSOR_FLEUR;
4506 state.mstate <- Mpan (x, y);
4507 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4508 | Unone | Utext _ ->
4509 if down
4510 then (
4511 if canselect ()
4512 then (
4513 state.mstate <- Msel ((x, y), (x, y));
4514 postRedisplay "mouse select";
4517 else (
4518 match state.mstate with
4519 | Mnone -> ()
4520 | Mzoom _ | Mscrollx | Mscrolly -> state.mstate <- Mnone
4521 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4522 | Mpan _ ->
4523 Wsi.setcursor Wsi.CURSOR_INHERIT;
4524 state.mstate <- Mnone
4525 | Msel ((x0, y0), (x1, y1)) ->
4526 let rec loop = function
4527 | [] -> ()
4528 | l :: rest ->
4529 let inside =
4530 let a0 = l.pagedispy in
4531 let a1 = a0 + l.pagevh in
4532 let b0 = l.pagedispx in
4533 let b1 = b0 + l.pagevw in
4534 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4535 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4537 if inside
4538 then
4539 match getopaque l.pageno with
4540 | Some opaque ->
4541 let dosel cmd () =
4542 pipef ~closew:false "Msel"
4543 (fun w ->
4544 copysel w opaque;
4545 postRedisplay "Msel") cmd
4547 dosel conf.selcmd ();
4548 state.roam <- dosel conf.paxcmd;
4549 | None -> ()
4550 else loop rest
4552 loop state.layout;
4553 resetmstate ();
4556 | _ -> ()
4559 let birdseyemouse button down x y mask
4560 (conf, leftx, _, hooverpageno, anchor) =
4561 match button with
4562 | 1 when down ->
4563 let rec loop = function
4564 | [] -> ()
4565 | l :: rest ->
4566 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4567 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4568 then (
4569 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4571 else loop rest
4573 loop state.layout
4574 | 3 -> ()
4575 | _ -> viewmouse button down x y mask
4578 let uioh = object
4579 method display = ()
4581 method key key mask =
4582 begin match state.mode with
4583 | Textentry textentry -> textentrykeyboard key mask textentry
4584 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4585 | View -> viewkeyboard key mask
4586 | LinkNav linknav -> linknavkeyboard key mask linknav
4587 end;
4588 state.uioh
4590 method button button bstate x y mask =
4591 begin match state.mode with
4592 | LinkNav _ | View -> viewmouse button bstate x y mask
4593 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4594 | Textentry _ -> ()
4595 end;
4596 state.uioh
4598 method multiclick clicks x y mask =
4599 begin match state.mode with
4600 | LinkNav _ | View -> viewmulticlick clicks x y mask
4601 | Birdseye _ | Textentry _ -> ()
4602 end;
4603 state.uioh
4605 method motion x y =
4606 begin match state.mode with
4607 | Textentry _ -> ()
4608 | View | Birdseye _ | LinkNav _ ->
4609 match state.mstate with
4610 | Mzoom _ | Mnone -> ()
4611 | Mpan (x0, y0) ->
4612 let dx = x - x0
4613 and dy = y0 - y in
4614 state.mstate <- Mpan (x, y);
4615 let x = if canpan () then panbound (state.x + dx) else state.x in
4616 let y = clamp dy in
4617 gotoxy x y
4619 | Msel (a, _) ->
4620 state.mstate <- Msel (a, (x, y));
4621 postRedisplay "motion select";
4623 | Mscrolly ->
4624 let y = min state.winh (max 0 y) in
4625 scrolly y
4627 | Mscrollx ->
4628 let x = min state.winw (max 0 x) in
4629 scrollx x
4631 | Mzoomrect (p0, _) ->
4632 state.mstate <- Mzoomrect (p0, (x, y));
4633 postRedisplay "motion zoomrect";
4634 end;
4635 state.uioh
4637 method pmotion x y =
4638 begin match state.mode with
4639 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4640 let rec loop = function
4641 | [] ->
4642 if hooverpageno != -1
4643 then (
4644 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4645 postRedisplay "pmotion birdseye no hoover";
4647 | l :: rest ->
4648 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4649 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4650 then (
4651 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4652 postRedisplay "pmotion birdseye hoover";
4654 else loop rest
4656 loop state.layout
4658 | Textentry _ -> ()
4660 | LinkNav _ | View ->
4661 match state.mstate with
4662 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4663 | Mnone ->
4664 updateunder x y;
4665 if canselect ()
4666 then
4667 match conf.pax with
4668 | None -> ()
4669 | Some past ->
4670 let now = now () in
4671 let delta = now -. past in
4672 if delta > 0.01
4673 then paxunder x y
4674 else conf.pax <- Some now
4675 end;
4676 state.uioh
4678 method infochanged _ = ()
4680 method scrollph =
4681 let maxy = maxy () in
4682 let p, h =
4683 if maxy = 0
4684 then 0.0, float state.winh
4685 else scrollph state.y maxy
4687 vscrollw (), p, h
4689 method scrollpw =
4690 let fwinw = float (state.winw - vscrollw ()) in
4691 let sw =
4692 let sw = fwinw /. float state.w in
4693 let sw = fwinw *. sw in
4694 max sw (float conf.scrollh)
4696 let position =
4697 let maxx = state.w + state.winw in
4698 let x = state.winw - state.x in
4699 let percent = float x /. float maxx in
4700 (fwinw -. sw) *. percent
4702 hscrollh (), position, sw
4704 method modehash =
4705 let modename =
4706 match state.mode with
4707 | LinkNav _ -> "links"
4708 | Textentry _ -> "textentry"
4709 | Birdseye _ -> "birdseye"
4710 | View -> "view"
4712 findkeyhash conf modename
4714 method eformsgs = true
4715 method alwaysscrolly = false
4716 method scroll dx dy =
4717 let x = if canpan () then panbound (state.x + dx) else state.x in
4718 gotoxy x (clamp (2 * dy));
4719 state.uioh
4720 method zoom z x y =
4721 pivotzoom ~x ~y (conf.zoom *. exp z);
4722 end;;
4724 let addrect pageno r g b a x0 y0 x1 y1 =
4725 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4728 let ract cmds =
4729 let cl = splitatchar cmds ' ' in
4730 let scan s fmt f =
4731 try Scanf.sscanf s fmt f
4732 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4733 cmds @@ exntos exn
4735 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4736 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4737 s pageno r g b a x0 y0 x1 y1;
4738 onpagerect
4739 pageno
4740 (fun w h ->
4741 let _,w1,h1,_ = getpagedim pageno in
4742 let sw = float w1 /. float w
4743 and sh = float h1 /. float h in
4744 let x0s = x0 *. sw
4745 and x1s = x1 *. sw
4746 and y0s = y0 *. sh
4747 and y1s = y1 *. sh in
4748 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4749 let color = (r, g, b, a) in
4750 if conf.verbose then debugrect rect;
4751 state.rects <- (pageno, color, rect) :: state.rects;
4752 postRedisplay s;
4755 match cl with
4756 | "reload", "" -> reload ()
4757 | "goto", args ->
4758 scan args "%u %f %f"
4759 (fun pageno x y ->
4760 let cmd, _ = state.geomcmds in
4761 if emptystr cmd
4762 then gotopagexy pageno x y
4763 else
4764 let f prevf () =
4765 gotopagexy pageno x y;
4766 prevf ()
4768 state.reprf <- f state.reprf
4770 | "goto1", args -> scan args "%u %f" gotopage
4771 | "gotor", args -> scan args "%S" gotoremote
4772 | "rect", args ->
4773 scan args "%u %u %f %f %f %f"
4774 (fun pageno c x0 y0 x1 y1 ->
4775 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4776 rectx "rect" pageno color x0 y0 x1 y1;
4778 | "prect", args ->
4779 scan args "%u %f %f %f %f %f %f %f %f"
4780 (fun pageno r g b alpha x0 y0 x1 y1 ->
4781 addrect pageno r g b alpha x0 y0 x1 y1;
4782 postRedisplay "prect"
4784 | "pgoto", args ->
4785 scan args "%u %f %f"
4786 (fun pageno x y ->
4787 let optopaque =
4788 match getopaque pageno with
4789 | Some opaque -> opaque
4790 | None -> ~< E.s
4792 pgoto optopaque pageno x y;
4793 let rec fixx = function
4794 | [] -> ()
4795 | l :: rest ->
4796 if l.pageno = pageno
4797 then gotoxy (state.x - l.pagedispx) state.y
4798 else fixx rest
4800 let layout =
4801 let mult =
4802 match conf.columns with
4803 | Csingle _ | Csplit _ -> 1
4804 | Cmulti ((n, _, _), _) -> n
4806 layout 0 state.y (state.winw * mult) state.winh
4808 fixx layout
4810 | "activatewin", "" -> Wsi.activatewin ()
4811 | "quit", "" -> raise Quit
4812 | "keys", keys ->
4813 begin try
4814 let l = Config.keys_of_string keys in
4815 List.iter (fun (k, m) -> keyboard k m) l
4816 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4817 cmds @@ exntos exn
4819 | "clearrects", "" ->
4820 Hashtbl.clear state.prects;
4821 postRedisplay "clearrects"
4822 | _ ->
4823 adderrfmt "remote command"
4824 "error processing remote command: %S\n" cmds;
4827 let remote =
4828 let scratch = Bytes.create 80 in
4829 let buf = Buffer.create 80 in
4830 fun fd ->
4831 match tempfailureretry (Unix.read fd scratch 0) 80 with
4832 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4833 | 0 ->
4834 Unix.close fd;
4835 if Buffer.length buf > 0
4836 then (
4837 let s = Buffer.contents buf in
4838 Buffer.clear buf;
4839 ract s;
4841 None
4842 | n ->
4843 let rec eat ppos =
4844 let nlpos =
4845 match Bytes.index_from scratch ppos '\n' with
4846 | pos -> if pos >= n then -1 else pos
4847 | exception Not_found -> -1
4849 if nlpos >= 0
4850 then (
4851 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4852 let s = Buffer.contents buf in
4853 Buffer.clear buf;
4854 ract s;
4855 eat (nlpos+1);
4857 else (
4858 Buffer.add_subbytes buf scratch ppos (n-ppos);
4859 Some fd
4861 in eat 0
4864 let remoteopen path =
4865 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4866 with exn ->
4867 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4868 None
4871 let () =
4872 let gcconfig = ref false in
4873 let trimcachepath = ref E.s in
4874 let rcmdpath = ref E.s in
4875 let pageno = ref None in
4876 let openlast = ref false in
4877 let doreap = ref false in
4878 let csspath = ref None in
4879 selfexec := Sys.executable_name;
4880 Arg.parse
4881 (Arg.align
4882 [("-p", Arg.String (fun s -> state.password <- s),
4883 "<password> Set password");
4885 ("-f", Arg.String
4886 (fun s ->
4887 Config.fontpath := s;
4888 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4890 "<path> Set path to the user interface font");
4892 ("-c", Arg.String
4893 (fun s ->
4894 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4895 Config.confpath := s),
4896 "<path> Set path to the configuration file");
4898 ("-last", Arg.Set openlast, " Open last document");
4900 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4901 "<page-number> Jump to page");
4903 ("-tcf", Arg.String (fun s -> trimcachepath := s),
4904 "<path> Set path to the trim cache file");
4906 ("-dest", Arg.String (fun s -> state.nameddest <- s),
4907 "<named-destination> Set named destination");
4909 ("-remote", Arg.String (fun s -> rcmdpath := s),
4910 "<path> Set path to the source of remote commands");
4912 ("-gc", Arg.Set gcconfig, " Collect config garbage");
4914 ("-v", Arg.Unit (fun () ->
4915 Printf.printf
4916 "%s\nconfiguration file: %s\n"
4917 (Help.version ())
4918 Config.defconfpath;
4919 exit 0), " Print version and exit");
4921 ("-css", Arg.String (fun s -> csspath := Some s),
4922 "<path> Set path to the style sheet to use with EPUB/HTML");
4924 ("-origin", Arg.String (fun s -> state.origin <- s),
4925 "<origin> <undocumented>");
4927 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
4928 ("-layout-height", Arg.Set_int layouth,
4929 "<height> layout height html/epub/etc (-1, 0, N)");
4932 (fun s -> state.path <- s)
4933 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4935 let histmode = emptystr state.path && not !openlast in
4937 if not (Config.load !openlast)
4938 then dolog "failed to load configuration";
4940 begin match !pageno with
4941 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
4942 | None -> ()
4943 end;
4945 fillhelp ();
4946 if !gcconfig
4947 then (
4948 Config.gc ();
4949 exit 0
4952 let mu =
4953 object (self)
4954 val mutable m_clicks = 0
4955 val mutable m_click_x = 0
4956 val mutable m_click_y = 0
4957 val mutable m_lastclicktime = infinity
4959 method private cleanup =
4960 state.roam <- noroam;
4961 Hashtbl.iter (fun _ opaque -> clearmark opaque) state.pagemap
4962 method expose = postRedisplay "expose"
4963 method visible v =
4964 let name =
4965 match v with
4966 | Wsi.Unobscured -> "unobscured"
4967 | Wsi.PartiallyObscured -> "partiallyobscured"
4968 | Wsi.FullyObscured -> "fullyobscured"
4970 vlog "visibility change %s" name
4971 method display = display ()
4972 method map mapped = vlog "mapped %b" mapped
4973 method reshape w h =
4974 self#cleanup;
4975 reshape w h
4976 method mouse b d x y m =
4977 if d && canselect ()
4978 then (
4980 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
4982 m_click_x <- x;
4983 m_click_y <- y;
4984 if b = 1
4985 then (
4986 let t = now () in
4987 if abs x - m_click_x > 10
4988 || abs y - m_click_y > 10
4989 || abs_float (t -. m_lastclicktime) > 0.3
4990 then m_clicks <- 0;
4991 m_clicks <- m_clicks + 1;
4992 m_lastclicktime <- t;
4993 if m_clicks = 1
4994 then (
4995 self#cleanup;
4996 postRedisplay "cleanup";
4997 state.uioh <- state.uioh#button b d x y m;
4999 else state.uioh <- state.uioh#multiclick m_clicks x y m
5001 else (
5002 self#cleanup;
5003 m_clicks <- 0;
5004 m_lastclicktime <- infinity;
5005 state.uioh <- state.uioh#button b d x y m
5008 else state.uioh <- state.uioh#button b d x y m
5009 method motion x y =
5010 state.mpos <- (x, y);
5011 state.uioh <- state.uioh#motion x y
5012 method pmotion x y =
5013 state.mpos <- (x, y);
5014 state.uioh <- state.uioh#pmotion x y
5015 method key k m =
5016 vlog "k=%#x m=%#x" k m;
5017 let mascm = m land (
5018 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
5019 ) in
5020 let keyboard k m =
5021 let x = state.x and y = state.y in
5022 keyboard k m;
5023 if x != state.x || y != state.y then self#cleanup
5025 match state.keystate with
5026 | KSnone ->
5027 let km = k, mascm in
5028 begin
5029 match
5030 let modehash = state.uioh#modehash in
5031 try Hashtbl.find modehash km
5032 with Not_found ->
5033 try Hashtbl.find (findkeyhash conf "global") km
5034 with Not_found -> KMinsrt (k, m)
5035 with
5036 | KMinsrt (k, m) -> keyboard k m
5037 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5038 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5040 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5041 List.iter (fun (k, m) -> keyboard k m) insrt;
5042 state.keystate <- KSnone
5043 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5044 state.keystate <- KSinto (keys, insrt)
5045 | KSinto _ -> state.keystate <- KSnone
5047 method enter x y =
5048 state.mpos <- (x, y);
5049 state.uioh <- state.uioh#pmotion x y
5050 method leave = state.mpos <- (-1, -1)
5051 method winstate wsl = state.winstate <- wsl
5052 method quit : 'a. 'a = raise Quit
5053 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5054 method zoom z x y = state.uioh#zoom z x y
5055 method opendoc path =
5056 state.mode <- View;
5057 state.uioh <- uioh;
5058 postRedisplay "opendoc";
5059 opendoc path state.password
5062 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5063 state.wsfd <- wsfd;
5065 if not @@ List.exists GlMisc.check_extension
5066 [ "GL_ARB_texture_rectangle"
5067 ; "GL_EXT_texture_recangle"
5068 ; "GL_NV_texture_rectangle" ]
5069 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5071 if substratis (GlMisc.get_string `renderer) 0 "Mesa DRI Intel("
5072 then (
5073 defconf.sliceheight <- 1024;
5074 defconf.texcount <- 32;
5075 defconf.usepbo <- true;
5078 let cs, ss =
5079 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5080 | exception exn ->
5081 dolog "socketpair failed: %s" @@ exntos exn;
5082 exit 1
5083 | (r, w) ->
5084 cloexec r;
5085 cloexec w;
5086 r, w
5089 setcheckers conf.checkers;
5091 opengl_has_pbo := GlMisc.check_extension "GL_ARB_pixel_buffer_object";
5093 begin match !csspath with
5094 | None -> ()
5095 | Some "" -> conf.css <- E.s
5096 | Some path ->
5097 let css = filecontents path in
5098 let l = String.length css in
5099 conf.css <-
5100 if substratis css (l-2) "\r\n"
5101 then String.sub css 0 (l-2)
5102 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
5103 end;
5104 init cs (
5105 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5106 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5107 !Config.fontpath, !trimcachepath, !opengl_has_pbo
5109 List.iter GlArray.enable [`texture_coord; `vertex];
5110 state.ss <- ss;
5111 reshape ~firsttime:true winw winh;
5112 state.uioh <- uioh;
5113 if histmode
5114 then (
5115 Wsi.settitle "llpp (history)";
5116 enterhistmode ();
5118 else (
5119 state.text <- "Opening " ^ (mbtoutf8 state.path);
5120 opendoc state.path state.password;
5122 display ();
5123 Wsi.mapwin ();
5124 Wsi.setcursor Wsi.CURSOR_INHERIT;
5125 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5127 let rec reap () =
5128 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5129 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5130 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5131 | 0, _ -> ()
5132 | _pid, _status -> reap ()
5134 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5136 let optrfd =
5137 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
5140 let rec loop deadline =
5141 if !doreap
5142 then (
5143 doreap := false;
5144 reap ()
5146 let r = [state.ss; state.wsfd] in
5147 let r =
5148 match !optrfd with
5149 | None -> r
5150 | Some fd -> fd :: r
5152 if !redisplay
5153 then (
5154 Glutils.redisplay := false;
5155 display ();
5157 let timeout =
5158 let now = now () in
5159 if deadline > now
5160 then (
5161 if deadline = infinity
5162 then ~-.1.0
5163 else max 0.0 (deadline -. now)
5165 else 0.0
5167 let r, _, _ =
5168 try Unix.select r [] [] timeout
5169 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5171 begin match r with
5172 | [] ->
5173 let newdeadline =
5174 match state.autoscroll with
5175 | Some step when step != 0 ->
5176 if state.slideshow land 1 = 1
5177 then (
5178 if state.slideshow land 2 = 0
5179 then state.slideshow <- state.slideshow lor 2
5180 else if step < 0 then prevpage () else nextpage ();
5181 deadline +. (float (abs step))
5183 else
5184 let y = state.y + step in
5185 let fy = if conf.maxhfit then state.winh else 0 in
5186 let y =
5187 if y < 0
5188 then state.maxy - fy
5189 else if y >= state.maxy - fy then 0 else y
5191 gotoxy state.x y;
5192 deadline +. 0.01
5193 | _ -> infinity
5195 loop newdeadline
5197 | l ->
5198 let rec checkfds = function
5199 | [] -> ()
5200 | fd :: rest when fd = state.ss ->
5201 let cmd = rcmd state.ss in
5202 act cmd;
5203 checkfds rest
5205 | fd :: rest when fd = state.wsfd ->
5206 Wsi.readresp fd;
5207 checkfds rest
5209 | fd :: rest when Some fd = !optrfd ->
5210 begin match remote fd with
5211 | None -> optrfd := remoteopen !rcmdpath;
5212 | opt -> optrfd := opt
5213 end;
5214 checkfds rest
5216 | _ :: rest ->
5217 dolog "select returned unknown descriptor";
5218 checkfds rest
5220 checkfds l;
5221 let newdeadline =
5222 let deadline1 =
5223 if deadline = infinity
5224 then now () +. 0.01
5225 else deadline
5227 match state.autoscroll with
5228 | Some step when step != 0 -> deadline1
5229 | _ -> infinity
5231 loop newdeadline
5232 end;
5234 match loop infinity with
5235 | exception Quit ->
5236 Config.save leavebirdseye;
5237 if hasunsavedchanges ()
5238 then save ()
5239 | _ -> error "umpossible - infinity reached"