CRC over MD5
[llpp.git] / main.ml
blobaac704c4594462c4758a9345554f1adc8be821d8
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 _debugl l =
64 dolog {|l %d dim=%d {
65 WxH %dx%d
66 vWxH %dx%d
67 pagex,y %d,%d
68 dispx,y %d,%d
69 column %d
70 }|}
71 l.pageno l.pagedimno
72 l.pagew l.pageh
73 l.pagevw l.pagevh
74 l.pagex l.pagey
75 l.pagedispx l.pagedispy
76 l.pagecol
79 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
80 dolog {|rect {
81 x0,y0=(% f, % f)
82 x1,y1=(% f, % f)
83 x2,y2=(% f, % f)
84 x3,y3=(% f, % f)
85 }|} x0 y0 x1 y1 x2 y2 x3 y3;
88 let pgscale h = truncate (float h *. conf.pgscale);;
90 let hscrollh () =
91 if state.uioh#alwaysscrolly || ((conf.scrollb land scrollbhv != 0)
92 && (state.w > state.winw))
93 then conf.scrollbw
94 else 0
97 let setfontsize n =
98 fstate.fontsize <- n;
99 fstate.wwidth <- measurestr fstate.fontsize "w";
100 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
103 let vlog fmt =
104 if conf.verbose
105 then dolog fmt
106 else Printf.kprintf ignore fmt
109 let launchpath () =
110 if emptystr conf.pathlauncher
111 then dolog "%s" state.path
112 else (
113 let command = Str.global_replace percentsre state.path conf.pathlauncher in
114 match spawn command [] with
115 | _pid -> ()
116 | exception exn ->
117 dolog "failed to execute `%s': %s" command @@ exntos exn
121 let getopaque pageno =
122 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
123 with Not_found -> None
126 let pagetranslatepoint l x y =
127 let dy = y - l.pagedispy in
128 let y = dy + l.pagey in
129 let dx = x - l.pagedispx in
130 let x = dx + l.pagex in
131 (x, y);
134 let onppundermouse g x y d =
135 let rec f = function
136 | l :: rest ->
137 begin match getopaque l.pageno with
138 | Some opaque ->
139 let x0 = l.pagedispx in
140 let x1 = x0 + l.pagevw in
141 let y0 = l.pagedispy in
142 let y1 = y0 + l.pagevh in
143 if y >= y0 && y <= y1 && x >= x0 && x <= x1
144 then
145 let px, py = pagetranslatepoint l x y in
146 match g opaque l px py with
147 | Some res -> res
148 | None -> f rest
149 else f rest
150 | _ ->
151 f rest
153 | [] -> d
155 f state.layout
158 let getunder x y =
159 let g opaque l px py =
160 if state.bzoom
161 then (
162 match rectofblock opaque px py with
163 | Some [|x0;x1;y0;y1|] ->
164 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
165 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
166 state.rects <- [l.pageno, color, rect];
167 postRedisplay "getunder";
168 | _ -> ()
170 let under = whatsunder opaque px py in
171 if under = Unone then None else Some under
173 onppundermouse g x y Unone
176 let unproject x y =
177 let g opaque l x y =
178 match unproject opaque x y with
179 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
180 | None -> None
182 onppundermouse g x y None;
185 let showtext c s =
186 state.text <- Printf.sprintf "%c%s" c s;
187 postRedisplay "showtext";
190 let impmsg fmt =
191 Format.ksprintf (fun s -> showtext '!' s) fmt;
194 let pipesel opaque cmd =
195 if hassel opaque
196 then pipef ~closew:false "pipesel"
197 (fun w ->
198 copysel w opaque;
199 postRedisplay "pipesel"
200 ) cmd
203 let paxunder x y =
204 let g opaque l px py =
205 if markunder opaque px py conf.paxmark
206 then (
207 Some (fun () ->
208 match getopaque l.pageno with
209 | None -> ()
210 | Some opaque -> pipesel opaque conf.paxcmd
213 else None
215 postRedisplay "paxunder";
216 if conf.paxmark = Mark_page
217 then
218 List.iter (fun l ->
219 match getopaque l.pageno with
220 | None -> ()
221 | Some opaque -> clearmark opaque) state.layout;
222 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
225 let undertext = function
226 | Unone -> "none"
227 | Ulinkuri s -> s
228 | Utext s -> "font: " ^ s
229 | Uannotation (opaque, slinkindex) ->
230 "annotation: " ^ getannotcontents opaque slinkindex
233 let updateunder x y =
234 match getunder x y with
235 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
236 | Ulinkuri uri ->
237 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
238 Wsi.setcursor Wsi.CURSOR_INFO
239 | Utext s ->
240 if conf.underinfo then showtext 'f' ("ont: " ^ s);
241 Wsi.setcursor Wsi.CURSOR_TEXT
242 | Uannotation _ ->
243 if conf.underinfo then showtext 'a' "nnotation";
244 Wsi.setcursor Wsi.CURSOR_INFO
247 let showlinktype under =
248 if conf.underinfo && under != Unone
249 then showtext ' ' @@ undertext under
252 let intentry_with_suffix text key =
253 let text =
254 match [@warning "-4"] key with
255 | Keys.Ascii ('0'..'9' as c) -> addchar text c
256 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
257 addchar text @@ asciilower c
258 | _ ->
259 state.text <- Printf.sprintf "invalid key";
260 text
262 TEcont text
265 let wcmd fmt =
266 let b = Buffer.create 16 in
267 Printf.kbprintf
268 (fun b ->
269 let b = Buffer.to_bytes b in
270 wcmd state.ss b @@ Bytes.length b
271 ) b fmt
274 let nogeomcmds cmds =
275 match cmds with
276 | s, [] -> emptystr s
277 | _ -> false
280 let layoutN ((columns, coverA, coverB), b) x y sw sh =
281 let rec fold accu n =
282 if n = Array.length b
283 then accu
284 else
285 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
286 if (vy - y) > sh &&
287 (n = coverA - 1
288 || n = state.pagecount - coverB
289 || (n - coverA) mod columns = columns - 1)
290 then accu
291 else
292 let accu =
293 if vy + h > y
294 then
295 let pagey = max 0 (y - vy) in
296 let pagedispy = if pagey > 0 then 0 else vy - y in
297 let pagedispx, pagex =
298 let pdx =
299 if n = coverA - 1 || n = state.pagecount - coverB
300 then x + (sw - w) / 2
301 else dx + xoff + x
303 if pdx < 0
304 then 0, -pdx
305 else pdx, 0
307 let pagevw =
308 let vw = sw - pagedispx in
309 let pw = w - pagex in
310 min vw pw
312 let pagevh = min (h - pagey) (sh - pagedispy) in
313 if pagevw > 0 && pagevh > 0
314 then
315 let e =
316 { pageno = n
317 ; pagedimno = pdimno
318 ; pagew = w
319 ; pageh = h
320 ; pagex = pagex
321 ; pagey = pagey
322 ; pagevw = pagevw
323 ; pagevh = pagevh
324 ; pagedispx = pagedispx
325 ; pagedispy = pagedispy
326 ; pagecol = 0
329 e :: accu
330 else
331 accu
332 else
333 accu
335 fold accu (n+1)
337 if Array.length b = 0
338 then []
339 else List.rev (fold [] (page_of_y y))
342 let layoutS (columns, b) x y sw sh =
343 let rec fold accu n =
344 if n = Array.length b
345 then accu
346 else
347 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
348 if (vy - y) > sh
349 then accu
350 else
351 let accu =
352 if vy + pageh > y
353 then
354 let x = xoff + x in
355 let pagey = max 0 (y - vy) in
356 let pagedispy = if pagey > 0 then 0 else vy - y in
357 let pagedispx, pagex =
358 if px = 0
359 then (
360 if x < 0
361 then 0, -x
362 else x, 0
364 else (
365 let px = px - x in
366 if px < 0
367 then -px, 0
368 else 0, px
371 let pagecolw = pagew/columns in
372 let pagedispx =
373 if pagecolw < sw
374 then pagedispx + ((sw - pagecolw) / 2)
375 else pagedispx
377 let pagevw =
378 let vw = sw - pagedispx in
379 let pw = pagew - pagex in
380 min vw pw
382 let pagevw = min pagevw pagecolw in
383 let pagevh = min (pageh - pagey) (sh - pagedispy) in
384 if pagevw > 0 && pagevh > 0
385 then
386 let e =
387 { pageno = n/columns
388 ; pagedimno = pdimno
389 ; pagew = pagew
390 ; pageh = pageh
391 ; pagex = pagex
392 ; pagey = pagey
393 ; pagevw = pagevw
394 ; pagevh = pagevh
395 ; pagedispx = pagedispx
396 ; pagedispy = pagedispy
397 ; pagecol = n mod columns
400 e :: accu
401 else
402 accu
403 else
404 accu
406 fold accu (n+1)
408 List.rev (fold [] 0)
411 let layout x y sw sh =
412 if nogeomcmds state.geomcmds
413 then
414 match conf.columns with
415 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
416 | Cmulti c -> layoutN c x y sw sh
417 | Csplit s -> layoutS s x y sw sh
418 else []
421 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
423 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
425 let itertiles l f =
426 let tilex = l.pagex mod conf.tilew in
427 let tiley = l.pagey mod conf.tileh in
429 let col = l.pagex / conf.tilew in
430 let row = l.pagey / conf.tileh in
432 let rec rowloop row y0 dispy h =
433 if h = 0
434 then ()
435 else (
436 let dh = conf.tileh - y0 in
437 let dh = min h dh in
438 let rec colloop col x0 dispx w =
439 if w = 0
440 then ()
441 else (
442 let dw = conf.tilew - x0 in
443 let dw = min w dw in
444 f col row dispx dispy x0 y0 dw dh;
445 colloop (col+1) 0 (dispx+dw) (w-dw)
448 colloop col tilex l.pagedispx l.pagevw;
449 rowloop (row+1) 0 (dispy+dh) (h-dh)
452 if l.pagevw > 0 && l.pagevh > 0
453 then rowloop row tiley l.pagedispy l.pagevh;
456 let gettileopaque l col row =
457 let key =
458 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
460 try Some (Hashtbl.find state.tilemap key)
461 with Not_found -> None
464 let puttileopaque l col row gen colorspace angle opaque size elapsed =
465 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
466 Hashtbl.add state.tilemap key (opaque, size, elapsed)
469 let drawtiles l color =
470 GlDraw.color color;
471 begintiles ();
472 let f col row x y tilex tiley w h =
473 match gettileopaque l col row with
474 | Some (opaque, _, t) ->
475 let params = x, y, w, h, tilex, tiley in
476 if conf.invert
477 then GlTex.env (`mode `blend);
478 drawtile params opaque;
479 if conf.invert
480 then GlTex.env (`mode `modulate);
481 if conf.debug
482 then (
483 endtiles ();
484 let s = Printf.sprintf
485 "%d[%d,%d] %f sec"
486 l.pageno col row t
488 let w = measurestr fstate.fontsize s in
489 GlDraw.color (0.0, 0.0, 0.0);
490 filledrect (float (x-2))
491 (float (y-2))
492 (float (x+2) +. w)
493 (float (y + fstate.fontsize + 2));
494 GlDraw.color color;
495 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
496 begintiles ();
499 | None ->
500 endtiles ();
501 let w =
502 let lw = state.winw - x in
503 min lw w
504 and h =
505 let lh = state.winh - y in
506 min lh h
508 if conf.invert
509 then GlTex.env (`mode `blend);
510 begin match state.checkerstexid with
511 | Some id ->
512 Gl.enable `texture_2d;
513 GlTex.bind_texture ~target:`texture_2d id;
514 let x0 = float x
515 and y0 = float y
516 and x1 = float (x+w)
517 and y1 = float (y+h) in
519 let tw = float w /. 16.0
520 and th = float h /. 16.0 in
521 let tx0 = float tilex /. 16.0
522 and ty0 = float tiley /. 16.0 in
523 let tx1 = tx0 +. tw
524 and ty1 = ty0 +. th in
525 Raw.sets_float Glutils.vraw ~pos:0
526 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
527 Raw.sets_float Glutils.traw ~pos:0
528 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
529 GlArray.vertex `two Glutils.vraw;
530 GlArray.tex_coord `two Glutils.traw;
531 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
532 Gl.disable `texture_2d;
534 | None ->
535 GlDraw.color (1.0, 1.0, 1.0);
536 filledrect (float x) (float y) (float (x+w)) (float (y+h));
537 end;
538 if conf.invert
539 then GlTex.env (`mode `modulate);
540 if w > 128 && h > fstate.fontsize + 10
541 then (
542 let c = if conf.invert then 1.0 else 0.0 in
543 GlDraw.color (c, c, c);
544 let c, r =
545 if conf.verbose
546 then (col*conf.tilew, row*conf.tileh)
547 else col, row
549 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
551 GlDraw.color color;
552 begintiles ();
554 itertiles l f;
555 endtiles ();
558 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
560 let tilevisible1 l x y =
561 let ax0 = l.pagex
562 and ax1 = l.pagex + l.pagevw
563 and ay0 = l.pagey
564 and ay1 = l.pagey + l.pagevh in
566 let bx0 = x
567 and by0 = y in
568 let bx1 = min (bx0 + conf.tilew) l.pagew
569 and by1 = min (by0 + conf.tileh) l.pageh in
571 let rx0 = max ax0 bx0
572 and ry0 = max ay0 by0
573 and rx1 = min ax1 bx1
574 and ry1 = min ay1 by1 in
576 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
577 nonemptyintersection
580 let tilevisible layout n x y =
581 let rec findpageinlayout m = function
582 | l :: rest when l.pageno = n ->
583 tilevisible1 l x y || (
584 match conf.columns with
585 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
586 | Csplit _ | Csingle _ | Cmulti _ -> false
588 | _ :: rest -> findpageinlayout 0 rest
589 | [] -> false
591 findpageinlayout 0 layout;
594 let tileready l x y =
595 tilevisible1 l x y &&
596 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
599 let tilepage n p layout =
600 let rec loop = function
601 | l :: rest ->
602 if l.pageno = n
603 then
604 let f col row _ _ _ _ _ _ =
605 if state.currently = Idle
606 then
607 match gettileopaque l col row with
608 | Some _ -> ()
609 | None ->
610 let x = col*conf.tilew
611 and y = row*conf.tileh in
612 let w =
613 let w = l.pagew - x in
614 min w conf.tilew
616 let h =
617 let h = l.pageh - y in
618 min h conf.tileh
620 let pbo =
621 if conf.usepbo
622 then getpbo w h conf.colorspace
623 else ~< "0"
625 wcmd "tile %s %d %d %d %d %s"
626 (~> p) x y w h (~> pbo);
627 state.currently <-
628 Tiling (
629 l, p, conf.colorspace, conf.angle,
630 state.gen, col, row, conf.tilew, conf.tileh
633 itertiles l f;
634 else
635 loop rest
637 | [] -> ()
639 if nogeomcmds state.geomcmds
640 then loop layout;
643 let preloadlayout x y sw sh =
644 let y = if y < sh then 0 else y - sh in
645 let x = min 0 (x + sw) in
646 let h = sh*3 in
647 let w = sw*3 in
648 layout x y w h;
651 let load pages =
652 let rec loop pages =
653 if state.currently != Idle
654 then ()
655 else
656 match pages with
657 | l :: rest ->
658 begin match getopaque l.pageno with
659 | None ->
660 wcmd "page %d %d" l.pageno l.pagedimno;
661 state.currently <- Loading (l, state.gen);
662 | Some opaque ->
663 tilepage l.pageno opaque pages;
664 loop rest
665 end;
666 | _ -> ()
668 if nogeomcmds state.geomcmds
669 then loop pages
672 let preload pages =
673 load pages;
674 if conf.preload && state.currently = Idle
675 then load (preloadlayout state.x state.y state.winw state.winh);
678 let layoutready layout =
679 let rec fold all ls =
680 all && match ls with
681 | l :: rest ->
682 let seen = ref false in
683 let allvisible = ref true in
684 let foo col row _ _ _ _ _ _ =
685 seen := true;
686 allvisible := !allvisible &&
687 begin match gettileopaque l col row with
688 | Some _ -> true
689 | None -> false
692 itertiles l foo;
693 fold (!seen && !allvisible) rest
694 | [] -> true
696 let alltilesvisible = fold true layout in
697 alltilesvisible;
700 let gotoxy x y =
701 let y = bound y 0 state.maxy in
702 let y, layout =
703 let layout = layout x y state.winw state.winh in
704 postRedisplay "gotoxy ready";
705 y, layout
707 state.x <- x;
708 state.y <- y;
709 state.layout <- layout;
710 begin match state.mode with
711 | LinkNav ln ->
712 begin match ln with
713 | Ltexact (pageno, linkno) ->
714 let rec loop = function
715 | [] ->
716 state.lnava <- Some (pageno, linkno);
717 state.mode <- LinkNav (Ltgendir 0)
718 | l :: _ when l.pageno = pageno ->
719 begin match getopaque pageno with
720 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
721 | Some opaque ->
722 let x0, y0, x1, y1 = getlinkrect opaque linkno in
723 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
724 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
725 then state.mode <- LinkNav (Ltgendir 0)
727 | _ :: rest -> loop rest
729 loop layout
730 | Ltnotready _ | Ltgendir _ -> ()
732 | Birdseye _ | Textentry _ | View -> ()
733 end;
734 begin match state.mode with
735 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
736 if not (pagevisible layout pageno)
737 then (
738 match state.layout with
739 | [] -> ()
740 | l :: _ ->
741 state.mode <- Birdseye (
742 conf, leftx, l.pageno, hooverpageno, anchor
745 | LinkNav lt ->
746 begin match lt with
747 | Ltnotready (_, dir)
748 | Ltgendir dir ->
749 let linknav =
750 let rec loop = function
751 | [] -> lt
752 | l :: rest ->
753 match getopaque l.pageno with
754 | None -> Ltnotready (l.pageno, dir)
755 | Some opaque ->
756 let link =
757 let ld =
758 if dir = 0
759 then LDfirstvisible (l.pagex, l.pagey, dir)
760 else (
761 if dir > 0 then LDfirst else LDlast
764 findlink opaque ld
766 match link with
767 | Lnotfound -> loop rest
768 | Lfound n ->
769 showlinktype (getlink opaque n);
770 Ltexact (l.pageno, n)
772 loop state.layout
774 state.mode <- LinkNav linknav
775 | Ltexact _ -> ()
777 | Textentry _ | View -> ()
778 end;
779 preload layout;
780 if conf.updatecurs
781 then (
782 let mx, my = state.mpos in
783 updateunder mx my;
787 let conttiling pageno opaque =
788 tilepage pageno opaque
789 (if conf.preload
790 then preloadlayout state.x state.y state.winw state.winh
791 else state.layout)
794 let gotoxy x y =
795 if not conf.verbose then state.text <- E.s;
796 gotoxy x y;
799 let getanchory (n, top, dtop) =
800 let y, h = getpageyh n in
801 if conf.presentation
802 then
803 let ips = calcips h in
804 y + truncate (top*.float h -. dtop*.float ips) + ips;
805 else
806 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
809 let gotoanchor anchor =
810 gotoxy state.x (getanchory anchor);
813 let addnav () =
814 getanchor () |> cbput state.hists.nav;
817 let addnavnorc () =
818 getanchor () |> cbput_dont_update_rc state.hists.nav;
821 let getnav dir =
822 let anchor = cbgetc state.hists.nav dir in
823 getanchory anchor;
826 let gotopage n top =
827 let y, h = getpageyh n in
828 let y = y + (truncate (top *. float h)) in
829 gotoxy state.x y
832 let gotopage1 n top =
833 let y = getpagey n in
834 let y = y + top in
835 gotoxy state.x y
838 let invalidate s f =
839 Glutils.redisplay := false;
840 state.layout <- [];
841 state.pdims <- [];
842 state.rects <- [];
843 state.rects1 <- [];
844 match state.geomcmds with
845 | ps, [] when emptystr ps ->
846 f ();
847 state.geomcmds <- s, [];
849 | ps, [] ->
850 state.geomcmds <- ps, [s, f];
852 | ps, (s', _) :: rest when s' = s ->
853 state.geomcmds <- ps, ((s, f) :: rest);
855 | ps, cmds ->
856 state.geomcmds <- ps, ((s, f) :: cmds);
859 let flushpages () =
860 Hashtbl.iter (fun _ opaque ->
861 wcmd "freepage %s" (~> opaque);
862 ) state.pagemap;
863 Hashtbl.clear state.pagemap;
866 let flushtiles () =
867 if not (Queue.is_empty state.tilelru)
868 then (
869 Queue.iter (fun (k, p, s) ->
870 wcmd "freetile %s" (~> p);
871 state.memused <- state.memused - s;
872 Hashtbl.remove state.tilemap k;
873 ) state.tilelru;
874 state.uioh#infochanged Memused;
875 Queue.clear state.tilelru;
877 load state.layout;
880 let stateh h =
881 let h = truncate (float h*.conf.zoom) in
882 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
883 h - d
886 let fillhelp () =
887 state.help <-
888 let sl = keystostrlist conf in
889 let rec loop accu =
890 function | [] -> accu
891 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
892 in Help.makehelp conf.urilauncher
893 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
896 let opendoc path password =
897 state.path <- path;
898 state.password <- password;
899 state.gen <- state.gen + 1;
900 state.docinfo <- [];
901 state.outlines <- [||];
903 flushpages ();
904 setaalevel conf.aalevel;
905 let titlepath =
906 if emptystr state.origin
907 then path
908 else state.origin
910 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
911 wcmd "open %d %d %s\000%s\000%s\000"
912 (btod conf.usedoccss) !layouth
913 path password conf.css;
914 invalidate "reqlayout"
915 (fun () ->
916 wcmd "reqlayout %d %d %d %s\000"
917 conf.angle (FMTE.to_int conf.fitmodel)
918 (stateh state.winh) state.nameddest
920 fillhelp ();
923 let reload () =
924 state.anchor <- getanchor ();
925 opendoc state.path state.password;
928 let scalecolor c =
929 let c = c *. conf.colorscale in
930 (c, c, c);
933 let scalecolor2 (r, g, b) =
934 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
937 let docolumns columns =
938 match columns with
939 | Csingle _ ->
940 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
941 let rec loop pageno pdimno pdim y ph pdims =
942 if pageno = state.pagecount
943 then ()
944 else
945 let pdimno, ((_, w, h, xoff) as pdim), pdims =
946 match pdims with
947 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
948 pdimno+1, pdim, rest
949 | _ ->
950 pdimno, pdim, pdims
952 let x = max 0 (((state.winw - w) / 2) - xoff) in
953 let y =
954 y + (if conf.presentation
955 then (if pageno = 0 then calcips h else calcips ph + calcips h)
956 else (if pageno = 0 then 0 else conf.interpagespace)
959 a.(pageno) <- (pdimno, x, y, pdim);
960 loop (pageno+1) pdimno pdim (y + h) h pdims
962 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
963 conf.columns <- Csingle a;
965 | Cmulti ((columns, coverA, coverB), _) ->
966 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
967 let rec loop pageno pdimno pdim x y rowh pdims =
968 let rec fixrow m =
969 if m = pageno then () else
970 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
971 if h < rowh
972 then (
973 let y = y + (rowh - h) / 2 in
974 a.(m) <- (pdimno, x, y, pdim);
976 fixrow (m+1)
978 if pageno = state.pagecount
979 then fixrow (((pageno - 1) / columns) * columns)
980 else
981 let pdimno, ((_, w, h, xoff) as pdim), pdims =
982 match pdims with
983 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
984 pdimno+1, pdim, rest
985 | _ ->
986 pdimno, pdim, pdims
988 let x, y, rowh' =
989 if pageno = coverA - 1 || pageno = state.pagecount - coverB
990 then (
991 let x = (state.winw - w) / 2 in
992 let ips =
993 if conf.presentation then calcips h else conf.interpagespace in
994 x, y + ips + rowh, h
996 else (
997 if (pageno - coverA) mod columns = 0
998 then (
999 let x = max 0 (state.winw - state.w) / 2 in
1000 let y =
1001 if conf.presentation
1002 then
1003 let ips = calcips h in
1004 y + (if pageno = 0 then 0 else calcips rowh + ips)
1005 else
1006 y + (if pageno = 0 then 0 else conf.interpagespace)
1008 x, y + rowh, h
1010 else x, y, max rowh h
1013 let y =
1014 if pageno > 1 && (pageno - coverA) mod columns = 0
1015 then (
1016 let y =
1017 if pageno = columns && conf.presentation
1018 then (
1019 let ips = calcips rowh in
1020 for i = 0 to pred columns
1022 let (pdimno, x, y, pdim) = a.(i) in
1023 a.(i) <- (pdimno, x, y+ips, pdim)
1024 done;
1025 y+ips;
1027 else y
1029 fixrow (pageno - columns);
1032 else y
1034 a.(pageno) <- (pdimno, x, y, pdim);
1035 let x = x + w + xoff*2 + conf.interpagespace in
1036 loop (pageno+1) pdimno pdim x y rowh' pdims
1038 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1039 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1041 | Csplit (c, _) ->
1042 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1043 let rec loop pageno pdimno pdim y pdims =
1044 if pageno = state.pagecount
1045 then ()
1046 else
1047 let pdimno, ((_, w, h, _) as pdim), pdims =
1048 match pdims with
1049 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1050 pdimno+1, pdim, rest
1051 | _ ->
1052 pdimno, pdim, pdims
1054 let cw = w / c in
1055 let rec loop1 n x y =
1056 if n = c then y else (
1057 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1058 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1061 let y = loop1 0 0 y in
1062 loop (pageno+1) pdimno pdim y pdims
1064 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1065 conf.columns <- Csplit (c, a);
1068 let represent () =
1069 docolumns conf.columns;
1070 state.maxy <- calcheight ();
1071 if state.reprf == noreprf
1072 then (
1073 match state.mode with
1074 | Birdseye (_, _, pageno, _, _) ->
1075 let y, h = getpageyh pageno in
1076 let top = (state.winh - h) / 2 in
1077 gotoxy state.x (max 0 (y - top))
1078 | Textentry _ | View | LinkNav _ ->
1079 let y = getanchory state.anchor in
1080 let y = min y (state.maxy - state.winh) in
1081 gotoxy state.x y;
1083 else (
1084 state.reprf ();
1085 state.reprf <- noreprf;
1089 let reshape ?(firsttime=false) w h =
1090 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
1091 if not firsttime && nogeomcmds state.geomcmds
1092 then state.anchor <- getanchor ();
1094 state.winw <- w;
1095 let w = truncate (float w *. conf.zoom) in
1096 let w = max w 2 in
1097 state.winh <- h;
1098 setfontsize fstate.fontsize;
1099 GlMat.mode `modelview;
1100 GlMat.load_identity ();
1102 GlMat.mode `projection;
1103 GlMat.load_identity ();
1104 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1105 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1106 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
1108 let relx =
1109 if conf.zoom <= 1.0
1110 then 0.0
1111 else float state.x /. float state.w
1113 invalidate "geometry"
1114 (fun () ->
1115 state.w <- w;
1116 if not firsttime
1117 then state.x <- truncate (relx *. float w);
1118 let w =
1119 match conf.columns with
1120 | Csingle _ -> w
1121 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1122 | Csplit (c, _) -> w * c
1124 wcmd "geometry %d %d %d"
1125 w (stateh h) (FMTE.to_int conf.fitmodel)
1129 let gctiles () =
1130 let len = Queue.length state.tilelru in
1131 let layout = lazy (if conf.preload
1132 then preloadlayout state.x state.y state.winw state.winh
1133 else state.layout) in
1134 let rec loop qpos =
1135 if state.memused > conf.memlimit
1136 then (
1137 if qpos < len
1138 then
1139 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1140 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1141 let (_, pw, ph, _) = getpagedim n in
1142 if gen = state.gen
1143 && colorspace = conf.colorspace
1144 && angle = conf.angle
1145 && pagew = pw
1146 && pageh = ph
1147 && (
1148 let x = col*conf.tilew
1149 and y = row*conf.tileh in
1150 tilevisible (Lazy.force_val layout) n x y
1152 then Queue.push lruitem state.tilelru
1153 else (
1154 freepbo p;
1155 wcmd "freetile %s" (~> p);
1156 state.memused <- state.memused - s;
1157 state.uioh#infochanged Memused;
1158 Hashtbl.remove state.tilemap k;
1160 loop (qpos+1)
1163 loop 0
1166 let onpagerect pageno f =
1167 let b =
1168 match conf.columns with
1169 | Cmulti (_, b) -> b
1170 | Csingle b -> b
1171 | Csplit (_, b) -> b
1173 if pageno >= 0 && pageno < Array.length b
1174 then
1175 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1176 f w h
1179 let gotopagexy1 pageno x y =
1180 let _,w1,h1,leftx = getpagedim pageno in
1181 let top = y /. (float h1) in
1182 let left = x /. (float w1) in
1183 let py, w, h = getpageywh pageno in
1184 let wh = state.winh in
1185 let x = left *. (float w) in
1186 let x = leftx + state.x + truncate x in
1187 let sx =
1188 if x < 0 || x >= state.winw
1189 then state.x - x
1190 else state.x
1192 let pdy = truncate (top *. float h) in
1193 let y' = py + pdy in
1194 let dy = y' - state.y in
1195 let sy =
1196 if x != state.x || not (dy > 0 && dy < wh)
1197 then (
1198 if conf.presentation
1199 then
1200 if abs (py - y') > wh
1201 then y'
1202 else py
1203 else y';
1205 else state.y
1207 if state.x != sx || state.y != sy
1208 then gotoxy sx sy
1209 else gotoxy state.x state.y;
1212 let gotopagexy pageno x y =
1213 match state.mode with
1214 | Birdseye _ -> gotopage pageno 0.0
1215 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1218 let getpassword () =
1219 let passcmd = getenvwithdef "LLPP_ASKPASS" conf.passcmd in
1220 if emptystr passcmd
1221 then E.s
1222 else getcmdoutput
1223 (fun s ->
1224 impmsg "error getting password: %s" s;
1225 dolog "%s" s) passcmd;
1228 let pgoto opaque pageno x y =
1229 let pdimno = getpdimno pageno in
1230 let x, y = project opaque pageno pdimno x y in
1231 gotopagexy pageno x y;
1234 let act cmds =
1235 (* dolog "%S" cmds; *)
1236 let spl = splitatchar cmds ' ' in
1237 let scan s fmt f =
1238 try Scanf.sscanf s fmt f
1239 with exn ->
1240 dolog "error processing '%S': %s" cmds @@ exntos exn;
1241 exit 1
1243 let addoutline outline =
1244 match state.currently with
1245 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1246 | Idle -> state.currently <- Outlining [outline]
1247 | Loading _ | Tiling _ ->
1248 dolog "invalid outlining state";
1249 logcurrently state.currently
1251 match spl with
1252 | "clear", "" ->
1253 state.pdims <- [];
1254 state.uioh#infochanged Pdim;
1256 | "clearrects", "" ->
1257 state.rects <- state.rects1;
1258 postRedisplay "clearrects";
1260 | "continue", args ->
1261 let n = scan args "%u" (fun n -> n) in
1262 state.pagecount <- n;
1263 begin match state.currently with
1264 | Outlining l ->
1265 state.currently <- Idle;
1266 state.outlines <- Array.of_list (List.rev l)
1267 | Idle | Loading _ | Tiling _ -> ()
1268 end;
1270 let cur, cmds = state.geomcmds in
1271 if emptystr cur
1272 then failwith "umpossible";
1274 begin match List.rev cmds with
1275 | [] ->
1276 state.geomcmds <- E.s, [];
1277 represent ();
1278 | (s, f) :: rest ->
1279 f ();
1280 state.geomcmds <- s, List.rev rest;
1281 end;
1282 postRedisplay "continue";
1284 | "msg", args ->
1285 showtext ' ' args
1287 | "vmsg", args ->
1288 if conf.verbose
1289 then showtext ' ' args
1291 | "emsg", args ->
1292 Buffer.add_string state.errmsgs args;
1293 state.newerrmsgs <- true;
1294 postRedisplay "error message"
1296 | "progress", args ->
1297 let progress, text =
1298 scan args "%f %n"
1299 (fun f pos ->
1300 f, String.sub args pos (String.length args - pos))
1302 state.text <- text;
1303 state.progress <- progress;
1304 postRedisplay "progress"
1306 | "firstmatch", args ->
1307 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1308 scan args "%u %d %f %f %f %f %f %f %f %f"
1309 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1310 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1312 let y = (getpagey pageno) + truncate y0 in
1313 let x =
1314 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1315 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1316 else state.x
1318 addnav ();
1319 gotoxy x y;
1320 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1321 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1323 | "match", args ->
1324 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1325 scan args "%u %d %f %f %f %f %f %f %f %f"
1326 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1327 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1329 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1330 state.rects1 <-
1331 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1333 | "page", args ->
1334 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1335 let pageopaque = ~< pageopaques in
1336 begin match state.currently with
1337 | Loading (l, gen) ->
1338 vlog "page %d took %f sec" l.pageno t;
1339 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1340 let preloadedpages =
1341 if conf.preload
1342 then preloadlayout state.x state.y state.winw state.winh
1343 else state.layout
1345 let evict () =
1346 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1347 IntSet.empty preloadedpages
1349 let evictedpages =
1350 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1351 if not (IntSet.mem pageno set)
1352 then (
1353 wcmd "freepage %s" (~> opaque);
1354 key :: accu
1356 else accu
1357 ) state.pagemap []
1359 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1361 evict ();
1362 state.currently <- Idle;
1363 if gen = state.gen
1364 then (
1365 tilepage l.pageno pageopaque state.layout;
1366 load state.layout;
1367 load preloadedpages;
1368 let visible = pagevisible state.layout l.pageno in
1369 if visible
1370 then (
1371 match state.mode with
1372 | LinkNav (Ltnotready (pageno, dir)) ->
1373 if pageno = l.pageno
1374 then (
1375 let link =
1376 let ld =
1377 if dir = 0
1378 then LDfirstvisible (l.pagex, l.pagey, dir)
1379 else (
1380 if dir > 0 then LDfirst else LDlast
1383 findlink pageopaque ld
1385 match link with
1386 | Lnotfound -> ()
1387 | Lfound n ->
1388 showlinktype (getlink pageopaque n);
1389 state.mode <- LinkNav (Ltexact (l.pageno, n))
1391 | LinkNav (Ltgendir _)
1392 | LinkNav (Ltexact _)
1393 | View
1394 | Birdseye _
1395 | Textentry _ -> ()
1398 if visible && layoutready state.layout
1399 then (
1400 postRedisplay "page";
1404 | Idle | Tiling _ | Outlining _ ->
1405 dolog "Inconsistent loading state";
1406 logcurrently state.currently;
1407 exit 1
1410 | "tile" , args ->
1411 let (x, y, opaques, size, t) =
1412 scan args "%u %u %s %u %f"
1413 (fun x y p size t -> (x, y, p, size, t))
1415 let opaque = ~< opaques in
1416 begin match state.currently with
1417 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1418 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1420 unmappbo opaque;
1421 if tilew != conf.tilew || tileh != conf.tileh
1422 then (
1423 wcmd "freetile %s" (~> opaque);
1424 state.currently <- Idle;
1425 load state.layout;
1427 else (
1428 puttileopaque l col row gen cs angle opaque size t;
1429 state.memused <- state.memused + size;
1430 state.uioh#infochanged Memused;
1431 gctiles ();
1432 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1433 opaque, size) state.tilelru;
1435 state.currently <- Idle;
1436 if gen = state.gen
1437 && conf.colorspace = cs
1438 && conf.angle = angle
1439 && tilevisible state.layout l.pageno x y
1440 then conttiling l.pageno pageopaque;
1442 preload state.layout;
1443 if gen = state.gen
1444 && conf.colorspace = cs
1445 && conf.angle = angle
1446 && tilevisible state.layout l.pageno x y
1447 && layoutready state.layout
1448 then postRedisplay "tile nothrottle";
1451 | Idle | Loading _ | Outlining _ ->
1452 dolog "Inconsistent tiling state";
1453 logcurrently state.currently;
1454 exit 1
1457 | "pdim", args ->
1458 let (n, w, h, _) as pdim =
1459 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1461 let pdim =
1462 match conf.fitmodel with
1463 | FitWidth -> pdim
1464 | FitPage | FitProportional ->
1465 match conf.columns with
1466 | Csplit _ -> (n, w, h, 0)
1467 | Csingle _ | Cmulti _ -> pdim
1469 state.pdims <- pdim :: state.pdims;
1470 state.uioh#infochanged Pdim
1472 | "o", args ->
1473 let (l, n, t, h, pos) =
1474 scan args "%u %u %d %u %n"
1475 (fun l n t h pos -> l, n, t, h, pos)
1477 let s = String.sub args pos (String.length args - pos) in
1478 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1480 | "ou", args ->
1481 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1482 let s = String.sub args pos len in
1483 let pos2 = pos + len + 1 in
1484 let uri = String.sub args pos2 (String.length args - pos2) in
1485 addoutline (s, l, Ouri uri)
1487 | "on", args ->
1488 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1489 let s = String.sub args pos (String.length args - pos) in
1490 addoutline (s, l, Onone)
1492 | "a", args ->
1493 let (n, l, t) =
1494 scan args "%u %d %d" (fun n l t -> n, l, t)
1496 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1498 | "info", args ->
1499 let c, v = splitatchar args '\t' in
1500 let s =
1501 if nonemptystr v
1502 then
1503 if c = "Title"
1504 then (
1505 conf.title <- v;
1506 if not !ignoredoctitlte
1507 then Wsi.settitle v;
1508 args
1510 else
1511 if let len = String.length c in
1512 len > 6 && ((String.sub c (len-4) 4) = "date")
1513 then (
1514 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1515 then
1516 let b = Buffer.create 10 in
1517 Printf.bprintf b "%s\t" c;
1518 let sub p l c =
1520 Buffer.add_substring b v p l;
1521 Buffer.add_char b c;
1522 with exn -> Buffer.add_string b @@ exntos exn
1524 sub 2 4 '/';
1525 sub 6 2 '/';
1526 sub 8 2 ' ';
1527 sub 10 2 ':';
1528 sub 12 2 ':';
1529 sub 14 2 ' ';
1530 Buffer.add_char b '[';
1531 Buffer.add_string b v;
1532 Buffer.add_char b ']';
1533 Buffer.contents b
1534 else args
1536 else args
1537 else args
1539 state.docinfo <- (1, s) :: state.docinfo
1541 | "infoend", "" ->
1542 state.docinfo <- List.rev state.docinfo;
1543 state.uioh#infochanged Docinfo
1545 | "pass", args ->
1546 if args = "fail"
1547 then Wsi.settitle "Wrong password";
1548 let password = getpassword () in
1549 if emptystr password
1550 then error "document is password protected"
1551 else opendoc state.path password
1553 | _ ->
1554 error "unknown cmd `%S'" cmds
1557 let onhist cb =
1558 let rc = cb.rc in
1559 let action = function
1560 | HCprev -> cbget cb ~-1
1561 | HCnext -> cbget cb 1
1562 | HCfirst -> cbget cb ~-(cb.rc)
1563 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1564 and cancel () = cb.rc <- rc
1565 in (action, cancel)
1568 let search pattern forward =
1569 match conf.columns with
1570 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1571 | Csingle _ | Cmulti _ ->
1572 if nonemptystr pattern
1573 then
1574 let pn, py =
1575 match state.layout with
1576 | [] -> 0, 0
1577 | l :: _ ->
1578 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1580 wcmd "search %d %d %d %d,%s\000"
1581 (btod conf.icase) pn py (btod forward) pattern;
1584 let intentry text key =
1585 let text =
1586 if emptystr text && key = Keys.Ascii '-'
1587 then addchar text '-'
1588 else
1589 match [@warning "-4"] key with
1590 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1591 | _ ->
1592 state.text <- "invalid key";
1593 text
1595 TEcont text
1598 let linknact f s =
1599 if nonemptystr s
1600 then (
1601 let n =
1602 let l = String.length s in
1603 let rec loop pos n =
1604 if pos = l
1605 then n
1606 else
1607 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1608 loop (pos+1) (n*26 + m)
1609 in loop 0 0
1611 let rec loop n = function
1612 | [] -> ()
1613 | l :: rest ->
1614 match getopaque l.pageno with
1615 | None -> loop n rest
1616 | Some opaque ->
1617 let m = getlinkcount opaque in
1618 if n < m
1619 then (
1620 let under = getlink opaque n in
1621 f under
1623 else loop (n-m) rest
1625 loop n state.layout;
1629 let linknentry text = function [@warning "-4"]
1630 | Keys.Ascii c ->
1631 let text = addchar text c in
1632 linknact (fun under -> state.text <- undertext under) text;
1633 TEcont text
1634 | _ ->
1635 state.text <- Printf.sprintf "invalid key";
1636 TEcont text
1639 let textentry text = function [@warning "-4"]
1640 | Keys.Ascii c -> TEcont (addchar text c)
1641 | Keys.Code c -> TEcont (text ^ toutf8 c)
1642 | _ -> TEcont text
1645 let reqlayout angle fitmodel =
1646 if nogeomcmds state.geomcmds
1647 then state.anchor <- getanchor ();
1648 conf.angle <- angle mod 360;
1649 if conf.angle != 0
1650 then (
1651 match state.mode with
1652 | LinkNav _ -> state.mode <- View
1653 | Birdseye _ | Textentry _ | View -> ()
1655 conf.fitmodel <- fitmodel;
1656 invalidate
1657 "reqlayout"
1658 (fun () ->
1659 wcmd "reqlayout %d %d %d"
1660 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh)
1664 let settrim trimmargins trimfuzz =
1665 if nogeomcmds state.geomcmds
1666 then state.anchor <- getanchor ();
1667 conf.trimmargins <- trimmargins;
1668 conf.trimfuzz <- trimfuzz;
1669 let x0, y0, x1, y1 = trimfuzz in
1670 invalidate
1671 "settrim" (fun () ->
1672 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
1673 flushpages ();
1676 let setzoom zoom =
1677 let zoom = max 0.0001 zoom in
1678 if zoom <> conf.zoom
1679 then (
1680 state.prevzoom <- (conf.zoom, state.x);
1681 conf.zoom <- zoom;
1682 reshape state.winw state.winh;
1683 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1687 let pivotzoom ?(vw=min state.w state.winw)
1688 ?(vh=min (state.maxy-state.y) state.winh)
1689 ?(x=vw/2) ?(y=vh/2) zoom =
1690 let w = float state.w /. zoom in
1691 let hw = w /. 2.0 in
1692 let ratio = float vh /. float vw in
1693 let hh = hw *. ratio in
1694 let x0 = float x -. hw
1695 and y0 = float y -. hh in
1696 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1697 setzoom zoom;
1700 let pivotzoom ?vw ?vh ?x ?y zoom =
1701 if nogeomcmds state.geomcmds
1702 then
1703 if zoom > 1.0
1704 then pivotzoom ?vw ?vh ?x ?y zoom
1705 else setzoom zoom
1708 let setcolumns mode columns coverA coverB =
1709 state.prevcolumns <- Some (conf.columns, conf.zoom);
1710 if columns < 0
1711 then (
1712 if isbirdseye mode
1713 then impmsg "split mode doesn't work in bird's eye"
1714 else (
1715 conf.columns <- Csplit (-columns, E.a);
1716 state.x <- 0;
1717 conf.zoom <- 1.0;
1720 else (
1721 if columns < 2
1722 then (
1723 conf.columns <- Csingle E.a;
1724 state.x <- 0;
1725 setzoom 1.0;
1727 else (
1728 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1729 conf.zoom <- 1.0;
1732 reshape state.winw state.winh;
1735 let resetmstate () =
1736 state.mstate <- Mnone;
1737 Wsi.setcursor Wsi.CURSOR_INHERIT;
1740 let enterbirdseye () =
1741 let zoom = float conf.thumbw /. float state.winw in
1742 let birdseyepageno =
1743 let cy = state.winh / 2 in
1744 let fold = function
1745 | [] -> 0
1746 | l :: rest ->
1747 let rec fold best = function
1748 | [] -> best.pageno
1749 | l :: rest ->
1750 let d = cy - (l.pagedispy + l.pagevh/2)
1751 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1752 if abs d < abs dbest
1753 then fold l rest
1754 else best.pageno
1755 in fold l rest
1757 fold state.layout
1759 state.mode <-
1760 Birdseye (
1761 { conf with zoom = conf.zoom },
1762 state.x, birdseyepageno, -1, getanchor ()
1764 resetmstate ();
1765 conf.zoom <- zoom;
1766 conf.presentation <- false;
1767 conf.interpagespace <- 10;
1768 conf.hlinks <- false;
1769 conf.fitmodel <- FitPage;
1770 state.x <- 0;
1771 conf.columns <- (
1772 match conf.beyecolumns with
1773 | Some c ->
1774 conf.zoom <- 1.0;
1775 Cmulti ((c, 0, 0), E.a)
1776 | None -> Csingle E.a
1778 if conf.verbose
1779 then
1780 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1781 (100.0*.zoom)
1782 else
1783 state.text <- E.s
1785 reshape state.winw state.winh;
1788 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1789 state.mode <- View;
1790 conf.zoom <- c.zoom;
1791 conf.presentation <- c.presentation;
1792 conf.interpagespace <- c.interpagespace;
1793 conf.hlinks <- c.hlinks;
1794 conf.fitmodel <- c.fitmodel;
1795 conf.beyecolumns <- (
1796 match conf.columns with
1797 | Cmulti ((c, _, _), _) -> Some c
1798 | Csingle _ -> None
1799 | Csplit _ -> failwith "leaving bird's eye split mode"
1801 conf.columns <- (
1802 match c.columns with
1803 | Cmulti (c, _) -> Cmulti (c, E.a)
1804 | Csingle _ -> Csingle E.a
1805 | Csplit (c, _) -> Csplit (c, E.a)
1807 if conf.verbose
1808 then
1809 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1810 (100.0*.conf.zoom)
1812 reshape state.winw state.winh;
1813 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1814 state.x <- leftx;
1817 let togglebirdseye () =
1818 match state.mode with
1819 | Birdseye vals -> leavebirdseye vals true
1820 | View -> enterbirdseye ()
1821 | Textentry _ | LinkNav _ -> ()
1824 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1825 let pageno = max 0 (pageno - incr) in
1826 let rec loop = function
1827 | [] -> gotopage1 pageno 0
1828 | l :: _ when l.pageno = pageno ->
1829 if l.pagedispy >= 0 && l.pagey = 0
1830 then postRedisplay "upbirdseye"
1831 else gotopage1 pageno 0
1832 | _ :: rest -> loop rest
1834 loop state.layout;
1835 state.text <- E.s;
1836 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1839 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1840 let pageno = min (state.pagecount - 1) (pageno + incr) in
1841 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1842 let rec loop = function
1843 | [] ->
1844 let y, h = getpageyh pageno in
1845 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1846 gotoxy state.x (clamp dy)
1847 | l :: _ when l.pageno = pageno ->
1848 if l.pagevh != l.pageh
1849 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1850 else postRedisplay "downbirdseye"
1851 | _ :: rest -> loop rest
1853 loop state.layout;
1854 state.text <- E.s;
1857 let optentry mode _ key =
1858 let btos b = if b then "on" else "off" in
1859 match [@warning "-4"] key with
1860 | Keys.Ascii 'C' ->
1861 let ondone s =
1863 let n, a, b = multicolumns_of_string s in
1864 setcolumns mode n a b;
1865 with exn ->
1866 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1868 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1870 | Keys.Ascii 'Z' ->
1871 let ondone s =
1873 let zoom = float (int_of_string s) /. 100.0 in
1874 pivotzoom zoom
1875 with exn ->
1876 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1878 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1880 | Keys.Ascii 'i' ->
1881 conf.icase <- not conf.icase;
1882 TEdone ("case insensitive search " ^ (btos conf.icase))
1884 | Keys.Ascii 'v' ->
1885 conf.verbose <- not conf.verbose;
1886 TEdone ("verbose " ^ (btos conf.verbose))
1888 | Keys.Ascii 'd' ->
1889 conf.debug <- not conf.debug;
1890 TEdone ("debug " ^ (btos conf.debug))
1892 | Keys.Ascii 'f' ->
1893 conf.underinfo <- not conf.underinfo;
1894 TEdone ("underinfo " ^ btos conf.underinfo)
1896 | Keys.Ascii 'l' ->
1897 let fm =
1898 match conf.fitmodel with
1899 | FitProportional -> FitWidth
1900 | FitWidth | FitPage -> FitProportional
1902 reqlayout conf.angle fm;
1903 TEdone ("proportional display " ^ btos (fm == FitProportional))
1905 | Keys.Ascii 'T' ->
1906 settrim (not conf.trimmargins) conf.trimfuzz;
1907 TEdone ("trim margins " ^ btos conf.trimmargins)
1909 | Keys.Ascii 'I' ->
1910 conf.invert <- not conf.invert;
1911 TEdone ("invert colors " ^ btos conf.invert)
1913 | Keys.Ascii 'x' ->
1914 let ondone s =
1915 cbput state.hists.sel s;
1916 conf.selcmd <- s;
1918 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1919 textentry, ondone, true)
1921 | Keys.Ascii 'M' ->
1922 if conf.pax == None
1923 then conf.pax <- Some 0.0
1924 else conf.pax <- None;
1925 TEdone ("PAX " ^ btos (conf.pax != None))
1927 | (Keys.Ascii c) ->
1928 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1929 TEstop
1931 | _ ->
1932 TEcont state.text
1935 let adderrmsg src msg =
1936 Buffer.add_string state.errmsgs msg;
1937 state.newerrmsgs <- true;
1938 postRedisplay src
1941 let adderrfmt src fmt =
1942 Format.ksprintf (fun s -> adderrmsg src s) fmt;
1945 class outlinelistview ~zebra ~source =
1946 let settext autonarrow s =
1947 if autonarrow
1948 then
1949 let ss = source#statestr in
1950 state.text <-
1951 if emptystr ss
1952 then "[" ^ s ^ "]"
1953 else "{" ^ ss ^ "} [" ^ s ^ "]"
1954 else state.text <- s
1956 object (self)
1957 inherit listview
1958 ~zebra
1959 ~helpmode:false
1960 ~source:(source :> lvsource)
1961 ~trusted:false
1962 ~modehash:(findkeyhash conf "outline")
1963 as super
1965 val m_autonarrow = false
1967 method! key key mask =
1968 let maxrows =
1969 if emptystr state.text
1970 then fstate.maxrows
1971 else fstate.maxrows - 2
1973 let calcfirst first active =
1974 if active > first
1975 then
1976 let rows = active - first in
1977 if rows > maxrows then active - maxrows else first
1978 else active
1980 let navigate incr =
1981 let active = m_active + incr in
1982 let active = bound active 0 (source#getitemcount - 1) in
1983 let first = calcfirst m_first active in
1984 postRedisplay "outline navigate";
1985 coe {< m_active = active; m_first = first >}
1987 let navscroll first =
1988 let active =
1989 let dist = m_active - first in
1990 if dist < 0
1991 then first
1992 else (
1993 if dist < maxrows
1994 then m_active
1995 else first + maxrows
1998 postRedisplay "outline navscroll";
1999 coe {< m_first = first; m_active = active >}
2001 let ctrl = Wsi.withctrl mask in
2002 let open Keys in
2003 match Wsi.kc2kt key with
2004 | Ascii 'a' when ctrl ->
2005 let text =
2006 if m_autonarrow
2007 then (source#denarrow; E.s)
2008 else (
2009 let pattern = source#renarrow in
2010 if nonemptystr m_qsearch
2011 then (source#narrow m_qsearch; m_qsearch)
2012 else pattern
2015 settext (not m_autonarrow) text;
2016 postRedisplay "toggle auto narrowing";
2017 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
2019 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
2020 settext true E.s;
2021 postRedisplay "toggle auto narrowing";
2022 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
2024 | Ascii 'n' when ctrl ->
2025 source#narrow m_qsearch;
2026 if not m_autonarrow
2027 then source#add_narrow_pattern m_qsearch;
2028 postRedisplay "outline ctrl-n";
2029 coe {< m_first = 0; m_active = 0 >}
2031 | Ascii 'S' when ctrl ->
2032 let active = source#calcactive (getanchor ()) in
2033 let first = firstof m_first active in
2034 postRedisplay "outline ctrl-s";
2035 coe {< m_first = first; m_active = active >}
2037 | Ascii 'u' when ctrl ->
2038 postRedisplay "outline ctrl-u";
2039 if m_autonarrow && nonemptystr m_qsearch
2040 then (
2041 ignore (source#renarrow);
2042 settext m_autonarrow E.s;
2043 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
2045 else (
2046 source#del_narrow_pattern;
2047 let pattern = source#renarrow in
2048 let text =
2049 if emptystr pattern then E.s else "Narrowed to " ^ pattern
2051 settext m_autonarrow text;
2052 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
2055 | Ascii 'l' when ctrl ->
2056 let first = max 0 (m_active - (fstate.maxrows / 2)) in
2057 postRedisplay "outline ctrl-l";
2058 coe {< m_first = first >}
2060 | Ascii '\t' when m_autonarrow ->
2061 if nonemptystr m_qsearch
2062 then (
2063 postRedisplay "outline list view tab";
2064 source#add_narrow_pattern m_qsearch;
2065 settext true E.s;
2066 coe {< m_qsearch = E.s >}
2068 else coe self
2070 | Escape when m_autonarrow ->
2071 if nonemptystr m_qsearch
2072 then source#add_narrow_pattern m_qsearch;
2073 super#key key mask
2075 | Enter when m_autonarrow ->
2076 if nonemptystr m_qsearch
2077 then source#add_narrow_pattern m_qsearch;
2078 super#key key mask
2080 | (Ascii _ | Code _) when m_autonarrow ->
2081 let pattern = m_qsearch ^ toutf8 key in
2082 postRedisplay "outlinelistview autonarrow add";
2083 source#narrow pattern;
2084 settext true pattern;
2085 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
2087 | Backspace when m_autonarrow ->
2088 if emptystr m_qsearch
2089 then coe self
2090 else
2091 let pattern = withoutlastutf8 m_qsearch in
2092 postRedisplay "outlinelistview autonarrow backspace";
2093 ignore (source#renarrow);
2094 source#narrow pattern;
2095 settext true pattern;
2096 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
2098 | Up when ctrl ->
2099 navscroll (max 0 (m_first - 1))
2101 | Down when ctrl ->
2102 navscroll (min (source#getitemcount - 1) (m_first + 1))
2104 | Up -> navigate ~-1
2105 | Down -> navigate 1
2106 | Prior -> navigate ~-(fstate.maxrows)
2107 | Next -> navigate fstate.maxrows
2109 | Right ->
2110 let o =
2111 if ctrl
2112 then (
2113 postRedisplay "outline ctrl right";
2114 {< m_pan = m_pan + 1 >}
2116 else self#updownlevel 1
2118 coe o
2120 | Left ->
2121 let o =
2122 if ctrl
2123 then (
2124 postRedisplay "outline ctrl left";
2125 {< m_pan = m_pan - 1 >}
2127 else self#updownlevel ~-1
2129 coe o
2131 | Home ->
2132 postRedisplay "outline home";
2133 coe {< m_first = 0; m_active = 0 >}
2135 | End ->
2136 let active = source#getitemcount - 1 in
2137 let first = max 0 (active - fstate.maxrows) in
2138 postRedisplay "outline end";
2139 coe {< m_active = active; m_first = first >}
2141 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
2142 super#key key mask
2143 end;;
2145 let genhistoutlines () =
2146 Config.gethist ()
2147 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
2148 compare c2.lastvisit c1.lastvisit)
2149 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
2150 let path = if nonemptystr origin then origin else path in
2151 let base = mbtoutf8 @@ Filename.basename path in
2152 (base ^ "\000" ^ c.title, 1, Ohistory hist)
2156 let gotohist (path, c, bookmarks, x, anchor, origin) =
2157 Config.save leavebirdseye;
2158 state.anchor <- anchor;
2159 state.bookmarks <- bookmarks;
2160 state.origin <- origin;
2161 state.x <- x;
2162 setconf conf c;
2163 let x0, y0, x1, y1 = conf.trimfuzz in
2164 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2165 reshape ~firsttime:true state.winw state.winh;
2166 opendoc path origin;
2167 setzoom c.zoom;
2170 let setcheckers enabled =
2171 match state.checkerstexid with
2172 | None ->
2173 if enabled then state.checkerstexid <- Some (makecheckers ())
2175 | Some checkerstexid ->
2176 if not enabled
2177 then (
2178 GlTex.delete_texture checkerstexid;
2179 state.checkerstexid <- None;
2183 let describe_layout layout =
2184 let d =
2185 match layout with
2186 | [] -> "Page 0"
2187 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2188 | l :: rest ->
2189 let rangestr a b =
2190 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2191 else
2192 let sep = if a.pageno+1 = b.pageno then ", " else Unisyms.ellipsis in
2193 Printf.sprintf "%d%s%d" (a.pageno+1) sep (b.pageno+1)
2195 let rec fold s la lb = function
2196 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2197 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2198 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2200 fold "Pages" l l rest
2202 let percent =
2203 let maxy = maxy () in
2204 if maxy <= 0
2205 then 100.
2206 else 100. *. (float state.y /. float maxy)
2208 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2211 let setpresentationmode v =
2212 let n = page_of_y state.y in
2213 state.anchor <- (n, 0.0, 1.0);
2214 conf.presentation <- v;
2215 if conf.fitmodel = FitPage
2216 then reqlayout conf.angle conf.fitmodel;
2217 represent ();
2220 let enterinfomode =
2221 let btos b = if b then Unisyms.radical else E.s in
2222 let showextended = ref false in
2223 let showcolors = ref false in
2224 let leave mode _ = state.mode <- mode in
2225 let src =
2226 (object
2227 val mutable m_l = []
2228 val mutable m_a = E.a
2229 val mutable m_prev_uioh = nouioh
2230 val mutable m_prev_mode = View
2232 inherit lvsourcebase
2234 method reset prev_mode prev_uioh =
2235 m_a <- Array.of_list (List.rev m_l);
2236 m_l <- [];
2237 m_prev_mode <- prev_mode;
2238 m_prev_uioh <- prev_uioh;
2240 method int name get set =
2241 m_l <-
2242 (name, `int get, 1,
2243 Action (
2244 fun u ->
2245 let ondone s =
2246 try set (int_of_string s)
2247 with exn ->
2248 state.text <- Printf.sprintf "bad integer `%s': %s"
2249 s @@ exntos exn
2251 state.text <- E.s;
2252 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2253 state.mode <- Textentry (te, leave m_prev_mode);
2255 )) :: m_l
2257 method int_with_suffix name get set =
2258 m_l <-
2259 (name, `intws get, 1,
2260 Action (
2261 fun u ->
2262 let ondone s =
2263 try set (int_of_string_with_suffix s)
2264 with exn ->
2265 state.text <- Printf.sprintf "bad integer `%s': %s"
2266 s @@ exntos exn
2268 state.text <- E.s;
2269 let te =
2270 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2272 state.mode <- Textentry (te, leave m_prev_mode);
2274 )) :: m_l
2276 method bool ?(offset=1) ?(btos=btos) name get set =
2277 m_l <-
2278 (name, `bool (btos, get), offset, Action (
2279 fun u ->
2280 let v = get () in
2281 set (not v);
2283 )) :: m_l
2285 method color name get set =
2286 m_l <-
2287 (name, `color get, 1,
2288 Action (
2289 fun u ->
2290 let invalid = (nan, nan, nan) in
2291 let ondone s =
2292 let c =
2293 try color_of_string s
2294 with exn ->
2295 state.text <- Printf.sprintf "bad color `%s': %s"
2296 s @@ exntos exn;
2297 invalid
2299 if c <> invalid
2300 then set c;
2302 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2303 state.text <- color_to_string (get ());
2304 state.mode <- Textentry (te, leave m_prev_mode);
2306 )) :: m_l
2308 method string name get set =
2309 m_l <-
2310 (name, `string get, 1,
2311 Action (
2312 fun u ->
2313 let ondone s = set s in
2314 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2315 state.mode <- Textentry (te, leave m_prev_mode);
2317 )) :: m_l
2319 method colorspace name get set =
2320 m_l <-
2321 (name, `string get, 1,
2322 Action (
2323 fun _ ->
2324 let source =
2325 (object
2326 inherit lvsourcebase
2328 initializer
2329 m_active <- CSTE.to_int conf.colorspace;
2330 m_first <- 0;
2332 method getitemcount =
2333 Array.length CSTE.names
2334 method getitem n =
2335 (CSTE.names.(n), 0)
2336 method exit ~uioh ~cancel ~active ~first ~pan =
2337 ignore (uioh, first, pan);
2338 if not cancel then set active;
2339 None
2340 method hasaction _ = true
2341 end)
2343 state.text <- E.s;
2344 let modehash = findkeyhash conf "info" in
2345 coe (new listview ~zebra:false ~helpmode:false
2346 ~source ~trusted:true ~modehash)
2347 )) :: m_l
2349 method paxmark name get set =
2350 m_l <-
2351 (name, `string get, 1,
2352 Action (
2353 fun _ ->
2354 let source =
2355 (object
2356 inherit lvsourcebase
2358 initializer
2359 m_active <- MTE.to_int conf.paxmark;
2360 m_first <- 0;
2362 method getitemcount = Array.length MTE.names
2363 method getitem n = (MTE.names.(n), 0)
2364 method exit ~uioh ~cancel ~active ~first ~pan =
2365 ignore (uioh, first, pan);
2366 if not cancel then set active;
2367 None
2368 method hasaction _ = true
2369 end)
2371 state.text <- E.s;
2372 let modehash = findkeyhash conf "info" in
2373 coe (new listview ~zebra:false ~helpmode:false
2374 ~source ~trusted:true ~modehash)
2375 )) :: m_l
2377 method fitmodel name get set =
2378 m_l <-
2379 (name, `string get, 1,
2380 Action (
2381 fun _ ->
2382 let source =
2383 (object
2384 inherit lvsourcebase
2386 initializer
2387 m_active <- FMTE.to_int conf.fitmodel;
2388 m_first <- 0;
2390 method getitemcount = Array.length FMTE.names
2391 method getitem n = (FMTE.names.(n), 0)
2392 method exit ~uioh ~cancel ~active ~first ~pan =
2393 ignore (uioh, first, pan);
2394 if not cancel then set active;
2395 None
2396 method hasaction _ = true
2397 end)
2399 state.text <- E.s;
2400 let modehash = findkeyhash conf "info" in
2401 coe (new listview ~zebra:false ~helpmode:false
2402 ~source ~trusted:true ~modehash)
2403 )) :: m_l
2405 method caption s offset =
2406 m_l <- (s, `empty, offset, Noaction) :: m_l
2408 method caption2 s f offset =
2409 m_l <- (s, `string f, offset, Noaction) :: m_l
2411 method getitemcount = Array.length m_a
2413 method getitem n =
2414 let tostr = function
2415 | `int f -> string_of_int (f ())
2416 | `intws f -> string_with_suffix_of_int (f ())
2417 | `string f -> f ()
2418 | `color f -> color_to_string (f ())
2419 | `bool (btos, f) -> btos (f ())
2420 | `empty -> E.s
2422 let name, t, offset, _ = m_a.(n) in
2423 ((let s = tostr t in
2424 if nonemptystr s
2425 then Printf.sprintf "%s\t%s" name s
2426 else name),
2427 offset)
2429 method exit ~uioh ~cancel ~active ~first ~pan =
2430 let uiohopt =
2431 if not cancel
2432 then (
2433 let uioh =
2434 match m_a.(active) with
2435 | _, _, _, Action f -> f uioh
2436 | _, _, _, Noaction -> uioh
2438 Some uioh
2440 else None
2442 m_active <- active;
2443 m_first <- first;
2444 m_pan <- pan;
2445 uiohopt
2447 method hasaction n =
2448 match m_a.(n) with
2449 | _, _, _, Action _ -> true
2450 | _, _, _, Noaction -> false
2452 initializer m_active <- 1
2453 end)
2455 let rec fillsrc prevmode prevuioh =
2456 let sep () = src#caption E.s 0 in
2457 let colorp name get set =
2458 src#string name
2459 (fun () -> color_to_string (get ()))
2460 (fun v ->
2462 let c = color_of_string v in
2463 set c
2464 with exn ->
2465 state.text <-
2466 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2469 let rgba name get set =
2470 src#string name
2471 (fun () -> rgba_to_string (get ()))
2472 (fun v ->
2474 let c = rgba_of_string v in
2475 set c
2476 with exn ->
2477 state.text <-
2478 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2481 let oldmode = state.mode in
2482 let birdseye = isbirdseye state.mode in
2484 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2486 src#bool "presentation mode"
2487 (fun () -> conf.presentation)
2488 (fun v -> setpresentationmode v);
2490 src#bool "ignore case in searches"
2491 (fun () -> conf.icase)
2492 (fun v -> conf.icase <- v);
2494 src#bool "preload"
2495 (fun () -> conf.preload)
2496 (fun v -> conf.preload <- v);
2498 src#bool "highlight links"
2499 (fun () -> conf.hlinks)
2500 (fun v -> conf.hlinks <- v);
2502 src#bool "under info"
2503 (fun () -> conf.underinfo)
2504 (fun v -> conf.underinfo <- v);
2506 src#fitmodel "fit model"
2507 (fun () -> FMTE.to_string conf.fitmodel)
2508 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2510 src#bool "trim margins"
2511 (fun () -> conf.trimmargins)
2512 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2514 sep ();
2515 src#int "inter-page space"
2516 (fun () -> conf.interpagespace)
2517 (fun n ->
2518 conf.interpagespace <- n;
2519 docolumns conf.columns;
2520 let pageno, py =
2521 match state.layout with
2522 | [] -> 0, 0
2523 | l :: _ ->
2524 l.pageno, l.pagey
2526 state.maxy <- calcheight ();
2527 let y = getpagey pageno in
2528 gotoxy state.x (y + py)
2531 src#int "page bias"
2532 (fun () -> conf.pagebias)
2533 (fun v -> conf.pagebias <- v);
2535 src#int "scroll step"
2536 (fun () -> conf.scrollstep)
2537 (fun n -> conf.scrollstep <- n);
2539 src#int "horizontal scroll step"
2540 (fun () -> conf.hscrollstep)
2541 (fun v -> conf.hscrollstep <- v);
2543 src#int "auto scroll step"
2544 (fun () ->
2545 match state.autoscroll with
2546 | Some step -> step
2547 | _ -> conf.autoscrollstep)
2548 (fun n ->
2549 let n = boundastep state.winh n in
2550 if state.autoscroll <> None
2551 then state.autoscroll <- Some n;
2552 conf.autoscrollstep <- n);
2554 src#int "zoom"
2555 (fun () -> truncate (conf.zoom *. 100.))
2556 (fun v -> pivotzoom ((float v) /. 100.));
2558 src#int "rotation"
2559 (fun () -> conf.angle)
2560 (fun v -> reqlayout v conf.fitmodel);
2562 src#int "scroll bar width"
2563 (fun () -> conf.scrollbw)
2564 (fun v ->
2565 conf.scrollbw <- v;
2566 reshape state.winw state.winh;
2569 src#int "scroll handle height"
2570 (fun () -> conf.scrollh)
2571 (fun v -> conf.scrollh <- v;);
2573 src#int "thumbnail width"
2574 (fun () -> conf.thumbw)
2575 (fun v ->
2576 conf.thumbw <- min 4096 v;
2577 match oldmode with
2578 | Birdseye beye ->
2579 leavebirdseye beye false;
2580 enterbirdseye ()
2581 | Textentry _
2582 | View
2583 | LinkNav _ -> ()
2586 let mode = state.mode in
2587 src#string "columns"
2588 (fun () ->
2589 match conf.columns with
2590 | Csingle _ -> "1"
2591 | Cmulti (multi, _) -> multicolumns_to_string multi
2592 | Csplit (count, _) -> "-" ^ string_of_int count
2594 (fun v ->
2595 let n, a, b = multicolumns_of_string v in
2596 setcolumns mode n a b);
2598 sep ();
2599 src#caption "Pixmap cache" 0;
2600 src#int_with_suffix "size (advisory)"
2601 (fun () -> conf.memlimit)
2602 (fun v -> conf.memlimit <- v);
2604 src#caption2 "used"
2605 (fun () ->
2606 Printf.sprintf "%s bytes, %d tiles"
2607 (string_with_suffix_of_int state.memused)
2608 (Hashtbl.length state.tilemap)) 1;
2610 sep ();
2611 src#caption "Layout" 0;
2612 src#caption2 "Dimension"
2613 (fun () ->
2614 Printf.sprintf "%dx%d (virtual %dx%d)"
2615 state.winw state.winh
2616 state.w state.maxy)
2618 if conf.debug
2619 then
2620 src#caption2 "Position" (fun () ->
2621 Printf.sprintf "%dx%d" state.x state.y
2623 else
2624 src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2626 sep ();
2627 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2628 "Save these parameters as global defaults at exit"
2629 (fun () -> conf.bedefault)
2630 (fun v -> conf.bedefault <- v);
2632 sep ();
2633 let btos b = if b then Unisyms.lguillemet else Unisyms.rguillemet in
2634 src#bool ~offset:0 ~btos "Extended parameters"
2635 (fun () -> !showextended)
2636 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2637 if !showextended
2638 then (
2639 src#bool "checkers"
2640 (fun () -> conf.checkers)
2641 (fun v -> conf.checkers <- v; setcheckers v);
2642 src#bool "update cursor"
2643 (fun () -> conf.updatecurs)
2644 (fun v -> conf.updatecurs <- v);
2645 src#bool "scroll-bar on the left"
2646 (fun () -> conf.leftscroll)
2647 (fun v -> conf.leftscroll <- v);
2648 src#bool "verbose"
2649 (fun () -> conf.verbose)
2650 (fun v -> conf.verbose <- v);
2651 src#bool "invert colors"
2652 (fun () -> conf.invert)
2653 (fun v -> conf.invert <- v);
2654 src#bool "max fit"
2655 (fun () -> conf.maxhfit)
2656 (fun v -> conf.maxhfit <- v);
2657 src#bool "pax mode"
2658 (fun () -> conf.pax != None)
2659 (fun v ->
2660 if v
2661 then conf.pax <- Some (now ())
2662 else conf.pax <- None);
2663 src#string "uri launcher"
2664 (fun () -> conf.urilauncher)
2665 (fun v -> conf.urilauncher <- v);
2666 src#string "path launcher"
2667 (fun () -> conf.pathlauncher)
2668 (fun v -> conf.pathlauncher <- v);
2669 src#string "tile size"
2670 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2671 (fun v ->
2673 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2674 conf.tilew <- max 64 w;
2675 conf.tileh <- max 64 h;
2676 flushtiles ();
2677 with exn ->
2678 state.text <- Printf.sprintf "bad tile size `%s': %s"
2679 v @@ exntos exn
2681 src#int "texture count"
2682 (fun () -> conf.texcount)
2683 (fun v ->
2684 if realloctexts v
2685 then conf.texcount <- v
2686 else impmsg "failed to set texture count please retry later"
2688 src#int "slice height"
2689 (fun () -> conf.sliceheight)
2690 (fun v ->
2691 conf.sliceheight <- v;
2692 wcmd "sliceh %d" conf.sliceheight;
2694 src#int "anti-aliasing level"
2695 (fun () -> conf.aalevel)
2696 (fun v ->
2697 conf.aalevel <- bound v 0 8;
2698 state.anchor <- getanchor ();
2699 opendoc state.path state.password;
2701 src#string "page scroll scaling factor"
2702 (fun () -> string_of_float conf.pgscale)
2703 (fun v ->
2705 let s = float_of_string v in
2706 conf.pgscale <- s
2707 with exn ->
2708 state.text <- Printf.sprintf
2709 "bad page scroll scaling factor `%s': %s" v
2710 @@ exntos exn
2713 src#int "ui font size"
2714 (fun () -> fstate.fontsize)
2715 (fun v -> setfontsize (bound v 5 100));
2716 src#int "hint font size"
2717 (fun () -> conf.hfsize)
2718 (fun v -> conf.hfsize <- bound v 5 100);
2719 src#string "trim fuzz"
2720 (fun () -> irect_to_string conf.trimfuzz)
2721 (fun v ->
2723 conf.trimfuzz <- irect_of_string v;
2724 if conf.trimmargins
2725 then settrim true conf.trimfuzz;
2726 with exn ->
2727 state.text <- Printf.sprintf "bad irect `%s': %s" v
2728 @@ exntos exn
2730 src#string "selection command"
2731 (fun () -> conf.selcmd)
2732 (fun v -> conf.selcmd <- v);
2733 src#string "synctex command"
2734 (fun () -> conf.stcmd)
2735 (fun v -> conf.stcmd <- v);
2736 src#string "pax command"
2737 (fun () -> conf.paxcmd)
2738 (fun v -> conf.paxcmd <- v);
2739 src#string "ask password command"
2740 (fun () -> conf.passcmd)
2741 (fun v -> conf.passcmd <- v);
2742 src#string "save path command"
2743 (fun () -> conf.savecmd)
2744 (fun v -> conf.savecmd <- v);
2745 src#colorspace "color space"
2746 (fun () -> CSTE.to_string conf.colorspace)
2747 (fun v ->
2748 conf.colorspace <- CSTE.of_int v;
2749 wcmd "cs %d" v;
2750 load state.layout;
2752 src#paxmark "pax mark method"
2753 (fun () -> MTE.to_string conf.paxmark)
2754 (fun v -> conf.paxmark <- MTE.of_int v);
2755 if bousable () && !opengl_has_pbo
2756 then
2757 src#bool "use PBO"
2758 (fun () -> conf.usepbo)
2759 (fun v -> conf.usepbo <- v);
2760 src#bool "mouse wheel scrolls pages"
2761 (fun () -> conf.wheelbypage)
2762 (fun v -> conf.wheelbypage <- v);
2763 src#bool "open remote links in a new instance"
2764 (fun () -> conf.riani)
2765 (fun v -> conf.riani <- v);
2766 src#bool "edit annotations inline"
2767 (fun () -> conf.annotinline)
2768 (fun v -> conf.annotinline <- v);
2769 src#bool "coarse positioning in presentation mode"
2770 (fun () -> conf.coarseprespos)
2771 (fun v -> conf.coarseprespos <- v);
2772 src#bool "use document CSS"
2773 (fun () -> conf.usedoccss)
2774 (fun v ->
2775 conf.usedoccss <- v;
2776 state.anchor <- getanchor ();
2777 opendoc state.path state.password;
2779 src#bool ~btos "colors"
2780 (fun () -> !showcolors)
2781 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2782 if !showcolors
2783 then (
2784 colorp " background"
2785 (fun () -> conf.bgcolor)
2786 (fun v -> conf.bgcolor <- v);
2787 rgba " scrollbar"
2788 (fun () -> conf.sbarcolor)
2789 (fun v -> conf.sbarcolor <- v);
2790 rgba " scrollbar handle"
2791 (fun () -> conf.sbarhndlcolor)
2792 (fun v -> conf.sbarhndlcolor <- v);
2796 sep ();
2797 src#caption "Document" 0;
2798 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2799 src#caption2 "Pages"
2800 (fun () -> string_of_int state.pagecount) 1;
2801 src#caption2 "Dimensions"
2802 (fun () -> string_of_int (List.length state.pdims)) 1;
2803 if nonemptystr conf.css
2804 then src#caption2 "CSS" (fun () -> conf.css) 1;
2805 if conf.trimmargins
2806 then (
2807 sep ();
2808 src#caption "Trimmed margins" 0;
2809 src#caption2 "Dimensions"
2810 (fun () -> string_of_int (List.length state.pdims)) 1;
2813 sep ();
2814 src#caption "OpenGL" 0;
2815 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2816 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2818 sep ();
2819 src#caption "Location" 0;
2820 if nonemptystr state.origin
2821 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2822 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2824 src#reset prevmode prevuioh;
2826 fun () ->
2827 state.text <- E.s;
2828 resetmstate ();
2829 let prevmode = state.mode
2830 and prevuioh = state.uioh in
2831 fillsrc prevmode prevuioh;
2832 let source = (src :> lvsource) in
2833 let modehash = findkeyhash conf "info" in
2834 state.uioh <-
2835 coe (object (self)
2836 inherit listview ~zebra:false ~helpmode:false
2837 ~source ~trusted:true ~modehash as super
2838 val mutable m_prevmemused = 0
2839 method! infochanged = function
2840 | Memused ->
2841 if m_prevmemused != state.memused
2842 then (
2843 m_prevmemused <- state.memused;
2844 postRedisplay "memusedchanged";
2846 | Pdim -> postRedisplay "pdimchanged"
2847 | Docinfo -> fillsrc prevmode prevuioh
2849 method! key key mask =
2850 if not (Wsi.withctrl mask)
2851 then
2852 match [@warning "-4"] Wsi.kc2kt key with
2853 | Keys.Left -> coe (self#updownlevel ~-1)
2854 | Keys.Right -> coe (self#updownlevel 1)
2855 | _ -> super#key key mask
2856 else super#key key mask
2857 end);
2858 postRedisplay "info";
2861 let enterhelpmode =
2862 let source =
2863 (object
2864 inherit lvsourcebase
2865 method getitemcount = Array.length state.help
2866 method getitem n =
2867 let s, l, _ = state.help.(n) in
2868 (s, l)
2870 method exit ~uioh ~cancel ~active ~first ~pan =
2871 let optuioh =
2872 if not cancel
2873 then (
2874 match state.help.(active) with
2875 | _, _, Action f -> Some (f uioh)
2876 | _, _, Noaction -> Some uioh
2878 else None
2880 m_active <- active;
2881 m_first <- first;
2882 m_pan <- pan;
2883 optuioh
2885 method hasaction n =
2886 match state.help.(n) with
2887 | _, _, Action _ -> true
2888 | _, _, Noaction -> false
2890 initializer
2891 m_active <- -1
2892 end)
2893 in fun () ->
2894 let modehash = findkeyhash conf "help" in
2895 resetmstate ();
2896 state.uioh <- coe (new listview
2897 ~zebra:false ~helpmode:true
2898 ~source ~trusted:true ~modehash);
2899 postRedisplay "help";
2902 let entermsgsmode =
2903 let msgsource =
2904 (object
2905 inherit lvsourcebase
2906 val mutable m_items = E.a
2908 method getitemcount = 1 + Array.length m_items
2910 method getitem n =
2911 if n = 0
2912 then "[Clear]", 0
2913 else m_items.(n-1), 0
2915 method exit ~uioh ~cancel ~active ~first ~pan =
2916 ignore uioh;
2917 if not cancel
2918 then (
2919 if active = 0
2920 then Buffer.clear state.errmsgs;
2922 m_active <- active;
2923 m_first <- first;
2924 m_pan <- pan;
2925 None
2927 method hasaction n =
2928 n = 0
2930 method reset =
2931 state.newerrmsgs <- false;
2932 let l = Str.split newlinere (Buffer.contents state.errmsgs) in
2933 m_items <- Array.of_list l
2935 initializer
2936 m_active <- 0
2937 end)
2938 in fun () ->
2939 state.text <- E.s;
2940 resetmstate ();
2941 msgsource#reset;
2942 let source = (msgsource :> lvsource) in
2943 let modehash = findkeyhash conf "listview" in
2944 state.uioh <-
2945 coe (object
2946 inherit listview ~zebra:false ~helpmode:false
2947 ~source ~trusted:false ~modehash as super
2948 method! display =
2949 if state.newerrmsgs
2950 then msgsource#reset;
2951 super#display
2952 end);
2953 postRedisplay "msgs";
2956 let getusertext s =
2957 let editor = getenvwithdef "EDITOR" E.s in
2958 if emptystr editor
2959 then E.s
2960 else
2961 let tmppath = Filename.temp_file "llpp" "note" in
2962 if nonemptystr s
2963 then (
2964 let oc = open_out tmppath in
2965 output_string oc s;
2966 close_out oc;
2968 let execstr = editor ^ " " ^ tmppath in
2969 let s =
2970 match spawn execstr [] with
2971 | exception exn ->
2972 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
2974 | pid ->
2975 match Unix.waitpid [] pid with
2976 | exception exn ->
2977 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
2979 | (_pid, status) ->
2980 match status with
2981 | Unix.WEXITED 0 -> filecontents tmppath
2982 | Unix.WEXITED n ->
2983 impmsg "editor process(%s) exited abnormally: %d" execstr n;
2985 | Unix.WSIGNALED n ->
2986 impmsg "editor process(%s) was killed by signal %d" execstr n;
2988 | Unix.WSTOPPED n ->
2989 impmsg "editor(%s) process was stopped by signal %d" execstr n;
2992 match Unix.unlink tmppath with
2993 | exception exn ->
2994 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
2996 | () -> s
2999 let enterannotmode opaque slinkindex =
3000 let msgsource =
3001 (object
3002 inherit lvsourcebase
3003 val mutable m_text = E.s
3004 val mutable m_items = E.a
3006 method getitemcount = Array.length m_items
3008 method getitem n =
3009 let label, _func = m_items.(n) in
3010 label, 0
3012 method exit ~uioh ~cancel ~active ~first ~pan =
3013 ignore (uioh, first, pan);
3014 if not cancel
3015 then (
3016 let _label, func = m_items.(active) in
3017 func ()
3019 None
3021 method hasaction n = nonemptystr @@ fst m_items.(n)
3023 method reset s =
3024 let rec split accu b i =
3025 let p = b+i in
3026 if p = String.length s
3027 then (String.sub s b (p-b), unit) :: accu
3028 else
3029 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
3030 then
3031 let ss = if i = 0 then E.s else String.sub s b i in
3032 split ((ss, unit)::accu) (p+1) 0
3033 else
3034 split accu b (i+1)
3036 let cleanup () =
3037 wcmd "freepage %s" (~> opaque);
3038 let keys =
3039 Hashtbl.fold (fun key opaque' accu ->
3040 if opaque' = opaque'
3041 then key :: accu else accu) state.pagemap []
3043 List.iter (Hashtbl.remove state.pagemap) keys;
3044 flushtiles ();
3045 gotoxy state.x state.y
3047 let dele () =
3048 delannot opaque slinkindex;
3049 cleanup ();
3051 let edit inline () =
3052 let update s =
3053 if emptystr s
3054 then dele ()
3055 else (
3056 modannot opaque slinkindex s;
3057 cleanup ();
3060 if inline
3061 then
3062 let mode = state.mode in
3063 state.mode <-
3064 Textentry (
3065 ("annotation: ", m_text, None, textentry, update, true),
3066 fun _ -> state.mode <- mode);
3067 state.text <- E.s;
3068 enttext ();
3069 else
3070 let s = getusertext m_text in
3071 update s
3073 m_text <- s;
3074 m_items <-
3075 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
3076 :: ("[Delete]", dele)
3077 :: ("[Edit]", edit conf.annotinline)
3078 :: (E.s, unit)
3079 :: split [] 0 0 |> List.rev |> Array.of_list
3081 initializer
3082 m_active <- 0
3083 end)
3085 state.text <- E.s;
3086 let s = getannotcontents opaque slinkindex in
3087 resetmstate ();
3088 msgsource#reset s;
3089 let source = (msgsource :> lvsource) in
3090 let modehash = findkeyhash conf "listview" in
3091 state.uioh <- coe (object
3092 inherit listview ~zebra:false ~helpmode:false
3093 ~source ~trusted:false ~modehash
3094 end);
3095 postRedisplay "enterannotmode";
3098 let gotoremote spec =
3099 let filename, dest = splitatchar spec '#' in
3100 let getpath filename =
3101 let path =
3102 if nonemptystr filename
3103 then
3104 if Filename.is_relative filename
3105 then
3106 let dir = Filename.dirname state.path in
3107 let dir =
3108 if Filename.is_implicit dir
3109 then Filename.concat (Sys.getcwd ()) dir
3110 else dir
3112 Filename.concat dir filename
3113 else filename
3114 else E.s
3116 if Sys.file_exists path
3117 then path
3118 else E.s
3120 let path = getpath filename in
3121 let dospawn lcmd =
3122 if conf.riani
3123 then
3124 let cmd = Lazy.force_val lcmd in
3125 match spawn cmd with
3126 | _pid -> ()
3127 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
3128 else
3129 let anchor = getanchor () in
3130 let ranchor = state.path, state.password, anchor, state.origin in
3131 state.origin <- E.s;
3132 state.ranchors <- ranchor :: state.ranchors;
3133 opendoc path E.s;
3135 if substratis spec 0 "page="
3136 then
3137 match Scanf.sscanf spec "page=%d" (fun n -> n) with
3138 | pageno ->
3139 state.anchor <- (pageno, 0.0, 0.0);
3140 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
3141 | exception exn ->
3142 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
3143 else (
3144 state.nameddest <- dest;
3145 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
3149 let gotounder = function
3150 | Ulinkuri s when isexternallink s ->
3151 if substratis s 0 "file://"
3152 then gotoremote @@ String.sub s 7 (String.length s - 7)
3153 else Help.gotouri conf.urilauncher s
3154 | Ulinkuri s ->
3155 let pageno, x, y = uritolocation s in
3156 addnav ();
3157 gotopagexy pageno x y
3158 | Utext _ | Unone -> ()
3159 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
3162 let gotooutline (_, _, kind) =
3163 match kind with
3164 | Onone -> ()
3165 | Oanchor anchor ->
3166 let (pageno, y, _) = anchor in
3167 let y = getanchory
3168 (if conf.presentation then (pageno, y, 1.0) else anchor)
3170 addnav ();
3171 gotoxy state.x y
3172 | Ouri uri -> gotounder (Ulinkuri uri)
3173 | Olaunch _cmd -> failwith "gotounder (Ulaunch cmd)"
3174 | Oremote _remote -> failwith "gotounder (Uremote remote)"
3175 | Ohistory hist -> gotohist hist
3176 | Oremotedest _remotedest -> failwith "gotounder (Uremotedest remotedest)"
3179 class outlinesoucebase fetchoutlines = object (self)
3180 inherit lvsourcebase
3181 val mutable m_items = E.a
3182 val mutable m_minfo = E.a
3183 val mutable m_orig_items = E.a
3184 val mutable m_orig_minfo = E.a
3185 val mutable m_narrow_patterns = []
3186 val mutable m_gen = -1
3188 method getitemcount = Array.length m_items
3190 method getitem n =
3191 let s, n, _ = m_items.(n) in
3192 (s, n+0)
3194 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3195 ignore (uioh, first);
3196 let items, minfo =
3197 if m_narrow_patterns = []
3198 then m_orig_items, m_orig_minfo
3199 else m_items, m_minfo
3201 m_pan <- pan;
3202 if not cancel
3203 then (
3204 m_items <- items;
3205 m_minfo <- minfo;
3206 gotooutline m_items.(active);
3208 else (
3209 m_items <- items;
3210 m_minfo <- minfo;
3212 None
3214 method hasaction (_:int) = true
3216 method greetmsg =
3217 if Array.length m_items != Array.length m_orig_items
3218 then
3219 let s =
3220 match m_narrow_patterns with
3221 | one :: [] -> one
3222 | many -> String.concat Unisyms.ellipsis (List.rev many)
3224 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3225 else E.s
3227 method statestr =
3228 match m_narrow_patterns with
3229 | [] -> E.s
3230 | one :: [] -> one
3231 | head :: _ -> Unisyms.ellipsis ^ head
3233 method narrow pattern =
3234 match Str.regexp_case_fold pattern with
3235 | exception _ -> ()
3236 | re ->
3237 let rec loop accu minfo n =
3238 if n = -1
3239 then (
3240 m_items <- Array.of_list accu;
3241 m_minfo <- Array.of_list minfo;
3243 else
3244 let (s, _, _) as o = m_items.(n) in
3245 let accu, minfo =
3246 match Str.search_forward re s 0 with
3247 | exception Not_found -> accu, minfo
3248 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3250 loop accu minfo (n-1)
3252 loop [] [] (Array.length m_items - 1)
3254 method! getminfo = m_minfo
3256 method denarrow =
3257 m_orig_items <- fetchoutlines ();
3258 m_minfo <- m_orig_minfo;
3259 m_items <- m_orig_items
3261 method add_narrow_pattern pattern =
3262 m_narrow_patterns <- pattern :: m_narrow_patterns
3264 method del_narrow_pattern =
3265 match m_narrow_patterns with
3266 | _ :: rest -> m_narrow_patterns <- rest
3267 | [] -> ()
3269 method renarrow =
3270 self#denarrow;
3271 match m_narrow_patterns with
3272 | pattern :: [] -> self#narrow pattern; pattern
3273 | list ->
3274 List.fold_left (fun accu pattern ->
3275 self#narrow pattern;
3276 pattern ^ Unisyms.ellipsis ^ accu) E.s list
3278 method calcactive (_:anchor) = 0
3280 method reset anchor items =
3281 if state.gen != m_gen
3282 then (
3283 m_orig_items <- items;
3284 m_items <- items;
3285 m_narrow_patterns <- [];
3286 m_minfo <- E.a;
3287 m_orig_minfo <- E.a;
3288 m_gen <- state.gen;
3290 else (
3291 if items != m_orig_items
3292 then (
3293 m_orig_items <- items;
3294 if m_narrow_patterns == []
3295 then m_items <- items;
3298 let active = self#calcactive anchor in
3299 m_active <- active;
3300 m_first <- firstof m_first active
3304 let outlinesource fetchoutlines =
3305 (object
3306 inherit outlinesoucebase fetchoutlines
3307 method! calcactive anchor =
3308 let rely = getanchory anchor in
3309 let rec loop n best bestd =
3310 if n = Array.length m_items
3311 then best
3312 else
3313 let _, _, kind = m_items.(n) in
3314 match kind with
3315 | Oanchor anchor ->
3316 let orely = getanchory anchor in
3317 let d = abs (orely - rely) in
3318 if d < bestd
3319 then loop (n+1) n d
3320 else loop (n+1) best bestd
3321 | Onone | Oremote _ | Olaunch _
3322 | Oremotedest _ | Ouri _ | Ohistory _ ->
3323 loop (n+1) best bestd
3325 loop 0 ~-1 max_int
3326 end)
3329 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3330 let mkselector sourcetype =
3331 let fetchoutlines () =
3332 match sourcetype with
3333 | `bookmarks -> Array.of_list state.bookmarks
3334 | `outlines -> state.outlines
3335 | `history -> genhistoutlines () |> Array.of_list
3337 let source =
3338 if sourcetype = `history
3339 then new outlinesoucebase fetchoutlines
3340 else outlinesource fetchoutlines
3342 (fun errmsg ->
3343 let outlines = fetchoutlines () in
3344 if Array.length outlines = 0
3345 then showtext ' ' errmsg
3346 else (
3347 resetmstate ();
3348 Wsi.setcursor Wsi.CURSOR_INHERIT;
3349 let anchor = getanchor () in
3350 source#reset anchor outlines;
3351 state.text <- source#greetmsg;
3352 state.uioh <-
3353 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3354 postRedisplay "enter selector";
3358 let mkenter sourcetype errmsg =
3359 let enter = mkselector sourcetype in
3360 fun () -> enter errmsg
3362 ( mkenter `outlines "document has no outline"
3363 , mkenter `bookmarks "document has no bookmarks (yet)"
3364 , mkenter `history "history is empty" )
3367 let quickbookmark ?title () =
3368 match state.layout with
3369 | [] -> ()
3370 | l :: _ ->
3371 let title =
3372 match title with
3373 | None ->
3374 Unix.(
3375 let tm = localtime (now ()) in
3376 Printf.sprintf
3377 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3378 (l.pageno+1)
3379 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3381 | Some title -> title
3383 state.bookmarks <- (title, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3386 let setautoscrollspeed step goingdown =
3387 let incr = max 1 ((abs step) / 2) in
3388 let incr = if goingdown then incr else -incr in
3389 let astep = boundastep state.winh (step + incr) in
3390 state.autoscroll <- Some astep;
3393 let canpan () =
3394 match conf.columns with
3395 | Csplit _ -> true
3396 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3399 let panbound x = bound x (-state.w) state.winw;;
3401 let existsinrow pageno (columns, coverA, coverB) p =
3402 let last = ((pageno - coverA) mod columns) + columns in
3403 let rec any = function
3404 | [] -> false
3405 | l :: rest ->
3406 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3407 then p l
3408 else (
3409 if not (p l)
3410 then (if l.pageno = last then false else any rest)
3411 else true
3414 any state.layout
3417 let nextpage () =
3418 match state.layout with
3419 | [] ->
3420 let pageno = page_of_y state.y in
3421 gotoxy state.x (getpagey (pageno+1))
3422 | l :: rest ->
3423 match conf.columns with
3424 | Csingle _ ->
3425 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3426 then
3427 let y = clamp (pgscale state.winh) in
3428 gotoxy state.x y
3429 else
3430 let pageno = min (l.pageno+1) (state.pagecount-1) in
3431 gotoxy state.x (getpagey pageno)
3432 | Cmulti ((c, _, _) as cl, _) ->
3433 if conf.presentation
3434 && (existsinrow l.pageno cl
3435 (fun l -> l.pageh > l.pagey + l.pagevh))
3436 then
3437 let y = clamp (pgscale state.winh) in
3438 gotoxy state.x y
3439 else
3440 let pageno = min (l.pageno+c) (state.pagecount-1) in
3441 gotoxy state.x (getpagey pageno)
3442 | Csplit (n, _) ->
3443 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3444 then
3445 let pagey, pageh = getpageyh l.pageno in
3446 let pagey = pagey + pageh * l.pagecol in
3447 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3448 gotoxy state.x (pagey + pageh + ips)
3451 let prevpage () =
3452 match state.layout with
3453 | [] ->
3454 let pageno = page_of_y state.y in
3455 gotoxy state.x (getpagey (pageno-1))
3456 | l :: _ ->
3457 match conf.columns with
3458 | Csingle _ ->
3459 if conf.presentation && l.pagey != 0
3460 then
3461 gotoxy state.x (clamp (pgscale ~-(state.winh)))
3462 else
3463 let pageno = max 0 (l.pageno-1) in
3464 gotoxy state.x (getpagey pageno)
3465 | Cmulti ((c, _, coverB) as cl, _) ->
3466 if conf.presentation &&
3467 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3468 then
3469 gotoxy state.x (clamp (pgscale ~-(state.winh)))
3470 else
3471 let decr =
3472 if l.pageno = state.pagecount - coverB
3473 then 1
3474 else c
3476 let pageno = max 0 (l.pageno-decr) in
3477 gotoxy state.x (getpagey pageno)
3478 | Csplit (n, _) ->
3479 let y =
3480 if l.pagecol = 0
3481 then
3482 if l.pageno = 0
3483 then l.pagey
3484 else
3485 let pageno = max 0 (l.pageno-1) in
3486 let pagey, pageh = getpageyh pageno in
3487 pagey + (n-1)*pageh
3488 else
3489 let pagey, pageh = getpageyh l.pageno in
3490 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3492 gotoxy state.x y
3495 let save () =
3496 if emptystr conf.savecmd
3497 then adderrmsg "savepath-command is empty"
3498 "don't know where to save modified document"
3499 else
3500 let savecmd = Str.global_replace percentsre state.path conf.savecmd in
3501 let path =
3502 getcmdoutput
3503 (fun exn ->
3504 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3505 savecmd
3507 if nonemptystr path
3508 then
3509 let tmp = path ^ ".tmp" in
3510 savedoc tmp;
3511 Unix.rename tmp path;
3514 let viewkeyboard key mask =
3515 let enttext te =
3516 let mode = state.mode in
3517 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3518 state.text <- E.s;
3519 enttext ();
3520 postRedisplay "view:enttext"
3522 let ctrl = Wsi.withctrl mask in
3523 let open Keys in
3524 match Wsi.kc2kt key with
3525 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3527 | Ascii 'Q' -> exit 0
3529 | Ascii 'W' ->
3530 if hasunsavedchanges ()
3531 then save ()
3533 | Insert ->
3534 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3535 then (
3536 state.mode <- (
3537 match state.lnava with
3538 | None -> LinkNav (Ltgendir 0)
3539 | Some pn -> LinkNav (Ltexact pn)
3541 gotoxy state.x state.y;
3543 else impmsg "keyboard link navigation does not work under rotation"
3545 | Escape | Ascii 'q' ->
3546 begin match state.mstate with
3547 | Mzoomrect _ ->
3548 resetmstate ();
3549 postRedisplay "kill rect";
3550 | Msel _
3551 | Mpan _
3552 | Mscrolly | Mscrollx
3553 | Mzoom _
3554 | Mnone ->
3555 begin match state.mode with
3556 | LinkNav ln ->
3557 begin match ln with
3558 | Ltexact pl -> state.lnava <- Some pl
3559 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3560 end;
3561 state.mode <- View;
3562 postRedisplay "esc leave linknav"
3563 | Birdseye _ | Textentry _ | View ->
3564 match state.ranchors with
3565 | [] -> raise Quit
3566 | (path, password, anchor, origin) :: rest ->
3567 state.ranchors <- rest;
3568 state.anchor <- anchor;
3569 state.origin <- origin;
3570 state.nameddest <- E.s;
3571 opendoc path password
3572 end;
3573 end;
3575 | Backspace ->
3576 addnavnorc ();
3577 gotoxy state.x (getnav ~-1)
3579 | Ascii 'o' ->
3580 enteroutlinemode ()
3582 | Ascii 'H' ->
3583 enterhistmode ()
3585 | Ascii 'u' ->
3586 state.rects <- [];
3587 state.text <- E.s;
3588 Hashtbl.iter (fun _ opaque ->
3589 clearmark opaque;
3590 Hashtbl.clear state.prects) state.pagemap;
3591 postRedisplay "dehighlight";
3593 | Ascii (('/' | '?') as c) ->
3594 let ondone isforw s =
3595 cbput state.hists.pat s;
3596 state.searchpattern <- s;
3597 search s isforw
3599 let s = String.make 1 c in
3600 enttext (s, E.s, Some (onhist state.hists.pat),
3601 textentry, ondone (c = '/'), true)
3603 | Ascii '+' | Ascii '=' when ctrl ->
3604 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3605 pivotzoom (conf.zoom +. incr)
3607 | Ascii '+' ->
3608 let ondone s =
3609 let n =
3610 try int_of_string s with exn ->
3611 state.text <-
3612 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3613 max_int
3615 if n != max_int
3616 then (
3617 conf.pagebias <- n;
3618 state.text <- "page bias is now " ^ string_of_int n;
3621 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3623 | Ascii '-' when ctrl ->
3624 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3625 pivotzoom (max 0.01 (conf.zoom -. decr))
3627 | Ascii '-' ->
3628 let ondone msg = state.text <- msg in
3629 enttext ("option: ", E.s, None,
3630 optentry state.mode, ondone, true)
3632 | Ascii '0' when ctrl ->
3633 if conf.zoom = 1.0
3634 then gotoxy 0 state.y
3635 else setzoom 1.0
3637 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3638 let cols =
3639 match conf.columns with
3640 | Csingle _ | Cmulti _ -> 1
3641 | Csplit (n, _) -> n
3643 let h = state.winh -
3644 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3646 let zoom = zoomforh state.winw h 0 cols in
3647 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3648 then setzoom zoom
3650 | Ascii '3' when ctrl ->
3651 let fm =
3652 match conf.fitmodel with
3653 | FitWidth -> FitProportional
3654 | FitProportional -> FitPage
3655 | FitPage -> FitWidth
3657 state.text <- "fit model: " ^ FMTE.to_string fm;
3658 reqlayout conf.angle fm
3660 | Ascii '4' when ctrl ->
3661 let zoom = getmaxw () /. float state.winw in
3662 if zoom > 0.0 then setzoom zoom
3664 | Fn 9 ->
3665 togglebirdseye ()
3667 | Ascii '9' when ctrl ->
3668 togglebirdseye ()
3670 | Ascii ('0'..'9' as c) when not ctrl ->
3671 let ondone s =
3672 let n =
3673 try int_of_string s with exn ->
3674 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3677 if n >= 0
3678 then (
3679 addnav ();
3680 cbput state.hists.pag (string_of_int n);
3681 gotopage1 (n + conf.pagebias - 1) 0;
3684 let pageentry text = function [@warning "-4"]
3685 | Keys.Ascii 'g' -> TEdone text
3686 | key -> intentry text key
3688 let text = String.make 1 c in
3689 enttext (":", text, Some (onhist state.hists.pag),
3690 pageentry, ondone, true)
3692 | Ascii 'b' ->
3693 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3694 postRedisplay "toggle scrollbar";
3696 | Ascii 'B' ->
3697 state.bzoom <- not state.bzoom;
3698 state.rects <- [];
3699 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3701 | Ascii 'l' ->
3702 conf.hlinks <- not conf.hlinks;
3703 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3704 postRedisplay "toggle highlightlinks";
3706 | Ascii 'F' ->
3707 if conf.angle mod 360 = 0
3708 then (
3709 state.glinks <- true;
3710 let mode = state.mode in
3711 state.mode <-
3712 Textentry (
3713 (":", E.s, None, linknentry, linknact gotounder, false),
3714 (fun _ ->
3715 state.glinks <- false;
3716 state.mode <- mode)
3718 state.text <- E.s;
3719 postRedisplay "view:linkent(F)"
3721 else impmsg "hint mode does not work under rotation"
3723 | Ascii 'y' ->
3724 state.glinks <- true;
3725 let mode = state.mode in
3726 state.mode <-
3727 Textentry (
3728 (":", E.s, None, linknentry,
3729 linknact (fun under ->
3730 selstring conf.selcmd (undertext under)), false),
3731 (fun _ ->
3732 state.glinks <- false;
3733 state.mode <- mode)
3735 state.text <- E.s;
3736 postRedisplay "view:linkent"
3738 | Ascii 'a' ->
3739 begin match state.autoscroll with
3740 | Some step ->
3741 conf.autoscrollstep <- step;
3742 state.autoscroll <- None
3743 | None ->
3744 state.autoscroll <- Some conf.autoscrollstep;
3745 state.slideshow <- state.slideshow land lnot 2
3748 | Ascii 'p' when ctrl ->
3749 launchpath () (* XXX where do error messages go? *)
3751 | Ascii 'P' ->
3752 setpresentationmode (not conf.presentation);
3753 showtext ' ' ("presentation mode " ^
3754 if conf.presentation then "on" else "off");
3756 | Ascii 'f' ->
3757 if List.mem Wsi.Fullscreen state.winstate
3758 then Wsi.reshape conf.cwinw conf.cwinh
3759 else Wsi.fullscreen ()
3761 | Ascii ('p'|'N') ->
3762 search state.searchpattern false
3764 | Ascii 'n' | Fn 3 ->
3765 search state.searchpattern true
3767 | Ascii 't' ->
3768 begin match state.layout with
3769 | [] -> ()
3770 | l :: _ ->
3771 gotoxy state.x (getpagey l.pageno)
3774 | Ascii ' ' ->
3775 nextpage ()
3777 | Delete ->
3778 prevpage ()
3780 | Ascii '=' ->
3781 showtext ' ' (describe_layout state.layout);
3783 | Ascii 'w' ->
3784 begin match state.layout with
3785 | [] -> ()
3786 | l :: _ ->
3787 Wsi.reshape l.pagew l.pageh;
3788 postRedisplay "w"
3791 | Ascii '\'' ->
3792 enterbookmarkmode ()
3794 | Ascii 'h' | Fn 1 ->
3795 enterhelpmode ()
3797 | Ascii 'i' ->
3798 enterinfomode ()
3800 | Ascii 'e' when Buffer.length state.errmsgs > 0 ->
3801 entermsgsmode ()
3803 | Ascii 'm' ->
3804 let ondone s =
3805 match state.layout with
3806 | l :: _ ->
3807 if nonemptystr s
3808 then
3809 state.bookmarks <-
3810 (s, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3811 | _ -> ()
3813 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3815 | Ascii '~' ->
3816 quickbookmark ();
3817 showtext ' ' "Quick bookmark added";
3819 | Ascii 'x' -> state.roam ()
3821 | Ascii ('<'|'>' as c) ->
3822 reqlayout
3823 (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3825 | Ascii ('['|']' as c) ->
3826 conf.colorscale <-
3827 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3828 postRedisplay "brightness";
3830 | Ascii 'c' when state.mode = View ->
3831 if Wsi.withalt mask
3832 then (
3833 if conf.zoom > 1.0
3834 then
3835 let m = (state.winw - state.w) / 2 in
3836 gotoxy m state.y
3838 else
3839 let (c, a, b), z =
3840 match state.prevcolumns with
3841 | None -> (1, 0, 0), 1.0
3842 | Some (columns, z) ->
3843 let cab =
3844 match columns with
3845 | Csplit (c, _) -> -c, 0, 0
3846 | Cmulti ((c, a, b), _) -> c, a, b
3847 | Csingle _ -> 1, 0, 0
3849 cab, z
3851 setcolumns View c a b;
3852 setzoom z
3854 | Down | Up when ctrl && Wsi.withshift mask ->
3855 let zoom, x = state.prevzoom in
3856 setzoom zoom;
3857 state.x <- x;
3859 | Ascii 'k' | Up ->
3860 begin match state.autoscroll with
3861 | None ->
3862 begin match state.mode with
3863 | Birdseye beye -> upbirdseye 1 beye
3864 | Textentry _ | View | LinkNav _ ->
3865 if ctrl
3866 then gotoxy state.x (clamp ~-(state.winh/2))
3867 else (
3868 if not (Wsi.withshift mask) && conf.presentation
3869 then prevpage ()
3870 else gotoxy state.x (clamp (-conf.scrollstep))
3873 | Some n ->
3874 setautoscrollspeed n false
3877 | Ascii 'j' | Down ->
3878 begin match state.autoscroll with
3879 | None ->
3880 begin match state.mode with
3881 | Birdseye beye -> downbirdseye 1 beye
3882 | Textentry _ | View | LinkNav _ ->
3883 if ctrl
3884 then gotoxy state.x (clamp (state.winh/2))
3885 else (
3886 if not (Wsi.withshift mask) && conf.presentation
3887 then nextpage ()
3888 else gotoxy state.x (clamp (conf.scrollstep))
3891 | Some n ->
3892 setautoscrollspeed n true
3895 | Left | Right when not (Wsi.withalt mask) ->
3896 if canpan ()
3897 then
3898 let dx =
3899 if ctrl
3900 then state.winw / 2
3901 else conf.hscrollstep
3903 let dx =
3904 let pv = Wsi.kc2kt key in
3905 if pv = Keys.Left then dx else -dx
3907 gotoxy (panbound (state.x + dx)) state.y
3908 else (
3909 state.text <- E.s;
3910 postRedisplay "left/right"
3913 | Prior ->
3914 let y =
3915 if ctrl
3916 then
3917 match state.layout with
3918 | [] -> state.y
3919 | l :: _ -> state.y - l.pagey
3920 else
3921 clamp (pgscale (-state.winh))
3923 gotoxy state.x y
3925 | Next ->
3926 let y =
3927 if ctrl
3928 then
3929 match List.rev state.layout with
3930 | [] -> state.y
3931 | l :: _ -> getpagey l.pageno
3932 else
3933 clamp (pgscale state.winh)
3935 gotoxy state.x y
3937 | Ascii 'g' | Home ->
3938 addnav ();
3939 gotoxy 0 0
3940 | Ascii 'G' | End ->
3941 addnav ();
3942 gotoxy 0 (clamp state.maxy)
3944 | Right when Wsi.withalt mask ->
3945 addnavnorc ();
3946 gotoxy state.x (getnav 1)
3947 | Left when Wsi.withalt mask ->
3948 addnavnorc ();
3949 gotoxy state.x (getnav ~-1)
3951 | Ascii 'r' ->
3952 reload ()
3954 | Ascii 'v' when conf.debug ->
3955 state.rects <- [];
3956 List.iter (fun l ->
3957 match getopaque l.pageno with
3958 | None -> ()
3959 | Some opaque ->
3960 let x0, y0, x1, y1 = pagebbox opaque in
3961 let rect = (float x0, float y0,
3962 float x1, float y0,
3963 float x1, float y1,
3964 float x0, float y1) in
3965 debugrect rect;
3966 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3967 state.rects <- (l.pageno, color, rect) :: state.rects;
3968 ) state.layout;
3969 postRedisplay "v";
3971 | Ascii '|' ->
3972 let mode = state.mode in
3973 let cmd = ref E.s in
3974 let onleave = function
3975 | Cancel -> state.mode <- mode
3976 | Confirm ->
3977 List.iter (fun l ->
3978 match getopaque l.pageno with
3979 | Some opaque -> pipesel opaque !cmd
3980 | None -> ()) state.layout;
3981 state.mode <- mode
3983 let ondone s =
3984 cbput state.hists.sel s;
3985 cmd := s
3987 let te =
3988 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3990 postRedisplay "|";
3991 state.mode <- Textentry (te, onleave);
3993 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3994 vlog "huh? %s" (Wsi.keyname key)
3997 let linknavkeyboard key mask linknav =
3998 let pv = Wsi.kc2kt key in
3999 let getpage pageno =
4000 let rec loop = function
4001 | [] -> None
4002 | l :: _ when l.pageno = pageno -> Some l
4003 | _ :: rest -> loop rest
4004 in loop state.layout
4006 let doexact (pageno, n) =
4007 match getopaque pageno, getpage pageno with
4008 | Some opaque, Some l ->
4009 if pv = Keys.Enter
4010 then
4011 let under = getlink opaque n in
4012 postRedisplay "link gotounder";
4013 gotounder under;
4014 state.mode <- View;
4015 else
4016 let opt, dir =
4017 let open Keys in
4018 match pv with
4019 | Home -> Some (findlink opaque LDfirst), -1
4020 | End -> Some (findlink opaque LDlast), 1
4021 | Left -> Some (findlink opaque (LDleft n)), -1
4022 | Right -> Some (findlink opaque (LDright n)), 1
4023 | Up -> Some (findlink opaque (LDup n)), -1
4024 | Down -> Some (findlink opaque (LDdown n)), 1
4026 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
4027 | Code _|Fn _|Ctrl _|Backspace -> None, 0
4029 let pwl l dir =
4030 begin match findpwl l.pageno dir with
4031 | Pwlnotfound -> ()
4032 | Pwl pageno ->
4033 let notfound dir =
4034 state.mode <- LinkNav (Ltgendir dir);
4035 let y, h = getpageyh pageno in
4036 let y =
4037 if dir < 0
4038 then y + h - state.winh
4039 else y
4041 gotoxy state.x y
4043 begin match getopaque pageno, getpage pageno with
4044 | Some opaque, Some _ ->
4045 let link =
4046 let ld = if dir > 0 then LDfirst else LDlast in
4047 findlink opaque ld
4049 begin match link with
4050 | Lfound m ->
4051 showlinktype (getlink opaque m);
4052 state.mode <- LinkNav (Ltexact (pageno, m));
4053 postRedisplay "linknav jpage";
4054 | Lnotfound -> notfound dir
4055 end;
4056 | _ -> notfound dir
4057 end;
4058 end;
4060 begin match opt with
4061 | Some Lnotfound -> pwl l dir;
4062 | Some (Lfound m) ->
4063 if m = n
4064 then pwl l dir
4065 else (
4066 let _, y0, _, y1 = getlinkrect opaque m in
4067 if y0 < l.pagey
4068 then gotopage1 l.pageno y0
4069 else (
4070 let d = fstate.fontsize + 1 in
4071 if y1 - l.pagey > l.pagevh - d
4072 then gotopage1 l.pageno (y1 - state.winh + d)
4073 else postRedisplay "linknav";
4075 showlinktype (getlink opaque m);
4076 state.mode <- LinkNav (Ltexact (l.pageno, m));
4079 | None -> viewkeyboard key mask
4080 end;
4081 | _ -> viewkeyboard key mask
4083 if pv = Keys.Insert
4084 then (
4085 begin match linknav with
4086 | Ltexact pa -> state.lnava <- Some pa
4087 | Ltgendir _ | Ltnotready _ -> ()
4088 end;
4089 state.mode <- View;
4090 postRedisplay "leave linknav"
4092 else
4093 match linknav with
4094 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
4095 | Ltexact exact -> doexact exact
4098 let keyboard key mask =
4099 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
4100 then wcmd "interrupt"
4101 else state.uioh <- state.uioh#key key mask
4104 let birdseyekeyboard key mask
4105 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
4106 let incr =
4107 match conf.columns with
4108 | Csingle _ -> 1
4109 | Cmulti ((c, _, _), _) -> c
4110 | Csplit _ -> failwith "bird's eye split mode"
4112 let pgh layout = List.fold_left
4113 (fun m l -> max l.pageh m) state.winh layout in
4114 let open Keys in
4115 match Wsi.kc2kt key with
4116 | Ascii 'l' when Wsi.withctrl mask ->
4117 let y, h = getpageyh pageno in
4118 let top = (state.winh - h) / 2 in
4119 gotoxy state.x (max 0 (y - top))
4120 | Enter -> leavebirdseye beye false
4121 | Escape -> leavebirdseye beye true
4122 | Up -> upbirdseye incr beye
4123 | Down -> downbirdseye incr beye
4124 | Left -> upbirdseye 1 beye
4125 | Right -> downbirdseye 1 beye
4127 | Prior ->
4128 begin match state.layout with
4129 | l :: _ ->
4130 if l.pagey != 0
4131 then (
4132 state.mode <- Birdseye (
4133 oconf, leftx, l.pageno, hooverpageno, anchor
4135 gotopage1 l.pageno 0;
4137 else (
4138 let layout = layout state.x (state.y-state.winh)
4139 state.winw
4140 (pgh state.layout) in
4141 match layout with
4142 | [] -> gotoxy state.x (clamp (-state.winh))
4143 | l :: _ ->
4144 state.mode <- Birdseye (
4145 oconf, leftx, l.pageno, hooverpageno, anchor
4147 gotopage1 l.pageno 0
4150 | [] -> gotoxy state.x (clamp (-state.winh))
4151 end;
4153 | Next ->
4154 begin match List.rev state.layout with
4155 | l :: _ ->
4156 let layout = layout state.x
4157 (state.y + (pgh state.layout))
4158 state.winw state.winh in
4159 begin match layout with
4160 | [] ->
4161 let incr = l.pageh - l.pagevh in
4162 if incr = 0
4163 then (
4164 state.mode <-
4165 Birdseye (
4166 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4168 postRedisplay "birdseye pagedown";
4170 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4172 | l :: _ ->
4173 state.mode <-
4174 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4175 gotopage1 l.pageno 0;
4178 | [] -> gotoxy state.x (clamp state.winh)
4179 end;
4181 | Home ->
4182 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4183 gotopage1 0 0
4185 | End ->
4186 let pageno = state.pagecount - 1 in
4187 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4188 if not (pagevisible state.layout pageno)
4189 then
4190 let h =
4191 match List.rev state.pdims with
4192 | [] -> state.winh
4193 | (_, _, h, _) :: _ -> h
4195 gotoxy
4196 state.x
4197 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4198 else postRedisplay "birdseye end";
4200 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4203 let drawpage l =
4204 let color =
4205 match state.mode with
4206 | Textentry _ -> scalecolor 0.4
4207 | LinkNav _ | View -> scalecolor 1.0
4208 | Birdseye (_, _, pageno, hooverpageno, _) ->
4209 if l.pageno = hooverpageno
4210 then scalecolor 0.9
4211 else (
4212 if l.pageno = pageno
4213 then (
4214 let c = scalecolor 1.0 in
4215 GlDraw.color c;
4216 GlDraw.line_width 3.0;
4217 let dispx = l.pagedispx in
4218 linerect
4219 (float (dispx-1)) (float (l.pagedispy-1))
4220 (float (dispx+l.pagevw+1))
4221 (float (l.pagedispy+l.pagevh+1))
4223 GlDraw.line_width 1.0;
4226 else scalecolor 0.8
4229 drawtiles l color;
4232 let postdrawpage l linkindexbase =
4233 match getopaque l.pageno with
4234 | Some opaque ->
4235 if tileready l l.pagex l.pagey
4236 then
4237 let x = l.pagedispx - l.pagex
4238 and y = l.pagedispy - l.pagey in
4239 let hlmask =
4240 match conf.columns with
4241 | Csingle _ | Cmulti _ ->
4242 (if conf.hlinks then 1 else 0)
4243 + (if state.glinks
4244 && not (isbirdseye state.mode) then 2 else 0)
4245 | Csplit _ -> 0
4247 let s =
4248 match state.mode with
4249 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4250 | Textentry _
4251 | Birdseye _
4252 | View
4253 | LinkNav _ -> E.s
4255 Hashtbl.find_all state.prects l.pageno |>
4256 List.iter (fun vals -> drawprect opaque x y vals);
4257 let n = postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4258 if n < 0
4259 then (Glutils.redisplay := true; 0)
4260 else n
4261 else 0
4262 | _ -> 0
4265 let scrollindicator () =
4266 let sbw, ph, sh = state.uioh#scrollph in
4267 let sbh, pw, sw = state.uioh#scrollpw in
4269 let x0,x1,hx0 =
4270 if conf.leftscroll
4271 then (0, sbw, sbw)
4272 else ((state.winw - sbw), state.winw, 0)
4275 Gl.enable `blend;
4276 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4277 let (r, g, b, alpha) = conf.sbarcolor in
4278 GlDraw.color (r, g, b) ~alpha;
4279 filledrect (float x0) 0. (float x1) (float state.winh);
4280 filledrect
4281 (float hx0) (float (state.winh - sbh))
4282 (float (hx0 + state.winw)) (float state.winh);
4283 let (r, g, b, alpha) = conf.sbarhndlcolor in
4284 GlDraw.color (r, g, b) ~alpha;
4286 filledrect (float x0) ph (float x1) (ph +. sh);
4287 let pw = pw +. float hx0 in
4288 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4289 Gl.disable `blend;
4292 let showsel () =
4293 match state.mstate with
4294 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4297 | Msel ((x0, y0), (x1, y1)) ->
4298 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4299 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4300 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4301 if n0 != -1 && n0 = n1 then seltext o0 (px0, py0, px1, py1);
4304 let showrects =
4305 function [] -> ()
4306 | rects ->
4307 Gl.enable `blend;
4308 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4309 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4310 List.iter
4311 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4312 List.iter (fun l ->
4313 if l.pageno = pageno
4314 then (
4315 let dx = float (l.pagedispx - l.pagex) in
4316 let dy = float (l.pagedispy - l.pagey) in
4317 let r, g, b, alpha = c in
4318 GlDraw.color (r, g, b) ~alpha;
4319 filledrect2 (x0+.dx) (y0+.dy)
4320 (x1+.dx) (y1+.dy)
4321 (x3+.dx) (y3+.dy)
4322 (x2+.dx) (y2+.dy);
4324 ) state.layout
4325 ) rects;
4326 Gl.disable `blend;
4329 let display () =
4330 GlDraw.color (scalecolor2 conf.bgcolor);
4331 GlClear.color (scalecolor2 conf.bgcolor);
4332 GlClear.clear [`color];
4333 List.iter drawpage state.layout;
4334 let rects =
4335 match state.mode with
4336 | LinkNav (Ltexact (pageno, linkno)) ->
4337 begin match getopaque pageno with
4338 | Some opaque ->
4339 let x0, y0, x1, y1 = getlinkrect opaque linkno in
4340 let color = (0.0, 0.0, 0.5, 0.5) in
4341 (pageno, color,
4342 (float x0, float y0,
4343 float x1, float y0,
4344 float x1, float y1,
4345 float x0, float y1)
4346 ) :: state.rects
4347 | None -> state.rects
4349 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4350 | Birdseye _
4351 | Textentry _
4352 | View -> state.rects
4354 showrects rects;
4355 let rec postloop linkindexbase = function
4356 | l :: rest ->
4357 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4358 postloop linkindexbase rest
4359 | [] -> ()
4361 showsel ();
4362 postloop 0 state.layout;
4363 state.uioh#display;
4364 begin match state.mstate with
4365 | Mzoomrect ((x0, y0), (x1, y1)) ->
4366 Gl.enable `blend;
4367 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4368 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4369 filledrect (float x0) (float y0) (float x1) (float y1);
4370 Gl.disable `blend;
4371 | Msel _
4372 | Mpan _
4373 | Mscrolly | Mscrollx
4374 | Mzoom _
4375 | Mnone -> ()
4376 end;
4377 enttext ();
4378 scrollindicator ();
4379 Wsi.swapb ();
4382 let zoomrect x y x1 y1 =
4383 let x0 = min x x1
4384 and x1 = max x x1
4385 and y0 = min y y1 in
4386 let zoom = (float state.w) /. float (x1 - x0) in
4387 let margin =
4388 let simple () =
4389 if state.w < state.winw
4390 then (state.winw - state.w) / 2
4391 else 0
4393 match conf.fitmodel with
4394 | FitWidth | FitProportional -> simple ()
4395 | FitPage ->
4396 match conf.columns with
4397 | Csplit _ ->
4398 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4399 | Cmulti _ | Csingle _ -> simple ()
4401 gotoxy ((state.x + margin) - x0) (state.y + y0);
4402 state.anchor <- getanchor ();
4403 setzoom zoom;
4404 resetmstate ();
4407 let annot inline x y =
4408 match unproject x y with
4409 | Some (opaque, n, ux, uy) ->
4410 let add text =
4411 addannot opaque ux uy text;
4412 wcmd "freepage %s" (~> opaque);
4413 Hashtbl.remove state.pagemap (n, state.gen);
4414 flushtiles ();
4415 gotoxy state.x state.y
4417 if inline
4418 then
4419 let ondone s = add s in
4420 let mode = state.mode in
4421 state.mode <- Textentry (
4422 ("annotation: ", E.s, None, textentry, ondone, true),
4423 fun _ -> state.mode <- mode);
4424 state.text <- E.s;
4425 enttext ();
4426 postRedisplay "annot"
4427 else
4428 add @@ getusertext E.s
4429 | _ -> ()
4432 let zoomblock x y =
4433 let g opaque l px py =
4434 match rectofblock opaque px py with
4435 | Some a ->
4436 let x0 = a.(0) -. 20. in
4437 let x1 = a.(1) +. 20. in
4438 let y0 = a.(2) -. 20. in
4439 let zoom = (float state.w) /. (x1 -. x0) in
4440 let pagey = getpagey l.pageno in
4441 let margin = (state.w - l.pagew)/2 in
4442 let nx = -truncate x0 - margin in
4443 gotoxy nx (pagey + truncate y0);
4444 state.anchor <- getanchor ();
4445 setzoom zoom;
4446 None
4447 | None -> None
4449 match conf.columns with
4450 | Csplit _ ->
4451 impmsg "block zooming does not work properly in split columns mode"
4452 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4455 let scrollx x =
4456 let winw = state.winw - 1 in
4457 let s = float x /. float winw in
4458 let destx = truncate (float (state.w + winw) *. s) in
4459 gotoxy (winw - destx) state.y;
4460 state.mstate <- Mscrollx;
4463 let scrolly y =
4464 let s = float y /. float state.winh in
4465 let desty = truncate (s *. float (maxy ())) in
4466 gotoxy state.x desty;
4467 state.mstate <- Mscrolly;
4470 let viewmulticlick clicks x y mask =
4471 let g opaque l px py =
4472 let mark =
4473 match clicks with
4474 | 2 -> Mark_word
4475 | 3 -> Mark_line
4476 | 4 -> Mark_block
4477 | _ -> Mark_page
4479 if markunder opaque px py mark
4480 then (
4481 Some (fun () ->
4482 let dopipe cmd =
4483 match getopaque l.pageno with
4484 | None -> ()
4485 | Some opaque -> pipesel opaque cmd
4487 state.roam <- (fun () -> dopipe conf.paxcmd);
4488 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4491 else None
4493 postRedisplay "viewmulticlick";
4494 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4497 let canselect () =
4498 match conf.columns with
4499 | Csplit _ -> false
4500 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4503 let viewmouse button down x y mask =
4504 match button with
4505 | n when (n == 4 || n == 5) && not down ->
4506 if Wsi.withctrl mask
4507 then (
4508 let incr =
4509 if n = 5
4510 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4511 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4513 let fx, fy =
4514 match state.mstate with
4515 | Mzoom (oldn, _, pos) when n = oldn -> pos
4516 | Mzoomrect _ | Mnone | Mpan _
4517 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4519 let zoom = conf.zoom -. incr in
4520 state.mstate <- Mzoom (n, 0, (x, y));
4521 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4522 then pivotzoom ~x ~y zoom
4523 else pivotzoom zoom
4525 else (
4526 match state.autoscroll with
4527 | Some step -> setautoscrollspeed step (n=4)
4528 | None ->
4529 if conf.wheelbypage || conf.presentation
4530 then (
4531 if n = 4
4532 then prevpage ()
4533 else nextpage ()
4535 else
4536 let incr =
4537 if n = 4
4538 then -conf.scrollstep
4539 else conf.scrollstep
4541 let incr = incr * 2 in
4542 let y = clamp incr in
4543 gotoxy state.x y
4546 | n when (n = 6 || n = 7) && not down && canpan () ->
4547 let x =
4548 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4549 gotoxy x state.y
4551 | 1 when Wsi.withshift mask ->
4552 state.mstate <- Mnone;
4553 if not down
4554 then (
4555 match unproject x y with
4556 | None -> ()
4557 | Some (_, pageno, ux, uy) ->
4558 let cmd = Printf.sprintf
4559 "%s %s %d %d %d"
4560 conf.stcmd state.path pageno ux uy
4562 match spawn cmd [] with
4563 | exception exn ->
4564 impmsg "execution of synctex command(%S) failed: %S"
4565 conf.stcmd @@ exntos exn
4566 | _pid -> ()
4569 | 1 when Wsi.withctrl mask ->
4570 if down
4571 then (
4572 Wsi.setcursor Wsi.CURSOR_FLEUR;
4573 state.mstate <- Mpan (x, y)
4575 else
4576 state.mstate <- Mnone
4578 | 3 ->
4579 if down
4580 then (
4581 if Wsi.withshift mask
4582 then (
4583 annot conf.annotinline x y;
4584 postRedisplay "addannot"
4586 else
4587 let p = (x, y) in
4588 Wsi.setcursor Wsi.CURSOR_CYCLE;
4589 state.mstate <- Mzoomrect (p, p)
4591 else (
4592 match state.mstate with
4593 | Mzoomrect ((x0, y0), _) ->
4594 if abs (x-x0) > 10 && abs (y - y0) > 10
4595 then zoomrect x0 y0 x y
4596 else (
4597 resetmstate ();
4598 postRedisplay "kill accidental zoom rect";
4600 | Msel _
4601 | Mpan _
4602 | Mscrolly | Mscrollx
4603 | Mzoom _
4604 | Mnone -> resetmstate ()
4607 | 1 when vscrollhit x ->
4608 if down
4609 then
4610 let _, position, sh = state.uioh#scrollph in
4611 if y > truncate position && y < truncate (position +. sh)
4612 then state.mstate <- Mscrolly
4613 else scrolly y
4614 else
4615 state.mstate <- Mnone
4617 | 1 when y > state.winh - hscrollh () ->
4618 if down
4619 then
4620 let _, position, sw = state.uioh#scrollpw in
4621 if x > truncate position && x < truncate (position +. sw)
4622 then state.mstate <- Mscrollx
4623 else scrollx x
4624 else
4625 state.mstate <- Mnone
4627 | 1 when state.bzoom -> if not down then zoomblock x y
4629 | 1 ->
4630 let dest = if down then getunder x y else Unone in
4631 begin match dest with
4632 | Ulinkuri _ ->
4633 gotounder dest
4635 | Unone when down ->
4636 Wsi.setcursor Wsi.CURSOR_FLEUR;
4637 state.mstate <- Mpan (x, y);
4639 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4641 | Unone | Utext _ ->
4642 if down
4643 then (
4644 if canselect ()
4645 then (
4646 state.mstate <- Msel ((x, y), (x, y));
4647 postRedisplay "mouse select";
4650 else (
4651 match state.mstate with
4652 | Mnone -> ()
4654 | Mzoom _ | Mscrollx | Mscrolly ->
4655 state.mstate <- Mnone
4657 | Mzoomrect ((x0, y0), _) ->
4658 zoomrect x0 y0 x y
4660 | Mpan _ ->
4661 Wsi.setcursor Wsi.CURSOR_INHERIT;
4662 state.mstate <- Mnone
4664 | Msel ((x0, y0), (x1, y1)) ->
4665 let rec loop = function
4666 | [] -> ()
4667 | l :: rest ->
4668 let inside =
4669 let a0 = l.pagedispy in
4670 let a1 = a0 + l.pagevh in
4671 let b0 = l.pagedispx in
4672 let b1 = b0 + l.pagevw in
4673 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4674 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4676 if inside
4677 then
4678 match getopaque l.pageno with
4679 | Some opaque ->
4680 let dosel cmd () =
4681 pipef ~closew:false "Msel"
4682 (fun w ->
4683 copysel w opaque;
4684 postRedisplay "Msel") cmd
4686 dosel conf.selcmd ();
4687 state.roam <- dosel conf.paxcmd;
4688 | None -> ()
4689 else loop rest
4691 loop state.layout;
4692 resetmstate ();
4696 | _ -> ()
4699 let birdseyemouse button down x y mask
4700 (conf, leftx, _, hooverpageno, anchor) =
4701 match button with
4702 | 1 when down ->
4703 let rec loop = function
4704 | [] -> ()
4705 | l :: rest ->
4706 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4707 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4708 then (
4709 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4711 else loop rest
4713 loop state.layout
4714 | 3 -> ()
4715 | _ -> viewmouse button down x y mask
4718 let uioh = object
4719 method display = ()
4721 method key key mask =
4722 begin match state.mode with
4723 | Textentry textentry -> textentrykeyboard key mask textentry
4724 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4725 | View -> viewkeyboard key mask
4726 | LinkNav linknav -> linknavkeyboard key mask linknav
4727 end;
4728 state.uioh
4730 method button button bstate x y mask =
4731 begin match state.mode with
4732 | LinkNav _ | View -> viewmouse button bstate x y mask
4733 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4734 | Textentry _ -> ()
4735 end;
4736 state.uioh
4738 method multiclick clicks x y mask =
4739 begin match state.mode with
4740 | LinkNav _ | View -> viewmulticlick clicks x y mask
4741 | Birdseye _ | Textentry _ -> ()
4742 end;
4743 state.uioh
4745 method motion x y =
4746 begin match state.mode with
4747 | Textentry _ -> ()
4748 | View | Birdseye _ | LinkNav _ ->
4749 match state.mstate with
4750 | Mzoom _ | Mnone -> ()
4752 | Mpan (x0, y0) ->
4753 let dx = x - x0
4754 and dy = y0 - y in
4755 state.mstate <- Mpan (x, y);
4756 let x = if canpan () then panbound (state.x + dx) else state.x in
4757 let y = clamp dy in
4758 gotoxy x y
4760 | Msel (a, _) ->
4761 state.mstate <- Msel (a, (x, y));
4762 postRedisplay "motion select";
4764 | Mscrolly ->
4765 let y = min state.winh (max 0 y) in
4766 scrolly y
4768 | Mscrollx ->
4769 let x = min state.winw (max 0 x) in
4770 scrollx x
4772 | Mzoomrect (p0, _) ->
4773 state.mstate <- Mzoomrect (p0, (x, y));
4774 postRedisplay "motion zoomrect";
4775 end;
4776 state.uioh
4778 method pmotion x y =
4779 begin match state.mode with
4780 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4781 let rec loop = function
4782 | [] ->
4783 if hooverpageno != -1
4784 then (
4785 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4786 postRedisplay "pmotion birdseye no hoover";
4788 | l :: rest ->
4789 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4790 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4791 then (
4792 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4793 postRedisplay "pmotion birdseye hoover";
4795 else loop rest
4797 loop state.layout
4799 | Textentry _ -> ()
4801 | LinkNav _ | View ->
4802 match state.mstate with
4803 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4804 | Mnone ->
4805 updateunder x y;
4806 if canselect ()
4807 then
4808 match conf.pax with
4809 | None -> ()
4810 | Some past ->
4811 let now = now () in
4812 let delta = now -. past in
4813 if delta > 0.01
4814 then paxunder x y
4815 else conf.pax <- Some now
4816 end;
4817 state.uioh
4819 method infochanged _ = ()
4821 method scrollph =
4822 let maxy = maxy () in
4823 let p, h =
4824 if maxy = 0
4825 then 0.0, float state.winh
4826 else scrollph state.y maxy
4828 vscrollw (), p, h
4830 method scrollpw =
4831 let fwinw = float (state.winw - vscrollw ()) in
4832 let sw =
4833 let sw = fwinw /. float state.w in
4834 let sw = fwinw *. sw in
4835 max sw (float conf.scrollh)
4837 let position =
4838 let maxx = state.w + state.winw in
4839 let x = state.winw - state.x in
4840 let percent = float x /. float maxx in
4841 (fwinw -. sw) *. percent
4843 hscrollh (), position, sw
4845 method modehash =
4846 let modename =
4847 match state.mode with
4848 | LinkNav _ -> "links"
4849 | Textentry _ -> "textentry"
4850 | Birdseye _ -> "birdseye"
4851 | View -> "view"
4853 findkeyhash conf modename
4855 method eformsgs = true
4856 method alwaysscrolly = false
4857 method scroll dx dy =
4858 let x = if canpan () then panbound (state.x + dx) else state.x in
4859 gotoxy x (clamp (2 * dy));
4860 state.uioh
4861 method zoom z x y =
4862 pivotzoom ~x ~y (conf.zoom *. exp z);
4863 end;;
4865 let addrect pageno r g b a x0 y0 x1 y1 =
4866 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4869 let ract cmds =
4870 let cl = splitatchar cmds ' ' in
4871 let scan s fmt f =
4872 try Scanf.sscanf s fmt f
4873 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4874 cmds @@ exntos exn
4876 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4877 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4878 s pageno r g b a x0 y0 x1 y1;
4879 onpagerect
4880 pageno
4881 (fun w h ->
4882 let _,w1,h1,_ = getpagedim pageno in
4883 let sw = float w1 /. float w
4884 and sh = float h1 /. float h in
4885 let x0s = x0 *. sw
4886 and x1s = x1 *. sw
4887 and y0s = y0 *. sh
4888 and y1s = y1 *. sh in
4889 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4890 let color = (r, g, b, a) in
4891 if conf.verbose then debugrect rect;
4892 state.rects <- (pageno, color, rect) :: state.rects;
4893 postRedisplay s;
4896 match cl with
4897 | "reload", "" -> reload ()
4898 | "goto", args ->
4899 scan args "%u %f %f"
4900 (fun pageno x y ->
4901 let cmd, _ = state.geomcmds in
4902 if emptystr cmd
4903 then gotopagexy pageno x y
4904 else
4905 let f prevf () =
4906 gotopagexy pageno x y;
4907 prevf ()
4909 state.reprf <- f state.reprf
4911 | "goto1", args -> scan args "%u %f" gotopage
4912 | "gotor", args -> scan args "%S" gotoremote
4913 | "rect", args ->
4914 scan args "%u %u %f %f %f %f"
4915 (fun pageno c x0 y0 x1 y1 ->
4916 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4917 rectx "rect" pageno color x0 y0 x1 y1;
4919 | "prect", args ->
4920 scan args "%u %f %f %f %f %f %f %f %f"
4921 (fun pageno r g b alpha x0 y0 x1 y1 ->
4922 addrect pageno r g b alpha x0 y0 x1 y1;
4923 postRedisplay "prect"
4925 | "pgoto", args ->
4926 scan args "%u %f %f"
4927 (fun pageno x y ->
4928 let optopaque =
4929 match getopaque pageno with
4930 | Some opaque -> opaque
4931 | None -> ~< E.s
4933 pgoto optopaque pageno x y;
4934 let rec fixx = function
4935 | [] -> ()
4936 | l :: rest ->
4937 if l.pageno = pageno
4938 then gotoxy (state.x - l.pagedispx) state.y
4939 else fixx rest
4941 let layout =
4942 let mult =
4943 match conf.columns with
4944 | Csingle _ | Csplit _ -> 1
4945 | Cmulti ((n, _, _), _) -> n
4947 layout 0 state.y (state.winw * mult) state.winh
4949 fixx layout
4951 | "activatewin", "" -> Wsi.activatewin ()
4952 | "quit", "" -> raise Quit
4953 | "keys", keys ->
4954 begin try
4955 let l = Config.keys_of_string keys in
4956 List.iter (fun (k, m) -> keyboard k m) l
4957 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4958 cmds @@ exntos exn
4960 | "clearrects", "" ->
4961 Hashtbl.clear state.prects;
4962 postRedisplay "clearrects"
4963 | _ ->
4964 adderrfmt "remote command"
4965 "error processing remote command: %S\n" cmds;
4968 let remote =
4969 let scratch = Bytes.create 80 in
4970 let buf = Buffer.create 80 in
4971 fun fd ->
4972 match tempfailureretry (Unix.read fd scratch 0) 80 with
4973 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4974 | 0 ->
4975 Unix.close fd;
4976 if Buffer.length buf > 0
4977 then (
4978 let s = Buffer.contents buf in
4979 Buffer.clear buf;
4980 ract s;
4982 None
4983 | n ->
4984 let rec eat ppos =
4985 let nlpos =
4986 match Bytes.index_from scratch ppos '\n' with
4987 | pos -> if pos >= n then -1 else pos
4988 | exception Not_found -> -1
4990 if nlpos >= 0
4991 then (
4992 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4993 let s = Buffer.contents buf in
4994 Buffer.clear buf;
4995 ract s;
4996 eat (nlpos+1);
4998 else (
4999 Buffer.add_subbytes buf scratch ppos (n-ppos);
5000 Some fd
5002 in eat 0
5005 let remoteopen path =
5006 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
5007 with exn ->
5008 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
5009 None
5012 let () =
5013 let gcconfig = ref false in
5014 let trimcachepath = ref E.s in
5015 let rcmdpath = ref E.s in
5016 let pageno = ref None in
5017 let openlast = ref false in
5018 let doreap = ref false in
5019 let csspath = ref None in
5020 selfexec := Sys.executable_name;
5021 Arg.parse
5022 (Arg.align
5023 [("-p", Arg.String (fun s -> state.password <- s),
5024 "<password> Set password");
5026 ("-f", Arg.String
5027 (fun s ->
5028 Config.fontpath := s;
5029 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
5031 "<path> Set path to the user interface font");
5033 ("-c", Arg.String
5034 (fun s ->
5035 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
5036 Config.confpath := s),
5037 "<path> Set path to the configuration file");
5039 ("-last", Arg.Set openlast, " Open last document");
5041 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
5042 "<page-number> Jump to page");
5044 ("-tcf", Arg.String (fun s -> trimcachepath := s),
5045 "<path> Set path to the trim cache file");
5047 ("-dest", Arg.String (fun s -> state.nameddest <- s),
5048 "<named-destination> Set named destination");
5050 ("-remote", Arg.String (fun s -> rcmdpath := s),
5051 "<path> Set path to the source of remote commands");
5053 ("-gc", Arg.Set gcconfig, " Collect config garbage");
5055 ("-v", Arg.Unit (fun () ->
5056 Printf.printf
5057 "%s\nconfiguration file: %s\n"
5058 (Help.version ())
5059 Config.defconfpath;
5060 exit 0), " Print version and exit");
5062 ("-css", Arg.String (fun s -> csspath := Some s),
5063 "<path> Set path to the style sheet to use with EPUB/HTML");
5065 ("-origin", Arg.String (fun s -> state.origin <- s),
5066 "<origin> <undocumented>");
5068 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
5069 ("-layout-height", Arg.Set_int layouth,
5070 "<height> layout height html/epub/etc (-1, 0, N)");
5073 (fun s -> state.path <- s)
5074 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
5076 let histmode = emptystr state.path && not !openlast in
5078 if not (Config.load !openlast)
5079 then dolog "failed to load configuration";
5081 begin match !pageno with
5082 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
5083 | None -> ()
5084 end;
5086 fillhelp ();
5087 if !gcconfig
5088 then (
5089 Config.gc ();
5090 exit 0
5093 let mu =
5094 object (self)
5095 val mutable m_clicks = 0
5096 val mutable m_click_x = 0
5097 val mutable m_click_y = 0
5098 val mutable m_lastclicktime = infinity
5100 method private cleanup =
5101 state.roam <- noroam;
5102 Hashtbl.iter (fun _ opaque -> clearmark opaque) state.pagemap
5103 method expose = postRedisplay "expose"
5104 method visible v =
5105 let name =
5106 match v with
5107 | Wsi.Unobscured -> "unobscured"
5108 | Wsi.PartiallyObscured -> "partiallyobscured"
5109 | Wsi.FullyObscured -> "fullyobscured"
5111 vlog "visibility change %s" name
5112 method display = display ()
5113 method map mapped = vlog "mapped %b" mapped
5114 method reshape w h =
5115 self#cleanup;
5116 reshape w h
5117 method mouse b d x y m =
5118 if d && canselect ()
5119 then (
5121 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
5123 m_click_x <- x;
5124 m_click_y <- y;
5125 if b = 1
5126 then (
5127 let t = now () in
5128 if abs x - m_click_x > 10
5129 || abs y - m_click_y > 10
5130 || abs_float (t -. m_lastclicktime) > 0.3
5131 then m_clicks <- 0;
5132 m_clicks <- m_clicks + 1;
5133 m_lastclicktime <- t;
5134 if m_clicks = 1
5135 then (
5136 self#cleanup;
5137 postRedisplay "cleanup";
5138 state.uioh <- state.uioh#button b d x y m;
5140 else state.uioh <- state.uioh#multiclick m_clicks x y m
5142 else (
5143 self#cleanup;
5144 m_clicks <- 0;
5145 m_lastclicktime <- infinity;
5146 state.uioh <- state.uioh#button b d x y m
5149 else (
5150 state.uioh <- state.uioh#button b d x y m
5152 method motion x y =
5153 state.mpos <- (x, y);
5154 state.uioh <- state.uioh#motion x y
5155 method pmotion x y =
5156 state.mpos <- (x, y);
5157 state.uioh <- state.uioh#pmotion x y
5158 method key k m =
5159 vlog "k=%#x m=%#x" k m;
5160 let mascm = m land (
5161 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
5162 ) in
5163 let keyboard k m =
5164 let x = state.x and y = state.y in
5165 keyboard k m;
5166 if x != state.x || y != state.y then self#cleanup
5168 match state.keystate with
5169 | KSnone ->
5170 let km = k, mascm in
5171 begin
5172 match
5173 let modehash = state.uioh#modehash in
5174 try Hashtbl.find modehash km
5175 with Not_found ->
5176 try Hashtbl.find (findkeyhash conf "global") km
5177 with Not_found -> KMinsrt (k, m)
5178 with
5179 | KMinsrt (k, m) -> keyboard k m
5180 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5181 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5183 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5184 List.iter (fun (k, m) -> keyboard k m) insrt;
5185 state.keystate <- KSnone
5186 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5187 state.keystate <- KSinto (keys, insrt)
5188 | KSinto _ -> state.keystate <- KSnone
5190 method enter x y =
5191 state.mpos <- (x, y);
5192 state.uioh <- state.uioh#pmotion x y
5193 method leave = state.mpos <- (-1, -1)
5194 method winstate wsl = state.winstate <- wsl
5195 method quit : 'a. 'a = raise Quit
5196 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5197 method zoom z x y = state.uioh#zoom z x y
5198 method opendoc path =
5199 state.mode <- View;
5200 state.uioh <- uioh;
5201 postRedisplay "opendoc";
5202 opendoc path state.password
5205 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5206 state.wsfd <- wsfd;
5208 if not @@ List.exists GlMisc.check_extension
5209 [ "GL_ARB_texture_rectangle"
5210 ; "GL_EXT_texture_recangle"
5211 ; "GL_NV_texture_rectangle" ]
5212 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5214 if substratis (GlMisc.get_string `renderer) 0 "Mesa DRI Intel("
5215 then (
5216 defconf.sliceheight <- 1024;
5217 defconf.texcount <- 32;
5218 defconf.usepbo <- true;
5221 let cs, ss =
5222 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5223 | exception exn ->
5224 dolog "socketpair failed: %s" @@ exntos exn;
5225 exit 1
5226 | (r, w) ->
5227 cloexec r;
5228 cloexec w;
5229 r, w
5232 setcheckers conf.checkers;
5234 opengl_has_pbo := GlMisc.check_extension "GL_ARB_pixel_buffer_object";
5236 begin match !csspath with
5237 | None -> ()
5238 | Some "" -> conf.css <- E.s
5239 | Some path ->
5240 let css = filecontents path in
5241 let l = String.length css in
5242 conf.css <-
5243 if substratis css (l-2) "\r\n"
5244 then String.sub css 0 (l-2)
5245 else (if css.[l-1] = '\n'
5246 then String.sub css 0 (l-1)
5247 else css);
5248 end;
5249 init cs (
5250 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5251 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5252 !Config.fontpath, !trimcachepath, !opengl_has_pbo
5254 List.iter GlArray.enable [`texture_coord; `vertex];
5255 state.ss <- ss;
5256 reshape ~firsttime:true winw winh;
5257 state.uioh <- uioh;
5258 if histmode
5259 then (
5260 Wsi.settitle "llpp (history)";
5261 enterhistmode ();
5263 else (
5264 state.text <- "Opening " ^ (mbtoutf8 state.path);
5265 opendoc state.path state.password;
5267 display ();
5268 Wsi.mapwin ();
5269 Wsi.setcursor Wsi.CURSOR_INHERIT;
5270 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5272 let rec reap () =
5273 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5274 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5275 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5276 | 0, _ -> ()
5277 | _pid, _status -> reap ()
5279 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5281 let optrfd =
5282 ref (
5283 if nonemptystr !rcmdpath
5284 then remoteopen !rcmdpath
5285 else None
5289 let rec loop deadline =
5290 if !doreap
5291 then (
5292 doreap := false;
5293 reap ()
5295 let r = [state.ss; state.wsfd] in
5296 let r =
5297 match !optrfd with
5298 | None -> r
5299 | Some fd -> fd :: r
5301 if !redisplay
5302 then (
5303 Glutils.redisplay := false;
5304 display ();
5306 let timeout =
5307 let now = now () in
5308 if deadline > now
5309 then (
5310 if deadline = infinity
5311 then ~-.1.0
5312 else max 0.0 (deadline -. now)
5314 else 0.0
5316 let r, _, _ =
5317 try Unix.select r [] [] timeout
5318 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5320 begin match r with
5321 | [] ->
5322 let newdeadline =
5323 match state.autoscroll with
5324 | Some step when step != 0 ->
5325 if state.slideshow land 1 = 1
5326 then (
5327 if state.slideshow land 2 = 0
5328 then state.slideshow <- state.slideshow lor 2
5329 else if step < 0 then prevpage () else nextpage ();
5330 deadline +. (float (abs step))
5332 else
5333 let y = state.y + step in
5334 let fy = if conf.maxhfit then state.winh else 0 in
5335 let y =
5336 if y < 0
5337 then state.maxy - fy
5338 else if y >= state.maxy - fy then 0 else y
5340 gotoxy state.x y;
5341 deadline +. 0.01
5342 | _ -> infinity
5344 loop newdeadline
5346 | l ->
5347 let rec checkfds = function
5348 | [] -> ()
5349 | fd :: rest when fd = state.ss ->
5350 let cmd = rcmd state.ss in
5351 act cmd;
5352 checkfds rest
5354 | fd :: rest when fd = state.wsfd ->
5355 Wsi.readresp fd;
5356 checkfds rest
5358 | fd :: rest when Some fd = !optrfd ->
5359 begin match remote fd with
5360 | None -> optrfd := remoteopen !rcmdpath;
5361 | opt -> optrfd := opt
5362 end;
5363 checkfds rest
5365 | _ :: rest ->
5366 dolog "select returned unknown descriptor";
5367 checkfds rest
5369 checkfds l;
5370 let newdeadline =
5371 let deadline1 =
5372 if deadline = infinity
5373 then now () +. 0.01
5374 else deadline
5376 match state.autoscroll with
5377 | Some step when step != 0 -> deadline1
5378 | _ -> infinity
5380 loop newdeadline
5381 end;
5383 match loop infinity with
5384 | exception Quit ->
5385 Config.save leavebirdseye;
5386 if hasunsavedchanges ()
5387 then save ()
5388 | _ -> error "umpossible - infinity reached"