Honor interpagespace in presentation mode for taller than window height pages
[llpp.git] / main.ml
blobf42d0b60a1365d3556fe39c498dbf40070ca3797
1 exception Quit;;
3 type under =
4 | Unone
5 | Ulinkuri of string
6 | Ulinkgoto of (int * int)
7 | Utext of facename
8 | Uunexpected of string
9 | Ulaunch of string
10 | Unamed of string
11 | Uremote of (string * int)
12 and facename = string;;
14 let dolog fmt = Printf.kprintf prerr_endline fmt;;
15 let now = Unix.gettimeofday;;
17 type params = (angle * proportional * trimparams
18 * texcount * sliceheight * memsize
19 * colorspace * fontpath)
20 and pageno = int
21 and width = int
22 and height = int
23 and leftx = int
24 and opaque = string
25 and recttype = int
26 and pixmapsize = int
27 and angle = int
28 and proportional = bool
29 and trimmargins = bool
30 and interpagespace = int
31 and texcount = int
32 and sliceheight = int
33 and gen = int
34 and top = float
35 and fontpath = string
36 and memsize = int
37 and aalevel = int
38 and irect = (int * int * int * int)
39 and trimparams = (trimmargins * irect)
40 and colorspace = | Rgb | Bgr | Gray
43 type link =
44 | Lnotfound
45 | Lfound of int
46 and linkdir =
47 | LDfirst
48 | LDlast
49 | LDfirstvisible of (int * int * int)
50 | LDleft of int
51 | LDright of int
52 | LDdown of int
53 | LDup of int
56 type pagewithlinks =
57 | Pwlnotfound
58 | Pwl of int
61 type keymap =
62 | KMinsrt of key
63 | KMinsrl of key list
64 | KMmulti of key list * key list
65 and key = int * int
66 and keyhash = (key, keymap) Hashtbl.t
67 and keystate =
68 | KSnone
69 | KSinto of (key list * key list)
72 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
73 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
75 type pipe = (Unix.file_descr * Unix.file_descr);;
77 external init : pipe -> params -> unit = "ml_init";;
78 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
79 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
80 external getpdimrect : int -> float array = "ml_getpdimrect";;
81 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
82 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
83 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
84 external measurestr : int -> string -> float = "ml_measure_string";;
85 external getmaxw : unit -> float = "ml_getmaxw";;
86 external postprocess :
87 opaque -> int -> int -> int -> (int * string * int) -> int = "ml_postprocess";;
88 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
89 external platform : unit -> platform = "ml_platform";;
90 external setaalevel : int -> unit = "ml_setaalevel";;
91 external realloctexts : int -> bool = "ml_realloctexts";;
92 external cloexec : Unix.file_descr -> unit = "ml_cloexec";;
93 external findlink : opaque -> linkdir -> link = "ml_findlink";;
94 external getlink : opaque -> int -> under = "ml_getlink";;
95 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
96 external getlinkcount : opaque -> int = "ml_getlinkcount";;
97 external findpwl: int -> int -> pagewithlinks = "ml_find_page_with_links"
98 external popen : string -> (Unix.file_descr * int) list -> unit = "ml_popen";;
100 let platform_to_string = function
101 | Punknown -> "unknown"
102 | Plinux -> "Linux"
103 | Posx -> "OSX"
104 | Psun -> "Sun"
105 | Pfreebsd -> "FreeBSD"
106 | Pdragonflybsd -> "DragonflyBSD"
107 | Popenbsd -> "OpenBSD"
108 | Pnetbsd -> "NetBSD"
109 | Pcygwin -> "Cygwin"
112 let platform = platform ();;
114 let popen cmd fda =
115 if platform = Pcygwin
116 then (
117 let sh = "/bin/sh" in
118 let args = [|sh; "-c"; cmd|] in
119 let rec std si so se = function
120 | [] -> si, so, se
121 | (fd, 0) :: rest -> std fd so se rest
122 | (fd, -1) :: rest ->
123 Unix.set_close_on_exec fd;
124 std si so se rest
125 | (_, n) :: _ ->
126 failwith ("unexpected fdn in cygwin popen " ^ string_of_int n)
128 let si, so, se = std Unix.stdin Unix.stdout Unix.stderr fda in
129 ignore (Unix.create_process sh args si so se)
131 else popen cmd fda;
134 type x = int
135 and y = int
136 and tilex = int
137 and tiley = int
138 and tileparams = (x * y * width * height * tilex * tiley)
141 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
143 type mpos = int * int
144 and mstate =
145 | Msel of (mpos * mpos)
146 | Mpan of mpos
147 | Mscrolly | Mscrollx
148 | Mzoom of (int * int)
149 | Mzoomrect of (mpos * mpos)
150 | Mnone
153 type textentry = string * string * onhist option * onkey * ondone * cancelonempty
154 and onkey = string -> int -> te
155 and ondone = string -> unit
156 and histcancel = unit -> unit
157 and onhist = ((histcmd -> string) * histcancel)
158 and histcmd = HCnext | HCprev | HCfirst | HClast
159 and cancelonempty = bool
160 and te =
161 | TEstop
162 | TEdone of string
163 | TEcont of string
164 | TEswitch of textentry
167 type 'a circbuf =
168 { store : 'a array
169 ; mutable rc : int
170 ; mutable wc : int
171 ; mutable len : int
175 let bound v minv maxv =
176 max minv (min maxv v);
179 let cbnew n v =
180 { store = Array.create n v
181 ; rc = 0
182 ; wc = 0
183 ; len = 0
187 let drawstring size x y s =
188 Gl.enable `blend;
189 Gl.enable `texture_2d;
190 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
191 ignore (drawstr size x y s);
192 Gl.disable `blend;
193 Gl.disable `texture_2d;
196 let drawstring1 size x y s =
197 drawstr size x y s;
200 let drawstring2 size x y fmt =
201 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
204 let cbcap b = Array.length b.store;;
206 let cbput b v =
207 let cap = cbcap b in
208 b.store.(b.wc) <- v;
209 b.wc <- (b.wc + 1) mod cap;
210 b.rc <- b.wc;
211 b.len <- min (b.len + 1) cap;
214 let cbempty b = b.len = 0;;
216 let cbgetg b circular dir =
217 if cbempty b
218 then b.store.(0)
219 else
220 let rc = b.rc + dir in
221 let rc =
222 if circular
223 then (
224 if rc = -1
225 then b.len-1
226 else (
227 if rc = b.len
228 then 0
229 else rc
232 else max 0 (min rc (b.len-1))
234 b.rc <- rc;
235 b.store.(rc);
238 let cbget b = cbgetg b false;;
239 let cbgetc b = cbgetg b true;;
241 type page =
242 { pageno : int
243 ; pagedimno : int
244 ; pagew : int
245 ; pageh : int
246 ; pagex : int
247 ; pagey : int
248 ; pagevw : int
249 ; pagevh : int
250 ; pagedispx : int
251 ; pagedispy : int
252 ; pagecol : int
256 let debugl l =
257 dolog "l %d dim=%d {" l.pageno l.pagedimno;
258 dolog " WxH %dx%d" l.pagew l.pageh;
259 dolog " vWxH %dx%d" l.pagevw l.pagevh;
260 dolog " pagex,y %d,%d" l.pagex l.pagey;
261 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
262 dolog " column %d" l.pagecol;
263 dolog "}";
266 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
267 dolog "rect {";
268 dolog " x0,y0=(% f, % f)" x0 y0;
269 dolog " x1,y1=(% f, % f)" x1 y1;
270 dolog " x2,y2=(% f, % f)" x2 y2;
271 dolog " x3,y3=(% f, % f)" x3 y3;
272 dolog "}";
275 type multicolumns = multicol * pagegeom
276 and splitcolumns = columncount * pagegeom
277 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
278 and multicol = columncount * covercount * covercount
279 and pdimno = int
280 and columncount = int
281 and covercount = int;;
283 type conf =
284 { mutable scrollbw : int
285 ; mutable scrollh : int
286 ; mutable icase : bool
287 ; mutable preload : bool
288 ; mutable pagebias : int
289 ; mutable verbose : bool
290 ; mutable debug : bool
291 ; mutable scrollstep : int
292 ; mutable hscrollstep : int
293 ; mutable maxhfit : bool
294 ; mutable crophack : bool
295 ; mutable autoscrollstep : int
296 ; mutable maxwait : float option
297 ; mutable hlinks : bool
298 ; mutable underinfo : bool
299 ; mutable interpagespace : interpagespace
300 ; mutable zoom : float
301 ; mutable presentation : bool
302 ; mutable angle : angle
303 ; mutable winw : int
304 ; mutable winh : int
305 ; mutable savebmarks : bool
306 ; mutable proportional : proportional
307 ; mutable trimmargins : trimmargins
308 ; mutable trimfuzz : irect
309 ; mutable memlimit : memsize
310 ; mutable texcount : texcount
311 ; mutable sliceheight : sliceheight
312 ; mutable thumbw : width
313 ; mutable jumpback : bool
314 ; mutable bgcolor : float * float * float
315 ; mutable bedefault : bool
316 ; mutable scrollbarinpm : bool
317 ; mutable tilew : int
318 ; mutable tileh : int
319 ; mutable mustoresize : memsize
320 ; mutable checkers : bool
321 ; mutable aalevel : int
322 ; mutable urilauncher : string
323 ; mutable pathlauncher : string
324 ; mutable colorspace : colorspace
325 ; mutable invert : bool
326 ; mutable colorscale : float
327 ; mutable redirectstderr : bool
328 ; mutable ghyllscroll : (int * int * int) option
329 ; mutable columns : columns
330 ; mutable beyecolumns : columncount option
331 ; mutable selcmd : string
332 ; mutable updatecurs : bool
333 ; mutable keyhashes : (string * keyhash) list
334 ; mutable hfsize : int
335 ; mutable pgscale : float
337 and columns =
338 | Csingle
339 | Cmulti of multicolumns
340 | Csplit of splitcolumns
343 type anchor = pageno * top;;
345 type outline = string * int * anchor;;
347 type rect = float * float * float * float * float * float * float * float;;
349 type tile = opaque * pixmapsize * elapsed
350 and elapsed = float;;
351 type pagemapkey = pageno * gen;;
352 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
353 and row = int
354 and col = int;;
356 let emptyanchor = (0, 0.0);;
358 type infochange = | Memused | Docinfo | Pdim;;
360 class type uioh = object
361 method display : unit
362 method key : int -> int -> uioh
363 method button : int -> bool -> int -> int -> int -> uioh
364 method motion : int -> int -> uioh
365 method pmotion : int -> int -> uioh
366 method infochanged : infochange -> unit
367 method scrollpw : (int * float * float)
368 method scrollph : (int * float * float)
369 method modehash : keyhash
370 end;;
372 type mode =
373 | Birdseye of (conf * leftx * pageno * pageno * anchor)
374 | Textentry of (textentry * onleave)
375 | View
376 | LinkNav of linktarget
377 and onleave = leavetextentrystatus -> unit
378 and leavetextentrystatus = | Cancel | Confirm
379 and helpitem = string * int * action
380 and action =
381 | Noaction
382 | Action of (uioh -> uioh)
383 and linktarget =
384 | Ltexact of (pageno * int)
385 | Ltgendir of int
388 let isbirdseye = function Birdseye _ -> true | _ -> false;;
389 let istextentry = function Textentry _ -> true | _ -> false;;
391 type currently =
392 | Idle
393 | Loading of (page * gen)
394 | Tiling of (
395 page * opaque * colorspace * angle * gen * col * row * width * height
397 | Outlining of outline list
400 let emptykeyhash = Hashtbl.create 0;;
401 let nouioh : uioh = object (self)
402 method display = ()
403 method key _ _ = self
404 method button _ _ _ _ _ = self
405 method motion _ _ = self
406 method pmotion _ _ = self
407 method infochanged _ = ()
408 method scrollpw = (0, nan, nan)
409 method scrollph = (0, nan, nan)
410 method modehash = emptykeyhash
411 end;;
413 type state =
414 { mutable sr : Unix.file_descr
415 ; mutable sw : Unix.file_descr
416 ; mutable wsfd : Unix.file_descr
417 ; mutable errfd : Unix.file_descr option
418 ; mutable stderr : Unix.file_descr
419 ; mutable errmsgs : Buffer.t
420 ; mutable newerrmsgs : bool
421 ; mutable w : int
422 ; mutable x : int
423 ; mutable y : int
424 ; mutable scrollw : int
425 ; mutable hscrollh : int
426 ; mutable anchor : anchor
427 ; mutable ranchors : (string * string * anchor) list
428 ; mutable maxy : int
429 ; mutable layout : page list
430 ; pagemap : (pagemapkey, opaque) Hashtbl.t
431 ; tilemap : (tilemapkey, tile) Hashtbl.t
432 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
433 ; mutable pdims : (pageno * width * height * leftx) list
434 ; mutable pagecount : int
435 ; mutable currently : currently
436 ; mutable mstate : mstate
437 ; mutable searchpattern : string
438 ; mutable rects : (pageno * recttype * rect) list
439 ; mutable rects1 : (pageno * recttype * rect) list
440 ; mutable text : string
441 ; mutable fullscreen : (width * height) option
442 ; mutable mode : mode
443 ; mutable uioh : uioh
444 ; mutable outlines : outline array
445 ; mutable bookmarks : outline list
446 ; mutable path : string
447 ; mutable password : string
448 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
449 ; mutable memused : memsize
450 ; mutable gen : gen
451 ; mutable throttle : (page list * int * float) option
452 ; mutable autoscroll : int option
453 ; mutable ghyll : (int option -> unit)
454 ; mutable help : helpitem array
455 ; mutable docinfo : (int * string) list
456 ; mutable texid : GlTex.texture_id option
457 ; hists : hists
458 ; mutable prevzoom : float
459 ; mutable progress : float
460 ; mutable redisplay : bool
461 ; mutable mpos : mpos
462 ; mutable keystate : keystate
463 ; mutable glinks : bool
464 ; mutable prevcolumns : (columns * float) option
466 and hists =
467 { pat : string circbuf
468 ; pag : string circbuf
469 ; nav : anchor circbuf
470 ; sel : string circbuf
474 let defconf =
475 { scrollbw = 7
476 ; scrollh = 12
477 ; icase = true
478 ; preload = true
479 ; pagebias = 0
480 ; verbose = false
481 ; debug = false
482 ; scrollstep = 24
483 ; hscrollstep = 24
484 ; maxhfit = true
485 ; crophack = false
486 ; autoscrollstep = 2
487 ; maxwait = None
488 ; hlinks = false
489 ; underinfo = false
490 ; interpagespace = 2
491 ; zoom = 1.0
492 ; presentation = false
493 ; angle = 0
494 ; winw = 900
495 ; winh = 900
496 ; savebmarks = true
497 ; proportional = true
498 ; trimmargins = false
499 ; trimfuzz = (0,0,0,0)
500 ; memlimit = 32 lsl 20
501 ; texcount = 256
502 ; sliceheight = 24
503 ; thumbw = 76
504 ; jumpback = true
505 ; bgcolor = (0.5, 0.5, 0.5)
506 ; bedefault = false
507 ; scrollbarinpm = true
508 ; tilew = 2048
509 ; tileh = 2048
510 ; mustoresize = 256 lsl 20
511 ; checkers = true
512 ; aalevel = 8
513 ; urilauncher =
514 (match platform with
515 | Plinux | Pfreebsd | Pdragonflybsd
516 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
517 | Posx -> "open \"%s\""
518 | Pcygwin -> "cygstart \"%s\""
519 | Punknown -> "echo %s")
520 ; pathlauncher = "lp \"%s\""
521 ; selcmd =
522 (match platform with
523 | Plinux | Pfreebsd | Pdragonflybsd
524 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
525 | Posx -> "pbcopy"
526 | Pcygwin -> "wsel"
527 | Punknown -> "cat")
528 ; colorspace = Rgb
529 ; invert = false
530 ; colorscale = 1.0
531 ; redirectstderr = false
532 ; ghyllscroll = None
533 ; columns = Csingle
534 ; beyecolumns = None
535 ; updatecurs = false
536 ; hfsize = 12
537 ; pgscale = 1.0
538 ; keyhashes =
539 let mk n = (n, Hashtbl.create 1) in
540 [ mk "global"
541 ; mk "info"
542 ; mk "help"
543 ; mk "outline"
544 ; mk "listview"
545 ; mk "birdseye"
546 ; mk "textentry"
547 ; mk "links"
548 ; mk "view"
553 let findkeyhash c name =
554 try List.assoc name c.keyhashes
555 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
558 let conf = { defconf with angle = defconf.angle };;
560 let pgscale h = truncate (float h *. conf.pgscale);;
562 type fontstate =
563 { mutable fontsize : int
564 ; mutable wwidth : float
565 ; mutable maxrows : int
569 let fstate =
570 { fontsize = 14
571 ; wwidth = nan
572 ; maxrows = -1
576 let setfontsize n =
577 fstate.fontsize <- n;
578 fstate.wwidth <- measurestr fstate.fontsize "w";
579 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
582 let geturl s =
583 let colonpos = try String.index s ':' with Not_found -> -1 in
584 let len = String.length s in
585 if colonpos >= 0 && colonpos + 3 < len
586 then (
587 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
588 then
589 let schemestartpos =
590 try String.rindex_from s colonpos ' '
591 with Not_found -> -1
593 let scheme =
594 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
596 match scheme with
597 | "http" | "ftp" | "mailto" ->
598 let epos =
599 try String.index_from s colonpos ' '
600 with Not_found -> len
602 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
603 | _ -> ""
604 else ""
606 else ""
609 let gotouri uri =
610 if String.length conf.urilauncher = 0
611 then print_endline uri
612 else (
613 let url = geturl uri in
614 if String.length url = 0
615 then print_endline uri
616 else
617 let re = Str.regexp "%s" in
618 let command = Str.global_replace re url conf.urilauncher in
619 try popen command []
620 with exn ->
621 Printf.eprintf
622 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
623 flush stderr;
627 let version () =
628 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
629 (platform_to_string platform) Sys.word_size Sys.ocaml_version
632 let makehelp () =
633 let strings = version () :: "" :: Help.keys in
634 Array.of_list (
635 List.map (fun s ->
636 let url = geturl s in
637 if String.length url > 0
638 then (s, 0, Action (fun u -> gotouri url; u))
639 else (s, 0, Noaction)
640 ) strings);
643 let noghyll _ = ();;
644 let firstgeomcmds = "", [];;
646 let state =
647 { sr = Unix.stdin
648 ; sw = Unix.stdin
649 ; wsfd = Unix.stdin
650 ; errfd = None
651 ; stderr = Unix.stderr
652 ; errmsgs = Buffer.create 0
653 ; newerrmsgs = false
654 ; x = 0
655 ; y = 0
656 ; w = 0
657 ; scrollw = 0
658 ; hscrollh = 0
659 ; anchor = emptyanchor
660 ; ranchors = []
661 ; layout = []
662 ; maxy = max_int
663 ; tilelru = Queue.create ()
664 ; pagemap = Hashtbl.create 10
665 ; tilemap = Hashtbl.create 10
666 ; pdims = []
667 ; pagecount = 0
668 ; currently = Idle
669 ; mstate = Mnone
670 ; rects = []
671 ; rects1 = []
672 ; text = ""
673 ; mode = View
674 ; fullscreen = None
675 ; searchpattern = ""
676 ; outlines = [||]
677 ; bookmarks = []
678 ; path = ""
679 ; password = ""
680 ; geomcmds = firstgeomcmds
681 ; hists =
682 { nav = cbnew 10 (0, 0.0)
683 ; pat = cbnew 10 ""
684 ; pag = cbnew 10 ""
685 ; sel = cbnew 10 ""
687 ; memused = 0
688 ; gen = 0
689 ; throttle = None
690 ; autoscroll = None
691 ; ghyll = noghyll
692 ; help = makehelp ()
693 ; docinfo = []
694 ; texid = None
695 ; prevzoom = 1.0
696 ; progress = -1.0
697 ; uioh = nouioh
698 ; redisplay = true
699 ; mpos = (-1, -1)
700 ; keystate = KSnone
701 ; glinks = false
702 ; prevcolumns = None
706 let vlog fmt =
707 if conf.verbose
708 then
709 Printf.kprintf prerr_endline fmt
710 else
711 Printf.kprintf ignore fmt
714 let launchpath () =
715 if String.length conf.pathlauncher = 0
716 then print_endline state.path
717 else (
718 let re = Str.regexp "%s" in
719 let command = Str.global_replace re state.path conf.pathlauncher in
720 try popen command []
721 with exn ->
722 Printf.eprintf
723 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
724 flush stderr;
728 module Ne = struct
729 type 'a t = | Res of 'a | Exn of exn;;
731 let pipe () =
732 try Res (Unix.pipe ())
733 with exn -> Exn exn
736 let clo fd f =
737 try Unix.close fd
738 with exn -> f (Printexc.to_string exn)
741 let dup fd =
742 try Res (Unix.dup fd)
743 with exn -> Exn exn
746 let dup2 fd1 fd2 =
747 try Res (Unix.dup2 fd1 fd2)
748 with exn -> Exn exn
750 end;;
752 let redirectstderr () =
753 let clofail what errmsg = dolog "failed to close %s: %s" what errmsg in
754 if conf.redirectstderr
755 then
756 match Ne.pipe () with
757 | Ne.Exn exn ->
758 dolog "failed to create stderr redirection pipes: %s"
759 (Printexc.to_string exn)
761 | Ne.Res (r, w) ->
762 begin match Ne.dup Unix.stderr with
763 | Ne.Exn exn ->
764 dolog "failed to dup stderr: %s" (Printexc.to_string exn);
765 Ne.clo r (clofail "pipe/r");
766 Ne.clo w (clofail "pipe/w");
768 | Ne.Res dupstderr ->
769 begin match Ne.dup2 w Unix.stderr with
770 | Ne.Exn exn ->
771 dolog "failed to dup2 to stderr: %s"
772 (Printexc.to_string exn);
773 Ne.clo dupstderr (clofail "stderr duplicate");
774 Ne.clo r (clofail "redir pipe/r");
775 Ne.clo w (clofail "redir pipe/w");
777 | Ne.Res () ->
778 state.stderr <- dupstderr;
779 state.errfd <- Some r;
780 end;
782 else (
783 state.newerrmsgs <- false;
784 begin match state.errfd with
785 | Some fd ->
786 begin match Ne.dup2 state.stderr Unix.stderr with
787 | Ne.Exn exn ->
788 dolog "failed to dup2 original stderr: %s"
789 (Printexc.to_string exn)
790 | Ne.Res () ->
791 Ne.clo fd (clofail "dup of stderr");
792 Unix.dup2 state.stderr Unix.stderr;
793 state.errfd <- None;
794 end;
795 | None -> ()
796 end;
797 prerr_string (Buffer.contents state.errmsgs);
798 flush stderr;
799 Buffer.clear state.errmsgs;
803 module G =
804 struct
805 let postRedisplay who =
806 if conf.verbose
807 then prerr_endline ("redisplay for " ^ who);
808 state.redisplay <- true;
810 end;;
812 let getopaque pageno =
813 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
814 with Not_found -> None
817 let putopaque pageno opaque =
818 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
821 let pagetranslatepoint l x y =
822 let dy = y - l.pagedispy in
823 let y = dy + l.pagey in
824 let dx = x - l.pagedispx in
825 let x = dx + l.pagex in
826 (x, y);
829 let getunder x y =
830 let rec f = function
831 | l :: rest ->
832 begin match getopaque l.pageno with
833 | Some opaque ->
834 let x0 = l.pagedispx in
835 let x1 = x0 + l.pagevw in
836 let y0 = l.pagedispy in
837 let y1 = y0 + l.pagevh in
838 if y >= y0 && y <= y1 && x >= x0 && x <= x1
839 then
840 let px, py = pagetranslatepoint l x y in
841 match whatsunder opaque px py with
842 | Unone -> f rest
843 | under -> under
844 else f rest
845 | _ ->
846 f rest
848 | [] -> Unone
850 f state.layout
853 let showtext c s =
854 state.text <- Printf.sprintf "%c%s" c s;
855 G.postRedisplay "showtext";
858 let undertext = function
859 | Unone -> "none"
860 | Ulinkuri s -> s
861 | Ulinkgoto (pageno, _) -> Printf.sprintf "%s: page %d" state.path (pageno+1)
862 | Utext s -> "font: " ^ s
863 | Uunexpected s -> "unexpected: " ^ s
864 | Ulaunch s -> "launch: " ^ s
865 | Unamed s -> "named: " ^ s
866 | Uremote (filename, pageno) ->
867 Printf.sprintf "%s: page %d" filename (pageno+1)
870 let updateunder x y =
871 match getunder x y with
872 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
873 | Ulinkuri uri ->
874 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
875 Wsi.setcursor Wsi.CURSOR_INFO
876 | Ulinkgoto (pageno, _) ->
877 if conf.underinfo
878 then showtext 'p' ("age: " ^ string_of_int (pageno+1));
879 Wsi.setcursor Wsi.CURSOR_INFO
880 | Utext s ->
881 if conf.underinfo then showtext 'f' ("ont: " ^ s);
882 Wsi.setcursor Wsi.CURSOR_TEXT
883 | Uunexpected s ->
884 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
885 Wsi.setcursor Wsi.CURSOR_INHERIT
886 | Ulaunch s ->
887 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
888 Wsi.setcursor Wsi.CURSOR_INHERIT
889 | Unamed s ->
890 if conf.underinfo then showtext 'n' ("amed: " ^ s);
891 Wsi.setcursor Wsi.CURSOR_INHERIT
892 | Uremote (filename, pageno) ->
893 if conf.underinfo then showtext 'r'
894 (Printf.sprintf "emote: %s (%d)" filename (pageno+1));
895 Wsi.setcursor Wsi.CURSOR_INFO
898 let showlinktype under =
899 if conf.underinfo
900 then
901 match under with
902 | Unone -> ()
903 | under ->
904 let s = undertext under in
905 showtext ' ' s
908 let addchar s c =
909 let b = Buffer.create (String.length s + 1) in
910 Buffer.add_string b s;
911 Buffer.add_char b c;
912 Buffer.contents b;
915 let colorspace_of_string s =
916 match String.lowercase s with
917 | "rgb" -> Rgb
918 | "bgr" -> Bgr
919 | "gray" -> Gray
920 | _ -> failwith "invalid colorspace"
923 let int_of_colorspace = function
924 | Rgb -> 0
925 | Bgr -> 1
926 | Gray -> 2
929 let colorspace_of_int = function
930 | 0 -> Rgb
931 | 1 -> Bgr
932 | 2 -> Gray
933 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
936 let colorspace_to_string = function
937 | Rgb -> "rgb"
938 | Bgr -> "bgr"
939 | Gray -> "gray"
942 let intentry_with_suffix text key =
943 let c =
944 if key >= 32 && key < 127
945 then Char.chr key
946 else '\000'
948 match Char.lowercase c with
949 | '0' .. '9' ->
950 let text = addchar text c in
951 TEcont text
953 | 'k' | 'm' | 'g' ->
954 let text = addchar text c in
955 TEcont text
957 | _ ->
958 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
959 TEcont text
962 let multicolumns_to_string (n, a, b) =
963 if a = 0 && b = 0
964 then Printf.sprintf "%d" n
965 else Printf.sprintf "%d,%d,%d" n a b;
968 let multicolumns_of_string s =
970 (int_of_string s, 0, 0)
971 with _ ->
972 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
975 let readcmd fd =
976 let s = "xxxx" in
977 let n = Unix.read fd s 0 4 in
978 if n != 4 then failwith "incomplete read(len)";
979 let len = 0
980 lor (Char.code s.[0] lsl 24)
981 lor (Char.code s.[1] lsl 16)
982 lor (Char.code s.[2] lsl 8)
983 lor (Char.code s.[3] lsl 0)
985 let s = String.create len in
986 let n = Unix.read fd s 0 len in
987 if n != len then failwith "incomplete read(data)";
991 let btod b = if b then 1 else 0;;
993 let wcmd fmt =
994 let b = Buffer.create 16 in
995 Buffer.add_string b "llll";
996 Printf.kbprintf
997 (fun b ->
998 let s = Buffer.contents b in
999 let n = String.length s in
1000 let len = n - 4 in
1001 (* dolog "wcmd %S" (String.sub s 4 len); *)
1002 s.[0] <- Char.chr ((len lsr 24) land 0xff);
1003 s.[1] <- Char.chr ((len lsr 16) land 0xff);
1004 s.[2] <- Char.chr ((len lsr 8) land 0xff);
1005 s.[3] <- Char.chr (len land 0xff);
1006 let n' = Unix.write state.sw s 0 n in
1007 if n' != n then failwith "write failed";
1008 ) b fmt;
1011 let calcips h =
1012 if conf.presentation
1013 then
1014 let d = conf.winh - h in
1015 max conf.interpagespace ((d + 1) / 2)
1016 else
1017 conf.interpagespace
1020 let calcheight () =
1021 let rec f pn ph pi fh l =
1022 match l with
1023 | (n, _, h, _) :: rest ->
1024 let ips = calcips h in
1025 let fh =
1026 if conf.presentation
1027 then fh+ips
1028 else (
1029 if isbirdseye state.mode && pn = 0
1030 then fh + ips
1031 else fh
1034 let fh = fh + ((n - pn) * (ph + pi)) in
1035 f n h ips fh rest;
1037 | [] ->
1038 let inc =
1039 if conf.presentation || (isbirdseye state.mode && pn = 0)
1040 then 0
1041 else -pi
1043 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
1044 max 0 fh
1046 let fh = f 0 0 0 0 state.pdims in
1050 let calcheight () =
1051 match conf.columns with
1052 | Csingle -> calcheight ()
1053 | Cmulti ((c, _, _), b) ->
1054 let rec loop y h n =
1055 if n < 0
1056 then loop y h (n+1)
1057 else (
1058 if n = Array.length b
1059 then y + h
1060 else
1061 let (_, _, y', (_, _, h', _)) = b.(n) in
1062 let y = min y y'
1063 and h = max h h' in
1064 loop y h (n+1)
1067 loop max_int 0 (((Array.length b - 1) / c) * c)
1068 | Csplit (_, b) ->
1069 if Array.length b > 0
1070 then
1071 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1072 y + h
1073 else 0
1076 let getpageyh pageno =
1077 let rec f pn ph pi y l =
1078 match l with
1079 | (n, _, h, _) :: rest ->
1080 let ips = calcips h in
1081 if n >= pageno
1082 then
1083 let h = if n = pageno then h else ph in
1084 if conf.presentation && n = pageno
1085 then
1086 y + (pageno - pn) * (ph + pi) + pi, h
1087 else
1088 y + (pageno - pn) * (ph + pi), h
1089 else
1090 let y = y + (if conf.presentation then pi else 0) in
1091 let y = y + (n - pn) * (ph + pi) in
1092 f n h ips y rest
1094 | [] ->
1095 y + (pageno - pn) * (ph + pi), ph
1097 f 0 0 0 0 state.pdims
1100 let getpageyh pageno =
1101 match conf.columns with
1102 | Csingle -> getpageyh pageno
1103 | Cmulti (_, b) ->
1104 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1105 y, h
1106 | Csplit (c, b) ->
1107 let n = pageno*c in
1108 let (_, _, y, (_, _, h, _)) = b.(n) in
1109 y, h
1112 let getpagedim pageno =
1113 let rec f ppdim l =
1114 match l with
1115 | (n, _, _, _) as pdim :: rest ->
1116 if n >= pageno
1117 then (if n = pageno then pdim else ppdim)
1118 else f pdim rest
1120 | [] -> ppdim
1122 f (-1, -1, -1, -1) state.pdims
1125 let getpagey pageno = fst (getpageyh pageno);;
1127 let nogeomcmds cmds =
1128 match cmds with
1129 | s, [] -> String.length s = 0
1130 | _ -> false
1133 let layout1 y sh =
1134 let sh = sh - state.hscrollh in
1135 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
1136 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
1137 match pdims with
1138 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
1139 let ips = calcips h in
1140 let yinc =
1141 if conf.presentation || (isbirdseye state.mode && pageno = 0)
1142 then ips
1143 else 0
1145 (w, h, ips, xoff), rest, pdimno + 1, yinc
1146 | _ ->
1147 prev, pdims, pdimno, 0
1149 let dy = dy + yinc in
1150 let py = py + yinc in
1151 if pageno = state.pagecount || dy >= sh
1152 then
1153 accu
1154 else
1155 let vy = y + dy in
1156 if py + h <= vy - yinc
1157 then
1158 let py = py + h + ips in
1159 let dy = max 0 (py - y) in
1160 f ~pageno:(pageno+1)
1161 ~pdimno
1162 ~prev:curr
1165 ~pdims:rest
1166 ~accu
1167 else
1168 let pagey = vy - py in
1169 let pagevh = h - pagey in
1170 let pagevh = min (sh - dy) pagevh in
1171 let off = if yinc > 0 then py - vy else 0 in
1172 let py = py + h + ips in
1173 let pagex, dx =
1174 let xoff = xoff +
1175 if state.w < conf.winw - state.scrollw
1176 then (conf.winw - state.scrollw - state.w) / 2
1177 else 0
1179 let dispx = xoff + state.x in
1180 if dispx < 0
1181 then (-dispx, 0)
1182 else (0, dispx)
1184 let pagevw =
1185 let lw = w - pagex in
1186 min lw (conf.winw - state.scrollw)
1188 let e =
1189 { pageno = pageno
1190 ; pagedimno = pdimno
1191 ; pagew = w
1192 ; pageh = h
1193 ; pagex = pagex
1194 ; pagey = pagey + off
1195 ; pagevw = pagevw
1196 ; pagevh = pagevh - off
1197 ; pagedispx = dx
1198 ; pagedispy = dy + off
1199 ; pagecol = 0
1202 let accu = e :: accu in
1203 f ~pageno:(pageno+1)
1204 ~pdimno
1205 ~prev:curr
1207 ~dy:(dy+pagevh+ips)
1208 ~pdims:rest
1209 ~accu
1211 let accu =
1213 ~pageno:0
1214 ~pdimno:~-1
1215 ~prev:(0,0,0,0)
1216 ~py:0
1217 ~dy:0
1218 ~pdims:state.pdims
1219 ~accu:[]
1221 List.rev accu
1224 let layoutN ((columns, coverA, coverB), b) y sh =
1225 let sh = sh - state.hscrollh in
1226 let rec fold accu n =
1227 if n = Array.length b
1228 then accu
1229 else
1230 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1231 if (vy - y) > sh &&
1232 (n = coverA - 1
1233 || n = state.pagecount - coverB
1234 || (n - coverA) mod columns = columns - 1)
1235 then accu
1236 else
1237 let accu =
1238 if vy + h > y
1239 then
1240 let pagey = max 0 (y - vy) in
1241 let pagedispy = if pagey > 0 then 0 else vy - y in
1242 let pagedispx, pagex =
1243 let pdx =
1244 if n = coverA - 1 || n = state.pagecount - coverB
1245 then state.x + (conf.winw - state.scrollw - w) / 2
1246 else dx + xoff + state.x
1248 if pdx < 0
1249 then 0, -pdx
1250 else pdx, 0
1252 let pagevw =
1253 let vw = conf.winw - state.scrollw - pagedispx in
1254 let pw = w - pagex in
1255 min vw pw
1257 let pagevh = min (h - pagey) (sh - pagedispy) in
1258 if pagevw > 0 && pagevh > 0
1259 then
1260 let e =
1261 { pageno = n
1262 ; pagedimno = pdimno
1263 ; pagew = w
1264 ; pageh = h
1265 ; pagex = pagex
1266 ; pagey = pagey
1267 ; pagevw = pagevw
1268 ; pagevh = pagevh
1269 ; pagedispx = pagedispx
1270 ; pagedispy = pagedispy
1271 ; pagecol = 0
1274 e :: accu
1275 else
1276 accu
1277 else
1278 accu
1280 fold accu (n+1)
1282 List.rev (fold [] 0)
1285 let layoutS (columns, b) y sh =
1286 let sh = sh - state.hscrollh in
1287 let rec fold accu n =
1288 if n = Array.length b
1289 then accu
1290 else
1291 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1292 if (vy - y) > sh
1293 then accu
1294 else
1295 let accu =
1296 if vy + pageh > y
1297 then
1298 let x = xoff + state.x in
1299 let pagey = max 0 (y - vy) in
1300 let pagedispy = if pagey > 0 then 0 else vy - y in
1301 let pagedispx, pagex =
1302 if px = 0
1303 then (
1304 if x < 0
1305 then 0, -x
1306 else x, 0
1308 else (
1309 let px = px - x in
1310 if px < 0
1311 then -px, 0
1312 else 0, px
1315 let pagecolw = pagew/columns in
1316 let pagedispx =
1317 if pagecolw < conf.winw
1318 then pagedispx + ((conf.winw - state.scrollw - pagecolw) / 2)
1319 else pagedispx
1321 let pagevw =
1322 let vw = conf.winw - pagedispx - state.scrollw in
1323 let pw = pagew - pagex in
1324 min vw pw
1326 let pagevw = min pagevw pagecolw in
1327 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1328 if pagevw > 0 && pagevh > 0
1329 then
1330 let e =
1331 { pageno = n/columns
1332 ; pagedimno = pdimno
1333 ; pagew = pagew
1334 ; pageh = pageh
1335 ; pagex = pagex
1336 ; pagey = pagey
1337 ; pagevw = pagevw
1338 ; pagevh = pagevh
1339 ; pagedispx = pagedispx
1340 ; pagedispy = pagedispy
1341 ; pagecol = n mod columns
1344 e :: accu
1345 else
1346 accu
1347 else
1348 accu
1350 fold accu (n+1)
1352 List.rev (fold [] 0)
1355 let layout y sh =
1356 if nogeomcmds state.geomcmds
1357 then
1358 match conf.columns with
1359 | Csingle -> layout1 y sh
1360 | Cmulti c -> layoutN c y sh
1361 | Csplit s -> layoutS s y sh
1362 else []
1365 let clamp incr =
1366 let y = state.y + incr in
1367 let y = max 0 y in
1368 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1372 let itertiles l f =
1373 let tilex = l.pagex mod conf.tilew in
1374 let tiley = l.pagey mod conf.tileh in
1376 let col = l.pagex / conf.tilew in
1377 let row = l.pagey / conf.tileh in
1379 let rec rowloop row y0 dispy h =
1380 if h = 0
1381 then ()
1382 else (
1383 let dh = conf.tileh - y0 in
1384 let dh = min h dh in
1385 let rec colloop col x0 dispx w =
1386 if w = 0
1387 then ()
1388 else (
1389 let dw = conf.tilew - x0 in
1390 let dw = min w dw in
1392 f col row dispx dispy x0 y0 dw dh;
1393 colloop (col+1) 0 (dispx+dw) (w-dw)
1396 colloop col tilex l.pagedispx l.pagevw;
1397 rowloop (row+1) 0 (dispy+dh) (h-dh)
1400 if l.pagevw > 0 && l.pagevh > 0
1401 then rowloop row tiley l.pagedispy l.pagevh;
1404 let gettileopaque l col row =
1405 let key =
1406 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1408 try Some (Hashtbl.find state.tilemap key)
1409 with Not_found -> None
1412 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1413 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1414 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1417 let drawtiles l color =
1418 GlDraw.color color;
1419 let f col row x y tilex tiley w h =
1420 match gettileopaque l col row with
1421 | Some (opaque, _, t) ->
1422 let params = x, y, w, h, tilex, tiley in
1423 if conf.invert
1424 then (
1425 Gl.enable `blend;
1426 GlFunc.blend_func `zero `one_minus_src_color;
1428 drawtile params opaque;
1429 if conf.invert
1430 then Gl.disable `blend;
1431 if conf.debug
1432 then (
1433 let s = Printf.sprintf
1434 "%d[%d,%d] %f sec"
1435 l.pageno col row t
1437 let w = measurestr fstate.fontsize s in
1438 GlMisc.push_attrib [`current];
1439 GlDraw.color (0.0, 0.0, 0.0);
1440 GlDraw.rect
1441 (float (x-2), float (y-2))
1442 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1443 GlDraw.color (1.0, 1.0, 1.0);
1444 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1445 GlMisc.pop_attrib ();
1448 | _ ->
1449 let w =
1450 let lw = conf.winw - state.scrollw - x in
1451 min lw w
1452 and h =
1453 let lh = conf.winh - y in
1454 min lh h
1456 begin match state.texid with
1457 | Some id ->
1458 Gl.enable `texture_2d;
1459 GlTex.bind_texture `texture_2d id;
1460 let x0 = float x
1461 and y0 = float y
1462 and x1 = float (x+w)
1463 and y1 = float (y+h) in
1465 let tw = float w /. 64.0
1466 and th = float h /. 64.0 in
1467 let tx0 = float tilex /. 64.0
1468 and ty0 = float tiley /. 64.0 in
1469 let tx1 = tx0 +. tw
1470 and ty1 = ty0 +. th in
1471 GlDraw.begins `quads;
1472 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1473 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1474 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1475 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1476 GlDraw.ends ();
1478 Gl.disable `texture_2d;
1479 | None ->
1480 GlDraw.color (1.0, 1.0, 1.0);
1481 GlDraw.rect
1482 (float x, float y)
1483 (float (x+w), float (y+h));
1484 end;
1485 if w > 128 && h > fstate.fontsize + 10
1486 then (
1487 GlDraw.color (0.0, 0.0, 0.0);
1488 let c, r =
1489 if conf.verbose
1490 then (col*conf.tilew, row*conf.tileh)
1491 else col, row
1493 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1495 GlDraw.color color;
1497 itertiles l f
1500 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1502 let tilevisible1 l x y =
1503 let ax0 = l.pagex
1504 and ax1 = l.pagex + l.pagevw
1505 and ay0 = l.pagey
1506 and ay1 = l.pagey + l.pagevh in
1508 let bx0 = x
1509 and by0 = y in
1510 let bx1 = min (bx0 + conf.tilew) l.pagew
1511 and by1 = min (by0 + conf.tileh) l.pageh in
1513 let rx0 = max ax0 bx0
1514 and ry0 = max ay0 by0
1515 and rx1 = min ax1 bx1
1516 and ry1 = min ay1 by1 in
1518 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1519 nonemptyintersection
1522 let tilevisible layout n x y =
1523 let rec findpageinlayout m = function
1524 | l :: rest when l.pageno = n ->
1525 tilevisible1 l x y || (
1526 match conf.columns with
1527 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
1528 | _ -> false
1530 | _ :: rest -> findpageinlayout 0 rest
1531 | [] -> false
1533 findpageinlayout 0 layout;
1536 let tileready l x y =
1537 tilevisible1 l x y &&
1538 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1541 let tilepage n p layout =
1542 let rec loop = function
1543 | l :: rest ->
1544 if l.pageno = n
1545 then
1546 let f col row _ _ _ _ _ _ =
1547 if state.currently = Idle
1548 then
1549 match gettileopaque l col row with
1550 | Some _ -> ()
1551 | None ->
1552 let x = col*conf.tilew
1553 and y = row*conf.tileh in
1554 let w =
1555 let w = l.pagew - x in
1556 min w conf.tilew
1558 let h =
1559 let h = l.pageh - y in
1560 min h conf.tileh
1562 wcmd "tile %s %d %d %d %d" p x y w h;
1563 state.currently <-
1564 Tiling (
1565 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1566 conf.tilew, conf.tileh
1569 itertiles l f;
1570 else
1571 loop rest
1573 | [] -> ()
1575 if nogeomcmds state.geomcmds
1576 then loop layout;
1579 let preloadlayout visiblepages =
1580 let presentation = conf.presentation in
1581 let interpagespace = conf.interpagespace in
1582 conf.presentation <- false;
1583 conf.interpagespace <- 0;
1584 let y =
1585 match visiblepages with
1586 | [] -> if state.y >= state.maxy then state.maxy else 0
1587 | l :: _ -> getpagey l.pageno + (l.pagey - min 0 l.pagedispy)
1589 let y = if y < conf.winh then 0 else y - conf.winh in
1590 let h = conf.winh*3 in
1591 let pages = layout y h in
1592 conf.presentation <- presentation;
1593 conf.interpagespace <- interpagespace;
1594 pages;
1597 let load pages =
1598 let rec loop pages =
1599 if state.currently != Idle
1600 then ()
1601 else
1602 match pages with
1603 | l :: rest ->
1604 begin match getopaque l.pageno with
1605 | None ->
1606 wcmd "page %d %d" l.pageno l.pagedimno;
1607 state.currently <- Loading (l, state.gen);
1608 | Some opaque ->
1609 tilepage l.pageno opaque pages;
1610 loop rest
1611 end;
1612 | _ -> ()
1614 if nogeomcmds state.geomcmds
1615 then loop pages
1618 let preload pages =
1619 load pages;
1620 if conf.preload && state.currently = Idle
1621 then load (preloadlayout pages);
1624 let layoutready layout =
1625 let rec fold all ls =
1626 all && match ls with
1627 | l :: rest ->
1628 let seen = ref false in
1629 let allvisible = ref true in
1630 let foo col row _ _ _ _ _ _ =
1631 seen := true;
1632 allvisible := !allvisible &&
1633 begin match gettileopaque l col row with
1634 | Some _ -> true
1635 | None -> false
1638 itertiles l foo;
1639 fold (!seen && !allvisible) rest
1640 | [] -> true
1642 let alltilesvisible = fold true layout in
1643 alltilesvisible;
1646 let gotoy y =
1647 let y = bound y 0 state.maxy in
1648 let y, layout, proceed =
1649 match conf.maxwait with
1650 | Some time when state.ghyll == noghyll ->
1651 begin match state.throttle with
1652 | None ->
1653 let layout = layout y conf.winh in
1654 let ready = layoutready layout in
1655 if not ready
1656 then (
1657 load layout;
1658 state.throttle <- Some (layout, y, now ());
1660 else G.postRedisplay "gotoy showall (None)";
1661 y, layout, ready
1662 | Some (_, _, started) ->
1663 let dt = now () -. started in
1664 if dt > time
1665 then (
1666 state.throttle <- None;
1667 let layout = layout y conf.winh in
1668 load layout;
1669 G.postRedisplay "maxwait";
1670 y, layout, true
1672 else -1, [], false
1675 | _ ->
1676 let layout = layout y conf.winh in
1677 if true || layoutready layout
1678 then G.postRedisplay "gotoy ready";
1679 y, layout, true
1681 if proceed
1682 then (
1683 state.y <- y;
1684 state.layout <- layout;
1685 begin match state.mode with
1686 | LinkNav (Ltexact (pageno, linkno)) ->
1687 let rec loop = function
1688 | [] ->
1689 state.mode <- LinkNav (Ltgendir 0)
1690 | l :: _ when l.pageno = pageno ->
1691 begin match getopaque pageno with
1692 | None ->
1693 state.mode <- LinkNav (Ltgendir 0)
1694 | Some opaque ->
1695 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1696 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1697 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1698 then state.mode <- LinkNav (Ltgendir 0)
1700 | _ :: rest -> loop rest
1702 loop layout
1703 | _ -> ()
1704 end;
1705 begin match state.mode with
1706 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1707 if not (pagevisible layout pageno)
1708 then (
1709 match state.layout with
1710 | [] -> ()
1711 | l :: _ ->
1712 state.mode <- Birdseye (
1713 conf, leftx, l.pageno, hooverpageno, anchor
1716 | LinkNav (Ltgendir dir as lt) ->
1717 let linknav =
1718 let rec loop = function
1719 | [] -> lt
1720 | l :: rest ->
1721 match getopaque l.pageno with
1722 | None -> loop rest
1723 | Some opaque ->
1724 let link =
1725 let ld =
1726 if dir = 0
1727 then LDfirstvisible (l.pagex, l.pagey, dir)
1728 else (
1729 if dir > 0 then LDfirst else LDlast
1732 findlink opaque ld
1734 match link with
1735 | Lnotfound -> loop rest
1736 | Lfound n ->
1737 showlinktype (getlink opaque n);
1738 Ltexact (l.pageno, n)
1740 loop state.layout
1742 state.mode <- LinkNav linknav
1743 | _ -> ()
1744 end;
1745 preload layout;
1747 state.ghyll <- noghyll;
1748 if conf.updatecurs
1749 then (
1750 let mx, my = state.mpos in
1751 updateunder mx my;
1755 let conttiling pageno opaque =
1756 tilepage pageno opaque
1757 (if conf.preload then preloadlayout state.layout else state.layout)
1760 let gotoy_and_clear_text y =
1761 if not conf.verbose then state.text <- "";
1762 gotoy y;
1765 let getanchor () =
1766 match state.layout with
1767 | [] -> emptyanchor
1768 | l :: _ ->
1769 let coloff = l.pagecol * l.pageh in
1770 (l.pageno,
1771 (float (l.pagey - l.pagedispy) +. float coloff) /. float l.pageh)
1774 let getanchory (n, top) =
1775 let y, h = getpageyh n in
1776 y + (truncate (top *. float h));
1779 let gotoanchor anchor =
1780 gotoy (getanchory anchor);
1783 let addnav () =
1784 cbput state.hists.nav (getanchor ());
1787 let getnav dir =
1788 let anchor = cbgetc state.hists.nav dir in
1789 getanchory anchor;
1792 let gotoghyll y =
1793 let scroll f n a b =
1794 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1795 let snake f a b =
1796 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1797 if f < a
1798 then s (float f /. float a)
1799 else (
1800 if f > b
1801 then 1.0 -. s ((float (f-b) /. float (n-b)))
1802 else 1.0
1805 snake f a b
1806 and summa f n a b =
1807 (* courtesy:
1808 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1809 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1810 let iv1 = iv f in
1811 let ins = float a *. iv1
1812 and outs = float (n-b) *. iv1 in
1813 let ones = b - a in
1814 ins +. outs +. float ones
1816 let rec set (_N, _A, _B) y sy =
1817 let sum = summa 1.0 _N _A _B in
1818 let dy = float (y - sy) in
1819 state.ghyll <- (
1820 let rec gf n y1 o =
1821 if n >= _N
1822 then state.ghyll <- noghyll
1823 else
1824 let go n =
1825 let s = scroll n _N _A _B in
1826 let y1 = y1 +. ((s *. dy) /. sum) in
1827 gotoy_and_clear_text (truncate y1);
1828 state.ghyll <- gf (n+1) y1;
1830 match o with
1831 | None -> go n
1832 | Some y' -> set (_N/2, 0, 0) y' state.y
1834 gf 0 (float state.y)
1837 match conf.ghyllscroll with
1838 | None ->
1839 gotoy_and_clear_text y
1840 | Some nab ->
1841 if state.ghyll == noghyll
1842 then set nab y state.y
1843 else state.ghyll (Some y)
1846 let gotopage n top =
1847 let y, h = getpageyh n in
1848 let y = y + (truncate (top *. float h)) in
1849 gotoghyll y
1852 let gotopage1 n top =
1853 let y = getpagey n in
1854 let y = y + top in
1855 gotoghyll y
1858 let invalidate s f =
1859 state.layout <- [];
1860 state.pdims <- [];
1861 state.rects <- [];
1862 state.rects1 <- [];
1863 match state.geomcmds with
1864 | ps, [] when String.length ps = 0 ->
1865 f ();
1866 state.geomcmds <- s, [];
1868 | ps, [] ->
1869 state.geomcmds <- ps, [s, f];
1871 | ps, (s', _) :: rest when s' = s ->
1872 state.geomcmds <- ps, ((s, f) :: rest);
1874 | ps, cmds ->
1875 state.geomcmds <- ps, ((s, f) :: cmds);
1878 let opendoc path password =
1879 state.path <- path;
1880 state.password <- password;
1881 state.gen <- state.gen + 1;
1882 state.docinfo <- [];
1884 setaalevel conf.aalevel;
1885 Wsi.settitle ("llpp " ^ Filename.basename path);
1886 wcmd "open %s\000%s\000" path password;
1887 invalidate "reqlayout"
1888 (fun () ->
1889 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1892 let scalecolor c =
1893 let c = c *. conf.colorscale in
1894 (c, c, c);
1897 let scalecolor2 (r, g, b) =
1898 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1901 let docolumns = function
1902 | Csingle -> ()
1904 | Cmulti ((columns, coverA, coverB), _) ->
1905 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1906 let rec loop pageno pdimno pdim x y rowh pdims =
1907 let rec fixrow m = if m = pageno then () else
1908 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1909 if h < rowh
1910 then (
1911 let y = y + (rowh - h) / 2 in
1912 a.(m) <- (pdimno, x, y, pdim);
1914 fixrow (m+1)
1916 if pageno = state.pagecount
1917 then fixrow (((pageno - 1) / columns) * columns)
1918 else
1919 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1920 match pdims with
1921 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1922 pdimno+1, pdim, rest
1923 | _ ->
1924 pdimno, pdim, pdims
1926 let x, y, rowh' =
1927 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1928 then (
1929 (conf.winw - state.scrollw - w) / 2,
1930 y + rowh + conf.interpagespace, h
1932 else (
1933 if (pageno - coverA) mod columns = 0
1934 then 0, y + rowh + conf.interpagespace, h
1935 else x, y, max rowh h
1938 if pageno > 1 && (pageno - coverA) mod columns = 0
1939 then fixrow (pageno - columns);
1940 a.(pageno) <- (pdimno, x, y, pdim);
1941 let x = x + w + xoff*2 + conf.interpagespace in
1942 loop (pageno+1) pdimno pdim x y rowh' pdims
1944 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1945 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1947 | Csplit (c, _) ->
1948 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1949 let rec loop pageno pdimno pdim y pdims =
1950 if pageno = state.pagecount
1951 then ()
1952 else
1953 let pdimno, ((_, w, h, _) as pdim), pdims =
1954 match pdims with
1955 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1956 pdimno+1, pdim, rest
1957 | _ ->
1958 pdimno, pdim, pdims
1960 let cw = w / c in
1961 let rec loop1 n x y =
1962 if n = c then y else (
1963 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1964 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1967 let y = loop1 0 0 y in
1968 loop (pageno+1) pdimno pdim y pdims
1970 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1971 conf.columns <- Csplit (c, a);
1974 let represent () =
1975 docolumns conf.columns;
1976 state.maxy <- calcheight ();
1977 state.hscrollh <-
1978 if state.w <= conf.winw - state.scrollw
1979 then 0
1980 else state.scrollw
1982 match state.mode with
1983 | Birdseye (_, _, pageno, _, _) ->
1984 let y, h = getpageyh pageno in
1985 let top = (conf.winh - h) / 2 in
1986 gotoy (max 0 (y - top))
1987 | _ -> gotoanchor state.anchor
1990 let reshape w h =
1991 GlDraw.viewport 0 0 w h;
1992 let firsttime = state.geomcmds == firstgeomcmds in
1993 if not firsttime && nogeomcmds state.geomcmds
1994 then state.anchor <- getanchor ();
1996 conf.winw <- w;
1997 let w = truncate (float w *. conf.zoom) - state.scrollw in
1998 let w = max w 2 in
1999 conf.winh <- h;
2000 setfontsize fstate.fontsize;
2001 GlMat.mode `modelview;
2002 GlMat.load_identity ();
2004 GlMat.mode `projection;
2005 GlMat.load_identity ();
2006 GlMat.rotate ~x:1.0 ~angle:180.0 ();
2007 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
2008 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
2010 let relx =
2011 if conf.zoom <= 1.0
2012 then 0.0
2013 else float state.x /. float state.w
2015 invalidate "geometry"
2016 (fun () ->
2017 state.w <- w;
2018 if not firsttime
2019 then state.x <- truncate (relx *. float w);
2020 let w =
2021 match conf.columns with
2022 | Csingle -> w
2023 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
2024 | Csplit (c, _) -> w * c
2026 wcmd "geometry %d %d" w h);
2029 let enttext () =
2030 let len = String.length state.text in
2031 let drawstring s =
2032 let hscrollh =
2033 match state.mode with
2034 | Textentry _
2035 | View ->
2036 let h, _, _ = state.uioh#scrollpw in
2038 | _ -> 0
2040 let rect x w =
2041 GlDraw.rect
2042 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
2043 (x+.w, float (conf.winh - hscrollh))
2046 let w = float (conf.winw - state.scrollw - 1) in
2047 if state.progress >= 0.0 && state.progress < 1.0
2048 then (
2049 GlDraw.color (0.3, 0.3, 0.3);
2050 let w1 = w *. state.progress in
2051 rect 0.0 w1;
2052 GlDraw.color (0.0, 0.0, 0.0);
2053 rect w1 (w-.w1)
2055 else (
2056 GlDraw.color (0.0, 0.0, 0.0);
2057 rect 0.0 w;
2060 GlDraw.color (1.0, 1.0, 1.0);
2061 drawstring fstate.fontsize
2062 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
2064 let s =
2065 match state.mode with
2066 | Textentry ((prefix, text, _, _, _, _), _) ->
2067 let s =
2068 if len > 0
2069 then
2070 Printf.sprintf "%s%s_ [%s]" prefix text state.text
2071 else
2072 Printf.sprintf "%s%s_" prefix text
2076 | _ -> state.text
2078 let s =
2079 if state.newerrmsgs
2080 then (
2081 if not (istextentry state.mode)
2082 then
2083 let s1 = "(press 'e' to review error messasges)" in
2084 if String.length s > 0 then s ^ " " ^ s1 else s1
2085 else s
2087 else s
2089 if String.length s > 0
2090 then drawstring s
2093 let gctiles () =
2094 let len = Queue.length state.tilelru in
2095 let rec loop qpos =
2096 if state.memused <= conf.memlimit
2097 then ()
2098 else (
2099 if qpos < len
2100 then
2101 let (k, p, s) as lruitem = Queue.pop state.tilelru in
2102 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
2103 let (_, pw, ph, _) = getpagedim n in
2105 gen = state.gen
2106 && colorspace = conf.colorspace
2107 && angle = conf.angle
2108 && pagew = pw
2109 && pageh = ph
2110 && (
2111 let layout =
2112 match state.throttle with
2113 | None ->
2114 if conf.preload
2115 then preloadlayout state.layout
2116 else state.layout
2117 | Some (layout, _, _) ->
2118 layout
2120 let x = col*conf.tilew
2121 and y = row*conf.tileh in
2122 tilevisible layout n x y
2124 then Queue.push lruitem state.tilelru
2125 else (
2126 wcmd "freetile %s" p;
2127 state.memused <- state.memused - s;
2128 state.uioh#infochanged Memused;
2129 Hashtbl.remove state.tilemap k;
2131 loop (qpos+1)
2134 loop 0
2137 let flushtiles () =
2138 Queue.iter (fun (k, p, s) ->
2139 wcmd "freetile %s" p;
2140 state.memused <- state.memused - s;
2141 state.uioh#infochanged Memused;
2142 Hashtbl.remove state.tilemap k;
2143 ) state.tilelru;
2144 Queue.clear state.tilelru;
2145 load state.layout;
2148 let logcurrently = function
2149 | Idle -> dolog "Idle"
2150 | Loading (l, gen) ->
2151 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2152 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2153 dolog
2154 "Tiling %d[%d,%d] page=%s cs=%s angle"
2155 l.pageno col row pageopaque
2156 (colorspace_to_string colorspace)
2158 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2159 angle gen conf.angle state.gen
2160 tilew tileh
2161 conf.tilew conf.tileh
2163 | Outlining _ ->
2164 dolog "outlining"
2167 let act cmds =
2168 (* dolog "%S" cmds; *)
2169 let op, args =
2170 let spacepos =
2171 try String.index cmds ' '
2172 with Not_found -> -1
2174 if spacepos = -1
2175 then cmds, ""
2176 else
2177 let l = String.length cmds in
2178 let op = String.sub cmds 0 spacepos in
2179 op, begin
2180 if l - spacepos < 2 then ""
2181 else String.sub cmds (spacepos+1) (l-spacepos-1)
2184 match op with
2185 | "clear" ->
2186 state.uioh#infochanged Pdim;
2187 state.pdims <- [];
2189 | "clearrects" ->
2190 state.rects <- state.rects1;
2191 G.postRedisplay "clearrects";
2193 | "continue" ->
2194 let n =
2195 try Scanf.sscanf args "%u" (fun n -> n)
2196 with exn ->
2197 dolog "error processing 'continue' %S: %s"
2198 cmds (Printexc.to_string exn);
2199 exit 1;
2201 state.pagecount <- n;
2202 begin match state.currently with
2203 | Outlining l ->
2204 state.currently <- Idle;
2205 state.outlines <- Array.of_list (List.rev l)
2206 | _ -> ()
2207 end;
2209 let cur, cmds = state.geomcmds in
2210 if String.length cur = 0
2211 then failwith "umpossible";
2213 begin match List.rev cmds with
2214 | [] ->
2215 state.geomcmds <- "", [];
2216 represent ();
2217 | (s, f) :: rest ->
2218 f ();
2219 state.geomcmds <- s, List.rev rest;
2220 end;
2221 if conf.maxwait = None
2222 then G.postRedisplay "continue";
2224 | "title" ->
2225 Wsi.settitle args
2227 | "msg" ->
2228 showtext ' ' args
2230 | "vmsg" ->
2231 if conf.verbose
2232 then showtext ' ' args
2234 | "progress" ->
2235 let progress, text =
2237 Scanf.sscanf args "%f %n"
2238 (fun f pos ->
2239 f, String.sub args pos (String.length args - pos))
2240 with exn ->
2241 dolog "error processing 'progress' %S: %s"
2242 cmds (Printexc.to_string exn);
2243 exit 1;
2245 state.text <- text;
2246 state.progress <- progress;
2247 G.postRedisplay "progress"
2249 | "firstmatch" ->
2250 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2252 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2253 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2254 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2255 with exn ->
2256 dolog "error processing 'firstmatch' %S: %s"
2257 cmds (Printexc.to_string exn);
2258 exit 1;
2260 let y = (getpagey pageno) + truncate y0 in
2261 addnav ();
2262 gotoy y;
2263 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2265 | "match" ->
2266 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2268 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2269 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2270 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2271 with exn ->
2272 dolog "error processing 'match' %S: %s"
2273 cmds (Printexc.to_string exn);
2274 exit 1;
2276 state.rects1 <-
2277 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2279 | "page" ->
2280 let pageopaque, t =
2282 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2283 with exn ->
2284 dolog "error processing 'page' %S: %s"
2285 cmds (Printexc.to_string exn);
2286 exit 1;
2288 begin match state.currently with
2289 | Loading (l, gen) ->
2290 vlog "page %d took %f sec" l.pageno t;
2291 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2292 begin match state.throttle with
2293 | None ->
2294 let preloadedpages =
2295 if conf.preload
2296 then preloadlayout state.layout
2297 else state.layout
2299 let evict () =
2300 let module IntSet =
2301 Set.Make (struct type t = int let compare = (-) end) in
2302 let set =
2303 List.fold_left (fun s l -> IntSet.add l.pageno s)
2304 IntSet.empty preloadedpages
2306 let evictedpages =
2307 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2308 if not (IntSet.mem pageno set)
2309 then (
2310 wcmd "freepage %s" opaque;
2311 key :: accu
2313 else accu
2314 ) state.pagemap []
2316 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2318 evict ();
2319 state.currently <- Idle;
2320 if gen = state.gen
2321 then (
2322 tilepage l.pageno pageopaque state.layout;
2323 load state.layout;
2324 load preloadedpages;
2325 if pagevisible state.layout l.pageno
2326 && layoutready state.layout
2327 then G.postRedisplay "page";
2330 | Some (layout, _, _) ->
2331 state.currently <- Idle;
2332 tilepage l.pageno pageopaque layout;
2333 load state.layout
2334 end;
2336 | _ ->
2337 dolog "Inconsistent loading state";
2338 logcurrently state.currently;
2339 exit 1
2342 | "tile" ->
2343 let (x, y, opaque, size, t) =
2345 Scanf.sscanf args "%u %u %s %u %f"
2346 (fun x y p size t -> (x, y, p, size, t))
2347 with exn ->
2348 dolog "error processing 'tile' %S: %s"
2349 cmds (Printexc.to_string exn);
2350 exit 1;
2352 begin match state.currently with
2353 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2354 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2356 if tilew != conf.tilew || tileh != conf.tileh
2357 then (
2358 wcmd "freetile %s" opaque;
2359 state.currently <- Idle;
2360 load state.layout;
2362 else (
2363 puttileopaque l col row gen cs angle opaque size t;
2364 state.memused <- state.memused + size;
2365 state.uioh#infochanged Memused;
2366 gctiles ();
2367 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2368 opaque, size) state.tilelru;
2370 let layout =
2371 match state.throttle with
2372 | None -> state.layout
2373 | Some (layout, _, _) -> layout
2376 state.currently <- Idle;
2377 if gen = state.gen
2378 && conf.colorspace = cs
2379 && conf.angle = angle
2380 && tilevisible layout l.pageno x y
2381 then conttiling l.pageno pageopaque;
2383 begin match state.throttle with
2384 | None ->
2385 preload state.layout;
2386 if gen = state.gen
2387 && conf.colorspace = cs
2388 && conf.angle = angle
2389 && tilevisible state.layout l.pageno x y
2390 then G.postRedisplay "tile nothrottle";
2392 | Some (layout, y, _) ->
2393 let ready = layoutready layout in
2394 if ready
2395 then (
2396 state.y <- y;
2397 state.layout <- layout;
2398 state.throttle <- None;
2399 G.postRedisplay "throttle";
2401 else load layout;
2402 end;
2405 | _ ->
2406 dolog "Inconsistent tiling state";
2407 logcurrently state.currently;
2408 exit 1
2411 | "pdim" ->
2412 let pdim =
2414 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2415 with exn ->
2416 dolog "error processing 'pdim' %S: %s"
2417 cmds (Printexc.to_string exn);
2418 exit 1;
2420 state.uioh#infochanged Pdim;
2421 state.pdims <- pdim :: state.pdims
2423 | "o" ->
2424 let (l, n, t, h, pos) =
2426 Scanf.sscanf args "%u %u %d %u %n"
2427 (fun l n t h pos -> l, n, t, h, pos)
2428 with exn ->
2429 dolog "error processing 'o' %S: %s"
2430 cmds (Printexc.to_string exn);
2431 exit 1;
2433 let s = String.sub args pos (String.length args - pos) in
2434 let outline = (s, l, (n, float t /. float h)) in
2435 begin match state.currently with
2436 | Outlining outlines ->
2437 state.currently <- Outlining (outline :: outlines)
2438 | Idle ->
2439 state.currently <- Outlining [outline]
2440 | currently ->
2441 dolog "invalid outlining state";
2442 logcurrently currently
2445 | "info" ->
2446 state.docinfo <- (1, args) :: state.docinfo
2448 | "infoend" ->
2449 state.uioh#infochanged Docinfo;
2450 state.docinfo <- List.rev state.docinfo
2452 | _ ->
2453 dolog "unknown cmd `%S'" cmds
2456 let onhist cb =
2457 let rc = cb.rc in
2458 let action = function
2459 | HCprev -> cbget cb ~-1
2460 | HCnext -> cbget cb 1
2461 | HCfirst -> cbget cb ~-(cb.rc)
2462 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2463 and cancel () = cb.rc <- rc
2464 in (action, cancel)
2467 let search pattern forward =
2468 if String.length pattern > 0
2469 then
2470 let pn, py =
2471 match state.layout with
2472 | [] -> 0, 0
2473 | l :: _ ->
2474 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2476 wcmd "search %d %d %d %d,%s\000"
2477 (btod conf.icase) pn py (btod forward) pattern;
2480 let intentry text key =
2481 let c =
2482 if key >= 32 && key < 127
2483 then Char.chr key
2484 else '\000'
2486 match c with
2487 | '0' .. '9' ->
2488 let text = addchar text c in
2489 TEcont text
2491 | _ ->
2492 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2493 TEcont text
2496 let linknentry text key =
2497 let c =
2498 if key >= 32 && key < 127
2499 then Char.chr key
2500 else '\000'
2502 match c with
2503 | 'a' .. 'z' ->
2504 let text = addchar text c in
2505 TEcont text
2507 | _ ->
2508 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2509 TEcont text
2512 let linkndone f s =
2513 if String.length s > 0
2514 then (
2515 let n =
2516 let l = String.length s in
2517 let rec loop pos n = if pos = l then n else
2518 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
2519 loop (pos+1) (n*26 + m)
2520 in loop 0 0
2522 let rec loop n = function
2523 | [] -> ()
2524 | l :: rest ->
2525 match getopaque l.pageno with
2526 | None -> loop n rest
2527 | Some opaque ->
2528 let m = getlinkcount opaque in
2529 if n < m
2530 then (
2531 let under = getlink opaque n in
2532 f under
2534 else loop (n-m) rest
2536 loop n state.layout;
2540 let textentry text key =
2541 if key land 0xff00 = 0xff00
2542 then TEcont text
2543 else TEcont (text ^ Wsi.toutf8 key)
2546 let reqlayout angle proportional =
2547 match state.throttle with
2548 | None ->
2549 if nogeomcmds state.geomcmds
2550 then state.anchor <- getanchor ();
2551 conf.angle <- angle mod 360;
2552 if conf.angle != 0
2553 then (
2554 match state.mode with
2555 | LinkNav _ -> state.mode <- View
2556 | _ -> ()
2558 conf.proportional <- proportional;
2559 invalidate "reqlayout"
2560 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2561 | _ -> ()
2564 let settrim trimmargins trimfuzz =
2565 if nogeomcmds state.geomcmds
2566 then state.anchor <- getanchor ();
2567 conf.trimmargins <- trimmargins;
2568 conf.trimfuzz <- trimfuzz;
2569 let x0, y0, x1, y1 = trimfuzz in
2570 invalidate "settrim"
2571 (fun () ->
2572 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2573 Hashtbl.iter (fun _ opaque ->
2574 wcmd "freepage %s" opaque;
2575 ) state.pagemap;
2576 Hashtbl.clear state.pagemap;
2579 let setzoom zoom =
2580 match state.throttle with
2581 | None ->
2582 let zoom = max 0.01 zoom in
2583 if zoom <> conf.zoom
2584 then (
2585 state.prevzoom <- conf.zoom;
2586 conf.zoom <- zoom;
2587 reshape conf.winw conf.winh;
2588 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2591 | Some (layout, y, started) ->
2592 let time =
2593 match conf.maxwait with
2594 | None -> 0.0
2595 | Some t -> t
2597 let dt = now () -. started in
2598 if dt > time
2599 then (
2600 state.y <- y;
2601 load layout;
2605 let setcolumns mode columns coverA coverB =
2606 state.prevcolumns <- Some (conf.columns, conf.zoom);
2607 if columns < 0
2608 then (
2609 if isbirdseye mode
2610 then showtext '!' "split mode doesn't work in bird's eye"
2611 else (
2612 conf.columns <- Csplit (-columns, [||]);
2613 state.x <- 0;
2614 conf.zoom <- 1.0;
2617 else (
2618 if columns < 2
2619 then (
2620 conf.columns <- Csingle;
2621 state.x <- 0;
2622 setzoom 1.0;
2624 else (
2625 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2626 conf.zoom <- 1.0;
2629 reshape conf.winw conf.winh;
2632 let enterbirdseye () =
2633 let zoom = float conf.thumbw /. float conf.winw in
2634 let birdseyepageno =
2635 let cy = conf.winh / 2 in
2636 let fold = function
2637 | [] -> 0
2638 | l :: rest ->
2639 let rec fold best = function
2640 | [] -> best.pageno
2641 | l :: rest ->
2642 let d = cy - (l.pagedispy + l.pagevh/2)
2643 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2644 if abs d < abs dbest
2645 then fold l rest
2646 else best.pageno
2647 in fold l rest
2649 fold state.layout
2651 state.mode <- Birdseye (
2652 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2654 conf.zoom <- zoom;
2655 conf.presentation <- false;
2656 conf.interpagespace <- 10;
2657 conf.hlinks <- false;
2658 state.x <- 0;
2659 state.mstate <- Mnone;
2660 conf.maxwait <- None;
2661 conf.columns <- (
2662 match conf.beyecolumns with
2663 | Some c ->
2664 conf.zoom <- 1.0;
2665 Cmulti ((c, 0, 0), [||])
2666 | None -> Csingle
2668 Wsi.setcursor Wsi.CURSOR_INHERIT;
2669 if conf.verbose
2670 then
2671 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2672 (100.0*.zoom)
2673 else
2674 state.text <- ""
2676 reshape conf.winw conf.winh;
2679 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2680 state.mode <- View;
2681 conf.zoom <- c.zoom;
2682 conf.presentation <- c.presentation;
2683 conf.interpagespace <- c.interpagespace;
2684 conf.maxwait <- c.maxwait;
2685 conf.hlinks <- c.hlinks;
2686 conf.beyecolumns <- (
2687 match conf.columns with
2688 | Cmulti ((c, _, _), _) -> Some c
2689 | Csingle -> None
2690 | Csplit _ -> failwith "leaving bird's eye split mode"
2692 conf.columns <- (
2693 match c.columns with
2694 | Cmulti (c, _) -> Cmulti (c, [||])
2695 | Csingle -> Csingle
2696 | Csplit (c, _) -> Csplit (c, [||])
2698 state.x <- leftx;
2699 if conf.verbose
2700 then
2701 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2702 (100.0*.conf.zoom)
2704 reshape conf.winw conf.winh;
2705 state.anchor <- if goback then anchor else (pageno, 0.0);
2708 let togglebirdseye () =
2709 match state.mode with
2710 | Birdseye vals -> leavebirdseye vals true
2711 | View -> enterbirdseye ()
2712 | _ -> ()
2715 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2716 let pageno = max 0 (pageno - incr) in
2717 let rec loop = function
2718 | [] -> gotopage1 pageno 0
2719 | l :: _ when l.pageno = pageno ->
2720 if l.pagedispy >= 0 && l.pagey = 0
2721 then G.postRedisplay "upbirdseye"
2722 else gotopage1 pageno 0
2723 | _ :: rest -> loop rest
2725 loop state.layout;
2726 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2729 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2730 let pageno = min (state.pagecount - 1) (pageno + incr) in
2731 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2732 let rec loop = function
2733 | [] ->
2734 let y, h = getpageyh pageno in
2735 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2736 gotoy (clamp dy)
2737 | l :: _ when l.pageno = pageno ->
2738 if l.pagevh != l.pageh
2739 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2740 else G.postRedisplay "downbirdseye"
2741 | _ :: rest -> loop rest
2743 loop state.layout
2746 let optentry mode _ key =
2747 let btos b = if b then "on" else "off" in
2748 if key >= 32 && key < 127
2749 then
2750 let c = Char.chr key in
2751 match c with
2752 | 's' ->
2753 let ondone s =
2754 try conf.scrollstep <- int_of_string s with exc ->
2755 state.text <- Printf.sprintf "bad integer `%s': %s"
2756 s (Printexc.to_string exc)
2758 TEswitch ("scroll step: ", "", None, intentry, ondone, true)
2760 | 'A' ->
2761 let ondone s =
2763 conf.autoscrollstep <- int_of_string s;
2764 if state.autoscroll <> None
2765 then state.autoscroll <- Some conf.autoscrollstep
2766 with exc ->
2767 state.text <- Printf.sprintf "bad integer `%s': %s"
2768 s (Printexc.to_string exc)
2770 TEswitch ("auto scroll step: ", "", None, intentry, ondone, true)
2772 | 'C' ->
2773 let ondone s =
2775 let n, a, b = multicolumns_of_string s in
2776 setcolumns mode n a b;
2777 with exc ->
2778 state.text <- Printf.sprintf "bad columns `%s': %s"
2779 s (Printexc.to_string exc)
2781 TEswitch ("columns: ", "", None, textentry, ondone, true)
2783 | 'Z' ->
2784 let ondone s =
2786 let zoom = float (int_of_string s) /. 100.0 in
2787 setzoom zoom
2788 with exc ->
2789 state.text <- Printf.sprintf "bad integer `%s': %s"
2790 s (Printexc.to_string exc)
2792 TEswitch ("zoom: ", "", None, intentry, ondone, true)
2794 | 't' ->
2795 let ondone s =
2797 conf.thumbw <- bound (int_of_string s) 2 4096;
2798 state.text <-
2799 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2800 begin match mode with
2801 | Birdseye beye ->
2802 leavebirdseye beye false;
2803 enterbirdseye ();
2804 | _ -> ();
2806 with exc ->
2807 state.text <- Printf.sprintf "bad integer `%s': %s"
2808 s (Printexc.to_string exc)
2810 TEswitch ("thumbnail width: ", "", None, intentry, ondone, true)
2812 | 'R' ->
2813 let ondone s =
2814 match try
2815 Some (int_of_string s)
2816 with exc ->
2817 state.text <- Printf.sprintf "bad integer `%s': %s"
2818 s (Printexc.to_string exc);
2819 None
2820 with
2821 | Some angle -> reqlayout angle conf.proportional
2822 | None -> ()
2824 TEswitch ("rotation: ", "", None, intentry, ondone, true)
2826 | 'i' ->
2827 conf.icase <- not conf.icase;
2828 TEdone ("case insensitive search " ^ (btos conf.icase))
2830 | 'p' ->
2831 conf.preload <- not conf.preload;
2832 gotoy state.y;
2833 TEdone ("preload " ^ (btos conf.preload))
2835 | 'v' ->
2836 conf.verbose <- not conf.verbose;
2837 TEdone ("verbose " ^ (btos conf.verbose))
2839 | 'd' ->
2840 conf.debug <- not conf.debug;
2841 TEdone ("debug " ^ (btos conf.debug))
2843 | 'h' ->
2844 conf.maxhfit <- not conf.maxhfit;
2845 state.maxy <- calcheight ();
2846 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2848 | 'c' ->
2849 conf.crophack <- not conf.crophack;
2850 TEdone ("crophack " ^ btos conf.crophack)
2852 | 'a' ->
2853 let s =
2854 match conf.maxwait with
2855 | None ->
2856 conf.maxwait <- Some infinity;
2857 "always wait for page to complete"
2858 | Some _ ->
2859 conf.maxwait <- None;
2860 "show placeholder if page is not ready"
2862 TEdone s
2864 | 'f' ->
2865 conf.underinfo <- not conf.underinfo;
2866 TEdone ("underinfo " ^ btos conf.underinfo)
2868 | 'P' ->
2869 conf.savebmarks <- not conf.savebmarks;
2870 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2872 | 'S' ->
2873 let ondone s =
2875 let pageno, py =
2876 match state.layout with
2877 | [] -> 0, 0
2878 | l :: _ ->
2879 l.pageno, l.pagey
2881 conf.interpagespace <- int_of_string s;
2882 docolumns conf.columns;
2883 state.maxy <- calcheight ();
2884 let y = getpagey pageno in
2885 gotoy (y + py)
2886 with exc ->
2887 state.text <- Printf.sprintf "bad integer `%s': %s"
2888 s (Printexc.to_string exc)
2890 TEswitch ("vertical margin: ", "", None, intentry, ondone, true)
2892 | 'l' ->
2893 reqlayout conf.angle (not conf.proportional);
2894 TEdone ("proportional display " ^ btos conf.proportional)
2896 | 'T' ->
2897 settrim (not conf.trimmargins) conf.trimfuzz;
2898 TEdone ("trim margins " ^ btos conf.trimmargins)
2900 | 'I' ->
2901 conf.invert <- not conf.invert;
2902 TEdone ("invert colors " ^ btos conf.invert)
2904 | 'x' ->
2905 let ondone s =
2906 cbput state.hists.sel s;
2907 conf.selcmd <- s;
2909 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2910 textentry, ondone, true)
2912 | _ ->
2913 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2914 TEstop
2915 else
2916 TEcont state.text
2919 class type lvsource = object
2920 method getitemcount : int
2921 method getitem : int -> (string * int)
2922 method hasaction : int -> bool
2923 method exit :
2924 uioh:uioh ->
2925 cancel:bool ->
2926 active:int ->
2927 first:int ->
2928 pan:int ->
2929 qsearch:string ->
2930 uioh option
2931 method getactive : int
2932 method getfirst : int
2933 method getqsearch : string
2934 method setqsearch : string -> unit
2935 method getpan : int
2936 end;;
2938 class virtual lvsourcebase = object
2939 val mutable m_active = 0
2940 val mutable m_first = 0
2941 val mutable m_qsearch = ""
2942 val mutable m_pan = 0
2943 method getactive = m_active
2944 method getfirst = m_first
2945 method getqsearch = m_qsearch
2946 method getpan = m_pan
2947 method setqsearch s = m_qsearch <- s
2948 end;;
2950 let withoutlastutf8 s =
2951 let len = String.length s in
2952 if len = 0
2953 then s
2954 else
2955 let rec find pos =
2956 if pos = 0
2957 then pos
2958 else
2959 let b = Char.code s.[pos] in
2960 if b land 0b110000 = 0b11000000
2961 then find (pos-1)
2962 else pos-1
2964 let first =
2965 if Char.code s.[len-1] land 0x80 = 0
2966 then len-1
2967 else find (len-1)
2969 String.sub s 0 first;
2972 let textentrykeyboard
2973 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
2974 let enttext te =
2975 state.mode <- Textentry (te, onleave);
2976 state.text <- "";
2977 enttext ();
2978 G.postRedisplay "textentrykeyboard enttext";
2980 let histaction cmd =
2981 match opthist with
2982 | None -> ()
2983 | Some (action, _) ->
2984 state.mode <- Textentry (
2985 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
2987 G.postRedisplay "textentry histaction"
2989 match key with
2990 | 0xff08 -> (* backspace *)
2991 let s = withoutlastutf8 text in
2992 let len = String.length s in
2993 if cancelonempty && len = 0
2994 then (
2995 onleave Cancel;
2996 G.postRedisplay "textentrykeyboard after cancel";
2998 else (
2999 enttext (c, s, opthist, onkey, ondone, cancelonempty)
3002 | 0xff0d ->
3003 ondone text;
3004 onleave Confirm;
3005 G.postRedisplay "textentrykeyboard after confirm"
3007 | 0xff52 -> histaction HCprev
3008 | 0xff54 -> histaction HCnext
3009 | 0xff50 -> histaction HCfirst
3010 | 0xff57 -> histaction HClast
3012 | 0xff1b -> (* escape*)
3013 if String.length text = 0
3014 then (
3015 begin match opthist with
3016 | None -> ()
3017 | Some (_, onhistcancel) -> onhistcancel ()
3018 end;
3019 onleave Cancel;
3020 state.text <- "";
3021 G.postRedisplay "textentrykeyboard after cancel2"
3023 else (
3024 enttext (c, "", opthist, onkey, ondone, cancelonempty)
3027 | 0xff9f | 0xffff -> () (* delete *)
3029 | _ when key != 0 && key land 0xff00 != 0xff00 ->
3030 begin match onkey text key with
3031 | TEdone text ->
3032 ondone text;
3033 onleave Confirm;
3034 G.postRedisplay "textentrykeyboard after confirm2";
3036 | TEcont text ->
3037 enttext (c, text, opthist, onkey, ondone, cancelonempty);
3039 | TEstop ->
3040 onleave Cancel;
3041 G.postRedisplay "textentrykeyboard after cancel3"
3043 | TEswitch te ->
3044 state.mode <- Textentry (te, onleave);
3045 G.postRedisplay "textentrykeyboard switch";
3046 end;
3048 | _ ->
3049 vlog "unhandled key %s" (Wsi.keyname key)
3052 let firstof first active =
3053 if first > active || abs (first - active) > fstate.maxrows - 1
3054 then max 0 (active - (fstate.maxrows/2))
3055 else first
3058 let calcfirst first active =
3059 if active > first
3060 then
3061 let rows = active - first in
3062 if rows > fstate.maxrows then active - fstate.maxrows else first
3063 else active
3066 let scrollph y maxy =
3067 let sh = (float (maxy + conf.winh) /. float conf.winh) in
3068 let sh = float conf.winh /. sh in
3069 let sh = max sh (float conf.scrollh) in
3071 let percent =
3072 if y = state.maxy
3073 then 1.0
3074 else float y /. float maxy
3076 let position = (float conf.winh -. sh) *. percent in
3078 let position =
3079 if position +. sh > float conf.winh
3080 then float conf.winh -. sh
3081 else position
3083 position, sh;
3086 let coe s = (s :> uioh);;
3088 class listview ~(source:lvsource) ~trusted ~modehash =
3089 object (self)
3090 val m_pan = source#getpan
3091 val m_first = source#getfirst
3092 val m_active = source#getactive
3093 val m_qsearch = source#getqsearch
3094 val m_prev_uioh = state.uioh
3096 method private elemunder y =
3097 let n = y / (fstate.fontsize+1) in
3098 if m_first + n < source#getitemcount
3099 then (
3100 if source#hasaction (m_first + n)
3101 then Some (m_first + n)
3102 else None
3104 else None
3106 method display =
3107 Gl.enable `blend;
3108 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3109 GlDraw.color (0., 0., 0.) ~alpha:0.85;
3110 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
3111 GlDraw.color (1., 1., 1.);
3112 Gl.enable `texture_2d;
3113 let fs = fstate.fontsize in
3114 let nfs = fs + 1 in
3115 let ww = fstate.wwidth in
3116 let tabw = 30.0*.ww in
3117 let itemcount = source#getitemcount in
3118 let rec loop row =
3119 if (row - m_first) * nfs > conf.winh
3120 then ()
3121 else (
3122 if row >= 0 && row < itemcount
3123 then (
3124 let (s, level) = source#getitem row in
3125 let y = (row - m_first) * nfs in
3126 let x = 5.0 +. float (level + m_pan) *. ww in
3127 if row = m_active
3128 then (
3129 Gl.disable `texture_2d;
3130 GlDraw.polygon_mode `both `line;
3131 GlDraw.color (1., 1., 1.) ~alpha:0.9;
3132 GlDraw.rect (1., float (y + 1))
3133 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
3134 GlDraw.polygon_mode `both `fill;
3135 GlDraw.color (1., 1., 1.);
3136 Gl.enable `texture_2d;
3139 let drawtabularstring s =
3140 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
3141 if trusted
3142 then
3143 let tabpos = try String.index s '\t' with Not_found -> -1 in
3144 if tabpos > 0
3145 then
3146 let len = String.length s - tabpos - 1 in
3147 let s1 = String.sub s 0 tabpos
3148 and s2 = String.sub s (tabpos + 1) len in
3149 let nx = drawstr x s1 in
3150 let sw = nx -. x in
3151 let x = x +. (max tabw sw) in
3152 drawstr x s2
3153 else
3154 drawstr x s
3155 else
3156 drawstr x s
3158 let _ = drawtabularstring s in
3159 loop (row+1)
3163 loop m_first;
3164 Gl.disable `blend;
3165 Gl.disable `texture_2d;
3167 method updownlevel incr =
3168 let len = source#getitemcount in
3169 let curlevel =
3170 if m_active >= 0 && m_active < len
3171 then snd (source#getitem m_active)
3172 else -1
3174 let rec flow i =
3175 if i = len then i-1 else if i = -1 then 0 else
3176 let _, l = source#getitem i in
3177 if l != curlevel then i else flow (i+incr)
3179 let active = flow m_active in
3180 let first = calcfirst m_first active in
3181 G.postRedisplay "outline updownlevel";
3182 {< m_active = active; m_first = first >}
3184 method private key1 key mask =
3185 let set1 active first qsearch =
3186 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3188 let search active pattern incr =
3189 let dosearch re =
3190 let rec loop n =
3191 if n >= 0 && n < source#getitemcount
3192 then (
3193 let s, _ = source#getitem n in
3195 (try ignore (Str.search_forward re s 0); true
3196 with Not_found -> false)
3197 then Some n
3198 else loop (n + incr)
3200 else None
3202 loop active
3205 let re = Str.regexp_case_fold pattern in
3206 dosearch re
3207 with Failure s ->
3208 state.text <- s;
3209 None
3211 let itemcount = source#getitemcount in
3212 let find start incr =
3213 let rec find i =
3214 if i = -1 || i = itemcount
3215 then -1
3216 else (
3217 if source#hasaction i
3218 then i
3219 else find (i + incr)
3222 find start
3224 let set active first =
3225 let first = bound first 0 (itemcount - fstate.maxrows) in
3226 state.text <- "";
3227 coe {< m_active = active; m_first = first >}
3229 let navigate incr =
3230 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3231 let active, first =
3232 let incr1 = if incr > 0 then 1 else -1 in
3233 if isvisible m_first m_active
3234 then
3235 let next =
3236 let next = m_active + incr in
3237 let next =
3238 if next < 0 || next >= itemcount
3239 then -1
3240 else find next incr1
3242 if next = -1 || abs (m_active - next) > fstate.maxrows
3243 then -1
3244 else next
3246 if next = -1
3247 then
3248 let first = m_first + incr in
3249 let first = bound first 0 (itemcount - 1) in
3250 let next =
3251 let next = m_active + incr in
3252 let next = bound next 0 (itemcount - 1) in
3253 find next ~-incr1
3255 let active = if next = -1 then m_active else next in
3256 active, first
3257 else
3258 let first = min next m_first in
3259 let first =
3260 if abs (next - first) > fstate.maxrows
3261 then first + incr
3262 else first
3264 next, first
3265 else
3266 let first = m_first + incr in
3267 let first = bound first 0 (itemcount - 1) in
3268 let active =
3269 let next = m_active + incr in
3270 let next = bound next 0 (itemcount - 1) in
3271 let next = find next incr1 in
3272 let active =
3273 if next = -1 || abs (m_active - first) > fstate.maxrows
3274 then (
3275 let active = if m_active = -1 then next else m_active in
3276 active
3278 else next
3280 if isvisible first active
3281 then active
3282 else -1
3284 active, first
3286 G.postRedisplay "listview navigate";
3287 set active first;
3289 match key with
3290 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3291 let incr = if key = 0x72 then -1 else 1 in
3292 let active, first =
3293 match search (m_active + incr) m_qsearch incr with
3294 | None ->
3295 state.text <- m_qsearch ^ " [not found]";
3296 m_active, m_first
3297 | Some active ->
3298 state.text <- m_qsearch;
3299 active, firstof m_first active
3301 G.postRedisplay "listview ctrl-r/s";
3302 set1 active first m_qsearch;
3304 | 0xff08 -> (* backspace *)
3305 if String.length m_qsearch = 0
3306 then coe self
3307 else (
3308 let qsearch = withoutlastutf8 m_qsearch in
3309 let len = String.length qsearch in
3310 if len = 0
3311 then (
3312 state.text <- "";
3313 G.postRedisplay "listview empty qsearch";
3314 set1 m_active m_first "";
3316 else
3317 let active, first =
3318 match search m_active qsearch ~-1 with
3319 | None ->
3320 state.text <- qsearch ^ " [not found]";
3321 m_active, m_first
3322 | Some active ->
3323 state.text <- qsearch;
3324 active, firstof m_first active
3326 G.postRedisplay "listview backspace qsearch";
3327 set1 active first qsearch
3330 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3331 let pattern = m_qsearch ^ Wsi.toutf8 key in
3332 let active, first =
3333 match search m_active pattern 1 with
3334 | None ->
3335 state.text <- pattern ^ " [not found]";
3336 m_active, m_first
3337 | Some active ->
3338 state.text <- pattern;
3339 active, firstof m_first active
3341 G.postRedisplay "listview qsearch add";
3342 set1 active first pattern;
3344 | 0xff1b -> (* escape *)
3345 state.text <- "";
3346 if String.length m_qsearch = 0
3347 then (
3348 G.postRedisplay "list view escape";
3349 begin
3350 match
3351 source#exit (coe self) true m_active m_first m_pan m_qsearch
3352 with
3353 | None -> m_prev_uioh
3354 | Some uioh -> uioh
3357 else (
3358 G.postRedisplay "list view kill qsearch";
3359 source#setqsearch "";
3360 coe {< m_qsearch = "" >}
3363 | 0xff0d -> (* return *)
3364 state.text <- "";
3365 let self = {< m_qsearch = "" >} in
3366 source#setqsearch "";
3367 let opt =
3368 G.postRedisplay "listview enter";
3369 if m_active >= 0 && m_active < source#getitemcount
3370 then (
3371 source#exit (coe self) false m_active m_first m_pan "";
3373 else (
3374 source#exit (coe self) true m_active m_first m_pan "";
3377 begin match opt with
3378 | None -> m_prev_uioh
3379 | Some uioh -> uioh
3382 | 0xff9f | 0xffff -> (* delete *)
3383 coe self
3385 | 0xff52 -> navigate ~-1 (* up *)
3386 | 0xff54 -> navigate 1 (* down *)
3387 | 0xff55 -> navigate ~-(fstate.maxrows) (* prior *)
3388 | 0xff56 -> navigate fstate.maxrows (* next *)
3390 | 0xff53 -> (* right *)
3391 state.text <- "";
3392 G.postRedisplay "listview right";
3393 coe {< m_pan = m_pan - 1 >}
3395 | 0xff51 -> (* left *)
3396 state.text <- "";
3397 G.postRedisplay "listview left";
3398 coe {< m_pan = m_pan + 1 >}
3400 | 0xff50 -> (* home *)
3401 let active = find 0 1 in
3402 G.postRedisplay "listview home";
3403 set active 0;
3405 | 0xff57 -> (* end *)
3406 let first = max 0 (itemcount - fstate.maxrows) in
3407 let active = find (itemcount - 1) ~-1 in
3408 G.postRedisplay "listview end";
3409 set active first;
3411 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3412 coe self
3414 | _ ->
3415 dolog "listview unknown key %#x" key; coe self
3417 method key key mask =
3418 match state.mode with
3419 | Textentry te -> textentrykeyboard key mask te; coe self
3420 | _ -> self#key1 key mask
3422 method button button down x y _ =
3423 let opt =
3424 match button with
3425 | 1 when x > conf.winw - conf.scrollbw ->
3426 G.postRedisplay "listview scroll";
3427 if down
3428 then
3429 let _, position, sh = self#scrollph in
3430 if y > truncate position && y < truncate (position +. sh)
3431 then (
3432 state.mstate <- Mscrolly;
3433 Some (coe self)
3435 else
3436 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3437 let first = truncate (s *. float source#getitemcount) in
3438 let first = min source#getitemcount first in
3439 Some (coe {< m_first = first; m_active = first >})
3440 else (
3441 state.mstate <- Mnone;
3442 Some (coe self);
3444 | 1 when not down ->
3445 begin match self#elemunder y with
3446 | Some n ->
3447 G.postRedisplay "listview click";
3448 source#exit
3449 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3450 | _ ->
3451 Some (coe self)
3453 | n when (n == 4 || n == 5) && not down ->
3454 let len = source#getitemcount in
3455 let first =
3456 if n = 5 && m_first + fstate.maxrows >= len
3457 then
3458 m_first
3459 else
3460 let first = m_first + (if n == 4 then -1 else 1) in
3461 bound first 0 (len - 1)
3463 G.postRedisplay "listview wheel";
3464 Some (coe {< m_first = first >})
3465 | n when (n = 6 || n = 7) && not down ->
3466 let inc = m_first + (if n = 7 then -1 else 1) in
3467 G.postRedisplay "listview hwheel";
3468 Some (coe {< m_pan = m_pan + inc >})
3469 | _ ->
3470 Some (coe self)
3472 match opt with
3473 | None -> m_prev_uioh
3474 | Some uioh -> uioh
3476 method motion _ y =
3477 match state.mstate with
3478 | Mscrolly ->
3479 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3480 let first = truncate (s *. float source#getitemcount) in
3481 let first = min source#getitemcount first in
3482 G.postRedisplay "listview motion";
3483 coe {< m_first = first; m_active = first >}
3484 | _ -> coe self
3486 method pmotion x y =
3487 if x < conf.winw - conf.scrollbw
3488 then
3489 let n =
3490 match self#elemunder y with
3491 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3492 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3494 let o =
3495 if n != m_active
3496 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3497 else self
3499 coe o
3500 else (
3501 Wsi.setcursor Wsi.CURSOR_INHERIT;
3502 coe self
3505 method infochanged _ = ()
3507 method scrollpw = (0, 0.0, 0.0)
3508 method scrollph =
3509 let nfs = fstate.fontsize + 1 in
3510 let y = m_first * nfs in
3511 let itemcount = source#getitemcount in
3512 let maxi = max 0 (itemcount - fstate.maxrows) in
3513 let maxy = maxi * nfs in
3514 let p, h = scrollph y maxy in
3515 conf.scrollbw, p, h
3517 method modehash = modehash
3518 end;;
3520 class outlinelistview ~source =
3521 object (self)
3522 inherit listview
3523 ~source:(source :> lvsource)
3524 ~trusted:false
3525 ~modehash:(findkeyhash conf "outline")
3526 as super
3528 method key key mask =
3529 let calcfirst first active =
3530 if active > first
3531 then
3532 let rows = active - first in
3533 let maxrows =
3534 if String.length state.text = 0
3535 then fstate.maxrows
3536 else fstate.maxrows - 2
3538 if rows > maxrows then active - maxrows else first
3539 else active
3541 let navigate incr =
3542 let active = m_active + incr in
3543 let active = bound active 0 (source#getitemcount - 1) in
3544 let first = calcfirst m_first active in
3545 G.postRedisplay "outline navigate";
3546 coe {< m_active = active; m_first = first >}
3548 let ctrl = Wsi.withctrl mask in
3549 match key with
3550 | 110 when ctrl -> (* ctrl-n *)
3551 source#narrow m_qsearch;
3552 G.postRedisplay "outline ctrl-n";
3553 coe {< m_first = 0; m_active = 0 >}
3555 | 117 when ctrl -> (* ctrl-u *)
3556 source#denarrow;
3557 G.postRedisplay "outline ctrl-u";
3558 state.text <- "";
3559 coe {< m_first = 0; m_active = 0 >}
3561 | 108 when ctrl -> (* ctrl-l *)
3562 let first = m_active - (fstate.maxrows / 2) in
3563 G.postRedisplay "outline ctrl-l";
3564 coe {< m_first = first >}
3566 | 0xff9f | 0xffff -> (* delete *)
3567 source#remove m_active;
3568 G.postRedisplay "outline delete";
3569 let active = max 0 (m_active-1) in
3570 coe {< m_first = firstof m_first active;
3571 m_active = active >}
3573 | 0xff52 -> navigate ~-1 (* up *)
3574 | 0xff54 -> navigate 1 (* down *)
3575 | 0xff55 -> (* prior *)
3576 navigate ~-(fstate.maxrows)
3577 | 0xff56 -> (* next *)
3578 navigate fstate.maxrows
3580 | 0xff53 -> (* [ctrl-]right *)
3581 let o =
3582 if ctrl
3583 then (
3584 G.postRedisplay "outline ctrl right";
3585 {< m_pan = m_pan + 1 >}
3587 else self#updownlevel 1
3589 coe o
3591 | 0xff51 -> (* [ctrl-]left *)
3592 let o =
3593 if ctrl
3594 then (
3595 G.postRedisplay "outline ctrl left";
3596 {< m_pan = m_pan - 1 >}
3598 else self#updownlevel ~-1
3600 coe o
3602 | 0xff50 -> (* home *)
3603 G.postRedisplay "outline home";
3604 coe {< m_first = 0; m_active = 0 >}
3606 | 0xff57 -> (* end *)
3607 let active = source#getitemcount - 1 in
3608 let first = max 0 (active - fstate.maxrows) in
3609 G.postRedisplay "outline end";
3610 coe {< m_active = active; m_first = first >}
3612 | _ -> super#key key mask
3615 let outlinesource usebookmarks =
3616 let empty = [||] in
3617 (object
3618 inherit lvsourcebase
3619 val mutable m_items = empty
3620 val mutable m_orig_items = empty
3621 val mutable m_prev_items = empty
3622 val mutable m_narrow_pattern = ""
3623 val mutable m_hadremovals = false
3625 method getitemcount =
3626 Array.length m_items + (if m_hadremovals then 1 else 0)
3628 method getitem n =
3629 if n == Array.length m_items && m_hadremovals
3630 then
3631 ("[Confirm removal]", 0)
3632 else
3633 let s, n, _ = m_items.(n) in
3634 (s, n)
3636 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3637 ignore (uioh, first, qsearch);
3638 let confrimremoval = m_hadremovals && active = Array.length m_items in
3639 let items =
3640 if String.length m_narrow_pattern = 0
3641 then m_orig_items
3642 else m_items
3644 if not cancel
3645 then (
3646 if not confrimremoval
3647 then(
3648 let _, _, anchor = m_items.(active) in
3649 gotoanchor anchor;
3650 m_items <- items;
3652 else (
3653 state.bookmarks <- Array.to_list m_items;
3654 m_orig_items <- m_items;
3657 else m_items <- items;
3658 m_pan <- pan;
3659 None
3661 method hasaction _ = true
3663 method greetmsg =
3664 if Array.length m_items != Array.length m_orig_items
3665 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3666 else ""
3668 method narrow pattern =
3669 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3670 match reopt with
3671 | None -> ()
3672 | Some re ->
3673 let rec loop accu n =
3674 if n = -1
3675 then (
3676 m_narrow_pattern <- pattern;
3677 m_items <- Array.of_list accu
3679 else
3680 let (s, _, _) as o = m_items.(n) in
3681 let accu =
3682 if (try ignore (Str.search_forward re s 0); true
3683 with Not_found -> false)
3684 then o :: accu
3685 else accu
3687 loop accu (n-1)
3689 loop [] (Array.length m_items - 1)
3691 method denarrow =
3692 m_orig_items <- (
3693 if usebookmarks
3694 then Array.of_list state.bookmarks
3695 else state.outlines
3697 m_items <- m_orig_items
3699 method remove m =
3700 if usebookmarks
3701 then
3702 if m >= 0 && m < Array.length m_items
3703 then (
3704 m_hadremovals <- true;
3705 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3706 let n = if n >= m then n+1 else n in
3707 m_items.(n)
3711 method reset anchor items =
3712 m_hadremovals <- false;
3713 if m_orig_items == empty || m_prev_items != items
3714 then (
3715 m_orig_items <- items;
3716 if String.length m_narrow_pattern = 0
3717 then m_items <- items;
3719 m_prev_items <- items;
3720 let rely = getanchory anchor in
3721 let active =
3722 let rec loop n best bestd =
3723 if n = Array.length m_items
3724 then best
3725 else
3726 let (_, _, anchor) = m_items.(n) in
3727 let orely = getanchory anchor in
3728 let d = abs (orely - rely) in
3729 if d < bestd
3730 then loop (n+1) n d
3731 else loop (n+1) best bestd
3733 loop 0 ~-1 max_int
3735 m_active <- active;
3736 m_first <- firstof m_first active
3737 end)
3740 let enterselector usebookmarks =
3741 let source = outlinesource usebookmarks in
3742 fun errmsg ->
3743 let outlines =
3744 if usebookmarks
3745 then Array.of_list state.bookmarks
3746 else state.outlines
3748 if Array.length outlines = 0
3749 then (
3750 showtext ' ' errmsg;
3752 else (
3753 state.text <- source#greetmsg;
3754 Wsi.setcursor Wsi.CURSOR_INHERIT;
3755 let anchor = getanchor () in
3756 source#reset anchor outlines;
3757 state.uioh <- coe (new outlinelistview ~source);
3758 G.postRedisplay "enter selector";
3762 let enteroutlinemode =
3763 let f = enterselector false in
3764 fun ()-> f "Document has no outline";
3767 let enterbookmarkmode =
3768 let f = enterselector true in
3769 fun () -> f "Document has no bookmarks (yet)";
3772 let color_of_string s =
3773 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3774 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3778 let color_to_string (r, g, b) =
3779 let r = truncate (r *. 256.0)
3780 and g = truncate (g *. 256.0)
3781 and b = truncate (b *. 256.0) in
3782 Printf.sprintf "%d/%d/%d" r g b
3785 let irect_of_string s =
3786 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3789 let irect_to_string (x0,y0,x1,y1) =
3790 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3793 let makecheckers () =
3794 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3795 following to say:
3796 converted by Issac Trotts. July 25, 2002 *)
3797 let image_height = 64
3798 and image_width = 64 in
3800 let make_image () =
3801 let image =
3802 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3804 for i = 0 to image_width - 1 do
3805 for j = 0 to image_height - 1 do
3806 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3807 (if (i land 8 ) lxor (j land 8) = 0
3808 then [|255;255;255|] else [|200;200;200|])
3809 done
3810 done;
3811 image
3813 let image = make_image () in
3814 let id = GlTex.gen_texture () in
3815 GlTex.bind_texture `texture_2d id;
3816 GlPix.store (`unpack_alignment 1);
3817 GlTex.image2d image;
3818 List.iter (GlTex.parameter ~target:`texture_2d)
3819 [ `wrap_s `repeat;
3820 `wrap_t `repeat;
3821 `mag_filter `nearest;
3822 `min_filter `nearest ];
3826 let setcheckers enabled =
3827 match state.texid with
3828 | None ->
3829 if enabled then state.texid <- Some (makecheckers ())
3831 | Some texid ->
3832 if not enabled
3833 then (
3834 GlTex.delete_texture texid;
3835 state.texid <- None;
3839 let int_of_string_with_suffix s =
3840 let l = String.length s in
3841 let s1, shift =
3842 if l > 1
3843 then
3844 let suffix = Char.lowercase s.[l-1] in
3845 match suffix with
3846 | 'k' -> String.sub s 0 (l-1), 10
3847 | 'm' -> String.sub s 0 (l-1), 20
3848 | 'g' -> String.sub s 0 (l-1), 30
3849 | _ -> s, 0
3850 else s, 0
3852 let n = int_of_string s1 in
3853 let m = n lsl shift in
3854 if m < 0 || m < n
3855 then raise (Failure "value too large")
3856 else m
3859 let string_with_suffix_of_int n =
3860 if n = 0
3861 then "0"
3862 else
3863 let n, s =
3864 if n land ((1 lsl 20) - 1) = 0
3865 then n lsr 20, "M"
3866 else (
3867 if n land ((1 lsl 10) - 1) = 0
3868 then n lsr 10, "K"
3869 else n, ""
3872 let rec loop s n =
3873 let h = n mod 1000 in
3874 let n = n / 1000 in
3875 if n = 0
3876 then string_of_int h ^ s
3877 else (
3878 let s = Printf.sprintf "_%03d%s" h s in
3879 loop s n
3882 loop "" n ^ s;
3885 let defghyllscroll = (40, 8, 32);;
3886 let ghyllscroll_of_string s =
3887 let (n, a, b) as nab =
3888 if s = "default"
3889 then defghyllscroll
3890 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3892 if n <= a || n <= b || a >= b
3893 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3894 nab;
3897 let ghyllscroll_to_string ((n, a, b) as nab) =
3898 if nab = defghyllscroll
3899 then "default"
3900 else Printf.sprintf "%d,%d,%d" n a b;
3903 let describe_location () =
3904 let f (fn, _) l =
3905 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3907 let fn, ln = List.fold_left f (-1, -1) state.layout in
3908 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3909 let percent =
3910 if maxy <= 0
3911 then 100.
3912 else (100. *. (float state.y /. float maxy))
3914 if fn = ln
3915 then
3916 Printf.sprintf "page %d of %d [%.2f%%]"
3917 (fn+1) state.pagecount percent
3918 else
3919 Printf.sprintf
3920 "pages %d-%d of %d [%.2f%%]"
3921 (fn+1) (ln+1) state.pagecount percent
3924 let enterinfomode =
3925 let btos b = if b then "\xe2\x88\x9a" else "" in
3926 let showextended = ref false in
3927 let leave mode = function
3928 | Confirm -> state.mode <- mode
3929 | Cancel -> state.mode <- mode in
3930 let src =
3931 (object
3932 val mutable m_first_time = true
3933 val mutable m_l = []
3934 val mutable m_a = [||]
3935 val mutable m_prev_uioh = nouioh
3936 val mutable m_prev_mode = View
3938 inherit lvsourcebase
3940 method reset prev_mode prev_uioh =
3941 m_a <- Array.of_list (List.rev m_l);
3942 m_l <- [];
3943 m_prev_mode <- prev_mode;
3944 m_prev_uioh <- prev_uioh;
3945 if m_first_time
3946 then (
3947 let rec loop n =
3948 if n >= Array.length m_a
3949 then ()
3950 else
3951 match m_a.(n) with
3952 | _, _, _, Action _ -> m_active <- n
3953 | _ -> loop (n+1)
3955 loop 0;
3956 m_first_time <- false;
3959 method int name get set =
3960 m_l <-
3961 (name, `int get, 1, Action (
3962 fun u ->
3963 let ondone s =
3964 try set (int_of_string s)
3965 with exn ->
3966 state.text <- Printf.sprintf "bad integer `%s': %s"
3967 s (Printexc.to_string exn)
3969 state.text <- "";
3970 let te = name ^ ": ", "", None, intentry, ondone, true in
3971 state.mode <- Textentry (te, leave m_prev_mode);
3973 )) :: m_l
3975 method int_with_suffix name get set =
3976 m_l <-
3977 (name, `intws get, 1, Action (
3978 fun u ->
3979 let ondone s =
3980 try set (int_of_string_with_suffix s)
3981 with exn ->
3982 state.text <- Printf.sprintf "bad integer `%s': %s"
3983 s (Printexc.to_string exn)
3985 state.text <- "";
3986 let te =
3987 name ^ ": ", "", None, intentry_with_suffix, ondone, true
3989 state.mode <- Textentry (te, leave m_prev_mode);
3991 )) :: m_l
3993 method bool ?(offset=1) ?(btos=btos) name get set =
3994 m_l <-
3995 (name, `bool (btos, get), offset, Action (
3996 fun u ->
3997 let v = get () in
3998 set (not v);
4000 )) :: m_l
4002 method color name get set =
4003 m_l <-
4004 (name, `color get, 1, Action (
4005 fun u ->
4006 let invalid = (nan, nan, nan) in
4007 let ondone s =
4008 let c =
4009 try color_of_string s
4010 with exn ->
4011 state.text <- Printf.sprintf "bad color `%s': %s"
4012 s (Printexc.to_string exn);
4013 invalid
4015 if c <> invalid
4016 then set c;
4018 let te = name ^ ": ", "", None, textentry, ondone, true in
4019 state.text <- color_to_string (get ());
4020 state.mode <- Textentry (te, leave m_prev_mode);
4022 )) :: m_l
4024 method string name get set =
4025 m_l <-
4026 (name, `string get, 1, Action (
4027 fun u ->
4028 let ondone s = set s in
4029 let te = name ^ ": ", "", None, textentry, ondone, true in
4030 state.mode <- Textentry (te, leave m_prev_mode);
4032 )) :: m_l
4034 method colorspace name get set =
4035 m_l <-
4036 (name, `string get, 1, Action (
4037 fun _ ->
4038 let source =
4039 let vals = [| "rgb"; "bgr"; "gray" |] in
4040 (object
4041 inherit lvsourcebase
4043 initializer
4044 m_active <- int_of_colorspace conf.colorspace;
4045 m_first <- 0;
4047 method getitemcount = Array.length vals
4048 method getitem n = (vals.(n), 0)
4049 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4050 ignore (uioh, first, pan, qsearch);
4051 if not cancel then set active;
4052 None
4053 method hasaction _ = true
4054 end)
4056 state.text <- "";
4057 let modehash = findkeyhash conf "info" in
4058 coe (new listview ~source ~trusted:true ~modehash)
4059 )) :: m_l
4061 method caption s offset =
4062 m_l <- (s, `empty, offset, Noaction) :: m_l
4064 method caption2 s f offset =
4065 m_l <- (s, `string f, offset, Noaction) :: m_l
4067 method getitemcount = Array.length m_a
4069 method getitem n =
4070 let tostr = function
4071 | `int f -> string_of_int (f ())
4072 | `intws f -> string_with_suffix_of_int (f ())
4073 | `string f -> f ()
4074 | `color f -> color_to_string (f ())
4075 | `bool (btos, f) -> btos (f ())
4076 | `empty -> ""
4078 let name, t, offset, _ = m_a.(n) in
4079 ((let s = tostr t in
4080 if String.length s > 0
4081 then Printf.sprintf "%s\t%s" name s
4082 else name),
4083 offset)
4085 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4086 let uiohopt =
4087 if not cancel
4088 then (
4089 m_qsearch <- qsearch;
4090 let uioh =
4091 match m_a.(active) with
4092 | _, _, _, Action f -> f uioh
4093 | _ -> uioh
4095 Some uioh
4097 else None
4099 m_active <- active;
4100 m_first <- first;
4101 m_pan <- pan;
4102 uiohopt
4104 method hasaction n =
4105 match m_a.(n) with
4106 | _, _, _, Action _ -> true
4107 | _ -> false
4108 end)
4110 let rec fillsrc prevmode prevuioh =
4111 let sep () = src#caption "" 0 in
4112 let colorp name get set =
4113 src#string name
4114 (fun () -> color_to_string (get ()))
4115 (fun v ->
4117 let c = color_of_string v in
4118 set c
4119 with exn ->
4120 state.text <- Printf.sprintf "bad color `%s': %s"
4121 v (Printexc.to_string exn);
4124 let oldmode = state.mode in
4125 let birdseye = isbirdseye state.mode in
4127 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
4129 src#bool "presentation mode"
4130 (fun () -> conf.presentation)
4131 (fun v ->
4132 conf.presentation <- v;
4133 state.anchor <- getanchor ();
4134 represent ());
4136 src#bool "ignore case in searches"
4137 (fun () -> conf.icase)
4138 (fun v -> conf.icase <- v);
4140 src#bool "preload"
4141 (fun () -> conf.preload)
4142 (fun v -> conf.preload <- v);
4144 src#bool "highlight links"
4145 (fun () -> conf.hlinks)
4146 (fun v -> conf.hlinks <- v);
4148 src#bool "under info"
4149 (fun () -> conf.underinfo)
4150 (fun v -> conf.underinfo <- v);
4152 src#bool "persistent bookmarks"
4153 (fun () -> conf.savebmarks)
4154 (fun v -> conf.savebmarks <- v);
4156 src#bool "proportional display"
4157 (fun () -> conf.proportional)
4158 (fun v -> reqlayout conf.angle v);
4160 src#bool "trim margins"
4161 (fun () -> conf.trimmargins)
4162 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
4164 src#bool "persistent location"
4165 (fun () -> conf.jumpback)
4166 (fun v -> conf.jumpback <- v);
4168 sep ();
4169 src#int "inter-page space"
4170 (fun () -> conf.interpagespace)
4171 (fun n ->
4172 conf.interpagespace <- n;
4173 docolumns conf.columns;
4174 let pageno, py =
4175 match state.layout with
4176 | [] -> 0, 0
4177 | l :: _ ->
4178 l.pageno, l.pagey
4180 state.maxy <- calcheight ();
4181 let y = getpagey pageno in
4182 gotoy (y + py)
4185 src#int "page bias"
4186 (fun () -> conf.pagebias)
4187 (fun v -> conf.pagebias <- v);
4189 src#int "scroll step"
4190 (fun () -> conf.scrollstep)
4191 (fun n -> conf.scrollstep <- n);
4193 src#int "horizontal scroll step"
4194 (fun () -> conf.hscrollstep)
4195 (fun v -> conf.hscrollstep <- v);
4197 src#int "auto scroll step"
4198 (fun () ->
4199 match state.autoscroll with
4200 | Some step -> step
4201 | _ -> conf.autoscrollstep)
4202 (fun n ->
4203 if state.autoscroll <> None
4204 then state.autoscroll <- Some n;
4205 conf.autoscrollstep <- n);
4207 src#int "zoom"
4208 (fun () -> truncate (conf.zoom *. 100.))
4209 (fun v -> setzoom ((float v) /. 100.));
4211 src#int "rotation"
4212 (fun () -> conf.angle)
4213 (fun v -> reqlayout v conf.proportional);
4215 src#int "scroll bar width"
4216 (fun () -> state.scrollw)
4217 (fun v ->
4218 state.scrollw <- v;
4219 conf.scrollbw <- v;
4220 reshape conf.winw conf.winh;
4223 src#int "scroll handle height"
4224 (fun () -> conf.scrollh)
4225 (fun v -> conf.scrollh <- v;);
4227 src#int "thumbnail width"
4228 (fun () -> conf.thumbw)
4229 (fun v ->
4230 conf.thumbw <- min 4096 v;
4231 match oldmode with
4232 | Birdseye beye ->
4233 leavebirdseye beye false;
4234 enterbirdseye ()
4235 | _ -> ()
4238 let mode = state.mode in
4239 src#string "columns"
4240 (fun () ->
4241 match conf.columns with
4242 | Csingle -> "1"
4243 | Cmulti (multi, _) -> multicolumns_to_string multi
4244 | Csplit (count, _) -> "-" ^ string_of_int count
4246 (fun v ->
4247 let n, a, b = multicolumns_of_string v in
4248 setcolumns mode n a b);
4250 sep ();
4251 src#caption "Presentation mode" 0;
4252 src#bool "scrollbar visible"
4253 (fun () -> conf.scrollbarinpm)
4254 (fun v ->
4255 if v != conf.scrollbarinpm
4256 then (
4257 conf.scrollbarinpm <- v;
4258 if conf.presentation
4259 then (
4260 state.scrollw <- if v then conf.scrollbw else 0;
4261 reshape conf.winw conf.winh;
4266 sep ();
4267 src#caption "Pixmap cache" 0;
4268 src#int_with_suffix "size (advisory)"
4269 (fun () -> conf.memlimit)
4270 (fun v -> conf.memlimit <- v);
4272 src#caption2 "used"
4273 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4274 (string_with_suffix_of_int state.memused)
4275 (Hashtbl.length state.tilemap)) 1;
4277 sep ();
4278 src#caption "Layout" 0;
4279 src#caption2 "Dimension"
4280 (fun () ->
4281 Printf.sprintf "%dx%d (virtual %dx%d)"
4282 conf.winw conf.winh
4283 state.w state.maxy)
4285 if conf.debug
4286 then
4287 src#caption2 "Position" (fun () ->
4288 Printf.sprintf "%dx%d" state.x state.y
4290 else
4291 src#caption2 "Visible" (fun () -> describe_location ()) 1
4294 sep ();
4295 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4296 "Save these parameters as global defaults at exit"
4297 (fun () -> conf.bedefault)
4298 (fun v -> conf.bedefault <- v)
4301 sep ();
4302 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4303 src#bool ~offset:0 ~btos "Extended parameters"
4304 (fun () -> !showextended)
4305 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4306 if !showextended
4307 then (
4308 src#bool "checkers"
4309 (fun () -> conf.checkers)
4310 (fun v -> conf.checkers <- v; setcheckers v);
4311 src#bool "update cursor"
4312 (fun () -> conf.updatecurs)
4313 (fun v -> conf.updatecurs <- v);
4314 src#bool "verbose"
4315 (fun () -> conf.verbose)
4316 (fun v -> conf.verbose <- v);
4317 src#bool "invert colors"
4318 (fun () -> conf.invert)
4319 (fun v -> conf.invert <- v);
4320 src#bool "max fit"
4321 (fun () -> conf.maxhfit)
4322 (fun v -> conf.maxhfit <- v);
4323 src#bool "redirect stderr"
4324 (fun () -> conf.redirectstderr)
4325 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4326 src#string "uri launcher"
4327 (fun () -> conf.urilauncher)
4328 (fun v -> conf.urilauncher <- v);
4329 src#string "path launcher"
4330 (fun () -> conf.pathlauncher)
4331 (fun v -> conf.pathlauncher <- v);
4332 src#string "tile size"
4333 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4334 (fun v ->
4336 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4337 conf.tilew <- max 64 w;
4338 conf.tileh <- max 64 h;
4339 flushtiles ();
4340 with exn ->
4341 state.text <- Printf.sprintf "bad tile size `%s': %s"
4342 v (Printexc.to_string exn));
4343 src#int "texture count"
4344 (fun () -> conf.texcount)
4345 (fun v ->
4346 if realloctexts v
4347 then conf.texcount <- v
4348 else showtext '!' " Failed to set texture count please retry later"
4350 src#int "slice height"
4351 (fun () -> conf.sliceheight)
4352 (fun v ->
4353 conf.sliceheight <- v;
4354 wcmd "sliceh %d" conf.sliceheight;
4356 src#int "anti-aliasing level"
4357 (fun () -> conf.aalevel)
4358 (fun v ->
4359 conf.aalevel <- bound v 0 8;
4360 state.anchor <- getanchor ();
4361 opendoc state.path state.password;
4363 src#string "page scroll scaling factor"
4364 (fun () -> string_of_float conf.pgscale)
4365 (fun v ->
4367 let s = float_of_string v in
4368 conf.pgscale <- s
4369 with exn ->
4370 state.text <- Printf.sprintf
4371 "bad page scroll scaling factor `%s': %s"
4372 v (Printexc.to_string exn)
4375 src#int "ui font size"
4376 (fun () -> fstate.fontsize)
4377 (fun v -> setfontsize (bound v 5 100));
4378 src#int "hint font size"
4379 (fun () -> conf.hfsize)
4380 (fun v -> conf.hfsize <- bound v 5 100);
4381 colorp "background color"
4382 (fun () -> conf.bgcolor)
4383 (fun v -> conf.bgcolor <- v);
4384 src#bool "crop hack"
4385 (fun () -> conf.crophack)
4386 (fun v -> conf.crophack <- v);
4387 src#string "trim fuzz"
4388 (fun () -> irect_to_string conf.trimfuzz)
4389 (fun v ->
4391 conf.trimfuzz <- irect_of_string v;
4392 if conf.trimmargins
4393 then settrim true conf.trimfuzz;
4394 with exn ->
4395 state.text <- Printf.sprintf "bad irect `%s': %s"
4396 v (Printexc.to_string exn)
4398 src#string "throttle"
4399 (fun () ->
4400 match conf.maxwait with
4401 | None -> "show place holder if page is not ready"
4402 | Some time ->
4403 if time = infinity
4404 then "wait for page to fully render"
4405 else
4406 "wait " ^ string_of_float time
4407 ^ " seconds before showing placeholder"
4409 (fun v ->
4411 let f = float_of_string v in
4412 if f <= 0.0
4413 then conf.maxwait <- None
4414 else conf.maxwait <- Some f
4415 with exn ->
4416 state.text <- Printf.sprintf "bad time `%s': %s"
4417 v (Printexc.to_string exn)
4419 src#string "ghyll scroll"
4420 (fun () ->
4421 match conf.ghyllscroll with
4422 | None -> ""
4423 | Some nab -> ghyllscroll_to_string nab
4425 (fun v ->
4427 let gs =
4428 if String.length v = 0
4429 then None
4430 else Some (ghyllscroll_of_string v)
4432 conf.ghyllscroll <- gs
4433 with exn ->
4434 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4435 v (Printexc.to_string exn)
4437 src#string "selection command"
4438 (fun () -> conf.selcmd)
4439 (fun v -> conf.selcmd <- v);
4440 src#colorspace "color space"
4441 (fun () -> colorspace_to_string conf.colorspace)
4442 (fun v ->
4443 conf.colorspace <- colorspace_of_int v;
4444 wcmd "cs %d" v;
4445 load state.layout;
4449 sep ();
4450 src#caption "Document" 0;
4451 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4452 src#caption2 "Pages"
4453 (fun () -> string_of_int state.pagecount) 1;
4454 src#caption2 "Dimensions"
4455 (fun () -> string_of_int (List.length state.pdims)) 1;
4456 if conf.trimmargins
4457 then (
4458 sep ();
4459 src#caption "Trimmed margins" 0;
4460 src#caption2 "Dimensions"
4461 (fun () -> string_of_int (List.length state.pdims)) 1;
4464 sep ();
4465 src#caption "OpenGL" 0;
4466 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
4467 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
4468 src#reset prevmode prevuioh;
4470 fun () ->
4471 state.text <- "";
4472 let prevmode = state.mode
4473 and prevuioh = state.uioh in
4474 fillsrc prevmode prevuioh;
4475 let source = (src :> lvsource) in
4476 let modehash = findkeyhash conf "info" in
4477 state.uioh <- coe (object (self)
4478 inherit listview ~source ~trusted:true ~modehash as super
4479 val mutable m_prevmemused = 0
4480 method infochanged = function
4481 | Memused ->
4482 if m_prevmemused != state.memused
4483 then (
4484 m_prevmemused <- state.memused;
4485 G.postRedisplay "memusedchanged";
4487 | Pdim -> G.postRedisplay "pdimchanged"
4488 | Docinfo -> fillsrc prevmode prevuioh
4490 method key key mask =
4491 if not (Wsi.withctrl mask)
4492 then
4493 match key with
4494 | 0xff51 -> coe (self#updownlevel ~-1)
4495 | 0xff53 -> coe (self#updownlevel 1)
4496 | _ -> super#key key mask
4497 else super#key key mask
4498 end);
4499 G.postRedisplay "info";
4502 let enterhelpmode =
4503 let source =
4504 (object
4505 inherit lvsourcebase
4506 method getitemcount = Array.length state.help
4507 method getitem n =
4508 let s, n, _ = state.help.(n) in
4509 (s, n)
4511 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4512 let optuioh =
4513 if not cancel
4514 then (
4515 m_qsearch <- qsearch;
4516 match state.help.(active) with
4517 | _, _, Action f -> Some (f uioh)
4518 | _ -> Some (uioh)
4520 else None
4522 m_active <- active;
4523 m_first <- first;
4524 m_pan <- pan;
4525 optuioh
4527 method hasaction n =
4528 match state.help.(n) with
4529 | _, _, Action _ -> true
4530 | _ -> false
4532 initializer
4533 m_active <- -1
4534 end)
4535 in fun () ->
4536 let modehash = findkeyhash conf "help" in
4537 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4538 G.postRedisplay "help";
4541 let entermsgsmode =
4542 let msgsource =
4543 let re = Str.regexp "[\r\n]" in
4544 (object
4545 inherit lvsourcebase
4546 val mutable m_items = [||]
4548 method getitemcount = 1 + Array.length m_items
4550 method getitem n =
4551 if n = 0
4552 then "[Clear]", 0
4553 else m_items.(n-1), 0
4555 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4556 ignore uioh;
4557 if not cancel
4558 then (
4559 if active = 0
4560 then Buffer.clear state.errmsgs;
4561 m_qsearch <- qsearch;
4563 m_active <- active;
4564 m_first <- first;
4565 m_pan <- pan;
4566 None
4568 method hasaction n =
4569 n = 0
4571 method reset =
4572 state.newerrmsgs <- false;
4573 let l = Str.split re (Buffer.contents state.errmsgs) in
4574 m_items <- Array.of_list l
4576 initializer
4577 m_active <- 0
4578 end)
4579 in fun () ->
4580 state.text <- "";
4581 msgsource#reset;
4582 let source = (msgsource :> lvsource) in
4583 let modehash = findkeyhash conf "listview" in
4584 state.uioh <- coe (object
4585 inherit listview ~source ~trusted:false ~modehash as super
4586 method display =
4587 if state.newerrmsgs
4588 then msgsource#reset;
4589 super#display
4590 end);
4591 G.postRedisplay "msgs";
4594 let quickbookmark ?title () =
4595 match state.layout with
4596 | [] -> ()
4597 | l :: _ ->
4598 let title =
4599 match title with
4600 | None ->
4601 let sec = Unix.gettimeofday () in
4602 let tm = Unix.localtime sec in
4603 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4604 (l.pageno+1)
4605 tm.Unix.tm_mday
4606 tm.Unix.tm_mon
4607 (tm.Unix.tm_year + 1900)
4608 tm.Unix.tm_hour
4609 tm.Unix.tm_min
4610 | Some title -> title
4612 state.bookmarks <-
4613 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4614 :: state.bookmarks
4617 let doreshape w h =
4618 state.fullscreen <- None;
4619 Wsi.reshape w h;
4622 let setautoscrollspeed step goingdown =
4623 let incr = max 1 ((abs step) / 2) in
4624 let incr = if goingdown then incr else -incr in
4625 let astep = step + incr in
4626 state.autoscroll <- Some astep;
4629 let gotounder = function
4630 | Ulinkgoto (pageno, top) ->
4631 if pageno >= 0
4632 then (
4633 addnav ();
4634 gotopage1 pageno top;
4637 | Ulinkuri s ->
4638 gotouri s
4640 | Uremote (filename, pageno) ->
4641 let path =
4642 if Sys.file_exists filename
4643 then filename
4644 else
4645 let dir = Filename.dirname state.path in
4646 let path = Filename.concat dir filename in
4647 if Sys.file_exists path
4648 then path
4649 else ""
4651 if String.length path > 0
4652 then (
4653 let anchor = getanchor () in
4654 let ranchor = state.path, state.password, anchor in
4655 state.anchor <- (pageno, 0.0);
4656 state.ranchors <- ranchor :: state.ranchors;
4657 opendoc path "";
4659 else showtext '!' ("Could not find " ^ filename)
4661 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4664 let canpan () =
4665 match conf.columns with
4666 | Csplit _ -> true
4667 | _ -> conf.zoom > 1.0
4670 let viewkeyboard key mask =
4671 let enttext te =
4672 let mode = state.mode in
4673 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4674 state.text <- "";
4675 enttext ();
4676 G.postRedisplay "view:enttext"
4678 let ctrl = Wsi.withctrl mask in
4679 match key with
4680 | 81 -> (* Q *)
4681 exit 0
4683 | 0xff63 -> (* insert *)
4684 if conf.angle mod 360 = 0
4685 then (
4686 state.mode <- LinkNav (Ltgendir 0);
4687 gotoy state.y;
4689 else showtext '!' "Keyboard link naviagtion does not work under rotation"
4691 | 0xff1b | 113 -> (* escape / q *)
4692 begin match state.mstate with
4693 | Mzoomrect _ ->
4694 state.mstate <- Mnone;
4695 Wsi.setcursor Wsi.CURSOR_INHERIT;
4696 G.postRedisplay "kill zoom rect";
4697 | _ ->
4698 match state.ranchors with
4699 | [] -> raise Quit
4700 | (path, password, anchor) :: rest ->
4701 state.ranchors <- rest;
4702 state.anchor <- anchor;
4703 opendoc path password
4704 end;
4706 | 0xff08 -> (* backspace *)
4707 let y = getnav ~-1 in
4708 gotoy_and_clear_text y
4710 | 111 -> (* o *)
4711 enteroutlinemode ()
4713 | 117 -> (* u *)
4714 state.rects <- [];
4715 state.text <- "";
4716 G.postRedisplay "dehighlight";
4718 | 47 | 63 -> (* / ? *)
4719 let ondone isforw s =
4720 cbput state.hists.pat s;
4721 state.searchpattern <- s;
4722 search s isforw
4724 let s = String.create 1 in
4725 s.[0] <- Char.chr key;
4726 enttext (s, "", Some (onhist state.hists.pat),
4727 textentry, ondone (key = 47), true)
4729 | 43 | 0xffab | 61 when ctrl -> (* ctrl-+ or ctrl-=*)
4730 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4731 setzoom (conf.zoom +. incr)
4733 | 43 | 0xffab -> (* + *)
4734 let ondone s =
4735 let n =
4736 try int_of_string s with exc ->
4737 state.text <- Printf.sprintf "bad integer `%s': %s"
4738 s (Printexc.to_string exc);
4739 max_int
4741 if n != max_int
4742 then (
4743 conf.pagebias <- n;
4744 state.text <- "page bias is now " ^ string_of_int n;
4747 enttext ("page bias: ", "", None, intentry, ondone, true)
4749 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4750 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4751 setzoom (max 0.01 (conf.zoom -. decr))
4753 | 45 | 0xffad -> (* - *)
4754 let ondone msg = state.text <- msg in
4755 enttext (
4756 "option [acfhilpstvxACFPRSZTIS]: ", "", None,
4757 optentry state.mode, ondone, true
4760 | 48 when ctrl -> (* ctrl-0 *)
4761 setzoom 1.0
4763 | 49 when ctrl -> (* ctrl-1 *)
4764 let cols =
4765 match conf.columns with
4766 | Csingle | Cmulti _ -> 1
4767 | Csplit (n, _) -> n
4769 let zoom = zoomforh conf.winw conf.winh state.scrollw cols in
4770 if zoom < 1.0
4771 then setzoom zoom
4773 | 0xffc6 -> (* f9 *)
4774 togglebirdseye ()
4776 | 57 when ctrl -> (* ctrl-9 *)
4777 togglebirdseye ()
4779 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4780 when not ctrl -> (* 0..9 *)
4781 let ondone s =
4782 let n =
4783 try int_of_string s with exc ->
4784 state.text <- Printf.sprintf "bad integer `%s': %s"
4785 s (Printexc.to_string exc);
4788 if n >= 0
4789 then (
4790 addnav ();
4791 cbput state.hists.pag (string_of_int n);
4792 gotopage1 (n + conf.pagebias - 1) 0;
4795 let pageentry text key =
4796 match Char.unsafe_chr key with
4797 | 'g' -> TEdone text
4798 | _ -> intentry text key
4800 let text = "x" in text.[0] <- Char.chr key;
4801 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone, true)
4803 | 98 -> (* b *)
4804 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4805 reshape conf.winw conf.winh;
4807 | 108 -> (* l *)
4808 conf.hlinks <- not conf.hlinks;
4809 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4810 G.postRedisplay "toggle highlightlinks";
4812 | 70 -> (* F *)
4813 state.glinks <- true;
4814 let mode = state.mode in
4815 state.mode <- Textentry (
4816 (":", "", None, linknentry, linkndone (fun under ->
4817 addnav ();
4818 gotounder under
4819 ), false
4820 ), fun _ ->
4821 state.glinks <- false;
4822 state.mode <- mode
4824 state.text <- "";
4825 G.postRedisplay "view:linkent(F)"
4827 | 121 -> (* y *)
4828 state.glinks <- true;
4829 let mode = state.mode in
4830 state.mode <- Textentry (
4831 (":", "", None, linknentry, linkndone (fun under ->
4832 match Ne.pipe () with
4833 | Ne.Exn exn ->
4834 showtext '!' (Printf.sprintf "pipe failed: %s"
4835 (Printexc.to_string exn));
4836 | Ne.Res (r, w) ->
4837 let popened =
4838 try popen conf.selcmd [r, 0; w, -1]; true
4839 with exn ->
4840 showtext '!'
4841 (Printf.sprintf "failed to execute %s: %s"
4842 conf.selcmd (Printexc.to_string exn));
4843 false
4845 let clo cap fd =
4846 Ne.clo fd (fun msg ->
4847 showtext '!' (Printf.sprintf "failed to close %s: %s" cap msg)
4850 let s = undertext under in
4851 if popened
4852 then
4853 (try
4854 let l = String.length s in
4855 let n = Unix.write w s 0 l in
4856 if n != l
4857 then
4858 showtext '!'
4859 (Printf.sprintf
4860 "failed to write %d characters to sel pipe, wrote %d"
4863 with exn ->
4864 showtext '!'
4865 (Printf.sprintf "failed to write to sel pipe: %s"
4866 (Printexc.to_string exn)
4869 else dolog "%s" s;
4870 clo "pipe/r" r;
4871 clo "pipe/w" w;
4872 ), false
4874 fun _ ->
4875 state.glinks <- false;
4876 state.mode <- mode
4878 state.text <- "";
4879 G.postRedisplay "view:linkent"
4881 | 97 -> (* a *)
4882 begin match state.autoscroll with
4883 | Some step ->
4884 conf.autoscrollstep <- step;
4885 state.autoscroll <- None
4886 | None ->
4887 if conf.autoscrollstep = 0
4888 then state.autoscroll <- Some 1
4889 else state.autoscroll <- Some conf.autoscrollstep
4892 | 112 when ctrl -> (* ctrl-p *)
4893 launchpath ()
4895 | 80 -> (* P *)
4896 conf.presentation <- not conf.presentation;
4897 if conf.presentation
4898 then (
4899 if not conf.scrollbarinpm
4900 then state.scrollw <- 0;
4902 else
4903 state.scrollw <- conf.scrollbw;
4905 showtext ' ' ("presentation mode " ^
4906 if conf.presentation then "on" else "off");
4907 state.anchor <- getanchor ();
4908 represent ()
4910 | 102 -> (* f *)
4911 begin match state.fullscreen with
4912 | None ->
4913 state.fullscreen <- Some (conf.winw, conf.winh);
4914 Wsi.fullscreen ()
4915 | Some (w, h) ->
4916 state.fullscreen <- None;
4917 doreshape w h
4920 | 103 -> (* g *)
4921 gotoy_and_clear_text 0
4923 | 71 -> (* G *)
4924 gotopage1 (state.pagecount - 1) 0
4926 | 112 | 78 -> (* p|N *)
4927 search state.searchpattern false
4929 | 110 | 0xffc0 -> (* n|F3 *)
4930 search state.searchpattern true
4932 | 116 -> (* t *)
4933 begin match state.layout with
4934 | [] -> ()
4935 | l :: _ ->
4936 gotoy_and_clear_text (getpagey l.pageno)
4939 | 32 -> (* ' ' *)
4940 begin match state.layout with
4941 | [] -> ()
4942 | l :: rest ->
4943 match conf.columns with
4944 | Csingle | Cmulti _ ->
4945 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4946 then
4947 let y = clamp (pgscale conf.winh) in
4948 gotoy_and_clear_text y
4949 else
4950 let pageno = min (l.pageno+1) (state.pagecount-1) in
4951 gotoy_and_clear_text (getpagey pageno)
4952 | Csplit (n, _) ->
4953 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
4954 then
4955 let pagey, pageh = getpageyh l.pageno in
4956 let pagey = pagey + pageh * l.pagecol in
4957 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
4958 gotoy_and_clear_text (pagey + pageh + ips)
4961 | 0xff9f | 0xffff -> (* delete *)
4962 begin match state.layout with
4963 | [] -> ()
4964 | l :: _ ->
4965 match conf.columns with
4966 | Csingle | Cmulti _ ->
4967 if conf.presentation && l.pagey != 0
4968 then
4969 gotoy_and_clear_text (clamp (pgscale ~-(conf.winh)))
4970 else
4971 let pageno = max 0 (l.pageno-1) in
4972 gotoy_and_clear_text (getpagey pageno)
4973 | Csplit (n, _) ->
4974 let y =
4975 if l.pagecol = 0
4976 then
4977 if l.pageno = 0
4978 then l.pagey
4979 else
4980 let pageno = max 0 (l.pageno-1) in
4981 let pagey, pageh = getpageyh pageno in
4982 pagey + (n-1)*pageh
4983 else
4984 let pagey, pageh = getpageyh l.pageno in
4985 pagey + pageh * (l.pagecol-1) - conf.interpagespace
4987 gotoy_and_clear_text y
4990 | 61 -> (* = *)
4991 showtext ' ' (describe_location ());
4993 | 119 -> (* w *)
4994 begin match state.layout with
4995 | [] -> ()
4996 | l :: _ ->
4997 doreshape (l.pagew + state.scrollw) l.pageh;
4998 G.postRedisplay "w"
5001 | 39 -> (* ' *)
5002 enterbookmarkmode ()
5004 | 104 | 0xffbe -> (* h|F1 *)
5005 enterhelpmode ()
5007 | 105 -> (* i *)
5008 enterinfomode ()
5010 | 101 when conf.redirectstderr -> (* e *)
5011 entermsgsmode ()
5013 | 109 -> (* m *)
5014 let ondone s =
5015 match state.layout with
5016 | l :: _ ->
5017 state.bookmarks <-
5018 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
5019 :: state.bookmarks
5020 | _ -> ()
5022 enttext ("bookmark: ", "", None, textentry, ondone, true)
5024 | 126 -> (* ~ *)
5025 quickbookmark ();
5026 showtext ' ' "Quick bookmark added";
5028 | 122 -> (* z *)
5029 begin match state.layout with
5030 | l :: _ ->
5031 let rect = getpdimrect l.pagedimno in
5032 let w, h =
5033 if conf.crophack
5034 then
5035 (truncate (1.8 *. (rect.(1) -. rect.(0))),
5036 truncate (1.2 *. (rect.(3) -. rect.(0))))
5037 else
5038 (truncate (rect.(1) -. rect.(0)),
5039 truncate (rect.(3) -. rect.(0)))
5041 let w = truncate ((float w)*.conf.zoom)
5042 and h = truncate ((float h)*.conf.zoom) in
5043 if w != 0 && h != 0
5044 then (
5045 state.anchor <- getanchor ();
5046 doreshape (w + state.scrollw) (h + conf.interpagespace)
5048 G.postRedisplay "z";
5050 | [] -> ()
5053 | 50 when ctrl -> (* ctrl-2 *)
5054 let maxw = getmaxw () in
5055 if maxw > 0.0
5056 then setzoom (maxw /. float conf.winw)
5058 | 60 | 62 -> (* < > *)
5059 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
5061 | 91 | 93 -> (* [ ] *)
5062 conf.colorscale <-
5063 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
5065 G.postRedisplay "brightness";
5067 | 99 when state.mode = View -> (* c *)
5068 let (c, a, b), z =
5069 match state.prevcolumns with
5070 | None -> (1, 0, 0), 1.0
5071 | Some (columns, z) ->
5072 let cab =
5073 match columns with
5074 | Csplit (c, _) -> -c, 0, 0
5075 | Cmulti ((c, a, b), _) -> c, a, b
5076 | Csingle -> 1, 0, 0
5078 cab, z
5080 setcolumns View c a b;
5081 setzoom z;
5083 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
5084 setzoom state.prevzoom
5086 | 107 | 0xff52 -> (* k up *)
5087 begin match state.autoscroll with
5088 | None ->
5089 begin match state.mode with
5090 | Birdseye beye -> upbirdseye 1 beye
5091 | _ ->
5092 if ctrl
5093 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
5094 else gotoy_and_clear_text (clamp (-conf.scrollstep))
5096 | Some n ->
5097 setautoscrollspeed n false
5100 | 106 | 0xff54 -> (* j down *)
5101 begin match state.autoscroll with
5102 | None ->
5103 begin match state.mode with
5104 | Birdseye beye -> downbirdseye 1 beye
5105 | _ ->
5106 if ctrl
5107 then gotoy_and_clear_text (clamp (conf.winh/2))
5108 else gotoy_and_clear_text (clamp conf.scrollstep)
5110 | Some n ->
5111 setautoscrollspeed n true
5114 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
5115 if canpan ()
5116 then
5117 let dx =
5118 if ctrl
5119 then conf.winw / 2
5120 else 10
5122 let dx = if key = 0xff51 then dx else -dx in
5123 state.x <- state.x + dx;
5124 gotoy_and_clear_text state.y
5125 else (
5126 state.text <- "";
5127 G.postRedisplay "lef/right"
5130 | 0xff55 -> (* prior *)
5131 let y =
5132 if ctrl
5133 then
5134 match state.layout with
5135 | [] -> state.y
5136 | l :: _ -> state.y - l.pagey
5137 else
5138 clamp (pgscale (-conf.winh))
5140 gotoghyll y
5142 | 0xff56 -> (* next *)
5143 let y =
5144 if ctrl
5145 then
5146 match List.rev state.layout with
5147 | [] -> state.y
5148 | l :: _ -> getpagey l.pageno
5149 else
5150 clamp (pgscale conf.winh)
5152 gotoghyll y
5154 | 0xff50 -> gotoghyll 0
5155 | 0xff57 -> gotoghyll (clamp state.maxy)
5156 | 0xff53 when Wsi.withalt mask ->
5157 gotoghyll (getnav ~-1)
5158 | 0xff51 when Wsi.withalt mask ->
5159 gotoghyll (getnav 1)
5161 | 114 -> (* r *)
5162 state.anchor <- getanchor ();
5163 opendoc state.path state.password
5165 | 118 when conf.debug -> (* v *)
5166 state.rects <- [];
5167 List.iter (fun l ->
5168 match getopaque l.pageno with
5169 | None -> ()
5170 | Some opaque ->
5171 let x0, y0, x1, y1 = pagebbox opaque in
5172 let a,b = float x0, float y0 in
5173 let c,d = float x1, float y0 in
5174 let e,f = float x1, float y1 in
5175 let h,j = float x0, float y1 in
5176 let rect = (a,b,c,d,e,f,h,j) in
5177 debugrect rect;
5178 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
5179 ) state.layout;
5180 G.postRedisplay "v";
5182 | _ ->
5183 vlog "huh? %s" (Wsi.keyname key)
5186 let linknavkeyboard key mask linknav =
5187 let getpage pageno =
5188 let rec loop = function
5189 | [] -> None
5190 | l :: _ when l.pageno = pageno -> Some l
5191 | _ :: rest -> loop rest
5192 in loop state.layout
5194 let doexact (pageno, n) =
5195 match getopaque pageno, getpage pageno with
5196 | Some opaque, Some l ->
5197 if key = 0xff0d
5198 then
5199 let under = getlink opaque n in
5200 G.postRedisplay "link gotounder";
5201 gotounder under;
5202 state.mode <- View;
5203 else
5204 let opt, dir =
5205 match key with
5206 | 0xff50 -> (* home *)
5207 Some (findlink opaque LDfirst), -1
5209 | 0xff57 -> (* end *)
5210 Some (findlink opaque LDlast), 1
5212 | 0xff51 -> (* left *)
5213 Some (findlink opaque (LDleft n)), -1
5215 | 0xff53 -> (* right *)
5216 Some (findlink opaque (LDright n)), 1
5218 | 0xff52 -> (* up *)
5219 Some (findlink opaque (LDup n)), -1
5221 | 0xff54 -> (* down *)
5222 Some (findlink opaque (LDdown n)), 1
5224 | _ -> None, 0
5226 let pwl l dir =
5227 begin match findpwl l.pageno dir with
5228 | Pwlnotfound -> ()
5229 | Pwl pageno ->
5230 let notfound dir =
5231 state.mode <- LinkNav (Ltgendir dir);
5232 let y, h = getpageyh pageno in
5233 let y =
5234 if dir < 0
5235 then y + h - conf.winh
5236 else y
5238 gotoy y
5240 begin match getopaque pageno, getpage pageno with
5241 | Some opaque, Some _ ->
5242 let link =
5243 let ld = if dir > 0 then LDfirst else LDlast in
5244 findlink opaque ld
5246 begin match link with
5247 | Lfound m ->
5248 showlinktype (getlink opaque m);
5249 state.mode <- LinkNav (Ltexact (pageno, m));
5250 G.postRedisplay "linknav jpage";
5251 | _ -> notfound dir
5252 end;
5253 | _ -> notfound dir
5254 end;
5255 end;
5257 begin match opt with
5258 | Some Lnotfound -> pwl l dir;
5259 | Some (Lfound m) ->
5260 if m = n
5261 then pwl l dir
5262 else (
5263 let _, y0, _, y1 = getlinkrect opaque m in
5264 if y0 < l.pagey
5265 then gotopage1 l.pageno y0
5266 else (
5267 let d = fstate.fontsize + 1 in
5268 if y1 - l.pagey > l.pagevh - d
5269 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh + d)
5270 else G.postRedisplay "linknav";
5272 showlinktype (getlink opaque m);
5273 state.mode <- LinkNav (Ltexact (l.pageno, m));
5276 | None -> viewkeyboard key mask
5277 end;
5278 | _ -> viewkeyboard key mask
5280 if key = 0xff63
5281 then (
5282 state.mode <- View;
5283 G.postRedisplay "leave linknav"
5285 else
5286 match linknav with
5287 | Ltgendir _ -> viewkeyboard key mask
5288 | Ltexact exact -> doexact exact
5291 let keyboard key mask =
5292 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5293 then wcmd "interrupt"
5294 else state.uioh <- state.uioh#key key mask
5297 let birdseyekeyboard key mask
5298 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5299 let incr =
5300 match conf.columns with
5301 | Csingle -> 1
5302 | Cmulti ((c, _, _), _) -> c
5303 | Csplit _ -> failwith "bird's eye split mode"
5305 match key with
5306 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5307 let y, h = getpageyh pageno in
5308 let top = (conf.winh - h) / 2 in
5309 gotoy (max 0 (y - top))
5310 | 0xff0d -> leavebirdseye beye false
5311 | 0xff1b -> leavebirdseye beye true (* escape *)
5312 | 0xff52 -> upbirdseye incr beye (* prior *)
5313 | 0xff54 -> downbirdseye incr beye (* next *)
5314 | 0xff51 -> upbirdseye 1 beye (* up *)
5315 | 0xff53 -> downbirdseye 1 beye (* down *)
5317 | 0xff55 ->
5318 begin match state.layout with
5319 | l :: _ ->
5320 if l.pagey != 0
5321 then (
5322 state.mode <- Birdseye (
5323 oconf, leftx, l.pageno, hooverpageno, anchor
5325 gotopage1 l.pageno 0;
5327 else (
5328 let layout = layout (state.y-conf.winh) conf.winh in
5329 match layout with
5330 | [] -> gotoy (clamp (-conf.winh))
5331 | l :: _ ->
5332 state.mode <- Birdseye (
5333 oconf, leftx, l.pageno, hooverpageno, anchor
5335 gotopage1 l.pageno 0
5338 | [] -> gotoy (clamp (-conf.winh))
5339 end;
5341 | 0xff56 ->
5342 begin match List.rev state.layout with
5343 | l :: _ ->
5344 let layout = layout (state.y + conf.winh) conf.winh in
5345 begin match layout with
5346 | [] ->
5347 let incr = l.pageh - l.pagevh in
5348 if incr = 0
5349 then (
5350 state.mode <-
5351 Birdseye (
5352 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5354 G.postRedisplay "birdseye pagedown";
5356 else gotoy (clamp (incr + conf.interpagespace*2));
5358 | l :: _ ->
5359 state.mode <-
5360 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5361 gotopage1 l.pageno 0;
5364 | [] -> gotoy (clamp conf.winh)
5365 end;
5367 | 0xff50 ->
5368 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5369 gotopage1 0 0
5371 | 0xff57 ->
5372 let pageno = state.pagecount - 1 in
5373 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5374 if not (pagevisible state.layout pageno)
5375 then
5376 let h =
5377 match List.rev state.pdims with
5378 | [] -> conf.winh
5379 | (_, _, h, _) :: _ -> h
5381 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
5382 else G.postRedisplay "birdseye end";
5383 | _ -> viewkeyboard key mask
5386 let drawpage l linkindexbase =
5387 let color =
5388 match state.mode with
5389 | Textentry _ -> scalecolor 0.4
5390 | LinkNav _
5391 | View -> scalecolor 1.0
5392 | Birdseye (_, _, pageno, hooverpageno, _) ->
5393 if l.pageno = hooverpageno
5394 then scalecolor 0.9
5395 else (
5396 if l.pageno = pageno
5397 then scalecolor 1.0
5398 else scalecolor 0.8
5401 drawtiles l color;
5402 begin match getopaque l.pageno with
5403 | Some opaque ->
5404 if tileready l l.pagex l.pagey
5405 then
5406 let x = l.pagedispx - l.pagex
5407 and y = l.pagedispy - l.pagey in
5408 let hlmask =
5409 match conf.columns with
5410 | Csingle | Cmulti _ ->
5411 (if conf.hlinks then 1 else 0)
5412 + (if state.glinks
5413 && not (isbirdseye state.mode) then 2 else 0)
5414 | _ -> 0
5416 let s =
5417 match state.mode with
5418 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5419 | _ -> ""
5421 postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize);
5422 else 0
5424 | _ -> 0
5425 end;
5428 let scrollindicator () =
5429 let sbw, ph, sh = state.uioh#scrollph in
5430 let sbh, pw, sw = state.uioh#scrollpw in
5432 GlDraw.color (0.64, 0.64, 0.64);
5433 GlDraw.rect
5434 (float (conf.winw - sbw), 0.)
5435 (float conf.winw, float conf.winh)
5437 GlDraw.rect
5438 (0., float (conf.winh - sbh))
5439 (float (conf.winw - state.scrollw - 1), float conf.winh)
5441 GlDraw.color (0.0, 0.0, 0.0);
5443 GlDraw.rect
5444 (float (conf.winw - sbw), ph)
5445 (float conf.winw, ph +. sh)
5447 GlDraw.rect
5448 (pw, float (conf.winh - sbh))
5449 (pw +. sw, float conf.winh)
5453 let showsel () =
5454 match state.mstate with
5455 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5458 | Msel ((x0, y0), (x1, y1)) ->
5459 let rec loop = function
5460 | l :: ls ->
5461 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5462 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5463 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5464 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5465 then
5466 match getopaque l.pageno with
5467 | Some opaque ->
5468 let x0, y0 = pagetranslatepoint l x0 y0 in
5469 let x1, y1 = pagetranslatepoint l x1 y1 in
5470 seltext opaque (x0, y0, x1, y1);
5471 | _ -> ()
5472 else loop ls
5473 | [] -> ()
5475 loop state.layout
5478 let showrects rects =
5479 Gl.enable `blend;
5480 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5481 GlDraw.polygon_mode `both `fill;
5482 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5483 List.iter
5484 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5485 List.iter (fun l ->
5486 if l.pageno = pageno
5487 then (
5488 let dx = float (l.pagedispx - l.pagex) in
5489 let dy = float (l.pagedispy - l.pagey) in
5490 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5491 GlDraw.begins `quads;
5493 GlDraw.vertex2 (x0+.dx, y0+.dy);
5494 GlDraw.vertex2 (x1+.dx, y1+.dy);
5495 GlDraw.vertex2 (x2+.dx, y2+.dy);
5496 GlDraw.vertex2 (x3+.dx, y3+.dy);
5498 GlDraw.ends ();
5500 ) state.layout
5501 ) rects
5503 Gl.disable `blend;
5506 let display () =
5507 GlClear.color (scalecolor2 conf.bgcolor);
5508 GlClear.clear [`color];
5509 let rec loop linkindexbase = function
5510 | l :: rest ->
5511 let linkindexbase = linkindexbase + drawpage l linkindexbase in
5512 loop linkindexbase rest
5513 | [] -> ()
5515 loop 0 state.layout;
5516 let rects =
5517 match state.mode with
5518 | LinkNav (Ltexact (pageno, linkno)) ->
5519 begin match getopaque pageno with
5520 | Some opaque ->
5521 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5522 (pageno, 5, (
5523 float x0, float y0,
5524 float x1, float y0,
5525 float x1, float y1,
5526 float x0, float y1)
5527 ) :: state.rects
5528 | None -> state.rects
5530 | _ -> state.rects
5532 showrects rects;
5533 showsel ();
5534 state.uioh#display;
5535 begin match state.mstate with
5536 | Mzoomrect ((x0, y0), (x1, y1)) ->
5537 Gl.enable `blend;
5538 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5539 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5540 GlDraw.rect (float x0, float y0)
5541 (float x1, float y1);
5542 Gl.disable `blend;
5543 | _ -> ()
5544 end;
5545 enttext ();
5546 scrollindicator ();
5547 Wsi.swapb ();
5550 let zoomrect x y x1 y1 =
5551 let x0 = min x x1
5552 and x1 = max x x1
5553 and y0 = min y y1 in
5554 gotoy (state.y + y0);
5555 state.anchor <- getanchor ();
5556 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5557 let margin =
5558 if state.w < conf.winw - state.scrollw
5559 then (conf.winw - state.scrollw - state.w) / 2
5560 else 0
5562 state.x <- (state.x + margin) - x0;
5563 setzoom zoom;
5564 Wsi.setcursor Wsi.CURSOR_INHERIT;
5565 state.mstate <- Mnone;
5568 let scrollx x =
5569 let winw = conf.winw - state.scrollw - 1 in
5570 let s = float x /. float winw in
5571 let destx = truncate (float (state.w + winw) *. s) in
5572 state.x <- winw - destx;
5573 gotoy_and_clear_text state.y;
5574 state.mstate <- Mscrollx;
5577 let scrolly y =
5578 let s = float y /. float conf.winh in
5579 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5580 gotoy_and_clear_text desty;
5581 state.mstate <- Mscrolly;
5584 let viewmouse button down x y mask =
5585 match button with
5586 | n when (n == 4 || n == 5) && not down ->
5587 if Wsi.withctrl mask
5588 then (
5589 match state.mstate with
5590 | Mzoom (oldn, i) ->
5591 if oldn = n
5592 then (
5593 if i = 2
5594 then
5595 let incr =
5596 match n with
5597 | 5 ->
5598 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5599 | _ ->
5600 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5602 let zoom = conf.zoom -. incr in
5603 setzoom zoom;
5604 state.mstate <- Mzoom (n, 0);
5605 else
5606 state.mstate <- Mzoom (n, i+1);
5608 else state.mstate <- Mzoom (n, 0)
5610 | _ -> state.mstate <- Mzoom (n, 0)
5612 else (
5613 match state.autoscroll with
5614 | Some step -> setautoscrollspeed step (n=4)
5615 | None ->
5616 let incr =
5617 if n = 4
5618 then -conf.scrollstep
5619 else conf.scrollstep
5621 let incr = incr * 2 in
5622 let y = clamp incr in
5623 gotoy_and_clear_text y
5626 | n when (n = 6 || n = 7) && not down && canpan () ->
5627 state.x <- state.x + (if n = 7 then -2 else 2) * conf.hscrollstep;
5628 gotoy_and_clear_text state.y
5630 | 1 when Wsi.withctrl mask ->
5631 if down
5632 then (
5633 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5634 state.mstate <- Mpan (x, y)
5636 else
5637 state.mstate <- Mnone
5639 | 3 ->
5640 if down
5641 then (
5642 Wsi.setcursor Wsi.CURSOR_CYCLE;
5643 let p = (x, y) in
5644 state.mstate <- Mzoomrect (p, p)
5646 else (
5647 match state.mstate with
5648 | Mzoomrect ((x0, y0), _) ->
5649 if abs (x-x0) > 10 && abs (y - y0) > 10
5650 then zoomrect x0 y0 x y
5651 else (
5652 state.mstate <- Mnone;
5653 Wsi.setcursor Wsi.CURSOR_INHERIT;
5654 G.postRedisplay "kill accidental zoom rect";
5656 | _ ->
5657 Wsi.setcursor Wsi.CURSOR_INHERIT;
5658 state.mstate <- Mnone
5661 | 1 when x > conf.winw - state.scrollw ->
5662 if down
5663 then
5664 let _, position, sh = state.uioh#scrollph in
5665 if y > truncate position && y < truncate (position +. sh)
5666 then state.mstate <- Mscrolly
5667 else scrolly y
5668 else
5669 state.mstate <- Mnone
5671 | 1 when y > conf.winh - state.hscrollh ->
5672 if down
5673 then
5674 let _, position, sw = state.uioh#scrollpw in
5675 if x > truncate position && x < truncate (position +. sw)
5676 then state.mstate <- Mscrollx
5677 else scrollx x
5678 else
5679 state.mstate <- Mnone
5681 | 1 ->
5682 let dest = if down then getunder x y else Unone in
5683 begin match dest with
5684 | Ulinkgoto _
5685 | Ulinkuri _
5686 | Uremote _
5687 | Uunexpected _ | Ulaunch _ | Unamed _ ->
5688 gotounder dest
5690 | Unone when down ->
5691 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5692 state.mstate <- Mpan (x, y);
5694 | Unone | Utext _ ->
5695 if down
5696 then (
5697 if conf.angle mod 360 = 0
5698 then (
5699 state.mstate <- Msel ((x, y), (x, y));
5700 G.postRedisplay "mouse select";
5703 else (
5704 match state.mstate with
5705 | Mnone -> ()
5707 | Mzoom _ | Mscrollx | Mscrolly ->
5708 state.mstate <- Mnone
5710 | Mzoomrect ((x0, y0), _) ->
5711 zoomrect x0 y0 x y
5713 | Mpan _ ->
5714 Wsi.setcursor Wsi.CURSOR_INHERIT;
5715 state.mstate <- Mnone
5717 | Msel ((_, y0), (_, y1)) ->
5718 let rec loop = function
5719 | [] -> ()
5720 | l :: rest ->
5721 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5722 || ((y1 >= l.pagedispy
5723 && y1 <= (l.pagedispy + l.pagevh)))
5724 then
5725 match getopaque l.pageno with
5726 | Some opaque ->
5727 begin
5728 match Ne.pipe () with
5729 | Ne.Exn exn ->
5730 showtext '!'
5731 (Printf.sprintf
5732 "can not create sel pipe: %s"
5733 (Printexc.to_string exn));
5734 | Ne.Res (r, w) ->
5735 let doclose what fd =
5736 Ne.clo fd (fun msg ->
5737 dolog "%s close failed: %s" what msg)
5740 popen conf.selcmd [r, 0; w, -1];
5741 copysel w opaque;
5742 doclose "pipe/r" r;
5743 G.postRedisplay "copysel";
5744 with exn ->
5745 dolog "can not execute %S: %s"
5746 conf.selcmd (Printexc.to_string exn);
5747 doclose "pipe/r" r;
5748 doclose "pipe/w" w;
5750 | None -> ()
5751 else loop rest
5753 loop state.layout;
5754 Wsi.setcursor Wsi.CURSOR_INHERIT;
5755 state.mstate <- Mnone;
5759 | _ -> ()
5762 let birdseyemouse button down x y mask
5763 (conf, leftx, _, hooverpageno, anchor) =
5764 match button with
5765 | 1 when down ->
5766 let rec loop = function
5767 | [] -> ()
5768 | l :: rest ->
5769 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5770 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5771 then (
5772 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5774 else loop rest
5776 loop state.layout
5777 | 3 -> ()
5778 | _ -> viewmouse button down x y mask
5781 let mouse button down x y mask =
5782 state.uioh <- state.uioh#button button down x y mask;
5785 let motion ~x ~y =
5786 state.uioh <- state.uioh#motion x y
5789 let pmotion ~x ~y =
5790 state.uioh <- state.uioh#pmotion x y;
5793 let uioh = object
5794 method display = ()
5796 method key key mask =
5797 begin match state.mode with
5798 | Textentry textentry -> textentrykeyboard key mask textentry
5799 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5800 | View -> viewkeyboard key mask
5801 | LinkNav linknav -> linknavkeyboard key mask linknav
5802 end;
5803 state.uioh
5805 method button button bstate x y mask =
5806 begin match state.mode with
5807 | LinkNav _
5808 | View -> viewmouse button bstate x y mask
5809 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5810 | Textentry _ -> ()
5811 end;
5812 state.uioh
5814 method motion x y =
5815 begin match state.mode with
5816 | Textentry _ -> ()
5817 | View | Birdseye _ | LinkNav _ ->
5818 match state.mstate with
5819 | Mzoom _ | Mnone -> ()
5821 | Mpan (x0, y0) ->
5822 let dx = x - x0
5823 and dy = y0 - y in
5824 state.mstate <- Mpan (x, y);
5825 if canpan ()
5826 then state.x <- state.x + dx;
5827 let y = clamp dy in
5828 gotoy_and_clear_text y
5830 | Msel (a, _) ->
5831 state.mstate <- Msel (a, (x, y));
5832 G.postRedisplay "motion select";
5834 | Mscrolly ->
5835 let y = min conf.winh (max 0 y) in
5836 scrolly y
5838 | Mscrollx ->
5839 let x = min conf.winw (max 0 x) in
5840 scrollx x
5842 | Mzoomrect (p0, _) ->
5843 state.mstate <- Mzoomrect (p0, (x, y));
5844 G.postRedisplay "motion zoomrect";
5845 end;
5846 state.uioh
5848 method pmotion x y =
5849 begin match state.mode with
5850 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5851 let rec loop = function
5852 | [] ->
5853 if hooverpageno != -1
5854 then (
5855 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5856 G.postRedisplay "pmotion birdseye no hoover";
5858 | l :: rest ->
5859 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5860 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5861 then (
5862 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5863 G.postRedisplay "pmotion birdseye hoover";
5865 else loop rest
5867 loop state.layout
5869 | Textentry _ -> ()
5871 | LinkNav _
5872 | View ->
5873 match state.mstate with
5874 | Mnone -> updateunder x y
5875 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5877 end;
5878 state.uioh
5880 method infochanged _ = ()
5882 method scrollph =
5883 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5884 let p, h = scrollph state.y maxy in
5885 state.scrollw, p, h
5887 method scrollpw =
5888 let winw = conf.winw - state.scrollw - 1 in
5889 let fwinw = float winw in
5890 let sw =
5891 let sw = fwinw /. float state.w in
5892 let sw = fwinw *. sw in
5893 max sw (float conf.scrollh)
5895 let position, sw =
5896 let f = state.w+winw in
5897 let r = float (winw-state.x) /. float f in
5898 let p = fwinw *. r in
5899 p-.sw/.2., sw
5901 let sw =
5902 if position +. sw > fwinw
5903 then fwinw -. position
5904 else sw
5906 state.hscrollh, position, sw
5908 method modehash =
5909 let modename =
5910 match state.mode with
5911 | LinkNav _ -> "links"
5912 | Textentry _ -> "textentry"
5913 | Birdseye _ -> "birdseye"
5914 | View -> "view"
5916 findkeyhash conf modename
5917 end;;
5919 module Config =
5920 struct
5921 open Parser
5923 let fontpath = ref "";;
5925 module KeyMap =
5926 Map.Make (struct type t = (int * int) let compare = compare end);;
5928 let unent s =
5929 let l = String.length s in
5930 let b = Buffer.create l in
5931 unent b s 0 l;
5932 Buffer.contents b;
5935 let home =
5936 try Sys.getenv "HOME"
5937 with exn ->
5938 prerr_endline
5939 ("Can not determine home directory location: " ^
5940 Printexc.to_string exn);
5944 let modifier_of_string = function
5945 | "alt" -> Wsi.altmask
5946 | "shift" -> Wsi.shiftmask
5947 | "ctrl" | "control" -> Wsi.ctrlmask
5948 | "meta" -> Wsi.metamask
5949 | _ -> 0
5952 let key_of_string =
5953 let r = Str.regexp "-" in
5954 fun s ->
5955 let elems = Str.full_split r s in
5956 let f n k m =
5957 let g s =
5958 let m1 = modifier_of_string s in
5959 if m1 = 0
5960 then (Wsi.namekey s, m)
5961 else (k, m lor m1)
5962 in function
5963 | Str.Delim s when n land 1 = 0 -> g s
5964 | Str.Text s -> g s
5965 | Str.Delim _ -> (k, m)
5967 let rec loop n k m = function
5968 | [] -> (k, m)
5969 | x :: xs ->
5970 let k, m = f n k m x in
5971 loop (n+1) k m xs
5973 loop 0 0 0 elems
5976 let keys_of_string =
5977 let r = Str.regexp "[ \t]" in
5978 fun s ->
5979 let elems = Str.split r s in
5980 List.map key_of_string elems
5983 let copykeyhashes c =
5984 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
5987 let config_of c attrs =
5988 let apply c k v =
5990 match k with
5991 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5992 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5993 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5994 | "preload" -> { c with preload = bool_of_string v }
5995 | "page-bias" -> { c with pagebias = int_of_string v }
5996 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5997 | "horizontal-scroll-step" ->
5998 { c with hscrollstep = max (int_of_string v) 1 }
5999 | "auto-scroll-step" ->
6000 { c with autoscrollstep = max 0 (int_of_string v) }
6001 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
6002 | "crop-hack" -> { c with crophack = bool_of_string v }
6003 | "throttle" ->
6004 let mw =
6005 match String.lowercase v with
6006 | "true" -> Some infinity
6007 | "false" -> None
6008 | f -> Some (float_of_string f)
6010 { c with maxwait = mw}
6011 | "highlight-links" -> { c with hlinks = bool_of_string v }
6012 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
6013 | "vertical-margin" ->
6014 { c with interpagespace = max 0 (int_of_string v) }
6015 | "zoom" ->
6016 let zoom = float_of_string v /. 100. in
6017 let zoom = max zoom 0.0 in
6018 { c with zoom = zoom }
6019 | "presentation" -> { c with presentation = bool_of_string v }
6020 | "rotation-angle" -> { c with angle = int_of_string v }
6021 | "width" -> { c with winw = max 20 (int_of_string v) }
6022 | "height" -> { c with winh = max 20 (int_of_string v) }
6023 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
6024 | "proportional-display" -> { c with proportional = bool_of_string v }
6025 | "pixmap-cache-size" ->
6026 { c with memlimit = max 2 (int_of_string_with_suffix v) }
6027 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
6028 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
6029 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
6030 | "persistent-location" -> { c with jumpback = bool_of_string v }
6031 | "background-color" -> { c with bgcolor = color_of_string v }
6032 | "scrollbar-in-presentation" ->
6033 { c with scrollbarinpm = bool_of_string v }
6034 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
6035 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
6036 | "mupdf-store-size" ->
6037 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
6038 | "checkers" -> { c with checkers = bool_of_string v }
6039 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
6040 | "trim-margins" -> { c with trimmargins = bool_of_string v }
6041 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
6042 | "uri-launcher" -> { c with urilauncher = unent v }
6043 | "path-launcher" -> { c with pathlauncher = unent v }
6044 | "color-space" -> { c with colorspace = colorspace_of_string v }
6045 | "invert-colors" -> { c with invert = bool_of_string v }
6046 | "brightness" -> { c with colorscale = float_of_string v }
6047 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
6048 | "ghyllscroll" ->
6049 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
6050 | "columns" ->
6051 let (n, _, _) as nab = multicolumns_of_string v in
6052 if n < 0
6053 then { c with columns = Csplit (-n, [||]) }
6054 else { c with columns = Cmulti (nab, [||]) }
6055 | "birds-eye-columns" ->
6056 { c with beyecolumns = Some (max (int_of_string v) 2) }
6057 | "selection-command" -> { c with selcmd = unent v }
6058 | "update-cursor" -> { c with updatecurs = bool_of_string v }
6059 | "hint-font-size" -> { c with hfsize = bound (int_of_string v) 5 100 }
6060 | "page-scroll-scale" -> { c with pgscale = float_of_string v }
6061 | _ -> c
6062 with exn ->
6063 prerr_endline ("Error processing attribute (`" ^
6064 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
6067 let rec fold c = function
6068 | [] -> c
6069 | (k, v) :: rest ->
6070 let c = apply c k v in
6071 fold c rest
6073 fold { c with keyhashes = copykeyhashes c } attrs;
6076 let fromstring f pos n v d =
6077 try f v
6078 with exn ->
6079 dolog "Error processing attribute (%S=%S) at %d\n%s"
6080 n v pos (Printexc.to_string exn)
6085 let bookmark_of attrs =
6086 let rec fold title page rely = function
6087 | ("title", v) :: rest -> fold v page rely rest
6088 | ("page", v) :: rest -> fold title v rely rest
6089 | ("rely", v) :: rest -> fold title page v rest
6090 | _ :: rest -> fold title page rely rest
6091 | [] -> title, page, rely
6093 fold "invalid" "0" "0" attrs
6096 let doc_of attrs =
6097 let rec fold path page rely pan = function
6098 | ("path", v) :: rest -> fold v page rely pan rest
6099 | ("page", v) :: rest -> fold path v rely pan rest
6100 | ("rely", v) :: rest -> fold path page v pan rest
6101 | ("pan", v) :: rest -> fold path page rely v rest
6102 | _ :: rest -> fold path page rely pan rest
6103 | [] -> path, page, rely, pan
6105 fold "" "0" "0" "0" attrs
6108 let map_of attrs =
6109 let rec fold rs ls = function
6110 | ("out", v) :: rest -> fold v ls rest
6111 | ("in", v) :: rest -> fold rs v rest
6112 | _ :: rest -> fold ls rs rest
6113 | [] -> ls, rs
6115 fold "" "" attrs
6118 let setconf dst src =
6119 dst.scrollbw <- src.scrollbw;
6120 dst.scrollh <- src.scrollh;
6121 dst.icase <- src.icase;
6122 dst.preload <- src.preload;
6123 dst.pagebias <- src.pagebias;
6124 dst.verbose <- src.verbose;
6125 dst.scrollstep <- src.scrollstep;
6126 dst.maxhfit <- src.maxhfit;
6127 dst.crophack <- src.crophack;
6128 dst.autoscrollstep <- src.autoscrollstep;
6129 dst.maxwait <- src.maxwait;
6130 dst.hlinks <- src.hlinks;
6131 dst.underinfo <- src.underinfo;
6132 dst.interpagespace <- src.interpagespace;
6133 dst.zoom <- src.zoom;
6134 dst.presentation <- src.presentation;
6135 dst.angle <- src.angle;
6136 dst.winw <- src.winw;
6137 dst.winh <- src.winh;
6138 dst.savebmarks <- src.savebmarks;
6139 dst.memlimit <- src.memlimit;
6140 dst.proportional <- src.proportional;
6141 dst.texcount <- src.texcount;
6142 dst.sliceheight <- src.sliceheight;
6143 dst.thumbw <- src.thumbw;
6144 dst.jumpback <- src.jumpback;
6145 dst.bgcolor <- src.bgcolor;
6146 dst.scrollbarinpm <- src.scrollbarinpm;
6147 dst.tilew <- src.tilew;
6148 dst.tileh <- src.tileh;
6149 dst.mustoresize <- src.mustoresize;
6150 dst.checkers <- src.checkers;
6151 dst.aalevel <- src.aalevel;
6152 dst.trimmargins <- src.trimmargins;
6153 dst.trimfuzz <- src.trimfuzz;
6154 dst.urilauncher <- src.urilauncher;
6155 dst.colorspace <- src.colorspace;
6156 dst.invert <- src.invert;
6157 dst.colorscale <- src.colorscale;
6158 dst.redirectstderr <- src.redirectstderr;
6159 dst.ghyllscroll <- src.ghyllscroll;
6160 dst.columns <- src.columns;
6161 dst.beyecolumns <- src.beyecolumns;
6162 dst.selcmd <- src.selcmd;
6163 dst.updatecurs <- src.updatecurs;
6164 dst.pathlauncher <- src.pathlauncher;
6165 dst.keyhashes <- copykeyhashes src;
6166 dst.hfsize <- src.hfsize;
6167 dst.hscrollstep <- src.hscrollstep;
6168 dst.pgscale <- src.pgscale;
6171 let get s =
6172 let h = Hashtbl.create 10 in
6173 let dc = { defconf with angle = defconf.angle } in
6174 let rec toplevel v t spos _ =
6175 match t with
6176 | Vdata | Vcdata | Vend -> v
6177 | Vopen ("llppconfig", _, closed) ->
6178 if closed
6179 then v
6180 else { v with f = llppconfig }
6181 | Vopen _ ->
6182 error "unexpected subelement at top level" s spos
6183 | Vclose _ -> error "unexpected close at top level" s spos
6185 and llppconfig v t spos _ =
6186 match t with
6187 | Vdata | Vcdata -> v
6188 | Vend -> error "unexpected end of input in llppconfig" s spos
6189 | Vopen ("defaults", attrs, closed) ->
6190 let c = config_of dc attrs in
6191 setconf dc c;
6192 if closed
6193 then v
6194 else { v with f = defaults }
6196 | Vopen ("ui-font", attrs, closed) ->
6197 let rec getsize size = function
6198 | [] -> size
6199 | ("size", v) :: rest ->
6200 let size =
6201 fromstring int_of_string spos "size" v fstate.fontsize in
6202 getsize size rest
6203 | l -> getsize size l
6205 fstate.fontsize <- getsize fstate.fontsize attrs;
6206 if closed
6207 then v
6208 else { v with f = uifont (Buffer.create 10) }
6210 | Vopen ("doc", attrs, closed) ->
6211 let pathent, spage, srely, span = doc_of attrs in
6212 let path = unent pathent
6213 and pageno = fromstring int_of_string spos "page" spage 0
6214 and rely = fromstring float_of_string spos "rely" srely 0.0
6215 and pan = fromstring int_of_string spos "pan" span 0 in
6216 let c = config_of dc attrs in
6217 let anchor = (pageno, rely) in
6218 if closed
6219 then (Hashtbl.add h path (c, [], pan, anchor); v)
6220 else { v with f = doc path pan anchor c [] }
6222 | Vopen _ ->
6223 error "unexpected subelement in llppconfig" s spos
6225 | Vclose "llppconfig" -> { v with f = toplevel }
6226 | Vclose _ -> error "unexpected close in llppconfig" s spos
6228 and defaults v t spos _ =
6229 match t with
6230 | Vdata | Vcdata -> v
6231 | Vend -> error "unexpected end of input in defaults" s spos
6232 | Vopen ("keymap", attrs, closed) ->
6233 let modename =
6234 try List.assoc "mode" attrs
6235 with Not_found -> "global" in
6236 if closed
6237 then v
6238 else
6239 let ret keymap =
6240 let h = findkeyhash dc modename in
6241 KeyMap.iter (Hashtbl.replace h) keymap;
6242 defaults
6244 { v with f = pkeymap ret KeyMap.empty }
6246 | Vopen (_, _, _) ->
6247 error "unexpected subelement in defaults" s spos
6249 | Vclose "defaults" ->
6250 { v with f = llppconfig }
6252 | Vclose _ -> error "unexpected close in defaults" s spos
6254 and uifont b v t spos epos =
6255 match t with
6256 | Vdata | Vcdata ->
6257 Buffer.add_substring b s spos (epos - spos);
6259 | Vopen (_, _, _) ->
6260 error "unexpected subelement in ui-font" s spos
6261 | Vclose "ui-font" ->
6262 if String.length !fontpath = 0
6263 then fontpath := Buffer.contents b;
6264 { v with f = llppconfig }
6265 | Vclose _ -> error "unexpected close in ui-font" s spos
6266 | Vend -> error "unexpected end of input in ui-font" s spos
6268 and doc path pan anchor c bookmarks v t spos _ =
6269 match t with
6270 | Vdata | Vcdata -> v
6271 | Vend -> error "unexpected end of input in doc" s spos
6272 | Vopen ("bookmarks", _, closed) ->
6273 if closed
6274 then v
6275 else { v with f = pbookmarks path pan anchor c bookmarks }
6277 | Vopen ("keymap", attrs, closed) ->
6278 let modename =
6279 try List.assoc "mode" attrs
6280 with Not_found -> "global"
6282 if closed
6283 then v
6284 else
6285 let ret keymap =
6286 let h = findkeyhash c modename in
6287 KeyMap.iter (Hashtbl.replace h) keymap;
6288 doc path pan anchor c bookmarks
6290 { v with f = pkeymap ret KeyMap.empty }
6292 | Vopen (_, _, _) ->
6293 error "unexpected subelement in doc" s spos
6295 | Vclose "doc" ->
6296 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
6297 { v with f = llppconfig }
6299 | Vclose _ -> error "unexpected close in doc" s spos
6301 and pkeymap ret keymap v t spos _ =
6302 match t with
6303 | Vdata | Vcdata -> v
6304 | Vend -> error "unexpected end of input in keymap" s spos
6305 | Vopen ("map", attrs, closed) ->
6306 let r, l = map_of attrs in
6307 let kss = fromstring keys_of_string spos "in" r [] in
6308 let lss = fromstring keys_of_string spos "out" l [] in
6309 let keymap =
6310 match kss with
6311 | [] -> keymap
6312 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
6313 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
6315 if closed
6316 then { v with f = pkeymap ret keymap }
6317 else
6318 let f () = v in
6319 { v with f = skip "map" f }
6321 | Vopen _ ->
6322 error "unexpected subelement in keymap" s spos
6324 | Vclose "keymap" ->
6325 { v with f = ret keymap }
6327 | Vclose _ -> error "unexpected close in keymap" s spos
6329 and pbookmarks path pan anchor c bookmarks v t spos _ =
6330 match t with
6331 | Vdata | Vcdata -> v
6332 | Vend -> error "unexpected end of input in bookmarks" s spos
6333 | Vopen ("item", attrs, closed) ->
6334 let titleent, spage, srely = bookmark_of attrs in
6335 let page = fromstring int_of_string spos "page" spage 0
6336 and rely = fromstring float_of_string spos "rely" srely 0.0 in
6337 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
6338 if closed
6339 then { v with f = pbookmarks path pan anchor c bookmarks }
6340 else
6341 let f () = v in
6342 { v with f = skip "item" f }
6344 | Vopen _ ->
6345 error "unexpected subelement in bookmarks" s spos
6347 | Vclose "bookmarks" ->
6348 { v with f = doc path pan anchor c bookmarks }
6350 | Vclose _ -> error "unexpected close in bookmarks" s spos
6352 and skip tag f v t spos _ =
6353 match t with
6354 | Vdata | Vcdata -> v
6355 | Vend ->
6356 error ("unexpected end of input in skipped " ^ tag) s spos
6357 | Vopen (tag', _, closed) ->
6358 if closed
6359 then v
6360 else
6361 let f' () = { v with f = skip tag f } in
6362 { v with f = skip tag' f' }
6363 | Vclose ctag ->
6364 if tag = ctag
6365 then f ()
6366 else error ("unexpected close in skipped " ^ tag) s spos
6369 parse { f = toplevel; accu = () } s;
6370 h, dc;
6373 let do_load f ic =
6375 let len = in_channel_length ic in
6376 let s = String.create len in
6377 really_input ic s 0 len;
6378 f s;
6379 with
6380 | Parse_error (msg, s, pos) ->
6381 let subs = subs s pos in
6382 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6383 failwith ("parse error: " ^ s)
6385 | exn ->
6386 failwith ("config load error: " ^ Printexc.to_string exn)
6389 let defconfpath =
6390 let dir =
6392 let dir = Filename.concat home ".config" in
6393 if Sys.is_directory dir then dir else home
6394 with _ -> home
6396 Filename.concat dir "llpp.conf"
6399 let confpath = ref defconfpath;;
6401 let load1 f =
6402 if Sys.file_exists !confpath
6403 then
6404 match
6405 (try Some (open_in_bin !confpath)
6406 with exn ->
6407 prerr_endline
6408 ("Error opening configuation file `" ^ !confpath ^ "': " ^
6409 Printexc.to_string exn);
6410 None
6412 with
6413 | Some ic ->
6414 begin try
6415 f (do_load get ic)
6416 with exn ->
6417 prerr_endline
6418 ("Error loading configuation from `" ^ !confpath ^ "': " ^
6419 Printexc.to_string exn);
6420 end;
6421 close_in ic;
6423 | None -> ()
6424 else
6425 f (Hashtbl.create 0, defconf)
6428 let load () =
6429 let f (h, dc) =
6430 let pc, pb, px, pa =
6432 Hashtbl.find h (Filename.basename state.path)
6433 with Not_found -> dc, [], 0, (0, 0.0)
6435 setconf defconf dc;
6436 setconf conf pc;
6437 state.bookmarks <- pb;
6438 state.x <- px;
6439 state.scrollw <- conf.scrollbw;
6440 if conf.jumpback
6441 then state.anchor <- pa;
6442 cbput state.hists.nav pa;
6444 load1 f
6447 let add_attrs bb always dc c =
6448 let ob s a b =
6449 if always || a != b
6450 then Printf.bprintf bb "\n %s='%b'" s a
6451 and oi s a b =
6452 if always || a != b
6453 then Printf.bprintf bb "\n %s='%d'" s a
6454 and oI s a b =
6455 if always || a != b
6456 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6457 and oz s a b =
6458 if always || a <> b
6459 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
6460 and oF s a b =
6461 if always || a <> b
6462 then Printf.bprintf bb "\n %s='%f'" s a
6463 and oc s a b =
6464 if always || a <> b
6465 then
6466 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6467 and oC s a b =
6468 if always || a <> b
6469 then
6470 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6471 and oR s a b =
6472 if always || a <> b
6473 then
6474 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6475 and os s a b =
6476 if always || a <> b
6477 then
6478 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6479 and og s a b =
6480 if always || a <> b
6481 then
6482 match a with
6483 | None -> ()
6484 | Some (_N, _A, _B) ->
6485 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6486 and oW s a b =
6487 if always || a <> b
6488 then
6489 let v =
6490 match a with
6491 | None -> "false"
6492 | Some f ->
6493 if f = infinity
6494 then "true"
6495 else string_of_float f
6497 Printf.bprintf bb "\n %s='%s'" s v
6498 and oco s a b =
6499 if always || a <> b
6500 then
6501 match a with
6502 | Cmulti ((n, a, b), _) when n > 1 ->
6503 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6504 | Csplit (n, _) when n > 1 ->
6505 Printf.bprintf bb "\n %s='%d'" s ~-n
6506 | _ -> ()
6507 and obeco s a b =
6508 if always || a <> b
6509 then
6510 match a with
6511 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6512 | _ -> ()
6514 let w, h =
6515 if always
6516 then dc.winw, dc.winh
6517 else
6518 match state.fullscreen with
6519 | Some wh -> wh
6520 | None -> c.winw, c.winh
6522 let zoom, presentation, interpagespace, maxwait =
6523 if always
6524 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
6525 else
6526 match state.mode with
6527 | Birdseye (bc, _, _, _, _) ->
6528 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
6529 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
6531 oi "width" w dc.winw;
6532 oi "height" h dc.winh;
6533 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6534 oi "scroll-handle-height" c.scrollh dc.scrollh;
6535 ob "case-insensitive-search" c.icase dc.icase;
6536 ob "preload" c.preload dc.preload;
6537 oi "page-bias" c.pagebias dc.pagebias;
6538 oi "scroll-step" c.scrollstep dc.scrollstep;
6539 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6540 ob "max-height-fit" c.maxhfit dc.maxhfit;
6541 ob "crop-hack" c.crophack dc.crophack;
6542 oW "throttle" maxwait dc.maxwait;
6543 ob "highlight-links" c.hlinks dc.hlinks;
6544 ob "under-cursor-info" c.underinfo dc.underinfo;
6545 oi "vertical-margin" interpagespace dc.interpagespace;
6546 oz "zoom" zoom dc.zoom;
6547 ob "presentation" presentation dc.presentation;
6548 oi "rotation-angle" c.angle dc.angle;
6549 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6550 ob "proportional-display" c.proportional dc.proportional;
6551 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6552 oi "tex-count" c.texcount dc.texcount;
6553 oi "slice-height" c.sliceheight dc.sliceheight;
6554 oi "thumbnail-width" c.thumbw dc.thumbw;
6555 ob "persistent-location" c.jumpback dc.jumpback;
6556 oc "background-color" c.bgcolor dc.bgcolor;
6557 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6558 oi "tile-width" c.tilew dc.tilew;
6559 oi "tile-height" c.tileh dc.tileh;
6560 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6561 ob "checkers" c.checkers dc.checkers;
6562 oi "aalevel" c.aalevel dc.aalevel;
6563 ob "trim-margins" c.trimmargins dc.trimmargins;
6564 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6565 os "uri-launcher" c.urilauncher dc.urilauncher;
6566 os "path-launcher" c.pathlauncher dc.pathlauncher;
6567 oC "color-space" c.colorspace dc.colorspace;
6568 ob "invert-colors" c.invert dc.invert;
6569 oF "brightness" c.colorscale dc.colorscale;
6570 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6571 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6572 oco "columns" c.columns dc.columns;
6573 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6574 os "selection-command" c.selcmd dc.selcmd;
6575 ob "update-cursor" c.updatecurs dc.updatecurs;
6576 oi "hint-font-size" c.hfsize dc.hfsize;
6577 oi "horizontal-scroll-step" c.hscrollstep dc.hscrollstep;
6578 oF "page-scroll-scale" c.pgscale dc.pgscale;
6581 let keymapsbuf always dc c =
6582 let bb = Buffer.create 16 in
6583 let rec loop = function
6584 | [] -> ()
6585 | (modename, h) :: rest ->
6586 let dh = findkeyhash dc modename in
6587 if always || h <> dh
6588 then (
6589 if Hashtbl.length h > 0
6590 then (
6591 if Buffer.length bb > 0
6592 then Buffer.add_char bb '\n';
6593 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6594 Hashtbl.iter (fun i o ->
6595 let isdifferent = always ||
6597 let dO = Hashtbl.find dh i in
6598 dO <> o
6599 with Not_found -> true
6601 if isdifferent
6602 then
6603 let addkm (k, m) =
6604 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6605 if Wsi.withalt m then Buffer.add_string bb "alt-";
6606 if Wsi.withshift m then Buffer.add_string bb "shift-";
6607 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6608 Buffer.add_string bb (Wsi.keyname k);
6610 let addkms l =
6611 let rec loop = function
6612 | [] -> ()
6613 | km :: [] -> addkm km
6614 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6616 loop l
6618 Buffer.add_string bb "<map in='";
6619 addkm i;
6620 match o with
6621 | KMinsrt km ->
6622 Buffer.add_string bb "' out='";
6623 addkm km;
6624 Buffer.add_string bb "'/>\n"
6626 | KMinsrl kms ->
6627 Buffer.add_string bb "' out='";
6628 addkms kms;
6629 Buffer.add_string bb "'/>\n"
6631 | KMmulti (ins, kms) ->
6632 Buffer.add_char bb ' ';
6633 addkms ins;
6634 Buffer.add_string bb "' out='";
6635 addkms kms;
6636 Buffer.add_string bb "'/>\n"
6637 ) h;
6638 Buffer.add_string bb "</keymap>";
6641 loop rest
6643 loop c.keyhashes;
6647 let save () =
6648 let uifontsize = fstate.fontsize in
6649 let bb = Buffer.create 32768 in
6650 let f (h, dc) =
6651 let dc = if conf.bedefault then conf else dc in
6652 Buffer.add_string bb "<llppconfig>\n";
6654 if String.length !fontpath > 0
6655 then
6656 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6657 uifontsize
6658 !fontpath
6659 else (
6660 if uifontsize <> 14
6661 then
6662 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6665 Buffer.add_string bb "<defaults ";
6666 add_attrs bb true dc dc;
6667 let kb = keymapsbuf true dc dc in
6668 if Buffer.length kb > 0
6669 then (
6670 Buffer.add_string bb ">\n";
6671 Buffer.add_buffer bb kb;
6672 Buffer.add_string bb "\n</defaults>\n";
6674 else Buffer.add_string bb "/>\n";
6676 let adddoc path pan anchor c bookmarks =
6677 if bookmarks == [] && c = dc && anchor = emptyanchor
6678 then ()
6679 else (
6680 Printf.bprintf bb "<doc path='%s'"
6681 (enent path 0 (String.length path));
6683 if anchor <> emptyanchor
6684 then (
6685 let n, y = anchor in
6686 Printf.bprintf bb " page='%d'" n;
6687 if y > 1e-6
6688 then
6689 Printf.bprintf bb " rely='%f'" y
6693 if pan != 0
6694 then Printf.bprintf bb " pan='%d'" pan;
6696 add_attrs bb false dc c;
6697 let kb = keymapsbuf false dc c in
6699 begin match bookmarks with
6700 | [] ->
6701 if Buffer.length kb > 0
6702 then (
6703 Buffer.add_string bb ">\n";
6704 Buffer.add_buffer bb kb;
6705 Buffer.add_string bb "\n</doc>\n";
6707 else Buffer.add_string bb "/>\n"
6708 | _ ->
6709 Buffer.add_string bb ">\n<bookmarks>\n";
6710 List.iter (fun (title, _level, (page, rely)) ->
6711 Printf.bprintf bb
6712 "<item title='%s' page='%d'"
6713 (enent title 0 (String.length title))
6714 page
6716 if rely > 1e-6
6717 then
6718 Printf.bprintf bb " rely='%f'" rely
6720 Buffer.add_string bb "/>\n";
6721 ) bookmarks;
6722 Buffer.add_string bb "</bookmarks>";
6723 if Buffer.length kb > 0
6724 then (
6725 Buffer.add_string bb "\n";
6726 Buffer.add_buffer bb kb;
6728 Buffer.add_string bb "\n</doc>\n";
6729 end;
6733 let pan, conf =
6734 match state.mode with
6735 | Birdseye (c, pan, _, _, _) ->
6736 let beyecolumns =
6737 match conf.columns with
6738 | Cmulti ((c, _, _), _) -> Some c
6739 | Csingle -> None
6740 | Csplit _ -> None
6741 and columns =
6742 match c.columns with
6743 | Cmulti (c, _) -> Cmulti (c, [||])
6744 | Csingle -> Csingle
6745 | Csplit _ -> failwith "quit from bird's eye while split"
6747 pan, { c with beyecolumns = beyecolumns; columns = columns }
6748 | _ -> state.x, conf
6750 let basename = Filename.basename state.path in
6751 adddoc basename pan (getanchor ())
6752 { conf with
6753 autoscrollstep =
6754 match state.autoscroll with
6755 | Some step -> step
6756 | None -> conf.autoscrollstep }
6757 (if conf.savebmarks then state.bookmarks else []);
6759 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
6760 if basename <> path
6761 then adddoc path x y c bookmarks
6762 ) h;
6763 Buffer.add_string bb "</llppconfig>";
6765 load1 f;
6766 if Buffer.length bb > 0
6767 then
6769 let tmp = !confpath ^ ".tmp" in
6770 let oc = open_out_bin tmp in
6771 Buffer.output_buffer oc bb;
6772 close_out oc;
6773 Unix.rename tmp !confpath;
6774 with exn ->
6775 prerr_endline
6776 ("error while saving configuration: " ^ Printexc.to_string exn)
6778 end;;
6780 let () =
6781 Arg.parse
6782 (Arg.align
6783 [("-p", Arg.String (fun s -> state.password <- s) ,
6784 "<password> Set password");
6786 ("-f", Arg.String (fun s -> Config.fontpath := s),
6787 "<path> Set path to the user interface font");
6789 ("-c", Arg.String (fun s -> Config.confpath := s),
6790 "<path> Set path to the configuration file");
6792 ("-v", Arg.Unit (fun () ->
6793 Printf.printf
6794 "%s\nconfiguration path: %s\n"
6795 (version ())
6796 Config.defconfpath
6798 exit 0), " Print version and exit");
6801 (fun s -> state.path <- s)
6802 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6804 if String.length state.path = 0
6805 then (prerr_endline "file name missing"; exit 1);
6807 Config.load ();
6809 let globalkeyhash = findkeyhash conf "global" in
6810 let wsfd, winw, winh = Wsi.init (object
6811 method expose =
6812 if nogeomcmds state.geomcmds || platform == Posx
6813 then display ()
6814 else (
6815 GlClear.color (scalecolor2 conf.bgcolor);
6816 GlClear.clear [`color];
6818 method display = display ()
6819 method reshape w h = reshape w h
6820 method mouse b d x y m = mouse b d x y m
6821 method motion x y = state.mpos <- (x, y); motion x y
6822 method pmotion x y = state.mpos <- (x, y); pmotion x y
6823 method key k m =
6824 let mascm = m land (
6825 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
6826 ) in
6827 match state.keystate with
6828 | KSnone ->
6829 let km = k, mascm in
6830 begin
6831 match
6832 let modehash = state.uioh#modehash in
6833 try Hashtbl.find modehash km
6834 with Not_found ->
6835 try Hashtbl.find globalkeyhash km
6836 with Not_found -> KMinsrt (k, m)
6837 with
6838 | KMinsrt (k, m) -> keyboard k m
6839 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6840 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6842 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
6843 List.iter (fun (k, m) -> keyboard k m) insrt;
6844 state.keystate <- KSnone
6845 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
6846 state.keystate <- KSinto (keys, insrt)
6847 | _ ->
6848 state.keystate <- KSnone
6850 method enter x y = state.mpos <- (x, y); pmotion x y
6851 method leave = state.mpos <- (-1, -1)
6852 method quit = raise Quit
6853 end) conf.winw conf.winh (platform = Posx) in
6855 state.wsfd <- wsfd;
6857 if not (
6858 List.exists GlMisc.check_extension
6859 [ "GL_ARB_texture_rectangle"
6860 ; "GL_EXT_texture_recangle"
6861 ; "GL_NV_texture_rectangle" ]
6863 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
6865 let cr, sw =
6866 match Ne.pipe () with
6867 | Ne.Exn exn ->
6868 Printf.eprintf "pipe/crsw failed: %s" (Printexc.to_string exn);
6869 exit 1
6870 | Ne.Res rw -> rw
6871 and sr, cw =
6872 match Ne.pipe () with
6873 | Ne.Exn exn ->
6874 Printf.eprintf "pipe/srcw failed: %s" (Printexc.to_string exn);
6875 exit 1
6876 | Ne.Res rw -> rw
6879 cloexec cr;
6880 cloexec sw;
6881 cloexec sr;
6882 cloexec cw;
6884 setcheckers conf.checkers;
6885 redirectstderr ();
6887 init (cr, cw) (
6888 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
6889 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6890 !Config.fontpath
6892 state.sr <- sr;
6893 state.sw <- sw;
6894 state.text <- "Opening " ^ state.path;
6895 reshape winw winh;
6896 opendoc state.path state.password;
6897 state.uioh <- uioh;
6899 let rec loop deadline =
6900 let r =
6901 match state.errfd with
6902 | None -> [state.sr; state.wsfd]
6903 | Some fd -> [state.sr; state.wsfd; fd]
6905 if state.redisplay
6906 then (
6907 state.redisplay <- false;
6908 display ();
6910 let timeout =
6911 let now = now () in
6912 if deadline > now
6913 then (
6914 if deadline = infinity
6915 then ~-.1.0
6916 else max 0.0 (deadline -. now)
6918 else 0.0
6920 let r, _, _ =
6921 try Unix.select r [] [] timeout
6922 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
6924 begin match r with
6925 | [] ->
6926 state.ghyll None;
6927 let newdeadline =
6928 if state.ghyll == noghyll
6929 then
6930 match state.autoscroll with
6931 | Some step when step != 0 ->
6932 let y = state.y + step in
6933 let y =
6934 if y < 0
6935 then state.maxy
6936 else if y >= state.maxy then 0 else y
6938 gotoy y;
6939 if state.mode = View
6940 then state.text <- "";
6941 deadline +. 0.01
6942 | _ -> infinity
6943 else deadline +. 0.01
6945 loop newdeadline
6947 | l ->
6948 let rec checkfds = function
6949 | [] -> ()
6950 | fd :: rest when fd = state.sr ->
6951 let cmd = readcmd state.sr in
6952 act cmd;
6953 checkfds rest
6955 | fd :: rest when fd = state.wsfd ->
6956 Wsi.readresp fd;
6957 checkfds rest
6959 | fd :: rest ->
6960 let s = String.create 80 in
6961 let n = Unix.read fd s 0 80 in
6962 if conf.redirectstderr
6963 then (
6964 Buffer.add_substring state.errmsgs s 0 n;
6965 state.newerrmsgs <- true;
6966 state.redisplay <- true;
6968 else (
6969 prerr_string (String.sub s 0 n);
6970 flush stderr;
6972 checkfds rest
6974 checkfds l;
6975 let newdeadline =
6976 let deadline1 =
6977 if deadline = infinity
6978 then now () +. 0.01
6979 else deadline
6981 match state.autoscroll with
6982 | Some step when step != 0 -> deadline1
6983 | _ -> if state.ghyll == noghyll then infinity else deadline1
6985 loop newdeadline
6986 end;
6989 loop infinity;
6990 with Quit ->
6991 Config.save ();