Consolidate
[llpp.git] / main.ml
blob7027dcae64a16e144fb231db545b1cdbbe2ccba8
1 open Utils;;
2 open Config;;
3 open Glutils;;
4 open Listview;;
6 external init : Unix.file_descr -> initparams -> unit = "ml_init";;
7 external seltext : opaque -> (int * int * int * int) -> unit = "ml_seltext";;
8 external hassel : opaque -> bool = "ml_hassel";;
9 external getpdimrect : int -> float array = "ml_getpdimrect";;
10 external whatsunder : opaque -> int -> int -> under = "ml_whatsunder";;
11 external markunder : opaque -> int -> int -> mark -> bool = "ml_markunder";;
12 external clearmark : opaque -> unit = "ml_clearmark";;
13 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
14 external getmaxw : unit -> float = "ml_getmaxw";;
15 external postprocess :
16 opaque -> int -> int -> int -> (int * string * int) -> int
17 = "ml_postprocess";;
18 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
19 external setaalevel : int -> unit = "ml_setaalevel";;
20 external realloctexts : int -> bool = "ml_realloctexts";;
21 external findlink : opaque -> linkdir -> link = "ml_findlink";;
22 external getlink : opaque -> int -> under = "ml_getlink";;
23 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
24 external getlinkcount : opaque -> int = "ml_getlinkcount";;
25 external findpwl : int -> int -> pagewithlinks = "ml_find_page_with_links";;
26 external getpbo : width -> height -> colorspace -> opaque = "ml_getpbo";;
27 external freepbo : opaque -> unit = "ml_freepbo";;
28 external unmappbo : opaque -> unit = "ml_unmappbo";;
29 external bousable : unit -> bool = "ml_bo_usable";;
30 external unproject :
31 opaque -> int -> int -> (int * int) option = "ml_unproject";;
32 external project :
33 opaque -> int -> int -> float -> float -> (float * float) = "ml_project";;
34 external drawtile :
35 tileparams -> opaque -> unit = "ml_drawtile";;
36 external rectofblock :
37 opaque -> int -> int -> float array option = "ml_rectofblock";;
38 external begintiles : unit -> unit = "ml_begintiles";;
39 external endtiles : unit -> unit = "ml_endtiles";;
40 external addannot : opaque -> int -> int -> string -> unit = "ml_addannot";;
41 external modannot : opaque -> slinkindex -> string -> unit = "ml_modannot";;
42 external delannot : opaque -> slinkindex -> unit = "ml_delannot";;
43 external hasunsavedchanges : unit -> bool = "ml_hasunsavedchanges";;
44 external savedoc : string -> unit = "ml_savedoc";;
45 external getannotcontents :
46 opaque -> slinkindex -> string = "ml_getannotcontents";;
47 external drawprect :
48 opaque -> int -> int -> float array -> unit = "ml_drawprect";;
49 external wcmd : Unix.file_descr -> bytes -> int -> unit = "ml_wcmd";;
50 external rcmd : Unix.file_descr -> string = "ml_rcmd";;
51 external uritolocation :
52 string -> (pageno * float * float) = "ml_uritolocation";;
53 external isexternallink : string -> bool = "ml_isexternallink";;
55 (* copysel _will_ close the supplied descriptor *)
56 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
58 let selfexec = ref E.s;;
59 let ignoredoctitlte = ref false;;
60 let opengl_has_pbo = ref false;;
61 let layouth = ref ~-1;;
63 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
64 dolog {|rect {
65 x0,y0=(% f, % f)
66 x1,y1=(% f, % f)
67 x2,y2=(% f, % f)
68 x3,y3=(% f, % f)
69 }|} x0 y0 x1 y1 x2 y2 x3 y3;
72 let pgscale h = truncate (float h *. conf.pgscale);;
74 let hscrollh () =
75 if state.uioh#alwaysscrolly || ((conf.scrollb land scrollbhv != 0)
76 && (state.w > state.winw))
77 then conf.scrollbw
78 else 0
81 let setfontsize n =
82 fstate.fontsize <- n;
83 fstate.wwidth <- measurestr fstate.fontsize "w";
84 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
87 let vlog fmt =
88 if conf.verbose
89 then dolog fmt
90 else Printf.kprintf ignore fmt
93 let launchpath () =
94 if emptystr conf.pathlauncher
95 then dolog "%s" state.path
96 else (
97 let command =
98 Str.global_replace Utils.Re.percent state.path conf.pathlauncher in
99 match spawn command [] with
100 | _pid -> ()
101 | exception exn ->
102 dolog "failed to execute `%s': %s" command @@ exntos exn
106 let getopaque pageno =
107 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
108 with Not_found -> None
111 let pagetranslatepoint l x y =
112 let dy = y - l.pagedispy in
113 let y = dy + l.pagey in
114 let dx = x - l.pagedispx in
115 let x = dx + l.pagex in
116 (x, y);
119 let onppundermouse g x y d =
120 let rec f = function
121 | l :: rest ->
122 begin match getopaque l.pageno with
123 | Some opaque ->
124 let x0 = l.pagedispx in
125 let x1 = x0 + l.pagevw in
126 let y0 = l.pagedispy in
127 let y1 = y0 + l.pagevh in
128 if y >= y0 && y <= y1 && x >= x0 && x <= x1
129 then
130 let px, py = pagetranslatepoint l x y in
131 match g opaque l px py with
132 | Some res -> res
133 | None -> f rest
134 else f rest
135 | _ ->
136 f rest
138 | [] -> d
140 f state.layout
143 let getunder x y =
144 let g opaque l px py =
145 if state.bzoom
146 then (
147 match rectofblock opaque px py with
148 | Some [|x0;x1;y0;y1|] ->
149 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
150 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
151 state.rects <- [l.pageno, color, rect];
152 postRedisplay "getunder";
153 | _ -> ()
155 let under = whatsunder opaque px py in
156 if under = Unone then None else Some under
158 onppundermouse g x y Unone
161 let unproject x y =
162 let g opaque l x y =
163 match unproject opaque x y with
164 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
165 | None -> None
167 onppundermouse g x y None;
170 let showtext c s =
171 state.text <- Printf.sprintf "%c%s" c s;
172 postRedisplay "showtext";
175 let impmsg fmt = Format.ksprintf (fun s -> showtext '!' s) fmt;;
177 let pipesel opaque cmd =
178 if hassel opaque
179 then pipef ~closew:false "pipesel"
180 (fun w ->
181 copysel w opaque;
182 postRedisplay "pipesel"
183 ) cmd
186 let paxunder x y =
187 let g opaque l px py =
188 if markunder opaque px py conf.paxmark
189 then (
190 Some (fun () ->
191 match getopaque l.pageno with
192 | None -> ()
193 | Some opaque -> pipesel opaque conf.paxcmd
196 else None
198 postRedisplay "paxunder";
199 if conf.paxmark = Mark_page
200 then
201 List.iter (fun l ->
202 match getopaque l.pageno with
203 | None -> ()
204 | Some opaque -> clearmark opaque) state.layout;
205 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
208 let undertext = function
209 | Unone -> "none"
210 | Ulinkuri s -> s
211 | Utext s -> "font: " ^ s
212 | Uannotation (opaque, slinkindex) ->
213 "annotation: " ^ getannotcontents opaque slinkindex
216 let updateunder x y =
217 match getunder x y with
218 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
219 | Ulinkuri uri ->
220 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
221 Wsi.setcursor Wsi.CURSOR_INFO
222 | Utext s ->
223 if conf.underinfo then showtext 'f' ("ont: " ^ s);
224 Wsi.setcursor Wsi.CURSOR_TEXT
225 | Uannotation _ ->
226 if conf.underinfo then showtext 'a' "nnotation";
227 Wsi.setcursor Wsi.CURSOR_INFO
230 let showlinktype under =
231 if conf.underinfo && under != Unone
232 then showtext ' ' @@ undertext under
235 let intentry_with_suffix text key =
236 let text =
237 match [@warning "-4"] key with
238 | Keys.Ascii ('0'..'9' as c) -> addchar text c
239 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
240 addchar text @@ asciilower c
241 | _ ->
242 state.text <- Printf.sprintf "invalid key";
243 text
245 TEcont text
248 let wcmd fmt =
249 let b = Buffer.create 16 in
250 Printf.kbprintf
251 (fun b ->
252 let b = Buffer.to_bytes b in
253 wcmd state.ss b @@ Bytes.length b
254 ) b fmt
257 let nogeomcmds cmds =
258 match cmds with
259 | s, [] -> emptystr s
260 | _ -> false
263 let layoutN ((columns, coverA, coverB), b) x y sw sh =
264 let rec fold accu n =
265 if n = Array.length b
266 then accu
267 else
268 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
269 if (vy - y) > sh &&
270 (n = coverA - 1
271 || n = state.pagecount - coverB
272 || (n - coverA) mod columns = columns - 1)
273 then accu
274 else
275 let accu =
276 if vy + h > y
277 then
278 let pagey = max 0 (y - vy) in
279 let pagedispy = if pagey > 0 then 0 else vy - y in
280 let pagedispx, pagex =
281 let pdx =
282 if n = coverA - 1 || n = state.pagecount - coverB
283 then x + (sw - w) / 2
284 else dx + xoff + x
286 if pdx < 0
287 then 0, -pdx
288 else pdx, 0
290 let pagevw =
291 let vw = sw - pagedispx in
292 let pw = w - pagex in
293 min vw pw
295 let pagevh = min (h - pagey) (sh - pagedispy) in
296 if pagevw > 0 && pagevh > 0
297 then
298 let e =
299 { pageno = n
300 ; pagedimno = pdimno
301 ; pagew = w
302 ; pageh = h
303 ; pagex = pagex
304 ; pagey = pagey
305 ; pagevw = pagevw
306 ; pagevh = pagevh
307 ; pagedispx = pagedispx
308 ; pagedispy = pagedispy
309 ; pagecol = 0
312 e :: accu
313 else
314 accu
315 else
316 accu
318 fold accu (n+1)
320 if Array.length b = 0
321 then []
322 else List.rev (fold [] (page_of_y y))
325 let layoutS (columns, b) x y sw sh =
326 let rec fold accu n =
327 if n = Array.length b
328 then accu
329 else
330 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
331 if (vy - y) > sh
332 then accu
333 else
334 let accu =
335 if vy + pageh > y
336 then
337 let x = xoff + x in
338 let pagey = max 0 (y - vy) in
339 let pagedispy = if pagey > 0 then 0 else vy - y in
340 let pagedispx, pagex =
341 if px = 0
342 then (
343 if x < 0
344 then 0, -x
345 else x, 0
347 else (
348 let px = px - x in
349 if px < 0
350 then -px, 0
351 else 0, px
354 let pagecolw = pagew/columns in
355 let pagedispx =
356 if pagecolw < sw
357 then pagedispx + ((sw - pagecolw) / 2)
358 else pagedispx
360 let pagevw =
361 let vw = sw - pagedispx in
362 let pw = pagew - pagex in
363 min vw pw
365 let pagevw = min pagevw pagecolw in
366 let pagevh = min (pageh - pagey) (sh - pagedispy) in
367 if pagevw > 0 && pagevh > 0
368 then
369 let e =
370 { pageno = n/columns
371 ; pagedimno = pdimno
372 ; pagew = pagew
373 ; pageh = pageh
374 ; pagex = pagex
375 ; pagey = pagey
376 ; pagevw = pagevw
377 ; pagevh = pagevh
378 ; pagedispx = pagedispx
379 ; pagedispy = pagedispy
380 ; pagecol = n mod columns
383 e :: accu
384 else
385 accu
386 else
387 accu
389 fold accu (n+1)
391 List.rev (fold [] 0)
394 let layout x y sw sh =
395 if nogeomcmds state.geomcmds
396 then
397 match conf.columns with
398 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
399 | Cmulti c -> layoutN c x y sw sh
400 | Csplit s -> layoutS s x y sw sh
401 else []
404 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
406 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
408 let itertiles l f =
409 let tilex = l.pagex mod conf.tilew in
410 let tiley = l.pagey mod conf.tileh in
412 let col = l.pagex / conf.tilew in
413 let row = l.pagey / conf.tileh in
415 let rec rowloop row y0 dispy h =
416 if h = 0
417 then ()
418 else (
419 let dh = conf.tileh - y0 in
420 let dh = min h dh in
421 let rec colloop col x0 dispx w =
422 if w != 0
423 then
424 let dw = conf.tilew - x0 in
425 let dw = min w dw in
426 f col row dispx dispy x0 y0 dw dh;
427 colloop (col+1) 0 (dispx+dw) (w-dw)
429 colloop col tilex l.pagedispx l.pagevw;
430 rowloop (row+1) 0 (dispy+dh) (h-dh)
433 if l.pagevw > 0 && l.pagevh > 0
434 then rowloop row tiley l.pagedispy l.pagevh;
437 let gettileopaque l col row =
438 let key = l.pageno, state.gen, conf.colorspace,
439 conf.angle, l.pagew, l.pageh, col, row in
440 try Some (Hashtbl.find state.tilemap key)
441 with Not_found -> None
444 let puttileopaque l col row gen colorspace angle opaque size elapsed =
445 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
446 Hashtbl.add state.tilemap key (opaque, size, elapsed)
449 let drawtiles l color =
450 GlDraw.color color;
451 begintiles ();
452 let f col row x y tilex tiley w h =
453 match gettileopaque l col row with
454 | Some (opaque, _, t) ->
455 let params = x, y, w, h, tilex, tiley in
456 if conf.invert
457 then GlTex.env (`mode `blend);
458 drawtile params opaque;
459 if conf.invert
460 then GlTex.env (`mode `modulate);
461 if conf.debug
462 then (
463 endtiles ();
464 let s = Printf.sprintf
465 "%d[%d,%d] %f sec"
466 l.pageno col row t
468 let w = measurestr fstate.fontsize s in
469 GlDraw.color (0.0, 0.0, 0.0);
470 filledrect
471 (float (x-2))
472 (float (y-2))
473 (float (x+2) +. w)
474 (float (y + fstate.fontsize + 2));
475 GlDraw.color color;
476 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
477 begintiles ();
480 | None ->
481 endtiles ();
482 let w =
483 let lw = state.winw - x in
484 min lw w
485 and h =
486 let lh = state.winh - y in
487 min lh h
489 if conf.invert
490 then GlTex.env (`mode `blend);
491 begin match state.checkerstexid with
492 | Some id ->
493 Gl.enable `texture_2d;
494 GlTex.bind_texture ~target:`texture_2d id;
495 let x0 = float x
496 and y0 = float y
497 and x1 = float (x+w)
498 and y1 = float (y+h) in
500 let tw = float w /. 16.0
501 and th = float h /. 16.0 in
502 let tx0 = float tilex /. 16.0
503 and ty0 = float tiley /. 16.0 in
504 let tx1 = tx0 +. tw
505 and ty1 = ty0 +. th in
506 Raw.sets_float Glutils.vraw ~pos:0
507 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
508 Raw.sets_float Glutils.traw ~pos:0
509 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
510 GlArray.vertex `two Glutils.vraw;
511 GlArray.tex_coord `two Glutils.traw;
512 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
513 Gl.disable `texture_2d;
515 | None ->
516 GlDraw.color (1.0, 1.0, 1.0);
517 filledrect (float x) (float y) (float (x+w)) (float (y+h));
518 end;
519 if conf.invert
520 then GlTex.env (`mode `modulate);
521 if w > 128 && h > fstate.fontsize + 10
522 then (
523 let c = if conf.invert then 1.0 else 0.0 in
524 GlDraw.color (c, c, c);
525 let c, r =
526 if conf.verbose
527 then (col*conf.tilew, row*conf.tileh)
528 else col, row
530 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
532 GlDraw.color color;
533 begintiles ();
535 itertiles l f;
536 endtiles ();
539 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
541 let tilevisible1 l x y =
542 let ax0 = l.pagex
543 and ax1 = l.pagex + l.pagevw
544 and ay0 = l.pagey
545 and ay1 = l.pagey + l.pagevh in
547 let bx0 = x
548 and by0 = y in
549 let bx1 = min (bx0 + conf.tilew) l.pagew
550 and by1 = min (by0 + conf.tileh) l.pageh in
552 let rx0 = max ax0 bx0
553 and ry0 = max ay0 by0
554 and rx1 = min ax1 bx1
555 and ry1 = min ay1 by1 in
557 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
558 nonemptyintersection
561 let tilevisible layout n x y =
562 let rec findpageinlayout m = function
563 | l :: rest when l.pageno = n ->
564 tilevisible1 l x y || (
565 match conf.columns with
566 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
567 | Csplit _ | Csingle _ | Cmulti _ -> false
569 | _ :: rest -> findpageinlayout 0 rest
570 | [] -> false
572 findpageinlayout 0 layout;
575 let tileready l x y =
576 tilevisible1 l x y &&
577 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
580 let tilepage n p layout =
581 let rec loop = function
582 | l :: rest ->
583 if l.pageno = n
584 then
585 let f col row _ _ _ _ _ _ =
586 if state.currently = Idle
587 then
588 match gettileopaque l col row with
589 | Some _ -> ()
590 | None ->
591 let x = col*conf.tilew
592 and y = row*conf.tileh in
593 let w =
594 let w = l.pagew - x in
595 min w conf.tilew
597 let h =
598 let h = l.pageh - y in
599 min h conf.tileh
601 let pbo =
602 if conf.usepbo
603 then getpbo w h conf.colorspace
604 else ~< "0"
606 wcmd "tile %s %d %d %d %d %s" (~> p) x y w h (~> pbo);
607 state.currently <-
608 Tiling (
609 l, p, conf.colorspace, conf.angle,
610 state.gen, col, row, conf.tilew, conf.tileh
613 itertiles l f;
614 else
615 loop rest
617 | [] -> ()
619 if nogeomcmds state.geomcmds
620 then loop layout;
623 let preloadlayout x y sw sh =
624 let y = if y < sh then 0 else y - sh in
625 let x = min 0 (x + sw) in
626 let h = sh*3 in
627 let w = sw*3 in
628 layout x y w h;
631 let load pages =
632 let rec loop pages =
633 if state.currently = Idle
634 then
635 match pages with
636 | l :: rest ->
637 begin match getopaque l.pageno with
638 | None ->
639 wcmd "page %d %d" l.pageno l.pagedimno;
640 state.currently <- Loading (l, state.gen);
641 | Some opaque ->
642 tilepage l.pageno opaque pages;
643 loop rest
644 end;
645 | _ -> ()
647 if nogeomcmds state.geomcmds
648 then loop pages
651 let preload pages =
652 load pages;
653 if conf.preload && state.currently = Idle
654 then load (preloadlayout state.x state.y state.winw state.winh);
657 let layoutready layout =
658 let rec fold all ls =
659 all && match ls with
660 | l :: rest ->
661 let seen = ref false in
662 let allvisible = ref true in
663 let foo col row _ _ _ _ _ _ =
664 seen := true;
665 allvisible := !allvisible &&
666 begin match gettileopaque l col row with
667 | Some _ -> true
668 | None -> false
671 itertiles l foo;
672 fold (!seen && !allvisible) rest
673 | [] -> true
675 let alltilesvisible = fold true layout in
676 alltilesvisible;
679 let gotoxy x y =
680 let y = bound y 0 state.maxy in
681 let y, layout =
682 let layout = layout x y state.winw state.winh in
683 postRedisplay "gotoxy ready";
684 y, layout
686 state.x <- x;
687 state.y <- y;
688 state.layout <- layout;
689 begin match state.mode with
690 | LinkNav ln ->
691 begin match ln with
692 | Ltexact (pageno, linkno) ->
693 let rec loop = function
694 | [] ->
695 state.lnava <- Some (pageno, linkno);
696 state.mode <- LinkNav (Ltgendir 0)
697 | l :: _ when l.pageno = pageno ->
698 begin match getopaque pageno with
699 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
700 | Some opaque ->
701 let x0, y0, x1, y1 = getlinkrect opaque linkno in
702 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
703 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
704 then state.mode <- LinkNav (Ltgendir 0)
706 | _ :: rest -> loop rest
708 loop layout
709 | Ltnotready _ | Ltgendir _ -> ()
711 | Birdseye _ | Textentry _ | View -> ()
712 end;
713 begin match state.mode with
714 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
715 if not (pagevisible layout pageno)
716 then (
717 match state.layout with
718 | [] -> ()
719 | l :: _ ->
720 state.mode <- Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
722 | LinkNav lt ->
723 begin match lt with
724 | Ltnotready (_, dir)
725 | Ltgendir dir ->
726 let linknav =
727 let rec loop = function
728 | [] -> lt
729 | l :: rest ->
730 match getopaque l.pageno with
731 | None -> Ltnotready (l.pageno, dir)
732 | Some opaque ->
733 let link =
734 let ld =
735 if dir = 0
736 then LDfirstvisible (l.pagex, l.pagey, dir)
737 else (
738 if dir > 0 then LDfirst else LDlast
741 findlink opaque ld
743 match link with
744 | Lnotfound -> loop rest
745 | Lfound n ->
746 showlinktype (getlink opaque n);
747 Ltexact (l.pageno, n)
749 loop state.layout
751 state.mode <- LinkNav linknav
752 | Ltexact _ -> ()
754 | Textentry _ | View -> ()
755 end;
756 preload layout;
757 if conf.updatecurs
758 then (
759 let mx, my = state.mpos in
760 updateunder mx my;
764 let conttiling pageno opaque =
765 tilepage pageno opaque
766 (if conf.preload
767 then preloadlayout state.x state.y state.winw state.winh
768 else state.layout)
771 let gotoxy x y =
772 if not conf.verbose then state.text <- E.s;
773 gotoxy x y;
776 let getanchory (n, top, dtop) =
777 let y, h = getpageyh n in
778 if conf.presentation
779 then
780 let ips = calcips h in
781 y + truncate (top*.float h -. dtop*.float ips) + ips;
782 else
783 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
786 let gotoanchor anchor =
787 gotoxy state.x (getanchory anchor);
790 let addnav () =
791 getanchor () |> cbput state.hists.nav;
794 let addnavnorc () =
795 getanchor () |> cbput_dont_update_rc state.hists.nav;
798 let getnav dir =
799 let anchor = cbgetc state.hists.nav dir in
800 getanchory anchor;
803 let gotopage n top =
804 let y, h = getpageyh n in
805 let y = y + (truncate (top *. float h)) in
806 gotoxy state.x y
809 let gotopage1 n top =
810 let y = getpagey n in
811 let y = y + top in
812 gotoxy state.x y
815 let invalidate s f =
816 Glutils.redisplay := false;
817 state.layout <- [];
818 state.pdims <- [];
819 state.rects <- [];
820 state.rects1 <- [];
821 match state.geomcmds with
822 | ps, [] when emptystr ps ->
823 f ();
824 state.geomcmds <- s, [];
826 | ps, [] ->
827 state.geomcmds <- ps, [s, f];
829 | ps, (s', _) :: rest when s' = s ->
830 state.geomcmds <- ps, ((s, f) :: rest);
832 | ps, cmds ->
833 state.geomcmds <- ps, ((s, f) :: cmds);
836 let flushpages () =
837 Hashtbl.iter (fun _ opaque -> wcmd "freepage %s" (~> opaque)) state.pagemap;
838 Hashtbl.clear state.pagemap;
841 let flushtiles () =
842 if not (Queue.is_empty state.tilelru)
843 then (
844 Queue.iter (fun (k, p, s) ->
845 wcmd "freetile %s" (~> p);
846 state.memused <- state.memused - s;
847 Hashtbl.remove state.tilemap k;
848 ) state.tilelru;
849 state.uioh#infochanged Memused;
850 Queue.clear state.tilelru;
852 load state.layout;
855 let stateh h =
856 let h = truncate (float h*.conf.zoom) in
857 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
858 h - d
861 let fillhelp () =
862 state.help <-
863 let sl = keystostrlist conf in
864 let rec loop accu =
865 function | [] -> accu
866 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
867 in Help.makehelp conf.urilauncher
868 @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
871 let opendoc path password =
872 state.path <- path;
873 state.password <- password;
874 state.gen <- state.gen + 1;
875 state.docinfo <- [];
876 state.outlines <- [||];
878 flushpages ();
879 setaalevel conf.aalevel;
880 let titlepath =
881 if emptystr state.origin
882 then path
883 else state.origin
885 Wsi.settitle ("llpp " ^ mbtoutf8 (Filename.basename titlepath));
886 wcmd "open %d %d %s\000%s\000%s\000"
887 (btod conf.usedoccss) !layouth
888 path password conf.css;
889 invalidate "reqlayout"
890 (fun () ->
891 wcmd "reqlayout %d %d %d %s\000"
892 conf.angle (FMTE.to_int conf.fitmodel)
893 (stateh state.winh) state.nameddest
895 fillhelp ();
898 let reload () =
899 state.anchor <- getanchor ();
900 opendoc state.path state.password;
903 let scalecolor c =
904 let c = c *. conf.colorscale in
905 (c, c, c);
908 let scalecolor2 (r, g, b) =
909 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
912 let docolumns columns =
913 match columns with
914 | Csingle _ ->
915 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
916 let rec loop pageno pdimno pdim y ph pdims =
917 if pageno = state.pagecount
918 then ()
919 else
920 let pdimno, ((_, w, h, xoff) as pdim), pdims =
921 match pdims with
922 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
923 pdimno+1, pdim, rest
924 | _ ->
925 pdimno, pdim, pdims
927 let x = max 0 (((state.winw - w) / 2) - xoff) in
928 let y =
929 y + (if conf.presentation
930 then (if pageno = 0 then calcips h else calcips ph + calcips h)
931 else (if pageno = 0 then 0 else conf.interpagespace))
933 a.(pageno) <- (pdimno, x, y, pdim);
934 loop (pageno+1) pdimno pdim (y + h) h pdims
936 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
937 conf.columns <- Csingle a;
939 | Cmulti ((columns, coverA, coverB), _) ->
940 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
941 let rec loop pageno pdimno pdim x y rowh pdims =
942 let rec fixrow m =
943 if m = pageno then () else
944 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
945 if h < rowh
946 then (
947 let y = y + (rowh - h) / 2 in
948 a.(m) <- (pdimno, x, y, pdim);
950 fixrow (m+1)
952 if pageno = state.pagecount
953 then fixrow (((pageno - 1) / columns) * columns)
954 else
955 let pdimno, ((_, w, h, xoff) as pdim), pdims =
956 match pdims with
957 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
958 pdimno+1, pdim, rest
959 | _ ->
960 pdimno, pdim, pdims
962 let x, y, rowh' =
963 if pageno = coverA - 1 || pageno = state.pagecount - coverB
964 then (
965 let x = (state.winw - w) / 2 in
966 let ips =
967 if conf.presentation then calcips h else conf.interpagespace in
968 x, y + ips + rowh, h
970 else (
971 if (pageno - coverA) mod columns = 0
972 then (
973 let x = max 0 (state.winw - state.w) / 2 in
974 let y =
975 if conf.presentation
976 then
977 let ips = calcips h in
978 y + (if pageno = 0 then 0 else calcips rowh + ips)
979 else
980 y + (if pageno = 0 then 0 else conf.interpagespace)
982 x, y + rowh, h
984 else x, y, max rowh h
987 let y =
988 if pageno > 1 && (pageno - coverA) mod columns = 0
989 then (
990 let y =
991 if pageno = columns && conf.presentation
992 then (
993 let ips = calcips rowh in
994 for i = 0 to pred columns
996 let (pdimno, x, y, pdim) = a.(i) in
997 a.(i) <- (pdimno, x, y+ips, pdim)
998 done;
999 y+ips;
1001 else y
1003 fixrow (pageno - columns);
1006 else y
1008 a.(pageno) <- (pdimno, x, y, pdim);
1009 let x = x + w + xoff*2 + conf.interpagespace in
1010 loop (pageno+1) pdimno pdim x y rowh' pdims
1012 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1013 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1015 | Csplit (c, _) ->
1016 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1017 let rec loop pageno pdimno pdim y pdims =
1018 if pageno != state.pagecount
1019 then
1020 let pdimno, ((_, w, h, _) as pdim), pdims =
1021 match pdims with
1022 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1023 pdimno+1, pdim, rest
1024 | _ ->
1025 pdimno, pdim, pdims
1027 let cw = w / c in
1028 let rec loop1 n x y =
1029 if n = c then y else (
1030 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1031 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1034 let y = loop1 0 0 y in
1035 loop (pageno+1) pdimno pdim y pdims
1037 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1038 conf.columns <- Csplit (c, a);
1041 let represent () =
1042 docolumns conf.columns;
1043 state.maxy <- calcheight ();
1044 if state.reprf == noreprf
1045 then (
1046 match state.mode with
1047 | Birdseye (_, _, pageno, _, _) ->
1048 let y, h = getpageyh pageno in
1049 let top = (state.winh - h) / 2 in
1050 gotoxy state.x (max 0 (y - top))
1051 | Textentry _ | View | LinkNav _ ->
1052 let y = getanchory state.anchor in
1053 let y = min y (state.maxy - state.winh) in
1054 gotoxy state.x y;
1056 else (
1057 state.reprf ();
1058 state.reprf <- noreprf;
1062 let reshape ?(firsttime=false) w h =
1063 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
1064 if not firsttime && nogeomcmds state.geomcmds
1065 then state.anchor <- getanchor ();
1067 state.winw <- w;
1068 let w = truncate (float w *. conf.zoom) in
1069 let w = max w 2 in
1070 state.winh <- h;
1071 setfontsize fstate.fontsize;
1072 GlMat.mode `modelview;
1073 GlMat.load_identity ();
1075 GlMat.mode `projection;
1076 GlMat.load_identity ();
1077 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1078 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1079 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
1081 let relx =
1082 if conf.zoom <= 1.0
1083 then 0.0
1084 else float state.x /. float state.w
1086 invalidate "geometry"
1087 (fun () ->
1088 state.w <- w;
1089 if not firsttime
1090 then state.x <- truncate (relx *. float w);
1091 let w =
1092 match conf.columns with
1093 | Csingle _ -> w
1094 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1095 | Csplit (c, _) -> w * c
1097 wcmd "geometry %d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
1101 let gctiles () =
1102 let len = Queue.length state.tilelru in
1103 let layout = lazy (if conf.preload
1104 then preloadlayout state.x state.y state.winw state.winh
1105 else state.layout) in
1106 let rec loop qpos =
1107 if state.memused > conf.memlimit
1108 then (
1109 if qpos < len
1110 then
1111 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1112 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1113 let (_, pw, ph, _) = getpagedim n in
1114 if gen = state.gen
1115 && colorspace = conf.colorspace
1116 && angle = conf.angle
1117 && pagew = pw
1118 && pageh = ph
1119 && (
1120 let x = col*conf.tilew and y = row*conf.tileh in
1121 tilevisible (Lazy.force_val layout) n x y
1123 then Queue.push lruitem state.tilelru
1124 else (
1125 freepbo p;
1126 wcmd "freetile %s" (~> p);
1127 state.memused <- state.memused - s;
1128 state.uioh#infochanged Memused;
1129 Hashtbl.remove state.tilemap k;
1131 loop (qpos+1)
1134 loop 0
1137 let onpagerect pageno f =
1138 let b =
1139 match conf.columns with
1140 | Cmulti (_, b) -> b
1141 | Csingle b -> b
1142 | Csplit (_, b) -> b
1144 if pageno >= 0 && pageno < Array.length b
1145 then
1146 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1147 f w h
1150 let gotopagexy1 pageno x y =
1151 let _,w1,h1,leftx = getpagedim pageno in
1152 let top = y /. (float h1) in
1153 let left = x /. (float w1) in
1154 let py, w, h = getpageywh pageno in
1155 let wh = state.winh in
1156 let x = left *. (float w) in
1157 let x = leftx + state.x + truncate x in
1158 let sx =
1159 if x < 0 || x >= state.winw
1160 then state.x - x
1161 else state.x
1163 let pdy = truncate (top *. float h) in
1164 let y' = py + pdy in
1165 let dy = y' - state.y in
1166 let sy =
1167 if x != state.x || not (dy > 0 && dy < wh)
1168 then (
1169 if conf.presentation
1170 then
1171 if abs (py - y') > wh
1172 then y'
1173 else py
1174 else y';
1176 else state.y
1178 if state.x != sx || state.y != sy
1179 then gotoxy sx sy
1180 else gotoxy state.x state.y;
1183 let gotopagexy pageno x y =
1184 match state.mode with
1185 | Birdseye _ -> gotopage pageno 0.0
1186 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1189 let getpassword () =
1190 let passcmd = getenvwithdef "LLPP_ASKPASS" conf.passcmd in
1191 if emptystr passcmd
1192 then E.s
1193 else getcmdoutput
1194 (fun s ->
1195 impmsg "error getting password: %s" s;
1196 dolog "%s" s) passcmd;
1199 let pgoto opaque pageno x y =
1200 let pdimno = getpdimno pageno in
1201 let x, y = project opaque pageno pdimno x y in
1202 gotopagexy pageno x y;
1205 let act cmds =
1206 (* dolog "%S" cmds; *)
1207 let spl = splitatchar cmds ' ' in
1208 let scan s fmt f =
1209 try Scanf.sscanf s fmt f
1210 with exn ->
1211 dolog "error processing '%S': %s" cmds @@ exntos exn;
1212 exit 1
1214 let addoutline outline =
1215 match state.currently with
1216 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1217 | Idle -> state.currently <- Outlining [outline]
1218 | Loading _ | Tiling _ ->
1219 dolog "invalid outlining state";
1220 logcurrently state.currently
1222 match spl with
1223 | "clear", "" ->
1224 state.pdims <- [];
1225 state.uioh#infochanged Pdim;
1227 | "clearrects", "" ->
1228 state.rects <- state.rects1;
1229 postRedisplay "clearrects";
1231 | "continue", args ->
1232 let n = scan args "%u" (fun n -> n) in
1233 state.pagecount <- n;
1234 begin match state.currently with
1235 | Outlining l ->
1236 state.currently <- Idle;
1237 state.outlines <- Array.of_list (List.rev l)
1238 | Idle | Loading _ | Tiling _ -> ()
1239 end;
1241 let cur, cmds = state.geomcmds in
1242 if emptystr cur
1243 then failwith "umpossible";
1245 begin match List.rev cmds with
1246 | [] ->
1247 state.geomcmds <- E.s, [];
1248 represent ();
1249 | (s, f) :: rest ->
1250 f ();
1251 state.geomcmds <- s, List.rev rest;
1252 end;
1253 postRedisplay "continue";
1255 | "msg", args ->
1256 showtext ' ' args
1258 | "vmsg", args ->
1259 if conf.verbose
1260 then showtext ' ' args
1262 | "emsg", args ->
1263 Buffer.add_string state.errmsgs args;
1264 state.newerrmsgs <- true;
1265 postRedisplay "error message"
1267 | "progress", args ->
1268 let progress, text =
1269 scan args "%f %n"
1270 (fun f pos ->
1271 f, String.sub args pos (String.length args - pos))
1273 state.text <- text;
1274 state.progress <- progress;
1275 postRedisplay "progress"
1277 | "firstmatch", args ->
1278 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1279 scan args "%u %d %f %f %f %f %f %f %f %f"
1280 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1281 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1283 let y = (getpagey pageno) + truncate y0 in
1284 let x =
1285 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1286 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1287 else state.x
1289 addnav ();
1290 gotoxy x y;
1291 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1292 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1294 | "match", args ->
1295 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1296 scan args "%u %d %f %f %f %f %f %f %f %f"
1297 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1298 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1300 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1301 state.rects1 <-
1302 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1304 | "page", args ->
1305 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1306 let pageopaque = ~< pageopaques in
1307 begin match state.currently with
1308 | Loading (l, gen) ->
1309 vlog "page %d took %f sec" l.pageno t;
1310 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1311 let preloadedpages =
1312 if conf.preload
1313 then preloadlayout state.x state.y state.winw state.winh
1314 else state.layout
1316 let evict () =
1317 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1318 IntSet.empty preloadedpages
1320 let evictedpages =
1321 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1322 if not (IntSet.mem pageno set)
1323 then (
1324 wcmd "freepage %s" (~> opaque);
1325 key :: accu
1327 else accu
1328 ) state.pagemap []
1330 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1332 evict ();
1333 state.currently <- Idle;
1334 if gen = state.gen
1335 then (
1336 tilepage l.pageno pageopaque state.layout;
1337 load state.layout;
1338 load preloadedpages;
1339 let visible = pagevisible state.layout l.pageno in
1340 if visible
1341 then (
1342 match state.mode with
1343 | LinkNav (Ltnotready (pageno, dir)) ->
1344 if pageno = l.pageno
1345 then (
1346 let link =
1347 let ld =
1348 if dir = 0
1349 then LDfirstvisible (l.pagex, l.pagey, dir)
1350 else (
1351 if dir > 0 then LDfirst else LDlast
1354 findlink pageopaque ld
1356 match link with
1357 | Lnotfound -> ()
1358 | Lfound n ->
1359 showlinktype (getlink pageopaque n);
1360 state.mode <- LinkNav (Ltexact (l.pageno, n))
1362 | LinkNav (Ltgendir _)
1363 | LinkNav (Ltexact _)
1364 | View
1365 | Birdseye _
1366 | Textentry _ -> ()
1369 if visible && layoutready state.layout
1370 then (
1371 postRedisplay "page";
1375 | Idle | Tiling _ | Outlining _ ->
1376 dolog "Inconsistent loading state";
1377 logcurrently state.currently;
1378 exit 1
1381 | "tile" , args ->
1382 let (x, y, opaques, size, t) =
1383 scan args "%u %u %s %u %f"
1384 (fun x y p size t -> (x, y, p, size, t))
1386 let opaque = ~< opaques in
1387 begin match state.currently with
1388 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1389 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1391 unmappbo opaque;
1392 if tilew != conf.tilew || tileh != conf.tileh
1393 then (
1394 wcmd "freetile %s" (~> opaque);
1395 state.currently <- Idle;
1396 load state.layout;
1398 else (
1399 puttileopaque l col row gen cs angle opaque size t;
1400 state.memused <- state.memused + size;
1401 state.uioh#infochanged Memused;
1402 gctiles ();
1403 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1404 opaque, size) state.tilelru;
1406 state.currently <- Idle;
1407 if gen = state.gen
1408 && conf.colorspace = cs
1409 && conf.angle = angle
1410 && tilevisible state.layout l.pageno x y
1411 then conttiling l.pageno pageopaque;
1413 preload state.layout;
1414 if gen = state.gen
1415 && conf.colorspace = cs
1416 && conf.angle = angle
1417 && tilevisible state.layout l.pageno x y
1418 && layoutready state.layout
1419 then postRedisplay "tile nothrottle";
1422 | Idle | Loading _ | Outlining _ ->
1423 dolog "Inconsistent tiling state";
1424 logcurrently state.currently;
1425 exit 1
1428 | "pdim", args ->
1429 let (n, w, h, _) as pdim =
1430 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1432 let pdim =
1433 match conf.fitmodel with
1434 | FitWidth -> pdim
1435 | FitPage | FitProportional ->
1436 match conf.columns with
1437 | Csplit _ -> (n, w, h, 0)
1438 | Csingle _ | Cmulti _ -> pdim
1440 state.pdims <- pdim :: state.pdims;
1441 state.uioh#infochanged Pdim
1443 | "o", args ->
1444 let (l, n, t, h, pos) =
1445 scan args "%u %u %d %u %n"
1446 (fun l n t h pos -> l, n, t, h, pos)
1448 let s = String.sub args pos (String.length args - pos) in
1449 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1451 | "ou", args ->
1452 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1453 let s = String.sub args pos len in
1454 let pos2 = pos + len + 1 in
1455 let uri = String.sub args pos2 (String.length args - pos2) in
1456 addoutline (s, l, Ouri uri)
1458 | "on", args ->
1459 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1460 let s = String.sub args pos (String.length args - pos) in
1461 addoutline (s, l, Onone)
1463 | "a", args ->
1464 let (n, l, t) =
1465 scan args "%u %d %d" (fun n l t -> n, l, t)
1467 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1469 | "info", args ->
1470 let c, v = splitatchar args '\t' in
1471 let s =
1472 if nonemptystr v
1473 then
1474 if c = "Title"
1475 then (
1476 conf.title <- v;
1477 if not !ignoredoctitlte
1478 then Wsi.settitle v;
1479 args
1481 else
1482 if let len = String.length c in
1483 len > 6 && ((String.sub c (len-4) 4) = "date")
1484 then (
1485 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1486 then
1487 let b = Buffer.create 10 in
1488 Printf.bprintf b "%s\t" c;
1489 let sub p l c =
1491 Buffer.add_substring b v p l;
1492 Buffer.add_char b c;
1493 with exn -> Buffer.add_string b @@ exntos exn
1495 sub 2 4 '/';
1496 sub 6 2 '/';
1497 sub 8 2 ' ';
1498 sub 10 2 ':';
1499 sub 12 2 ':';
1500 sub 14 2 ' ';
1501 Buffer.add_char b '[';
1502 Buffer.add_string b v;
1503 Buffer.add_char b ']';
1504 Buffer.contents b
1505 else args
1507 else args
1508 else args
1510 state.docinfo <- (1, s) :: state.docinfo
1512 | "infoend", "" ->
1513 state.docinfo <- List.rev state.docinfo;
1514 state.uioh#infochanged Docinfo
1516 | "pass", args ->
1517 if args = "fail"
1518 then Wsi.settitle "Wrong password";
1519 let password = getpassword () in
1520 if emptystr password
1521 then error "document is password protected"
1522 else opendoc state.path password
1524 | _ ->
1525 error "unknown cmd `%S'" cmds
1528 let onhist cb =
1529 let rc = cb.rc in
1530 let action = function
1531 | HCprev -> cbget cb ~-1
1532 | HCnext -> cbget cb 1
1533 | HCfirst -> cbget cb ~-(cb.rc)
1534 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1535 and cancel () = cb.rc <- rc
1536 in (action, cancel)
1539 let search pattern forward =
1540 match conf.columns with
1541 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1542 | Csingle _ | Cmulti _ ->
1543 if nonemptystr pattern
1544 then
1545 let pn, py =
1546 match state.layout with
1547 | [] -> 0, 0
1548 | l :: _ ->
1549 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1551 wcmd "search %d %d %d %d,%s\000"
1552 (btod conf.icase) pn py (btod forward) pattern;
1555 let intentry text key =
1556 let text =
1557 if emptystr text && key = Keys.Ascii '-'
1558 then addchar text '-'
1559 else
1560 match [@warning "-4"] key with
1561 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1562 | _ ->
1563 state.text <- "invalid key";
1564 text
1566 TEcont text
1569 let linknact f s =
1570 if nonemptystr s
1571 then (
1572 let n =
1573 let l = String.length s in
1574 let rec loop pos n =
1575 if pos = l
1576 then n
1577 else
1578 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1579 loop (pos+1) (n*26 + m)
1580 in loop 0 0
1582 let rec loop n = function
1583 | [] -> ()
1584 | l :: rest ->
1585 match getopaque l.pageno with
1586 | None -> loop n rest
1587 | Some opaque ->
1588 let m = getlinkcount opaque in
1589 if n < m
1590 then (
1591 let under = getlink opaque n in
1592 f under
1594 else loop (n-m) rest
1596 loop n state.layout;
1600 let linknentry text =
1601 function [@warning "-4"]
1602 | Keys.Ascii c ->
1603 let text = addchar text c in
1604 linknact (fun under -> state.text <- undertext under) text;
1605 TEcont text
1606 | _ ->
1607 state.text <- Printf.sprintf "invalid key";
1608 TEcont text
1611 let textentry text = function [@warning "-4"]
1612 | Keys.Ascii c -> TEcont (addchar text c)
1613 | Keys.Code c -> TEcont (text ^ toutf8 c)
1614 | _ -> TEcont text
1617 let reqlayout angle fitmodel =
1618 if nogeomcmds state.geomcmds
1619 then state.anchor <- getanchor ();
1620 conf.angle <- angle mod 360;
1621 if conf.angle != 0
1622 then (
1623 match state.mode with
1624 | LinkNav _ -> state.mode <- View
1625 | Birdseye _ | Textentry _ | View -> ()
1627 conf.fitmodel <- fitmodel;
1628 invalidate
1629 "reqlayout"
1630 (fun () ->
1631 wcmd "reqlayout %d %d %d"
1632 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh)
1636 let settrim trimmargins trimfuzz =
1637 if nogeomcmds state.geomcmds
1638 then state.anchor <- getanchor ();
1639 conf.trimmargins <- trimmargins;
1640 conf.trimfuzz <- trimfuzz;
1641 let x0, y0, x1, y1 = trimfuzz in
1642 invalidate
1643 "settrim" (fun () ->
1644 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
1645 flushpages ();
1648 let setzoom zoom =
1649 let zoom = max 0.0001 zoom in
1650 if zoom <> conf.zoom
1651 then (
1652 state.prevzoom <- (conf.zoom, state.x);
1653 conf.zoom <- zoom;
1654 reshape state.winw state.winh;
1655 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1659 let pivotzoom ?(vw=min state.w state.winw)
1660 ?(vh=min (state.maxy-state.y) state.winh)
1661 ?(x=vw/2) ?(y=vh/2) zoom =
1662 let w = float state.w /. zoom in
1663 let hw = w /. 2.0 in
1664 let ratio = float vh /. float vw in
1665 let hh = hw *. ratio in
1666 let x0 = float x -. hw
1667 and y0 = float y -. hh in
1668 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1669 setzoom zoom;
1672 let pivotzoom ?vw ?vh ?x ?y zoom =
1673 if nogeomcmds state.geomcmds
1674 then
1675 if zoom > 1.0
1676 then pivotzoom ?vw ?vh ?x ?y zoom
1677 else setzoom zoom
1680 let setcolumns mode columns coverA coverB =
1681 state.prevcolumns <- Some (conf.columns, conf.zoom);
1682 if columns < 0
1683 then (
1684 if isbirdseye mode
1685 then impmsg "split mode doesn't work in bird's eye"
1686 else (
1687 conf.columns <- Csplit (-columns, E.a);
1688 state.x <- 0;
1689 conf.zoom <- 1.0;
1692 else (
1693 if columns < 2
1694 then (
1695 conf.columns <- Csingle E.a;
1696 state.x <- 0;
1697 setzoom 1.0;
1699 else (
1700 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1701 conf.zoom <- 1.0;
1704 reshape state.winw state.winh;
1707 let resetmstate () =
1708 state.mstate <- Mnone;
1709 Wsi.setcursor Wsi.CURSOR_INHERIT;
1712 let enterbirdseye () =
1713 let zoom = float conf.thumbw /. float state.winw in
1714 let birdseyepageno =
1715 let cy = state.winh / 2 in
1716 let fold = function
1717 | [] -> 0
1718 | l :: rest ->
1719 let rec fold best = function
1720 | [] -> best.pageno
1721 | l :: rest ->
1722 let d = cy - (l.pagedispy + l.pagevh/2)
1723 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1724 if abs d < abs dbest
1725 then fold l rest
1726 else best.pageno
1727 in fold l rest
1729 fold state.layout
1731 state.mode <-
1732 Birdseye (
1733 { conf with zoom = conf.zoom },
1734 state.x, birdseyepageno, -1, getanchor ()
1736 resetmstate ();
1737 conf.zoom <- zoom;
1738 conf.presentation <- false;
1739 conf.interpagespace <- 10;
1740 conf.hlinks <- false;
1741 conf.fitmodel <- FitPage;
1742 state.x <- 0;
1743 conf.columns <- (
1744 match conf.beyecolumns with
1745 | Some c ->
1746 conf.zoom <- 1.0;
1747 Cmulti ((c, 0, 0), E.a)
1748 | None -> Csingle E.a
1750 if conf.verbose
1751 then
1752 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1753 (100.0*.zoom)
1754 else state.text <- E.s;
1755 reshape state.winw state.winh;
1758 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1759 state.mode <- View;
1760 conf.zoom <- c.zoom;
1761 conf.presentation <- c.presentation;
1762 conf.interpagespace <- c.interpagespace;
1763 conf.hlinks <- c.hlinks;
1764 conf.fitmodel <- c.fitmodel;
1765 conf.beyecolumns <- (
1766 match conf.columns with
1767 | Cmulti ((c, _, _), _) -> Some c
1768 | Csingle _ -> None
1769 | Csplit _ -> failwith "leaving bird's eye split mode"
1771 conf.columns <- (
1772 match c.columns with
1773 | Cmulti (c, _) -> Cmulti (c, E.a)
1774 | Csingle _ -> Csingle E.a
1775 | Csplit (c, _) -> Csplit (c, E.a)
1777 if conf.verbose
1778 then
1779 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1780 (100.0*.conf.zoom);
1781 reshape state.winw state.winh;
1782 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
1783 state.x <- leftx;
1786 let togglebirdseye () =
1787 match state.mode with
1788 | Birdseye vals -> leavebirdseye vals true
1789 | View -> enterbirdseye ()
1790 | Textentry _ | LinkNav _ -> ()
1793 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1794 let pageno = max 0 (pageno - incr) in
1795 let rec loop = function
1796 | [] -> gotopage1 pageno 0
1797 | l :: _ when l.pageno = pageno ->
1798 if l.pagedispy >= 0 && l.pagey = 0
1799 then postRedisplay "upbirdseye"
1800 else gotopage1 pageno 0
1801 | _ :: rest -> loop rest
1803 loop state.layout;
1804 state.text <- E.s;
1805 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1808 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1809 let pageno = min (state.pagecount - 1) (pageno + incr) in
1810 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1811 let rec loop = function
1812 | [] ->
1813 let y, h = getpageyh pageno in
1814 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
1815 gotoxy state.x (clamp dy)
1816 | l :: _ when l.pageno = pageno ->
1817 if l.pagevh != l.pageh
1818 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
1819 else postRedisplay "downbirdseye"
1820 | _ :: rest -> loop rest
1822 loop state.layout;
1823 state.text <- E.s;
1826 let optentry mode _ key =
1827 let btos b = if b then "on" else "off" in
1828 match [@warning "-4"] key with
1829 | Keys.Ascii 'C' ->
1830 let ondone s =
1832 let n, a, b = multicolumns_of_string s in
1833 setcolumns mode n a b;
1834 with exn ->
1835 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
1837 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1839 | Keys.Ascii 'Z' ->
1840 let ondone s =
1842 let zoom = float (int_of_string s) /. 100.0 in
1843 pivotzoom zoom
1844 with exn ->
1845 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
1847 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1849 | Keys.Ascii 'i' ->
1850 conf.icase <- not conf.icase;
1851 TEdone ("case insensitive search " ^ (btos conf.icase))
1853 | Keys.Ascii 'v' ->
1854 conf.verbose <- not conf.verbose;
1855 TEdone ("verbose " ^ (btos conf.verbose))
1857 | Keys.Ascii 'd' ->
1858 conf.debug <- not conf.debug;
1859 TEdone ("debug " ^ (btos conf.debug))
1861 | Keys.Ascii 'f' ->
1862 conf.underinfo <- not conf.underinfo;
1863 TEdone ("underinfo " ^ btos conf.underinfo)
1865 | Keys.Ascii 'T' ->
1866 settrim (not conf.trimmargins) conf.trimfuzz;
1867 TEdone ("trim margins " ^ btos conf.trimmargins)
1869 | Keys.Ascii 'I' ->
1870 conf.invert <- not conf.invert;
1871 TEdone ("invert colors " ^ btos conf.invert)
1873 | Keys.Ascii 'x' ->
1874 let ondone s =
1875 cbput state.hists.sel s;
1876 conf.selcmd <- s;
1878 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
1879 textentry, ondone, true)
1881 | Keys.Ascii 'M' ->
1882 if conf.pax == None
1883 then conf.pax <- Some 0.0
1884 else conf.pax <- None;
1885 TEdone ("PAX " ^ btos (conf.pax != None))
1887 | (Keys.Ascii c) ->
1888 state.text <- Printf.sprintf "bad option %d `%c'" (Char.code c) c;
1889 TEstop
1891 | _ ->
1892 TEcont state.text
1895 let adderrmsg src msg =
1896 Buffer.add_string state.errmsgs msg;
1897 state.newerrmsgs <- true;
1898 postRedisplay src
1901 let adderrfmt src fmt = Format.ksprintf (fun s -> adderrmsg src s) fmt;;
1903 class outlinelistview ~zebra ~source =
1904 let settext autonarrow s =
1905 if autonarrow
1906 then
1907 let ss = source#statestr in
1908 state.text <-
1909 if emptystr ss
1910 then "[" ^ s ^ "]"
1911 else "{" ^ ss ^ "} [" ^ s ^ "]"
1912 else state.text <- s
1914 object (self)
1915 inherit listview
1916 ~zebra
1917 ~helpmode:false
1918 ~source:(source :> lvsource)
1919 ~trusted:false
1920 ~modehash:(findkeyhash conf "outline")
1921 as super
1923 val m_autonarrow = false
1925 method! key key mask =
1926 let maxrows =
1927 if emptystr state.text
1928 then fstate.maxrows
1929 else fstate.maxrows - 2
1931 let calcfirst first active =
1932 if active > first
1933 then
1934 let rows = active - first in
1935 if rows > maxrows then active - maxrows else first
1936 else active
1938 let navigate incr =
1939 let active = m_active + incr in
1940 let active = bound active 0 (source#getitemcount - 1) in
1941 let first = calcfirst m_first active in
1942 postRedisplay "outline navigate";
1943 coe {< m_active = active; m_first = first >}
1945 let navscroll first =
1946 let active =
1947 let dist = m_active - first in
1948 if dist < 0
1949 then first
1950 else (
1951 if dist < maxrows
1952 then m_active
1953 else first + maxrows
1956 postRedisplay "outline navscroll";
1957 coe {< m_first = first; m_active = active >}
1959 let ctrl = Wsi.withctrl mask in
1960 let open Keys in
1961 match Wsi.kc2kt key with
1962 | Ascii 'a' when ctrl ->
1963 let text =
1964 if m_autonarrow
1965 then (
1966 source#denarrow;
1969 else (
1970 let pattern = source#renarrow in
1971 if nonemptystr m_qsearch
1972 then (source#narrow m_qsearch; m_qsearch)
1973 else pattern
1976 settext (not m_autonarrow) text;
1977 postRedisplay "toggle auto narrowing";
1978 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1980 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1981 settext true E.s;
1982 postRedisplay "toggle auto narrowing";
1983 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1985 | Ascii 'n' when ctrl ->
1986 source#narrow m_qsearch;
1987 if not m_autonarrow
1988 then source#add_narrow_pattern m_qsearch;
1989 postRedisplay "outline ctrl-n";
1990 coe {< m_first = 0; m_active = 0 >}
1992 | Ascii 'S' when ctrl ->
1993 let active = source#calcactive (getanchor ()) in
1994 let first = firstof m_first active in
1995 postRedisplay "outline ctrl-s";
1996 coe {< m_first = first; m_active = active >}
1998 | Ascii 'u' when ctrl ->
1999 postRedisplay "outline ctrl-u";
2000 if m_autonarrow && nonemptystr m_qsearch
2001 then (
2002 ignore (source#renarrow);
2003 settext m_autonarrow E.s;
2004 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
2006 else (
2007 source#del_narrow_pattern;
2008 let pattern = source#renarrow in
2009 let text =
2010 if emptystr pattern then E.s else "Narrowed to " ^ pattern
2012 settext m_autonarrow text;
2013 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
2016 | Ascii 'l' when ctrl ->
2017 let first = max 0 (m_active - (fstate.maxrows / 2)) in
2018 postRedisplay "outline ctrl-l";
2019 coe {< m_first = first >}
2021 | Ascii '\t' when m_autonarrow ->
2022 if nonemptystr m_qsearch
2023 then (
2024 postRedisplay "outline list view tab";
2025 source#add_narrow_pattern m_qsearch;
2026 settext true E.s;
2027 coe {< m_qsearch = E.s >}
2029 else coe self
2031 | Escape when m_autonarrow ->
2032 if nonemptystr m_qsearch
2033 then source#add_narrow_pattern m_qsearch;
2034 super#key key mask
2036 | Enter when m_autonarrow ->
2037 if nonemptystr m_qsearch
2038 then source#add_narrow_pattern m_qsearch;
2039 super#key key mask
2041 | (Ascii _ | Code _) when m_autonarrow ->
2042 let pattern = m_qsearch ^ toutf8 key in
2043 postRedisplay "outlinelistview autonarrow add";
2044 source#narrow pattern;
2045 settext true pattern;
2046 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
2048 | Backspace when m_autonarrow ->
2049 if emptystr m_qsearch
2050 then coe self
2051 else
2052 let pattern = withoutlastutf8 m_qsearch in
2053 postRedisplay "outlinelistview autonarrow backspace";
2054 ignore (source#renarrow);
2055 source#narrow pattern;
2056 settext true pattern;
2057 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
2059 | Up when ctrl ->
2060 navscroll (max 0 (m_first - 1))
2062 | Down when ctrl ->
2063 navscroll (min (source#getitemcount - 1) (m_first + 1))
2065 | Up -> navigate ~-1
2066 | Down -> navigate 1
2067 | Prior -> navigate ~-(fstate.maxrows)
2068 | Next -> navigate fstate.maxrows
2070 | Right ->
2071 let o =
2072 if ctrl
2073 then (
2074 postRedisplay "outline ctrl right";
2075 {< m_pan = m_pan + 1 >}
2077 else self#updownlevel 1
2079 coe o
2081 | Left ->
2082 let o =
2083 if ctrl
2084 then (
2085 postRedisplay "outline ctrl left";
2086 {< m_pan = m_pan - 1 >}
2088 else self#updownlevel ~-1
2090 coe o
2092 | Home ->
2093 postRedisplay "outline home";
2094 coe {< m_first = 0; m_active = 0 >}
2096 | End ->
2097 let active = source#getitemcount - 1 in
2098 let first = max 0 (active - fstate.maxrows) in
2099 postRedisplay "outline end";
2100 coe {< m_active = active; m_first = first >}
2102 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
2103 super#key key mask
2104 end;;
2106 let genhistoutlines () =
2107 Config.gethist ()
2108 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
2109 compare c2.lastvisit c1.lastvisit)
2110 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
2111 let path = if nonemptystr origin then origin else path in
2112 let base = mbtoutf8 @@ Filename.basename path in
2113 (base ^ "\000" ^ c.title, 1, Ohistory hist)
2117 let gotohist (path, c, bookmarks, x, anchor, origin) =
2118 Config.save leavebirdseye;
2119 state.anchor <- anchor;
2120 state.bookmarks <- bookmarks;
2121 state.origin <- origin;
2122 state.x <- x;
2123 setconf conf c;
2124 let x0, y0, x1, y1 = conf.trimfuzz in
2125 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
2126 reshape ~firsttime:true state.winw state.winh;
2127 opendoc path origin;
2128 setzoom c.zoom;
2131 let setcheckers enabled =
2132 match state.checkerstexid with
2133 | None -> if enabled then state.checkerstexid <- Some (makecheckers ())
2135 | Some checkerstexid ->
2136 if not enabled
2137 then (
2138 GlTex.delete_texture checkerstexid;
2139 state.checkerstexid <- None;
2143 let describe_layout layout =
2144 let d =
2145 match layout with
2146 | [] -> "Page 0"
2147 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
2148 | l :: rest ->
2149 let rangestr a b =
2150 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
2151 else
2152 let sep =
2153 if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis in
2154 Printf.sprintf "%d%s%d" (a.pageno+1) sep (b.pageno+1)
2156 let rec fold s la lb = function
2157 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
2158 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
2159 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
2161 fold "Pages" l l rest
2163 let percent =
2164 let maxy = maxy () in
2165 if maxy <= 0
2166 then 100.
2167 else 100. *. (float state.y /. float maxy)
2169 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
2172 let setpresentationmode v =
2173 let n = page_of_y state.y in
2174 state.anchor <- (n, 0.0, 1.0);
2175 conf.presentation <- v;
2176 if conf.fitmodel = FitPage
2177 then reqlayout conf.angle conf.fitmodel;
2178 represent ();
2181 let enterinfomode =
2182 let btos b = if b then Utf8syms.radical else E.s in
2183 let showextended = ref false in
2184 let showcolors = ref false in
2185 let leave mode _ = state.mode <- mode in
2186 let src =
2187 (object
2188 val mutable m_l = []
2189 val mutable m_a = E.a
2190 val mutable m_prev_uioh = nouioh
2191 val mutable m_prev_mode = View
2193 inherit lvsourcebase
2195 method reset prev_mode prev_uioh =
2196 m_a <- Array.of_list (List.rev m_l);
2197 m_l <- [];
2198 m_prev_mode <- prev_mode;
2199 m_prev_uioh <- prev_uioh;
2201 method int name get set =
2202 m_l <-
2203 (name, `int get, 1,
2204 Action (
2205 fun u ->
2206 let ondone s =
2207 try set (int_of_string s)
2208 with exn ->
2209 state.text <- Printf.sprintf "bad integer `%s': %s"
2210 s @@ exntos exn
2212 state.text <- E.s;
2213 let te = name ^ ": ", E.s, None, intentry, ondone, true in
2214 state.mode <- Textentry (te, leave m_prev_mode);
2216 )) :: m_l
2218 method int_with_suffix name get set =
2219 m_l <-
2220 (name, `intws get, 1,
2221 Action (
2222 fun u ->
2223 let ondone s =
2224 try set (int_of_string_with_suffix s)
2225 with exn ->
2226 state.text <- Printf.sprintf "bad integer `%s': %s"
2227 s @@ exntos exn
2229 state.text <- E.s;
2230 let te =
2231 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
2233 state.mode <- Textentry (te, leave m_prev_mode);
2235 )) :: m_l
2237 method bool ?(offset=1) ?(btos=btos) name get set =
2238 m_l <-
2239 (name, `bool (btos, get), offset, Action (
2240 fun u ->
2241 let v = get () in
2242 set (not v);
2244 )) :: m_l
2246 method color name get set =
2247 m_l <-
2248 (name, `color get, 1,
2249 Action (
2250 fun u ->
2251 let invalid = (nan, nan, nan) in
2252 let ondone s =
2253 let c =
2254 try color_of_string s
2255 with exn ->
2256 state.text <- Printf.sprintf "bad color `%s': %s"
2257 s @@ exntos exn;
2258 invalid
2260 if c <> invalid
2261 then set c;
2263 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2264 state.text <- color_to_string (get ());
2265 state.mode <- Textentry (te, leave m_prev_mode);
2267 )) :: m_l
2269 method string name get set =
2270 m_l <-
2271 (name, `string get, 1,
2272 Action (
2273 fun u ->
2274 let ondone s = set s in
2275 let te = name ^ ": ", E.s, None, textentry, ondone, true in
2276 state.mode <- Textentry (te, leave m_prev_mode);
2278 )) :: m_l
2280 method colorspace name get set =
2281 m_l <-
2282 (name, `string get, 1,
2283 Action (
2284 fun _ ->
2285 let source =
2286 (object
2287 inherit lvsourcebase
2289 initializer
2290 m_active <- CSTE.to_int conf.colorspace;
2291 m_first <- 0;
2293 method getitemcount =
2294 Array.length CSTE.names
2295 method getitem n =
2296 (CSTE.names.(n), 0)
2297 method exit ~uioh ~cancel ~active ~first ~pan =
2298 ignore (uioh, first, pan);
2299 if not cancel then set active;
2300 None
2301 method hasaction _ = true
2302 end)
2304 state.text <- E.s;
2305 let modehash = findkeyhash conf "info" in
2306 coe (new listview ~zebra:false ~helpmode:false
2307 ~source ~trusted:true ~modehash)
2308 )) :: m_l
2310 method paxmark name get set =
2311 m_l <-
2312 (name, `string get, 1,
2313 Action (
2314 fun _ ->
2315 let source =
2316 (object
2317 inherit lvsourcebase
2319 initializer
2320 m_active <- MTE.to_int conf.paxmark;
2321 m_first <- 0;
2323 method getitemcount = Array.length MTE.names
2324 method getitem n = (MTE.names.(n), 0)
2325 method exit ~uioh ~cancel ~active ~first ~pan =
2326 ignore (uioh, first, pan);
2327 if not cancel then set active;
2328 None
2329 method hasaction _ = true
2330 end)
2332 state.text <- E.s;
2333 let modehash = findkeyhash conf "info" in
2334 coe (new listview ~zebra:false ~helpmode:false
2335 ~source ~trusted:true ~modehash)
2336 )) :: m_l
2338 method fitmodel name get set =
2339 m_l <-
2340 (name, `string get, 1,
2341 Action (
2342 fun _ ->
2343 let source =
2344 (object
2345 inherit lvsourcebase
2347 initializer
2348 m_active <- FMTE.to_int conf.fitmodel;
2349 m_first <- 0;
2351 method getitemcount = Array.length FMTE.names
2352 method getitem n = (FMTE.names.(n), 0)
2353 method exit ~uioh ~cancel ~active ~first ~pan =
2354 ignore (uioh, first, pan);
2355 if not cancel then set active;
2356 None
2357 method hasaction _ = true
2358 end)
2360 state.text <- E.s;
2361 let modehash = findkeyhash conf "info" in
2362 coe (new listview ~zebra:false ~helpmode:false
2363 ~source ~trusted:true ~modehash)
2364 )) :: m_l
2366 method caption s offset =
2367 m_l <- (s, `empty, offset, Noaction) :: m_l
2369 method caption2 s f offset =
2370 m_l <- (s, `string f, offset, Noaction) :: m_l
2372 method getitemcount = Array.length m_a
2374 method getitem n =
2375 let tostr = function
2376 | `int f -> string_of_int (f ())
2377 | `intws f -> string_with_suffix_of_int (f ())
2378 | `string f -> f ()
2379 | `color f -> color_to_string (f ())
2380 | `bool (btos, f) -> btos (f ())
2381 | `empty -> E.s
2383 let name, t, offset, _ = m_a.(n) in
2384 ((let s = tostr t in
2385 if nonemptystr s
2386 then Printf.sprintf "%s\t%s" name s
2387 else name),
2388 offset)
2390 method exit ~uioh ~cancel ~active ~first ~pan =
2391 let uiohopt =
2392 if not cancel
2393 then (
2394 let uioh =
2395 match m_a.(active) with
2396 | _, _, _, Action f -> f uioh
2397 | _, _, _, Noaction -> uioh
2399 Some uioh
2401 else None
2403 m_active <- active;
2404 m_first <- first;
2405 m_pan <- pan;
2406 uiohopt
2408 method hasaction n =
2409 match m_a.(n) with
2410 | _, _, _, Action _ -> true
2411 | _, _, _, Noaction -> false
2413 initializer m_active <- 1
2414 end)
2416 let rec fillsrc prevmode prevuioh =
2417 let sep () = src#caption E.s 0 in
2418 let colorp name get set =
2419 src#string name
2420 (fun () -> color_to_string (get ()))
2421 (fun v ->
2423 let c = color_of_string v in
2424 set c
2425 with exn ->
2426 state.text <-
2427 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2430 let rgba name get set =
2431 src#string name
2432 (fun () -> rgba_to_string (get ()))
2433 (fun v ->
2435 let c = rgba_of_string v in
2436 set c
2437 with exn ->
2438 state.text <-
2439 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
2442 let oldmode = state.mode in
2443 let birdseye = isbirdseye state.mode in
2445 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2447 src#bool "presentation mode"
2448 (fun () -> conf.presentation)
2449 (fun v -> setpresentationmode v);
2451 src#bool "ignore case in searches"
2452 (fun () -> conf.icase)
2453 (fun v -> conf.icase <- v);
2455 src#bool "preload"
2456 (fun () -> conf.preload)
2457 (fun v -> conf.preload <- v);
2459 src#bool "highlight links"
2460 (fun () -> conf.hlinks)
2461 (fun v -> conf.hlinks <- v);
2463 src#bool "under info"
2464 (fun () -> conf.underinfo)
2465 (fun v -> conf.underinfo <- v);
2467 src#fitmodel "fit model"
2468 (fun () -> FMTE.to_string conf.fitmodel)
2469 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2471 src#bool "trim margins"
2472 (fun () -> conf.trimmargins)
2473 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2475 sep ();
2476 src#int "inter-page space"
2477 (fun () -> conf.interpagespace)
2478 (fun n ->
2479 conf.interpagespace <- n;
2480 docolumns conf.columns;
2481 let pageno, py =
2482 match state.layout with
2483 | [] -> 0, 0
2484 | l :: _ ->
2485 l.pageno, l.pagey
2487 state.maxy <- calcheight ();
2488 let y = getpagey pageno in
2489 gotoxy state.x (y + py)
2492 src#int "page bias"
2493 (fun () -> conf.pagebias)
2494 (fun v -> conf.pagebias <- v);
2496 src#int "scroll step"
2497 (fun () -> conf.scrollstep)
2498 (fun n -> conf.scrollstep <- n);
2500 src#int "horizontal scroll step"
2501 (fun () -> conf.hscrollstep)
2502 (fun v -> conf.hscrollstep <- v);
2504 src#int "auto scroll step"
2505 (fun () ->
2506 match state.autoscroll with
2507 | Some step -> step
2508 | _ -> conf.autoscrollstep)
2509 (fun n ->
2510 let n = boundastep state.winh n in
2511 if state.autoscroll <> None
2512 then state.autoscroll <- Some n;
2513 conf.autoscrollstep <- n);
2515 src#int "zoom"
2516 (fun () -> truncate (conf.zoom *. 100.))
2517 (fun v -> pivotzoom ((float v) /. 100.));
2519 src#int "rotation"
2520 (fun () -> conf.angle)
2521 (fun v -> reqlayout v conf.fitmodel);
2523 src#int "scroll bar width"
2524 (fun () -> conf.scrollbw)
2525 (fun v ->
2526 conf.scrollbw <- v;
2527 reshape state.winw state.winh;
2530 src#int "scroll handle height"
2531 (fun () -> conf.scrollh)
2532 (fun v -> conf.scrollh <- v;);
2534 src#int "thumbnail width"
2535 (fun () -> conf.thumbw)
2536 (fun v ->
2537 conf.thumbw <- min 4096 v;
2538 match oldmode with
2539 | Birdseye beye ->
2540 leavebirdseye beye false;
2541 enterbirdseye ()
2542 | Textentry _
2543 | View
2544 | LinkNav _ -> ()
2547 let mode = state.mode in
2548 src#string "columns"
2549 (fun () ->
2550 match conf.columns with
2551 | Csingle _ -> "1"
2552 | Cmulti (multi, _) -> multicolumns_to_string multi
2553 | Csplit (count, _) -> "-" ^ string_of_int count
2555 (fun v ->
2556 let n, a, b = multicolumns_of_string v in
2557 setcolumns mode n a b);
2559 sep ();
2560 src#caption "Pixmap cache" 0;
2561 src#int_with_suffix "size (advisory)"
2562 (fun () -> conf.memlimit)
2563 (fun v -> conf.memlimit <- v);
2565 src#caption2 "used"
2566 (fun () ->
2567 Printf.sprintf "%s bytes, %d tiles"
2568 (string_with_suffix_of_int state.memused)
2569 (Hashtbl.length state.tilemap)) 1;
2571 sep ();
2572 src#caption "Layout" 0;
2573 src#caption2 "Dimension"
2574 (fun () ->
2575 Printf.sprintf "%dx%d (virtual %dx%d)"
2576 state.winw state.winh
2577 state.w state.maxy)
2579 if conf.debug
2580 then
2581 src#caption2 "Position" (fun () ->
2582 Printf.sprintf "%dx%d" state.x state.y
2584 else
2585 src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
2587 sep ();
2588 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
2589 "Save these parameters as global defaults at exit"
2590 (fun () -> conf.bedefault)
2591 (fun v -> conf.bedefault <- v);
2593 sep ();
2594 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2595 src#bool ~offset:0 ~btos "Extended parameters"
2596 (fun () -> !showextended)
2597 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2598 if !showextended
2599 then (
2600 src#bool "checkers"
2601 (fun () -> conf.checkers)
2602 (fun v -> conf.checkers <- v; setcheckers v);
2603 src#bool "update cursor"
2604 (fun () -> conf.updatecurs)
2605 (fun v -> conf.updatecurs <- v);
2606 src#bool "scroll-bar on the left"
2607 (fun () -> conf.leftscroll)
2608 (fun v -> conf.leftscroll <- v);
2609 src#bool "verbose"
2610 (fun () -> conf.verbose)
2611 (fun v -> conf.verbose <- v);
2612 src#bool "invert colors"
2613 (fun () -> conf.invert)
2614 (fun v -> conf.invert <- v);
2615 src#bool "max fit"
2616 (fun () -> conf.maxhfit)
2617 (fun v -> conf.maxhfit <- v);
2618 src#bool "pax mode"
2619 (fun () -> conf.pax != None)
2620 (fun v ->
2621 if v
2622 then conf.pax <- Some (now ())
2623 else conf.pax <- None);
2624 src#string "uri launcher"
2625 (fun () -> conf.urilauncher)
2626 (fun v -> conf.urilauncher <- v);
2627 src#string "path launcher"
2628 (fun () -> conf.pathlauncher)
2629 (fun v -> conf.pathlauncher <- v);
2630 src#string "tile size"
2631 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2632 (fun v ->
2634 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2635 conf.tilew <- max 64 w;
2636 conf.tileh <- max 64 h;
2637 flushtiles ();
2638 with exn ->
2639 state.text <- Printf.sprintf "bad tile size `%s': %s"
2640 v @@ exntos exn
2642 src#int "texture count"
2643 (fun () -> conf.texcount)
2644 (fun v ->
2645 if realloctexts v
2646 then conf.texcount <- v
2647 else impmsg "failed to set texture count please retry later"
2649 src#int "slice height"
2650 (fun () -> conf.sliceheight)
2651 (fun v ->
2652 conf.sliceheight <- v;
2653 wcmd "sliceh %d" conf.sliceheight;
2655 src#int "anti-aliasing level"
2656 (fun () -> conf.aalevel)
2657 (fun v ->
2658 conf.aalevel <- bound v 0 8;
2659 state.anchor <- getanchor ();
2660 opendoc state.path state.password;
2662 src#string "page scroll scaling factor"
2663 (fun () -> string_of_float conf.pgscale)
2664 (fun v ->
2666 let s = float_of_string v in
2667 conf.pgscale <- s
2668 with exn ->
2669 state.text <- Printf.sprintf
2670 "bad page scroll scaling factor `%s': %s" v
2671 @@ exntos exn
2673 src#int "ui font size"
2674 (fun () -> fstate.fontsize)
2675 (fun v -> setfontsize (bound v 5 100));
2676 src#int "hint font size"
2677 (fun () -> conf.hfsize)
2678 (fun v -> conf.hfsize <- bound v 5 100);
2679 src#string "trim fuzz"
2680 (fun () -> irect_to_string conf.trimfuzz)
2681 (fun v ->
2683 conf.trimfuzz <- irect_of_string v;
2684 if conf.trimmargins
2685 then settrim true conf.trimfuzz;
2686 with exn ->
2687 state.text <- Printf.sprintf "bad irect `%s': %s" v
2688 @@ exntos exn
2690 src#string "selection command"
2691 (fun () -> conf.selcmd)
2692 (fun v -> conf.selcmd <- v);
2693 src#string "synctex command"
2694 (fun () -> conf.stcmd)
2695 (fun v -> conf.stcmd <- v);
2696 src#string "pax command"
2697 (fun () -> conf.paxcmd)
2698 (fun v -> conf.paxcmd <- v);
2699 src#string "ask password command"
2700 (fun () -> conf.passcmd)
2701 (fun v -> conf.passcmd <- v);
2702 src#string "save path command"
2703 (fun () -> conf.savecmd)
2704 (fun v -> conf.savecmd <- v);
2705 src#colorspace "color space"
2706 (fun () -> CSTE.to_string conf.colorspace)
2707 (fun v ->
2708 conf.colorspace <- CSTE.of_int v;
2709 wcmd "cs %d" v;
2710 load state.layout;
2712 src#paxmark "pax mark method"
2713 (fun () -> MTE.to_string conf.paxmark)
2714 (fun v -> conf.paxmark <- MTE.of_int v);
2715 if bousable () && !opengl_has_pbo
2716 then
2717 src#bool "use PBO"
2718 (fun () -> conf.usepbo)
2719 (fun v -> conf.usepbo <- v);
2720 src#bool "mouse wheel scrolls pages"
2721 (fun () -> conf.wheelbypage)
2722 (fun v -> conf.wheelbypage <- v);
2723 src#bool "open remote links in a new instance"
2724 (fun () -> conf.riani)
2725 (fun v -> conf.riani <- v);
2726 src#bool "edit annotations inline"
2727 (fun () -> conf.annotinline)
2728 (fun v -> conf.annotinline <- v);
2729 src#bool "coarse positioning in presentation mode"
2730 (fun () -> conf.coarseprespos)
2731 (fun v -> conf.coarseprespos <- v);
2732 src#bool "use document CSS"
2733 (fun () -> conf.usedoccss)
2734 (fun v ->
2735 conf.usedoccss <- v;
2736 state.anchor <- getanchor ();
2737 opendoc state.path state.password;
2739 src#bool ~btos "colors"
2740 (fun () -> !showcolors)
2741 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2742 if !showcolors
2743 then (
2744 colorp " background"
2745 (fun () -> conf.bgcolor)
2746 (fun v -> conf.bgcolor <- v);
2747 rgba " scrollbar"
2748 (fun () -> conf.sbarcolor)
2749 (fun v -> conf.sbarcolor <- v);
2750 rgba " scrollbar handle"
2751 (fun () -> conf.sbarhndlcolor)
2752 (fun v -> conf.sbarhndlcolor <- v);
2756 sep ();
2757 src#caption "Document" 0;
2758 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
2759 src#caption2 "Pages"
2760 (fun () -> string_of_int state.pagecount) 1;
2761 src#caption2 "Dimensions"
2762 (fun () -> string_of_int (List.length state.pdims)) 1;
2763 if nonemptystr conf.css
2764 then src#caption2 "CSS" (fun () -> conf.css) 1;
2765 if conf.trimmargins
2766 then (
2767 sep ();
2768 src#caption "Trimmed margins" 0;
2769 src#caption2 "Dimensions"
2770 (fun () -> string_of_int (List.length state.pdims)) 1;
2773 sep ();
2774 src#caption "OpenGL" 0;
2775 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
2776 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
2778 sep ();
2779 src#caption "Location" 0;
2780 if nonemptystr state.origin
2781 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
2782 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
2784 src#reset prevmode prevuioh;
2786 fun () ->
2787 state.text <- E.s;
2788 resetmstate ();
2789 let prevmode = state.mode
2790 and prevuioh = state.uioh in
2791 fillsrc prevmode prevuioh;
2792 let source = (src :> lvsource) in
2793 let modehash = findkeyhash conf "info" in
2794 state.uioh <-
2795 coe (object (self)
2796 inherit listview ~zebra:false ~helpmode:false
2797 ~source ~trusted:true ~modehash as super
2798 val mutable m_prevmemused = 0
2799 method! infochanged = function
2800 | Memused ->
2801 if m_prevmemused != state.memused
2802 then (
2803 m_prevmemused <- state.memused;
2804 postRedisplay "memusedchanged";
2806 | Pdim -> postRedisplay "pdimchanged"
2807 | Docinfo -> fillsrc prevmode prevuioh
2809 method! key key mask =
2810 if not (Wsi.withctrl mask)
2811 then
2812 match [@warning "-4"] Wsi.kc2kt key with
2813 | Keys.Left -> coe (self#updownlevel ~-1)
2814 | Keys.Right -> coe (self#updownlevel 1)
2815 | _ -> super#key key mask
2816 else super#key key mask
2817 end);
2818 postRedisplay "info";
2821 let enterhelpmode =
2822 let source =
2823 (object
2824 inherit lvsourcebase
2825 method getitemcount = Array.length state.help
2826 method getitem n =
2827 let s, l, _ = state.help.(n) in
2828 (s, l)
2830 method exit ~uioh ~cancel ~active ~first ~pan =
2831 let optuioh =
2832 if not cancel
2833 then (
2834 match state.help.(active) with
2835 | _, _, Action f -> Some (f uioh)
2836 | _, _, Noaction -> Some uioh
2838 else None
2840 m_active <- active;
2841 m_first <- first;
2842 m_pan <- pan;
2843 optuioh
2845 method hasaction n =
2846 match state.help.(n) with
2847 | _, _, Action _ -> true
2848 | _, _, Noaction -> false
2850 initializer
2851 m_active <- -1
2852 end)
2853 in fun () ->
2854 let modehash = findkeyhash conf "help" in
2855 resetmstate ();
2856 state.uioh <- coe (new listview
2857 ~zebra:false ~helpmode:true
2858 ~source ~trusted:true ~modehash);
2859 postRedisplay "help";
2862 let entermsgsmode =
2863 let msgsource =
2864 (object
2865 inherit lvsourcebase
2866 val mutable m_items = E.a
2868 method getitemcount = 1 + Array.length m_items
2870 method getitem n =
2871 if n = 0
2872 then "[Clear]", 0
2873 else m_items.(n-1), 0
2875 method exit ~uioh ~cancel ~active ~first ~pan =
2876 ignore uioh;
2877 if not cancel
2878 then (
2879 if active = 0
2880 then Buffer.clear state.errmsgs;
2882 m_active <- active;
2883 m_first <- first;
2884 m_pan <- pan;
2885 None
2887 method hasaction n =
2888 n = 0
2890 method reset =
2891 state.newerrmsgs <- false;
2892 let l = Str.split Utils.Re.crlf (Buffer.contents state.errmsgs) in
2893 m_items <- Array.of_list l
2895 initializer
2896 m_active <- 0
2897 end)
2898 in fun () ->
2899 state.text <- E.s;
2900 resetmstate ();
2901 msgsource#reset;
2902 let source = (msgsource :> lvsource) in
2903 let modehash = findkeyhash conf "listview" in
2904 state.uioh <-
2905 coe (object
2906 inherit listview ~zebra:false ~helpmode:false
2907 ~source ~trusted:false ~modehash as super
2908 method! display =
2909 if state.newerrmsgs
2910 then msgsource#reset;
2911 super#display
2912 end);
2913 postRedisplay "msgs";
2916 let getusertext s =
2917 let editor = getenvwithdef "EDITOR" E.s in
2918 if emptystr editor
2919 then E.s
2920 else
2921 let tmppath = Filename.temp_file "llpp" "note" in
2922 if nonemptystr s
2923 then (
2924 let oc = open_out tmppath in
2925 output_string oc s;
2926 close_out oc;
2928 let execstr = editor ^ " " ^ tmppath in
2929 let s =
2930 match spawn execstr [] with
2931 | exception exn ->
2932 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
2934 | pid ->
2935 match Unix.waitpid [] pid with
2936 | exception exn ->
2937 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
2939 | (_pid, status) ->
2940 match status with
2941 | Unix.WEXITED 0 -> filecontents tmppath
2942 | Unix.WEXITED n ->
2943 impmsg "editor process(%s) exited abnormally: %d" execstr n;
2945 | Unix.WSIGNALED n ->
2946 impmsg "editor process(%s) was killed by signal %d" execstr n;
2948 | Unix.WSTOPPED n ->
2949 impmsg "editor(%s) process was stopped by signal %d" execstr n;
2952 match Unix.unlink tmppath with
2953 | exception exn ->
2954 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
2956 | () -> s
2959 let enterannotmode opaque slinkindex =
2960 let msgsource =
2961 (object
2962 inherit lvsourcebase
2963 val mutable m_text = E.s
2964 val mutable m_items = E.a
2966 method getitemcount = Array.length m_items
2968 method getitem n =
2969 let label, _func = m_items.(n) in
2970 label, 0
2972 method exit ~uioh ~cancel ~active ~first ~pan =
2973 ignore (uioh, first, pan);
2974 if not cancel
2975 then (
2976 let _label, func = m_items.(active) in
2977 func ()
2979 None
2981 method hasaction n = nonemptystr @@ fst m_items.(n)
2983 method reset s =
2984 let rec split accu b i =
2985 let p = b+i in
2986 if p = String.length s
2987 then (String.sub s b (p-b), unit) :: accu
2988 else
2989 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2990 then
2991 let ss = if i = 0 then E.s else String.sub s b i in
2992 split ((ss, unit)::accu) (p+1) 0
2993 else
2994 split accu b (i+1)
2996 let cleanup () =
2997 wcmd "freepage %s" (~> opaque);
2998 let keys =
2999 Hashtbl.fold (fun key opaque' accu ->
3000 if opaque' = opaque'
3001 then key :: accu else accu) state.pagemap []
3003 List.iter (Hashtbl.remove state.pagemap) keys;
3004 flushtiles ();
3005 gotoxy state.x state.y
3007 let dele () =
3008 delannot opaque slinkindex;
3009 cleanup ();
3011 let edit inline () =
3012 let update s =
3013 if emptystr s
3014 then dele ()
3015 else (
3016 modannot opaque slinkindex s;
3017 cleanup ();
3020 if inline
3021 then
3022 let mode = state.mode in
3023 state.mode <-
3024 Textentry (
3025 ("annotation: ", m_text, None, textentry, update, true),
3026 fun _ -> state.mode <- mode);
3027 state.text <- E.s;
3028 enttext ();
3029 else
3030 let s = getusertext m_text in
3031 update s
3033 m_text <- s;
3034 m_items <-
3035 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
3036 :: ("[Delete]", dele)
3037 :: ("[Edit]", edit conf.annotinline)
3038 :: (E.s, unit)
3039 :: split [] 0 0 |> List.rev |> Array.of_list
3041 initializer
3042 m_active <- 0
3043 end)
3045 state.text <- E.s;
3046 let s = getannotcontents opaque slinkindex in
3047 resetmstate ();
3048 msgsource#reset s;
3049 let source = (msgsource :> lvsource) in
3050 let modehash = findkeyhash conf "listview" in
3051 state.uioh <- coe (object
3052 inherit listview ~zebra:false ~helpmode:false
3053 ~source ~trusted:false ~modehash
3054 end);
3055 postRedisplay "enterannotmode";
3058 let gotoremote spec =
3059 let filename, dest = splitatchar spec '#' in
3060 let getpath filename =
3061 let path =
3062 if nonemptystr filename
3063 then
3064 if Filename.is_relative filename
3065 then
3066 let dir = Filename.dirname state.path in
3067 let dir =
3068 if Filename.is_implicit dir
3069 then Filename.concat (Sys.getcwd ()) dir
3070 else dir
3072 Filename.concat dir filename
3073 else filename
3074 else E.s
3076 if Sys.file_exists path
3077 then path
3078 else E.s
3080 let path = getpath filename in
3081 let dospawn lcmd =
3082 if conf.riani
3083 then
3084 let cmd = Lazy.force_val lcmd in
3085 match spawn cmd with
3086 | _pid -> ()
3087 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
3088 else
3089 let anchor = getanchor () in
3090 let ranchor = state.path, state.password, anchor, state.origin in
3091 state.origin <- E.s;
3092 state.ranchors <- ranchor :: state.ranchors;
3093 opendoc path E.s;
3095 if substratis spec 0 "page="
3096 then
3097 match Scanf.sscanf spec "page=%d" (fun n -> n) with
3098 | pageno ->
3099 state.anchor <- (pageno, 0.0, 0.0);
3100 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
3101 | exception exn ->
3102 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
3103 else (
3104 state.nameddest <- dest;
3105 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
3109 let gotounder = function
3110 | Ulinkuri s when isexternallink s ->
3111 if substratis s 0 "file://"
3112 then gotoremote @@ String.sub s 7 (String.length s - 7)
3113 else Help.gotouri conf.urilauncher s
3114 | Ulinkuri s ->
3115 let pageno, x, y = uritolocation s in
3116 addnav ();
3117 gotopagexy pageno x y
3118 | Utext _ | Unone -> ()
3119 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
3122 let gotooutline (_, _, kind) =
3123 match kind with
3124 | Onone -> ()
3125 | Oanchor anchor ->
3126 let (pageno, y, _) = anchor in
3127 let y = getanchory
3128 (if conf.presentation then (pageno, y, 1.0) else anchor)
3130 addnav ();
3131 gotoxy state.x y
3132 | Ouri uri -> gotounder (Ulinkuri uri)
3133 | Olaunch _cmd -> failwith "gotounder (Ulaunch cmd)"
3134 | Oremote _remote -> failwith "gotounder (Uremote remote)"
3135 | Ohistory hist -> gotohist hist
3136 | Oremotedest _remotedest -> failwith "gotounder (Uremotedest remotedest)"
3139 class outlinesoucebase fetchoutlines = object (self)
3140 inherit lvsourcebase
3141 val mutable m_items = E.a
3142 val mutable m_minfo = E.a
3143 val mutable m_orig_items = E.a
3144 val mutable m_orig_minfo = E.a
3145 val mutable m_narrow_patterns = []
3146 val mutable m_gen = -1
3148 method getitemcount = Array.length m_items
3150 method getitem n =
3151 let s, n, _ = m_items.(n) in
3152 (s, n+0)
3154 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
3155 ignore (uioh, first);
3156 let items, minfo =
3157 if m_narrow_patterns = []
3158 then m_orig_items, m_orig_minfo
3159 else m_items, m_minfo
3161 m_pan <- pan;
3162 if not cancel
3163 then (
3164 m_items <- items;
3165 m_minfo <- minfo;
3166 gotooutline m_items.(active);
3168 else (
3169 m_items <- items;
3170 m_minfo <- minfo;
3172 None
3174 method hasaction (_:int) = true
3176 method greetmsg =
3177 if Array.length m_items != Array.length m_orig_items
3178 then
3179 let s =
3180 match m_narrow_patterns with
3181 | one :: [] -> one
3182 | many -> String.concat Utf8syms.ellipsis (List.rev many)
3184 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
3185 else E.s
3187 method statestr =
3188 match m_narrow_patterns with
3189 | [] -> E.s
3190 | one :: [] -> one
3191 | head :: _ -> Utf8syms.ellipsis ^ head
3193 method narrow pattern =
3194 match Str.regexp_case_fold pattern with
3195 | exception _ -> ()
3196 | re ->
3197 let rec loop accu minfo n =
3198 if n = -1
3199 then (
3200 m_items <- Array.of_list accu;
3201 m_minfo <- Array.of_list minfo;
3203 else
3204 let (s, _, _) as o = m_items.(n) in
3205 let accu, minfo =
3206 match Str.search_forward re s 0 with
3207 | exception Not_found -> accu, minfo
3208 | first -> o :: accu, (first, Str.match_end ()) :: minfo
3210 loop accu minfo (n-1)
3212 loop [] [] (Array.length m_items - 1)
3214 method! getminfo = m_minfo
3216 method denarrow =
3217 m_orig_items <- fetchoutlines ();
3218 m_minfo <- m_orig_minfo;
3219 m_items <- m_orig_items
3221 method add_narrow_pattern pattern =
3222 m_narrow_patterns <- pattern :: m_narrow_patterns
3224 method del_narrow_pattern =
3225 match m_narrow_patterns with
3226 | _ :: rest -> m_narrow_patterns <- rest
3227 | [] -> ()
3229 method renarrow =
3230 self#denarrow;
3231 match m_narrow_patterns with
3232 | pattern :: [] -> self#narrow pattern; pattern
3233 | list ->
3234 List.fold_left (fun accu pattern ->
3235 self#narrow pattern;
3236 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
3238 method calcactive (_:anchor) = 0
3240 method reset anchor items =
3241 if state.gen != m_gen
3242 then (
3243 m_orig_items <- items;
3244 m_items <- items;
3245 m_narrow_patterns <- [];
3246 m_minfo <- E.a;
3247 m_orig_minfo <- E.a;
3248 m_gen <- state.gen;
3250 else (
3251 if items != m_orig_items
3252 then (
3253 m_orig_items <- items;
3254 if m_narrow_patterns == []
3255 then m_items <- items;
3258 let active = self#calcactive anchor in
3259 m_active <- active;
3260 m_first <- firstof m_first active
3264 let outlinesource fetchoutlines =
3265 (object
3266 inherit outlinesoucebase fetchoutlines
3267 method! calcactive anchor =
3268 let rely = getanchory anchor in
3269 let rec loop n best bestd =
3270 if n = Array.length m_items
3271 then best
3272 else
3273 let _, _, kind = m_items.(n) in
3274 match kind with
3275 | Oanchor anchor ->
3276 let orely = getanchory anchor in
3277 let d = abs (orely - rely) in
3278 if d < bestd
3279 then loop (n+1) n d
3280 else loop (n+1) best bestd
3281 | Onone | Oremote _ | Olaunch _
3282 | Oremotedest _ | Ouri _ | Ohistory _ ->
3283 loop (n+1) best bestd
3285 loop 0 ~-1 max_int
3286 end)
3289 let enteroutlinemode, enterbookmarkmode, enterhistmode =
3290 let mkselector sourcetype =
3291 let fetchoutlines () =
3292 match sourcetype with
3293 | `bookmarks -> Array.of_list state.bookmarks
3294 | `outlines -> state.outlines
3295 | `history -> genhistoutlines () |> Array.of_list
3297 let source =
3298 if sourcetype = `history
3299 then new outlinesoucebase fetchoutlines
3300 else outlinesource fetchoutlines
3302 (fun errmsg ->
3303 let outlines = fetchoutlines () in
3304 if Array.length outlines = 0
3305 then showtext ' ' errmsg
3306 else (
3307 resetmstate ();
3308 Wsi.setcursor Wsi.CURSOR_INHERIT;
3309 let anchor = getanchor () in
3310 source#reset anchor outlines;
3311 state.text <- source#greetmsg;
3312 state.uioh <-
3313 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
3314 postRedisplay "enter selector";
3318 let mkenter sourcetype errmsg =
3319 let enter = mkselector sourcetype in
3320 fun () -> enter errmsg
3322 ( mkenter `outlines "document has no outline"
3323 , mkenter `bookmarks "document has no bookmarks (yet)"
3324 , mkenter `history "history is empty" )
3327 let quickbookmark ?title () =
3328 match state.layout with
3329 | [] -> ()
3330 | l :: _ ->
3331 let title =
3332 match title with
3333 | None ->
3334 Unix.(
3335 let tm = localtime (now ()) in
3336 Printf.sprintf
3337 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
3338 (l.pageno+1)
3339 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
3341 | Some title -> title
3343 state.bookmarks <- (title, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3346 let setautoscrollspeed step goingdown =
3347 let incr = max 1 ((abs step) / 2) in
3348 let incr = if goingdown then incr else -incr in
3349 let astep = boundastep state.winh (step + incr) in
3350 state.autoscroll <- Some astep;
3353 let canpan () =
3354 match conf.columns with
3355 | Csplit _ -> true
3356 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
3359 let panbound x = bound x (-state.w) state.winw;;
3361 let existsinrow pageno (columns, coverA, coverB) p =
3362 let last = ((pageno - coverA) mod columns) + columns in
3363 let rec any = function
3364 | [] -> false
3365 | l :: rest ->
3366 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
3367 then p l
3368 else (
3369 if not (p l)
3370 then (if l.pageno = last then false else any rest)
3371 else true
3374 any state.layout
3377 let nextpage () =
3378 match state.layout with
3379 | [] ->
3380 let pageno = page_of_y state.y in
3381 gotoxy state.x (getpagey (pageno+1))
3382 | l :: rest ->
3383 match conf.columns with
3384 | Csingle _ ->
3385 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3386 then
3387 let y = clamp (pgscale state.winh) in
3388 gotoxy state.x y
3389 else
3390 let pageno = min (l.pageno+1) (state.pagecount-1) in
3391 gotoxy state.x (getpagey pageno)
3392 | Cmulti ((c, _, _) as cl, _) ->
3393 if conf.presentation
3394 && (existsinrow l.pageno cl
3395 (fun l -> l.pageh > l.pagey + l.pagevh))
3396 then
3397 let y = clamp (pgscale state.winh) in
3398 gotoxy state.x y
3399 else
3400 let pageno = min (l.pageno+c) (state.pagecount-1) in
3401 gotoxy state.x (getpagey pageno)
3402 | Csplit (n, _) ->
3403 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
3404 then
3405 let pagey, pageh = getpageyh l.pageno in
3406 let pagey = pagey + pageh * l.pagecol in
3407 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3408 gotoxy state.x (pagey + pageh + ips)
3411 let prevpage () =
3412 match state.layout with
3413 | [] ->
3414 let pageno = page_of_y state.y in
3415 gotoxy state.x (getpagey (pageno-1))
3416 | l :: _ ->
3417 match conf.columns with
3418 | Csingle _ ->
3419 if conf.presentation && l.pagey != 0
3420 then
3421 gotoxy state.x (clamp (pgscale ~-(state.winh)))
3422 else
3423 let pageno = max 0 (l.pageno-1) in
3424 gotoxy state.x (getpagey pageno)
3425 | Cmulti ((c, _, coverB) as cl, _) ->
3426 if conf.presentation &&
3427 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3428 then
3429 gotoxy state.x (clamp (pgscale ~-(state.winh)))
3430 else
3431 let decr =
3432 if l.pageno = state.pagecount - coverB
3433 then 1
3434 else c
3436 let pageno = max 0 (l.pageno-decr) in
3437 gotoxy state.x (getpagey pageno)
3438 | Csplit (n, _) ->
3439 let y =
3440 if l.pagecol = 0
3441 then
3442 if l.pageno = 0
3443 then l.pagey
3444 else
3445 let pageno = max 0 (l.pageno-1) in
3446 let pagey, pageh = getpageyh pageno in
3447 pagey + (n-1)*pageh
3448 else
3449 let pagey, pageh = getpageyh l.pageno in
3450 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3452 gotoxy state.x y
3455 let save () =
3456 if emptystr conf.savecmd
3457 then adderrmsg "savepath-command is empty"
3458 "don't know where to save modified document"
3459 else
3460 let savecmd = Str.global_replace Utils.Re.percent state.path conf.savecmd in
3461 let path =
3462 getcmdoutput
3463 (fun exn ->
3464 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
3465 savecmd
3467 if nonemptystr path
3468 then
3469 let tmp = path ^ ".tmp" in
3470 savedoc tmp;
3471 Unix.rename tmp path;
3474 let viewkeyboard key mask =
3475 let enttext te =
3476 let mode = state.mode in
3477 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3478 state.text <- E.s;
3479 enttext ();
3480 postRedisplay "view:enttext"
3482 let ctrl = Wsi.withctrl mask in
3483 let open Keys in
3484 match Wsi.kc2kt key with
3485 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
3487 | Ascii 'Q' -> exit 0
3489 | Ascii 'W' ->
3490 if hasunsavedchanges ()
3491 then save ()
3493 | Insert ->
3494 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
3495 then (
3496 state.mode <- (
3497 match state.lnava with
3498 | None -> LinkNav (Ltgendir 0)
3499 | Some pn -> LinkNav (Ltexact pn)
3501 gotoxy state.x state.y;
3503 else impmsg "keyboard link navigation does not work under rotation"
3505 | Escape | Ascii 'q' ->
3506 begin match state.mstate with
3507 | Mzoomrect _ ->
3508 resetmstate ();
3509 postRedisplay "kill rect";
3510 | Msel _
3511 | Mpan _
3512 | Mscrolly | Mscrollx
3513 | Mzoom _
3514 | Mnone ->
3515 begin match state.mode with
3516 | LinkNav ln ->
3517 begin match ln with
3518 | Ltexact pl -> state.lnava <- Some pl
3519 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
3520 end;
3521 state.mode <- View;
3522 postRedisplay "esc leave linknav"
3523 | Birdseye _ | Textentry _ | View ->
3524 match state.ranchors with
3525 | [] -> raise Quit
3526 | (path, password, anchor, origin) :: rest ->
3527 state.ranchors <- rest;
3528 state.anchor <- anchor;
3529 state.origin <- origin;
3530 state.nameddest <- E.s;
3531 opendoc path password
3532 end;
3533 end;
3535 | Backspace ->
3536 addnavnorc ();
3537 gotoxy state.x (getnav ~-1)
3539 | Ascii 'o' ->
3540 enteroutlinemode ()
3542 | Ascii 'H' ->
3543 enterhistmode ()
3545 | Ascii 'u' ->
3546 state.rects <- [];
3547 state.text <- E.s;
3548 Hashtbl.iter (fun _ opaque ->
3549 clearmark opaque;
3550 Hashtbl.clear state.prects) state.pagemap;
3551 postRedisplay "dehighlight";
3553 | Ascii (('/' | '?') as c) ->
3554 let ondone isforw s =
3555 cbput state.hists.pat s;
3556 state.searchpattern <- s;
3557 search s isforw
3559 let s = String.make 1 c in
3560 enttext (s, E.s, Some (onhist state.hists.pat),
3561 textentry, ondone (c = '/'), true)
3563 | Ascii '+' | Ascii '=' when ctrl ->
3564 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3565 pivotzoom (conf.zoom +. incr)
3567 | Ascii '+' ->
3568 let ondone s =
3569 let n =
3570 try int_of_string s with exn ->
3571 state.text <-
3572 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3573 max_int
3575 if n != max_int
3576 then (
3577 conf.pagebias <- n;
3578 state.text <- "page bias is now " ^ string_of_int n;
3581 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3583 | Ascii '-' when ctrl ->
3584 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3585 pivotzoom (max 0.01 (conf.zoom -. decr))
3587 | Ascii '-' ->
3588 let ondone msg = state.text <- msg in
3589 enttext ("option: ", E.s, None,
3590 optentry state.mode, ondone, true)
3592 | Ascii '0' when ctrl ->
3593 if conf.zoom = 1.0
3594 then gotoxy 0 state.y
3595 else setzoom 1.0
3597 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3598 let cols =
3599 match conf.columns with
3600 | Csingle _ | Cmulti _ -> 1
3601 | Csplit (n, _) -> n
3603 let h = state.winh -
3604 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3606 let zoom = zoomforh state.winw h 0 cols in
3607 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3608 then setzoom zoom
3610 | Ascii '3' when ctrl ->
3611 let fm =
3612 match conf.fitmodel with
3613 | FitWidth -> FitProportional
3614 | FitProportional -> FitPage
3615 | FitPage -> FitWidth
3617 state.text <- "fit model: " ^ FMTE.to_string fm;
3618 reqlayout conf.angle fm
3620 | Ascii '4' when ctrl ->
3621 let zoom = getmaxw () /. float state.winw in
3622 if zoom > 0.0 then setzoom zoom
3624 | Fn 9 ->
3625 togglebirdseye ()
3627 | Ascii '9' when ctrl ->
3628 togglebirdseye ()
3630 | Ascii ('0'..'9' as c) when not ctrl ->
3631 let ondone s =
3632 let n =
3633 try int_of_string s with exn ->
3634 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3637 if n >= 0
3638 then (
3639 addnav ();
3640 cbput state.hists.pag (string_of_int n);
3641 gotopage1 (n + conf.pagebias - 1) 0;
3644 let pageentry text = function [@warning "-4"]
3645 | Keys.Ascii 'g' -> TEdone text
3646 | key -> intentry text key
3648 let text = String.make 1 c in
3649 enttext (":", text, Some (onhist state.hists.pag),
3650 pageentry, ondone, true)
3652 | Ascii 'b' ->
3653 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3654 postRedisplay "toggle scrollbar";
3656 | Ascii 'B' ->
3657 state.bzoom <- not state.bzoom;
3658 state.rects <- [];
3659 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
3661 | Ascii 'l' ->
3662 conf.hlinks <- not conf.hlinks;
3663 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3664 postRedisplay "toggle highlightlinks";
3666 | Ascii 'F' ->
3667 if conf.angle mod 360 = 0
3668 then (
3669 state.glinks <- true;
3670 let mode = state.mode in
3671 state.mode <-
3672 Textentry (
3673 (":", E.s, None, linknentry, linknact gotounder, false),
3674 (fun _ ->
3675 state.glinks <- false;
3676 state.mode <- mode)
3678 state.text <- E.s;
3679 postRedisplay "view:linkent(F)"
3681 else impmsg "hint mode does not work under rotation"
3683 | Ascii 'y' ->
3684 state.glinks <- true;
3685 let mode = state.mode in
3686 state.mode <-
3687 Textentry (
3688 (":", E.s, None, linknentry,
3689 linknact (fun under ->
3690 selstring conf.selcmd (undertext under)), false),
3691 (fun _ ->
3692 state.glinks <- false;
3693 state.mode <- mode)
3695 state.text <- E.s;
3696 postRedisplay "view:linkent"
3698 | Ascii 'a' ->
3699 begin match state.autoscroll with
3700 | Some step ->
3701 conf.autoscrollstep <- step;
3702 state.autoscroll <- None
3703 | None ->
3704 state.autoscroll <- Some conf.autoscrollstep;
3705 state.slideshow <- state.slideshow land lnot 2
3708 | Ascii 'p' when ctrl ->
3709 launchpath () (* XXX where do error messages go? *)
3711 | Ascii 'P' ->
3712 setpresentationmode (not conf.presentation);
3713 showtext ' ' ("presentation mode " ^
3714 if conf.presentation then "on" else "off");
3716 | Ascii 'f' ->
3717 if List.mem Wsi.Fullscreen state.winstate
3718 then Wsi.reshape conf.cwinw conf.cwinh
3719 else Wsi.fullscreen ()
3721 | Ascii ('p'|'N') ->
3722 search state.searchpattern false
3724 | Ascii 'n' | Fn 3 ->
3725 search state.searchpattern true
3727 | Ascii 't' ->
3728 begin match state.layout with
3729 | [] -> ()
3730 | l :: _ ->
3731 gotoxy state.x (getpagey l.pageno)
3734 | Ascii ' ' ->
3735 nextpage ()
3737 | Delete ->
3738 prevpage ()
3740 | Ascii '=' ->
3741 showtext ' ' (describe_layout state.layout);
3743 | Ascii 'w' ->
3744 begin match state.layout with
3745 | [] -> ()
3746 | l :: _ ->
3747 Wsi.reshape l.pagew l.pageh;
3748 postRedisplay "w"
3751 | Ascii '\'' ->
3752 enterbookmarkmode ()
3754 | Ascii 'h' | Fn 1 ->
3755 enterhelpmode ()
3757 | Ascii 'i' ->
3758 enterinfomode ()
3760 | Ascii 'e' when Buffer.length state.errmsgs > 0 ->
3761 entermsgsmode ()
3763 | Ascii 'm' ->
3764 let ondone s =
3765 match state.layout with
3766 | l :: _ when nonemptystr s ->
3767 state.bookmarks <- (s, 0, Oanchor (getanchor1 l)) :: state.bookmarks
3768 | _ -> ()
3770 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3772 | Ascii '~' ->
3773 quickbookmark ();
3774 showtext ' ' "Quick bookmark added";
3776 | Ascii 'x' -> state.roam ()
3778 | Ascii ('<'|'>' as c) ->
3779 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3781 | Ascii ('['|']' as c) ->
3782 conf.colorscale <-
3783 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3784 postRedisplay "brightness";
3786 | Ascii 'c' when state.mode = View ->
3787 if Wsi.withalt mask
3788 then (
3789 if conf.zoom > 1.0
3790 then
3791 let m = (state.winw - state.w) / 2 in
3792 gotoxy m state.y
3794 else
3795 let (c, a, b), z =
3796 match state.prevcolumns with
3797 | None -> (1, 0, 0), 1.0
3798 | Some (columns, z) ->
3799 let cab =
3800 match columns with
3801 | Csplit (c, _) -> -c, 0, 0
3802 | Cmulti ((c, a, b), _) -> c, a, b
3803 | Csingle _ -> 1, 0, 0
3805 cab, z
3807 setcolumns View c a b;
3808 setzoom z
3810 | Down | Up when ctrl && Wsi.withshift mask ->
3811 let zoom, x = state.prevzoom in
3812 setzoom zoom;
3813 state.x <- x;
3815 | Ascii 'k' | Up ->
3816 begin match state.autoscroll with
3817 | None ->
3818 begin match state.mode with
3819 | Birdseye beye -> upbirdseye 1 beye
3820 | Textentry _ | View | LinkNav _ ->
3821 if ctrl
3822 then gotoxy state.x (clamp ~-(state.winh/2))
3823 else (
3824 if not (Wsi.withshift mask) && conf.presentation
3825 then prevpage ()
3826 else gotoxy state.x (clamp (-conf.scrollstep))
3829 | Some n ->
3830 setautoscrollspeed n false
3833 | Ascii 'j' | Down ->
3834 begin match state.autoscroll with
3835 | None ->
3836 begin match state.mode with
3837 | Birdseye beye -> downbirdseye 1 beye
3838 | Textentry _ | View | LinkNav _ ->
3839 if ctrl
3840 then gotoxy state.x (clamp (state.winh/2))
3841 else (
3842 if not (Wsi.withshift mask) && conf.presentation
3843 then nextpage ()
3844 else gotoxy state.x (clamp (conf.scrollstep))
3847 | Some n ->
3848 setautoscrollspeed n true
3851 | Left | Right when not (Wsi.withalt mask) ->
3852 if canpan ()
3853 then
3854 let dx =
3855 if ctrl
3856 then state.winw / 2
3857 else conf.hscrollstep
3859 let dx =
3860 let pv = Wsi.kc2kt key in
3861 if pv = Keys.Left then dx else -dx
3863 gotoxy (panbound (state.x + dx)) state.y
3864 else (
3865 state.text <- E.s;
3866 postRedisplay "left/right"
3869 | Prior ->
3870 let y =
3871 if ctrl
3872 then
3873 match state.layout with
3874 | [] -> state.y
3875 | l :: _ -> state.y - l.pagey
3876 else
3877 clamp (pgscale (-state.winh))
3879 gotoxy state.x y
3881 | Next ->
3882 let y =
3883 if ctrl
3884 then
3885 match List.rev state.layout with
3886 | [] -> state.y
3887 | l :: _ -> getpagey l.pageno
3888 else
3889 clamp (pgscale state.winh)
3891 gotoxy state.x y
3893 | Ascii 'g' | Home ->
3894 addnav ();
3895 gotoxy 0 0
3896 | Ascii 'G' | End ->
3897 addnav ();
3898 gotoxy 0 (clamp state.maxy)
3900 | Right when Wsi.withalt mask ->
3901 addnavnorc ();
3902 gotoxy state.x (getnav 1)
3903 | Left when Wsi.withalt mask ->
3904 addnavnorc ();
3905 gotoxy state.x (getnav ~-1)
3907 | Ascii 'r' ->
3908 reload ()
3910 | Ascii 'v' when conf.debug ->
3911 state.rects <- [];
3912 List.iter (fun l ->
3913 match getopaque l.pageno with
3914 | None -> ()
3915 | Some opaque ->
3916 let x0, y0, x1, y1 = pagebbox opaque in
3917 let rect = (float x0, float y0,
3918 float x1, float y0,
3919 float x1, float y1,
3920 float x0, float y1) in
3921 debugrect rect;
3922 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3923 state.rects <- (l.pageno, color, rect) :: state.rects;
3924 ) state.layout;
3925 postRedisplay "v";
3927 | Ascii '|' ->
3928 let mode = state.mode in
3929 let cmd = ref E.s in
3930 let onleave = function
3931 | Cancel -> state.mode <- mode
3932 | Confirm ->
3933 List.iter (fun l ->
3934 match getopaque l.pageno with
3935 | Some opaque -> pipesel opaque !cmd
3936 | None -> ()) state.layout;
3937 state.mode <- mode
3939 let ondone s =
3940 cbput state.hists.sel s;
3941 cmd := s
3943 let te =
3944 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
3946 postRedisplay "|";
3947 state.mode <- Textentry (te, onleave);
3949 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3950 vlog "huh? %s" (Wsi.keyname key)
3953 let linknavkeyboard key mask linknav =
3954 let pv = Wsi.kc2kt key in
3955 let getpage pageno =
3956 let rec loop = function
3957 | [] -> None
3958 | l :: _ when l.pageno = pageno -> Some l
3959 | _ :: rest -> loop rest
3960 in loop state.layout
3962 let doexact (pageno, n) =
3963 match getopaque pageno, getpage pageno with
3964 | Some opaque, Some l ->
3965 if pv = Keys.Enter
3966 then
3967 let under = getlink opaque n in
3968 postRedisplay "link gotounder";
3969 gotounder under;
3970 state.mode <- View;
3971 else
3972 let opt, dir =
3973 let open Keys in
3974 match pv with
3975 | Home -> Some (findlink opaque LDfirst), -1
3976 | End -> Some (findlink opaque LDlast), 1
3977 | Left -> Some (findlink opaque (LDleft n)), -1
3978 | Right -> Some (findlink opaque (LDright n)), 1
3979 | Up -> Some (findlink opaque (LDup n)), -1
3980 | Down -> Some (findlink opaque (LDdown n)), 1
3981 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3982 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3984 let pwl l dir =
3985 begin match findpwl l.pageno dir with
3986 | Pwlnotfound -> ()
3987 | Pwl pageno ->
3988 let notfound dir =
3989 state.mode <- LinkNav (Ltgendir dir);
3990 let y, h = getpageyh pageno in
3991 let y =
3992 if dir < 0
3993 then y + h - state.winh
3994 else y
3996 gotoxy state.x y
3998 begin match getopaque pageno, getpage pageno with
3999 | Some opaque, Some _ ->
4000 let link =
4001 let ld = if dir > 0 then LDfirst else LDlast in
4002 findlink opaque ld
4004 begin match link with
4005 | Lfound m ->
4006 showlinktype (getlink opaque m);
4007 state.mode <- LinkNav (Ltexact (pageno, m));
4008 postRedisplay "linknav jpage";
4009 | Lnotfound -> notfound dir
4010 end;
4011 | _ -> notfound dir
4012 end;
4013 end;
4015 begin match opt with
4016 | Some Lnotfound -> pwl l dir;
4017 | Some (Lfound m) ->
4018 if m = n
4019 then pwl l dir
4020 else (
4021 let _, y0, _, y1 = getlinkrect opaque m in
4022 if y0 < l.pagey
4023 then gotopage1 l.pageno y0
4024 else (
4025 let d = fstate.fontsize + 1 in
4026 if y1 - l.pagey > l.pagevh - d
4027 then gotopage1 l.pageno (y1 - state.winh + d)
4028 else postRedisplay "linknav";
4030 showlinktype (getlink opaque m);
4031 state.mode <- LinkNav (Ltexact (l.pageno, m));
4034 | None -> viewkeyboard key mask
4035 end;
4036 | _ -> viewkeyboard key mask
4038 if pv = Keys.Insert
4039 then (
4040 begin match linknav with
4041 | Ltexact pa -> state.lnava <- Some pa
4042 | Ltgendir _ | Ltnotready _ -> ()
4043 end;
4044 state.mode <- View;
4045 postRedisplay "leave linknav"
4047 else
4048 match linknav with
4049 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
4050 | Ltexact exact -> doexact exact
4053 let keyboard key mask =
4054 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
4055 then wcmd "interrupt"
4056 else state.uioh <- state.uioh#key key mask
4059 let birdseyekeyboard key mask
4060 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
4061 let incr =
4062 match conf.columns with
4063 | Csingle _ -> 1
4064 | Cmulti ((c, _, _), _) -> c
4065 | Csplit _ -> failwith "bird's eye split mode"
4067 let pgh layout = List.fold_left
4068 (fun m l -> max l.pageh m) state.winh layout in
4069 let open Keys in
4070 match Wsi.kc2kt key with
4071 | Ascii 'l' when Wsi.withctrl mask ->
4072 let y, h = getpageyh pageno in
4073 let top = (state.winh - h) / 2 in
4074 gotoxy state.x (max 0 (y - top))
4075 | Enter -> leavebirdseye beye false
4076 | Escape -> leavebirdseye beye true
4077 | Up -> upbirdseye incr beye
4078 | Down -> downbirdseye incr beye
4079 | Left -> upbirdseye 1 beye
4080 | Right -> downbirdseye 1 beye
4082 | Prior ->
4083 begin match state.layout with
4084 | l :: _ ->
4085 if l.pagey != 0
4086 then (
4087 state.mode <- Birdseye (
4088 oconf, leftx, l.pageno, hooverpageno, anchor
4090 gotopage1 l.pageno 0;
4092 else (
4093 let layout = layout state.x (state.y-state.winh)
4094 state.winw
4095 (pgh state.layout) in
4096 match layout with
4097 | [] -> gotoxy state.x (clamp (-state.winh))
4098 | l :: _ ->
4099 state.mode <- Birdseye (
4100 oconf, leftx, l.pageno, hooverpageno, anchor
4102 gotopage1 l.pageno 0
4105 | [] -> gotoxy state.x (clamp (-state.winh))
4106 end;
4108 | Next ->
4109 begin match List.rev state.layout with
4110 | l :: _ ->
4111 let layout = layout state.x
4112 (state.y + (pgh state.layout))
4113 state.winw state.winh in
4114 begin match layout with
4115 | [] ->
4116 let incr = l.pageh - l.pagevh in
4117 if incr = 0
4118 then (
4119 state.mode <-
4120 Birdseye (
4121 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4123 postRedisplay "birdseye pagedown";
4125 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
4127 | l :: _ ->
4128 state.mode <-
4129 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4130 gotopage1 l.pageno 0;
4133 | [] -> gotoxy state.x (clamp state.winh)
4134 end;
4136 | Home ->
4137 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4138 gotopage1 0 0
4140 | End ->
4141 let pageno = state.pagecount - 1 in
4142 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4143 if not (pagevisible state.layout pageno)
4144 then
4145 let h =
4146 match List.rev state.pdims with
4147 | [] -> state.winh
4148 | (_, _, h, _) :: _ -> h
4150 gotoxy
4151 state.x
4152 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
4153 else postRedisplay "birdseye end";
4155 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
4158 let drawpage l =
4159 let color =
4160 match state.mode with
4161 | Textentry _ -> scalecolor 0.4
4162 | LinkNav _ | View -> scalecolor 1.0
4163 | Birdseye (_, _, pageno, hooverpageno, _) ->
4164 if l.pageno = hooverpageno
4165 then scalecolor 0.9
4166 else (
4167 if l.pageno = pageno
4168 then (
4169 let c = scalecolor 1.0 in
4170 GlDraw.color c;
4171 GlDraw.line_width 3.0;
4172 let dispx = l.pagedispx in
4173 linerect
4174 (float (dispx-1)) (float (l.pagedispy-1))
4175 (float (dispx+l.pagevw+1))
4176 (float (l.pagedispy+l.pagevh+1));
4177 GlDraw.line_width 1.0;
4180 else scalecolor 0.8
4183 drawtiles l color;
4186 let postdrawpage l linkindexbase =
4187 match getopaque l.pageno with
4188 | Some opaque ->
4189 if tileready l l.pagex l.pagey
4190 then
4191 let x = l.pagedispx - l.pagex
4192 and y = l.pagedispy - l.pagey in
4193 let hlmask =
4194 match conf.columns with
4195 | Csingle _ | Cmulti _ ->
4196 (if conf.hlinks then 1 else 0)
4197 + (if state.glinks
4198 && not (isbirdseye state.mode) then 2 else 0)
4199 | Csplit _ -> 0
4201 let s =
4202 match state.mode with
4203 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
4204 | Textentry _
4205 | Birdseye _
4206 | View
4207 | LinkNav _ -> E.s
4209 Hashtbl.find_all state.prects l.pageno |>
4210 List.iter (fun vals -> drawprect opaque x y vals);
4211 let n = postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
4212 if n < 0
4213 then (Glutils.redisplay := true; 0)
4214 else n
4215 else 0
4216 | _ -> 0
4219 let scrollindicator () =
4220 let sbw, ph, sh = state.uioh#scrollph in
4221 let sbh, pw, sw = state.uioh#scrollpw in
4223 let x0,x1,hx0 =
4224 if conf.leftscroll
4225 then (0, sbw, sbw)
4226 else ((state.winw - sbw), state.winw, 0)
4229 Gl.enable `blend;
4230 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4231 let (r, g, b, alpha) = conf.sbarcolor in
4232 GlDraw.color (r, g, b) ~alpha;
4233 filledrect (float x0) 0. (float x1) (float state.winh);
4234 filledrect
4235 (float hx0) (float (state.winh - sbh))
4236 (float (hx0 + state.winw)) (float state.winh);
4237 let (r, g, b, alpha) = conf.sbarhndlcolor in
4238 GlDraw.color (r, g, b) ~alpha;
4240 filledrect (float x0) ph (float x1) (ph +. sh);
4241 let pw = pw +. float hx0 in
4242 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
4243 Gl.disable `blend;
4246 let showsel () =
4247 match state.mstate with
4248 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4251 | Msel ((x0, y0), (x1, y1)) ->
4252 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
4253 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
4254 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
4255 if n0 != -1 && n0 = n1 then seltext o0 (px0, py0, px1, py1);
4258 let showrects =
4259 function [] -> ()
4260 | rects ->
4261 Gl.enable `blend;
4262 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4263 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4264 List.iter
4265 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4266 List.iter (fun l ->
4267 if l.pageno = pageno
4268 then (
4269 let dx = float (l.pagedispx - l.pagex) in
4270 let dy = float (l.pagedispy - l.pagey) in
4271 let r, g, b, alpha = c in
4272 GlDraw.color (r, g, b) ~alpha;
4273 filledrect2 (x0+.dx) (y0+.dy)
4274 (x1+.dx) (y1+.dy)
4275 (x3+.dx) (y3+.dy)
4276 (x2+.dx) (y2+.dy);
4278 ) state.layout
4279 ) rects;
4280 Gl.disable `blend;
4283 let display () =
4284 GlDraw.color (scalecolor2 conf.bgcolor);
4285 GlClear.color (scalecolor2 conf.bgcolor);
4286 GlClear.clear [`color];
4287 List.iter drawpage state.layout;
4288 let rects =
4289 match state.mode with
4290 | LinkNav (Ltexact (pageno, linkno)) ->
4291 begin match getopaque pageno with
4292 | Some opaque ->
4293 let x0, y0, x1, y1 = getlinkrect opaque linkno in
4294 let color = (0.0, 0.0, 0.5, 0.5) in
4295 (pageno, color,
4296 (float x0, float y0,
4297 float x1, float y0,
4298 float x1, float y1,
4299 float x0, float y1)
4300 ) :: state.rects
4301 | None -> state.rects
4303 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
4304 | Birdseye _
4305 | Textentry _
4306 | View -> state.rects
4308 showrects rects;
4309 let rec postloop linkindexbase = function
4310 | l :: rest ->
4311 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
4312 postloop linkindexbase rest
4313 | [] -> ()
4315 showsel ();
4316 postloop 0 state.layout;
4317 state.uioh#display;
4318 begin match state.mstate with
4319 | Mzoomrect ((x0, y0), (x1, y1)) ->
4320 Gl.enable `blend;
4321 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4322 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
4323 filledrect (float x0) (float y0) (float x1) (float y1);
4324 Gl.disable `blend;
4325 | Msel _
4326 | Mpan _
4327 | Mscrolly | Mscrollx
4328 | Mzoom _
4329 | Mnone -> ()
4330 end;
4331 enttext ();
4332 scrollindicator ();
4333 Wsi.swapb ();
4336 let zoomrect x y x1 y1 =
4337 let x0 = min x x1
4338 and x1 = max x x1
4339 and y0 = min y y1 in
4340 let zoom = (float state.w) /. float (x1 - x0) in
4341 let margin =
4342 let simple () =
4343 if state.w < state.winw
4344 then (state.winw - state.w) / 2
4345 else 0
4347 match conf.fitmodel with
4348 | FitWidth | FitProportional -> simple ()
4349 | FitPage ->
4350 match conf.columns with
4351 | Csplit _ ->
4352 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
4353 | Cmulti _ | Csingle _ -> simple ()
4355 gotoxy ((state.x + margin) - x0) (state.y + y0);
4356 state.anchor <- getanchor ();
4357 setzoom zoom;
4358 resetmstate ();
4361 let annot inline x y =
4362 match unproject x y with
4363 | Some (opaque, n, ux, uy) ->
4364 let add text =
4365 addannot opaque ux uy text;
4366 wcmd "freepage %s" (~> opaque);
4367 Hashtbl.remove state.pagemap (n, state.gen);
4368 flushtiles ();
4369 gotoxy state.x state.y
4371 if inline
4372 then
4373 let ondone s = add s in
4374 let mode = state.mode in
4375 state.mode <- Textentry (
4376 ("annotation: ", E.s, None, textentry, ondone, true),
4377 fun _ -> state.mode <- mode);
4378 state.text <- E.s;
4379 enttext ();
4380 postRedisplay "annot"
4381 else
4382 add @@ getusertext E.s
4383 | _ -> ()
4386 let zoomblock x y =
4387 let g opaque l px py =
4388 match rectofblock opaque px py with
4389 | Some a ->
4390 let x0 = a.(0) -. 20. in
4391 let x1 = a.(1) +. 20. in
4392 let y0 = a.(2) -. 20. in
4393 let zoom = (float state.w) /. (x1 -. x0) in
4394 let pagey = getpagey l.pageno in
4395 let margin = (state.w - l.pagew)/2 in
4396 let nx = -truncate x0 - margin in
4397 gotoxy nx (pagey + truncate y0);
4398 state.anchor <- getanchor ();
4399 setzoom zoom;
4400 None
4401 | None -> None
4403 match conf.columns with
4404 | Csplit _ ->
4405 impmsg "block zooming does not work properly in split columns mode"
4406 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4409 let scrollx x =
4410 let winw = state.winw - 1 in
4411 let s = float x /. float winw in
4412 let destx = truncate (float (state.w + winw) *. s) in
4413 gotoxy (winw - destx) state.y;
4414 state.mstate <- Mscrollx;
4417 let scrolly y =
4418 let s = float y /. float state.winh in
4419 let desty = truncate (s *. float (maxy ())) in
4420 gotoxy state.x desty;
4421 state.mstate <- Mscrolly;
4424 let viewmulticlick clicks x y mask =
4425 let g opaque l px py =
4426 let mark =
4427 match clicks with
4428 | 2 -> Mark_word
4429 | 3 -> Mark_line
4430 | 4 -> Mark_block
4431 | _ -> Mark_page
4433 if markunder opaque px py mark
4434 then (
4435 Some (fun () ->
4436 let dopipe cmd =
4437 match getopaque l.pageno with
4438 | None -> ()
4439 | Some opaque -> pipesel opaque cmd
4441 state.roam <- (fun () -> dopipe conf.paxcmd);
4442 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4445 else None
4447 postRedisplay "viewmulticlick";
4448 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
4451 let canselect () =
4452 match conf.columns with
4453 | Csplit _ -> false
4454 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4457 let viewmouse button down x y mask =
4458 match button with
4459 | n when (n == 4 || n == 5) && not down ->
4460 if Wsi.withctrl mask
4461 then (
4462 let incr =
4463 if n = 5
4464 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4465 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4467 let fx, fy =
4468 match state.mstate with
4469 | Mzoom (oldn, _, pos) when n = oldn -> pos
4470 | Mzoomrect _ | Mnone | Mpan _
4471 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4473 let zoom = conf.zoom -. incr in
4474 state.mstate <- Mzoom (n, 0, (x, y));
4475 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4476 then pivotzoom ~x ~y zoom
4477 else pivotzoom zoom
4479 else (
4480 match state.autoscroll with
4481 | Some step -> setautoscrollspeed step (n=4)
4482 | None ->
4483 if conf.wheelbypage || conf.presentation
4484 then (
4485 if n = 4
4486 then prevpage ()
4487 else nextpage ()
4489 else
4490 let incr =
4491 if n = 4
4492 then -conf.scrollstep
4493 else conf.scrollstep
4495 let incr = incr * 2 in
4496 let y = clamp incr in
4497 gotoxy state.x y
4500 | n when (n = 6 || n = 7) && not down && canpan () ->
4501 let x =
4502 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4503 gotoxy x state.y
4505 | 1 when Wsi.withshift mask ->
4506 state.mstate <- Mnone;
4507 if not down
4508 then (
4509 match unproject x y with
4510 | None -> ()
4511 | Some (_, pageno, ux, uy) ->
4512 let cmd = Printf.sprintf
4513 "%s %s %d %d %d"
4514 conf.stcmd state.path pageno ux uy
4516 match spawn cmd [] with
4517 | exception exn ->
4518 impmsg "execution of synctex command(%S) failed: %S"
4519 conf.stcmd @@ exntos exn
4520 | _pid -> ()
4523 | 1 when Wsi.withctrl mask ->
4524 if down
4525 then (
4526 Wsi.setcursor Wsi.CURSOR_FLEUR;
4527 state.mstate <- Mpan (x, y)
4529 else
4530 state.mstate <- Mnone
4532 | 3 ->
4533 if down
4534 then (
4535 if Wsi.withshift mask
4536 then (
4537 annot conf.annotinline x y;
4538 postRedisplay "addannot"
4540 else
4541 let p = (x, y) in
4542 Wsi.setcursor Wsi.CURSOR_CYCLE;
4543 state.mstate <- Mzoomrect (p, p)
4545 else (
4546 match state.mstate with
4547 | Mzoomrect ((x0, y0), _) ->
4548 if abs (x-x0) > 10 && abs (y - y0) > 10
4549 then zoomrect x0 y0 x y
4550 else (
4551 resetmstate ();
4552 postRedisplay "kill accidental zoom rect";
4554 | Msel _
4555 | Mpan _
4556 | Mscrolly | Mscrollx
4557 | Mzoom _
4558 | Mnone -> resetmstate ()
4561 | 1 when vscrollhit x ->
4562 if down
4563 then
4564 let _, position, sh = state.uioh#scrollph in
4565 if y > truncate position && y < truncate (position +. sh)
4566 then state.mstate <- Mscrolly
4567 else scrolly y
4568 else
4569 state.mstate <- Mnone
4571 | 1 when y > state.winh - hscrollh () ->
4572 if down
4573 then
4574 let _, position, sw = state.uioh#scrollpw in
4575 if x > truncate position && x < truncate (position +. sw)
4576 then state.mstate <- Mscrollx
4577 else scrollx x
4578 else
4579 state.mstate <- Mnone
4581 | 1 when state.bzoom -> if not down then zoomblock x y
4583 | 1 ->
4584 let dest = if down then getunder x y else Unone in
4585 begin match dest with
4586 | Ulinkuri _ ->
4587 gotounder dest
4589 | Unone when down ->
4590 Wsi.setcursor Wsi.CURSOR_FLEUR;
4591 state.mstate <- Mpan (x, y);
4593 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4595 | Unone | Utext _ ->
4596 if down
4597 then (
4598 if canselect ()
4599 then (
4600 state.mstate <- Msel ((x, y), (x, y));
4601 postRedisplay "mouse select";
4604 else (
4605 match state.mstate with
4606 | Mnone -> ()
4608 | Mzoom _ | Mscrollx | Mscrolly ->
4609 state.mstate <- Mnone
4611 | Mzoomrect ((x0, y0), _) ->
4612 zoomrect x0 y0 x y
4614 | Mpan _ ->
4615 Wsi.setcursor Wsi.CURSOR_INHERIT;
4616 state.mstate <- Mnone
4618 | Msel ((x0, y0), (x1, y1)) ->
4619 let rec loop = function
4620 | [] -> ()
4621 | l :: rest ->
4622 let inside =
4623 let a0 = l.pagedispy in
4624 let a1 = a0 + l.pagevh in
4625 let b0 = l.pagedispx in
4626 let b1 = b0 + l.pagevw in
4627 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4628 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4630 if inside
4631 then
4632 match getopaque l.pageno with
4633 | Some opaque ->
4634 let dosel cmd () =
4635 pipef ~closew:false "Msel"
4636 (fun w ->
4637 copysel w opaque;
4638 postRedisplay "Msel") cmd
4640 dosel conf.selcmd ();
4641 state.roam <- dosel conf.paxcmd;
4642 | None -> ()
4643 else loop rest
4645 loop state.layout;
4646 resetmstate ();
4650 | _ -> ()
4653 let birdseyemouse button down x y mask
4654 (conf, leftx, _, hooverpageno, anchor) =
4655 match button with
4656 | 1 when down ->
4657 let rec loop = function
4658 | [] -> ()
4659 | l :: rest ->
4660 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4661 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4662 then (
4663 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4665 else loop rest
4667 loop state.layout
4668 | 3 -> ()
4669 | _ -> viewmouse button down x y mask
4672 let uioh = object
4673 method display = ()
4675 method key key mask =
4676 begin match state.mode with
4677 | Textentry textentry -> textentrykeyboard key mask textentry
4678 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4679 | View -> viewkeyboard key mask
4680 | LinkNav linknav -> linknavkeyboard key mask linknav
4681 end;
4682 state.uioh
4684 method button button bstate x y mask =
4685 begin match state.mode with
4686 | LinkNav _ | View -> viewmouse button bstate x y mask
4687 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4688 | Textentry _ -> ()
4689 end;
4690 state.uioh
4692 method multiclick clicks x y mask =
4693 begin match state.mode with
4694 | LinkNav _ | View -> viewmulticlick clicks x y mask
4695 | Birdseye _ | Textentry _ -> ()
4696 end;
4697 state.uioh
4699 method motion x y =
4700 begin match state.mode with
4701 | Textentry _ -> ()
4702 | View | Birdseye _ | LinkNav _ ->
4703 match state.mstate with
4704 | Mzoom _ | Mnone -> ()
4706 | Mpan (x0, y0) ->
4707 let dx = x - x0
4708 and dy = y0 - y in
4709 state.mstate <- Mpan (x, y);
4710 let x = if canpan () then panbound (state.x + dx) else state.x in
4711 let y = clamp dy in
4712 gotoxy x y
4714 | Msel (a, _) ->
4715 state.mstate <- Msel (a, (x, y));
4716 postRedisplay "motion select";
4718 | Mscrolly ->
4719 let y = min state.winh (max 0 y) in
4720 scrolly y
4722 | Mscrollx ->
4723 let x = min state.winw (max 0 x) in
4724 scrollx x
4726 | Mzoomrect (p0, _) ->
4727 state.mstate <- Mzoomrect (p0, (x, y));
4728 postRedisplay "motion zoomrect";
4729 end;
4730 state.uioh
4732 method pmotion x y =
4733 begin match state.mode with
4734 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4735 let rec loop = function
4736 | [] ->
4737 if hooverpageno != -1
4738 then (
4739 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4740 postRedisplay "pmotion birdseye no hoover";
4742 | l :: rest ->
4743 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4744 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4745 then (
4746 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4747 postRedisplay "pmotion birdseye hoover";
4749 else loop rest
4751 loop state.layout
4753 | Textentry _ -> ()
4755 | LinkNav _ | View ->
4756 match state.mstate with
4757 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4758 | Mnone ->
4759 updateunder x y;
4760 if canselect ()
4761 then
4762 match conf.pax with
4763 | None -> ()
4764 | Some past ->
4765 let now = now () in
4766 let delta = now -. past in
4767 if delta > 0.01
4768 then paxunder x y
4769 else conf.pax <- Some now
4770 end;
4771 state.uioh
4773 method infochanged _ = ()
4775 method scrollph =
4776 let maxy = maxy () in
4777 let p, h =
4778 if maxy = 0
4779 then 0.0, float state.winh
4780 else scrollph state.y maxy
4782 vscrollw (), p, h
4784 method scrollpw =
4785 let fwinw = float (state.winw - vscrollw ()) in
4786 let sw =
4787 let sw = fwinw /. float state.w in
4788 let sw = fwinw *. sw in
4789 max sw (float conf.scrollh)
4791 let position =
4792 let maxx = state.w + state.winw in
4793 let x = state.winw - state.x in
4794 let percent = float x /. float maxx in
4795 (fwinw -. sw) *. percent
4797 hscrollh (), position, sw
4799 method modehash =
4800 let modename =
4801 match state.mode with
4802 | LinkNav _ -> "links"
4803 | Textentry _ -> "textentry"
4804 | Birdseye _ -> "birdseye"
4805 | View -> "view"
4807 findkeyhash conf modename
4809 method eformsgs = true
4810 method alwaysscrolly = false
4811 method scroll dx dy =
4812 let x = if canpan () then panbound (state.x + dx) else state.x in
4813 gotoxy x (clamp (2 * dy));
4814 state.uioh
4815 method zoom z x y =
4816 pivotzoom ~x ~y (conf.zoom *. exp z);
4817 end;;
4819 let addrect pageno r g b a x0 y0 x1 y1 =
4820 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
4823 let ract cmds =
4824 let cl = splitatchar cmds ' ' in
4825 let scan s fmt f =
4826 try Scanf.sscanf s fmt f
4827 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4828 cmds @@ exntos exn
4830 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4831 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4832 s pageno r g b a x0 y0 x1 y1;
4833 onpagerect
4834 pageno
4835 (fun w h ->
4836 let _,w1,h1,_ = getpagedim pageno in
4837 let sw = float w1 /. float w
4838 and sh = float h1 /. float h in
4839 let x0s = x0 *. sw
4840 and x1s = x1 *. sw
4841 and y0s = y0 *. sh
4842 and y1s = y1 *. sh in
4843 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4844 let color = (r, g, b, a) in
4845 if conf.verbose then debugrect rect;
4846 state.rects <- (pageno, color, rect) :: state.rects;
4847 postRedisplay s;
4850 match cl with
4851 | "reload", "" -> reload ()
4852 | "goto", args ->
4853 scan args "%u %f %f"
4854 (fun pageno x y ->
4855 let cmd, _ = state.geomcmds in
4856 if emptystr cmd
4857 then gotopagexy pageno x y
4858 else
4859 let f prevf () =
4860 gotopagexy pageno x y;
4861 prevf ()
4863 state.reprf <- f state.reprf
4865 | "goto1", args -> scan args "%u %f" gotopage
4866 | "gotor", args -> scan args "%S" gotoremote
4867 | "rect", args ->
4868 scan args "%u %u %f %f %f %f"
4869 (fun pageno c x0 y0 x1 y1 ->
4870 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4871 rectx "rect" pageno color x0 y0 x1 y1;
4873 | "prect", args ->
4874 scan args "%u %f %f %f %f %f %f %f %f"
4875 (fun pageno r g b alpha x0 y0 x1 y1 ->
4876 addrect pageno r g b alpha x0 y0 x1 y1;
4877 postRedisplay "prect"
4879 | "pgoto", args ->
4880 scan args "%u %f %f"
4881 (fun pageno x y ->
4882 let optopaque =
4883 match getopaque pageno with
4884 | Some opaque -> opaque
4885 | None -> ~< E.s
4887 pgoto optopaque pageno x y;
4888 let rec fixx = function
4889 | [] -> ()
4890 | l :: rest ->
4891 if l.pageno = pageno
4892 then gotoxy (state.x - l.pagedispx) state.y
4893 else fixx rest
4895 let layout =
4896 let mult =
4897 match conf.columns with
4898 | Csingle _ | Csplit _ -> 1
4899 | Cmulti ((n, _, _), _) -> n
4901 layout 0 state.y (state.winw * mult) state.winh
4903 fixx layout
4905 | "activatewin", "" -> Wsi.activatewin ()
4906 | "quit", "" -> raise Quit
4907 | "keys", keys ->
4908 begin try
4909 let l = Config.keys_of_string keys in
4910 List.iter (fun (k, m) -> keyboard k m) l
4911 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4912 cmds @@ exntos exn
4914 | "clearrects", "" ->
4915 Hashtbl.clear state.prects;
4916 postRedisplay "clearrects"
4917 | _ ->
4918 adderrfmt "remote command"
4919 "error processing remote command: %S\n" cmds;
4922 let remote =
4923 let scratch = Bytes.create 80 in
4924 let buf = Buffer.create 80 in
4925 fun fd ->
4926 match tempfailureretry (Unix.read fd scratch 0) 80 with
4927 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4928 | 0 ->
4929 Unix.close fd;
4930 if Buffer.length buf > 0
4931 then (
4932 let s = Buffer.contents buf in
4933 Buffer.clear buf;
4934 ract s;
4936 None
4937 | n ->
4938 let rec eat ppos =
4939 let nlpos =
4940 match Bytes.index_from scratch ppos '\n' with
4941 | pos -> if pos >= n then -1 else pos
4942 | exception Not_found -> -1
4944 if nlpos >= 0
4945 then (
4946 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4947 let s = Buffer.contents buf in
4948 Buffer.clear buf;
4949 ract s;
4950 eat (nlpos+1);
4952 else (
4953 Buffer.add_subbytes buf scratch ppos (n-ppos);
4954 Some fd
4956 in eat 0
4959 let remoteopen path =
4960 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4961 with exn ->
4962 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4963 None
4966 let () =
4967 let gcconfig = ref false in
4968 let trimcachepath = ref E.s in
4969 let rcmdpath = ref E.s in
4970 let pageno = ref None in
4971 let openlast = ref false in
4972 let doreap = ref false in
4973 let csspath = ref None in
4974 selfexec := Sys.executable_name;
4975 Arg.parse
4976 (Arg.align
4977 [("-p", Arg.String (fun s -> state.password <- s),
4978 "<password> Set password");
4980 ("-f", Arg.String
4981 (fun s ->
4982 Config.fontpath := s;
4983 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
4985 "<path> Set path to the user interface font");
4987 ("-c", Arg.String
4988 (fun s ->
4989 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
4990 Config.confpath := s),
4991 "<path> Set path to the configuration file");
4993 ("-last", Arg.Set openlast, " Open last document");
4995 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4996 "<page-number> Jump to page");
4998 ("-tcf", Arg.String (fun s -> trimcachepath := s),
4999 "<path> Set path to the trim cache file");
5001 ("-dest", Arg.String (fun s -> state.nameddest <- s),
5002 "<named-destination> Set named destination");
5004 ("-remote", Arg.String (fun s -> rcmdpath := s),
5005 "<path> Set path to the source of remote commands");
5007 ("-gc", Arg.Set gcconfig, " Collect config garbage");
5009 ("-v", Arg.Unit (fun () ->
5010 Printf.printf
5011 "%s\nconfiguration file: %s\n"
5012 (Help.version ())
5013 Config.defconfpath;
5014 exit 0), " Print version and exit");
5016 ("-css", Arg.String (fun s -> csspath := Some s),
5017 "<path> Set path to the style sheet to use with EPUB/HTML");
5019 ("-origin", Arg.String (fun s -> state.origin <- s),
5020 "<origin> <undocumented>");
5022 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
5023 ("-layout-height", Arg.Set_int layouth,
5024 "<height> layout height html/epub/etc (-1, 0, N)");
5027 (fun s -> state.path <- s)
5028 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
5030 let histmode = emptystr state.path && not !openlast in
5032 if not (Config.load !openlast)
5033 then dolog "failed to load configuration";
5035 begin match !pageno with
5036 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
5037 | None -> ()
5038 end;
5040 fillhelp ();
5041 if !gcconfig
5042 then (
5043 Config.gc ();
5044 exit 0
5047 let mu =
5048 object (self)
5049 val mutable m_clicks = 0
5050 val mutable m_click_x = 0
5051 val mutable m_click_y = 0
5052 val mutable m_lastclicktime = infinity
5054 method private cleanup =
5055 state.roam <- noroam;
5056 Hashtbl.iter (fun _ opaque -> clearmark opaque) state.pagemap
5057 method expose = postRedisplay "expose"
5058 method visible v =
5059 let name =
5060 match v with
5061 | Wsi.Unobscured -> "unobscured"
5062 | Wsi.PartiallyObscured -> "partiallyobscured"
5063 | Wsi.FullyObscured -> "fullyobscured"
5065 vlog "visibility change %s" name
5066 method display = display ()
5067 method map mapped = vlog "mapped %b" mapped
5068 method reshape w h =
5069 self#cleanup;
5070 reshape w h
5071 method mouse b d x y m =
5072 if d && canselect ()
5073 then (
5075 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
5077 m_click_x <- x;
5078 m_click_y <- y;
5079 if b = 1
5080 then (
5081 let t = now () in
5082 if abs x - m_click_x > 10
5083 || abs y - m_click_y > 10
5084 || abs_float (t -. m_lastclicktime) > 0.3
5085 then m_clicks <- 0;
5086 m_clicks <- m_clicks + 1;
5087 m_lastclicktime <- t;
5088 if m_clicks = 1
5089 then (
5090 self#cleanup;
5091 postRedisplay "cleanup";
5092 state.uioh <- state.uioh#button b d x y m;
5094 else state.uioh <- state.uioh#multiclick m_clicks x y m
5096 else (
5097 self#cleanup;
5098 m_clicks <- 0;
5099 m_lastclicktime <- infinity;
5100 state.uioh <- state.uioh#button b d x y m
5103 else state.uioh <- state.uioh#button b d x y m
5104 method motion x y =
5105 state.mpos <- (x, y);
5106 state.uioh <- state.uioh#motion x y
5107 method pmotion x y =
5108 state.mpos <- (x, y);
5109 state.uioh <- state.uioh#pmotion x y
5110 method key k m =
5111 vlog "k=%#x m=%#x" k m;
5112 let mascm = m land (
5113 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
5114 ) in
5115 let keyboard k m =
5116 let x = state.x and y = state.y in
5117 keyboard k m;
5118 if x != state.x || y != state.y then self#cleanup
5120 match state.keystate with
5121 | KSnone ->
5122 let km = k, mascm in
5123 begin
5124 match
5125 let modehash = state.uioh#modehash in
5126 try Hashtbl.find modehash km
5127 with Not_found ->
5128 try Hashtbl.find (findkeyhash conf "global") km
5129 with Not_found -> KMinsrt (k, m)
5130 with
5131 | KMinsrt (k, m) -> keyboard k m
5132 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
5133 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
5135 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
5136 List.iter (fun (k, m) -> keyboard k m) insrt;
5137 state.keystate <- KSnone
5138 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
5139 state.keystate <- KSinto (keys, insrt)
5140 | KSinto _ -> state.keystate <- KSnone
5142 method enter x y =
5143 state.mpos <- (x, y);
5144 state.uioh <- state.uioh#pmotion x y
5145 method leave = state.mpos <- (-1, -1)
5146 method winstate wsl = state.winstate <- wsl
5147 method quit : 'a. 'a = raise Quit
5148 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
5149 method zoom z x y = state.uioh#zoom z x y
5150 method opendoc path =
5151 state.mode <- View;
5152 state.uioh <- uioh;
5153 postRedisplay "opendoc";
5154 opendoc path state.password
5157 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
5158 state.wsfd <- wsfd;
5160 if not @@ List.exists GlMisc.check_extension
5161 [ "GL_ARB_texture_rectangle"
5162 ; "GL_EXT_texture_recangle"
5163 ; "GL_NV_texture_rectangle" ]
5164 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
5166 if substratis (GlMisc.get_string `renderer) 0 "Mesa DRI Intel("
5167 then (
5168 defconf.sliceheight <- 1024;
5169 defconf.texcount <- 32;
5170 defconf.usepbo <- true;
5173 let cs, ss =
5174 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
5175 | exception exn ->
5176 dolog "socketpair failed: %s" @@ exntos exn;
5177 exit 1
5178 | (r, w) ->
5179 cloexec r;
5180 cloexec w;
5181 r, w
5184 setcheckers conf.checkers;
5186 opengl_has_pbo := GlMisc.check_extension "GL_ARB_pixel_buffer_object";
5188 begin match !csspath with
5189 | None -> ()
5190 | Some "" -> conf.css <- E.s
5191 | Some path ->
5192 let css = filecontents path in
5193 let l = String.length css in
5194 conf.css <-
5195 if substratis css (l-2) "\r\n"
5196 then String.sub css 0 (l-2)
5197 else (if css.[l-1] = '\n'
5198 then String.sub css 0 (l-1)
5199 else css);
5200 end;
5201 init cs (
5202 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
5203 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5204 !Config.fontpath, !trimcachepath, !opengl_has_pbo
5206 List.iter GlArray.enable [`texture_coord; `vertex];
5207 state.ss <- ss;
5208 reshape ~firsttime:true winw winh;
5209 state.uioh <- uioh;
5210 if histmode
5211 then (
5212 Wsi.settitle "llpp (history)";
5213 enterhistmode ();
5215 else (
5216 state.text <- "Opening " ^ (mbtoutf8 state.path);
5217 opendoc state.path state.password;
5219 display ();
5220 Wsi.mapwin ();
5221 Wsi.setcursor Wsi.CURSOR_INHERIT;
5222 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
5224 let rec reap () =
5225 match Unix.waitpid [Unix.WNOHANG] ~-1 with
5226 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
5227 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
5228 | 0, _ -> ()
5229 | _pid, _status -> reap ()
5231 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
5233 let optrfd =
5234 ref (
5235 if nonemptystr !rcmdpath
5236 then remoteopen !rcmdpath
5237 else None
5241 let rec loop deadline =
5242 if !doreap
5243 then (
5244 doreap := false;
5245 reap ()
5247 let r = [state.ss; state.wsfd] in
5248 let r =
5249 match !optrfd with
5250 | None -> r
5251 | Some fd -> fd :: r
5253 if !redisplay
5254 then (
5255 Glutils.redisplay := false;
5256 display ();
5258 let timeout =
5259 let now = now () in
5260 if deadline > now
5261 then (
5262 if deadline = infinity
5263 then ~-.1.0
5264 else max 0.0 (deadline -. now)
5266 else 0.0
5268 let r, _, _ =
5269 try Unix.select r [] [] timeout
5270 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
5272 begin match r with
5273 | [] ->
5274 let newdeadline =
5275 match state.autoscroll with
5276 | Some step when step != 0 ->
5277 if state.slideshow land 1 = 1
5278 then (
5279 if state.slideshow land 2 = 0
5280 then state.slideshow <- state.slideshow lor 2
5281 else if step < 0 then prevpage () else nextpage ();
5282 deadline +. (float (abs step))
5284 else
5285 let y = state.y + step in
5286 let fy = if conf.maxhfit then state.winh else 0 in
5287 let y =
5288 if y < 0
5289 then state.maxy - fy
5290 else if y >= state.maxy - fy then 0 else y
5292 gotoxy state.x y;
5293 deadline +. 0.01
5294 | _ -> infinity
5296 loop newdeadline
5298 | l ->
5299 let rec checkfds = function
5300 | [] -> ()
5301 | fd :: rest when fd = state.ss ->
5302 let cmd = rcmd state.ss in
5303 act cmd;
5304 checkfds rest
5306 | fd :: rest when fd = state.wsfd ->
5307 Wsi.readresp fd;
5308 checkfds rest
5310 | fd :: rest when Some fd = !optrfd ->
5311 begin match remote fd with
5312 | None -> optrfd := remoteopen !rcmdpath;
5313 | opt -> optrfd := opt
5314 end;
5315 checkfds rest
5317 | _ :: rest ->
5318 dolog "select returned unknown descriptor";
5319 checkfds rest
5321 checkfds l;
5322 let newdeadline =
5323 let deadline1 =
5324 if deadline = infinity
5325 then now () +. 0.01
5326 else deadline
5328 match state.autoscroll with
5329 | Some step when step != 0 -> deadline1
5330 | _ -> infinity
5332 loop newdeadline
5333 end;
5335 match loop infinity with
5336 | exception Quit ->
5337 Config.save leavebirdseye;
5338 if hasunsavedchanges ()
5339 then save ()
5340 | _ -> error "umpossible - infinity reached"