Whoops
[llpp.git] / main.ml
blob1fb108d84d58e4ce96a7da4030546c202146d867
1 open Utils;;
3 exception Quit;;
5 type under =
6 | Unone
7 | Ulinkuri of string
8 | Ulinkgoto of (int * int)
9 | Utext of facename
10 | Uunexpected of string
11 | Ulaunch of string
12 | Unamed of string
13 | Uremote of (string * int)
14 and facename = string;;
16 type params = (angle * fitmodel * trimparams
17 * texcount * sliceheight * memsize
18 * colorspace * fontpath * trimcachepath
19 * haspbo)
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 trimmargins = bool
29 and interpagespace = int
30 and texcount = int
31 and sliceheight = int
32 and gen = int
33 and top = float
34 and dtop = float
35 and fontpath = string
36 and trimcachepath = string
37 and memsize = int
38 and aalevel = int
39 and irect = (int * int * int * int)
40 and trimparams = (trimmargins * irect)
41 and colorspace = | Rgb | Bgr | Gray
42 and fitmodel = | FitWidth | FitProportional | FitPage
43 and haspbo = bool
46 type x = int
47 and y = int
48 and tilex = int
49 and tiley = int
50 and tileparams = (x * y * width * height * tilex * tiley)
53 type link =
54 | Lnotfound
55 | Lfound of int
56 and linkdir =
57 | LDfirst
58 | LDlast
59 | LDfirstvisible of (int * int * int)
60 | LDleft of int
61 | LDright of int
62 | LDdown of int
63 | LDup of int
66 type pagewithlinks =
67 | Pwlnotfound
68 | Pwl of int
71 type keymap =
72 | KMinsrt of key
73 | KMinsrl of key list
74 | KMmulti of key list * key list
75 and key = int * int
76 and keyhash = (key, keymap) Hashtbl.t
77 and keystate =
78 | KSnone
79 | KSinto of (key list * key list)
82 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
83 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
85 type pipe = (Unix.file_descr * Unix.file_descr);;
87 external init : pipe -> params -> unit = "ml_init";;
88 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
89 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
90 external getpdimrect : int -> float array = "ml_getpdimrect";;
91 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
92 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
93 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
94 external measurestr : int -> string -> float = "ml_measure_string";;
95 external postprocess :
96 opaque -> int -> int -> int -> (int * string * int) -> int
97 = "ml_postprocess";;
98 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
99 external platform : unit -> platform = "ml_platform";;
100 external setaalevel : int -> unit = "ml_setaalevel";;
101 external realloctexts : int -> bool = "ml_realloctexts";;
102 external findlink : opaque -> linkdir -> link = "ml_findlink";;
103 external getlink : opaque -> int -> under = "ml_getlink";;
104 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
105 external getlinkcount : opaque -> int = "ml_getlinkcount";;
106 external findpwl : int -> int -> pagewithlinks = "ml_find_page_with_links"
107 external popen : string -> (Unix.file_descr * int) list -> unit = "ml_popen";;
108 external getpbo : width -> height -> colorspace -> string = "ml_getpbo";;
109 external freepbo : string -> unit = "ml_freepbo";;
110 external unmappbo : string -> unit = "ml_unmappbo";;
111 external pbousable : unit -> bool = "ml_pbo_usable";;
112 external unproject : opaque -> int -> int -> (int * int) option
113 = "ml_unproject";;
114 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
116 let platform_to_string = function
117 | Punknown -> "unknown"
118 | Plinux -> "Linux"
119 | Posx -> "OSX"
120 | Psun -> "Sun"
121 | Pfreebsd -> "FreeBSD"
122 | Pdragonflybsd -> "DragonflyBSD"
123 | Popenbsd -> "OpenBSD"
124 | Pnetbsd -> "NetBSD"
125 | Pcygwin -> "Cygwin"
128 let platform = platform ();;
130 let now = Unix.gettimeofday;;
132 let popen cmd fda =
133 if platform = Pcygwin
134 then (
135 let sh = "/bin/sh" in
136 let args = [|sh; "-c"; cmd|] in
137 let rec std si so se = function
138 | [] -> si, so, se
139 | (fd, 0) :: rest -> std fd so se rest
140 | (fd, -1) :: rest ->
141 Unix.set_close_on_exec fd;
142 std si so se rest
143 | (_, n) :: _ ->
144 failwith ("unexpected fdn in cygwin popen " ^ string_of_int n)
146 let si, so, se = std Unix.stdin Unix.stdout Unix.stderr fda in
147 ignore (Unix.create_process sh args si so se)
149 else popen cmd fda;
152 type mpos = int * int
153 and mstate =
154 | Msel of (mpos * mpos)
155 | Mpan of mpos
156 | Mscrolly | Mscrollx
157 | Mzoom of (int * int)
158 | Mzoomrect of (mpos * mpos)
159 | Mnone
162 type textentry = string * string * onhist option * onkey * ondone * cancelonempty
163 and onkey = string -> int -> te
164 and ondone = string -> unit
165 and histcancel = unit -> unit
166 and onhist = ((histcmd -> string) * histcancel)
167 and histcmd = HCnext | HCprev | HCfirst | HClast
168 and cancelonempty = bool
169 and te =
170 | TEstop
171 | TEdone of string
172 | TEcont of string
173 | TEswitch of textentry
176 type 'a circbuf =
177 { store : 'a array
178 ; mutable rc : int
179 ; mutable wc : int
180 ; mutable len : int
184 let bound v minv maxv =
185 max minv (min maxv v);
188 let cbnew n v =
189 { store = Array.create n v
190 ; rc = 0
191 ; wc = 0
192 ; len = 0
196 let cbcap b = Array.length b.store;;
198 let cbput b v =
199 let cap = cbcap b in
200 b.store.(b.wc) <- v;
201 b.wc <- (b.wc + 1) mod cap;
202 b.rc <- b.wc;
203 b.len <- min (b.len + 1) cap;
206 let cbempty b = b.len = 0;;
208 let cbgetg b circular dir =
209 if cbempty b
210 then b.store.(0)
211 else
212 let rc = b.rc + dir in
213 let rc =
214 if circular
215 then (
216 if rc = -1
217 then b.len-1
218 else (
219 if rc >= b.len
220 then 0
221 else rc
224 else bound rc 0 (b.len-1)
226 b.rc <- rc;
227 b.store.(rc);
230 let cbget b = cbgetg b false;;
231 let cbgetc b = cbgetg b true;;
233 let drawstring size x y s =
234 Gl.enable `blend;
235 Gl.enable `texture_2d;
236 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
237 ignore (drawstr size x y s);
238 Gl.disable `blend;
239 Gl.disable `texture_2d;
242 let drawstring1 size x y s =
243 drawstr size x y s;
246 let drawstring2 size x y fmt =
247 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
250 type page =
251 { pageno : int
252 ; pagedimno : int
253 ; pagew : int
254 ; pageh : int
255 ; pagex : int
256 ; pagey : int
257 ; pagevw : int
258 ; pagevh : int
259 ; pagedispx : int
260 ; pagedispy : int
261 ; pagecol : int
265 let debugl l =
266 dolog "l %d dim=%d {" l.pageno l.pagedimno;
267 dolog " WxH %dx%d" l.pagew l.pageh;
268 dolog " vWxH %dx%d" l.pagevw l.pagevh;
269 dolog " pagex,y %d,%d" l.pagex l.pagey;
270 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
271 dolog " column %d" l.pagecol;
272 dolog "}";
275 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
276 dolog "rect {";
277 dolog " x0,y0=(% f, % f)" x0 y0;
278 dolog " x1,y1=(% f, % f)" x1 y1;
279 dolog " x2,y2=(% f, % f)" x2 y2;
280 dolog " x3,y3=(% f, % f)" x3 y3;
281 dolog "}";
284 type multicolumns = multicol * pagegeom
285 and singlecolumn = pagegeom
286 and splitcolumns = columncount * pagegeom
287 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
288 and multicol = columncount * covercount * covercount
289 and pdimno = int
290 and columncount = int
291 and covercount = int;;
293 type conf =
294 { mutable scrollbw : int
295 ; mutable scrollh : int
296 ; mutable icase : bool
297 ; mutable preload : bool
298 ; mutable pagebias : int
299 ; mutable verbose : bool
300 ; mutable debug : bool
301 ; mutable scrollstep : int
302 ; mutable hscrollstep : int
303 ; mutable maxhfit : bool
304 ; mutable crophack : bool
305 ; mutable autoscrollstep : int
306 ; mutable maxwait : float option
307 ; mutable hlinks : bool
308 ; mutable underinfo : bool
309 ; mutable interpagespace : interpagespace
310 ; mutable zoom : float
311 ; mutable presentation : bool
312 ; mutable angle : angle
313 ; mutable cwinw : int
314 ; mutable cwinh : int
315 ; mutable cx : int
316 ; mutable savebmarks : bool
317 ; mutable fitmodel : fitmodel
318 ; mutable trimmargins : trimmargins
319 ; mutable trimfuzz : irect
320 ; mutable memlimit : memsize
321 ; mutable texcount : texcount
322 ; mutable sliceheight : sliceheight
323 ; mutable thumbw : width
324 ; mutable jumpback : bool
325 ; mutable bgcolor : (float * float * float)
326 ; mutable bedefault : bool
327 ; mutable scrollbarinpm : bool
328 ; mutable tilew : int
329 ; mutable tileh : int
330 ; mutable mustoresize : memsize
331 ; mutable checkers : bool
332 ; mutable aalevel : int
333 ; mutable urilauncher : string
334 ; mutable pathlauncher : string
335 ; mutable colorspace : colorspace
336 ; mutable invert : bool
337 ; mutable colorscale : float
338 ; mutable redirectstderr : bool
339 ; mutable ghyllscroll : (int * int * int) option
340 ; mutable columns : columns
341 ; mutable beyecolumns : columncount option
342 ; mutable selcmd : string
343 ; mutable updatecurs : bool
344 ; mutable keyhashes : (string * keyhash) list
345 ; mutable hfsize : int
346 ; mutable pgscale : float
347 ; mutable usepbo : bool
348 ; mutable wheelbypage : bool
349 ; mutable stcmd : string
351 and columns =
352 | Csingle of singlecolumn
353 | Cmulti of multicolumns
354 | Csplit of splitcolumns
357 type anchor = pageno * top * dtop;;
359 type outline = string * int * anchor;;
361 type rect = float * float * float * float * float * float * float * float;;
363 type tile = opaque * pixmapsize * elapsed
364 and elapsed = float;;
365 type pagemapkey = pageno * gen;;
366 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
367 and row = int
368 and col = int;;
370 let emptyanchor = (0, 0.0, 0.0);;
372 type infochange = | Memused | Docinfo | Pdim;;
374 class type uioh = object
375 method display : unit
376 method key : int -> int -> uioh
377 method button : int -> bool -> int -> int -> int -> uioh
378 method motion : int -> int -> uioh
379 method pmotion : int -> int -> uioh
380 method infochanged : infochange -> unit
381 method scrollpw : (int * float * float)
382 method scrollph : (int * float * float)
383 method modehash : keyhash
384 method eformsgs : bool
385 end;;
387 type mode =
388 | Birdseye of (conf * leftx * pageno * pageno * anchor)
389 | Textentry of (textentry * onleave)
390 | View
391 | LinkNav of linktarget
392 and onleave = leavetextentrystatus -> unit
393 and leavetextentrystatus = | Cancel | Confirm
394 and helpitem = string * int * action
395 and action =
396 | Noaction
397 | Action of (uioh -> uioh)
398 and linktarget =
399 | Ltexact of (pageno * int)
400 | Ltgendir of int
403 let isbirdseye = function Birdseye _ -> true | _ -> false;;
404 let istextentry = function Textentry _ -> true | _ -> false;;
406 type currently =
407 | Idle
408 | Loading of (page * gen)
409 | Tiling of (
410 page * opaque * colorspace * angle * gen * col * row * width * height
412 | Outlining of outline list
415 let emptykeyhash = Hashtbl.create 0;;
416 let nouioh : uioh = object (self)
417 method display = ()
418 method key _ _ = self
419 method button _ _ _ _ _ = self
420 method motion _ _ = self
421 method pmotion _ _ = self
422 method infochanged _ = ()
423 method scrollpw = (0, nan, nan)
424 method scrollph = (0, nan, nan)
425 method modehash = emptykeyhash
426 method eformsgs = false
427 end;;
429 type state =
430 { mutable sr : Unix.file_descr
431 ; mutable sw : Unix.file_descr
432 ; mutable wsfd : Unix.file_descr
433 ; mutable errfd : Unix.file_descr option
434 ; mutable stderr : Unix.file_descr
435 ; mutable errmsgs : Buffer.t
436 ; mutable newerrmsgs : bool
437 ; mutable w : int
438 ; mutable x : int
439 ; mutable y : int
440 ; mutable scrollw : int
441 ; mutable hscrollh : int
442 ; mutable anchor : anchor
443 ; mutable ranchors : (string * string * anchor * string) list
444 ; mutable maxy : int
445 ; mutable layout : page list
446 ; pagemap : (pagemapkey, opaque) Hashtbl.t
447 ; tilemap : (tilemapkey, tile) Hashtbl.t
448 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
449 ; mutable pdims : (pageno * width * height * leftx) list
450 ; mutable pagecount : int
451 ; mutable currently : currently
452 ; mutable mstate : mstate
453 ; mutable searchpattern : string
454 ; mutable rects : (pageno * recttype * rect) list
455 ; mutable rects1 : (pageno * recttype * rect) list
456 ; mutable text : string
457 ; mutable winstate : Wsi.winstate list
458 ; mutable mode : mode
459 ; mutable uioh : uioh
460 ; mutable outlines : outline array
461 ; mutable bookmarks : outline list
462 ; mutable path : string
463 ; mutable password : string
464 ; mutable nameddest : string
465 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
466 ; mutable memused : memsize
467 ; mutable gen : gen
468 ; mutable throttle : (page list * int * float) option
469 ; mutable autoscroll : int option
470 ; mutable ghyll : (int option -> unit)
471 ; mutable help : helpitem array
472 ; mutable docinfo : (int * string) list
473 ; mutable texid : GlTex.texture_id option
474 ; hists : hists
475 ; mutable prevzoom : float
476 ; mutable progress : float
477 ; mutable redisplay : bool
478 ; mutable mpos : mpos
479 ; mutable keystate : keystate
480 ; mutable glinks : bool
481 ; mutable prevcolumns : (columns * float) option
482 ; mutable winw : int
483 ; mutable winh : int
484 ; mutable reprf : (unit -> unit)
485 ; mutable origin : string
487 and hists =
488 { pat : string circbuf
489 ; pag : string circbuf
490 ; nav : anchor circbuf
491 ; sel : string circbuf
495 let defconf =
496 { scrollbw = 7
497 ; scrollh = 12
498 ; icase = true
499 ; preload = true
500 ; pagebias = 0
501 ; verbose = false
502 ; debug = false
503 ; scrollstep = 24
504 ; hscrollstep = 24
505 ; maxhfit = true
506 ; crophack = false
507 ; autoscrollstep = 2
508 ; maxwait = None
509 ; hlinks = false
510 ; underinfo = false
511 ; interpagespace = 2
512 ; zoom = 1.0
513 ; presentation = false
514 ; angle = 0
515 ; cwinw = 900
516 ; cwinh = 900
517 ; cx = 0
518 ; savebmarks = true
519 ; fitmodel = FitProportional
520 ; trimmargins = false
521 ; trimfuzz = (0,0,0,0)
522 ; memlimit = 32 lsl 20
523 ; texcount = 256
524 ; sliceheight = 24
525 ; thumbw = 76
526 ; jumpback = true
527 ; bgcolor = (0.5, 0.5, 0.5)
528 ; bedefault = false
529 ; scrollbarinpm = true
530 ; tilew = 2048
531 ; tileh = 2048
532 ; mustoresize = 256 lsl 20
533 ; checkers = true
534 ; aalevel = 8
535 ; urilauncher =
536 (match platform with
537 | Plinux | Pfreebsd | Pdragonflybsd
538 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
539 | Posx -> "open \"%s\""
540 | Pcygwin -> "cygstart \"%s\""
541 | Punknown -> "echo %s")
542 ; pathlauncher = "lp \"%s\""
543 ; selcmd =
544 (match platform with
545 | Plinux | Pfreebsd | Pdragonflybsd
546 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
547 | Posx -> "pbcopy"
548 | Pcygwin -> "wsel"
549 | Punknown -> "cat")
550 ; colorspace = Rgb
551 ; invert = false
552 ; colorscale = 1.0
553 ; redirectstderr = false
554 ; ghyllscroll = None
555 ; columns = Csingle [||]
556 ; beyecolumns = None
557 ; updatecurs = false
558 ; hfsize = 12
559 ; pgscale = 1.0
560 ; usepbo = false
561 ; wheelbypage = false
562 ; stcmd = "echo SyncTex"
563 ; keyhashes =
564 let mk n = (n, Hashtbl.create 1) in
565 [ mk "global"
566 ; mk "info"
567 ; mk "help"
568 ; mk "outline"
569 ; mk "listview"
570 ; mk "birdseye"
571 ; mk "textentry"
572 ; mk "links"
573 ; mk "view"
578 let wtmode = ref false;;
580 let findkeyhash c name =
581 try List.assoc name c.keyhashes
582 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
585 let conf = { defconf with angle = defconf.angle };;
587 let pgscale h = truncate (float h *. conf.pgscale);;
589 type fontstate =
590 { mutable fontsize : int
591 ; mutable wwidth : float
592 ; mutable maxrows : int
596 let fstate =
597 { fontsize = 14
598 ; wwidth = nan
599 ; maxrows = -1
603 let geturl s =
604 let colonpos = try String.index s ':' with Not_found -> -1 in
605 let len = String.length s in
606 if colonpos >= 0 && colonpos + 3 < len
607 then (
608 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
609 then
610 let schemestartpos =
611 try String.rindex_from s colonpos ' '
612 with Not_found -> -1
614 let scheme =
615 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
617 match scheme with
618 | "http" | "ftp" | "mailto" ->
619 let epos =
620 try String.index_from s colonpos ' '
621 with Not_found -> len
623 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
624 | _ -> ""
625 else ""
627 else ""
630 let gotouri uri =
631 if String.length conf.urilauncher = 0
632 then print_endline uri
633 else (
634 let url = geturl uri in
635 if String.length url = 0
636 then print_endline uri
637 else
638 let re = Str.regexp "%s" in
639 let command = Str.global_replace re url conf.urilauncher in
640 try popen command []
641 with exn ->
642 Printf.eprintf
643 "failed to execute `%s': %s\n" command (exntos exn);
644 flush stderr;
648 let version () =
649 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
650 (platform_to_string platform) Sys.word_size Sys.ocaml_version
653 let makehelp () =
654 let strings = version () :: "" :: Help.keys in
655 Array.of_list (
656 List.map (fun s ->
657 let url = geturl s in
658 if String.length url > 0
659 then (s, 0, Action (fun u -> gotouri url; u))
660 else (s, 0, Noaction)
661 ) strings);
664 let noghyll _ = ();;
665 let firstgeomcmds = "", [];;
666 let noreprf () = ();;
668 let state =
669 { sr = Unix.stdin
670 ; sw = Unix.stdin
671 ; wsfd = Unix.stdin
672 ; errfd = None
673 ; stderr = Unix.stderr
674 ; errmsgs = Buffer.create 0
675 ; newerrmsgs = false
676 ; x = 0
677 ; y = 0
678 ; w = 0
679 ; scrollw = 0
680 ; hscrollh = 0
681 ; anchor = emptyanchor
682 ; ranchors = []
683 ; layout = []
684 ; maxy = max_int
685 ; tilelru = Queue.create ()
686 ; pagemap = Hashtbl.create 10
687 ; tilemap = Hashtbl.create 10
688 ; pdims = []
689 ; pagecount = 0
690 ; currently = Idle
691 ; mstate = Mnone
692 ; rects = []
693 ; rects1 = []
694 ; text = ""
695 ; mode = View
696 ; winstate = []
697 ; searchpattern = ""
698 ; outlines = [||]
699 ; bookmarks = []
700 ; path = ""
701 ; password = ""
702 ; nameddest = ""
703 ; geomcmds = firstgeomcmds
704 ; hists =
705 { nav = cbnew 10 emptyanchor
706 ; pat = cbnew 10 ""
707 ; pag = cbnew 10 ""
708 ; sel = cbnew 10 ""
710 ; memused = 0
711 ; gen = 0
712 ; throttle = None
713 ; autoscroll = None
714 ; ghyll = noghyll
715 ; help = makehelp ()
716 ; docinfo = []
717 ; texid = None
718 ; prevzoom = 1.0
719 ; progress = -1.0
720 ; uioh = nouioh
721 ; redisplay = true
722 ; mpos = (-1, -1)
723 ; keystate = KSnone
724 ; glinks = false
725 ; prevcolumns = None
726 ; winw = -1
727 ; winh = -1
728 ; reprf = noreprf
729 ; origin = ""
733 let setfontsize n =
734 fstate.fontsize <- n;
735 fstate.wwidth <- measurestr fstate.fontsize "w";
736 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
739 let vlog fmt =
740 if conf.verbose
741 then
742 Printf.kprintf prerr_endline fmt
743 else
744 Printf.kprintf ignore fmt
747 let launchpath () =
748 if String.length conf.pathlauncher = 0
749 then print_endline state.path
750 else (
751 let re = Str.regexp "%s" in
752 let command = Str.global_replace re state.path conf.pathlauncher in
753 try popen command []
754 with exn ->
755 Printf.eprintf "failed to execute `%s': %s\n" command (exntos exn);
756 flush stderr;
760 module Ne = struct
761 type 'a t = | Res of 'a | Exn of exn;;
763 let pipe () =
764 try Res (Unix.pipe ())
765 with exn -> Exn exn
768 let clo fd f =
769 try tempfailureretry Unix.close fd
770 with exn -> f (exntos exn)
773 let dup fd =
774 try Res (tempfailureretry Unix.dup fd)
775 with exn -> Exn exn
778 let dup2 fd1 fd2 =
779 try Res (tempfailureretry (Unix.dup2 fd1) fd2)
780 with exn -> Exn exn
782 end;;
784 let redirectstderr () =
785 let clofail what errmsg = dolog "failed to close %s: %s" what errmsg in
786 if conf.redirectstderr
787 then
788 match Ne.pipe () with
789 | Ne.Exn exn ->
790 dolog "failed to create stderr redirection pipes: %s" (exntos exn)
792 | Ne.Res (r, w) ->
793 begin match Ne.dup Unix.stderr with
794 | Ne.Exn exn ->
795 dolog "failed to dup stderr: %s" (exntos exn);
796 Ne.clo r (clofail "pipe/r");
797 Ne.clo w (clofail "pipe/w");
799 | Ne.Res dupstderr ->
800 begin match Ne.dup2 w Unix.stderr with
801 | Ne.Exn exn ->
802 dolog "failed to dup2 to stderr: %s" (exntos exn);
803 Ne.clo dupstderr (clofail "stderr duplicate");
804 Ne.clo r (clofail "redir pipe/r");
805 Ne.clo w (clofail "redir pipe/w");
807 | Ne.Res () ->
808 state.stderr <- dupstderr;
809 state.errfd <- Some r;
810 end;
812 else (
813 state.newerrmsgs <- false;
814 begin match state.errfd with
815 | Some fd ->
816 begin match Ne.dup2 state.stderr Unix.stderr with
817 | Ne.Exn exn ->
818 dolog "failed to dup2 original stderr: %s" (exntos exn)
819 | Ne.Res () ->
820 Ne.clo fd (clofail "dup of stderr");
821 state.errfd <- None;
822 end;
823 | None -> ()
824 end;
825 prerr_string (Buffer.contents state.errmsgs);
826 flush stderr;
827 Buffer.clear state.errmsgs;
831 module G =
832 struct
833 let postRedisplay who =
834 if conf.verbose
835 then prerr_endline ("redisplay for " ^ who);
836 state.redisplay <- true;
838 end;;
840 let getopaque pageno =
841 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
842 with Not_found -> None
845 let putopaque pageno opaque =
846 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
849 let pagetranslatepoint l x y =
850 let dy = y - l.pagedispy in
851 let y = dy + l.pagey in
852 let dx = x - l.pagedispx in
853 let x = dx + l.pagex in
854 (x, y);
857 let onppundermouse g x y d =
858 let rec f = function
859 | l :: rest ->
860 begin match getopaque l.pageno with
861 | Some opaque ->
862 let x0 = l.pagedispx in
863 let x1 = x0 + l.pagevw in
864 let y0 = l.pagedispy in
865 let y1 = y0 + l.pagevh in
866 if y >= y0 && y <= y1 && x >= x0 && x <= x1
867 then
868 let px, py = pagetranslatepoint l x y in
869 match g opaque l px py with
870 | Some res -> res
871 | None -> f rest
872 else f rest
873 | _ ->
874 f rest
876 | [] -> d
878 f state.layout
881 let getunder x y =
882 let g opaque _ px py =
883 match whatsunder opaque px py with
884 | Unone -> None
885 | under -> Some under
887 onppundermouse g x y Unone
890 let unproject x y =
891 let g opaque l x y =
892 match unproject opaque x y with
893 | Some (x, y) -> Some (Some (l.pageno, x, y))
894 | None -> None
896 onppundermouse g x y None;
899 let showtext c s =
900 state.text <- Printf.sprintf "%c%s" c s;
901 G.postRedisplay "showtext";
904 let selstring s =
905 match Ne.pipe () with
906 | Ne.Exn exn ->
907 showtext '!' (Printf.sprintf "pipe failed: %s" (exntos exn))
908 | Ne.Res (r, w) ->
909 let popened =
910 try popen conf.selcmd [r, 0; w, -1]; true
911 with exn ->
912 showtext '!'
913 (Printf.sprintf "failed to execute %s: %s"
914 conf.selcmd (exntos exn));
915 false
917 let clo cap fd =
918 Ne.clo fd (fun msg ->
919 showtext '!' (Printf.sprintf "failed to close %s: %s" cap msg)
922 if popened
923 then
924 (try
925 let l = String.length s in
926 let n = tempfailureretry (Unix.write w s 0) l in
927 if n != l
928 then
929 showtext '!'
930 (Printf.sprintf
931 "failed to write %d characters to sel pipe, wrote %d"
934 with exn ->
935 showtext '!'
936 (Printf.sprintf "failed to write to sel pipe: %s"
937 (exntos exn)
940 else dolog "%s" s;
941 clo "pipe/r" r;
942 clo "pipe/w" w;
945 let undertext = function
946 | Unone -> "none"
947 | Ulinkuri s -> s
948 | Ulinkgoto (pageno, _) -> Printf.sprintf "%s: page %d" state.path (pageno+1)
949 | Utext s -> "font: " ^ s
950 | Uunexpected s -> "unexpected: " ^ s
951 | Ulaunch s -> "launch: " ^ s
952 | Unamed s -> "named: " ^ s
953 | Uremote (filename, pageno) ->
954 Printf.sprintf "%s: page %d" filename (pageno+1)
957 let updateunder x y =
958 match getunder x y with
959 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
960 | Ulinkuri uri ->
961 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
962 Wsi.setcursor Wsi.CURSOR_INFO
963 | Ulinkgoto (pageno, _) ->
964 if conf.underinfo
965 then showtext 'p' ("age: " ^ string_of_int (pageno+1));
966 Wsi.setcursor Wsi.CURSOR_INFO
967 | Utext s ->
968 if conf.underinfo then showtext 'f' ("ont: " ^ s);
969 Wsi.setcursor Wsi.CURSOR_TEXT
970 | Uunexpected s ->
971 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
972 Wsi.setcursor Wsi.CURSOR_INHERIT
973 | Ulaunch s ->
974 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
975 Wsi.setcursor Wsi.CURSOR_INHERIT
976 | Unamed s ->
977 if conf.underinfo then showtext 'n' ("amed: " ^ s);
978 Wsi.setcursor Wsi.CURSOR_INHERIT
979 | Uremote (filename, pageno) ->
980 if conf.underinfo then showtext 'r'
981 (Printf.sprintf "emote: %s (%d)" filename (pageno+1));
982 Wsi.setcursor Wsi.CURSOR_INFO
985 let showlinktype under =
986 if conf.underinfo
987 then
988 match under with
989 | Unone -> ()
990 | under ->
991 let s = undertext under in
992 showtext ' ' s
995 let addchar s c =
996 let b = Buffer.create (String.length s + 1) in
997 Buffer.add_string b s;
998 Buffer.add_char b c;
999 Buffer.contents b;
1002 let colorspace_of_string s =
1003 match String.lowercase s with
1004 | "rgb" -> Rgb
1005 | "bgr" -> Bgr
1006 | "gray" -> Gray
1007 | _ -> failwith "invalid colorspace"
1010 let int_of_colorspace = function
1011 | Rgb -> 0
1012 | Bgr -> 1
1013 | Gray -> 2
1016 let colorspace_of_int = function
1017 | 0 -> Rgb
1018 | 1 -> Bgr
1019 | 2 -> Gray
1020 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
1023 let colorspace_to_string = function
1024 | Rgb -> "rgb"
1025 | Bgr -> "bgr"
1026 | Gray -> "gray"
1029 let fitmodel_of_string s =
1030 match String.lowercase s with
1031 | "width" -> FitWidth
1032 | "proportional" -> FitProportional
1033 | "page" -> FitPage
1034 | _ -> failwith "invalid fit model"
1037 let int_of_fitmodel = function
1038 | FitWidth -> 0
1039 | FitProportional -> 1
1040 | FitPage -> 2
1043 let fitmodel_of_int = function
1044 | 0 -> FitWidth
1045 | 1 -> FitProportional
1046 | 2 -> FitPage
1047 | n -> failwith ("invalid fit model index " ^ string_of_int n)
1050 let fitmodel_to_string = function
1051 | FitWidth -> "width"
1052 | FitProportional -> "proportional"
1053 | FitPage -> "page"
1056 let intentry_with_suffix text key =
1057 let c =
1058 if key >= 32 && key < 127
1059 then Char.chr key
1060 else '\000'
1062 match Char.lowercase c with
1063 | '0' .. '9' ->
1064 let text = addchar text c in
1065 TEcont text
1067 | 'k' | 'm' | 'g' ->
1068 let text = addchar text c in
1069 TEcont text
1071 | _ ->
1072 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
1073 TEcont text
1076 let multicolumns_to_string (n, a, b) =
1077 if a = 0 && b = 0
1078 then Printf.sprintf "%d" n
1079 else Printf.sprintf "%d,%d,%d" n a b;
1082 let multicolumns_of_string s =
1084 (int_of_string s, 0, 0)
1085 with _ ->
1086 Scanf.sscanf s "%u,%u,%u" (fun n a b ->
1087 if a > 1 || b > 1
1088 then failwith "subtly broken"; (n, a, b)
1092 let readcmd fd =
1093 let s = "xxxx" in
1094 let n = tempfailureretry (Unix.read fd s 0) 4 in
1095 if n != 4 then failwith "incomplete read(len)";
1096 let len = 0
1097 lor (Char.code s.[0] lsl 24)
1098 lor (Char.code s.[1] lsl 16)
1099 lor (Char.code s.[2] lsl 8)
1100 lor (Char.code s.[3] lsl 0)
1102 let s = String.create len in
1103 let n = tempfailureretry (Unix.read fd s 0) len in
1104 if n != len then failwith "incomplete read(data)";
1108 let btod b = if b then 1 else 0;;
1110 let wcmd fmt =
1111 let b = Buffer.create 16 in
1112 Buffer.add_string b "llll";
1113 Printf.kbprintf
1114 (fun b ->
1115 let s = Buffer.contents b in
1116 let n = String.length s in
1117 let len = n - 4 in
1118 (* dolog "wcmd %S" (String.sub s 4 len); *)
1119 s.[0] <- Char.chr ((len lsr 24) land 0xff);
1120 s.[1] <- Char.chr ((len lsr 16) land 0xff);
1121 s.[2] <- Char.chr ((len lsr 8) land 0xff);
1122 s.[3] <- Char.chr (len land 0xff);
1123 let n' = tempfailureretry (Unix.write state.sw s 0) n in
1124 if n' != n then failwith "write failed";
1125 ) b fmt;
1128 let calcips h =
1129 let d = state.winh - h in
1130 max conf.interpagespace ((d + 1) / 2)
1133 let rowyh (c, coverA, coverB) b n =
1134 if c = 1 || (n < coverA || n >= state.pagecount - coverB)
1135 then
1136 let _, _, vy, (_, _, h, _) = b.(n) in
1137 (vy, h)
1138 else
1139 let n' = n - coverA in
1140 let d = n' mod c in
1141 let s = n - d in
1142 let e = min state.pagecount (s + c) in
1143 let rec find m miny maxh = if m = e then miny, maxh else
1144 let _, _, y, (_, _, h, _) = b.(m) in
1145 let miny = min miny y in
1146 let maxh = max maxh h in
1147 find (m+1) miny maxh
1148 in find s max_int 0
1151 let calcheight () =
1152 match conf.columns with
1153 | Cmulti ((_, _, _) as cl, b) ->
1154 if Array.length b > 0
1155 then
1156 let y, h = rowyh cl b (Array.length b - 1) in
1157 y + h + (if conf.presentation then calcips h else 0)
1158 else 0
1159 | Csingle b ->
1160 if Array.length b > 0
1161 then
1162 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1163 y + h + (if conf.presentation then calcips h else 0)
1164 else 0
1165 | Csplit (_, b) ->
1166 if Array.length b > 0
1167 then
1168 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1169 y + h
1170 else 0
1173 let getpageyh pageno =
1174 let pageno = bound pageno 0 (state.pagecount-1) in
1175 match conf.columns with
1176 | Csingle b ->
1177 if Array.length b = 0
1178 then 0, 0
1179 else
1180 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1181 let y =
1182 if conf.presentation
1183 then y - calcips h
1184 else y
1186 y, h
1187 | Cmulti (cl, b) ->
1188 if Array.length b = 0
1189 then 0, 0
1190 else
1191 let y, h = rowyh cl b pageno in
1192 let y =
1193 if conf.presentation
1194 then y - calcips h
1195 else y
1197 y, h
1198 | Csplit (c, b) ->
1199 if Array.length b = 0
1200 then 0, 0
1201 else
1202 let n = pageno*c in
1203 let (_, _, y, (_, _, h, _)) = b.(n) in
1204 y, h
1207 let getpagedim pageno =
1208 let rec f ppdim l =
1209 match l with
1210 | (n, _, _, _) as pdim :: rest ->
1211 if n >= pageno
1212 then (if n = pageno then pdim else ppdim)
1213 else f pdim rest
1215 | [] -> ppdim
1217 f (-1, -1, -1, -1) state.pdims
1220 let getpagey pageno = fst (getpageyh pageno);;
1222 let nogeomcmds cmds =
1223 match cmds with
1224 | s, [] -> String.length s = 0
1225 | _ -> false
1228 let page_of_y y =
1229 let ((c, coverA, coverB) as cl), b =
1230 match conf.columns with
1231 | Csingle b -> (1, 0, 0), b
1232 | Cmulti (c, b) -> c, b
1233 | Csplit (_, b) -> (1, 0, 0), b
1235 if Array.length b = 0
1236 then -1
1237 else
1238 let rec bsearch nmin nmax =
1239 if nmin > nmax
1240 then bound nmin 0 (state.pagecount-1)
1241 else
1242 let n = (nmax + nmin) / 2 in
1243 let vy, h = rowyh cl b n in
1244 let y0, y1 =
1245 if conf.presentation
1246 then
1247 let ips = calcips h in
1248 let y0 = vy - ips in
1249 let y1 = vy + h + ips in
1250 y0, y1
1251 else (
1252 if n = 0
1253 then 0, vy + h + conf.interpagespace
1254 else
1255 let y0 = vy - conf.interpagespace in
1256 y0, y0 + h + conf.interpagespace
1259 if y >= y0 && y < y1
1260 then (
1261 if c = 1
1262 then n
1263 else (
1264 if n > coverA
1265 then
1266 if n < state.pagecount - coverB
1267 then ((n-coverA)/c)*c + coverA
1268 else n
1269 else n
1272 else (
1273 if y > y0
1274 then bsearch (n+1) nmax
1275 else bsearch nmin (n-1)
1278 let r = bsearch 0 (state.pagecount-1) in
1282 let layoutN ((columns, coverA, coverB), b) y sh =
1283 let sh = sh - state.hscrollh in
1284 let rec fold accu n =
1285 if n = Array.length b
1286 then accu
1287 else
1288 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1289 if (vy - y) > sh &&
1290 (n = coverA - 1
1291 || n = state.pagecount - coverB
1292 || (n - coverA) mod columns = columns - 1)
1293 then accu
1294 else
1295 let accu =
1296 if vy + h > y
1297 then
1298 let pagey = max 0 (y - vy) in
1299 let pagedispy = if pagey > 0 then 0 else vy - y in
1300 let pagedispx, pagex =
1301 let pdx =
1302 if n = coverA - 1 || n = state.pagecount - coverB
1303 then state.x + (state.winw - state.scrollw - w) / 2
1304 else dx + xoff + state.x
1306 if pdx < 0
1307 then 0, -pdx
1308 else pdx, 0
1310 let pagevw =
1311 let vw = state.winw - state.scrollw - pagedispx in
1312 let pw = w - pagex in
1313 min vw pw
1315 let pagevh = min (h - pagey) (sh - pagedispy) in
1316 if pagevw > 0 && pagevh > 0
1317 then
1318 let e =
1319 { pageno = n
1320 ; pagedimno = pdimno
1321 ; pagew = w
1322 ; pageh = h
1323 ; pagex = pagex
1324 ; pagey = pagey
1325 ; pagevw = pagevw
1326 ; pagevh = pagevh
1327 ; pagedispx = pagedispx
1328 ; pagedispy = pagedispy
1329 ; pagecol = 0
1332 e :: accu
1333 else
1334 accu
1335 else
1336 accu
1338 fold accu (n+1)
1340 List.rev (fold [] (page_of_y y));
1343 let layoutS (columns, b) y sh =
1344 let sh = sh - state.hscrollh in
1345 let rec fold accu n =
1346 if n = Array.length b
1347 then accu
1348 else
1349 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1350 if (vy - y) > sh
1351 then accu
1352 else
1353 let accu =
1354 if vy + pageh > y
1355 then
1356 let x = xoff + state.x in
1357 let pagey = max 0 (y - vy) in
1358 let pagedispy = if pagey > 0 then 0 else vy - y in
1359 let pagedispx, pagex =
1360 if px = 0
1361 then (
1362 if x < 0
1363 then 0, -x
1364 else x, 0
1366 else (
1367 let px = px - x in
1368 if px < 0
1369 then -px, 0
1370 else 0, px
1373 let pagecolw = pagew/columns in
1374 let pagedispx =
1375 if pagecolw < state.winw
1376 then pagedispx + ((state.winw - state.scrollw - pagecolw) / 2)
1377 else pagedispx
1379 let pagevw =
1380 let vw = state.winw - pagedispx - state.scrollw in
1381 let pw = pagew - pagex in
1382 min vw pw
1384 let pagevw = min pagevw pagecolw in
1385 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1386 if pagevw > 0 && pagevh > 0
1387 then
1388 let e =
1389 { pageno = n/columns
1390 ; pagedimno = pdimno
1391 ; pagew = pagew
1392 ; pageh = pageh
1393 ; pagex = pagex
1394 ; pagey = pagey
1395 ; pagevw = pagevw
1396 ; pagevh = pagevh
1397 ; pagedispx = pagedispx
1398 ; pagedispy = pagedispy
1399 ; pagecol = n mod columns
1402 e :: accu
1403 else
1404 accu
1405 else
1406 accu
1408 fold accu (n+1)
1410 List.rev (fold [] 0)
1413 let layout y sh =
1414 if nogeomcmds state.geomcmds
1415 then
1416 match conf.columns with
1417 | Csingle b -> layoutN ((1, 0, 0), b) y sh
1418 | Cmulti c -> layoutN c y sh
1419 | Csplit s -> layoutS s y sh
1420 else []
1423 let clamp incr =
1424 let y = state.y + incr in
1425 let y = max 0 y in
1426 let y = min y (state.maxy - (if conf.maxhfit then state.winh else 0)) in
1430 let itertiles l f =
1431 let tilex = l.pagex mod conf.tilew in
1432 let tiley = l.pagey mod conf.tileh in
1434 let col = l.pagex / conf.tilew in
1435 let row = l.pagey / conf.tileh in
1437 let rec rowloop row y0 dispy h =
1438 if h = 0
1439 then ()
1440 else (
1441 let dh = conf.tileh - y0 in
1442 let dh = min h dh in
1443 let rec colloop col x0 dispx w =
1444 if w = 0
1445 then ()
1446 else (
1447 let dw = conf.tilew - x0 in
1448 let dw = min w dw in
1450 f col row dispx dispy x0 y0 dw dh;
1451 colloop (col+1) 0 (dispx+dw) (w-dw)
1454 colloop col tilex l.pagedispx l.pagevw;
1455 rowloop (row+1) 0 (dispy+dh) (h-dh)
1458 if l.pagevw > 0 && l.pagevh > 0
1459 then rowloop row tiley l.pagedispy l.pagevh;
1462 let gettileopaque l col row =
1463 let key =
1464 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1466 try Some (Hashtbl.find state.tilemap key)
1467 with Not_found -> None
1470 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1471 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1472 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1475 let drawtiles l color =
1476 GlDraw.color color;
1477 let f col row x y tilex tiley w h =
1478 match gettileopaque l col row with
1479 | Some (opaque, _, t) ->
1480 let params = x, y, w, h, tilex, tiley in
1481 if conf.invert
1482 then (
1483 Gl.enable `blend;
1484 GlFunc.blend_func `zero `one_minus_src_color;
1486 drawtile params opaque;
1487 if conf.invert
1488 then Gl.disable `blend;
1489 if conf.debug
1490 then (
1491 let s = Printf.sprintf
1492 "%d[%d,%d] %f sec"
1493 l.pageno col row t
1495 let w = measurestr fstate.fontsize s in
1496 GlMisc.push_attrib [`current];
1497 GlDraw.color (0.0, 0.0, 0.0);
1498 GlDraw.rect
1499 (float (x-2), float (y-2))
1500 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1501 GlDraw.color (1.0, 1.0, 1.0);
1502 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1503 GlMisc.pop_attrib ();
1506 | _ ->
1507 let w =
1508 let lw = state.winw - state.scrollw - x in
1509 min lw w
1510 and h =
1511 let lh = state.winh - y in
1512 min lh h
1514 begin match state.texid with
1515 | Some id ->
1516 Gl.enable `texture_2d;
1517 GlTex.bind_texture `texture_2d id;
1518 let x0 = float x
1519 and y0 = float y
1520 and x1 = float (x+w)
1521 and y1 = float (y+h) in
1523 let tw = float w /. 16.0
1524 and th = float h /. 16.0 in
1525 let tx0 = float tilex /. 16.0
1526 and ty0 = float tiley /. 16.0 in
1527 let tx1 = tx0 +. tw
1528 and ty1 = ty0 +. th in
1529 GlDraw.begins `quads;
1530 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1531 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1532 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1533 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1534 GlDraw.ends ();
1536 Gl.disable `texture_2d;
1537 | None ->
1538 GlDraw.color (1.0, 1.0, 1.0);
1539 GlDraw.rect
1540 (float x, float y)
1541 (float (x+w), float (y+h));
1542 end;
1543 if w > 128 && h > fstate.fontsize + 10
1544 then (
1545 GlDraw.color (0.0, 0.0, 0.0);
1546 let c, r =
1547 if conf.verbose
1548 then (col*conf.tilew, row*conf.tileh)
1549 else col, row
1551 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1553 GlDraw.color color;
1555 itertiles l f
1558 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1560 let tilevisible1 l x y =
1561 let ax0 = l.pagex
1562 and ax1 = l.pagex + l.pagevw
1563 and ay0 = l.pagey
1564 and ay1 = l.pagey + l.pagevh in
1566 let bx0 = x
1567 and by0 = y in
1568 let bx1 = min (bx0 + conf.tilew) l.pagew
1569 and by1 = min (by0 + conf.tileh) l.pageh in
1571 let rx0 = max ax0 bx0
1572 and ry0 = max ay0 by0
1573 and rx1 = min ax1 bx1
1574 and ry1 = min ay1 by1 in
1576 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1577 nonemptyintersection
1580 let tilevisible layout n x y =
1581 let rec findpageinlayout m = function
1582 | l :: rest when l.pageno = n ->
1583 tilevisible1 l x y || (
1584 match conf.columns with
1585 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
1586 | _ -> false
1588 | _ :: rest -> findpageinlayout 0 rest
1589 | [] -> false
1591 findpageinlayout 0 layout;
1594 let tileready l x y =
1595 tilevisible1 l x y &&
1596 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1599 let tilepage n p layout =
1600 let rec loop = function
1601 | l :: rest ->
1602 if l.pageno = n
1603 then
1604 let f col row _ _ _ _ _ _ =
1605 if state.currently = Idle
1606 then
1607 match gettileopaque l col row with
1608 | Some _ -> ()
1609 | None ->
1610 let x = col*conf.tilew
1611 and y = row*conf.tileh in
1612 let w =
1613 let w = l.pagew - x in
1614 min w conf.tilew
1616 let h =
1617 let h = l.pageh - y in
1618 min h conf.tileh
1620 let pbo =
1621 if conf.usepbo
1622 then getpbo w h conf.colorspace
1623 else "0"
1625 wcmd "tile %s %d %d %d %d %s" p x y w h pbo;
1626 state.currently <-
1627 Tiling (
1628 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1629 conf.tilew, conf.tileh
1632 itertiles l f;
1633 else
1634 loop rest
1636 | [] -> ()
1638 if nogeomcmds state.geomcmds
1639 then loop layout;
1642 let preloadlayout y =
1643 let y = if y < state.winh then 0 else y - state.winh in
1644 let h = state.winh*3 in
1645 layout y h;
1648 let load pages =
1649 let rec loop pages =
1650 if state.currently != Idle
1651 then ()
1652 else
1653 match pages with
1654 | l :: rest ->
1655 begin match getopaque l.pageno with
1656 | None ->
1657 wcmd "page %d %d" l.pageno l.pagedimno;
1658 state.currently <- Loading (l, state.gen);
1659 | Some opaque ->
1660 tilepage l.pageno opaque pages;
1661 loop rest
1662 end;
1663 | _ -> ()
1665 if nogeomcmds state.geomcmds
1666 then loop pages
1669 let preload pages =
1670 load pages;
1671 if conf.preload && state.currently = Idle
1672 then load (preloadlayout state.y);
1675 let layoutready layout =
1676 let rec fold all ls =
1677 all && match ls with
1678 | l :: rest ->
1679 let seen = ref false in
1680 let allvisible = ref true in
1681 let foo col row _ _ _ _ _ _ =
1682 seen := true;
1683 allvisible := !allvisible &&
1684 begin match gettileopaque l col row with
1685 | Some _ -> true
1686 | None -> false
1689 itertiles l foo;
1690 fold (!seen && !allvisible) rest
1691 | [] -> true
1693 let alltilesvisible = fold true layout in
1694 alltilesvisible;
1697 let gotoy y =
1698 let y = bound y 0 state.maxy in
1699 let y, layout, proceed =
1700 match conf.maxwait with
1701 | Some time when state.ghyll == noghyll ->
1702 begin match state.throttle with
1703 | None ->
1704 let layout = layout y state.winh in
1705 let ready = layoutready layout in
1706 if not ready
1707 then (
1708 load layout;
1709 state.throttle <- Some (layout, y, now ());
1711 else G.postRedisplay "gotoy showall (None)";
1712 y, layout, ready
1713 | Some (_, _, started) ->
1714 let dt = now () -. started in
1715 if dt > time
1716 then (
1717 state.throttle <- None;
1718 let layout = layout y state.winh in
1719 load layout;
1720 G.postRedisplay "maxwait";
1721 y, layout, true
1723 else -1, [], false
1726 | _ ->
1727 let layout = layout y state.winh in
1728 if not !wtmode || layoutready layout
1729 then G.postRedisplay "gotoy ready";
1730 y, layout, true
1732 if proceed
1733 then (
1734 state.y <- y;
1735 state.layout <- layout;
1736 begin match state.mode with
1737 | LinkNav (Ltexact (pageno, linkno)) ->
1738 let rec loop = function
1739 | [] ->
1740 state.mode <- LinkNav (Ltgendir 0)
1741 | l :: _ when l.pageno = pageno ->
1742 begin match getopaque pageno with
1743 | None ->
1744 state.mode <- LinkNav (Ltgendir 0)
1745 | Some opaque ->
1746 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1747 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1748 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1749 then state.mode <- LinkNav (Ltgendir 0)
1751 | _ :: rest -> loop rest
1753 loop layout
1754 | _ -> ()
1755 end;
1756 begin match state.mode with
1757 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1758 if not (pagevisible layout pageno)
1759 then (
1760 match state.layout with
1761 | [] -> ()
1762 | l :: _ ->
1763 state.mode <- Birdseye (
1764 conf, leftx, l.pageno, hooverpageno, anchor
1767 | LinkNav (Ltgendir dir as lt) ->
1768 let linknav =
1769 let rec loop = function
1770 | [] -> lt
1771 | l :: rest ->
1772 match getopaque l.pageno with
1773 | None -> loop rest
1774 | Some opaque ->
1775 let link =
1776 let ld =
1777 if dir = 0
1778 then LDfirstvisible (l.pagex, l.pagey, dir)
1779 else (
1780 if dir > 0 then LDfirst else LDlast
1783 findlink opaque ld
1785 match link with
1786 | Lnotfound -> loop rest
1787 | Lfound n ->
1788 showlinktype (getlink opaque n);
1789 Ltexact (l.pageno, n)
1791 loop state.layout
1793 state.mode <- LinkNav linknav
1794 | _ -> ()
1795 end;
1796 preload layout;
1798 state.ghyll <- noghyll;
1799 if conf.updatecurs
1800 then (
1801 let mx, my = state.mpos in
1802 updateunder mx my;
1806 let conttiling pageno opaque =
1807 tilepage pageno opaque
1808 (if conf.preload then preloadlayout state.y else state.layout)
1811 let gotoy_and_clear_text y =
1812 if not conf.verbose then state.text <- "";
1813 gotoy y;
1816 let getanchor1 l =
1817 let top =
1818 let coloff = l.pagecol * l.pageh in
1819 float (l.pagey + coloff) /. float l.pageh
1821 let dtop =
1822 if l.pagedispy = 0
1823 then
1825 else
1826 if conf.presentation
1827 then float l.pagedispy /. float (calcips l.pageh)
1828 else float l.pagedispy /. float conf.interpagespace
1830 (l.pageno, top, dtop)
1833 let getanchor () =
1834 match state.layout with
1835 | l :: _ -> getanchor1 l
1836 | [] ->
1837 let n = page_of_y state.y in
1838 if n = -1
1839 then state.anchor
1840 else
1841 let y, h = getpageyh n in
1842 let dy = y - state.y in
1843 let dtop =
1844 if conf.presentation
1845 then
1846 let ips = calcips h in
1847 float (dy + ips) /. float ips
1848 else
1849 float dy /. float conf.interpagespace
1851 (n, 0.0, dtop)
1854 let getanchory (n, top, dtop) =
1855 let y, h = getpageyh n in
1856 if conf.presentation
1857 then
1858 let ips = calcips h in
1859 y + truncate (top*.float h -. dtop*.float ips) + ips;
1860 else
1861 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
1864 let gotoanchor anchor =
1865 gotoy (getanchory anchor);
1868 let addnav () =
1869 cbput state.hists.nav (getanchor ());
1872 let getnav dir =
1873 let anchor = cbgetc state.hists.nav dir in
1874 getanchory anchor;
1877 let gotoghyll y =
1878 let scroll f n a b =
1879 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1880 let snake f a b =
1881 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1882 if f < a
1883 then s (float f /. float a)
1884 else (
1885 if f > b
1886 then 1.0 -. s ((float (f-b) /. float (n-b)))
1887 else 1.0
1890 snake f a b
1891 and summa f n a b =
1892 (* courtesy: (calc-eval "integ(3x^2-2x^3,x)") *)
1893 let iv x = x**3.-.0.5*.x**4. in
1894 let iv1 = iv f in
1895 let ins = float a *. iv1
1896 and outs = float (n-b) *. iv1 in
1897 let ones = b - a in
1898 ins +. outs +. float ones
1900 let rec set (_N, _A, _B) y sy =
1901 let sum = summa 1.0 _N _A _B in
1902 let dy = float (y - sy) in
1903 state.ghyll <- (
1904 let rec gf n y1 o =
1905 if n >= _N
1906 then state.ghyll <- noghyll
1907 else
1908 let go n =
1909 let s = scroll n _N _A _B in
1910 let y1 = y1 +. ((s *. dy) /. sum) in
1911 gotoy_and_clear_text (truncate y1);
1912 state.ghyll <- gf (n+1) y1;
1914 match o with
1915 | None -> go n
1916 | Some y' -> set (_N/2, 1, 1) y' state.y
1918 gf 0 (float state.y)
1921 match conf.ghyllscroll with
1922 | None ->
1923 gotoy_and_clear_text y
1924 | Some nab ->
1925 if state.ghyll == noghyll
1926 then set nab y state.y
1927 else state.ghyll (Some y)
1930 let gotopage n top =
1931 let y, h = getpageyh n in
1932 let y = y + (truncate (top *. float h)) in
1933 gotoghyll y
1936 let gotopage1 n top =
1937 let y = getpagey n in
1938 let y = y + top in
1939 gotoghyll y
1942 let invalidate s f =
1943 state.layout <- [];
1944 state.pdims <- [];
1945 state.rects <- [];
1946 state.rects1 <- [];
1947 match state.geomcmds with
1948 | ps, [] when String.length ps = 0 ->
1949 f ();
1950 state.geomcmds <- s, [];
1952 | ps, [] ->
1953 state.geomcmds <- ps, [s, f];
1955 | ps, (s', _) :: rest when s' = s ->
1956 state.geomcmds <- ps, ((s, f) :: rest);
1958 | ps, cmds ->
1959 state.geomcmds <- ps, ((s, f) :: cmds);
1962 let flushpages () =
1963 Hashtbl.iter (fun _ opaque ->
1964 wcmd "freepage %s" opaque;
1965 ) state.pagemap;
1966 Hashtbl.clear state.pagemap;
1969 let flushtiles () =
1970 if not (Queue.is_empty state.tilelru)
1971 then (
1972 Queue.iter (fun (k, p, s) ->
1973 wcmd "freetile %s" p;
1974 state.memused <- state.memused - s;
1975 Hashtbl.remove state.tilemap k;
1976 ) state.tilelru;
1977 state.uioh#infochanged Memused;
1978 Queue.clear state.tilelru;
1980 load state.layout;
1983 let opendoc path password =
1984 state.path <- path;
1985 state.password <- password;
1986 state.gen <- state.gen + 1;
1987 state.docinfo <- [];
1989 flushpages ();
1990 setaalevel conf.aalevel;
1991 let titlepath =
1992 if String.length state.origin = 0
1993 then path
1994 else state.origin
1996 Wsi.settitle ("llpp " ^ (mbtoutf8 (Filename.basename titlepath)));
1997 wcmd "open %d %s\000%s\000" (btod !wtmode) path password;
1998 invalidate "reqlayout"
1999 (fun () ->
2000 wcmd "reqlayout %d %d %s\000"
2001 conf.angle (int_of_fitmodel conf.fitmodel) state.nameddest;
2005 let reload () =
2006 state.anchor <- getanchor ();
2007 opendoc state.path state.password;
2010 let scalecolor c =
2011 let c = c *. conf.colorscale in
2012 (c, c, c);
2015 let scalecolor2 (r, g, b) =
2016 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
2019 let docolumns = function
2020 | Csingle _ ->
2021 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
2022 let rec loop pageno pdimno pdim y ph pdims =
2023 if pageno = state.pagecount
2024 then ()
2025 else
2026 let pdimno, ((_, w, h, xoff) as pdim), pdims =
2027 match pdims with
2028 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2029 pdimno+1, pdim, rest
2030 | _ ->
2031 pdimno, pdim, pdims
2033 let x = max 0 (((state.winw - state.scrollw - w) / 2) - xoff) in
2034 let y = y +
2035 (if conf.presentation
2036 then (if pageno = 0 then calcips h else calcips ph + calcips h)
2037 else (if pageno = 0 then 0 else conf.interpagespace)
2040 a.(pageno) <- (pdimno, x, y, pdim);
2041 loop (pageno+1) pdimno pdim (y + h) h pdims
2043 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
2044 conf.columns <- Csingle a;
2046 | Cmulti ((columns, coverA, coverB), _) ->
2047 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
2048 let rec loop pageno pdimno pdim x y rowh pdims =
2049 let rec fixrow m = if m = pageno then () else
2050 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
2051 if h < rowh
2052 then (
2053 let y = y + (rowh - h) / 2 in
2054 a.(m) <- (pdimno, x, y, pdim);
2056 fixrow (m+1)
2058 if pageno = state.pagecount
2059 then fixrow (((pageno - 1) / columns) * columns)
2060 else
2061 let pdimno, ((_, w, h, xoff) as pdim), pdims =
2062 match pdims with
2063 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2064 pdimno+1, pdim, rest
2065 | _ ->
2066 pdimno, pdim, pdims
2068 let x, y, rowh' =
2069 if pageno = coverA - 1 || pageno = state.pagecount - coverB
2070 then (
2071 let x = (state.winw - state.scrollw - w) / 2 in
2072 let ips =
2073 if conf.presentation then calcips h else conf.interpagespace in
2074 x, y + ips + rowh, h
2076 else (
2077 if (pageno - coverA) mod columns = 0
2078 then (
2079 let x = max 0 (state.winw - state.scrollw - state.w) / 2 in
2080 let y =
2081 if conf.presentation
2082 then
2083 let ips = calcips h in
2084 y + (if pageno = 0 then 0 else calcips rowh + ips)
2085 else
2086 y + (if pageno = 0 then 0 else conf.interpagespace)
2088 x, y + rowh, h
2090 else x, y, max rowh h
2093 let y =
2094 if pageno > 1 && (pageno - coverA) mod columns = 0
2095 then (
2096 let y =
2097 if pageno = columns && conf.presentation
2098 then (
2099 let ips = calcips rowh in
2100 for i = 0 to pred columns
2102 let (pdimno, x, y, pdim) = a.(i) in
2103 a.(i) <- (pdimno, x, y+ips, pdim)
2104 done;
2105 y+ips;
2107 else y
2109 fixrow (pageno - columns);
2112 else y
2114 a.(pageno) <- (pdimno, x, y, pdim);
2115 let x = x + w + xoff*2 + conf.interpagespace in
2116 loop (pageno+1) pdimno pdim x y rowh' pdims
2118 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
2119 conf.columns <- Cmulti ((columns, coverA, coverB), a);
2121 | Csplit (c, _) ->
2122 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
2123 let rec loop pageno pdimno pdim y pdims =
2124 if pageno = state.pagecount
2125 then ()
2126 else
2127 let pdimno, ((_, w, h, _) as pdim), pdims =
2128 match pdims with
2129 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2130 pdimno+1, pdim, rest
2131 | _ ->
2132 pdimno, pdim, pdims
2134 let cw = w / c in
2135 let rec loop1 n x y =
2136 if n = c then y else (
2137 a.(pageno*c + n) <- (pdimno, x, y, pdim);
2138 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
2141 let y = loop1 0 0 y in
2142 loop (pageno+1) pdimno pdim y pdims
2144 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
2145 conf.columns <- Csplit (c, a);
2148 let represent () =
2149 docolumns conf.columns;
2150 state.maxy <- calcheight ();
2151 state.hscrollh <-
2152 if state.x = 0 && state.w <= state.winw - state.scrollw
2153 then 0
2154 else state.scrollw
2156 if state.reprf == noreprf
2157 then (
2158 match state.mode with
2159 | Birdseye (_, _, pageno, _, _) ->
2160 let y, h = getpageyh pageno in
2161 let top = (state.winh - h) / 2 in
2162 gotoy (max 0 (y - top))
2163 | _ -> gotoanchor state.anchor
2165 else (
2166 state.reprf ();
2167 state.reprf <- noreprf;
2171 let reshape w h =
2172 GlDraw.viewport 0 0 w h;
2173 let firsttime = state.geomcmds == firstgeomcmds in
2174 if not firsttime && nogeomcmds state.geomcmds
2175 then state.anchor <- getanchor ();
2177 state.winw <- w;
2178 let w = truncate (float w *. conf.zoom) - state.scrollw in
2179 let w = max w 2 in
2180 state.winh <- h;
2181 setfontsize fstate.fontsize;
2182 GlMat.mode `modelview;
2183 GlMat.load_identity ();
2185 GlMat.mode `projection;
2186 GlMat.load_identity ();
2187 GlMat.rotate ~x:1.0 ~angle:180.0 ();
2188 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
2189 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
2191 let relx =
2192 if conf.zoom <= 1.0
2193 then 0.0
2194 else float state.x /. float state.w
2196 invalidate "geometry"
2197 (fun () ->
2198 state.w <- w;
2199 if not firsttime
2200 then state.x <- truncate (relx *. float w);
2201 let w =
2202 match conf.columns with
2203 | Csingle _ -> w
2204 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
2205 | Csplit (c, _) -> w * c
2207 wcmd "geometry %d %d %d"
2208 w ((truncate (float h*.conf.zoom)) - 2*conf.interpagespace)
2209 (int_of_fitmodel conf.fitmodel)
2213 let enttext () =
2214 let len = String.length state.text in
2215 let drawstring s =
2216 let hscrollh =
2217 match state.mode with
2218 | Textentry _
2219 | View ->
2220 let h, _, _ = state.uioh#scrollpw in
2222 | _ -> 0
2224 let rect x w =
2225 GlDraw.rect
2226 (x, float (state.winh - (fstate.fontsize + 4) - hscrollh))
2227 (x+.w, float (state.winh - hscrollh))
2230 let w = float (state.winw - state.scrollw - 1) in
2231 if state.progress >= 0.0 && state.progress < 1.0
2232 then (
2233 GlDraw.color (0.3, 0.3, 0.3);
2234 let w1 = w *. state.progress in
2235 rect 0.0 w1;
2236 GlDraw.color (0.0, 0.0, 0.0);
2237 rect w1 (w-.w1)
2239 else (
2240 GlDraw.color (0.0, 0.0, 0.0);
2241 rect 0.0 w;
2244 GlDraw.color (1.0, 1.0, 1.0);
2245 drawstring fstate.fontsize
2246 (if len > 0 then 8 else 2) (state.winh - hscrollh - 5) s;
2248 let s =
2249 match state.mode with
2250 | Textentry ((prefix, text, _, _, _, _), _) ->
2251 let s =
2252 if len > 0
2253 then
2254 Printf.sprintf "%s%s_ [%s]" prefix text state.text
2255 else
2256 Printf.sprintf "%s%s_" prefix text
2260 | _ -> state.text
2262 let s =
2263 if state.newerrmsgs
2264 then (
2265 if not (istextentry state.mode) && state.uioh#eformsgs
2266 then
2267 let s1 = "(press 'e' to review error messasges)" in
2268 if String.length s > 0 then s ^ " " ^ s1 else s1
2269 else s
2271 else s
2273 if String.length s > 0
2274 then drawstring s
2277 let gctiles () =
2278 let len = Queue.length state.tilelru in
2279 let layout = lazy (
2280 match state.throttle with
2281 | None ->
2282 if conf.preload
2283 then preloadlayout state.y
2284 else state.layout
2285 | Some (layout, _, _) ->
2286 layout
2287 ) in
2288 let rec loop qpos =
2289 if state.memused <= conf.memlimit
2290 then ()
2291 else (
2292 if qpos < len
2293 then
2294 let (k, p, s) as lruitem = Queue.pop state.tilelru in
2295 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
2296 let (_, pw, ph, _) = getpagedim n in
2298 gen = state.gen
2299 && colorspace = conf.colorspace
2300 && angle = conf.angle
2301 && pagew = pw
2302 && pageh = ph
2303 && (
2304 let x = col*conf.tilew
2305 and y = row*conf.tileh in
2306 tilevisible (Lazy.force_val layout) n x y
2308 then Queue.push lruitem state.tilelru
2309 else (
2310 freepbo p;
2311 wcmd "freetile %s" p;
2312 state.memused <- state.memused - s;
2313 state.uioh#infochanged Memused;
2314 Hashtbl.remove state.tilemap k;
2316 loop (qpos+1)
2319 loop 0
2322 let logcurrently = function
2323 | Idle -> dolog "Idle"
2324 | Loading (l, gen) ->
2325 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2326 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2327 dolog
2328 "Tiling %d[%d,%d] page=%s cs=%s angle"
2329 l.pageno col row pageopaque
2330 (colorspace_to_string colorspace)
2332 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2333 angle gen conf.angle state.gen
2334 tilew tileh
2335 conf.tilew conf.tileh
2337 | Outlining _ ->
2338 dolog "outlining"
2341 let splitatspace =
2342 let r = Str.regexp " " in
2343 fun s -> Str.bounded_split r s 2;
2346 let onpagerect pageno f =
2347 let b =
2348 match conf.columns with
2349 | Cmulti (_, b) -> b
2350 | Csingle b -> b
2351 | Csplit (_, b) -> b
2353 if pageno >= 0 && pageno < Array.length b
2354 then
2355 let (pdimno, _, _, (_, _, _, _)) = b.(pageno) in
2356 let r = getpdimrect pdimno in
2357 f (r.(1)-.r.(0)) (r.(3)-.r.(2))
2360 let gotopagexy1 pageno x y =
2361 onpagerect pageno (fun w h ->
2362 let top = y /. h in
2363 let _,w1,_,leftx = getpagedim pageno in
2364 let wh = state.winh - state.hscrollh in
2365 let sw = float w1 /. w in
2366 let x = sw *. x in
2367 let x = leftx + state.x + truncate x in
2368 let sx =
2369 if x < 0 || x >= state.winw - state.scrollw
2370 then state.x - x
2371 else state.x
2373 let py, h = getpageyh pageno in
2374 let pdy = truncate (top *. float h) in
2375 let y' = py + pdy in
2376 let dy = y' - state.y in
2377 let sy =
2378 if x != state.x || not (dy > 0 && dy < wh)
2379 then (
2380 if conf.presentation
2381 then
2382 if abs (py - y') > wh
2383 then y'
2384 else py
2385 else y';
2387 else state.y
2389 if state.x != sx || state.y != sy
2390 then (
2391 let x, y =
2392 if !wtmode
2393 then (
2394 let ww = state.winw - state.scrollw in
2395 let qx = sx / ww
2396 and qy = pdy / wh in
2397 let x = qx * ww
2398 and y = py + qy * wh in
2399 let x = if -x + ww > w1 then -(w1-ww) else x
2400 and y' = if y + wh > state.maxy then state.maxy - wh else y in
2401 let y =
2402 if conf.presentation
2403 then
2404 if abs (py - y') > wh
2405 then y'
2406 else py
2407 else y';
2409 (x, y)
2411 else (sx, sy)
2413 state.x <- x;
2414 state.hscrollh <-
2415 if x = 0 && state.w <= state.winw - state.scrollw
2416 then 0
2417 else state.scrollw
2419 gotoy_and_clear_text y;
2421 else gotoy_and_clear_text state.y;
2425 let gotopagexy pageno x y =
2426 match state.mode with
2427 | Birdseye _ -> gotopage pageno 0.0
2428 | _ -> gotopagexy1 pageno x y
2431 let act cmds =
2432 (* dolog "%S" cmds; *)
2433 let cl = splitatspace cmds in
2434 let scan s fmt f =
2435 try Scanf.sscanf s fmt f
2436 with exn ->
2437 dolog "error processing '%S': %s" cmds (exntos exn);
2438 exit 1
2440 match cl with
2441 | "clear" :: [] ->
2442 state.uioh#infochanged Pdim;
2443 state.pdims <- [];
2445 | "clearrects" :: [] ->
2446 state.rects <- state.rects1;
2447 G.postRedisplay "clearrects";
2449 | "continue" :: args :: [] ->
2450 let n = scan args "%u" (fun n -> n) in
2451 state.pagecount <- n;
2452 begin match state.currently with
2453 | Outlining l ->
2454 state.currently <- Idle;
2455 state.outlines <- Array.of_list (List.rev l)
2456 | _ -> ()
2457 end;
2459 let cur, cmds = state.geomcmds in
2460 if String.length cur = 0
2461 then failwith "umpossible";
2463 begin match List.rev cmds with
2464 | [] ->
2465 state.geomcmds <- "", [];
2466 represent ();
2467 | (s, f) :: rest ->
2468 f ();
2469 state.geomcmds <- s, List.rev rest;
2470 end;
2471 if conf.maxwait = None && not !wtmode
2472 then G.postRedisplay "continue";
2474 | "title" :: args :: [] ->
2475 Wsi.settitle args
2477 | "msg" :: args :: [] ->
2478 showtext ' ' args
2480 | "vmsg" :: args :: [] ->
2481 if conf.verbose
2482 then showtext ' ' args
2484 | "emsg" :: args :: [] ->
2485 Buffer.add_string state.errmsgs args;
2486 state.newerrmsgs <- true;
2487 G.postRedisplay "error message"
2489 | "progress" :: args :: [] ->
2490 let progress, text =
2491 scan args "%f %n"
2492 (fun f pos ->
2493 f, String.sub args pos (String.length args - pos))
2495 state.text <- text;
2496 state.progress <- progress;
2497 G.postRedisplay "progress"
2499 | "firstmatch" :: args :: [] ->
2500 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2501 scan args "%u %d %f %f %f %f %f %f %f %f"
2502 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2503 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2505 let y = (getpagey pageno) + truncate y0 in
2506 addnav ();
2507 gotoy y;
2508 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2510 | "match" :: args :: [] ->
2511 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2512 scan args "%u %d %f %f %f %f %f %f %f %f"
2513 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2514 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2516 state.rects1 <-
2517 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2519 | "page" :: args :: [] ->
2520 let pageopaque, t = scan args "%s %f" (fun p t -> p, t) in
2521 begin match state.currently with
2522 | Loading (l, gen) ->
2523 vlog "page %d took %f sec" l.pageno t;
2524 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2525 begin match state.throttle with
2526 | None ->
2527 let preloadedpages =
2528 if conf.preload
2529 then preloadlayout state.y
2530 else state.layout
2532 let evict () =
2533 let set =
2534 List.fold_left (fun s l -> IntSet.add l.pageno s)
2535 IntSet.empty preloadedpages
2537 let evictedpages =
2538 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2539 if not (IntSet.mem pageno set)
2540 then (
2541 wcmd "freepage %s" opaque;
2542 key :: accu
2544 else accu
2545 ) state.pagemap []
2547 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2549 evict ();
2550 state.currently <- Idle;
2551 if gen = state.gen
2552 then (
2553 tilepage l.pageno pageopaque state.layout;
2554 load state.layout;
2555 load preloadedpages;
2556 if pagevisible state.layout l.pageno
2557 && layoutready state.layout
2558 then G.postRedisplay "page";
2561 | Some (layout, _, _) ->
2562 state.currently <- Idle;
2563 tilepage l.pageno pageopaque layout;
2564 load state.layout
2565 end;
2567 | _ ->
2568 dolog "Inconsistent loading state";
2569 logcurrently state.currently;
2570 exit 1
2573 | "tile" :: args :: [] ->
2574 let (x, y, opaque, size, t) =
2575 scan args "%u %u %s %u %f"
2576 (fun x y p size t -> (x, y, p, size, t))
2578 begin match state.currently with
2579 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2580 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2582 unmappbo opaque;
2583 if tilew != conf.tilew || tileh != conf.tileh
2584 then (
2585 wcmd "freetile %s" opaque;
2586 state.currently <- Idle;
2587 load state.layout;
2589 else (
2590 puttileopaque l col row gen cs angle opaque size t;
2591 state.memused <- state.memused + size;
2592 state.uioh#infochanged Memused;
2593 gctiles ();
2594 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2595 opaque, size) state.tilelru;
2597 let layout =
2598 match state.throttle with
2599 | None -> state.layout
2600 | Some (layout, _, _) -> layout
2603 state.currently <- Idle;
2604 if gen = state.gen
2605 && conf.colorspace = cs
2606 && conf.angle = angle
2607 && tilevisible layout l.pageno x y
2608 then conttiling l.pageno pageopaque;
2610 begin match state.throttle with
2611 | None ->
2612 preload state.layout;
2613 if gen = state.gen
2614 && conf.colorspace = cs
2615 && conf.angle = angle
2616 && tilevisible state.layout l.pageno x y
2617 && (not !wtmode || layoutready state.layout)
2618 then G.postRedisplay "tile nothrottle";
2620 | Some (layout, y, _) ->
2621 let ready = layoutready layout in
2622 if ready
2623 then (
2624 state.y <- y;
2625 state.layout <- layout;
2626 state.throttle <- None;
2627 G.postRedisplay "throttle";
2629 else load layout;
2630 end;
2633 | _ ->
2634 dolog "Inconsistent tiling state";
2635 logcurrently state.currently;
2636 exit 1
2639 | "pdim" :: args :: [] ->
2640 let (n, w, h, _) as pdim =
2641 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2643 let pdim =
2644 match conf.fitmodel, conf.columns with
2645 | (FitPage | FitProportional), Csplit _ -> (n, w, h, 0)
2646 | _ -> pdim
2648 state.uioh#infochanged Pdim;
2649 state.pdims <- pdim :: state.pdims
2651 | "o" :: args :: [] ->
2652 let (l, n, t, h, pos) =
2653 scan args "%u %u %d %u %n"
2654 (fun l n t h pos -> l, n, t, h, pos)
2656 let s = String.sub args pos (String.length args - pos) in
2657 let outline = (s, l, (n, float t /. float h, 0.0)) in
2658 begin match state.currently with
2659 | Outlining outlines ->
2660 state.currently <- Outlining (outline :: outlines)
2661 | Idle ->
2662 state.currently <- Outlining [outline]
2663 | currently ->
2664 dolog "invalid outlining state";
2665 logcurrently currently
2668 | "a" :: args :: [] ->
2669 let (n, l, t) =
2670 scan args "%u %d %d" (fun n l t -> n, l, t)
2672 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
2674 | "info" :: args :: [] ->
2675 state.docinfo <- (1, args) :: state.docinfo
2677 | "infoend" :: [] ->
2678 state.uioh#infochanged Docinfo;
2679 state.docinfo <- List.rev state.docinfo
2681 | _ ->
2682 failwith (Printf.sprintf "unknown cmd `%S'" cmds)
2685 let onhist cb =
2686 let rc = cb.rc in
2687 let action = function
2688 | HCprev -> cbget cb ~-1
2689 | HCnext -> cbget cb 1
2690 | HCfirst -> cbget cb ~-(cb.rc)
2691 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2692 and cancel () = cb.rc <- rc
2693 in (action, cancel)
2696 let search pattern forward =
2697 match conf.columns with
2698 | Csplit _ ->
2699 showtext '!' "searching does not work properly in split columns mode"
2700 | _ ->
2701 if String.length pattern > 0
2702 then
2703 let pn, py =
2704 match state.layout with
2705 | [] -> 0, 0
2706 | l :: _ ->
2707 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2709 wcmd "search %d %d %d %d,%s\000"
2710 (btod conf.icase) pn py (btod forward) pattern;
2713 let intentry text key =
2714 let c =
2715 if key >= 32 && key < 127
2716 then Char.chr key
2717 else '\000'
2719 match c with
2720 | '0' .. '9' ->
2721 let text = addchar text c in
2722 TEcont text
2724 | _ ->
2725 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2726 TEcont text
2729 let linknentry text key =
2730 let c =
2731 if key >= 32 && key < 127
2732 then Char.chr key
2733 else '\000'
2735 match c with
2736 | 'a' .. 'z' ->
2737 let text = addchar text c in
2738 TEcont text
2740 | _ ->
2741 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2742 TEcont text
2745 let linkndone f s =
2746 if String.length s > 0
2747 then (
2748 let n =
2749 let l = String.length s in
2750 let rec loop pos n = if pos = l then n else
2751 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
2752 loop (pos+1) (n*26 + m)
2753 in loop 0 0
2755 let rec loop n = function
2756 | [] -> ()
2757 | l :: rest ->
2758 match getopaque l.pageno with
2759 | None -> loop n rest
2760 | Some opaque ->
2761 let m = getlinkcount opaque in
2762 if n < m
2763 then (
2764 let under = getlink opaque n in
2765 f under
2767 else loop (n-m) rest
2769 loop n state.layout;
2773 let textentry text key =
2774 if key land 0xff00 = 0xff00
2775 then TEcont text
2776 else TEcont (text ^ toutf8 key)
2779 let reqlayout angle fitmodel =
2780 match state.throttle with
2781 | None ->
2782 if nogeomcmds state.geomcmds
2783 then state.anchor <- getanchor ();
2784 conf.angle <- angle mod 360;
2785 if conf.angle != 0
2786 then (
2787 match state.mode with
2788 | LinkNav _ -> state.mode <- View
2789 | _ -> ()
2791 conf.fitmodel <- fitmodel;
2792 invalidate "reqlayout"
2793 (fun () ->
2794 wcmd "reqlayout %d %d" conf.angle (int_of_fitmodel conf.fitmodel)
2796 | _ -> ()
2799 let settrim trimmargins trimfuzz =
2800 if nogeomcmds state.geomcmds
2801 then state.anchor <- getanchor ();
2802 conf.trimmargins <- trimmargins;
2803 conf.trimfuzz <- trimfuzz;
2804 let x0, y0, x1, y1 = trimfuzz in
2805 invalidate "settrim"
2806 (fun () ->
2807 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2808 flushpages ();
2811 let setzoom zoom =
2812 match state.throttle with
2813 | None ->
2814 let zoom = max 0.0001 zoom in
2815 if zoom <> conf.zoom
2816 then (
2817 state.prevzoom <- conf.zoom;
2818 conf.zoom <- zoom;
2819 reshape state.winw state.winh;
2820 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
2823 | Some (layout, y, started) ->
2824 let time =
2825 match conf.maxwait with
2826 | None -> 0.0
2827 | Some t -> t
2829 let dt = now () -. started in
2830 if dt > time
2831 then (
2832 state.y <- y;
2833 load layout;
2837 let setcolumns mode columns coverA coverB =
2838 state.prevcolumns <- Some (conf.columns, conf.zoom);
2839 if columns < 0
2840 then (
2841 if isbirdseye mode
2842 then showtext '!' "split mode doesn't work in bird's eye"
2843 else (
2844 conf.columns <- Csplit (-columns, [||]);
2845 state.x <- 0;
2846 conf.zoom <- 1.0;
2849 else (
2850 if columns < 2
2851 then (
2852 conf.columns <- Csingle [||];
2853 state.x <- 0;
2854 setzoom 1.0;
2856 else (
2857 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2858 conf.zoom <- 1.0;
2861 reshape state.winw state.winh;
2864 let enterbirdseye () =
2865 let zoom = float conf.thumbw /. float state.winw in
2866 let birdseyepageno =
2867 let cy = state.winh / 2 in
2868 let fold = function
2869 | [] -> 0
2870 | l :: rest ->
2871 let rec fold best = function
2872 | [] -> best.pageno
2873 | l :: rest ->
2874 let d = cy - (l.pagedispy + l.pagevh/2)
2875 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2876 if abs d < abs dbest
2877 then fold l rest
2878 else best.pageno
2879 in fold l rest
2881 fold state.layout
2883 state.mode <- Birdseye (
2884 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2886 conf.zoom <- zoom;
2887 conf.presentation <- false;
2888 conf.interpagespace <- 10;
2889 conf.hlinks <- false;
2890 conf.fitmodel <- FitProportional;
2891 state.x <- 0;
2892 state.mstate <- Mnone;
2893 conf.maxwait <- None;
2894 conf.columns <- (
2895 match conf.beyecolumns with
2896 | Some c ->
2897 conf.zoom <- 1.0;
2898 Cmulti ((c, 0, 0), [||])
2899 | None -> Csingle [||]
2901 Wsi.setcursor Wsi.CURSOR_INHERIT;
2902 if conf.verbose
2903 then
2904 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2905 (100.0*.zoom)
2906 else
2907 state.text <- ""
2909 reshape state.winw state.winh;
2912 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2913 state.mode <- View;
2914 conf.zoom <- c.zoom;
2915 conf.presentation <- c.presentation;
2916 conf.interpagespace <- c.interpagespace;
2917 conf.maxwait <- c.maxwait;
2918 conf.hlinks <- c.hlinks;
2919 conf.fitmodel <- c.fitmodel;
2920 conf.beyecolumns <- (
2921 match conf.columns with
2922 | Cmulti ((c, _, _), _) -> Some c
2923 | Csingle _ -> None
2924 | Csplit _ -> failwith "leaving bird's eye split mode"
2926 conf.columns <- (
2927 match c.columns with
2928 | Cmulti (c, _) -> Cmulti (c, [||])
2929 | Csingle _ -> Csingle [||]
2930 | Csplit (c, _) -> Csplit (c, [||])
2932 state.x <- leftx;
2933 if conf.verbose
2934 then
2935 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2936 (100.0*.conf.zoom)
2938 reshape state.winw state.winh;
2939 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
2942 let togglebirdseye () =
2943 match state.mode with
2944 | Birdseye vals -> leavebirdseye vals true
2945 | View -> enterbirdseye ()
2946 | _ -> ()
2949 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2950 let pageno = max 0 (pageno - incr) in
2951 let rec loop = function
2952 | [] -> gotopage1 pageno 0
2953 | l :: _ when l.pageno = pageno ->
2954 if l.pagedispy >= 0 && l.pagey = 0
2955 then G.postRedisplay "upbirdseye"
2956 else gotopage1 pageno 0
2957 | _ :: rest -> loop rest
2959 loop state.layout;
2960 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2963 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2964 let pageno = min (state.pagecount - 1) (pageno + incr) in
2965 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2966 let rec loop = function
2967 | [] ->
2968 let y, h = getpageyh pageno in
2969 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
2970 gotoy (clamp dy)
2971 | l :: _ when l.pageno = pageno ->
2972 if l.pagevh != l.pageh
2973 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2974 else G.postRedisplay "downbirdseye"
2975 | _ :: rest -> loop rest
2977 loop state.layout
2980 let optentry mode _ key =
2981 let btos b = if b then "on" else "off" in
2982 if key >= 32 && key < 127
2983 then
2984 let c = Char.chr key in
2985 match c with
2986 | 's' ->
2987 let ondone s =
2988 try conf.scrollstep <- int_of_string s with exc ->
2989 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
2991 TEswitch ("scroll step: ", "", None, intentry, ondone, true)
2993 | 'A' ->
2994 let ondone s =
2996 conf.autoscrollstep <- int_of_string s;
2997 if state.autoscroll <> None
2998 then state.autoscroll <- Some conf.autoscrollstep
2999 with exc ->
3000 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3002 TEswitch ("auto scroll step: ", "", None, intentry, ondone, true)
3004 | 'C' ->
3005 let ondone s =
3007 let n, a, b = multicolumns_of_string s in
3008 setcolumns mode n a b;
3009 with exc ->
3010 state.text <- Printf.sprintf "bad columns `%s': %s" s (exntos exc)
3012 TEswitch ("columns: ", "", None, textentry, ondone, true)
3014 | 'Z' ->
3015 let ondone s =
3017 let zoom = float (int_of_string s) /. 100.0 in
3018 setzoom zoom
3019 with exc ->
3020 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3022 TEswitch ("zoom: ", "", None, intentry, ondone, true)
3024 | 't' ->
3025 let ondone s =
3027 conf.thumbw <- bound (int_of_string s) 2 4096;
3028 state.text <-
3029 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
3030 begin match mode with
3031 | Birdseye beye ->
3032 leavebirdseye beye false;
3033 enterbirdseye ();
3034 | _ -> ();
3036 with exc ->
3037 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3039 TEswitch ("thumbnail width: ", "", None, intentry, ondone, true)
3041 | 'R' ->
3042 let ondone s =
3043 match try
3044 Some (int_of_string s)
3045 with exc ->
3046 state.text <- Printf.sprintf "bad integer `%s': %s"
3047 s (exntos exc);
3048 None
3049 with
3050 | Some angle -> reqlayout angle conf.fitmodel
3051 | None -> ()
3053 TEswitch ("rotation: ", "", None, intentry, ondone, true)
3055 | 'i' ->
3056 conf.icase <- not conf.icase;
3057 TEdone ("case insensitive search " ^ (btos conf.icase))
3059 | 'p' ->
3060 conf.preload <- not conf.preload;
3061 gotoy state.y;
3062 TEdone ("preload " ^ (btos conf.preload))
3064 | 'v' ->
3065 conf.verbose <- not conf.verbose;
3066 TEdone ("verbose " ^ (btos conf.verbose))
3068 | 'd' ->
3069 conf.debug <- not conf.debug;
3070 TEdone ("debug " ^ (btos conf.debug))
3072 | 'h' ->
3073 conf.maxhfit <- not conf.maxhfit;
3074 state.maxy <- calcheight ();
3075 TEdone ("maxhfit " ^ (btos conf.maxhfit))
3077 | 'c' ->
3078 conf.crophack <- not conf.crophack;
3079 TEdone ("crophack " ^ btos conf.crophack)
3081 | 'a' ->
3082 let s =
3083 match conf.maxwait with
3084 | None ->
3085 conf.maxwait <- Some infinity;
3086 "always wait for page to complete"
3087 | Some _ ->
3088 conf.maxwait <- None;
3089 "show placeholder if page is not ready"
3091 TEdone s
3093 | 'f' ->
3094 conf.underinfo <- not conf.underinfo;
3095 TEdone ("underinfo " ^ btos conf.underinfo)
3097 | 'P' ->
3098 conf.savebmarks <- not conf.savebmarks;
3099 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
3101 | 'S' ->
3102 let ondone s =
3104 let pageno, py =
3105 match state.layout with
3106 | [] -> 0, 0
3107 | l :: _ ->
3108 l.pageno, l.pagey
3110 conf.interpagespace <- int_of_string s;
3111 docolumns conf.columns;
3112 state.maxy <- calcheight ();
3113 let y = getpagey pageno in
3114 gotoy (y + py)
3115 with exc ->
3116 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3118 TEswitch ("vertical margin: ", "", None, intentry, ondone, true)
3120 | 'l' ->
3121 let fm =
3122 match conf.fitmodel with
3123 | FitProportional -> FitWidth
3124 | _ -> FitProportional
3126 reqlayout conf.angle fm;
3127 TEdone ("proportional display " ^ btos (fm == FitProportional))
3129 | 'T' ->
3130 settrim (not conf.trimmargins) conf.trimfuzz;
3131 TEdone ("trim margins " ^ btos conf.trimmargins)
3133 | 'I' ->
3134 conf.invert <- not conf.invert;
3135 TEdone ("invert colors " ^ btos conf.invert)
3137 | 'x' ->
3138 let ondone s =
3139 cbput state.hists.sel s;
3140 conf.selcmd <- s;
3142 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
3143 textentry, ondone, true)
3145 | _ ->
3146 state.text <- Printf.sprintf "bad option %d `%c'" key c;
3147 TEstop
3148 else
3149 TEcont state.text
3152 class type lvsource = object
3153 method getitemcount : int
3154 method getitem : int -> (string * int)
3155 method hasaction : int -> bool
3156 method exit :
3157 uioh:uioh ->
3158 cancel:bool ->
3159 active:int ->
3160 first:int ->
3161 pan:int ->
3162 qsearch:string ->
3163 uioh option
3164 method getactive : int
3165 method getfirst : int
3166 method getqsearch : string
3167 method setqsearch : string -> unit
3168 method getpan : int
3169 end;;
3171 class virtual lvsourcebase = object
3172 val mutable m_active = 0
3173 val mutable m_first = 0
3174 val mutable m_qsearch = ""
3175 val mutable m_pan = 0
3176 method getactive = m_active
3177 method getfirst = m_first
3178 method getqsearch = m_qsearch
3179 method getpan = m_pan
3180 method setqsearch s = m_qsearch <- s
3181 end;;
3183 let withoutlastutf8 s =
3184 let len = String.length s in
3185 if len = 0
3186 then s
3187 else
3188 let rec find pos =
3189 if pos = 0
3190 then pos
3191 else
3192 let b = Char.code s.[pos] in
3193 if b land 0b11000000 = 0b11000000
3194 then pos
3195 else find (pos-1)
3197 let first =
3198 if Char.code s.[len-1] land 0x80 = 0
3199 then len-1
3200 else find (len-1)
3202 String.sub s 0 first;
3205 let textentrykeyboard
3206 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
3207 let key =
3208 if key >= 0xffb0 && key <= 0xffb9
3209 then key - 0xffb0 + 48 else key
3211 let enttext te =
3212 state.mode <- Textentry (te, onleave);
3213 state.text <- "";
3214 enttext ();
3215 G.postRedisplay "textentrykeyboard enttext";
3217 let histaction cmd =
3218 match opthist with
3219 | None -> ()
3220 | Some (action, _) ->
3221 state.mode <- Textentry (
3222 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
3224 G.postRedisplay "textentry histaction"
3226 match key with
3227 | 0xff08 -> (* backspace *)
3228 let s = withoutlastutf8 text in
3229 let len = String.length s in
3230 if cancelonempty && len = 0
3231 then (
3232 onleave Cancel;
3233 G.postRedisplay "textentrykeyboard after cancel";
3235 else (
3236 enttext (c, s, opthist, onkey, ondone, cancelonempty)
3239 | 0xff0d | 0xff8d -> (* (kp) enter *)
3240 ondone text;
3241 onleave Confirm;
3242 G.postRedisplay "textentrykeyboard after confirm"
3244 | 0xff52 | 0xff97 -> histaction HCprev (* (kp) up *)
3245 | 0xff54 | 0xff99 -> histaction HCnext (* (kp) down *)
3246 | 0xff50 | 0xff95 -> histaction HCfirst (* (kp) home) *)
3247 | 0xff57 | 0xff9c -> histaction HClast (* (kp) end *)
3249 | 0xff1b -> (* escape*)
3250 if String.length text = 0
3251 then (
3252 begin match opthist with
3253 | None -> ()
3254 | Some (_, onhistcancel) -> onhistcancel ()
3255 end;
3256 onleave Cancel;
3257 state.text <- "";
3258 G.postRedisplay "textentrykeyboard after cancel2"
3260 else (
3261 enttext (c, "", opthist, onkey, ondone, cancelonempty)
3264 | 0xff9f | 0xffff -> () (* delete *)
3266 | _ when key != 0
3267 && key land 0xff00 != 0xff00 (* keyboard *)
3268 && key land 0xfe00 != 0xfe00 (* xkb *)
3269 && key land 0xfd00 != 0xfd00 (* 3270 *)
3271 begin match onkey text key with
3272 | TEdone text ->
3273 ondone text;
3274 onleave Confirm;
3275 G.postRedisplay "textentrykeyboard after confirm2";
3277 | TEcont text ->
3278 enttext (c, text, opthist, onkey, ondone, cancelonempty);
3280 | TEstop ->
3281 onleave Cancel;
3282 G.postRedisplay "textentrykeyboard after cancel3"
3284 | TEswitch te ->
3285 state.mode <- Textentry (te, onleave);
3286 G.postRedisplay "textentrykeyboard switch";
3287 end;
3289 | _ ->
3290 vlog "unhandled key %s" (Wsi.keyname key)
3293 let firstof first active =
3294 if first > active || abs (first - active) > fstate.maxrows - 1
3295 then max 0 (active - (fstate.maxrows/2))
3296 else first
3299 let calcfirst first active =
3300 if active > first
3301 then
3302 let rows = active - first in
3303 if rows > fstate.maxrows then active - fstate.maxrows else first
3304 else active
3307 let scrollph y maxy =
3308 let sh = float (maxy + state.winh) /. float state.winh in
3309 let sh = float state.winh /. sh in
3310 let sh = max sh (float conf.scrollh) in
3312 let percent = float y /. float maxy in
3313 let position = (float state.winh -. sh) *. percent in
3315 let position =
3316 if position +. sh > float state.winh
3317 then float state.winh -. sh
3318 else position
3320 position, sh;
3323 let coe s = (s :> uioh);;
3325 class listview ~(source:lvsource) ~trusted ~modehash =
3326 object (self)
3327 val m_pan = source#getpan
3328 val m_first = source#getfirst
3329 val m_active = source#getactive
3330 val m_qsearch = source#getqsearch
3331 val m_prev_uioh = state.uioh
3333 method private elemunder y =
3334 let n = y / (fstate.fontsize+1) in
3335 if m_first + n < source#getitemcount
3336 then (
3337 if source#hasaction (m_first + n)
3338 then Some (m_first + n)
3339 else None
3341 else None
3343 method display =
3344 Gl.enable `blend;
3345 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3346 GlDraw.color (0., 0., 0.) ~alpha:0.85;
3347 GlDraw.rect (0., 0.) (float state.winw, float state.winh);
3348 GlDraw.color (1., 1., 1.);
3349 Gl.enable `texture_2d;
3350 let fs = fstate.fontsize in
3351 let nfs = fs + 1 in
3352 let ww = fstate.wwidth in
3353 let tabw = 30.0*.ww in
3354 let itemcount = source#getitemcount in
3355 let rec loop row =
3356 if (row - m_first) > fstate.maxrows
3357 then ()
3358 else (
3359 if row >= 0 && row < itemcount
3360 then (
3361 let (s, level) = source#getitem row in
3362 let y = (row - m_first) * nfs in
3363 let x = 5.0 +. float (level + m_pan) *. ww in
3364 if row = m_active
3365 then (
3366 Gl.disable `texture_2d;
3367 GlDraw.polygon_mode `both `line;
3368 let alpha = if source#hasaction row then 0.9 else 0.3 in
3369 GlDraw.color (1., 1., 1.) ~alpha;
3370 GlDraw.rect (1., float (y + 1))
3371 (float (state.winw - conf.scrollbw - 1), float (y + fs + 3));
3372 GlDraw.polygon_mode `both `fill;
3373 GlDraw.color (1., 1., 1.);
3374 Gl.enable `texture_2d;
3377 let drawtabularstring s =
3378 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
3379 if trusted
3380 then
3381 let tabpos = try String.index s '\t' with Not_found -> -1 in
3382 if tabpos > 0
3383 then
3384 let len = String.length s - tabpos - 1 in
3385 let s1 = String.sub s 0 tabpos
3386 and s2 = String.sub s (tabpos + 1) len in
3387 let nx = drawstr x s1 in
3388 let sw = nx -. x in
3389 let x = x +. (max tabw sw) in
3390 drawstr x s2
3391 else
3392 drawstr x s
3393 else
3394 drawstr x s
3396 let _ = drawtabularstring s in
3397 loop (row+1)
3401 loop m_first;
3402 Gl.disable `blend;
3403 Gl.disable `texture_2d;
3405 method updownlevel incr =
3406 let len = source#getitemcount in
3407 let curlevel =
3408 if m_active >= 0 && m_active < len
3409 then snd (source#getitem m_active)
3410 else -1
3412 let rec flow i =
3413 if i = len then i-1 else if i = -1 then 0 else
3414 let _, l = source#getitem i in
3415 if l != curlevel then i else flow (i+incr)
3417 let active = flow m_active in
3418 let first = calcfirst m_first active in
3419 G.postRedisplay "outline updownlevel";
3420 {< m_active = active; m_first = first >}
3422 method private key1 key mask =
3423 let set1 active first qsearch =
3424 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3426 let search active pattern incr =
3427 let active = if active = -1 then m_first else active in
3428 let dosearch re =
3429 let rec loop n =
3430 if n >= 0 && n < source#getitemcount
3431 then (
3432 let s, _ = source#getitem n in
3434 (try ignore (Str.search_forward re s 0); true
3435 with Not_found -> false)
3436 then Some n
3437 else loop (n + incr)
3439 else None
3441 loop active
3444 let re = Str.regexp_case_fold pattern in
3445 dosearch re
3446 with Failure s ->
3447 state.text <- s;
3448 None
3450 let itemcount = source#getitemcount in
3451 let find start incr =
3452 let rec find i =
3453 if i = -1 || i = itemcount
3454 then -1
3455 else (
3456 if source#hasaction i
3457 then i
3458 else find (i + incr)
3461 find start
3463 let set active first =
3464 let first = bound first 0 (itemcount - fstate.maxrows) in
3465 state.text <- "";
3466 coe {< m_active = active; m_first = first; m_qsearch = "" >}
3468 let navigate incr =
3469 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3470 let active, first =
3471 let incr1 = if incr > 0 then 1 else -1 in
3472 if isvisible m_first m_active
3473 then
3474 let next =
3475 let next = m_active + incr in
3476 let next =
3477 if next < 0 || next >= itemcount
3478 then -1
3479 else find next incr1
3481 if abs (m_active - next) > fstate.maxrows
3482 then -1
3483 else next
3485 if next = -1
3486 then
3487 let first = m_first + incr in
3488 let first = bound first 0 (itemcount - 1) in
3489 let next =
3490 let next = m_active + incr in
3491 let next = bound next 0 (itemcount - 1) in
3492 find next ~-incr1
3494 let active =
3495 if next = -1
3496 then m_active
3497 else (
3498 if isvisible first next
3499 then next
3500 else m_active
3503 active, first
3504 else
3505 let first = min next m_first in
3506 let first =
3507 if abs (next - first) > fstate.maxrows
3508 then first + incr
3509 else first
3511 next, first
3512 else
3513 let first = m_first + incr in
3514 let first = bound first 0 (itemcount - 1) in
3515 let active =
3516 let next = m_active + incr in
3517 let next = bound next 0 (itemcount - 1) in
3518 let next = find next incr1 in
3519 let active =
3520 if next = -1 || abs (m_active - first) > fstate.maxrows
3521 then (
3522 let active = if m_active = -1 then next else m_active in
3523 active
3525 else next
3527 if isvisible first active
3528 then active
3529 else -1
3531 active, first
3533 G.postRedisplay "listview navigate";
3534 set active first;
3536 match key with
3537 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3538 let incr = if key = 0x72 then -1 else 1 in
3539 let active, first =
3540 match search (m_active + incr) m_qsearch incr with
3541 | None ->
3542 state.text <- m_qsearch ^ " [not found]";
3543 m_active, m_first
3544 | Some active ->
3545 state.text <- m_qsearch;
3546 active, firstof m_first active
3548 G.postRedisplay "listview ctrl-r/s";
3549 set1 active first m_qsearch;
3551 | 0xff63 when Wsi.withctrl mask -> (* ctrl-insert *)
3552 if m_active >= 0 && m_active < source#getitemcount
3553 then (
3554 let s, _ = source#getitem m_active in
3555 selstring s;
3557 coe self
3559 | 0xff08 -> (* backspace *)
3560 if String.length m_qsearch = 0
3561 then coe self
3562 else (
3563 let qsearch = withoutlastutf8 m_qsearch in
3564 let len = String.length qsearch in
3565 if len = 0
3566 then (
3567 state.text <- "";
3568 G.postRedisplay "listview empty qsearch";
3569 set1 m_active m_first "";
3571 else
3572 let active, first =
3573 match search m_active qsearch ~-1 with
3574 | None ->
3575 state.text <- qsearch ^ " [not found]";
3576 m_active, m_first
3577 | Some active ->
3578 state.text <- qsearch;
3579 active, firstof m_first active
3581 G.postRedisplay "listview backspace qsearch";
3582 set1 active first qsearch
3585 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3586 let pattern = m_qsearch ^ toutf8 key in
3587 let active, first =
3588 match search m_active pattern 1 with
3589 | None ->
3590 state.text <- pattern ^ " [not found]";
3591 m_active, m_first
3592 | Some active ->
3593 state.text <- pattern;
3594 active, firstof m_first active
3596 G.postRedisplay "listview qsearch add";
3597 set1 active first pattern;
3599 | 0xff1b -> (* escape *)
3600 state.text <- "";
3601 if String.length m_qsearch = 0
3602 then (
3603 G.postRedisplay "list view escape";
3604 begin
3605 match
3606 source#exit (coe self) true m_active m_first m_pan m_qsearch
3607 with
3608 | None -> m_prev_uioh
3609 | Some uioh -> uioh
3612 else (
3613 G.postRedisplay "list view kill qsearch";
3614 source#setqsearch "";
3615 coe {< m_qsearch = "" >}
3618 | 0xff0d | 0xff8d -> (* (kp) enter *)
3619 state.text <- "";
3620 let self = {< m_qsearch = "" >} in
3621 source#setqsearch "";
3622 let opt =
3623 G.postRedisplay "listview enter";
3624 if m_active >= 0 && m_active < source#getitemcount
3625 then (
3626 source#exit (coe self) false m_active m_first m_pan "";
3628 else (
3629 source#exit (coe self) true m_active m_first m_pan "";
3632 begin match opt with
3633 | None -> m_prev_uioh
3634 | Some uioh -> uioh
3637 | 0xff9f | 0xffff -> (* (kp) delete *)
3638 coe self
3640 | 0xff52 | 0xff97 -> navigate ~-1 (* (kp) up *)
3641 | 0xff54 | 0xff99 -> navigate 1 (* (kp) down *)
3642 | 0xff55 | 0xff9a -> navigate ~-(fstate.maxrows) (* (kp) prior *)
3643 | 0xff56 | 0xff9b -> navigate fstate.maxrows (* (kp) next *)
3645 | 0xff53 | 0xff98 -> (* (kp) right *)
3646 state.text <- "";
3647 G.postRedisplay "listview right";
3648 coe {< m_pan = m_pan - 1 >}
3650 | 0xff51 | 0xff96 -> (* (kp) left *)
3651 state.text <- "";
3652 G.postRedisplay "listview left";
3653 coe {< m_pan = m_pan + 1 >}
3655 | 0xff50 | 0xff95 -> (* (kp) home *)
3656 let active = find 0 1 in
3657 G.postRedisplay "listview home";
3658 set active 0;
3660 | 0xff57 | 0xff9c -> (* (kp) end *)
3661 let first = max 0 (itemcount - fstate.maxrows) in
3662 let active = find (itemcount - 1) ~-1 in
3663 G.postRedisplay "listview end";
3664 set active first;
3666 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3667 coe self
3669 | _ ->
3670 dolog "listview unknown key %#x" key; coe self
3672 method key key mask =
3673 match state.mode with
3674 | Textentry te -> textentrykeyboard key mask te; coe self
3675 | _ -> self#key1 key mask
3677 method button button down x y _ =
3678 let opt =
3679 match button with
3680 | 1 when x > state.winw - conf.scrollbw ->
3681 G.postRedisplay "listview scroll";
3682 if down
3683 then
3684 let _, position, sh = self#scrollph in
3685 if y > truncate position && y < truncate (position +. sh)
3686 then (
3687 state.mstate <- Mscrolly;
3688 Some (coe self)
3690 else
3691 let s = float (max 0 (y - conf.scrollh)) /. float state.winh in
3692 let first = truncate (s *. float source#getitemcount) in
3693 let first = min source#getitemcount first in
3694 Some (coe {< m_first = first; m_active = first >})
3695 else (
3696 state.mstate <- Mnone;
3697 Some (coe self);
3699 | 1 when not down ->
3700 begin match self#elemunder y with
3701 | Some n ->
3702 G.postRedisplay "listview click";
3703 source#exit
3704 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3705 | _ ->
3706 Some (coe self)
3708 | n when (n == 4 || n == 5) && not down ->
3709 let len = source#getitemcount in
3710 let first =
3711 if n = 5 && m_first + fstate.maxrows >= len
3712 then
3713 m_first
3714 else
3715 let first = m_first + (if n == 4 then -1 else 1) in
3716 bound first 0 (len - 1)
3718 G.postRedisplay "listview wheel";
3719 Some (coe {< m_first = first >})
3720 | n when (n = 6 || n = 7) && not down ->
3721 let inc = m_first + (if n = 7 then -1 else 1) in
3722 G.postRedisplay "listview hwheel";
3723 Some (coe {< m_pan = m_pan + inc >})
3724 | _ ->
3725 Some (coe self)
3727 match opt with
3728 | None -> m_prev_uioh
3729 | Some uioh -> uioh
3731 method motion _ y =
3732 match state.mstate with
3733 | Mscrolly ->
3734 let s = float (max 0 (y - conf.scrollh)) /. float state.winh in
3735 let first = truncate (s *. float source#getitemcount) in
3736 let first = min source#getitemcount first in
3737 G.postRedisplay "listview motion";
3738 coe {< m_first = first; m_active = first >}
3739 | _ -> coe self
3741 method pmotion x y =
3742 if x < state.winw - conf.scrollbw
3743 then
3744 let n =
3745 match self#elemunder y with
3746 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3747 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3749 let o =
3750 if n != m_active
3751 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3752 else self
3754 coe o
3755 else (
3756 Wsi.setcursor Wsi.CURSOR_INHERIT;
3757 coe self
3760 method infochanged _ = ()
3762 method scrollpw = (0, 0.0, 0.0)
3763 method scrollph =
3764 let nfs = fstate.fontsize + 1 in
3765 let y = m_first * nfs in
3766 let itemcount = source#getitemcount in
3767 let maxi = max 0 (itemcount - fstate.maxrows) in
3768 let maxy = maxi * nfs in
3769 let p, h = scrollph y maxy in
3770 conf.scrollbw, p, h
3772 method modehash = modehash
3773 method eformsgs = false
3774 end;;
3776 class outlinelistview ~source =
3777 object (self)
3778 inherit listview
3779 ~source:(source :> lvsource)
3780 ~trusted:false
3781 ~modehash:(findkeyhash conf "outline")
3782 as super
3784 method key key mask =
3785 let calcfirst first active =
3786 if active > first
3787 then
3788 let rows = active - first in
3789 let maxrows =
3790 if String.length state.text = 0
3791 then fstate.maxrows
3792 else fstate.maxrows - 2
3794 if rows > maxrows then active - maxrows else first
3795 else active
3797 let navigate incr =
3798 let active = m_active + incr in
3799 let active = bound active 0 (source#getitemcount - 1) in
3800 let first = calcfirst m_first active in
3801 G.postRedisplay "outline navigate";
3802 coe {< m_active = active; m_first = first >}
3804 let ctrl = Wsi.withctrl mask in
3805 match key with
3806 | 110 when ctrl -> (* ctrl-n *)
3807 source#narrow m_qsearch;
3808 G.postRedisplay "outline ctrl-n";
3809 coe {< m_first = 0; m_active = 0 >}
3811 | 117 when ctrl -> (* ctrl-u *)
3812 source#denarrow;
3813 G.postRedisplay "outline ctrl-u";
3814 state.text <- "";
3815 coe {< m_first = 0; m_active = 0 >}
3817 | 108 when ctrl -> (* ctrl-l *)
3818 let first = max 0 (m_active - (fstate.maxrows / 2)) in
3819 G.postRedisplay "outline ctrl-l";
3820 coe {< m_first = first >}
3822 | 0xff9f | 0xffff -> (* (kp) delete *)
3823 source#remove m_active;
3824 G.postRedisplay "outline delete";
3825 let active = max 0 (m_active-1) in
3826 coe {< m_first = firstof m_first active;
3827 m_active = active >}
3829 | 0xff52 | 0xff97 -> navigate ~-1 (* (kp) up *)
3830 | 0xff54 | 0xff99 -> navigate 1 (* (kp) down *)
3831 | 0xff55 | 0xff9a -> (* (kp) prior *)
3832 navigate ~-(fstate.maxrows)
3833 | 0xff56 | 0xff9b -> (* (kp) next *)
3834 navigate fstate.maxrows
3836 | 0xff53 | 0xff98 -> (* [ctrl-] (kp) right *)
3837 let o =
3838 if ctrl
3839 then (
3840 G.postRedisplay "outline ctrl right";
3841 {< m_pan = m_pan + 1 >}
3843 else self#updownlevel 1
3845 coe o
3847 | 0xff51 | 0xff96 -> (* [ctrl-] (kp) left *)
3848 let o =
3849 if ctrl
3850 then (
3851 G.postRedisplay "outline ctrl left";
3852 {< m_pan = m_pan - 1 >}
3854 else self#updownlevel ~-1
3856 coe o
3858 | 0xff50 | 0xff95 -> (* (kp) home *)
3859 G.postRedisplay "outline home";
3860 coe {< m_first = 0; m_active = 0 >}
3862 | 0xff57 | 0xff9c -> (* (kp) end *)
3863 let active = source#getitemcount - 1 in
3864 let first = max 0 (active - fstate.maxrows) in
3865 G.postRedisplay "outline end";
3866 coe {< m_active = active; m_first = first >}
3868 | _ -> super#key key mask
3871 let outlinesource usebookmarks =
3872 let empty = [||] in
3873 (object
3874 inherit lvsourcebase
3875 val mutable m_items = empty
3876 val mutable m_orig_items = empty
3877 val mutable m_prev_items = empty
3878 val mutable m_narrow_pattern = ""
3879 val mutable m_hadremovals = false
3881 method getitemcount =
3882 Array.length m_items + (if m_hadremovals then 1 else 0)
3884 method getitem n =
3885 if n == Array.length m_items && m_hadremovals
3886 then
3887 ("[Confirm removal]", 0)
3888 else
3889 let s, n, _ = m_items.(n) in
3890 (s, n)
3892 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3893 ignore (uioh, first, qsearch);
3894 let confrimremoval = m_hadremovals && active = Array.length m_items in
3895 let items =
3896 if String.length m_narrow_pattern = 0
3897 then m_orig_items
3898 else m_items
3900 if not cancel
3901 then (
3902 if not confrimremoval
3903 then(
3904 let _, _, anchor = m_items.(active) in
3905 gotoghyll (getanchory anchor);
3906 m_items <- items;
3908 else (
3909 state.bookmarks <- Array.to_list m_items;
3910 m_orig_items <- m_items;
3913 else m_items <- items;
3914 m_pan <- pan;
3915 None
3917 method hasaction _ = true
3919 method greetmsg =
3920 if Array.length m_items != Array.length m_orig_items
3921 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3922 else ""
3924 method narrow pattern =
3925 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3926 match reopt with
3927 | None -> ()
3928 | Some re ->
3929 let rec loop accu n =
3930 if n = -1
3931 then (
3932 m_narrow_pattern <- pattern;
3933 m_items <- Array.of_list accu
3935 else
3936 let (s, _, _) as o = m_items.(n) in
3937 let accu =
3938 if (try ignore (Str.search_forward re s 0); true
3939 with Not_found -> false)
3940 then o :: accu
3941 else accu
3943 loop accu (n-1)
3945 loop [] (Array.length m_items - 1)
3947 method denarrow =
3948 m_orig_items <- (
3949 if usebookmarks
3950 then Array.of_list state.bookmarks
3951 else state.outlines
3953 m_items <- m_orig_items
3955 method remove m =
3956 if usebookmarks
3957 then
3958 if m >= 0 && m < Array.length m_items
3959 then (
3960 m_hadremovals <- true;
3961 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3962 let n = if n >= m then n+1 else n in
3963 m_items.(n)
3967 method reset anchor items =
3968 m_hadremovals <- false;
3969 if m_orig_items == empty || m_prev_items != items
3970 then (
3971 m_orig_items <- items;
3972 if String.length m_narrow_pattern = 0
3973 then m_items <- items;
3975 m_prev_items <- items;
3976 let rely = getanchory anchor in
3977 let active =
3978 let rec loop n best bestd =
3979 if n = Array.length m_items
3980 then best
3981 else
3982 let (_, _, anchor) = m_items.(n) in
3983 let orely = getanchory anchor in
3984 let d = abs (orely - rely) in
3985 if d < bestd
3986 then loop (n+1) n d
3987 else loop (n+1) best bestd
3989 loop 0 ~-1 max_int
3991 m_active <- active;
3992 m_first <- firstof m_first active
3993 end)
3996 let enterselector usebookmarks =
3997 let source = outlinesource usebookmarks in
3998 fun errmsg ->
3999 let outlines =
4000 if usebookmarks
4001 then Array.of_list state.bookmarks
4002 else state.outlines
4004 if Array.length outlines = 0
4005 then (
4006 showtext ' ' errmsg;
4008 else (
4009 state.text <- source#greetmsg;
4010 Wsi.setcursor Wsi.CURSOR_INHERIT;
4011 let anchor = getanchor () in
4012 source#reset anchor outlines;
4013 state.uioh <- coe (new outlinelistview ~source);
4014 G.postRedisplay "enter selector";
4018 let enteroutlinemode =
4019 let f = enterselector false in
4020 fun ()-> f "Document has no outline";
4023 let enterbookmarkmode =
4024 let f = enterselector true in
4025 fun () -> f "Document has no bookmarks (yet)";
4028 let color_of_string s =
4029 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
4030 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
4034 let color_to_string (r, g, b) =
4035 let r = truncate (r *. 256.0)
4036 and g = truncate (g *. 256.0)
4037 and b = truncate (b *. 256.0) in
4038 Printf.sprintf "%d/%d/%d" r g b
4041 let irect_of_string s =
4042 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
4045 let irect_to_string (x0,y0,x1,y1) =
4046 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
4049 let makecheckers () =
4050 (* Based on lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
4051 following to say:
4052 converted by Issac Trotts. July 25, 2002 *)
4053 let image = GlPix.create `ubyte ~format:`luminance ~width:2 ~height:2 in
4054 Raw.sets_string (GlPix.to_raw image) ~pos:0 "\255\200\200\255";
4055 let id = GlTex.gen_texture () in
4056 GlTex.bind_texture `texture_2d id;
4057 GlPix.store (`unpack_alignment 1);
4058 GlTex.image2d image;
4059 List.iter (GlTex.parameter ~target:`texture_2d)
4060 [ `mag_filter `nearest; `min_filter `nearest ];
4064 let setcheckers enabled =
4065 match state.texid with
4066 | None ->
4067 if enabled then state.texid <- Some (makecheckers ())
4069 | Some texid ->
4070 if not enabled
4071 then (
4072 GlTex.delete_texture texid;
4073 state.texid <- None;
4077 let int_of_string_with_suffix s =
4078 let l = String.length s in
4079 let s1, shift =
4080 if l > 1
4081 then
4082 let suffix = Char.lowercase s.[l-1] in
4083 match suffix with
4084 | 'k' -> String.sub s 0 (l-1), 10
4085 | 'm' -> String.sub s 0 (l-1), 20
4086 | 'g' -> String.sub s 0 (l-1), 30
4087 | _ -> s, 0
4088 else s, 0
4090 let n = int_of_string s1 in
4091 let m = n lsl shift in
4092 if m < 0 || m < n
4093 then raise (Failure "value too large")
4094 else m
4097 let string_with_suffix_of_int n =
4098 if n = 0
4099 then "0"
4100 else
4101 let n, s =
4102 if n land ((1 lsl 30) - 1) = 0
4103 then n lsr 30, "G"
4104 else (
4105 if n land ((1 lsl 20) - 1) = 0
4106 then n lsr 20, "M"
4107 else (
4108 if n land ((1 lsl 10) - 1) = 0
4109 then n lsr 10, "K"
4110 else n, ""
4114 let rec loop s n =
4115 let h = n mod 1000 in
4116 let n = n / 1000 in
4117 if n = 0
4118 then string_of_int h ^ s
4119 else (
4120 let s = Printf.sprintf "_%03d%s" h s in
4121 loop s n
4124 loop "" n ^ s;
4127 let defghyllscroll = (40, 8, 32);;
4128 let ghyllscroll_of_string s =
4129 let (n, a, b) as nab =
4130 if s = "default"
4131 then defghyllscroll
4132 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
4134 if n <= a || n <= b || a >= b
4135 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
4136 nab;
4139 let ghyllscroll_to_string ((n, a, b) as nab) =
4140 if nab = defghyllscroll
4141 then "default"
4142 else Printf.sprintf "%d,%d,%d" n a b;
4145 let describe_location () =
4146 let fn = page_of_y state.y in
4147 let ln = page_of_y (state.y + state.winh - state.hscrollh - 1) in
4148 let maxy = state.maxy - (if conf.maxhfit then state.winh else 0) in
4149 let percent =
4150 if maxy <= 0
4151 then 100.
4152 else (100. *. (float state.y /. float maxy))
4154 if fn = ln
4155 then
4156 Printf.sprintf "page %d of %d [%.2f%%]"
4157 (fn+1) state.pagecount percent
4158 else
4159 Printf.sprintf
4160 "pages %d-%d of %d [%.2f%%]"
4161 (fn+1) (ln+1) state.pagecount percent
4164 let setpresentationmode v =
4165 let n = page_of_y state.y in
4166 state.anchor <- (n, 0.0, 1.0);
4167 conf.presentation <- v;
4168 if conf.presentation
4169 then (
4170 if not conf.scrollbarinpm
4171 then state.scrollw <- 0;
4173 else state.scrollw <- conf.scrollbw;
4174 represent ();
4177 let enterinfomode =
4178 let btos b = if b then "\xe2\x88\x9a" else "" in
4179 let showextended = ref false in
4180 let leave mode = function
4181 | Confirm -> state.mode <- mode
4182 | Cancel -> state.mode <- mode in
4183 let src =
4184 (object
4185 val mutable m_first_time = true
4186 val mutable m_l = []
4187 val mutable m_a = [||]
4188 val mutable m_prev_uioh = nouioh
4189 val mutable m_prev_mode = View
4191 inherit lvsourcebase
4193 method reset prev_mode prev_uioh =
4194 m_a <- Array.of_list (List.rev m_l);
4195 m_l <- [];
4196 m_prev_mode <- prev_mode;
4197 m_prev_uioh <- prev_uioh;
4198 if m_first_time
4199 then (
4200 let rec loop n =
4201 if n >= Array.length m_a
4202 then ()
4203 else
4204 match m_a.(n) with
4205 | _, _, _, Action _ -> m_active <- n
4206 | _ -> loop (n+1)
4208 loop 0;
4209 m_first_time <- false;
4212 method int name get set =
4213 m_l <-
4214 (name, `int get, 1, Action (
4215 fun u ->
4216 let ondone s =
4217 try set (int_of_string s)
4218 with exn ->
4219 state.text <- Printf.sprintf "bad integer `%s': %s"
4220 s (exntos exn)
4222 state.text <- "";
4223 let te = name ^ ": ", "", None, intentry, ondone, true in
4224 state.mode <- Textentry (te, leave m_prev_mode);
4226 )) :: m_l
4228 method int_with_suffix name get set =
4229 m_l <-
4230 (name, `intws get, 1, Action (
4231 fun u ->
4232 let ondone s =
4233 try set (int_of_string_with_suffix s)
4234 with exn ->
4235 state.text <- Printf.sprintf "bad integer `%s': %s"
4236 s (exntos exn)
4238 state.text <- "";
4239 let te =
4240 name ^ ": ", "", None, intentry_with_suffix, ondone, true
4242 state.mode <- Textentry (te, leave m_prev_mode);
4244 )) :: m_l
4246 method bool ?(offset=1) ?(btos=btos) name get set =
4247 m_l <-
4248 (name, `bool (btos, get), offset, Action (
4249 fun u ->
4250 let v = get () in
4251 set (not v);
4253 )) :: m_l
4255 method color name get set =
4256 m_l <-
4257 (name, `color get, 1, Action (
4258 fun u ->
4259 let invalid = (nan, nan, nan) in
4260 let ondone s =
4261 let c =
4262 try color_of_string s
4263 with exn ->
4264 state.text <- Printf.sprintf "bad color `%s': %s"
4265 s (exntos exn);
4266 invalid
4268 if c <> invalid
4269 then set c;
4271 let te = name ^ ": ", "", None, textentry, ondone, true in
4272 state.text <- color_to_string (get ());
4273 state.mode <- Textentry (te, leave m_prev_mode);
4275 )) :: m_l
4277 method string name get set =
4278 m_l <-
4279 (name, `string get, 1, Action (
4280 fun u ->
4281 let ondone s = set s in
4282 let te = name ^ ": ", "", None, textentry, ondone, true in
4283 state.mode <- Textentry (te, leave m_prev_mode);
4285 )) :: m_l
4287 method colorspace name get set =
4288 m_l <-
4289 (name, `string get, 1, Action (
4290 fun _ ->
4291 let source =
4292 let vals = [| "rgb"; "bgr"; "gray" |] in
4293 (object
4294 inherit lvsourcebase
4296 initializer
4297 m_active <- int_of_colorspace conf.colorspace;
4298 m_first <- 0;
4300 method getitemcount = Array.length vals
4301 method getitem n = (vals.(n), 0)
4302 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4303 ignore (uioh, first, pan, qsearch);
4304 if not cancel then set active;
4305 None
4306 method hasaction _ = true
4307 end)
4309 state.text <- "";
4310 let modehash = findkeyhash conf "info" in
4311 coe (new listview ~source ~trusted:true ~modehash)
4312 )) :: m_l
4314 method fitmodel name get set =
4315 m_l <-
4316 (name, `string get, 1, Action (
4317 fun _ ->
4318 let source =
4319 let vals = [| "fit width"; "proportional"; "fit page" |] in
4320 (object
4321 inherit lvsourcebase
4323 initializer
4324 m_active <- int_of_fitmodel conf.fitmodel;
4325 m_first <- 0;
4327 method getitemcount = Array.length vals
4328 method getitem n = (vals.(n), 0)
4329 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4330 ignore (uioh, first, pan, qsearch);
4331 if not cancel then set active;
4332 None
4333 method hasaction _ = true
4334 end)
4336 state.text <- "";
4337 let modehash = findkeyhash conf "info" in
4338 coe (new listview ~source ~trusted:true ~modehash)
4339 )) :: m_l
4341 method caption s offset =
4342 m_l <- (s, `empty, offset, Noaction) :: m_l
4344 method caption2 s f offset =
4345 m_l <- (s, `string f, offset, Noaction) :: m_l
4347 method getitemcount = Array.length m_a
4349 method getitem n =
4350 let tostr = function
4351 | `int f -> string_of_int (f ())
4352 | `intws f -> string_with_suffix_of_int (f ())
4353 | `string f -> f ()
4354 | `color f -> color_to_string (f ())
4355 | `bool (btos, f) -> btos (f ())
4356 | `empty -> ""
4358 let name, t, offset, _ = m_a.(n) in
4359 ((let s = tostr t in
4360 if String.length s > 0
4361 then Printf.sprintf "%s\t%s" name s
4362 else name),
4363 offset)
4365 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4366 let uiohopt =
4367 if not cancel
4368 then (
4369 m_qsearch <- qsearch;
4370 let uioh =
4371 match m_a.(active) with
4372 | _, _, _, Action f -> f uioh
4373 | _ -> uioh
4375 Some uioh
4377 else None
4379 m_active <- active;
4380 m_first <- first;
4381 m_pan <- pan;
4382 uiohopt
4384 method hasaction n =
4385 match m_a.(n) with
4386 | _, _, _, Action _ -> true
4387 | _ -> false
4388 end)
4390 let rec fillsrc prevmode prevuioh =
4391 let sep () = src#caption "" 0 in
4392 let colorp name get set =
4393 src#string name
4394 (fun () -> color_to_string (get ()))
4395 (fun v ->
4397 let c = color_of_string v in
4398 set c
4399 with exn ->
4400 state.text <- Printf.sprintf "bad color `%s': %s" v (exntos exn)
4403 let oldmode = state.mode in
4404 let birdseye = isbirdseye state.mode in
4406 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
4408 src#bool "presentation mode"
4409 (fun () -> conf.presentation)
4410 (fun v -> setpresentationmode v);
4412 src#bool "ignore case in searches"
4413 (fun () -> conf.icase)
4414 (fun v -> conf.icase <- v);
4416 src#bool "preload"
4417 (fun () -> conf.preload)
4418 (fun v -> conf.preload <- v);
4420 src#bool "highlight links"
4421 (fun () -> conf.hlinks)
4422 (fun v -> conf.hlinks <- v);
4424 src#bool "under info"
4425 (fun () -> conf.underinfo)
4426 (fun v -> conf.underinfo <- v);
4428 src#bool "persistent bookmarks"
4429 (fun () -> conf.savebmarks)
4430 (fun v -> conf.savebmarks <- v);
4432 src#fitmodel "fit model"
4433 (fun () -> fitmodel_to_string conf.fitmodel)
4434 (fun v -> reqlayout conf.angle (fitmodel_of_int v));
4436 src#bool "trim margins"
4437 (fun () -> conf.trimmargins)
4438 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
4440 src#bool "persistent location"
4441 (fun () -> conf.jumpback)
4442 (fun v -> conf.jumpback <- v);
4444 sep ();
4445 src#int "inter-page space"
4446 (fun () -> conf.interpagespace)
4447 (fun n ->
4448 conf.interpagespace <- n;
4449 docolumns conf.columns;
4450 let pageno, py =
4451 match state.layout with
4452 | [] -> 0, 0
4453 | l :: _ ->
4454 l.pageno, l.pagey
4456 state.maxy <- calcheight ();
4457 let y = getpagey pageno in
4458 gotoy (y + py)
4461 src#int "page bias"
4462 (fun () -> conf.pagebias)
4463 (fun v -> conf.pagebias <- v);
4465 src#int "scroll step"
4466 (fun () -> conf.scrollstep)
4467 (fun n -> conf.scrollstep <- n);
4469 src#int "horizontal scroll step"
4470 (fun () -> conf.hscrollstep)
4471 (fun v -> conf.hscrollstep <- v);
4473 src#int "auto scroll step"
4474 (fun () ->
4475 match state.autoscroll with
4476 | Some step -> step
4477 | _ -> conf.autoscrollstep)
4478 (fun n ->
4479 if state.autoscroll <> None
4480 then state.autoscroll <- Some n;
4481 conf.autoscrollstep <- n);
4483 src#int "zoom"
4484 (fun () -> truncate (conf.zoom *. 100.))
4485 (fun v -> setzoom ((float v) /. 100.));
4487 src#int "rotation"
4488 (fun () -> conf.angle)
4489 (fun v -> reqlayout v conf.fitmodel);
4491 src#int "scroll bar width"
4492 (fun () -> state.scrollw)
4493 (fun v ->
4494 state.scrollw <- v;
4495 conf.scrollbw <- v;
4496 reshape state.winw state.winh;
4499 src#int "scroll handle height"
4500 (fun () -> conf.scrollh)
4501 (fun v -> conf.scrollh <- v;);
4503 src#int "thumbnail width"
4504 (fun () -> conf.thumbw)
4505 (fun v ->
4506 conf.thumbw <- min 4096 v;
4507 match oldmode with
4508 | Birdseye beye ->
4509 leavebirdseye beye false;
4510 enterbirdseye ()
4511 | _ -> ()
4514 let mode = state.mode in
4515 src#string "columns"
4516 (fun () ->
4517 match conf.columns with
4518 | Csingle _ -> "1"
4519 | Cmulti (multi, _) -> multicolumns_to_string multi
4520 | Csplit (count, _) -> "-" ^ string_of_int count
4522 (fun v ->
4523 let n, a, b = multicolumns_of_string v in
4524 setcolumns mode n a b);
4526 sep ();
4527 src#caption "Presentation mode" 0;
4528 src#bool "scrollbar visible"
4529 (fun () -> conf.scrollbarinpm)
4530 (fun v ->
4531 if v != conf.scrollbarinpm
4532 then (
4533 conf.scrollbarinpm <- v;
4534 if conf.presentation
4535 then (
4536 state.scrollw <- if v then conf.scrollbw else 0;
4537 reshape state.winw state.winh;
4542 sep ();
4543 src#caption "Pixmap cache" 0;
4544 src#int_with_suffix "size (advisory)"
4545 (fun () -> conf.memlimit)
4546 (fun v -> conf.memlimit <- v);
4548 src#caption2 "used"
4549 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4550 (string_with_suffix_of_int state.memused)
4551 (Hashtbl.length state.tilemap)) 1;
4553 sep ();
4554 src#caption "Layout" 0;
4555 src#caption2 "Dimension"
4556 (fun () ->
4557 Printf.sprintf "%dx%d (virtual %dx%d)"
4558 state.winw state.winh
4559 state.w state.maxy)
4561 if conf.debug
4562 then
4563 src#caption2 "Position" (fun () ->
4564 Printf.sprintf "%dx%d" state.x state.y
4566 else
4567 src#caption2 "Position" (fun () -> describe_location ()) 1
4570 sep ();
4571 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4572 "Save these parameters as global defaults at exit"
4573 (fun () -> conf.bedefault)
4574 (fun v -> conf.bedefault <- v)
4577 sep ();
4578 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4579 src#bool ~offset:0 ~btos "Extended parameters"
4580 (fun () -> !showextended)
4581 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4582 if !showextended
4583 then (
4584 src#bool "checkers"
4585 (fun () -> conf.checkers)
4586 (fun v -> conf.checkers <- v; setcheckers v);
4587 src#bool "update cursor"
4588 (fun () -> conf.updatecurs)
4589 (fun v -> conf.updatecurs <- v);
4590 src#bool "verbose"
4591 (fun () -> conf.verbose)
4592 (fun v -> conf.verbose <- v);
4593 src#bool "invert colors"
4594 (fun () -> conf.invert)
4595 (fun v -> conf.invert <- v);
4596 src#bool "max fit"
4597 (fun () -> conf.maxhfit)
4598 (fun v -> conf.maxhfit <- v);
4599 src#bool "redirect stderr"
4600 (fun () -> conf.redirectstderr)
4601 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4602 src#string "uri launcher"
4603 (fun () -> conf.urilauncher)
4604 (fun v -> conf.urilauncher <- v);
4605 src#string "path launcher"
4606 (fun () -> conf.pathlauncher)
4607 (fun v -> conf.pathlauncher <- v);
4608 src#string "tile size"
4609 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4610 (fun v ->
4612 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4613 conf.tilew <- max 64 w;
4614 conf.tileh <- max 64 h;
4615 flushtiles ();
4616 with exn ->
4617 state.text <- Printf.sprintf "bad tile size `%s': %s"
4618 v (exntos exn)
4620 src#int "texture count"
4621 (fun () -> conf.texcount)
4622 (fun v ->
4623 if realloctexts v
4624 then conf.texcount <- v
4625 else showtext '!' " Failed to set texture count please retry later"
4627 src#int "slice height"
4628 (fun () -> conf.sliceheight)
4629 (fun v ->
4630 conf.sliceheight <- v;
4631 wcmd "sliceh %d" conf.sliceheight;
4633 src#int "anti-aliasing level"
4634 (fun () -> conf.aalevel)
4635 (fun v ->
4636 conf.aalevel <- bound v 0 8;
4637 state.anchor <- getanchor ();
4638 opendoc state.path state.password;
4640 src#string "page scroll scaling factor"
4641 (fun () -> string_of_float conf.pgscale)
4642 (fun v ->
4644 let s = float_of_string v in
4645 conf.pgscale <- s
4646 with exn ->
4647 state.text <- Printf.sprintf
4648 "bad page scroll scaling factor `%s': %s" v (exntos exn)
4651 src#int "ui font size"
4652 (fun () -> fstate.fontsize)
4653 (fun v -> setfontsize (bound v 5 100));
4654 src#int "hint font size"
4655 (fun () -> conf.hfsize)
4656 (fun v -> conf.hfsize <- bound v 5 100);
4657 colorp "background color"
4658 (fun () -> conf.bgcolor)
4659 (fun v -> conf.bgcolor <- v);
4660 src#bool "crop hack"
4661 (fun () -> conf.crophack)
4662 (fun v -> conf.crophack <- v);
4663 src#string "trim fuzz"
4664 (fun () -> irect_to_string conf.trimfuzz)
4665 (fun v ->
4667 conf.trimfuzz <- irect_of_string v;
4668 if conf.trimmargins
4669 then settrim true conf.trimfuzz;
4670 with exn ->
4671 state.text <- Printf.sprintf "bad irect `%s': %s" v (exntos exn)
4673 src#string "throttle"
4674 (fun () ->
4675 match conf.maxwait with
4676 | None -> "show place holder if page is not ready"
4677 | Some time ->
4678 if time = infinity
4679 then "wait for page to fully render"
4680 else
4681 "wait " ^ string_of_float time
4682 ^ " seconds before showing placeholder"
4684 (fun v ->
4686 let f = float_of_string v in
4687 if f <= 0.0
4688 then conf.maxwait <- None
4689 else conf.maxwait <- Some f
4690 with exn ->
4691 state.text <- Printf.sprintf "bad time `%s': %s" v (exntos exn)
4693 src#string "ghyll scroll"
4694 (fun () ->
4695 match conf.ghyllscroll with
4696 | None -> ""
4697 | Some nab -> ghyllscroll_to_string nab
4699 (fun v ->
4701 let gs =
4702 if String.length v = 0
4703 then None
4704 else Some (ghyllscroll_of_string v)
4706 conf.ghyllscroll <- gs
4707 with exn ->
4708 state.text <- Printf.sprintf "bad ghyll `%s': %s" v (exntos exn)
4710 src#string "selection command"
4711 (fun () -> conf.selcmd)
4712 (fun v -> conf.selcmd <- v);
4713 src#string "synctex command"
4714 (fun () -> conf.stcmd)
4715 (fun v -> conf.stcmd <- v);
4716 src#colorspace "color space"
4717 (fun () -> colorspace_to_string conf.colorspace)
4718 (fun v ->
4719 conf.colorspace <- colorspace_of_int v;
4720 wcmd "cs %d" v;
4721 load state.layout;
4723 if pbousable ()
4724 then
4725 src#bool "use PBO"
4726 (fun () -> conf.usepbo)
4727 (fun v -> conf.usepbo <- v);
4728 src#bool "mouse wheel scrolls pages"
4729 (fun () -> conf.wheelbypage)
4730 (fun v -> conf.wheelbypage <- v);
4733 sep ();
4734 src#caption "Document" 0;
4735 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4736 src#caption2 "Pages"
4737 (fun () -> string_of_int state.pagecount) 1;
4738 src#caption2 "Dimensions"
4739 (fun () -> string_of_int (List.length state.pdims)) 1;
4740 if conf.trimmargins
4741 then (
4742 sep ();
4743 src#caption "Trimmed margins" 0;
4744 src#caption2 "Dimensions"
4745 (fun () -> string_of_int (List.length state.pdims)) 1;
4748 sep ();
4749 src#caption "OpenGL" 0;
4750 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
4751 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
4753 sep ();
4754 src#caption "Location" 0;
4755 if String.length state.origin > 0
4756 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
4757 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
4759 src#reset prevmode prevuioh;
4761 fun () ->
4762 state.text <- "";
4763 let prevmode = state.mode
4764 and prevuioh = state.uioh in
4765 fillsrc prevmode prevuioh;
4766 let source = (src :> lvsource) in
4767 let modehash = findkeyhash conf "info" in
4768 state.uioh <- coe (object (self)
4769 inherit listview ~source ~trusted:true ~modehash as super
4770 val mutable m_prevmemused = 0
4771 method infochanged = function
4772 | Memused ->
4773 if m_prevmemused != state.memused
4774 then (
4775 m_prevmemused <- state.memused;
4776 G.postRedisplay "memusedchanged";
4778 | Pdim -> G.postRedisplay "pdimchanged"
4779 | Docinfo -> fillsrc prevmode prevuioh
4781 method key key mask =
4782 if not (Wsi.withctrl mask)
4783 then
4784 match key with
4785 | 0xff51 | 0xff96 -> coe (self#updownlevel ~-1) (* (kp) left *)
4786 | 0xff53 | 0xff98 -> coe (self#updownlevel 1) (* (kp) right *)
4787 | _ -> super#key key mask
4788 else super#key key mask
4789 end);
4790 G.postRedisplay "info";
4793 let enterhelpmode =
4794 let source =
4795 (object
4796 inherit lvsourcebase
4797 method getitemcount = Array.length state.help
4798 method getitem n =
4799 let s, l, _ = state.help.(n) in
4800 (s, l)
4802 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4803 let optuioh =
4804 if not cancel
4805 then (
4806 m_qsearch <- qsearch;
4807 match state.help.(active) with
4808 | _, _, Action f -> Some (f uioh)
4809 | _ -> Some (uioh)
4811 else None
4813 m_active <- active;
4814 m_first <- first;
4815 m_pan <- pan;
4816 optuioh
4818 method hasaction n =
4819 match state.help.(n) with
4820 | _, _, Action _ -> true
4821 | _ -> false
4823 initializer
4824 m_active <- -1
4825 end)
4826 in fun () ->
4827 let modehash = findkeyhash conf "help" in
4828 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4829 G.postRedisplay "help";
4832 let entermsgsmode =
4833 let msgsource =
4834 let re = Str.regexp "[\r\n]" in
4835 (object
4836 inherit lvsourcebase
4837 val mutable m_items = [||]
4839 method getitemcount = 1 + Array.length m_items
4841 method getitem n =
4842 if n = 0
4843 then "[Clear]", 0
4844 else m_items.(n-1), 0
4846 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4847 ignore uioh;
4848 if not cancel
4849 then (
4850 if active = 0
4851 then Buffer.clear state.errmsgs;
4852 m_qsearch <- qsearch;
4854 m_active <- active;
4855 m_first <- first;
4856 m_pan <- pan;
4857 None
4859 method hasaction n =
4860 n = 0
4862 method reset =
4863 state.newerrmsgs <- false;
4864 let l = Str.split re (Buffer.contents state.errmsgs) in
4865 m_items <- Array.of_list l
4867 initializer
4868 m_active <- 0
4869 end)
4870 in fun () ->
4871 state.text <- "";
4872 msgsource#reset;
4873 let source = (msgsource :> lvsource) in
4874 let modehash = findkeyhash conf "listview" in
4875 state.uioh <- coe (object
4876 inherit listview ~source ~trusted:false ~modehash as super
4877 method display =
4878 if state.newerrmsgs
4879 then msgsource#reset;
4880 super#display
4881 end);
4882 G.postRedisplay "msgs";
4885 let quickbookmark ?title () =
4886 match state.layout with
4887 | [] -> ()
4888 | l :: _ ->
4889 let title =
4890 match title with
4891 | None ->
4892 let sec = Unix.gettimeofday () in
4893 let tm = Unix.localtime sec in
4894 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4895 (l.pageno+1)
4896 tm.Unix.tm_mday
4897 tm.Unix.tm_mon
4898 (tm.Unix.tm_year + 1900)
4899 tm.Unix.tm_hour
4900 tm.Unix.tm_min
4901 | Some title -> title
4903 state.bookmarks <- (title, 0, getanchor1 l) :: state.bookmarks
4906 let setautoscrollspeed step goingdown =
4907 let incr = max 1 ((abs step) / 2) in
4908 let incr = if goingdown then incr else -incr in
4909 let astep = step + incr in
4910 state.autoscroll <- Some astep;
4913 let gotounder = function
4914 | Ulinkgoto (pageno, top) ->
4915 if pageno >= 0
4916 then (
4917 addnav ();
4918 gotopage1 pageno top;
4921 | Ulinkuri s ->
4922 gotouri s
4924 | Uremote (filename, pageno) ->
4925 let path =
4926 if Sys.file_exists filename
4927 then filename
4928 else
4929 let dir = Filename.dirname state.path in
4930 let path = Filename.concat dir filename in
4931 if Sys.file_exists path
4932 then path
4933 else ""
4935 if String.length path > 0
4936 then (
4937 let anchor = getanchor () in
4938 let ranchor = state.path, state.password, anchor, state.origin in
4939 state.origin <- "";
4940 state.anchor <- (pageno, 0.0, 0.0);
4941 state.ranchors <- ranchor :: state.ranchors;
4942 opendoc path "";
4944 else showtext '!' ("Could not find " ^ filename)
4946 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4949 let canpan () =
4950 match conf.columns with
4951 | Csplit _ -> true
4952 | _ -> state.x != 0 || conf.zoom > 1.0
4955 let panbound x = bound x (-state.w) (state.winw - state.scrollw);;
4957 let existsinrow pageno (columns, coverA, coverB) p =
4958 let last = ((pageno - coverA) mod columns) + columns in
4959 let rec any = function
4960 | [] -> false
4961 | l :: rest ->
4962 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
4963 then p l
4964 else (
4965 if not (p l)
4966 then (if l.pageno = last then false else any rest)
4967 else true
4970 any state.layout
4973 let nextpage () =
4974 match state.layout with
4975 | [] ->
4976 let pageno = page_of_y state.y in
4977 gotoghyll (getpagey (pageno+1))
4978 | l :: rest ->
4979 match conf.columns with
4980 | Csingle _ ->
4981 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4982 then
4983 let y = clamp (pgscale state.winh) in
4984 gotoghyll y
4985 else
4986 let pageno = min (l.pageno+1) (state.pagecount-1) in
4987 gotoghyll (getpagey pageno)
4988 | Cmulti ((c, _, _) as cl, _) ->
4989 if conf.presentation
4990 && (existsinrow l.pageno cl
4991 (fun l -> l.pageh > l.pagey + l.pagevh))
4992 then
4993 let y = clamp (pgscale state.winh) in
4994 gotoghyll y
4995 else
4996 let pageno = min (l.pageno+c) (state.pagecount-1) in
4997 gotoghyll (getpagey pageno)
4998 | Csplit (n, _) ->
4999 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
5000 then
5001 let pagey, pageh = getpageyh l.pageno in
5002 let pagey = pagey + pageh * l.pagecol in
5003 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
5004 gotoghyll (pagey + pageh + ips)
5007 let prevpage () =
5008 match state.layout with
5009 | [] ->
5010 let pageno = page_of_y state.y in
5011 gotoghyll (getpagey (pageno-1))
5012 | l :: _ ->
5013 match conf.columns with
5014 | Csingle _ ->
5015 if conf.presentation && l.pagey != 0
5016 then
5017 gotoghyll (clamp (pgscale ~-(state.winh)))
5018 else
5019 let pageno = max 0 (l.pageno-1) in
5020 gotoghyll (getpagey pageno)
5021 | Cmulti ((c, _, coverB) as cl, _) ->
5022 if conf.presentation &&
5023 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
5024 then
5025 gotoghyll (clamp (pgscale ~-(state.winh)))
5026 else
5027 let decr =
5028 if l.pageno = state.pagecount - coverB
5029 then 1
5030 else c
5032 let pageno = max 0 (l.pageno-decr) in
5033 gotoghyll (getpagey pageno)
5034 | Csplit (n, _) ->
5035 let y =
5036 if l.pagecol = 0
5037 then
5038 if l.pageno = 0
5039 then l.pagey
5040 else
5041 let pageno = max 0 (l.pageno-1) in
5042 let pagey, pageh = getpageyh pageno in
5043 pagey + (n-1)*pageh
5044 else
5045 let pagey, pageh = getpageyh l.pageno in
5046 pagey + pageh * (l.pagecol-1) - conf.interpagespace
5048 gotoghyll y
5051 let viewkeyboard key mask =
5052 let enttext te =
5053 let mode = state.mode in
5054 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
5055 state.text <- "";
5056 enttext ();
5057 G.postRedisplay "view:enttext"
5059 let ctrl = Wsi.withctrl mask in
5060 let key =
5061 if key >= 0xffb0 && key < 0xffb9 then key - 0xffb0 + 48 else key
5063 match key with
5064 | 81 -> (* Q *)
5065 exit 0
5067 | 0xff63 -> (* insert *)
5068 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
5069 then (
5070 state.mode <- LinkNav (Ltgendir 0);
5071 gotoy state.y;
5073 else showtext '!' "Keyboard link navigation does not work under rotation"
5075 | 0xff1b | 113 -> (* escape / q *)
5076 begin match state.mstate with
5077 | Mzoomrect _ ->
5078 state.mstate <- Mnone;
5079 Wsi.setcursor Wsi.CURSOR_INHERIT;
5080 G.postRedisplay "kill zoom rect";
5081 | _ ->
5082 begin match state.mode with
5083 | LinkNav _ ->
5084 state.mode <- View;
5085 G.postRedisplay "esc leave linknav"
5086 | _ ->
5087 match state.ranchors with
5088 | [] -> raise Quit
5089 | (path, password, anchor, origin) :: rest ->
5090 state.ranchors <- rest;
5091 state.anchor <- anchor;
5092 state.origin <- origin;
5093 opendoc path password
5094 end;
5095 end;
5097 | 0xff08 -> (* backspace *)
5098 gotoghyll (getnav ~-1)
5100 | 111 -> (* o *)
5101 enteroutlinemode ()
5103 | 117 -> (* u *)
5104 state.rects <- [];
5105 state.text <- "";
5106 G.postRedisplay "dehighlight";
5108 | 47 | 63 -> (* / ? *)
5109 let ondone isforw s =
5110 cbput state.hists.pat s;
5111 state.searchpattern <- s;
5112 search s isforw
5114 let s = String.create 1 in
5115 s.[0] <- Char.chr key;
5116 enttext (s, "", Some (onhist state.hists.pat),
5117 textentry, ondone (key = 47), true)
5119 | 43 | 0xffab | 61 when ctrl -> (* ctrl-+ or ctrl-= *)
5120 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
5121 setzoom (conf.zoom +. incr)
5123 | 43 | 0xffab -> (* + *)
5124 let ondone s =
5125 let n =
5126 try int_of_string s with exc ->
5127 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc);
5128 max_int
5130 if n != max_int
5131 then (
5132 conf.pagebias <- n;
5133 state.text <- "page bias is now " ^ string_of_int n;
5136 enttext ("page bias: ", "", None, intentry, ondone, true)
5138 | 45 | 0xffad when ctrl -> (* ctrl-- *)
5139 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
5140 setzoom (max 0.01 (conf.zoom -. decr))
5142 | 45 | 0xffad -> (* - *)
5143 let ondone msg = state.text <- msg in
5144 enttext (
5145 "option [acfhilpstvxACFPRSZTIS]: ", "", None,
5146 optentry state.mode, ondone, true
5149 | 48 when ctrl -> (* ctrl-0 *)
5150 if conf.zoom = 1.0
5151 then (
5152 state.x <- 0;
5153 state.hscrollh <-
5154 if state.w <= state.winw - state.scrollw
5155 then 0
5156 else state.scrollw
5158 gotoy state.y
5160 else setzoom 1.0
5162 | (49 | 50) when ctrl && conf.fitmodel != FitPage -> (* ctrl-1/2 *)
5163 let cols =
5164 match conf.columns with
5165 | Csingle _ | Cmulti _ -> 1
5166 | Csplit (n, _) -> n
5168 let h = state.winh -
5169 conf.interpagespace lsl (if conf.presentation then 1 else 0)
5171 let zoom = zoomforh state.winw h state.scrollw cols in
5172 if zoom > 0.0 && (key = 50 || zoom < 1.0)
5173 then setzoom zoom
5175 | 51 when ctrl -> (* ctrl-3 *)
5176 let fm =
5177 match conf.fitmodel with
5178 | FitWidth -> FitProportional
5179 | FitProportional -> FitPage
5180 | FitPage -> FitWidth
5182 state.text <- "fit model: " ^ fitmodel_to_string fm;
5183 reqlayout conf.angle fm
5185 | 0xffc6 -> (* f9 *)
5186 togglebirdseye ()
5188 | 57 when ctrl -> (* ctrl-9 *)
5189 togglebirdseye ()
5191 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
5192 when not ctrl -> (* 0..9 *)
5193 let ondone s =
5194 let n =
5195 try int_of_string s with exc ->
5196 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc);
5199 if n >= 0
5200 then (
5201 addnav ();
5202 cbput state.hists.pag (string_of_int n);
5203 gotopage1 (n + conf.pagebias - 1) 0;
5206 let pageentry text key =
5207 match Char.unsafe_chr key with
5208 | 'g' -> TEdone text
5209 | _ -> intentry text key
5211 let text = "x" in text.[0] <- Char.chr key;
5212 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone, true)
5214 | 98 -> (* b *)
5215 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
5216 reshape state.winw state.winh;
5218 | 108 -> (* l *)
5219 conf.hlinks <- not conf.hlinks;
5220 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
5221 G.postRedisplay "toggle highlightlinks";
5223 | 70 -> (* F *)
5224 state.glinks <- true;
5225 let mode = state.mode in
5226 state.mode <- Textentry (
5227 (":", "", None, linknentry, linkndone gotounder, false),
5228 (fun _ ->
5229 state.glinks <- false;
5230 state.mode <- mode)
5232 state.text <- "";
5233 G.postRedisplay "view:linkent(F)"
5235 | 121 -> (* y *)
5236 state.glinks <- true;
5237 let mode = state.mode in
5238 state.mode <- Textentry (
5240 ":", "", None, linknentry, linkndone (fun under ->
5241 selstring (undertext under);
5242 ), false
5244 fun _ ->
5245 state.glinks <- false;
5246 state.mode <- mode
5248 state.text <- "";
5249 G.postRedisplay "view:linkent"
5251 | 97 -> (* a *)
5252 begin match state.autoscroll with
5253 | Some step ->
5254 conf.autoscrollstep <- step;
5255 state.autoscroll <- None
5256 | None ->
5257 if conf.autoscrollstep = 0
5258 then state.autoscroll <- Some 1
5259 else state.autoscroll <- Some conf.autoscrollstep
5262 | 112 when ctrl -> (* ctrl-p *)
5263 launchpath ()
5265 | 80 -> (* P *)
5266 setpresentationmode (not conf.presentation);
5267 showtext ' ' ("presentation mode " ^
5268 if conf.presentation then "on" else "off");
5270 | 102 -> (* f *)
5271 if List.mem Wsi.Fullscreen state.winstate
5272 then Wsi.reshape conf.cwinw conf.cwinh
5273 else Wsi.fullscreen ()
5275 | 112 | 78 -> (* p|N *)
5276 search state.searchpattern false
5278 | 110 | 0xffc0 -> (* n|F3 *)
5279 search state.searchpattern true
5281 | 116 -> (* t *)
5282 begin match state.layout with
5283 | [] -> ()
5284 | l :: _ ->
5285 gotoghyll (getpagey l.pageno)
5288 | 32 -> (* space *)
5289 nextpage ()
5291 | 0xff9f | 0xffff -> (* delete *)
5292 prevpage ()
5294 | 61 -> (* = *)
5295 showtext ' ' (describe_location ());
5297 | 119 -> (* w *)
5298 begin match state.layout with
5299 | [] -> ()
5300 | l :: _ ->
5301 Wsi.reshape (l.pagew + state.scrollw) l.pageh;
5302 G.postRedisplay "w"
5305 | 39 -> (* ' *)
5306 enterbookmarkmode ()
5308 | 104 | 0xffbe -> (* h|F1 *)
5309 enterhelpmode ()
5311 | 105 -> (* i *)
5312 enterinfomode ()
5314 | 101 when Buffer.length state.errmsgs > 0 -> (* e *)
5315 entermsgsmode ()
5317 | 109 -> (* m *)
5318 let ondone s =
5319 match state.layout with
5320 | l :: _ ->
5321 if String.length s > 0
5322 then
5323 state.bookmarks <- (s, 0, getanchor1 l) :: state.bookmarks
5324 | _ -> ()
5326 enttext ("bookmark: ", "", None, textentry, ondone, true)
5328 | 126 -> (* ~ *)
5329 quickbookmark ();
5330 showtext ' ' "Quick bookmark added";
5332 | 122 -> (* z *)
5333 begin match state.layout with
5334 | l :: _ ->
5335 let rect = getpdimrect l.pagedimno in
5336 let w, h =
5337 if conf.crophack
5338 then
5339 (truncate (1.8 *. (rect.(1) -. rect.(0))),
5340 truncate (1.2 *. (rect.(3) -. rect.(0))))
5341 else
5342 (truncate (rect.(1) -. rect.(0)),
5343 truncate (rect.(3) -. rect.(0)))
5345 let w = truncate ((float w)*.conf.zoom)
5346 and h = truncate ((float h)*.conf.zoom) in
5347 if w != 0 && h != 0
5348 then (
5349 state.anchor <- getanchor ();
5350 Wsi.reshape (w + state.scrollw) (h + conf.interpagespace)
5352 G.postRedisplay "z";
5354 | [] -> ()
5357 | 60 | 62 -> (* < > *)
5358 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.fitmodel
5360 | 91 | 93 -> (* [ ] *)
5361 conf.colorscale <-
5362 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
5364 G.postRedisplay "brightness";
5366 | 99 when state.mode = View -> (* c *)
5367 let (c, a, b), z =
5368 match state.prevcolumns with
5369 | None -> (1, 0, 0), 1.0
5370 | Some (columns, z) ->
5371 let cab =
5372 match columns with
5373 | Csplit (c, _) -> -c, 0, 0
5374 | Cmulti ((c, a, b), _) -> c, a, b
5375 | Csingle _ -> 1, 0, 0
5377 cab, z
5379 setcolumns View c a b;
5380 setzoom z;
5382 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
5383 setzoom state.prevzoom
5385 | 107 | 0xff52 | 0xff97 -> (* k (kp) up *)
5386 begin match state.autoscroll with
5387 | None ->
5388 begin match state.mode with
5389 | Birdseye beye -> upbirdseye 1 beye
5390 | _ ->
5391 if ctrl
5392 then gotoy_and_clear_text (clamp ~-(state.winh/2))
5393 else (
5394 if not (Wsi.withshift mask) && conf.presentation
5395 then prevpage ()
5396 else gotoy_and_clear_text (clamp (-conf.scrollstep))
5399 | Some n ->
5400 setautoscrollspeed n false
5403 | 106 | 0xff54 | 0xff99 -> (* j (kp) down *)
5404 begin match state.autoscroll with
5405 | None ->
5406 begin match state.mode with
5407 | Birdseye beye -> downbirdseye 1 beye
5408 | _ ->
5409 if ctrl
5410 then gotoy_and_clear_text (clamp (state.winh/2))
5411 else (
5412 if not (Wsi.withshift mask) && conf.presentation
5413 then nextpage ()
5414 else gotoy_and_clear_text (clamp conf.scrollstep)
5417 | Some n ->
5418 setautoscrollspeed n true
5421 | 0xff51 | 0xff53 | 0xff96 | 0xff98
5422 when not (Wsi.withalt mask) -> (* (kp) left / right *)
5423 if canpan ()
5424 then
5425 let dx =
5426 if ctrl
5427 then state.winw / 2
5428 else conf.hscrollstep
5430 let dx = if key = 0xff51 or key = 0xff96 then dx else -dx in
5431 state.x <- panbound (state.x + dx);
5432 gotoy_and_clear_text state.y
5433 else (
5434 state.text <- "";
5435 G.postRedisplay "left/right"
5438 | 0xff55 | 0xff9a -> (* (kp) prior *)
5439 let y =
5440 if ctrl
5441 then
5442 match state.layout with
5443 | [] -> state.y
5444 | l :: _ -> state.y - l.pagey
5445 else
5446 clamp (pgscale (-state.winh))
5448 gotoghyll y
5450 | 0xff56 | 0xff9b -> (* (kp) next *)
5451 let y =
5452 if ctrl
5453 then
5454 match List.rev state.layout with
5455 | [] -> state.y
5456 | l :: _ -> getpagey l.pageno
5457 else
5458 clamp (pgscale state.winh)
5460 gotoghyll y
5462 | 103 | 0xff50 | 0xff95 -> (* g (kp) home *)
5463 gotoghyll 0
5464 | 71 | 0xff57 | 0xff9c -> (* G (kp) end *)
5465 gotoghyll (clamp state.maxy)
5467 | 0xff53 | 0xff98
5468 when Wsi.withalt mask -> (* alt-(kp) right *)
5469 gotoghyll (getnav 1)
5470 | 0xff51 | 0xff96
5471 when Wsi.withalt mask -> (* alt-(kp) left *)
5472 gotoghyll (getnav ~-1)
5474 | 114 -> (* r *)
5475 reload ()
5477 | 118 when conf.debug -> (* v *)
5478 state.rects <- [];
5479 List.iter (fun l ->
5480 match getopaque l.pageno with
5481 | None -> ()
5482 | Some opaque ->
5483 let x0, y0, x1, y1 = pagebbox opaque in
5484 let a,b = float x0, float y0 in
5485 let c,d = float x1, float y0 in
5486 let e,f = float x1, float y1 in
5487 let h,j = float x0, float y1 in
5488 let rect = (a,b,c,d,e,f,h,j) in
5489 debugrect rect;
5490 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
5491 ) state.layout;
5492 G.postRedisplay "v";
5494 | _ ->
5495 vlog "huh? %s" (Wsi.keyname key)
5498 let linknavkeyboard key mask linknav =
5499 let getpage pageno =
5500 let rec loop = function
5501 | [] -> None
5502 | l :: _ when l.pageno = pageno -> Some l
5503 | _ :: rest -> loop rest
5504 in loop state.layout
5506 let doexact (pageno, n) =
5507 match getopaque pageno, getpage pageno with
5508 | Some opaque, Some l ->
5509 if key = 0xff0d || key = 0xff8d (* (kp)enter *)
5510 then
5511 let under = getlink opaque n in
5512 G.postRedisplay "link gotounder";
5513 gotounder under;
5514 state.mode <- View;
5515 else
5516 let opt, dir =
5517 match key with
5518 | 0xff50 -> (* home *)
5519 Some (findlink opaque LDfirst), -1
5521 | 0xff57 -> (* end *)
5522 Some (findlink opaque LDlast), 1
5524 | 0xff51 -> (* left *)
5525 Some (findlink opaque (LDleft n)), -1
5527 | 0xff53 -> (* right *)
5528 Some (findlink opaque (LDright n)), 1
5530 | 0xff52 -> (* up *)
5531 Some (findlink opaque (LDup n)), -1
5533 | 0xff54 -> (* down *)
5534 Some (findlink opaque (LDdown n)), 1
5536 | _ -> None, 0
5538 let pwl l dir =
5539 begin match findpwl l.pageno dir with
5540 | Pwlnotfound -> ()
5541 | Pwl pageno ->
5542 let notfound dir =
5543 state.mode <- LinkNav (Ltgendir dir);
5544 let y, h = getpageyh pageno in
5545 let y =
5546 if dir < 0
5547 then y + h - state.winh
5548 else y
5550 gotoy y
5552 begin match getopaque pageno, getpage pageno with
5553 | Some opaque, Some _ ->
5554 let link =
5555 let ld = if dir > 0 then LDfirst else LDlast in
5556 findlink opaque ld
5558 begin match link with
5559 | Lfound m ->
5560 showlinktype (getlink opaque m);
5561 state.mode <- LinkNav (Ltexact (pageno, m));
5562 G.postRedisplay "linknav jpage";
5563 | _ -> notfound dir
5564 end;
5565 | _ -> notfound dir
5566 end;
5567 end;
5569 begin match opt with
5570 | Some Lnotfound -> pwl l dir;
5571 | Some (Lfound m) ->
5572 if m = n
5573 then pwl l dir
5574 else (
5575 let _, y0, _, y1 = getlinkrect opaque m in
5576 if y0 < l.pagey
5577 then gotopage1 l.pageno y0
5578 else (
5579 let d = fstate.fontsize + 1 in
5580 if y1 - l.pagey > l.pagevh - d
5581 then gotopage1 l.pageno (y1 - state.winh - state.hscrollh + d)
5582 else G.postRedisplay "linknav";
5584 showlinktype (getlink opaque m);
5585 state.mode <- LinkNav (Ltexact (l.pageno, m));
5588 | None -> viewkeyboard key mask
5589 end;
5590 | _ -> viewkeyboard key mask
5592 if key = 0xff63
5593 then (
5594 state.mode <- View;
5595 G.postRedisplay "leave linknav"
5597 else
5598 match linknav with
5599 | Ltgendir _ -> viewkeyboard key mask
5600 | Ltexact exact -> doexact exact
5603 let keyboard key mask =
5604 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5605 then wcmd "interrupt"
5606 else state.uioh <- state.uioh#key key mask
5609 let birdseyekeyboard key mask
5610 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5611 let incr =
5612 match conf.columns with
5613 | Csingle _ -> 1
5614 | Cmulti ((c, _, _), _) -> c
5615 | Csplit _ -> failwith "bird's eye split mode"
5617 let pgh layout = List.fold_left (fun m l -> max l.pageh m) state.winh layout in
5618 match key with
5619 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5620 let y, h = getpageyh pageno in
5621 let top = (state.winh - h) / 2 in
5622 gotoy (max 0 (y - top))
5623 | 0xff0d (* enter *)
5624 | 0xff8d -> leavebirdseye beye false (* kp enter *)
5625 | 0xff1b -> leavebirdseye beye true (* escape *)
5626 | 0xff52 -> upbirdseye incr beye (* up *)
5627 | 0xff54 -> downbirdseye incr beye (* down *)
5628 | 0xff51 -> upbirdseye 1 beye (* left *)
5629 | 0xff53 -> downbirdseye 1 beye (* right *)
5631 | 0xff55 -> (* prior *)
5632 begin match state.layout with
5633 | l :: _ ->
5634 if l.pagey != 0
5635 then (
5636 state.mode <- Birdseye (
5637 oconf, leftx, l.pageno, hooverpageno, anchor
5639 gotopage1 l.pageno 0;
5641 else (
5642 let layout = layout (state.y-state.winh) (pgh state.layout) in
5643 match layout with
5644 | [] -> gotoy (clamp (-state.winh))
5645 | l :: _ ->
5646 state.mode <- Birdseye (
5647 oconf, leftx, l.pageno, hooverpageno, anchor
5649 gotopage1 l.pageno 0
5652 | [] -> gotoy (clamp (-state.winh))
5653 end;
5655 | 0xff56 -> (* next *)
5656 begin match List.rev state.layout with
5657 | l :: _ ->
5658 let layout = layout (state.y + (pgh state.layout)) state.winh in
5659 begin match layout with
5660 | [] ->
5661 let incr = l.pageh - l.pagevh in
5662 if incr = 0
5663 then (
5664 state.mode <-
5665 Birdseye (
5666 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5668 G.postRedisplay "birdseye pagedown";
5670 else gotoy (clamp (incr + conf.interpagespace*2));
5672 | l :: _ ->
5673 state.mode <-
5674 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5675 gotopage1 l.pageno 0;
5678 | [] -> gotoy (clamp state.winh)
5679 end;
5681 | 0xff50 -> (* home *)
5682 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5683 gotopage1 0 0
5685 | 0xff57 -> (* end *)
5686 let pageno = state.pagecount - 1 in
5687 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5688 if not (pagevisible state.layout pageno)
5689 then
5690 let h =
5691 match List.rev state.pdims with
5692 | [] -> state.winh
5693 | (_, _, h, _) :: _ -> h
5695 gotoy (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
5696 else G.postRedisplay "birdseye end";
5697 | _ -> viewkeyboard key mask
5700 let drawpage l =
5701 let color =
5702 match state.mode with
5703 | Textentry _ -> scalecolor 0.4
5704 | LinkNav _
5705 | View -> scalecolor 1.0
5706 | Birdseye (_, _, pageno, hooverpageno, _) ->
5707 if l.pageno = hooverpageno
5708 then scalecolor 0.9
5709 else (
5710 if l.pageno = pageno
5711 then scalecolor 1.0
5712 else scalecolor 0.8
5715 drawtiles l color;
5718 let postdrawpage l linkindexbase =
5719 match getopaque l.pageno with
5720 | Some opaque ->
5721 if tileready l l.pagex l.pagey
5722 then
5723 let x = l.pagedispx - l.pagex
5724 and y = l.pagedispy - l.pagey in
5725 let hlmask =
5726 match conf.columns with
5727 | Csingle _ | Cmulti _ ->
5728 (if conf.hlinks then 1 else 0)
5729 + (if state.glinks
5730 && not (isbirdseye state.mode) then 2 else 0)
5731 | _ -> 0
5733 let s =
5734 match state.mode with
5735 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5736 | _ -> ""
5738 postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize);
5739 else 0
5740 | _ -> 0
5743 let scrollindicator () =
5744 let sbw, ph, sh = state.uioh#scrollph in
5745 let sbh, pw, sw = state.uioh#scrollpw in
5747 GlDraw.color (0.64, 0.64, 0.64);
5748 GlDraw.rect
5749 (float (state.winw - sbw), 0.)
5750 (float state.winw, float state.winh)
5752 GlDraw.rect
5753 (0., float (state.winh - sbh))
5754 (float (state.winw - state.scrollw - 1), float state.winh)
5756 GlDraw.color (0.0, 0.0, 0.0);
5758 GlDraw.rect
5759 (float (state.winw - sbw), ph)
5760 (float state.winw, ph +. sh)
5762 GlDraw.rect
5763 (pw, float (state.winh - sbh))
5764 (pw +. sw, float state.winh)
5768 let showsel () =
5769 match state.mstate with
5770 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5773 | Msel ((x0, y0), (x1, y1)) ->
5774 let rec loop = function
5775 | l :: ls ->
5776 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5777 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5778 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5779 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5780 then
5781 match getopaque l.pageno with
5782 | Some opaque ->
5783 let x0, y0 = pagetranslatepoint l x0 y0 in
5784 let x1, y1 = pagetranslatepoint l x1 y1 in
5785 seltext opaque (x0, y0, x1, y1);
5786 | _ -> ()
5787 else loop ls
5788 | [] -> ()
5790 loop state.layout
5793 let showrects rects =
5794 Gl.enable `blend;
5795 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5796 GlDraw.polygon_mode `both `fill;
5797 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5798 List.iter
5799 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5800 List.iter (fun l ->
5801 if l.pageno = pageno
5802 then (
5803 let dx = float (l.pagedispx - l.pagex) in
5804 let dy = float (l.pagedispy - l.pagey) in
5805 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5806 GlDraw.begins `quads;
5808 GlDraw.vertex2 (x0+.dx, y0+.dy);
5809 GlDraw.vertex2 (x1+.dx, y1+.dy);
5810 GlDraw.vertex2 (x2+.dx, y2+.dy);
5811 GlDraw.vertex2 (x3+.dx, y3+.dy);
5813 GlDraw.ends ();
5815 ) state.layout
5816 ) rects
5818 Gl.disable `blend;
5821 let display () =
5822 GlClear.color (scalecolor2 conf.bgcolor);
5823 GlClear.clear [`color];
5824 List.iter drawpage state.layout;
5825 let rects =
5826 match state.mode with
5827 | LinkNav (Ltexact (pageno, linkno)) ->
5828 begin match getopaque pageno with
5829 | Some opaque ->
5830 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5831 (pageno, 5, (
5832 float x0, float y0,
5833 float x1, float y0,
5834 float x1, float y1,
5835 float x0, float y1)
5836 ) :: state.rects
5837 | None -> state.rects
5839 | _ -> state.rects
5841 showrects rects;
5842 let rec postloop linkindexbase = function
5843 | l :: rest ->
5844 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
5845 postloop linkindexbase rest
5846 | [] -> ()
5848 showsel ();
5849 postloop 0 state.layout;
5850 state.uioh#display;
5851 begin match state.mstate with
5852 | Mzoomrect ((x0, y0), (x1, y1)) ->
5853 Gl.enable `blend;
5854 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5855 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5856 GlDraw.rect (float x0, float y0)
5857 (float x1, float y1);
5858 Gl.disable `blend;
5859 | _ -> ()
5860 end;
5861 enttext ();
5862 scrollindicator ();
5863 Wsi.swapb ();
5866 let zoomrect x y x1 y1 =
5867 let x0 = min x x1
5868 and x1 = max x x1
5869 and y0 = min y y1 in
5870 gotoy (state.y + y0);
5871 state.anchor <- getanchor ();
5872 let zoom = (float state.w) /. float (x1 - x0) in
5873 let margin =
5874 match conf.fitmodel, conf.columns with
5875 | FitPage, Csplit _ ->
5876 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
5878 | _, _ ->
5879 if state.w < state.winw - state.scrollw
5880 then (state.winw - state.scrollw - state.w) / 2
5881 else 0
5883 state.x <- (state.x + margin) - x0;
5884 setzoom zoom;
5885 Wsi.setcursor Wsi.CURSOR_INHERIT;
5886 state.mstate <- Mnone;
5889 let scrollx x =
5890 let winw = state.winw - state.scrollw - 1 in
5891 let s = float x /. float winw in
5892 let destx = truncate (float (state.w + winw) *. s) in
5893 state.x <- winw - destx;
5894 gotoy_and_clear_text state.y;
5895 state.mstate <- Mscrollx;
5898 let scrolly y =
5899 let s = float y /. float state.winh in
5900 let desty = truncate (float (state.maxy - state.winh) *. s) in
5901 gotoy_and_clear_text desty;
5902 state.mstate <- Mscrolly;
5905 let viewmouse button down x y mask =
5906 match button with
5907 | n when (n == 4 || n == 5) && not down ->
5908 if Wsi.withctrl mask
5909 then (
5910 match state.mstate with
5911 | Mzoom (oldn, i) ->
5912 if oldn = n
5913 then (
5914 if i = 2
5915 then
5916 let incr =
5917 match n with
5918 | 5 ->
5919 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5920 | _ ->
5921 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5923 let zoom = conf.zoom -. incr in
5924 setzoom zoom;
5925 state.mstate <- Mzoom (n, 0);
5926 else
5927 state.mstate <- Mzoom (n, i+1);
5929 else state.mstate <- Mzoom (n, 0)
5931 | _ -> state.mstate <- Mzoom (n, 0)
5933 else (
5934 match state.autoscroll with
5935 | Some step -> setautoscrollspeed step (n=4)
5936 | None ->
5937 if conf.wheelbypage || conf.presentation
5938 then (
5939 if n = 4
5940 then prevpage ()
5941 else nextpage ()
5943 else
5944 let incr =
5945 if n = 4
5946 then -conf.scrollstep
5947 else conf.scrollstep
5949 let incr = incr * 2 in
5950 let y = clamp incr in
5951 gotoy_and_clear_text y
5954 | n when (n = 6 || n = 7) && not down && canpan () ->
5955 state.x <-
5956 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep);
5957 gotoy_and_clear_text state.y
5959 | 1 when Wsi.withshift mask ->
5960 state.mstate <- Mnone;
5961 if not down
5962 then (
5963 match unproject x y with
5964 | Some (pageno, ux, uy) ->
5965 let cmd = Printf.sprintf
5966 "%s %s %d %d %d"
5967 conf.stcmd state.path pageno ux uy
5969 popen cmd []
5970 | None -> ()
5973 | 1 when Wsi.withctrl mask ->
5974 if down
5975 then (
5976 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5977 state.mstate <- Mpan (x, y)
5979 else
5980 state.mstate <- Mnone
5982 | 3 ->
5983 if down
5984 then (
5985 Wsi.setcursor Wsi.CURSOR_CYCLE;
5986 let p = (x, y) in
5987 state.mstate <- Mzoomrect (p, p)
5989 else (
5990 match state.mstate with
5991 | Mzoomrect ((x0, y0), _) ->
5992 if abs (x-x0) > 10 && abs (y - y0) > 10
5993 then zoomrect x0 y0 x y
5994 else (
5995 state.mstate <- Mnone;
5996 Wsi.setcursor Wsi.CURSOR_INHERIT;
5997 G.postRedisplay "kill accidental zoom rect";
5999 | _ ->
6000 Wsi.setcursor Wsi.CURSOR_INHERIT;
6001 state.mstate <- Mnone
6004 | 1 when x > state.winw - state.scrollw ->
6005 if down
6006 then
6007 let _, position, sh = state.uioh#scrollph in
6008 if y > truncate position && y < truncate (position +. sh)
6009 then state.mstate <- Mscrolly
6010 else scrolly y
6011 else
6012 state.mstate <- Mnone
6014 | 1 when y > state.winh - state.hscrollh ->
6015 if down
6016 then
6017 let _, position, sw = state.uioh#scrollpw in
6018 if x > truncate position && x < truncate (position +. sw)
6019 then state.mstate <- Mscrollx
6020 else scrollx x
6021 else
6022 state.mstate <- Mnone
6024 | 1 ->
6025 let dest = if down then getunder x y else Unone in
6026 begin match dest with
6027 | Ulinkgoto _
6028 | Ulinkuri _
6029 | Uremote _
6030 | Uunexpected _ | Ulaunch _ | Unamed _ ->
6031 gotounder dest
6033 | Unone when down ->
6034 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
6035 state.mstate <- Mpan (x, y);
6037 | Unone | Utext _ ->
6038 if down
6039 then (
6040 if conf.angle mod 360 = 0
6041 then (
6042 state.mstate <- Msel ((x, y), (x, y));
6043 G.postRedisplay "mouse select";
6046 else (
6047 match state.mstate with
6048 | Mnone -> ()
6050 | Mzoom _ | Mscrollx | Mscrolly ->
6051 state.mstate <- Mnone
6053 | Mzoomrect ((x0, y0), _) ->
6054 zoomrect x0 y0 x y
6056 | Mpan _ ->
6057 Wsi.setcursor Wsi.CURSOR_INHERIT;
6058 state.mstate <- Mnone
6060 | Msel ((x0, y0), (x1, y1)) ->
6061 let rec loop = function
6062 | [] -> ()
6063 | l :: rest ->
6064 let inside =
6065 let a0 = l.pagedispy in
6066 let a1 = a0 + l.pagevh in
6067 let b0 = l.pagedispx in
6068 let b1 = b0 + l.pagevw in
6069 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
6070 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
6072 if inside
6073 then
6074 match getopaque l.pageno with
6075 | Some opaque ->
6076 begin
6077 match Ne.pipe () with
6078 | Ne.Exn exn ->
6079 showtext '!'
6080 (Printf.sprintf
6081 "can not create sel pipe: %s"
6082 (exntos exn));
6083 | Ne.Res (r, w) ->
6084 let doclose what fd =
6085 Ne.clo fd (fun msg ->
6086 dolog "%s close failed: %s" what msg)
6089 popen conf.selcmd [r, 0; w, -1];
6090 copysel w opaque;
6091 doclose "pipe/r" r;
6092 G.postRedisplay "copysel";
6093 with exn ->
6094 dolog "can not execute %S: %s"
6095 conf.selcmd (exntos exn);
6096 doclose "pipe/r" r;
6097 doclose "pipe/w" w;
6099 | None -> ()
6100 else loop rest
6102 loop state.layout;
6103 Wsi.setcursor Wsi.CURSOR_INHERIT;
6104 state.mstate <- Mnone;
6108 | _ -> ()
6111 let birdseyemouse button down x y mask
6112 (conf, leftx, _, hooverpageno, anchor) =
6113 match button with
6114 | 1 when down ->
6115 let rec loop = function
6116 | [] -> ()
6117 | l :: rest ->
6118 if y > l.pagedispy && y < l.pagedispy + l.pagevh
6119 && x > l.pagedispx && x < l.pagedispx + l.pagevw
6120 then (
6121 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
6123 else loop rest
6125 loop state.layout
6126 | 3 -> ()
6127 | _ -> viewmouse button down x y mask
6130 let mouse button down x y mask =
6131 state.uioh <- state.uioh#button button down x y mask;
6134 let motion ~x ~y =
6135 state.uioh <- state.uioh#motion x y
6138 let pmotion ~x ~y =
6139 state.uioh <- state.uioh#pmotion x y;
6142 let uioh = object
6143 method display = ()
6145 method key key mask =
6146 begin match state.mode with
6147 | Textentry textentry -> textentrykeyboard key mask textentry
6148 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
6149 | View -> viewkeyboard key mask
6150 | LinkNav linknav -> linknavkeyboard key mask linknav
6151 end;
6152 state.uioh
6154 method button button bstate x y mask =
6155 begin match state.mode with
6156 | LinkNav _
6157 | View -> viewmouse button bstate x y mask
6158 | Birdseye beye -> birdseyemouse button bstate x y mask beye
6159 | Textentry _ -> ()
6160 end;
6161 state.uioh
6163 method motion x y =
6164 begin match state.mode with
6165 | Textentry _ -> ()
6166 | View | Birdseye _ | LinkNav _ ->
6167 match state.mstate with
6168 | Mzoom _ | Mnone -> ()
6170 | Mpan (x0, y0) ->
6171 let dx = x - x0
6172 and dy = y0 - y in
6173 state.mstate <- Mpan (x, y);
6174 if canpan ()
6175 then state.x <- panbound (state.x + dx);
6176 let y = clamp dy in
6177 gotoy_and_clear_text y
6179 | Msel (a, _) ->
6180 state.mstate <- Msel (a, (x, y));
6181 G.postRedisplay "motion select";
6183 | Mscrolly ->
6184 let y = min state.winh (max 0 y) in
6185 scrolly y
6187 | Mscrollx ->
6188 let x = min state.winw (max 0 x) in
6189 scrollx x
6191 | Mzoomrect (p0, _) ->
6192 state.mstate <- Mzoomrect (p0, (x, y));
6193 G.postRedisplay "motion zoomrect";
6194 end;
6195 state.uioh
6197 method pmotion x y =
6198 begin match state.mode with
6199 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
6200 let rec loop = function
6201 | [] ->
6202 if hooverpageno != -1
6203 then (
6204 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
6205 G.postRedisplay "pmotion birdseye no hoover";
6207 | l :: rest ->
6208 if y > l.pagedispy && y < l.pagedispy + l.pagevh
6209 && x > l.pagedispx && x < l.pagedispx + l.pagevw
6210 then (
6211 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
6212 G.postRedisplay "pmotion birdseye hoover";
6214 else loop rest
6216 loop state.layout
6218 | Textentry _ -> ()
6220 | LinkNav _
6221 | View ->
6222 match state.mstate with
6223 | Mnone -> updateunder x y
6224 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
6226 end;
6227 state.uioh
6229 method infochanged _ = ()
6231 method scrollph =
6232 let maxy = state.maxy - (if conf.maxhfit then state.winh else 0) in
6233 let p, h =
6234 if maxy = 0
6235 then 0.0, float state.winh
6236 else scrollph state.y maxy
6238 state.scrollw, p, h
6240 method scrollpw =
6241 let winw = state.winw - state.scrollw in
6242 let fwinw = float winw in
6243 let sw =
6244 let sw = fwinw /. float state.w in
6245 let sw = fwinw *. sw in
6246 max sw (float conf.scrollh)
6248 let position =
6249 let maxx = state.w + winw in
6250 let x = winw - state.x in
6251 let percent = float x /. float maxx in
6252 (fwinw -. sw) *. percent
6254 state.hscrollh, position, sw
6256 method modehash =
6257 let modename =
6258 match state.mode with
6259 | LinkNav _ -> "links"
6260 | Textentry _ -> "textentry"
6261 | Birdseye _ -> "birdseye"
6262 | View -> "view"
6264 findkeyhash conf modename
6266 method eformsgs = true
6267 end;;
6269 module Config =
6270 struct
6271 open Parser
6273 let fontpath = ref "";;
6275 module KeyMap =
6276 Map.Make (struct type t = (int * int) let compare = compare end);;
6278 let unent s =
6279 let l = String.length s in
6280 let b = Buffer.create l in
6281 unent b s 0 l;
6282 Buffer.contents b;
6285 let home =
6286 try Sys.getenv "HOME"
6287 with exn ->
6288 prerr_endline
6289 ("Can not determine home directory location: " ^ exntos exn);
6293 let modifier_of_string = function
6294 | "alt" -> Wsi.altmask
6295 | "shift" -> Wsi.shiftmask
6296 | "ctrl" | "control" -> Wsi.ctrlmask
6297 | "meta" -> Wsi.metamask
6298 | _ -> 0
6301 let key_of_string =
6302 let r = Str.regexp "-" in
6303 fun s ->
6304 let elems = Str.full_split r s in
6305 let f n k m =
6306 let g s =
6307 let m1 = modifier_of_string s in
6308 if m1 = 0
6309 then (Wsi.namekey s, m)
6310 else (k, m lor m1)
6311 in function
6312 | Str.Delim s when n land 1 = 0 -> g s
6313 | Str.Text s -> g s
6314 | Str.Delim _ -> (k, m)
6316 let rec loop n k m = function
6317 | [] -> (k, m)
6318 | x :: xs ->
6319 let k, m = f n k m x in
6320 loop (n+1) k m xs
6322 loop 0 0 0 elems
6325 let keys_of_string =
6326 let r = Str.regexp "[ \t]" in
6327 fun s ->
6328 let elems = Str.split r s in
6329 List.map key_of_string elems
6332 let copykeyhashes c =
6333 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
6336 let config_of c attrs =
6337 let apply c k v =
6339 match k with
6340 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
6341 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
6342 | "case-insensitive-search" -> { c with icase = bool_of_string v }
6343 | "preload" -> { c with preload = bool_of_string v }
6344 | "page-bias" -> { c with pagebias = int_of_string v }
6345 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
6346 | "horizontal-scroll-step" ->
6347 { c with hscrollstep = max (int_of_string v) 1 }
6348 | "auto-scroll-step" ->
6349 { c with autoscrollstep = max 0 (int_of_string v) }
6350 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
6351 | "crop-hack" -> { c with crophack = bool_of_string v }
6352 | "throttle" ->
6353 let mw =
6354 match String.lowercase v with
6355 | "true" -> Some infinity
6356 | "false" -> None
6357 | f -> Some (float_of_string f)
6359 { c with maxwait = mw}
6360 | "highlight-links" -> { c with hlinks = bool_of_string v }
6361 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
6362 | "vertical-margin" ->
6363 { c with interpagespace = max 0 (int_of_string v) }
6364 | "zoom" ->
6365 let zoom = float_of_string v /. 100. in
6366 let zoom = max zoom 0.0 in
6367 { c with zoom = zoom }
6368 | "presentation" -> { c with presentation = bool_of_string v }
6369 | "rotation-angle" -> { c with angle = int_of_string v }
6370 | "width" -> { c with cwinw = max 20 (int_of_string v) }
6371 | "height" -> { c with cwinh = max 20 (int_of_string v) }
6372 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
6373 | "proportional-display" ->
6374 let fm =
6375 if bool_of_string v
6376 then FitProportional
6377 else FitWidth
6379 { c with fitmodel = fm }
6380 | "fit-model" -> { c with fitmodel = fitmodel_of_string v }
6381 | "pixmap-cache-size" ->
6382 { c with memlimit = max 2 (int_of_string_with_suffix v) }
6383 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
6384 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
6385 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
6386 | "persistent-location" -> { c with jumpback = bool_of_string v }
6387 | "background-color" -> { c with bgcolor = color_of_string v }
6388 | "scrollbar-in-presentation" ->
6389 { c with scrollbarinpm = bool_of_string v }
6390 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
6391 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
6392 | "mupdf-store-size" ->
6393 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
6394 | "checkers" -> { c with checkers = bool_of_string v }
6395 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
6396 | "trim-margins" -> { c with trimmargins = bool_of_string v }
6397 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
6398 | "uri-launcher" -> { c with urilauncher = unent v }
6399 | "path-launcher" -> { c with pathlauncher = unent v }
6400 | "color-space" -> { c with colorspace = colorspace_of_string v }
6401 | "invert-colors" -> { c with invert = bool_of_string v }
6402 | "brightness" -> { c with colorscale = float_of_string v }
6403 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
6404 | "ghyllscroll" ->
6405 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
6406 | "columns" ->
6407 let (n, _, _) as nab = multicolumns_of_string v in
6408 if n < 0
6409 then { c with columns = Csplit (-n, [||]) }
6410 else { c with columns = Cmulti (nab, [||]) }
6411 | "birds-eye-columns" ->
6412 { c with beyecolumns = Some (max (int_of_string v) 2) }
6413 | "selection-command" -> { c with selcmd = unent v }
6414 | "synctex-command" -> { c with stcmd = unent v }
6415 | "update-cursor" -> { c with updatecurs = bool_of_string v }
6416 | "hint-font-size" -> { c with hfsize = bound (int_of_string v) 5 100 }
6417 | "page-scroll-scale" -> { c with pgscale = float_of_string v }
6418 | "use-pbo" -> { c with usepbo = bool_of_string v }
6419 | "wheel-scrolls-pages" -> { c with wheelbypage = bool_of_string v }
6420 | _ -> c
6421 with exn ->
6422 prerr_endline ("Error processing attribute (`" ^
6423 k ^ "'=`" ^ v ^ "'): " ^ exntos exn);
6426 let rec fold c = function
6427 | [] -> c
6428 | (k, v) :: rest ->
6429 let c = apply c k v in
6430 fold c rest
6432 fold { c with keyhashes = copykeyhashes c } attrs;
6435 let fromstring f pos n v d =
6436 try f v
6437 with exn ->
6438 dolog "Error processing attribute (%S=%S) at %d\n%s"
6439 n v pos (exntos exn)
6444 let bookmark_of attrs =
6445 let rec fold title page rely visy = function
6446 | ("title", v) :: rest -> fold v page rely visy rest
6447 | ("page", v) :: rest -> fold title v rely visy rest
6448 | ("rely", v) :: rest -> fold title page v visy rest
6449 | ("visy", v) :: rest -> fold title page rely v rest
6450 | _ :: rest -> fold title page rely visy rest
6451 | [] -> title, page, rely, visy
6453 fold "invalid" "0" "0" "0" attrs
6456 let doc_of attrs =
6457 let rec fold path page rely pan visy = function
6458 | ("path", v) :: rest -> fold v page rely pan visy rest
6459 | ("page", v) :: rest -> fold path v rely pan visy rest
6460 | ("rely", v) :: rest -> fold path page v pan visy rest
6461 | ("pan", v) :: rest -> fold path page rely v visy rest
6462 | ("visy", v) :: rest -> fold path page rely pan v rest
6463 | _ :: rest -> fold path page rely pan visy rest
6464 | [] -> path, page, rely, pan, visy
6466 fold "" "0" "0" "0" "0" attrs
6469 let map_of attrs =
6470 let rec fold rs ls = function
6471 | ("out", v) :: rest -> fold v ls rest
6472 | ("in", v) :: rest -> fold rs v rest
6473 | _ :: rest -> fold ls rs rest
6474 | [] -> ls, rs
6476 fold "" "" attrs
6479 let setconf dst src =
6480 dst.scrollbw <- src.scrollbw;
6481 dst.scrollh <- src.scrollh;
6482 dst.icase <- src.icase;
6483 dst.preload <- src.preload;
6484 dst.pagebias <- src.pagebias;
6485 dst.verbose <- src.verbose;
6486 dst.scrollstep <- src.scrollstep;
6487 dst.maxhfit <- src.maxhfit;
6488 dst.crophack <- src.crophack;
6489 dst.autoscrollstep <- src.autoscrollstep;
6490 dst.maxwait <- src.maxwait;
6491 dst.hlinks <- src.hlinks;
6492 dst.underinfo <- src.underinfo;
6493 dst.interpagespace <- src.interpagespace;
6494 dst.zoom <- src.zoom;
6495 dst.presentation <- src.presentation;
6496 dst.angle <- src.angle;
6497 dst.cwinw <- src.cwinw;
6498 dst.cwinh <- src.cwinh;
6499 dst.savebmarks <- src.savebmarks;
6500 dst.memlimit <- src.memlimit;
6501 dst.fitmodel <- src.fitmodel;
6502 dst.texcount <- src.texcount;
6503 dst.sliceheight <- src.sliceheight;
6504 dst.thumbw <- src.thumbw;
6505 dst.jumpback <- src.jumpback;
6506 dst.bgcolor <- src.bgcolor;
6507 dst.scrollbarinpm <- src.scrollbarinpm;
6508 dst.tilew <- src.tilew;
6509 dst.tileh <- src.tileh;
6510 dst.mustoresize <- src.mustoresize;
6511 dst.checkers <- src.checkers;
6512 dst.aalevel <- src.aalevel;
6513 dst.trimmargins <- src.trimmargins;
6514 dst.trimfuzz <- src.trimfuzz;
6515 dst.urilauncher <- src.urilauncher;
6516 dst.colorspace <- src.colorspace;
6517 dst.invert <- src.invert;
6518 dst.colorscale <- src.colorscale;
6519 dst.redirectstderr <- src.redirectstderr;
6520 dst.ghyllscroll <- src.ghyllscroll;
6521 dst.columns <- src.columns;
6522 dst.beyecolumns <- src.beyecolumns;
6523 dst.selcmd <- src.selcmd;
6524 dst.updatecurs <- src.updatecurs;
6525 dst.pathlauncher <- src.pathlauncher;
6526 dst.keyhashes <- copykeyhashes src;
6527 dst.hfsize <- src.hfsize;
6528 dst.hscrollstep <- src.hscrollstep;
6529 dst.pgscale <- src.pgscale;
6530 dst.usepbo <- src.usepbo;
6531 dst.wheelbypage <- src.wheelbypage;
6532 dst.stcmd <- src.stcmd;
6535 let get s =
6536 let h = Hashtbl.create 10 in
6537 let dc = { defconf with angle = defconf.angle } in
6538 let rec toplevel v t spos _ =
6539 match t with
6540 | Vdata | Vcdata | Vend -> v
6541 | Vopen ("llppconfig", _, closed) ->
6542 if closed
6543 then v
6544 else { v with f = llppconfig }
6545 | Vopen _ ->
6546 error "unexpected subelement at top level" s spos
6547 | Vclose _ -> error "unexpected close at top level" s spos
6549 and llppconfig v t spos _ =
6550 match t with
6551 | Vdata | Vcdata -> v
6552 | Vend -> error "unexpected end of input in llppconfig" s spos
6553 | Vopen ("defaults", attrs, closed) ->
6554 let c = config_of dc attrs in
6555 setconf dc c;
6556 if closed
6557 then v
6558 else { v with f = defaults }
6560 | Vopen ("ui-font", attrs, closed) ->
6561 let rec getsize size = function
6562 | [] -> size
6563 | ("size", v) :: rest ->
6564 let size =
6565 fromstring int_of_string spos "size" v fstate.fontsize in
6566 getsize size rest
6567 | l -> getsize size l
6569 fstate.fontsize <- getsize fstate.fontsize attrs;
6570 if closed
6571 then v
6572 else { v with f = uifont (Buffer.create 10) }
6574 | Vopen ("doc", attrs, closed) ->
6575 let pathent, spage, srely, span, svisy = doc_of attrs in
6576 let path = unent pathent
6577 and pageno = fromstring int_of_string spos "page" spage 0
6578 and rely = fromstring float_of_string spos "rely" srely 0.0
6579 and pan = fromstring int_of_string spos "pan" span 0
6580 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6581 let c = config_of dc attrs in
6582 let anchor = (pageno, rely, visy) in
6583 if closed
6584 then (Hashtbl.add h path (c, [], pan, anchor); v)
6585 else { v with f = doc path pan anchor c [] }
6587 | Vopen _ ->
6588 error "unexpected subelement in llppconfig" s spos
6590 | Vclose "llppconfig" -> { v with f = toplevel }
6591 | Vclose _ -> error "unexpected close in llppconfig" s spos
6593 and defaults v t spos _ =
6594 match t with
6595 | Vdata | Vcdata -> v
6596 | Vend -> error "unexpected end of input in defaults" s spos
6597 | Vopen ("keymap", attrs, closed) ->
6598 let modename =
6599 try List.assoc "mode" attrs
6600 with Not_found -> "global" in
6601 if closed
6602 then v
6603 else
6604 let ret keymap =
6605 let h = findkeyhash dc modename in
6606 KeyMap.iter (Hashtbl.replace h) keymap;
6607 defaults
6609 { v with f = pkeymap ret KeyMap.empty }
6611 | Vopen (_, _, _) ->
6612 error "unexpected subelement in defaults" s spos
6614 | Vclose "defaults" ->
6615 { v with f = llppconfig }
6617 | Vclose _ -> error "unexpected close in defaults" s spos
6619 and uifont b v t spos epos =
6620 match t with
6621 | Vdata | Vcdata ->
6622 Buffer.add_substring b s spos (epos - spos);
6624 | Vopen (_, _, _) ->
6625 error "unexpected subelement in ui-font" s spos
6626 | Vclose "ui-font" ->
6627 if String.length !fontpath = 0
6628 then fontpath := Buffer.contents b;
6629 { v with f = llppconfig }
6630 | Vclose _ -> error "unexpected close in ui-font" s spos
6631 | Vend -> error "unexpected end of input in ui-font" s spos
6633 and doc path pan anchor c bookmarks v t spos _ =
6634 match t with
6635 | Vdata | Vcdata -> v
6636 | Vend -> error "unexpected end of input in doc" s spos
6637 | Vopen ("bookmarks", _, closed) ->
6638 if closed
6639 then v
6640 else { v with f = pbookmarks path pan anchor c bookmarks }
6642 | Vopen ("keymap", attrs, closed) ->
6643 let modename =
6644 try List.assoc "mode" attrs
6645 with Not_found -> "global"
6647 if closed
6648 then v
6649 else
6650 let ret keymap =
6651 let h = findkeyhash c modename in
6652 KeyMap.iter (Hashtbl.replace h) keymap;
6653 doc path pan anchor c bookmarks
6655 { v with f = pkeymap ret KeyMap.empty }
6657 | Vopen (_, _, _) ->
6658 error "unexpected subelement in doc" s spos
6660 | Vclose "doc" ->
6661 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
6662 { v with f = llppconfig }
6664 | Vclose _ -> error "unexpected close in doc" s spos
6666 and pkeymap ret keymap v t spos _ =
6667 match t with
6668 | Vdata | Vcdata -> v
6669 | Vend -> error "unexpected end of input in keymap" s spos
6670 | Vopen ("map", attrs, closed) ->
6671 let r, l = map_of attrs in
6672 let kss = fromstring keys_of_string spos "in" r [] in
6673 let lss = fromstring keys_of_string spos "out" l [] in
6674 let keymap =
6675 match kss with
6676 | [] -> keymap
6677 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
6678 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
6680 if closed
6681 then { v with f = pkeymap ret keymap }
6682 else
6683 let f () = v in
6684 { v with f = skip "map" f }
6686 | Vopen _ ->
6687 error "unexpected subelement in keymap" s spos
6689 | Vclose "keymap" ->
6690 { v with f = ret keymap }
6692 | Vclose _ -> error "unexpected close in keymap" s spos
6694 and pbookmarks path pan anchor c bookmarks v t spos _ =
6695 match t with
6696 | Vdata | Vcdata -> v
6697 | Vend -> error "unexpected end of input in bookmarks" s spos
6698 | Vopen ("item", attrs, closed) ->
6699 let titleent, spage, srely, svisy = bookmark_of attrs in
6700 let page = fromstring int_of_string spos "page" spage 0
6701 and rely = fromstring float_of_string spos "rely" srely 0.0
6702 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6703 let bookmarks =
6704 (unent titleent, 0, (page, rely, visy)) :: bookmarks
6706 if closed
6707 then { v with f = pbookmarks path pan anchor c bookmarks }
6708 else
6709 let f () = v in
6710 { v with f = skip "item" f }
6712 | Vopen _ ->
6713 error "unexpected subelement in bookmarks" s spos
6715 | Vclose "bookmarks" ->
6716 { v with f = doc path pan anchor c bookmarks }
6718 | Vclose _ -> error "unexpected close in bookmarks" s spos
6720 and skip tag f v t spos _ =
6721 match t with
6722 | Vdata | Vcdata -> v
6723 | Vend ->
6724 error ("unexpected end of input in skipped " ^ tag) s spos
6725 | Vopen (tag', _, closed) ->
6726 if closed
6727 then v
6728 else
6729 let f' () = { v with f = skip tag f } in
6730 { v with f = skip tag' f' }
6731 | Vclose ctag ->
6732 if tag = ctag
6733 then f ()
6734 else error ("unexpected close in skipped " ^ tag) s spos
6737 parse { f = toplevel; accu = () } s;
6738 h, dc;
6741 let do_load f ic =
6743 let len = in_channel_length ic in
6744 let s = String.create len in
6745 really_input ic s 0 len;
6746 f s;
6747 with
6748 | Parse_error (msg, s, pos) ->
6749 let subs = subs s pos in
6750 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6751 failwith ("parse error: " ^ s)
6753 | exn ->
6754 failwith ("config load error: " ^ exntos exn)
6757 let defconfpath =
6758 let dir =
6760 let dir = Filename.concat home ".config" in
6761 if Sys.is_directory dir then dir else home
6762 with _ -> home
6764 Filename.concat dir "llpp.conf"
6767 let confpath = ref defconfpath;;
6769 let load1 f =
6770 if Sys.file_exists !confpath
6771 then
6772 match
6773 (try Some (open_in_bin !confpath)
6774 with exn ->
6775 prerr_endline
6776 ("Error opening configuration file `" ^ !confpath ^ "': " ^
6777 exntos exn);
6778 None
6780 with
6781 | Some ic ->
6782 let success =
6784 f (do_load get ic)
6785 with exn ->
6786 prerr_endline
6787 ("Error loading configuration from `" ^ !confpath ^ "': " ^
6788 exntos exn);
6789 false
6791 close_in ic;
6792 success
6794 | None -> false
6795 else
6796 f (Hashtbl.create 0, defconf)
6799 let load () =
6800 let f (h, dc) =
6801 let pc, pb, px, pa =
6803 let key =
6804 if String.length state.origin = 0
6805 then state.path
6806 else state.origin
6808 Hashtbl.find h (Filename.basename key)
6809 with Not_found -> dc, [], 0, emptyanchor
6811 setconf defconf dc;
6812 setconf conf pc;
6813 state.bookmarks <- pb;
6814 state.x <- px;
6815 state.scrollw <- conf.scrollbw;
6816 if conf.jumpback
6817 then state.anchor <- pa;
6818 cbput state.hists.nav pa;
6819 true
6821 load1 f
6824 let add_attrs bb always dc c =
6825 let ob s a b =
6826 if always || a != b
6827 then Printf.bprintf bb "\n %s='%b'" s a
6828 and oi s a b =
6829 if always || a != b
6830 then Printf.bprintf bb "\n %s='%d'" s a
6831 and oI s a b =
6832 if always || a != b
6833 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6834 and oz s a b =
6835 if always || a <> b
6836 then Printf.bprintf bb "\n %s='%g'" s (a*.100.)
6837 and oF s a b =
6838 if always || a <> b
6839 then Printf.bprintf bb "\n %s='%f'" s a
6840 and oc s a b =
6841 if always || a <> b
6842 then
6843 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6844 and oC s a b =
6845 if always || a <> b
6846 then
6847 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6848 and oR s a b =
6849 if always || a <> b
6850 then
6851 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6852 and os s a b =
6853 if always || a <> b
6854 then
6855 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6856 and og s a b =
6857 if always || a <> b
6858 then
6859 match a with
6860 | None -> ()
6861 | Some (_N, _A, _B) ->
6862 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6863 and oW s a b =
6864 if always || a <> b
6865 then
6866 let v =
6867 match a with
6868 | None -> "false"
6869 | Some f ->
6870 if f = infinity
6871 then "true"
6872 else string_of_float f
6874 Printf.bprintf bb "\n %s='%s'" s v
6875 and oco s a b =
6876 if always || a <> b
6877 then
6878 match a with
6879 | Cmulti ((n, a, b), _) when n > 1 ->
6880 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6881 | Csplit (n, _) when n > 1 ->
6882 Printf.bprintf bb "\n %s='%d'" s ~-n
6883 | _ -> ()
6884 and obeco s a b =
6885 if always || a <> b
6886 then
6887 match a with
6888 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6889 | _ -> ()
6890 and oFm s a b =
6891 if always || a <> b
6892 then
6893 Printf.bprintf bb "\n %s='%s'" s (fitmodel_to_string a)
6895 oi "width" c.cwinw dc.cwinw;
6896 oi "height" c.cwinh dc.cwinh;
6897 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6898 oi "scroll-handle-height" c.scrollh dc.scrollh;
6899 ob "case-insensitive-search" c.icase dc.icase;
6900 ob "preload" c.preload dc.preload;
6901 oi "page-bias" c.pagebias dc.pagebias;
6902 oi "scroll-step" c.scrollstep dc.scrollstep;
6903 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6904 ob "max-height-fit" c.maxhfit dc.maxhfit;
6905 ob "crop-hack" c.crophack dc.crophack;
6906 oW "throttle" c.maxwait dc.maxwait;
6907 ob "highlight-links" c.hlinks dc.hlinks;
6908 ob "under-cursor-info" c.underinfo dc.underinfo;
6909 oi "vertical-margin" c.interpagespace dc.interpagespace;
6910 oz "zoom" c.zoom dc.zoom;
6911 ob "presentation" c.presentation dc.presentation;
6912 oi "rotation-angle" c.angle dc.angle;
6913 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6914 oFm "fit-model" c.fitmodel dc.fitmodel;
6915 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6916 oi "tex-count" c.texcount dc.texcount;
6917 oi "slice-height" c.sliceheight dc.sliceheight;
6918 oi "thumbnail-width" c.thumbw dc.thumbw;
6919 ob "persistent-location" c.jumpback dc.jumpback;
6920 oc "background-color" c.bgcolor dc.bgcolor;
6921 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6922 oi "tile-width" c.tilew dc.tilew;
6923 oi "tile-height" c.tileh dc.tileh;
6924 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6925 ob "checkers" c.checkers dc.checkers;
6926 oi "aalevel" c.aalevel dc.aalevel;
6927 ob "trim-margins" c.trimmargins dc.trimmargins;
6928 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6929 os "uri-launcher" c.urilauncher dc.urilauncher;
6930 os "path-launcher" c.pathlauncher dc.pathlauncher;
6931 oC "color-space" c.colorspace dc.colorspace;
6932 ob "invert-colors" c.invert dc.invert;
6933 oF "brightness" c.colorscale dc.colorscale;
6934 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6935 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6936 oco "columns" c.columns dc.columns;
6937 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6938 os "selection-command" c.selcmd dc.selcmd;
6939 os "synctex-command" c.stcmd dc.stcmd;
6940 ob "update-cursor" c.updatecurs dc.updatecurs;
6941 oi "hint-font-size" c.hfsize dc.hfsize;
6942 oi "horizontal-scroll-step" c.hscrollstep dc.hscrollstep;
6943 oF "page-scroll-scale" c.pgscale dc.pgscale;
6944 ob "use-pbo" c.usepbo dc.usepbo;
6945 ob "wheel-scrolls-pages" c.wheelbypage dc.wheelbypage;
6948 let keymapsbuf always dc c =
6949 let bb = Buffer.create 16 in
6950 let rec loop = function
6951 | [] -> ()
6952 | (modename, h) :: rest ->
6953 let dh = findkeyhash dc modename in
6954 if always || h <> dh
6955 then (
6956 if Hashtbl.length h > 0
6957 then (
6958 if Buffer.length bb > 0
6959 then Buffer.add_char bb '\n';
6960 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6961 Hashtbl.iter (fun i o ->
6962 let isdifferent = always ||
6964 let dO = Hashtbl.find dh i in
6965 dO <> o
6966 with Not_found -> true
6968 if isdifferent
6969 then
6970 let addkm (k, m) =
6971 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6972 if Wsi.withalt m then Buffer.add_string bb "alt-";
6973 if Wsi.withshift m then Buffer.add_string bb "shift-";
6974 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6975 Buffer.add_string bb (Wsi.keyname k);
6977 let addkms l =
6978 let rec loop = function
6979 | [] -> ()
6980 | km :: [] -> addkm km
6981 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6983 loop l
6985 Buffer.add_string bb "<map in='";
6986 addkm i;
6987 match o with
6988 | KMinsrt km ->
6989 Buffer.add_string bb "' out='";
6990 addkm km;
6991 Buffer.add_string bb "'/>\n"
6993 | KMinsrl kms ->
6994 Buffer.add_string bb "' out='";
6995 addkms kms;
6996 Buffer.add_string bb "'/>\n"
6998 | KMmulti (ins, kms) ->
6999 Buffer.add_char bb ' ';
7000 addkms ins;
7001 Buffer.add_string bb "' out='";
7002 addkms kms;
7003 Buffer.add_string bb "'/>\n"
7004 ) h;
7005 Buffer.add_string bb "</keymap>";
7008 loop rest
7010 loop c.keyhashes;
7014 let save () =
7015 let uifontsize = fstate.fontsize in
7016 let bb = Buffer.create 32768 in
7017 let w, h, cx =
7018 List.fold_left
7019 (fun (w, h, _) ws ->
7020 match ws with
7021 | Wsi.Fullscreen -> (conf.cwinw, conf.cwinh, conf.cx)
7022 | Wsi.MaxVert -> (w, conf.cwinh, conf.cx)
7023 | Wsi.MaxHorz -> (conf.cwinw, h, conf.cx)
7025 (state.winw, state.winh, state.x) state.winstate
7027 conf.cwinw <- w;
7028 conf.cwinh <- h;
7029 let f (h, dc) =
7030 let dc = if conf.bedefault then conf else dc in
7031 Buffer.add_string bb "<llppconfig>\n";
7033 if String.length !fontpath > 0
7034 then
7035 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
7036 uifontsize
7037 !fontpath
7038 else (
7039 if uifontsize <> 14
7040 then
7041 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
7044 Buffer.add_string bb "<defaults ";
7045 add_attrs bb true dc dc;
7046 let kb = keymapsbuf true dc dc in
7047 if Buffer.length kb > 0
7048 then (
7049 Buffer.add_string bb ">\n";
7050 Buffer.add_buffer bb kb;
7051 Buffer.add_string bb "\n</defaults>\n";
7053 else Buffer.add_string bb "/>\n";
7055 let adddoc path pan anchor c bookmarks =
7056 if bookmarks == [] && c = dc && anchor = emptyanchor
7057 then ()
7058 else (
7059 Printf.bprintf bb "<doc path='%s'"
7060 (enent path 0 (String.length path));
7062 if anchor <> emptyanchor
7063 then (
7064 let n, rely, visy = anchor in
7065 Printf.bprintf bb " page='%d'" n;
7066 if rely > 1e-6
7067 then
7068 Printf.bprintf bb " rely='%f'" rely
7070 if abs_float visy > 1e-6
7071 then
7072 Printf.bprintf bb " visy='%f'" visy
7076 if pan != 0
7077 then Printf.bprintf bb " pan='%d'" pan;
7079 add_attrs bb false dc c;
7080 let kb = keymapsbuf false dc c in
7082 begin match bookmarks with
7083 | [] ->
7084 if Buffer.length kb > 0
7085 then (
7086 Buffer.add_string bb ">\n";
7087 Buffer.add_buffer bb kb;
7088 Buffer.add_string bb "\n</doc>\n";
7090 else Buffer.add_string bb "/>\n"
7091 | _ ->
7092 Buffer.add_string bb ">\n<bookmarks>\n";
7093 List.iter (fun (title, _level, (page, rely, visy)) ->
7094 Printf.bprintf bb
7095 "<item title='%s' page='%d'"
7096 (enent title 0 (String.length title))
7097 page
7099 if rely > 1e-6
7100 then
7101 Printf.bprintf bb " rely='%f'" rely
7103 if abs_float visy > 1e-6
7104 then
7105 Printf.bprintf bb " visy='%f'" visy
7107 Buffer.add_string bb "/>\n";
7108 ) bookmarks;
7109 Buffer.add_string bb "</bookmarks>";
7110 if Buffer.length kb > 0
7111 then (
7112 Buffer.add_string bb "\n";
7113 Buffer.add_buffer bb kb;
7115 Buffer.add_string bb "\n</doc>\n";
7116 end;
7120 let pan, conf =
7121 match state.mode with
7122 | Birdseye (c, pan, _, _, _) ->
7123 let beyecolumns =
7124 match conf.columns with
7125 | Cmulti ((c, _, _), _) -> Some c
7126 | Csingle _ -> None
7127 | Csplit _ -> None
7128 and columns =
7129 match c.columns with
7130 | Cmulti (c, _) -> Cmulti (c, [||])
7131 | Csingle _ -> Csingle [||]
7132 | Csplit _ -> failwith "quit from bird's eye while split"
7134 pan, { c with beyecolumns = beyecolumns; columns = columns }
7135 | _ -> cx, conf
7137 let basename = Filename.basename
7138 (if String.length state.origin = 0 then state.path else state.origin)
7140 adddoc basename pan (getanchor ())
7141 (let conf =
7142 let autoscrollstep =
7143 match state.autoscroll with
7144 | Some step -> step
7145 | None -> conf.autoscrollstep
7147 match state.mode with
7148 | Birdseye (bc, _, _, _, _) ->
7149 { conf with
7150 zoom = bc.zoom;
7151 presentation = bc.presentation;
7152 interpagespace = bc.interpagespace;
7153 maxwait = bc.maxwait;
7154 autoscrollstep = autoscrollstep }
7155 | _ -> { conf with autoscrollstep = autoscrollstep }
7156 in conf)
7157 (if conf.savebmarks then state.bookmarks else []);
7159 Hashtbl.iter (fun path (c, bookmarks, x, anchor) ->
7160 if basename <> path
7161 then adddoc path x anchor c bookmarks
7162 ) h;
7163 Buffer.add_string bb "</llppconfig>\n";
7164 true;
7166 if load1 f && Buffer.length bb > 0
7167 then
7169 let tmp = !confpath ^ ".tmp" in
7170 let oc = open_out_bin tmp in
7171 Buffer.output_buffer oc bb;
7172 close_out oc;
7173 Unix.rename tmp !confpath;
7174 with exn ->
7175 prerr_endline
7176 ("error while saving configuration: " ^ exntos exn)
7178 end;;
7180 let adderrmsg src msg =
7181 Buffer.add_string state.errmsgs msg;
7182 state.newerrmsgs <- true;
7183 G.postRedisplay src
7186 let adderrfmt src fmt =
7187 Format.kprintf (fun s -> adderrmsg src s) fmt;
7190 let ract cmds =
7191 let cl = splitatspace cmds in
7192 let scan s fmt f =
7193 try Scanf.sscanf s fmt f
7194 with exn ->
7195 adderrfmt "remote exec"
7196 "error processing '%S': %s\n" cmds (exntos exn)
7198 match cl with
7199 | "reload" :: [] -> reload ()
7200 | "goto" :: args :: [] ->
7201 scan args "%u %f %f"
7202 (fun pageno x y ->
7203 let cmd, _ = state.geomcmds in
7204 if String.length cmd = 0
7205 then gotopagexy pageno x y
7206 else
7207 let f prevf () =
7208 gotopagexy pageno x y;
7209 prevf ()
7211 state.reprf <- f state.reprf
7213 | "goto1" :: args :: [] -> scan args "%u %f" gotopage
7214 | "rect" :: args :: [] ->
7215 scan args "%u %u %f %f %f %f"
7216 (fun pageno color x0 y0 x1 y1 ->
7217 onpagerect pageno (fun w h ->
7218 let _,w1,h1,_ = getpagedim pageno in
7219 let sw = float w1 /. w
7220 and sh = float h1 /. h in
7221 let x0s = x0 *. sw
7222 and x1s = x1 *. sw
7223 and y0s = y0 *. sh
7224 and y1s = y1 *. sh in
7225 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
7226 debugrect rect;
7227 state.rects <- (pageno, color, rect) :: state.rects;
7228 G.postRedisplay "rect";
7231 | "activatewin" :: [] -> Wsi.activatewin ()
7232 | "quit" :: [] -> raise Quit
7233 | _ ->
7234 adderrfmt "remote command"
7235 "error processing remote command: %S\n" cmds;
7238 let remote =
7239 let scratch = String.create 80 in
7240 let buf = Buffer.create 80 in
7241 fun fd ->
7242 let rec tempfr () =
7243 try Some (Unix.read fd scratch 0 80)
7244 with
7245 | Unix.Unix_error (Unix.EAGAIN, _, _) -> None
7246 | Unix.Unix_error (Unix.EINTR, _, _) -> tempfr ()
7247 | exn -> raise exn
7249 match tempfr () with
7250 | None -> Some fd
7251 | Some n ->
7252 if n = 0
7253 then (
7254 Unix.close fd;
7255 if Buffer.length buf > 0
7256 then (
7257 let s = Buffer.contents buf in
7258 Buffer.clear buf;
7259 ract s;
7261 None
7263 else
7264 let rec eat ppos =
7265 let nlpos =
7267 let pos = String.index_from scratch ppos '\n' in
7268 if pos >= n then -1 else pos
7269 with Not_found -> -1
7271 if nlpos >= 0
7272 then (
7273 Buffer.add_substring buf scratch ppos (nlpos-ppos);
7274 let s = Buffer.contents buf in
7275 Buffer.clear buf;
7276 ract s;
7277 eat (nlpos+1);
7279 else (
7280 Buffer.add_substring buf scratch ppos (n-ppos);
7281 Some fd
7283 in eat 0
7286 let remoteopen path =
7287 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
7288 with exn ->
7289 adderrfmt "remoteopen" "error opening %S: %s" path (exntos exn);
7290 None
7293 let () =
7294 let trimcachepath = ref "" in
7295 let rcmdpath = ref "" in
7296 Arg.parse
7297 (Arg.align
7298 [("-p", Arg.String (fun s -> state.password <- s),
7299 "<password> Set password");
7301 ("-f", Arg.String (fun s -> Config.fontpath := s),
7302 "<path> Set path to the user interface font");
7304 ("-c", Arg.String (fun s -> Config.confpath := s),
7305 "<path> Set path to the configuration file");
7307 ("-tcf", Arg.String (fun s -> trimcachepath := s),
7308 "<path> Set path to the trim cache file");
7310 ("-dest", Arg.String (fun s -> state.nameddest <- s),
7311 "<named-destination> Set named destination");
7313 ("-wtmode", Arg.Set wtmode, " Operate in wt mode");
7315 ("-remote", Arg.String (fun s -> rcmdpath := s),
7316 "<path> Set path to the remote commands source");
7318 ("-origin", Arg.String (fun s -> state.origin <- s),
7319 "<original_path> Set original path");
7321 ("-v", Arg.Unit (fun () ->
7322 Printf.printf
7323 "%s\nconfiguration path: %s\n"
7324 (version ())
7325 Config.defconfpath
7327 exit 0), " Print version and exit");
7330 (fun s -> state.path <- s)
7331 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
7333 if String.length state.path = 0
7334 then (prerr_endline "file name missing"; exit 1);
7336 if not (Config.load ())
7337 then prerr_endline "failed to load configuration";
7339 let globalkeyhash = findkeyhash conf "global" in
7340 let wsfd, winw, winh = Wsi.init (object
7341 method expose =
7342 if nogeomcmds state.geomcmds || platform == Posx
7343 then display ()
7344 else (
7345 GlClear.color (scalecolor2 conf.bgcolor);
7346 GlClear.clear [`color];
7348 method display = display ()
7349 method reshape w h = reshape w h
7350 method mouse b d x y m = mouse b d x y m
7351 method motion x y = state.mpos <- (x, y); motion x y
7352 method pmotion x y = state.mpos <- (x, y); pmotion x y
7353 method key k m =
7354 let mascm = m land (
7355 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
7356 ) in
7357 match state.keystate with
7358 | KSnone ->
7359 let km = k, mascm in
7360 begin
7361 match
7362 let modehash = state.uioh#modehash in
7363 try Hashtbl.find modehash km
7364 with Not_found ->
7365 try Hashtbl.find globalkeyhash km
7366 with Not_found -> KMinsrt (k, m)
7367 with
7368 | KMinsrt (k, m) -> keyboard k m
7369 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
7370 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
7372 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
7373 List.iter (fun (k, m) -> keyboard k m) insrt;
7374 state.keystate <- KSnone
7375 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
7376 state.keystate <- KSinto (keys, insrt)
7377 | _ ->
7378 state.keystate <- KSnone
7380 method enter x y = state.mpos <- (x, y); pmotion x y
7381 method leave = state.mpos <- (-1, -1)
7382 method winstate wsl =
7383 conf.cx <- state.x;
7384 state.winstate <- wsl
7385 method quit = raise Quit
7386 end) conf.cwinw conf.cwinh (platform = Posx) in
7388 state.wsfd <- wsfd;
7390 if not (
7391 List.exists GlMisc.check_extension
7392 [ "GL_ARB_texture_rectangle"
7393 ; "GL_EXT_texture_recangle"
7394 ; "GL_NV_texture_rectangle" ]
7396 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
7398 let cr, sw =
7399 match Ne.pipe () with
7400 | Ne.Exn exn ->
7401 Printf.eprintf "pipe/crsw failed: %s" (exntos exn);
7402 exit 1
7403 | Ne.Res rw -> rw
7404 and sr, cw =
7405 match Ne.pipe () with
7406 | Ne.Exn exn ->
7407 Printf.eprintf "pipe/srcw failed: %s" (exntos exn);
7408 exit 1
7409 | Ne.Res rw -> rw
7412 cloexec cr;
7413 cloexec sw;
7414 cloexec sr;
7415 cloexec cw;
7417 setcheckers conf.checkers;
7418 redirectstderr ();
7420 init (cr, cw) (
7421 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
7422 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
7423 !Config.fontpath, !trimcachepath,
7424 GlMisc.check_extension "GL_ARB_pixel_buffer_object"
7426 state.sr <- sr;
7427 state.sw <- sw;
7428 state.text <- "Opening " ^ (mbtoutf8 state.path);
7429 reshape winw winh;
7430 opendoc state.path state.password;
7431 state.uioh <- uioh;
7433 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
7434 let optrfd =
7435 ref (
7436 if String.length !rcmdpath > 0
7437 then remoteopen !rcmdpath
7438 else None
7442 let rec loop deadline =
7443 let r =
7444 match state.errfd with
7445 | None -> [state.sr; state.wsfd]
7446 | Some fd -> [state.sr; state.wsfd; fd]
7448 let r =
7449 match !optrfd with
7450 | None -> r
7451 | Some fd -> fd :: r
7453 if state.redisplay
7454 then (
7455 state.redisplay <- false;
7456 display ();
7458 let timeout =
7459 let now = now () in
7460 if deadline > now
7461 then (
7462 if deadline = infinity
7463 then ~-.1.0
7464 else max 0.0 (deadline -. now)
7466 else 0.0
7468 let r, _, _ =
7469 try Unix.select r [] [] timeout
7470 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
7472 begin match r with
7473 | [] ->
7474 state.ghyll None;
7475 let newdeadline =
7476 if state.ghyll == noghyll
7477 then
7478 match state.autoscroll with
7479 | Some step when step != 0 ->
7480 let y = state.y + step in
7481 let y =
7482 if y < 0
7483 then state.maxy
7484 else if y >= state.maxy then 0 else y
7486 gotoy y;
7487 if state.mode = View
7488 then state.text <- "";
7489 deadline +. 0.01
7490 | _ -> infinity
7491 else deadline +. 0.01
7493 loop newdeadline
7495 | l ->
7496 let rec checkfds = function
7497 | [] -> ()
7498 | fd :: rest when fd = state.sr ->
7499 let cmd = readcmd state.sr in
7500 act cmd;
7501 checkfds rest
7503 | fd :: rest when fd = state.wsfd ->
7504 Wsi.readresp fd;
7505 checkfds rest
7507 | fd :: rest when Some fd = !optrfd ->
7508 begin match remote fd with
7509 | None -> optrfd := remoteopen !rcmdpath;
7510 | opt -> optrfd := opt
7511 end;
7512 checkfds rest
7514 | fd :: rest ->
7515 let s = String.create 80 in
7516 let n = tempfailureretry (Unix.read fd s 0) 80 in
7517 if conf.redirectstderr
7518 then (
7519 Buffer.add_substring state.errmsgs s 0 n;
7520 state.newerrmsgs <- true;
7521 state.redisplay <- true;
7523 else (
7524 prerr_string (String.sub s 0 n);
7525 flush stderr;
7527 checkfds rest
7529 checkfds l;
7530 let newdeadline =
7531 let deadline1 =
7532 if deadline = infinity
7533 then now () +. 0.01
7534 else deadline
7536 match state.autoscroll with
7537 | Some step when step != 0 -> deadline1
7538 | _ -> if state.ghyll == noghyll then infinity else deadline1
7540 loop newdeadline
7541 end;
7544 loop infinity;
7545 with Quit ->
7546 Config.save ();