Remove pointless code
[llpp.git] / main.ml
blob61c6e847e06c1d48c5106a50dd1400bfc04f2e59
1 exception Quit;;
3 let tempfailureretry = Wsi.tempfailureretry;;
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 let dolog fmt = Printf.kprintf prerr_endline fmt;;
17 let now = Unix.gettimeofday;;
19 type params = (angle * proportional * trimparams
20 * texcount * sliceheight * memsize
21 * colorspace * fontpath * trimcachepath
22 * haspbo)
23 and pageno = int
24 and width = int
25 and height = int
26 and leftx = int
27 and opaque = string
28 and recttype = int
29 and pixmapsize = int
30 and angle = int
31 and proportional = bool
32 and trimmargins = bool
33 and interpagespace = int
34 and texcount = int
35 and sliceheight = int
36 and gen = int
37 and top = float
38 and dtop = float
39 and fontpath = string
40 and trimcachepath = string
41 and memsize = int
42 and aalevel = int
43 and irect = (int * int * int * int)
44 and trimparams = (trimmargins * irect)
45 and colorspace = | Rgb | Bgr | Gray
46 and haspbo = bool
49 type link =
50 | Lnotfound
51 | Lfound of int
52 and linkdir =
53 | LDfirst
54 | LDlast
55 | LDfirstvisible of (int * int * int)
56 | LDleft of int
57 | LDright of int
58 | LDdown of int
59 | LDup of int
62 type pagewithlinks =
63 | Pwlnotfound
64 | Pwl of int
67 type keymap =
68 | KMinsrt of key
69 | KMinsrl of key list
70 | KMmulti of key list * key list
71 and key = int * int
72 and keyhash = (key, keymap) Hashtbl.t
73 and keystate =
74 | KSnone
75 | KSinto of (key list * key list)
78 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
79 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
81 type pipe = (Unix.file_descr * Unix.file_descr);;
83 external init : pipe -> params -> unit = "ml_init";;
84 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
85 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
86 external getpdimrect : int -> float array = "ml_getpdimrect";;
87 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
88 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
89 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
90 external measurestr : int -> string -> float = "ml_measure_string";;
91 external getmaxw : unit -> float = "ml_getmaxw";;
92 external postprocess :
93 opaque -> int -> int -> int -> (int * string * int) -> int = "ml_postprocess";;
94 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
95 external platform : unit -> platform = "ml_platform";;
96 external setaalevel : int -> unit = "ml_setaalevel";;
97 external realloctexts : int -> bool = "ml_realloctexts";;
98 external cloexec : Unix.file_descr -> unit = "ml_cloexec";;
99 external findlink : opaque -> linkdir -> link = "ml_findlink";;
100 external getlink : opaque -> int -> under = "ml_getlink";;
101 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
102 external getlinkcount : opaque -> int = "ml_getlinkcount";;
103 external findpwl: int -> int -> pagewithlinks = "ml_find_page_with_links"
104 external popen : string -> (Unix.file_descr * int) list -> unit = "ml_popen";;
105 external mbtoutf8 : string -> string = "ml_mbtoutf8";;
106 external getpbo : width -> height -> colorspace -> string = "ml_getpbo";;
107 external freepbo : string -> unit = "ml_freepbo";;
108 external unmappbo : string -> unit = "ml_unmappbo";;
109 external pbousable : unit -> bool = "ml_pbo_usable";;
111 let platform_to_string = function
112 | Punknown -> "unknown"
113 | Plinux -> "Linux"
114 | Posx -> "OSX"
115 | Psun -> "Sun"
116 | Pfreebsd -> "FreeBSD"
117 | Pdragonflybsd -> "DragonflyBSD"
118 | Popenbsd -> "OpenBSD"
119 | Pnetbsd -> "NetBSD"
120 | Pcygwin -> "Cygwin"
123 let platform = platform ();;
125 let popen cmd fda =
126 if platform = Pcygwin
127 then (
128 let sh = "/bin/sh" in
129 let args = [|sh; "-c"; cmd|] in
130 let rec std si so se = function
131 | [] -> si, so, se
132 | (fd, 0) :: rest -> std fd so se rest
133 | (fd, -1) :: rest ->
134 Unix.set_close_on_exec fd;
135 std si so se rest
136 | (_, n) :: _ ->
137 failwith ("unexpected fdn in cygwin popen " ^ string_of_int n)
139 let si, so, se = std Unix.stdin Unix.stdout Unix.stderr fda in
140 ignore (Unix.create_process sh args si so se)
142 else popen cmd fda;
145 type x = int
146 and y = int
147 and tilex = int
148 and tiley = int
149 and tileparams = (x * y * width * height * tilex * tiley)
152 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
154 type mpos = int * int
155 and mstate =
156 | Msel of (mpos * mpos)
157 | Mpan of mpos
158 | Mscrolly | Mscrollx
159 | Mzoom of (int * int)
160 | Mzoomrect of (mpos * mpos)
161 | Mnone
164 type textentry = string * string * onhist option * onkey * ondone * cancelonempty
165 and onkey = string -> int -> te
166 and ondone = string -> unit
167 and histcancel = unit -> unit
168 and onhist = ((histcmd -> string) * histcancel)
169 and histcmd = HCnext | HCprev | HCfirst | HClast
170 and cancelonempty = bool
171 and te =
172 | TEstop
173 | TEdone of string
174 | TEcont of string
175 | TEswitch of textentry
178 type 'a circbuf =
179 { store : 'a array
180 ; mutable rc : int
181 ; mutable wc : int
182 ; mutable len : int
186 let bound v minv maxv =
187 max minv (min maxv v);
190 let cbnew n v =
191 { store = Array.create n v
192 ; rc = 0
193 ; wc = 0
194 ; len = 0
198 let cbcap b = Array.length b.store;;
200 let cbput b v =
201 let cap = cbcap b in
202 b.store.(b.wc) <- v;
203 b.wc <- (b.wc + 1) mod cap;
204 b.rc <- b.wc;
205 b.len <- min (b.len + 1) cap;
208 let cbempty b = b.len = 0;;
210 let cbgetg b circular dir =
211 if cbempty b
212 then b.store.(0)
213 else
214 let rc = b.rc + dir in
215 let rc =
216 if circular
217 then (
218 if rc = -1
219 then b.len-1
220 else (
221 if rc >= b.len
222 then 0
223 else rc
226 else bound rc 0 (b.len-1)
228 b.rc <- rc;
229 b.store.(rc);
232 let cbget b = cbgetg b false;;
233 let cbgetc b = cbgetg b true;;
235 let drawstring size x y s =
236 Gl.enable `blend;
237 Gl.enable `texture_2d;
238 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
239 ignore (drawstr size x y s);
240 Gl.disable `blend;
241 Gl.disable `texture_2d;
244 let drawstring1 size x y s =
245 drawstr size x y s;
248 let drawstring2 size x y fmt =
249 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
252 type page =
253 { pageno : int
254 ; pagedimno : int
255 ; pagew : int
256 ; pageh : int
257 ; pagex : int
258 ; pagey : int
259 ; pagevw : int
260 ; pagevh : int
261 ; pagedispx : int
262 ; pagedispy : int
263 ; pagecol : int
267 let debugl l =
268 dolog "l %d dim=%d {" l.pageno l.pagedimno;
269 dolog " WxH %dx%d" l.pagew l.pageh;
270 dolog " vWxH %dx%d" l.pagevw l.pagevh;
271 dolog " pagex,y %d,%d" l.pagex l.pagey;
272 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
273 dolog " column %d" l.pagecol;
274 dolog "}";
277 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
278 dolog "rect {";
279 dolog " x0,y0=(% f, % f)" x0 y0;
280 dolog " x1,y1=(% f, % f)" x1 y1;
281 dolog " x2,y2=(% f, % f)" x2 y2;
282 dolog " x3,y3=(% f, % f)" x3 y3;
283 dolog "}";
286 type multicolumns = multicol * pagegeom
287 and singlecolumn = pagegeom
288 and splitcolumns = columncount * pagegeom
289 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
290 and multicol = columncount * covercount * covercount
291 and pdimno = int
292 and columncount = int
293 and covercount = int;;
295 type conf =
296 { mutable scrollbw : int
297 ; mutable scrollh : int
298 ; mutable icase : bool
299 ; mutable preload : bool
300 ; mutable pagebias : int
301 ; mutable verbose : bool
302 ; mutable debug : bool
303 ; mutable scrollstep : int
304 ; mutable hscrollstep : int
305 ; mutable maxhfit : bool
306 ; mutable crophack : bool
307 ; mutable autoscrollstep : int
308 ; mutable maxwait : float option
309 ; mutable hlinks : bool
310 ; mutable underinfo : bool
311 ; mutable interpagespace : interpagespace
312 ; mutable zoom : float
313 ; mutable presentation : bool
314 ; mutable angle : angle
315 ; mutable winw : int
316 ; mutable winh : int
317 ; mutable savebmarks : bool
318 ; mutable proportional : proportional
319 ; mutable trimmargins : trimmargins
320 ; mutable trimfuzz : irect
321 ; mutable memlimit : memsize
322 ; mutable texcount : texcount
323 ; mutable sliceheight : sliceheight
324 ; mutable thumbw : width
325 ; mutable jumpback : bool
326 ; mutable bgcolor : float * float * float
327 ; mutable bedefault : bool
328 ; mutable scrollbarinpm : bool
329 ; mutable tilew : int
330 ; mutable tileh : int
331 ; mutable mustoresize : memsize
332 ; mutable checkers : bool
333 ; mutable aalevel : int
334 ; mutable urilauncher : string
335 ; mutable pathlauncher : string
336 ; mutable colorspace : colorspace
337 ; mutable invert : bool
338 ; mutable colorscale : float
339 ; mutable redirectstderr : bool
340 ; mutable ghyllscroll : (int * int * int) option
341 ; mutable columns : columns
342 ; mutable beyecolumns : columncount option
343 ; mutable selcmd : string
344 ; mutable updatecurs : bool
345 ; mutable keyhashes : (string * keyhash) list
346 ; mutable hfsize : int
347 ; mutable pgscale : float
348 ; mutable usepbo : bool
349 ; mutable wheelbypage : bool
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 end;;
386 type mode =
387 | Birdseye of (conf * leftx * pageno * pageno * anchor)
388 | Textentry of (textentry * onleave)
389 | View
390 | LinkNav of linktarget
391 and onleave = leavetextentrystatus -> unit
392 and leavetextentrystatus = | Cancel | Confirm
393 and helpitem = string * int * action
394 and action =
395 | Noaction
396 | Action of (uioh -> uioh)
397 and linktarget =
398 | Ltexact of (pageno * int)
399 | Ltgendir of int
402 let isbirdseye = function Birdseye _ -> true | _ -> false;;
403 let istextentry = function Textentry _ -> true | _ -> false;;
405 type currently =
406 | Idle
407 | Loading of (page * gen)
408 | Tiling of (
409 page * opaque * colorspace * angle * gen * col * row * width * height
411 | Outlining of outline list
414 let emptykeyhash = Hashtbl.create 0;;
415 let nouioh : uioh = object (self)
416 method display = ()
417 method key _ _ = self
418 method button _ _ _ _ _ = self
419 method motion _ _ = self
420 method pmotion _ _ = self
421 method infochanged _ = ()
422 method scrollpw = (0, nan, nan)
423 method scrollph = (0, nan, nan)
424 method modehash = emptykeyhash
425 end;;
427 type state =
428 { mutable sr : Unix.file_descr
429 ; mutable sw : Unix.file_descr
430 ; mutable wsfd : Unix.file_descr
431 ; mutable errfd : Unix.file_descr option
432 ; mutable stderr : Unix.file_descr
433 ; mutable errmsgs : Buffer.t
434 ; mutable newerrmsgs : bool
435 ; mutable w : int
436 ; mutable x : int
437 ; mutable y : int
438 ; mutable scrollw : int
439 ; mutable hscrollh : int
440 ; mutable anchor : anchor
441 ; mutable ranchors : (string * string * anchor) list
442 ; mutable maxy : int
443 ; mutable layout : page list
444 ; pagemap : (pagemapkey, opaque) Hashtbl.t
445 ; tilemap : (tilemapkey, tile) Hashtbl.t
446 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
447 ; mutable pdims : (pageno * width * height * leftx) list
448 ; mutable pagecount : int
449 ; mutable currently : currently
450 ; mutable mstate : mstate
451 ; mutable searchpattern : string
452 ; mutable rects : (pageno * recttype * rect) list
453 ; mutable rects1 : (pageno * recttype * rect) list
454 ; mutable text : string
455 ; mutable fullscreen : (width * height) option
456 ; mutable mode : mode
457 ; mutable uioh : uioh
458 ; mutable outlines : outline array
459 ; mutable bookmarks : outline list
460 ; mutable path : string
461 ; mutable password : string
462 ; mutable nameddest : string
463 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
464 ; mutable memused : memsize
465 ; mutable gen : gen
466 ; mutable throttle : (page list * int * float) option
467 ; mutable autoscroll : int option
468 ; mutable ghyll : (int option -> unit)
469 ; mutable help : helpitem array
470 ; mutable docinfo : (int * string) list
471 ; mutable texid : GlTex.texture_id option
472 ; hists : hists
473 ; mutable prevzoom : float
474 ; mutable progress : float
475 ; mutable redisplay : bool
476 ; mutable mpos : mpos
477 ; mutable keystate : keystate
478 ; mutable glinks : bool
479 ; mutable prevcolumns : (columns * float) option
480 ; mutable wthack : bool
482 and hists =
483 { pat : string circbuf
484 ; pag : string circbuf
485 ; nav : anchor circbuf
486 ; sel : string circbuf
490 let defconf =
491 { scrollbw = 7
492 ; scrollh = 12
493 ; icase = true
494 ; preload = true
495 ; pagebias = 0
496 ; verbose = false
497 ; debug = false
498 ; scrollstep = 24
499 ; hscrollstep = 24
500 ; maxhfit = true
501 ; crophack = false
502 ; autoscrollstep = 2
503 ; maxwait = None
504 ; hlinks = false
505 ; underinfo = false
506 ; interpagespace = 2
507 ; zoom = 1.0
508 ; presentation = false
509 ; angle = 0
510 ; winw = 900
511 ; winh = 900
512 ; savebmarks = true
513 ; proportional = true
514 ; trimmargins = false
515 ; trimfuzz = (0,0,0,0)
516 ; memlimit = 32 lsl 20
517 ; texcount = 256
518 ; sliceheight = 24
519 ; thumbw = 76
520 ; jumpback = true
521 ; bgcolor = (0.5, 0.5, 0.5)
522 ; bedefault = false
523 ; scrollbarinpm = true
524 ; tilew = 2048
525 ; tileh = 2048
526 ; mustoresize = 256 lsl 20
527 ; checkers = true
528 ; aalevel = 8
529 ; urilauncher =
530 (match platform with
531 | Plinux | Pfreebsd | Pdragonflybsd
532 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
533 | Posx -> "open \"%s\""
534 | Pcygwin -> "cygstart \"%s\""
535 | Punknown -> "echo %s")
536 ; pathlauncher = "lp \"%s\""
537 ; selcmd =
538 (match platform with
539 | Plinux | Pfreebsd | Pdragonflybsd
540 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
541 | Posx -> "pbcopy"
542 | Pcygwin -> "wsel"
543 | Punknown -> "cat")
544 ; colorspace = Rgb
545 ; invert = false
546 ; colorscale = 1.0
547 ; redirectstderr = false
548 ; ghyllscroll = None
549 ; columns = Csingle [||]
550 ; beyecolumns = None
551 ; updatecurs = false
552 ; hfsize = 12
553 ; pgscale = 1.0
554 ; usepbo = false
555 ; wheelbypage = false
556 ; keyhashes =
557 let mk n = (n, Hashtbl.create 1) in
558 [ mk "global"
559 ; mk "info"
560 ; mk "help"
561 ; mk "outline"
562 ; mk "listview"
563 ; mk "birdseye"
564 ; mk "textentry"
565 ; mk "links"
566 ; mk "view"
571 let findkeyhash c name =
572 try List.assoc name c.keyhashes
573 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
576 let conf = { defconf with angle = defconf.angle };;
578 let pgscale h = truncate (float h *. conf.pgscale);;
580 type fontstate =
581 { mutable fontsize : int
582 ; mutable wwidth : float
583 ; mutable maxrows : int
587 let fstate =
588 { fontsize = 14
589 ; wwidth = nan
590 ; maxrows = -1
594 let setfontsize n =
595 fstate.fontsize <- n;
596 fstate.wwidth <- measurestr fstate.fontsize "w";
597 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
600 let geturl s =
601 let colonpos = try String.index s ':' with Not_found -> -1 in
602 let len = String.length s in
603 if colonpos >= 0 && colonpos + 3 < len
604 then (
605 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
606 then
607 let schemestartpos =
608 try String.rindex_from s colonpos ' '
609 with Not_found -> -1
611 let scheme =
612 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
614 match scheme with
615 | "http" | "ftp" | "mailto" ->
616 let epos =
617 try String.index_from s colonpos ' '
618 with Not_found -> len
620 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
621 | _ -> ""
622 else ""
624 else ""
627 let gotouri uri =
628 if String.length conf.urilauncher = 0
629 then print_endline uri
630 else (
631 let url = geturl uri in
632 if String.length url = 0
633 then print_endline uri
634 else
635 let re = Str.regexp "%s" in
636 let command = Str.global_replace re url conf.urilauncher in
637 try popen command []
638 with exn ->
639 Printf.eprintf
640 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
641 flush stderr;
645 let version () =
646 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
647 (platform_to_string platform) Sys.word_size Sys.ocaml_version
650 let makehelp () =
651 let strings = version () :: "" :: Help.keys in
652 Array.of_list (
653 List.map (fun s ->
654 let url = geturl s in
655 if String.length url > 0
656 then (s, 0, Action (fun u -> gotouri url; u))
657 else (s, 0, Noaction)
658 ) strings);
661 let noghyll _ = ();;
662 let firstgeomcmds = "", [];;
664 let state =
665 { sr = Unix.stdin
666 ; sw = Unix.stdin
667 ; wsfd = Unix.stdin
668 ; errfd = None
669 ; stderr = Unix.stderr
670 ; errmsgs = Buffer.create 0
671 ; newerrmsgs = false
672 ; x = 0
673 ; y = 0
674 ; w = 0
675 ; scrollw = 0
676 ; hscrollh = 0
677 ; anchor = emptyanchor
678 ; ranchors = []
679 ; layout = []
680 ; maxy = max_int
681 ; tilelru = Queue.create ()
682 ; pagemap = Hashtbl.create 10
683 ; tilemap = Hashtbl.create 10
684 ; pdims = []
685 ; pagecount = 0
686 ; currently = Idle
687 ; mstate = Mnone
688 ; rects = []
689 ; rects1 = []
690 ; text = ""
691 ; mode = View
692 ; fullscreen = None
693 ; searchpattern = ""
694 ; outlines = [||]
695 ; bookmarks = []
696 ; path = ""
697 ; password = ""
698 ; nameddest = ""
699 ; geomcmds = firstgeomcmds
700 ; hists =
701 { nav = cbnew 10 emptyanchor
702 ; pat = cbnew 10 ""
703 ; pag = cbnew 10 ""
704 ; sel = cbnew 10 ""
706 ; memused = 0
707 ; gen = 0
708 ; throttle = None
709 ; autoscroll = None
710 ; ghyll = noghyll
711 ; help = makehelp ()
712 ; docinfo = []
713 ; texid = None
714 ; prevzoom = 1.0
715 ; progress = -1.0
716 ; uioh = nouioh
717 ; redisplay = true
718 ; mpos = (-1, -1)
719 ; keystate = KSnone
720 ; glinks = false
721 ; prevcolumns = None
722 ; wthack = false
726 let vlog fmt =
727 if conf.verbose
728 then
729 Printf.kprintf prerr_endline fmt
730 else
731 Printf.kprintf ignore fmt
734 let launchpath () =
735 if String.length conf.pathlauncher = 0
736 then print_endline state.path
737 else (
738 let re = Str.regexp "%s" in
739 let command = Str.global_replace re state.path conf.pathlauncher in
740 try popen command []
741 with exn ->
742 Printf.eprintf
743 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
744 flush stderr;
748 module Ne = struct
749 type 'a t = | Res of 'a | Exn of exn;;
751 let pipe () =
752 try Res (Unix.pipe ())
753 with exn -> Exn exn
756 let clo fd f =
757 try tempfailureretry Unix.close fd
758 with exn -> f (Printexc.to_string exn)
761 let dup fd =
762 try Res (tempfailureretry Unix.dup fd)
763 with exn -> Exn exn
766 let dup2 fd1 fd2 =
767 try Res (tempfailureretry (Unix.dup2 fd1) fd2)
768 with exn -> Exn exn
770 end;;
772 let redirectstderr () =
773 let clofail what errmsg = dolog "failed to close %s: %s" what errmsg in
774 if conf.redirectstderr
775 then
776 match Ne.pipe () with
777 | Ne.Exn exn ->
778 dolog "failed to create stderr redirection pipes: %s"
779 (Printexc.to_string exn)
781 | Ne.Res (r, w) ->
782 begin match Ne.dup Unix.stderr with
783 | Ne.Exn exn ->
784 dolog "failed to dup stderr: %s" (Printexc.to_string exn);
785 Ne.clo r (clofail "pipe/r");
786 Ne.clo w (clofail "pipe/w");
788 | Ne.Res dupstderr ->
789 begin match Ne.dup2 w Unix.stderr with
790 | Ne.Exn exn ->
791 dolog "failed to dup2 to stderr: %s"
792 (Printexc.to_string exn);
793 Ne.clo dupstderr (clofail "stderr duplicate");
794 Ne.clo r (clofail "redir pipe/r");
795 Ne.clo w (clofail "redir pipe/w");
797 | Ne.Res () ->
798 state.stderr <- dupstderr;
799 state.errfd <- Some r;
800 end;
802 else (
803 state.newerrmsgs <- false;
804 begin match state.errfd with
805 | Some fd ->
806 begin match Ne.dup2 state.stderr Unix.stderr with
807 | Ne.Exn exn ->
808 dolog "failed to dup2 original stderr: %s"
809 (Printexc.to_string exn)
810 | Ne.Res () ->
811 Ne.clo fd (clofail "dup of stderr");
812 state.errfd <- None;
813 end;
814 | None -> ()
815 end;
816 prerr_string (Buffer.contents state.errmsgs);
817 flush stderr;
818 Buffer.clear state.errmsgs;
822 module G =
823 struct
824 let postRedisplay who =
825 if conf.verbose
826 then prerr_endline ("redisplay for " ^ who);
827 state.redisplay <- true;
829 end;;
831 let getopaque pageno =
832 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
833 with Not_found -> None
836 let putopaque pageno opaque =
837 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
840 let pagetranslatepoint l x y =
841 let dy = y - l.pagedispy in
842 let y = dy + l.pagey in
843 let dx = x - l.pagedispx in
844 let x = dx + l.pagex in
845 (x, y);
848 let getunder x y =
849 let rec f = function
850 | l :: rest ->
851 begin match getopaque l.pageno with
852 | Some opaque ->
853 let x0 = l.pagedispx in
854 let x1 = x0 + l.pagevw in
855 let y0 = l.pagedispy in
856 let y1 = y0 + l.pagevh in
857 if y >= y0 && y <= y1 && x >= x0 && x <= x1
858 then
859 let px, py = pagetranslatepoint l x y in
860 match whatsunder opaque px py with
861 | Unone -> f rest
862 | under -> under
863 else f rest
864 | _ ->
865 f rest
867 | [] -> Unone
869 f state.layout
872 let showtext c s =
873 state.text <- Printf.sprintf "%c%s" c s;
874 G.postRedisplay "showtext";
877 let undertext = function
878 | Unone -> "none"
879 | Ulinkuri s -> s
880 | Ulinkgoto (pageno, _) -> Printf.sprintf "%s: page %d" state.path (pageno+1)
881 | Utext s -> "font: " ^ s
882 | Uunexpected s -> "unexpected: " ^ s
883 | Ulaunch s -> "launch: " ^ s
884 | Unamed s -> "named: " ^ s
885 | Uremote (filename, pageno) ->
886 Printf.sprintf "%s: page %d" filename (pageno+1)
889 let updateunder x y =
890 match getunder x y with
891 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
892 | Ulinkuri uri ->
893 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
894 Wsi.setcursor Wsi.CURSOR_INFO
895 | Ulinkgoto (pageno, _) ->
896 if conf.underinfo
897 then showtext 'p' ("age: " ^ string_of_int (pageno+1));
898 Wsi.setcursor Wsi.CURSOR_INFO
899 | Utext s ->
900 if conf.underinfo then showtext 'f' ("ont: " ^ s);
901 Wsi.setcursor Wsi.CURSOR_TEXT
902 | Uunexpected s ->
903 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
904 Wsi.setcursor Wsi.CURSOR_INHERIT
905 | Ulaunch s ->
906 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
907 Wsi.setcursor Wsi.CURSOR_INHERIT
908 | Unamed s ->
909 if conf.underinfo then showtext 'n' ("amed: " ^ s);
910 Wsi.setcursor Wsi.CURSOR_INHERIT
911 | Uremote (filename, pageno) ->
912 if conf.underinfo then showtext 'r'
913 (Printf.sprintf "emote: %s (%d)" filename (pageno+1));
914 Wsi.setcursor Wsi.CURSOR_INFO
917 let showlinktype under =
918 if conf.underinfo
919 then
920 match under with
921 | Unone -> ()
922 | under ->
923 let s = undertext under in
924 showtext ' ' s
927 let addchar s c =
928 let b = Buffer.create (String.length s + 1) in
929 Buffer.add_string b s;
930 Buffer.add_char b c;
931 Buffer.contents b;
934 let colorspace_of_string s =
935 match String.lowercase s with
936 | "rgb" -> Rgb
937 | "bgr" -> Bgr
938 | "gray" -> Gray
939 | _ -> failwith "invalid colorspace"
942 let int_of_colorspace = function
943 | Rgb -> 0
944 | Bgr -> 1
945 | Gray -> 2
948 let colorspace_of_int = function
949 | 0 -> Rgb
950 | 1 -> Bgr
951 | 2 -> Gray
952 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
955 let colorspace_to_string = function
956 | Rgb -> "rgb"
957 | Bgr -> "bgr"
958 | Gray -> "gray"
961 let intentry_with_suffix text key =
962 let c =
963 if key >= 32 && key < 127
964 then Char.chr key
965 else '\000'
967 match Char.lowercase c with
968 | '0' .. '9' ->
969 let text = addchar text c in
970 TEcont text
972 | 'k' | 'm' | 'g' ->
973 let text = addchar text c in
974 TEcont text
976 | _ ->
977 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
978 TEcont text
981 let multicolumns_to_string (n, a, b) =
982 if a = 0 && b = 0
983 then Printf.sprintf "%d" n
984 else Printf.sprintf "%d,%d,%d" n a b;
987 let multicolumns_of_string s =
989 (int_of_string s, 0, 0)
990 with _ ->
991 Scanf.sscanf s "%u,%u,%u" (fun n a b ->
992 if a > 1 || b > 1
993 then failwith "subtly broken"; (n, a, b)
997 let readcmd fd =
998 let s = "xxxx" in
999 let n = tempfailureretry (Unix.read fd s 0) 4 in
1000 if n != 4 then failwith "incomplete read(len)";
1001 let len = 0
1002 lor (Char.code s.[0] lsl 24)
1003 lor (Char.code s.[1] lsl 16)
1004 lor (Char.code s.[2] lsl 8)
1005 lor (Char.code s.[3] lsl 0)
1007 let s = String.create len in
1008 let n = tempfailureretry (Unix.read fd s 0) len in
1009 if n != len then failwith "incomplete read(data)";
1013 let btod b = if b then 1 else 0;;
1015 let wcmd fmt =
1016 let b = Buffer.create 16 in
1017 Buffer.add_string b "llll";
1018 Printf.kbprintf
1019 (fun b ->
1020 let s = Buffer.contents b in
1021 let n = String.length s in
1022 let len = n - 4 in
1023 (* dolog "wcmd %S" (String.sub s 4 len); *)
1024 s.[0] <- Char.chr ((len lsr 24) land 0xff);
1025 s.[1] <- Char.chr ((len lsr 16) land 0xff);
1026 s.[2] <- Char.chr ((len lsr 8) land 0xff);
1027 s.[3] <- Char.chr (len land 0xff);
1028 let n' = tempfailureretry (Unix.write state.sw s 0) n in
1029 if n' != n then failwith "write failed";
1030 ) b fmt;
1033 let calcips h =
1034 let d = conf.winh - h in
1035 max conf.interpagespace ((d + 1) / 2)
1038 let rowyh (c, coverA, coverB) b n =
1039 if c = 1 || (n < coverA || n >= state.pagecount - coverB)
1040 then
1041 let _, _, vy, (_, _, h, _) = b.(n) in
1042 (vy, h)
1043 else
1044 let n' = n - coverA in
1045 let d = n' mod c in
1046 let s = n - d in
1047 let e = min state.pagecount (s + c) in
1048 let rec find m miny maxh = if m = e then miny, maxh else
1049 let _, _, y, (_, _, h, _) = b.(m) in
1050 let miny = min miny y in
1051 let maxh = max maxh h in
1052 find (m+1) miny maxh
1053 in find s max_int 0
1056 let calcheight () =
1057 match conf.columns with
1058 | Cmulti ((_, _, _) as cl, b) ->
1059 if Array.length b > 0
1060 then
1061 let y, h = rowyh cl b (Array.length b - 1) in
1062 y + h + (if conf.presentation then calcips h else 0)
1063 else 0
1064 | Csingle b ->
1065 if Array.length b > 0
1066 then
1067 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1068 y + h + (if conf.presentation then calcips h else 0)
1069 else 0
1070 | Csplit (_, b) ->
1071 if Array.length b > 0
1072 then
1073 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1074 y + h
1075 else 0
1078 let getpageyh pageno =
1079 let pageno = bound pageno 0 (state.pagecount-1) in
1080 match conf.columns with
1081 | Csingle b ->
1082 if Array.length b = 0
1083 then 0, 0
1084 else
1085 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1086 let y =
1087 if conf.presentation
1088 then y - calcips h
1089 else y
1091 y, h
1092 | Cmulti (cl, b) ->
1093 if Array.length b = 0
1094 then 0, 0
1095 else
1096 let y, h = rowyh cl b pageno in
1097 let y =
1098 if conf.presentation
1099 then y - calcips h
1100 else y
1102 y, h
1103 | Csplit (c, b) ->
1104 if Array.length b = 0
1105 then 0, 0
1106 else
1107 let n = pageno*c in
1108 let (_, _, y, (_, _, h, _)) = b.(n) in
1109 y, h
1112 let getpagedim pageno =
1113 let rec f ppdim l =
1114 match l with
1115 | (n, _, _, _) as pdim :: rest ->
1116 if n >= pageno
1117 then (if n = pageno then pdim else ppdim)
1118 else f pdim rest
1120 | [] -> ppdim
1122 f (-1, -1, -1, -1) state.pdims
1125 let getpagey pageno = fst (getpageyh pageno);;
1127 let nogeomcmds cmds =
1128 match cmds with
1129 | s, [] -> String.length s = 0
1130 | _ -> false
1133 let page_of_y y =
1134 let ((c, coverA, coverB) as cl), b =
1135 match conf.columns with
1136 | Csingle b -> (1, 0, 0), b
1137 | Cmulti (c, b) -> c, b
1138 | Csplit (_, b) -> (1, 0, 0), b
1140 let rec bsearch nmin nmax =
1141 if nmin > nmax
1142 then bound nmin 0 (state.pagecount-1)
1143 else
1144 let n = (nmax + nmin) / 2 in
1145 let vy, h = rowyh cl b n in
1146 let y0, y1 =
1147 if conf.presentation
1148 then
1149 let ips = calcips h in
1150 let y0 = vy - ips in
1151 let y1 = vy + h + ips in
1152 y0, y1
1153 else (
1154 if n = 0
1155 then 0, vy + h + conf.interpagespace
1156 else
1157 let y0 = vy - conf.interpagespace in
1158 y0, y0 + h + conf.interpagespace
1161 if y >= y0 && y < y1
1162 then (
1163 if c = 1
1164 then n
1165 else (
1166 if n > coverA
1167 then
1168 if n < state.pagecount - coverB
1169 then ((n-coverA)/c)*c + coverA
1170 else n
1171 else n
1174 else (
1175 if y > y0
1176 then bsearch (n+1) nmax
1177 else bsearch nmin (n-1)
1180 let r = bsearch 0 (state.pagecount-1) in
1184 let layoutN ((columns, coverA, coverB), b) y sh =
1185 let sh = sh - state.hscrollh in
1186 let rec fold accu n =
1187 if n = Array.length b
1188 then accu
1189 else
1190 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1191 if (vy - y) > sh &&
1192 (n = coverA - 1
1193 || n = state.pagecount - coverB
1194 || (n - coverA) mod columns = columns - 1)
1195 then accu
1196 else
1197 let accu =
1198 if vy + h > y
1199 then
1200 let pagey = max 0 (y - vy) in
1201 let pagedispy = if pagey > 0 then 0 else vy - y in
1202 let pagedispx, pagex =
1203 let pdx =
1204 if n = coverA - 1 || n = state.pagecount - coverB
1205 then state.x + (conf.winw - state.scrollw - w) / 2
1206 else dx + xoff + state.x
1208 if pdx < 0
1209 then 0, -pdx
1210 else pdx, 0
1212 let pagevw =
1213 let vw = conf.winw - state.scrollw - pagedispx in
1214 let pw = w - pagex in
1215 min vw pw
1217 let pagevh = min (h - pagey) (sh - pagedispy) in
1218 if pagevw > 0 && pagevh > 0
1219 then
1220 let e =
1221 { pageno = n
1222 ; pagedimno = pdimno
1223 ; pagew = w
1224 ; pageh = h
1225 ; pagex = pagex
1226 ; pagey = pagey
1227 ; pagevw = pagevw
1228 ; pagevh = pagevh
1229 ; pagedispx = pagedispx
1230 ; pagedispy = pagedispy
1231 ; pagecol = 0
1234 e :: accu
1235 else
1236 accu
1237 else
1238 accu
1240 fold accu (n+1)
1242 List.rev (fold [] (page_of_y y));
1245 let layoutS (columns, b) y sh =
1246 let sh = sh - state.hscrollh in
1247 let rec fold accu n =
1248 if n = Array.length b
1249 then accu
1250 else
1251 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1252 if (vy - y) > sh
1253 then accu
1254 else
1255 let accu =
1256 if vy + pageh > y
1257 then
1258 let x = xoff + state.x in
1259 let pagey = max 0 (y - vy) in
1260 let pagedispy = if pagey > 0 then 0 else vy - y in
1261 let pagedispx, pagex =
1262 if px = 0
1263 then (
1264 if x < 0
1265 then 0, -x
1266 else x, 0
1268 else (
1269 let px = px - x in
1270 if px < 0
1271 then -px, 0
1272 else 0, px
1275 let pagecolw = pagew/columns in
1276 let pagedispx =
1277 if pagecolw < conf.winw
1278 then pagedispx + ((conf.winw - state.scrollw - pagecolw) / 2)
1279 else pagedispx
1281 let pagevw =
1282 let vw = conf.winw - pagedispx - state.scrollw in
1283 let pw = pagew - pagex in
1284 min vw pw
1286 let pagevw = min pagevw pagecolw in
1287 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1288 if pagevw > 0 && pagevh > 0
1289 then
1290 let e =
1291 { pageno = n/columns
1292 ; pagedimno = pdimno
1293 ; pagew = pagew
1294 ; pageh = pageh
1295 ; pagex = pagex
1296 ; pagey = pagey
1297 ; pagevw = pagevw
1298 ; pagevh = pagevh
1299 ; pagedispx = pagedispx
1300 ; pagedispy = pagedispy
1301 ; pagecol = n mod columns
1304 e :: accu
1305 else
1306 accu
1307 else
1308 accu
1310 fold accu (n+1)
1312 List.rev (fold [] 0)
1315 let layout y sh =
1316 if nogeomcmds state.geomcmds
1317 then
1318 match conf.columns with
1319 | Csingle b -> layoutN ((1, 0, 0), b) y sh
1320 | Cmulti c -> layoutN c y sh
1321 | Csplit s -> layoutS s y sh
1322 else []
1325 let clamp incr =
1326 let y = state.y + incr in
1327 let y = max 0 y in
1328 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1332 let itertiles l f =
1333 let tilex = l.pagex mod conf.tilew in
1334 let tiley = l.pagey mod conf.tileh in
1336 let col = l.pagex / conf.tilew in
1337 let row = l.pagey / conf.tileh in
1339 let rec rowloop row y0 dispy h =
1340 if h = 0
1341 then ()
1342 else (
1343 let dh = conf.tileh - y0 in
1344 let dh = min h dh in
1345 let rec colloop col x0 dispx w =
1346 if w = 0
1347 then ()
1348 else (
1349 let dw = conf.tilew - x0 in
1350 let dw = min w dw in
1352 f col row dispx dispy x0 y0 dw dh;
1353 colloop (col+1) 0 (dispx+dw) (w-dw)
1356 colloop col tilex l.pagedispx l.pagevw;
1357 rowloop (row+1) 0 (dispy+dh) (h-dh)
1360 if l.pagevw > 0 && l.pagevh > 0
1361 then rowloop row tiley l.pagedispy l.pagevh;
1364 let gettileopaque l col row =
1365 let key =
1366 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1368 try Some (Hashtbl.find state.tilemap key)
1369 with Not_found -> None
1372 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1373 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1374 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1377 let drawtiles l color =
1378 GlDraw.color color;
1379 let f col row x y tilex tiley w h =
1380 match gettileopaque l col row with
1381 | Some (opaque, _, t) ->
1382 let params = x, y, w, h, tilex, tiley in
1383 if conf.invert
1384 then (
1385 Gl.enable `blend;
1386 GlFunc.blend_func `zero `one_minus_src_color;
1388 drawtile params opaque;
1389 if conf.invert
1390 then Gl.disable `blend;
1391 if conf.debug
1392 then (
1393 let s = Printf.sprintf
1394 "%d[%d,%d] %f sec"
1395 l.pageno col row t
1397 let w = measurestr fstate.fontsize s in
1398 GlMisc.push_attrib [`current];
1399 GlDraw.color (0.0, 0.0, 0.0);
1400 GlDraw.rect
1401 (float (x-2), float (y-2))
1402 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1403 GlDraw.color (1.0, 1.0, 1.0);
1404 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1405 GlMisc.pop_attrib ();
1408 | _ ->
1409 let w =
1410 let lw = conf.winw - state.scrollw - x in
1411 min lw w
1412 and h =
1413 let lh = conf.winh - y in
1414 min lh h
1416 begin match state.texid with
1417 | Some id ->
1418 Gl.enable `texture_2d;
1419 GlTex.bind_texture `texture_2d id;
1420 let x0 = float x
1421 and y0 = float y
1422 and x1 = float (x+w)
1423 and y1 = float (y+h) in
1425 let tw = float w /. 16.0
1426 and th = float h /. 16.0 in
1427 let tx0 = float tilex /. 16.0
1428 and ty0 = float tiley /. 16.0 in
1429 let tx1 = tx0 +. tw
1430 and ty1 = ty0 +. th in
1431 GlDraw.begins `quads;
1432 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1433 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1434 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1435 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1436 GlDraw.ends ();
1438 Gl.disable `texture_2d;
1439 | None ->
1440 GlDraw.color (1.0, 1.0, 1.0);
1441 GlDraw.rect
1442 (float x, float y)
1443 (float (x+w), float (y+h));
1444 end;
1445 if w > 128 && h > fstate.fontsize + 10
1446 then (
1447 GlDraw.color (0.0, 0.0, 0.0);
1448 let c, r =
1449 if conf.verbose
1450 then (col*conf.tilew, row*conf.tileh)
1451 else col, row
1453 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1455 GlDraw.color color;
1457 itertiles l f
1460 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1462 let tilevisible1 l x y =
1463 let ax0 = l.pagex
1464 and ax1 = l.pagex + l.pagevw
1465 and ay0 = l.pagey
1466 and ay1 = l.pagey + l.pagevh in
1468 let bx0 = x
1469 and by0 = y in
1470 let bx1 = min (bx0 + conf.tilew) l.pagew
1471 and by1 = min (by0 + conf.tileh) l.pageh in
1473 let rx0 = max ax0 bx0
1474 and ry0 = max ay0 by0
1475 and rx1 = min ax1 bx1
1476 and ry1 = min ay1 by1 in
1478 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1479 nonemptyintersection
1482 let tilevisible layout n x y =
1483 let rec findpageinlayout m = function
1484 | l :: rest when l.pageno = n ->
1485 tilevisible1 l x y || (
1486 match conf.columns with
1487 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
1488 | _ -> false
1490 | _ :: rest -> findpageinlayout 0 rest
1491 | [] -> false
1493 findpageinlayout 0 layout;
1496 let tileready l x y =
1497 tilevisible1 l x y &&
1498 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1501 let tilepage n p layout =
1502 let rec loop = function
1503 | l :: rest ->
1504 if l.pageno = n
1505 then
1506 let f col row _ _ _ _ _ _ =
1507 if state.currently = Idle
1508 then
1509 match gettileopaque l col row with
1510 | Some _ -> ()
1511 | None ->
1512 let x = col*conf.tilew
1513 and y = row*conf.tileh in
1514 let w =
1515 let w = l.pagew - x in
1516 min w conf.tilew
1518 let h =
1519 let h = l.pageh - y in
1520 min h conf.tileh
1522 let pbo =
1523 if conf.usepbo
1524 then getpbo w h conf.colorspace
1525 else "0"
1527 wcmd "tile %s %d %d %d %d %s" p x y w h pbo;
1528 state.currently <-
1529 Tiling (
1530 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1531 conf.tilew, conf.tileh
1534 itertiles l f;
1535 else
1536 loop rest
1538 | [] -> ()
1540 if nogeomcmds state.geomcmds
1541 then loop layout;
1544 let preloadlayout y =
1545 let y = if y < conf.winh then 0 else y - conf.winh in
1546 let h = conf.winh*3 in
1547 layout y h;
1550 let load pages =
1551 let rec loop pages =
1552 if state.currently != Idle
1553 then ()
1554 else
1555 match pages with
1556 | l :: rest ->
1557 begin match getopaque l.pageno with
1558 | None ->
1559 wcmd "page %d %d" l.pageno l.pagedimno;
1560 state.currently <- Loading (l, state.gen);
1561 | Some opaque ->
1562 tilepage l.pageno opaque pages;
1563 loop rest
1564 end;
1565 | _ -> ()
1567 if nogeomcmds state.geomcmds
1568 then loop pages
1571 let preload pages =
1572 load pages;
1573 if conf.preload && state.currently = Idle
1574 then load (preloadlayout state.y);
1577 let layoutready layout =
1578 let rec fold all ls =
1579 all && match ls with
1580 | l :: rest ->
1581 let seen = ref false in
1582 let allvisible = ref true in
1583 let foo col row _ _ _ _ _ _ =
1584 seen := true;
1585 allvisible := !allvisible &&
1586 begin match gettileopaque l col row with
1587 | Some _ -> true
1588 | None -> false
1591 itertiles l foo;
1592 fold (!seen && !allvisible) rest
1593 | [] -> true
1595 let alltilesvisible = fold true layout in
1596 alltilesvisible;
1599 let gotoy y =
1600 let y = bound y 0 state.maxy in
1601 let y, layout, proceed =
1602 match conf.maxwait with
1603 | Some time when state.ghyll == noghyll ->
1604 begin match state.throttle with
1605 | None ->
1606 let layout = layout y conf.winh in
1607 let ready = layoutready layout in
1608 if not ready
1609 then (
1610 load layout;
1611 state.throttle <- Some (layout, y, now ());
1613 else G.postRedisplay "gotoy showall (None)";
1614 y, layout, ready
1615 | Some (_, _, started) ->
1616 let dt = now () -. started in
1617 if dt > time
1618 then (
1619 state.throttle <- None;
1620 let layout = layout y conf.winh in
1621 load layout;
1622 G.postRedisplay "maxwait";
1623 y, layout, true
1625 else -1, [], false
1628 | _ ->
1629 let layout = layout y conf.winh in
1630 if true || layoutready layout
1631 then G.postRedisplay "gotoy ready";
1632 y, layout, true
1634 if proceed
1635 then (
1636 state.y <- y;
1637 state.layout <- layout;
1638 begin match state.mode with
1639 | LinkNav (Ltexact (pageno, linkno)) ->
1640 let rec loop = function
1641 | [] ->
1642 state.mode <- LinkNav (Ltgendir 0)
1643 | l :: _ when l.pageno = pageno ->
1644 begin match getopaque pageno with
1645 | None ->
1646 state.mode <- LinkNav (Ltgendir 0)
1647 | Some opaque ->
1648 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1649 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1650 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1651 then state.mode <- LinkNav (Ltgendir 0)
1653 | _ :: rest -> loop rest
1655 loop layout
1656 | _ -> ()
1657 end;
1658 begin match state.mode with
1659 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1660 if not (pagevisible layout pageno)
1661 then (
1662 match state.layout with
1663 | [] -> ()
1664 | l :: _ ->
1665 state.mode <- Birdseye (
1666 conf, leftx, l.pageno, hooverpageno, anchor
1669 | LinkNav (Ltgendir dir as lt) ->
1670 let linknav =
1671 let rec loop = function
1672 | [] -> lt
1673 | l :: rest ->
1674 match getopaque l.pageno with
1675 | None -> loop rest
1676 | Some opaque ->
1677 let link =
1678 let ld =
1679 if dir = 0
1680 then LDfirstvisible (l.pagex, l.pagey, dir)
1681 else (
1682 if dir > 0 then LDfirst else LDlast
1685 findlink opaque ld
1687 match link with
1688 | Lnotfound -> loop rest
1689 | Lfound n ->
1690 showlinktype (getlink opaque n);
1691 Ltexact (l.pageno, n)
1693 loop state.layout
1695 state.mode <- LinkNav linknav
1696 | _ -> ()
1697 end;
1698 preload layout;
1700 state.ghyll <- noghyll;
1701 if conf.updatecurs
1702 then (
1703 let mx, my = state.mpos in
1704 updateunder mx my;
1708 let conttiling pageno opaque =
1709 tilepage pageno opaque
1710 (if conf.preload then preloadlayout state.y else state.layout)
1713 let gotoy_and_clear_text y =
1714 if not conf.verbose then state.text <- "";
1715 gotoy y;
1718 let getanchor1 l =
1719 let top =
1720 let coloff = l.pagecol * l.pageh in
1721 float (l.pagey + coloff) /. float l.pageh
1723 let dtop =
1724 if l.pagedispy = 0
1725 then
1727 else
1728 if conf.presentation
1729 then float l.pagedispy /. float (calcips l.pageh)
1730 else float l.pagedispy /. float conf.interpagespace
1732 (l.pageno, top, dtop)
1735 let getanchor () =
1736 match state.layout with
1737 | l :: _ -> getanchor1 l
1738 | [] ->
1739 let n = page_of_y state.y in
1740 let y, h = getpageyh n in
1741 let dy = y - state.y in
1742 let dtop =
1743 if conf.presentation
1744 then
1745 let ips = calcips h in
1746 float (dy + ips) /. float ips
1747 else
1748 float dy /. float conf.interpagespace
1750 (n, 0.0, dtop)
1753 let getanchory (n, top, dtop) =
1754 let y, h = getpageyh n in
1755 if conf.presentation
1756 then
1757 let ips = calcips h in
1758 y + truncate (top*.float h -. dtop*.float ips) + ips;
1759 else
1760 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
1763 let gotoanchor anchor =
1764 gotoy (getanchory anchor);
1767 let addnav () =
1768 cbput state.hists.nav (getanchor ());
1771 let getnav dir =
1772 let anchor = cbgetc state.hists.nav dir in
1773 getanchory anchor;
1776 let gotoghyll y =
1777 let scroll f n a b =
1778 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1779 let snake f a b =
1780 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1781 if f < a
1782 then s (float f /. float a)
1783 else (
1784 if f > b
1785 then 1.0 -. s ((float (f-b) /. float (n-b)))
1786 else 1.0
1789 snake f a b
1790 and summa f n a b =
1791 (* courtesy:
1792 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1793 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1794 let iv1 = iv f in
1795 let ins = float a *. iv1
1796 and outs = float (n-b) *. iv1 in
1797 let ones = b - a in
1798 ins +. outs +. float ones
1800 let rec set (_N, _A, _B) y sy =
1801 let sum = summa 1.0 _N _A _B in
1802 let dy = float (y - sy) in
1803 state.ghyll <- (
1804 let rec gf n y1 o =
1805 if n >= _N
1806 then state.ghyll <- noghyll
1807 else
1808 let go n =
1809 let s = scroll n _N _A _B in
1810 let y1 = y1 +. ((s *. dy) /. sum) in
1811 gotoy_and_clear_text (truncate y1);
1812 state.ghyll <- gf (n+1) y1;
1814 match o with
1815 | None -> go n
1816 | Some y' -> set (_N/2, 1, 1) y' state.y
1818 gf 0 (float state.y)
1821 match conf.ghyllscroll with
1822 | None ->
1823 gotoy_and_clear_text y
1824 | Some nab ->
1825 if state.ghyll == noghyll
1826 then set nab y state.y
1827 else state.ghyll (Some y)
1830 let gotopage n top =
1831 let y, h = getpageyh n in
1832 let y = y + (truncate (top *. float h)) in
1833 gotoghyll y
1836 let gotopage1 n top =
1837 let y = getpagey n in
1838 let y = y + top in
1839 gotoghyll y
1842 let invalidate s f =
1843 state.layout <- [];
1844 state.pdims <- [];
1845 state.rects <- [];
1846 state.rects1 <- [];
1847 match state.geomcmds with
1848 | ps, [] when String.length ps = 0 ->
1849 f ();
1850 state.geomcmds <- s, [];
1852 | ps, [] ->
1853 state.geomcmds <- ps, [s, f];
1855 | ps, (s', _) :: rest when s' = s ->
1856 state.geomcmds <- ps, ((s, f) :: rest);
1858 | ps, cmds ->
1859 state.geomcmds <- ps, ((s, f) :: cmds);
1862 let flushpages () =
1863 Hashtbl.iter (fun _ opaque ->
1864 wcmd "freepage %s" opaque;
1865 ) state.pagemap;
1866 Hashtbl.clear state.pagemap;
1869 let opendoc path password nameddest =
1870 state.path <- path;
1871 state.password <- password;
1872 state.gen <- state.gen + 1;
1873 state.docinfo <- [];
1874 state.nameddest <- nameddest;
1876 flushpages ();
1877 setaalevel conf.aalevel;
1878 Wsi.settitle ("llpp " ^ (mbtoutf8 (Filename.basename path)));
1879 wcmd "open %d %s\000%s\000%s\000" (btod state.wthack) path password nameddest;
1880 invalidate "reqlayout"
1881 (fun () ->
1882 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1885 let reload () =
1886 state.anchor <- getanchor ();
1887 state.wthack <- true;
1888 opendoc state.path state.password state.nameddest;
1891 let scalecolor c =
1892 let c = c *. conf.colorscale in
1893 (c, c, c);
1896 let scalecolor2 (r, g, b) =
1897 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1900 let docolumns = function
1901 | Csingle _ ->
1902 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1903 let rec loop pageno pdimno pdim y ph pdims =
1904 if pageno = state.pagecount
1905 then ()
1906 else
1907 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1908 match pdims with
1909 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1910 pdimno+1, pdim, rest
1911 | _ ->
1912 pdimno, pdim, pdims
1914 let x = max 0 (((conf.winw - state.scrollw - w) / 2) - xoff) in
1915 let y = y +
1916 (if conf.presentation
1917 then (if pageno = 0 then calcips h else calcips ph + calcips h)
1918 else (if pageno = 0 then 0 else conf.interpagespace)
1921 a.(pageno) <- (pdimno, x, y, pdim);
1922 loop (pageno+1) pdimno pdim (y + h) h pdims
1924 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
1925 conf.columns <- Csingle a;
1927 | Cmulti ((columns, coverA, coverB), _) ->
1928 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1929 let rec loop pageno pdimno pdim x y rowh pdims =
1930 let rec fixrow m = if m = pageno then () else
1931 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1932 if h < rowh
1933 then (
1934 let y = y + (rowh - h) / 2 in
1935 a.(m) <- (pdimno, x, y, pdim);
1937 fixrow (m+1)
1939 if pageno = state.pagecount
1940 then fixrow (((pageno - 1) / columns) * columns)
1941 else
1942 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1943 match pdims with
1944 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1945 pdimno+1, pdim, rest
1946 | _ ->
1947 pdimno, pdim, pdims
1949 let x, y, rowh' =
1950 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1951 then (
1952 let x = (conf.winw - state.scrollw - w) / 2 in
1953 let ips =
1954 if conf.presentation then calcips h else conf.interpagespace in
1955 x, y + ips + rowh, h
1957 else (
1958 if (pageno - coverA) mod columns = 0
1959 then (
1960 let x = max 0 (conf.winw - state.scrollw - state.w) / 2 in
1961 let y =
1962 if conf.presentation
1963 then
1964 let ips = calcips h in
1965 y + (if pageno = 0 then 0 else calcips rowh + ips)
1966 else
1967 y + (if pageno = 0 then 0 else conf.interpagespace)
1969 x, y + rowh, h
1971 else x, y, max rowh h
1974 let y =
1975 if pageno > 1 && (pageno - coverA) mod columns = 0
1976 then (
1977 let y =
1978 if pageno = columns && conf.presentation
1979 then (
1980 let ips = calcips rowh in
1981 for i = 0 to pred columns
1983 let (pdimno, x, y, pdim) = a.(i) in
1984 a.(i) <- (pdimno, x, y+ips, pdim)
1985 done;
1986 y+ips;
1988 else y
1990 fixrow (pageno - columns);
1993 else y
1995 a.(pageno) <- (pdimno, x, y, pdim);
1996 let x = x + w + xoff*2 + conf.interpagespace in
1997 loop (pageno+1) pdimno pdim x y rowh' pdims
1999 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
2000 conf.columns <- Cmulti ((columns, coverA, coverB), a);
2002 | Csplit (c, _) ->
2003 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
2004 let rec loop pageno pdimno pdim y pdims =
2005 if pageno = state.pagecount
2006 then ()
2007 else
2008 let pdimno, ((_, w, h, _) as pdim), pdims =
2009 match pdims with
2010 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2011 pdimno+1, pdim, rest
2012 | _ ->
2013 pdimno, pdim, pdims
2015 let cw = w / c in
2016 let rec loop1 n x y =
2017 if n = c then y else (
2018 a.(pageno*c + n) <- (pdimno, x, y, pdim);
2019 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
2022 let y = loop1 0 0 y in
2023 loop (pageno+1) pdimno pdim y pdims
2025 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
2026 conf.columns <- Csplit (c, a);
2029 let represent () =
2030 docolumns conf.columns;
2031 state.maxy <- calcheight ();
2032 state.hscrollh <-
2033 if state.w <= conf.winw - state.scrollw
2034 then 0
2035 else state.scrollw
2037 match state.mode with
2038 | Birdseye (_, _, pageno, _, _) ->
2039 let y, h = getpageyh pageno in
2040 let top = (conf.winh - h) / 2 in
2041 gotoy (max 0 (y - top))
2042 | _ -> gotoanchor state.anchor
2045 let reshape w h =
2046 state.wthack <- false;
2047 GlDraw.viewport 0 0 w h;
2048 let firsttime = state.geomcmds == firstgeomcmds in
2049 if not firsttime && nogeomcmds state.geomcmds
2050 then state.anchor <- getanchor ();
2052 conf.winw <- w;
2053 let w = truncate (float w *. conf.zoom) - state.scrollw in
2054 let w = max w 2 in
2055 conf.winh <- h;
2056 setfontsize fstate.fontsize;
2057 GlMat.mode `modelview;
2058 GlMat.load_identity ();
2060 GlMat.mode `projection;
2061 GlMat.load_identity ();
2062 GlMat.rotate ~x:1.0 ~angle:180.0 ();
2063 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
2064 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
2066 let relx =
2067 if conf.zoom <= 1.0
2068 then 0.0
2069 else float state.x /. float state.w
2071 invalidate "geometry"
2072 (fun () ->
2073 state.w <- w;
2074 if not firsttime
2075 then state.x <- truncate (relx *. float w);
2076 let w =
2077 match conf.columns with
2078 | Csingle _ -> w
2079 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
2080 | Csplit (c, _) -> w * c
2082 wcmd "geometry %d %d" w h);
2085 let enttext () =
2086 let len = String.length state.text in
2087 let drawstring s =
2088 let hscrollh =
2089 match state.mode with
2090 | Textentry _
2091 | View ->
2092 let h, _, _ = state.uioh#scrollpw in
2094 | _ -> 0
2096 let rect x w =
2097 GlDraw.rect
2098 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
2099 (x+.w, float (conf.winh - hscrollh))
2102 let w = float (conf.winw - state.scrollw - 1) in
2103 if state.progress >= 0.0 && state.progress < 1.0
2104 then (
2105 GlDraw.color (0.3, 0.3, 0.3);
2106 let w1 = w *. state.progress in
2107 rect 0.0 w1;
2108 GlDraw.color (0.0, 0.0, 0.0);
2109 rect w1 (w-.w1)
2111 else (
2112 GlDraw.color (0.0, 0.0, 0.0);
2113 rect 0.0 w;
2116 GlDraw.color (1.0, 1.0, 1.0);
2117 drawstring fstate.fontsize
2118 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
2120 let s =
2121 match state.mode with
2122 | Textentry ((prefix, text, _, _, _, _), _) ->
2123 let s =
2124 if len > 0
2125 then
2126 Printf.sprintf "%s%s_ [%s]" prefix text state.text
2127 else
2128 Printf.sprintf "%s%s_" prefix text
2132 | _ -> state.text
2134 let s =
2135 if state.newerrmsgs
2136 then (
2137 if not (istextentry state.mode)
2138 then
2139 let s1 = "(press 'e' to review error messasges)" in
2140 if String.length s > 0 then s ^ " " ^ s1 else s1
2141 else s
2143 else s
2145 if String.length s > 0
2146 then drawstring s
2149 let gctiles () =
2150 let len = Queue.length state.tilelru in
2151 let layout = lazy (
2152 match state.throttle with
2153 | None ->
2154 if conf.preload
2155 then preloadlayout state.y
2156 else state.layout
2157 | Some (layout, _, _) ->
2158 layout
2159 ) in
2160 let rec loop qpos =
2161 if state.memused <= conf.memlimit
2162 then ()
2163 else (
2164 if qpos < len
2165 then
2166 let (k, p, s) as lruitem = Queue.pop state.tilelru in
2167 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
2168 let (_, pw, ph, _) = getpagedim n in
2170 gen = state.gen
2171 && colorspace = conf.colorspace
2172 && angle = conf.angle
2173 && pagew = pw
2174 && pageh = ph
2175 && (
2176 let x = col*conf.tilew
2177 and y = row*conf.tileh in
2178 tilevisible (Lazy.force_val layout) n x y
2180 then Queue.push lruitem state.tilelru
2181 else (
2182 freepbo p;
2183 wcmd "freetile %s" p;
2184 state.memused <- state.memused - s;
2185 state.uioh#infochanged Memused;
2186 Hashtbl.remove state.tilemap k;
2188 loop (qpos+1)
2191 loop 0
2194 let flushtiles () =
2195 Queue.iter (fun (k, p, s) ->
2196 wcmd "freetile %s" p;
2197 state.memused <- state.memused - s;
2198 state.uioh#infochanged Memused;
2199 Hashtbl.remove state.tilemap k;
2200 ) state.tilelru;
2201 Queue.clear state.tilelru;
2202 load state.layout;
2205 let logcurrently = function
2206 | Idle -> dolog "Idle"
2207 | Loading (l, gen) ->
2208 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2209 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2210 dolog
2211 "Tiling %d[%d,%d] page=%s cs=%s angle"
2212 l.pageno col row pageopaque
2213 (colorspace_to_string colorspace)
2215 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2216 angle gen conf.angle state.gen
2217 tilew tileh
2218 conf.tilew conf.tileh
2220 | Outlining _ ->
2221 dolog "outlining"
2224 let act cmds =
2225 (* dolog "%S" cmds; *)
2226 let op, args =
2227 let spacepos =
2228 try String.index cmds ' '
2229 with Not_found -> -1
2231 if spacepos = -1
2232 then cmds, ""
2233 else
2234 let l = String.length cmds in
2235 let op = String.sub cmds 0 spacepos in
2236 op, begin
2237 if l - spacepos < 2 then ""
2238 else String.sub cmds (spacepos+1) (l-spacepos-1)
2241 match op with
2242 | "clear" ->
2243 state.uioh#infochanged Pdim;
2244 state.pdims <- [];
2246 | "clearrects" ->
2247 state.rects <- state.rects1;
2248 G.postRedisplay "clearrects";
2250 | "continue" ->
2251 let n =
2252 try Scanf.sscanf args "%u" (fun n -> n)
2253 with exn ->
2254 dolog "error processing 'continue' %S: %s"
2255 cmds (Printexc.to_string exn);
2256 exit 1;
2258 state.pagecount <- n;
2259 begin match state.currently with
2260 | Outlining l ->
2261 state.currently <- Idle;
2262 state.outlines <- Array.of_list (List.rev l)
2263 | _ -> ()
2264 end;
2266 let cur, cmds = state.geomcmds in
2267 if String.length cur = 0
2268 then failwith "umpossible";
2270 begin match List.rev cmds with
2271 | [] ->
2272 state.geomcmds <- "", [];
2273 represent ();
2274 | (s, f) :: rest ->
2275 f ();
2276 state.geomcmds <- s, List.rev rest;
2277 end;
2278 if conf.maxwait = None
2279 then G.postRedisplay "continue";
2281 | "title" ->
2282 Wsi.settitle args
2284 | "msg" ->
2285 showtext ' ' args
2287 | "vmsg" ->
2288 if conf.verbose
2289 then showtext ' ' args
2291 | "emsg" ->
2292 Buffer.add_string state.errmsgs args;
2293 state.newerrmsgs <- true;
2294 G.postRedisplay "error message"
2296 | "progress" ->
2297 let progress, text =
2299 Scanf.sscanf args "%f %n"
2300 (fun f pos ->
2301 f, String.sub args pos (String.length args - pos))
2302 with exn ->
2303 dolog "error processing 'progress' %S: %s"
2304 cmds (Printexc.to_string exn);
2305 exit 1;
2307 state.text <- text;
2308 state.progress <- progress;
2309 G.postRedisplay "progress"
2311 | "firstmatch" ->
2312 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2314 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2315 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2316 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2317 with exn ->
2318 dolog "error processing 'firstmatch' %S: %s"
2319 cmds (Printexc.to_string exn);
2320 exit 1;
2322 let y = (getpagey pageno) + truncate y0 in
2323 addnav ();
2324 gotoy y;
2325 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2327 | "match" ->
2328 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2330 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2331 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2332 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2333 with exn ->
2334 dolog "error processing 'match' %S: %s"
2335 cmds (Printexc.to_string exn);
2336 exit 1;
2338 state.rects1 <-
2339 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2341 | "page" ->
2342 let pageopaque, t =
2344 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2345 with exn ->
2346 dolog "error processing 'page' %S: %s"
2347 cmds (Printexc.to_string exn);
2348 exit 1;
2350 begin match state.currently with
2351 | Loading (l, gen) ->
2352 vlog "page %d took %f sec" l.pageno t;
2353 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2354 begin match state.throttle with
2355 | None ->
2356 let preloadedpages =
2357 if conf.preload
2358 then preloadlayout state.y
2359 else state.layout
2361 let evict () =
2362 let module IntSet =
2363 Set.Make (struct type t = int let compare = (-) end) in
2364 let set =
2365 List.fold_left (fun s l -> IntSet.add l.pageno s)
2366 IntSet.empty preloadedpages
2368 let evictedpages =
2369 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2370 if not (IntSet.mem pageno set)
2371 then (
2372 wcmd "freepage %s" opaque;
2373 key :: accu
2375 else accu
2376 ) state.pagemap []
2378 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2380 evict ();
2381 state.currently <- Idle;
2382 if gen = state.gen
2383 then (
2384 tilepage l.pageno pageopaque state.layout;
2385 load state.layout;
2386 load preloadedpages;
2387 if pagevisible state.layout l.pageno
2388 && layoutready state.layout
2389 then G.postRedisplay "page";
2392 | Some (layout, _, _) ->
2393 state.currently <- Idle;
2394 tilepage l.pageno pageopaque layout;
2395 load state.layout
2396 end;
2398 | _ ->
2399 dolog "Inconsistent loading state";
2400 logcurrently state.currently;
2401 exit 1
2404 | "tile" ->
2405 let (x, y, opaque, size, t) =
2407 Scanf.sscanf args "%u %u %s %u %f"
2408 (fun x y p size t -> (x, y, p, size, t))
2409 with exn ->
2410 dolog "error processing 'tile' %S: %s"
2411 cmds (Printexc.to_string exn);
2412 exit 1;
2414 begin match state.currently with
2415 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2416 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2418 unmappbo opaque;
2419 if tilew != conf.tilew || tileh != conf.tileh
2420 then (
2421 wcmd "freetile %s" opaque;
2422 state.currently <- Idle;
2423 load state.layout;
2425 else (
2426 puttileopaque l col row gen cs angle opaque size t;
2427 state.memused <- state.memused + size;
2428 state.uioh#infochanged Memused;
2429 gctiles ();
2430 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2431 opaque, size) state.tilelru;
2433 let layout =
2434 match state.throttle with
2435 | None -> state.layout
2436 | Some (layout, _, _) -> layout
2439 state.currently <- Idle;
2440 if gen = state.gen
2441 && conf.colorspace = cs
2442 && conf.angle = angle
2443 && tilevisible layout l.pageno x y
2444 then conttiling l.pageno pageopaque;
2446 begin match state.throttle with
2447 | None ->
2448 state.wthack <- false;
2449 preload state.layout;
2450 if gen = state.gen
2451 && conf.colorspace = cs
2452 && conf.angle = angle
2453 && tilevisible state.layout l.pageno x y
2454 then G.postRedisplay "tile nothrottle";
2456 | Some (layout, y, _) ->
2457 let ready = layoutready layout in
2458 if ready
2459 then (
2460 state.wthack <- false;
2461 state.y <- y;
2462 state.layout <- layout;
2463 state.throttle <- None;
2464 G.postRedisplay "throttle";
2466 else load layout;
2467 end;
2470 | _ ->
2471 dolog "Inconsistent tiling state";
2472 logcurrently state.currently;
2473 exit 1
2476 | "pdim" ->
2477 let pdim =
2479 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2480 with exn ->
2481 dolog "error processing 'pdim' %S: %s"
2482 cmds (Printexc.to_string exn);
2483 exit 1;
2485 state.uioh#infochanged Pdim;
2486 state.pdims <- pdim :: state.pdims
2488 | "o" ->
2489 let (l, n, t, h, pos) =
2491 Scanf.sscanf args "%u %u %d %u %n"
2492 (fun l n t h pos -> l, n, t, h, pos)
2493 with exn ->
2494 dolog "error processing 'o' %S: %s"
2495 cmds (Printexc.to_string exn);
2496 exit 1;
2498 let s = String.sub args pos (String.length args - pos) in
2499 let outline = (s, l, (n, float t /. float h, 0.0)) in
2500 begin match state.currently with
2501 | Outlining outlines ->
2502 state.currently <- Outlining (outline :: outlines)
2503 | Idle ->
2504 state.currently <- Outlining [outline]
2505 | currently ->
2506 dolog "invalid outlining state";
2507 logcurrently currently
2510 | "a" ->
2511 let (n, t, h) =
2513 Scanf.sscanf args "%u %u %d"
2514 (fun n t h -> n, t, h)
2515 with exn ->
2516 dolog "error processing 'a' %S: %s"
2517 cmds (Printexc.to_string exn);
2518 exit 1;
2520 state.anchor <- (n, float t /. float h, 0.0)
2522 | "info" ->
2523 state.docinfo <- (1, args) :: state.docinfo
2525 | "infoend" ->
2526 state.uioh#infochanged Docinfo;
2527 state.docinfo <- List.rev state.docinfo
2529 | _ ->
2530 dolog "unknown cmd `%S'" cmds
2533 let onhist cb =
2534 let rc = cb.rc in
2535 let action = function
2536 | HCprev -> cbget cb ~-1
2537 | HCnext -> cbget cb 1
2538 | HCfirst -> cbget cb ~-(cb.rc)
2539 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2540 and cancel () = cb.rc <- rc
2541 in (action, cancel)
2544 let search pattern forward =
2545 if String.length pattern > 0
2546 then
2547 let pn, py =
2548 match state.layout with
2549 | [] -> 0, 0
2550 | l :: _ ->
2551 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2553 wcmd "search %d %d %d %d,%s\000"
2554 (btod conf.icase) pn py (btod forward) pattern;
2557 let intentry text key =
2558 let c =
2559 if key >= 32 && key < 127
2560 then Char.chr key
2561 else '\000'
2563 match c with
2564 | '0' .. '9' ->
2565 let text = addchar text c in
2566 TEcont text
2568 | _ ->
2569 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2570 TEcont text
2573 let linknentry text key =
2574 let c =
2575 if key >= 32 && key < 127
2576 then Char.chr key
2577 else '\000'
2579 match c with
2580 | 'a' .. 'z' ->
2581 let text = addchar text c in
2582 TEcont text
2584 | _ ->
2585 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2586 TEcont text
2589 let linkndone f s =
2590 if String.length s > 0
2591 then (
2592 let n =
2593 let l = String.length s in
2594 let rec loop pos n = if pos = l then n else
2595 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
2596 loop (pos+1) (n*26 + m)
2597 in loop 0 0
2599 let rec loop n = function
2600 | [] -> ()
2601 | l :: rest ->
2602 match getopaque l.pageno with
2603 | None -> loop n rest
2604 | Some opaque ->
2605 let m = getlinkcount opaque in
2606 if n < m
2607 then (
2608 let under = getlink opaque n in
2609 f under
2611 else loop (n-m) rest
2613 loop n state.layout;
2617 let textentry text key =
2618 if key land 0xff00 = 0xff00
2619 then TEcont text
2620 else TEcont (text ^ Wsi.toutf8 key)
2623 let reqlayout angle proportional =
2624 match state.throttle with
2625 | None ->
2626 if nogeomcmds state.geomcmds
2627 then state.anchor <- getanchor ();
2628 conf.angle <- angle mod 360;
2629 if conf.angle != 0
2630 then (
2631 match state.mode with
2632 | LinkNav _ -> state.mode <- View
2633 | _ -> ()
2635 conf.proportional <- proportional;
2636 invalidate "reqlayout"
2637 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2638 | _ -> ()
2641 let settrim trimmargins trimfuzz =
2642 if nogeomcmds state.geomcmds
2643 then state.anchor <- getanchor ();
2644 conf.trimmargins <- trimmargins;
2645 conf.trimfuzz <- trimfuzz;
2646 let x0, y0, x1, y1 = trimfuzz in
2647 invalidate "settrim"
2648 (fun () ->
2649 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2650 flushpages ();
2653 let setzoom zoom =
2654 match state.throttle with
2655 | None ->
2656 let zoom = max 0.01 zoom in
2657 if zoom <> conf.zoom
2658 then (
2659 state.prevzoom <- conf.zoom;
2660 conf.zoom <- zoom;
2661 reshape conf.winw conf.winh;
2662 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2665 | Some (layout, y, started) ->
2666 let time =
2667 match conf.maxwait with
2668 | None -> 0.0
2669 | Some t -> t
2671 let dt = now () -. started in
2672 if dt > time
2673 then (
2674 state.y <- y;
2675 load layout;
2679 let setcolumns mode columns coverA coverB =
2680 state.prevcolumns <- Some (conf.columns, conf.zoom);
2681 if columns < 0
2682 then (
2683 if isbirdseye mode
2684 then showtext '!' "split mode doesn't work in bird's eye"
2685 else (
2686 conf.columns <- Csplit (-columns, [||]);
2687 state.x <- 0;
2688 conf.zoom <- 1.0;
2691 else (
2692 if columns < 2
2693 then (
2694 conf.columns <- Csingle [||];
2695 state.x <- 0;
2696 setzoom 1.0;
2698 else (
2699 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2700 conf.zoom <- 1.0;
2703 reshape conf.winw conf.winh;
2706 let enterbirdseye () =
2707 let zoom = float conf.thumbw /. float conf.winw in
2708 let birdseyepageno =
2709 let cy = conf.winh / 2 in
2710 let fold = function
2711 | [] -> 0
2712 | l :: rest ->
2713 let rec fold best = function
2714 | [] -> best.pageno
2715 | l :: rest ->
2716 let d = cy - (l.pagedispy + l.pagevh/2)
2717 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2718 if abs d < abs dbest
2719 then fold l rest
2720 else best.pageno
2721 in fold l rest
2723 fold state.layout
2725 state.mode <- Birdseye (
2726 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2728 conf.zoom <- zoom;
2729 conf.presentation <- false;
2730 conf.interpagespace <- 10;
2731 conf.hlinks <- false;
2732 state.x <- 0;
2733 state.mstate <- Mnone;
2734 conf.maxwait <- None;
2735 conf.columns <- (
2736 match conf.beyecolumns with
2737 | Some c ->
2738 conf.zoom <- 1.0;
2739 Cmulti ((c, 0, 0), [||])
2740 | None -> Csingle [||]
2742 Wsi.setcursor Wsi.CURSOR_INHERIT;
2743 if conf.verbose
2744 then
2745 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2746 (100.0*.zoom)
2747 else
2748 state.text <- ""
2750 reshape conf.winw conf.winh;
2753 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2754 state.mode <- View;
2755 conf.zoom <- c.zoom;
2756 conf.presentation <- c.presentation;
2757 conf.interpagespace <- c.interpagespace;
2758 conf.maxwait <- c.maxwait;
2759 conf.hlinks <- c.hlinks;
2760 conf.beyecolumns <- (
2761 match conf.columns with
2762 | Cmulti ((c, _, _), _) -> Some c
2763 | Csingle _ -> None
2764 | Csplit _ -> failwith "leaving bird's eye split mode"
2766 conf.columns <- (
2767 match c.columns with
2768 | Cmulti (c, _) -> Cmulti (c, [||])
2769 | Csingle _ -> Csingle [||]
2770 | Csplit (c, _) -> Csplit (c, [||])
2772 state.x <- leftx;
2773 if conf.verbose
2774 then
2775 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2776 (100.0*.conf.zoom)
2778 reshape conf.winw conf.winh;
2779 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
2782 let togglebirdseye () =
2783 match state.mode with
2784 | Birdseye vals -> leavebirdseye vals true
2785 | View -> enterbirdseye ()
2786 | _ -> ()
2789 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2790 let pageno = max 0 (pageno - incr) in
2791 let rec loop = function
2792 | [] -> gotopage1 pageno 0
2793 | l :: _ when l.pageno = pageno ->
2794 if l.pagedispy >= 0 && l.pagey = 0
2795 then G.postRedisplay "upbirdseye"
2796 else gotopage1 pageno 0
2797 | _ :: rest -> loop rest
2799 loop state.layout;
2800 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2803 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2804 let pageno = min (state.pagecount - 1) (pageno + incr) in
2805 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2806 let rec loop = function
2807 | [] ->
2808 let y, h = getpageyh pageno in
2809 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2810 gotoy (clamp dy)
2811 | l :: _ when l.pageno = pageno ->
2812 if l.pagevh != l.pageh
2813 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2814 else G.postRedisplay "downbirdseye"
2815 | _ :: rest -> loop rest
2817 loop state.layout
2820 let optentry mode _ key =
2821 let btos b = if b then "on" else "off" in
2822 if key >= 32 && key < 127
2823 then
2824 let c = Char.chr key in
2825 match c with
2826 | 's' ->
2827 let ondone s =
2828 try conf.scrollstep <- int_of_string s with exc ->
2829 state.text <- Printf.sprintf "bad integer `%s': %s"
2830 s (Printexc.to_string exc)
2832 TEswitch ("scroll step: ", "", None, intentry, ondone, true)
2834 | 'A' ->
2835 let ondone s =
2837 conf.autoscrollstep <- int_of_string s;
2838 if state.autoscroll <> None
2839 then state.autoscroll <- Some conf.autoscrollstep
2840 with exc ->
2841 state.text <- Printf.sprintf "bad integer `%s': %s"
2842 s (Printexc.to_string exc)
2844 TEswitch ("auto scroll step: ", "", None, intentry, ondone, true)
2846 | 'C' ->
2847 let ondone s =
2849 let n, a, b = multicolumns_of_string s in
2850 setcolumns mode n a b;
2851 with exc ->
2852 state.text <- Printf.sprintf "bad columns `%s': %s"
2853 s (Printexc.to_string exc)
2855 TEswitch ("columns: ", "", None, textentry, ondone, true)
2857 | 'Z' ->
2858 let ondone s =
2860 let zoom = float (int_of_string s) /. 100.0 in
2861 setzoom zoom
2862 with exc ->
2863 state.text <- Printf.sprintf "bad integer `%s': %s"
2864 s (Printexc.to_string exc)
2866 TEswitch ("zoom: ", "", None, intentry, ondone, true)
2868 | 't' ->
2869 let ondone s =
2871 conf.thumbw <- bound (int_of_string s) 2 4096;
2872 state.text <-
2873 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2874 begin match mode with
2875 | Birdseye beye ->
2876 leavebirdseye beye false;
2877 enterbirdseye ();
2878 | _ -> ();
2880 with exc ->
2881 state.text <- Printf.sprintf "bad integer `%s': %s"
2882 s (Printexc.to_string exc)
2884 TEswitch ("thumbnail width: ", "", None, intentry, ondone, true)
2886 | 'R' ->
2887 let ondone s =
2888 match try
2889 Some (int_of_string s)
2890 with exc ->
2891 state.text <- Printf.sprintf "bad integer `%s': %s"
2892 s (Printexc.to_string exc);
2893 None
2894 with
2895 | Some angle -> reqlayout angle conf.proportional
2896 | None -> ()
2898 TEswitch ("rotation: ", "", None, intentry, ondone, true)
2900 | 'i' ->
2901 conf.icase <- not conf.icase;
2902 TEdone ("case insensitive search " ^ (btos conf.icase))
2904 | 'p' ->
2905 conf.preload <- not conf.preload;
2906 gotoy state.y;
2907 TEdone ("preload " ^ (btos conf.preload))
2909 | 'v' ->
2910 conf.verbose <- not conf.verbose;
2911 TEdone ("verbose " ^ (btos conf.verbose))
2913 | 'd' ->
2914 conf.debug <- not conf.debug;
2915 TEdone ("debug " ^ (btos conf.debug))
2917 | 'h' ->
2918 conf.maxhfit <- not conf.maxhfit;
2919 state.maxy <- calcheight ();
2920 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2922 | 'c' ->
2923 conf.crophack <- not conf.crophack;
2924 TEdone ("crophack " ^ btos conf.crophack)
2926 | 'a' ->
2927 let s =
2928 match conf.maxwait with
2929 | None ->
2930 conf.maxwait <- Some infinity;
2931 "always wait for page to complete"
2932 | Some _ ->
2933 conf.maxwait <- None;
2934 "show placeholder if page is not ready"
2936 TEdone s
2938 | 'f' ->
2939 conf.underinfo <- not conf.underinfo;
2940 TEdone ("underinfo " ^ btos conf.underinfo)
2942 | 'P' ->
2943 conf.savebmarks <- not conf.savebmarks;
2944 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2946 | 'S' ->
2947 let ondone s =
2949 let pageno, py =
2950 match state.layout with
2951 | [] -> 0, 0
2952 | l :: _ ->
2953 l.pageno, l.pagey
2955 conf.interpagespace <- int_of_string s;
2956 docolumns conf.columns;
2957 state.maxy <- calcheight ();
2958 let y = getpagey pageno in
2959 gotoy (y + py)
2960 with exc ->
2961 state.text <- Printf.sprintf "bad integer `%s': %s"
2962 s (Printexc.to_string exc)
2964 TEswitch ("vertical margin: ", "", None, intentry, ondone, true)
2966 | 'l' ->
2967 reqlayout conf.angle (not conf.proportional);
2968 TEdone ("proportional display " ^ btos conf.proportional)
2970 | 'T' ->
2971 settrim (not conf.trimmargins) conf.trimfuzz;
2972 TEdone ("trim margins " ^ btos conf.trimmargins)
2974 | 'I' ->
2975 conf.invert <- not conf.invert;
2976 TEdone ("invert colors " ^ btos conf.invert)
2978 | 'x' ->
2979 let ondone s =
2980 cbput state.hists.sel s;
2981 conf.selcmd <- s;
2983 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2984 textentry, ondone, true)
2986 | _ ->
2987 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2988 TEstop
2989 else
2990 TEcont state.text
2993 class type lvsource = object
2994 method getitemcount : int
2995 method getitem : int -> (string * int)
2996 method hasaction : int -> bool
2997 method exit :
2998 uioh:uioh ->
2999 cancel:bool ->
3000 active:int ->
3001 first:int ->
3002 pan:int ->
3003 qsearch:string ->
3004 uioh option
3005 method getactive : int
3006 method getfirst : int
3007 method getqsearch : string
3008 method setqsearch : string -> unit
3009 method getpan : int
3010 end;;
3012 class virtual lvsourcebase = object
3013 val mutable m_active = 0
3014 val mutable m_first = 0
3015 val mutable m_qsearch = ""
3016 val mutable m_pan = 0
3017 method getactive = m_active
3018 method getfirst = m_first
3019 method getqsearch = m_qsearch
3020 method getpan = m_pan
3021 method setqsearch s = m_qsearch <- s
3022 end;;
3024 let withoutlastutf8 s =
3025 let len = String.length s in
3026 if len = 0
3027 then s
3028 else
3029 let rec find pos =
3030 if pos = 0
3031 then pos
3032 else
3033 let b = Char.code s.[pos] in
3034 if b land 0b11000000 = 0b11000000
3035 then pos
3036 else find (pos-1)
3038 let first =
3039 if Char.code s.[len-1] land 0x80 = 0
3040 then len-1
3041 else find (len-1)
3043 String.sub s 0 first;
3046 let textentrykeyboard
3047 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
3048 let key =
3049 if key >= 0xffb0 && key <= 0xffb9
3050 then key - 0xffb0 + 48 else key
3052 let enttext te =
3053 state.mode <- Textentry (te, onleave);
3054 state.text <- "";
3055 enttext ();
3056 G.postRedisplay "textentrykeyboard enttext";
3058 let histaction cmd =
3059 match opthist with
3060 | None -> ()
3061 | Some (action, _) ->
3062 state.mode <- Textentry (
3063 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
3065 G.postRedisplay "textentry histaction"
3067 match key with
3068 | 0xff08 -> (* backspace *)
3069 let s = withoutlastutf8 text in
3070 let len = String.length s in
3071 if cancelonempty && len = 0
3072 then (
3073 onleave Cancel;
3074 G.postRedisplay "textentrykeyboard after cancel";
3076 else (
3077 enttext (c, s, opthist, onkey, ondone, cancelonempty)
3080 | 0xff0d | 0xff8d -> (* (kp) enter *)
3081 ondone text;
3082 onleave Confirm;
3083 G.postRedisplay "textentrykeyboard after confirm"
3085 | 0xff52 | 0xff97 -> histaction HCprev (* (kp) up *)
3086 | 0xff54 | 0xff99 -> histaction HCnext (* (kp) down *)
3087 | 0xff50 | 0xff95 -> histaction HCfirst (* (kp) home) *)
3088 | 0xff57 | 0xff9c -> histaction HClast (* (kp) end *)
3090 | 0xff1b -> (* escape*)
3091 if String.length text = 0
3092 then (
3093 begin match opthist with
3094 | None -> ()
3095 | Some (_, onhistcancel) -> onhistcancel ()
3096 end;
3097 onleave Cancel;
3098 state.text <- "";
3099 G.postRedisplay "textentrykeyboard after cancel2"
3101 else (
3102 enttext (c, "", opthist, onkey, ondone, cancelonempty)
3105 | 0xff9f | 0xffff -> () (* delete *)
3107 | _ when key != 0
3108 && key land 0xff00 != 0xff00 (* keyboard *)
3109 && key land 0xfe00 != 0xfe00 (* xkb *)
3110 && key land 0xfd00 != 0xfd00 (* 3270 *)
3112 begin match onkey text key with
3113 | TEdone text ->
3114 ondone text;
3115 onleave Confirm;
3116 G.postRedisplay "textentrykeyboard after confirm2";
3118 | TEcont text ->
3119 enttext (c, text, opthist, onkey, ondone, cancelonempty);
3121 | TEstop ->
3122 onleave Cancel;
3123 G.postRedisplay "textentrykeyboard after cancel3"
3125 | TEswitch te ->
3126 state.mode <- Textentry (te, onleave);
3127 G.postRedisplay "textentrykeyboard switch";
3128 end;
3130 | _ ->
3131 vlog "unhandled key %s" (Wsi.keyname key)
3134 let firstof first active =
3135 if first > active || abs (first - active) > fstate.maxrows - 1
3136 then max 0 (active - (fstate.maxrows/2))
3137 else first
3140 let calcfirst first active =
3141 if active > first
3142 then
3143 let rows = active - first in
3144 if rows > fstate.maxrows then active - fstate.maxrows else first
3145 else active
3148 let scrollph y maxy =
3149 let sh = (float (maxy + conf.winh) /. float conf.winh) in
3150 let sh = float conf.winh /. sh in
3151 let sh = max sh (float conf.scrollh) in
3153 let percent =
3154 if y = state.maxy
3155 then 1.0
3156 else float y /. float maxy
3158 let position = (float conf.winh -. sh) *. percent in
3160 let position =
3161 if position +. sh > float conf.winh
3162 then float conf.winh -. sh
3163 else position
3165 position, sh;
3168 let coe s = (s :> uioh);;
3170 class listview ~(source:lvsource) ~trusted ~modehash =
3171 object (self)
3172 val m_pan = source#getpan
3173 val m_first = source#getfirst
3174 val m_active = source#getactive
3175 val m_qsearch = source#getqsearch
3176 val m_prev_uioh = state.uioh
3178 method private elemunder y =
3179 let n = y / (fstate.fontsize+1) in
3180 if m_first + n < source#getitemcount
3181 then (
3182 if source#hasaction (m_first + n)
3183 then Some (m_first + n)
3184 else None
3186 else None
3188 method display =
3189 Gl.enable `blend;
3190 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3191 GlDraw.color (0., 0., 0.) ~alpha:0.85;
3192 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
3193 GlDraw.color (1., 1., 1.);
3194 Gl.enable `texture_2d;
3195 let fs = fstate.fontsize in
3196 let nfs = fs + 1 in
3197 let ww = fstate.wwidth in
3198 let tabw = 30.0*.ww in
3199 let itemcount = source#getitemcount in
3200 let rec loop row =
3201 if (row - m_first) > fstate.maxrows
3202 then ()
3203 else (
3204 if row >= 0 && row < itemcount
3205 then (
3206 let (s, level) = source#getitem row in
3207 let y = (row - m_first) * nfs in
3208 let x = 5.0 +. float (level + m_pan) *. ww in
3209 if row = m_active
3210 then (
3211 Gl.disable `texture_2d;
3212 GlDraw.polygon_mode `both `line;
3213 GlDraw.color (1., 1., 1.) ~alpha:0.9;
3214 GlDraw.rect (1., float (y + 1))
3215 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
3216 GlDraw.polygon_mode `both `fill;
3217 GlDraw.color (1., 1., 1.);
3218 Gl.enable `texture_2d;
3221 let drawtabularstring s =
3222 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
3223 if trusted
3224 then
3225 let tabpos = try String.index s '\t' with Not_found -> -1 in
3226 if tabpos > 0
3227 then
3228 let len = String.length s - tabpos - 1 in
3229 let s1 = String.sub s 0 tabpos
3230 and s2 = String.sub s (tabpos + 1) len in
3231 let nx = drawstr x s1 in
3232 let sw = nx -. x in
3233 let x = x +. (max tabw sw) in
3234 drawstr x s2
3235 else
3236 drawstr x s
3237 else
3238 drawstr x s
3240 let _ = drawtabularstring s in
3241 loop (row+1)
3245 loop m_first;
3246 Gl.disable `blend;
3247 Gl.disable `texture_2d;
3249 method updownlevel incr =
3250 let len = source#getitemcount in
3251 let curlevel =
3252 if m_active >= 0 && m_active < len
3253 then snd (source#getitem m_active)
3254 else -1
3256 let rec flow i =
3257 if i = len then i-1 else if i = -1 then 0 else
3258 let _, l = source#getitem i in
3259 if l != curlevel then i else flow (i+incr)
3261 let active = flow m_active in
3262 let first = calcfirst m_first active in
3263 G.postRedisplay "outline updownlevel";
3264 {< m_active = active; m_first = first >}
3266 method private key1 key mask =
3267 let set1 active first qsearch =
3268 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3270 let search active pattern incr =
3271 let dosearch re =
3272 let rec loop n =
3273 if n >= 0 && n < source#getitemcount
3274 then (
3275 let s, _ = source#getitem n in
3277 (try ignore (Str.search_forward re s 0); true
3278 with Not_found -> false)
3279 then Some n
3280 else loop (n + incr)
3282 else None
3284 loop active
3287 let re = Str.regexp_case_fold pattern in
3288 dosearch re
3289 with Failure s ->
3290 state.text <- s;
3291 None
3293 let itemcount = source#getitemcount in
3294 let find start incr =
3295 let rec find i =
3296 if i = -1 || i = itemcount
3297 then -1
3298 else (
3299 if source#hasaction i
3300 then i
3301 else find (i + incr)
3304 find start
3306 let set active first =
3307 let first = bound first 0 (itemcount - fstate.maxrows) in
3308 state.text <- "";
3309 coe {< m_active = active; m_first = first >}
3311 let navigate incr =
3312 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3313 let active, first =
3314 let incr1 = if incr > 0 then 1 else -1 in
3315 if isvisible m_first m_active
3316 then
3317 let next =
3318 let next = m_active + incr in
3319 let next =
3320 if next < 0 || next >= itemcount
3321 then -1
3322 else find next incr1
3324 if next = -1 || abs (m_active - next) > fstate.maxrows
3325 then -1
3326 else next
3328 if next = -1
3329 then
3330 let first = m_first + incr in
3331 let first = bound first 0 (itemcount - 1) in
3332 let next =
3333 let next = m_active + incr in
3334 let next = bound next 0 (itemcount - 1) in
3335 find next ~-incr1
3337 let active = if next = -1 then m_active else next in
3338 active, first
3339 else
3340 let first = min next m_first in
3341 let first =
3342 if abs (next - first) > fstate.maxrows
3343 then first + incr
3344 else first
3346 next, first
3347 else
3348 let first = m_first + incr in
3349 let first = bound first 0 (itemcount - 1) in
3350 let active =
3351 let next = m_active + incr in
3352 let next = bound next 0 (itemcount - 1) in
3353 let next = find next incr1 in
3354 let active =
3355 if next = -1 || abs (m_active - first) > fstate.maxrows
3356 then (
3357 let active = if m_active = -1 then next else m_active in
3358 active
3360 else next
3362 if isvisible first active
3363 then active
3364 else -1
3366 active, first
3368 G.postRedisplay "listview navigate";
3369 set active first;
3371 match key with
3372 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3373 let incr = if key = 0x72 then -1 else 1 in
3374 let active, first =
3375 match search (m_active + incr) m_qsearch incr with
3376 | None ->
3377 state.text <- m_qsearch ^ " [not found]";
3378 m_active, m_first
3379 | Some active ->
3380 state.text <- m_qsearch;
3381 active, firstof m_first active
3383 G.postRedisplay "listview ctrl-r/s";
3384 set1 active first m_qsearch;
3386 | 0xff08 -> (* backspace *)
3387 if String.length m_qsearch = 0
3388 then coe self
3389 else (
3390 let qsearch = withoutlastutf8 m_qsearch in
3391 let len = String.length qsearch in
3392 if len = 0
3393 then (
3394 state.text <- "";
3395 G.postRedisplay "listview empty qsearch";
3396 set1 m_active m_first "";
3398 else
3399 let active, first =
3400 match search m_active qsearch ~-1 with
3401 | None ->
3402 state.text <- qsearch ^ " [not found]";
3403 m_active, m_first
3404 | Some active ->
3405 state.text <- qsearch;
3406 active, firstof m_first active
3408 G.postRedisplay "listview backspace qsearch";
3409 set1 active first qsearch
3412 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3413 let pattern = m_qsearch ^ Wsi.toutf8 key in
3414 let active, first =
3415 match search m_active pattern 1 with
3416 | None ->
3417 state.text <- pattern ^ " [not found]";
3418 m_active, m_first
3419 | Some active ->
3420 state.text <- pattern;
3421 active, firstof m_first active
3423 G.postRedisplay "listview qsearch add";
3424 set1 active first pattern;
3426 | 0xff1b -> (* escape *)
3427 state.text <- "";
3428 if String.length m_qsearch = 0
3429 then (
3430 G.postRedisplay "list view escape";
3431 begin
3432 match
3433 source#exit (coe self) true m_active m_first m_pan m_qsearch
3434 with
3435 | None -> m_prev_uioh
3436 | Some uioh -> uioh
3439 else (
3440 G.postRedisplay "list view kill qsearch";
3441 source#setqsearch "";
3442 coe {< m_qsearch = "" >}
3445 | 0xff0d | 0xff8d -> (* (kp) enter *)
3446 state.text <- "";
3447 let self = {< m_qsearch = "" >} in
3448 source#setqsearch "";
3449 let opt =
3450 G.postRedisplay "listview enter";
3451 if m_active >= 0 && m_active < source#getitemcount
3452 then (
3453 source#exit (coe self) false m_active m_first m_pan "";
3455 else (
3456 source#exit (coe self) true m_active m_first m_pan "";
3459 begin match opt with
3460 | None -> m_prev_uioh
3461 | Some uioh -> uioh
3464 | 0xff9f | 0xffff -> (* (kp) delete *)
3465 coe self
3467 | 0xff52 | 0xff97 -> navigate ~-1 (* (kp) up *)
3468 | 0xff54 | 0xff99 -> navigate 1 (* (kp) down *)
3469 | 0xff55 | 0xff9a -> navigate ~-(fstate.maxrows) (* (kp) prior *)
3470 | 0xff56 | 0xff9b -> navigate fstate.maxrows (* (kp) next *)
3472 | 0xff53 | 0xff98 -> (* (kp) right *)
3473 state.text <- "";
3474 G.postRedisplay "listview right";
3475 coe {< m_pan = m_pan - 1 >}
3477 | 0xff51 | 0xff96 -> (* (kp) left *)
3478 state.text <- "";
3479 G.postRedisplay "listview left";
3480 coe {< m_pan = m_pan + 1 >}
3482 | 0xff50 | 0xff95 -> (* (kp) home *)
3483 let active = find 0 1 in
3484 G.postRedisplay "listview home";
3485 set active 0;
3487 | 0xff57 | 0xff9c -> (* (kp) end *)
3488 let first = max 0 (itemcount - fstate.maxrows) in
3489 let active = find (itemcount - 1) ~-1 in
3490 G.postRedisplay "listview end";
3491 set active first;
3493 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3494 coe self
3496 | _ ->
3497 dolog "listview unknown key %#x" key; coe self
3499 method key key mask =
3500 match state.mode with
3501 | Textentry te -> textentrykeyboard key mask te; coe self
3502 | _ -> self#key1 key mask
3504 method button button down x y _ =
3505 let opt =
3506 match button with
3507 | 1 when x > conf.winw - conf.scrollbw ->
3508 G.postRedisplay "listview scroll";
3509 if down
3510 then
3511 let _, position, sh = self#scrollph in
3512 if y > truncate position && y < truncate (position +. sh)
3513 then (
3514 state.mstate <- Mscrolly;
3515 Some (coe self)
3517 else
3518 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3519 let first = truncate (s *. float source#getitemcount) in
3520 let first = min source#getitemcount first in
3521 Some (coe {< m_first = first; m_active = first >})
3522 else (
3523 state.mstate <- Mnone;
3524 Some (coe self);
3526 | 1 when not down ->
3527 begin match self#elemunder y with
3528 | Some n ->
3529 G.postRedisplay "listview click";
3530 source#exit
3531 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3532 | _ ->
3533 Some (coe self)
3535 | n when (n == 4 || n == 5) && not down ->
3536 let len = source#getitemcount in
3537 let first =
3538 if n = 5 && m_first + fstate.maxrows >= len
3539 then
3540 m_first
3541 else
3542 let first = m_first + (if n == 4 then -1 else 1) in
3543 bound first 0 (len - 1)
3545 G.postRedisplay "listview wheel";
3546 Some (coe {< m_first = first >})
3547 | n when (n = 6 || n = 7) && not down ->
3548 let inc = m_first + (if n = 7 then -1 else 1) in
3549 G.postRedisplay "listview hwheel";
3550 Some (coe {< m_pan = m_pan + inc >})
3551 | _ ->
3552 Some (coe self)
3554 match opt with
3555 | None -> m_prev_uioh
3556 | Some uioh -> uioh
3558 method motion _ y =
3559 match state.mstate with
3560 | Mscrolly ->
3561 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3562 let first = truncate (s *. float source#getitemcount) in
3563 let first = min source#getitemcount first in
3564 G.postRedisplay "listview motion";
3565 coe {< m_first = first; m_active = first >}
3566 | _ -> coe self
3568 method pmotion x y =
3569 if x < conf.winw - conf.scrollbw
3570 then
3571 let n =
3572 match self#elemunder y with
3573 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3574 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3576 let o =
3577 if n != m_active
3578 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3579 else self
3581 coe o
3582 else (
3583 Wsi.setcursor Wsi.CURSOR_INHERIT;
3584 coe self
3587 method infochanged _ = ()
3589 method scrollpw = (0, 0.0, 0.0)
3590 method scrollph =
3591 let nfs = fstate.fontsize + 1 in
3592 let y = m_first * nfs in
3593 let itemcount = source#getitemcount in
3594 let maxi = max 0 (itemcount - fstate.maxrows) in
3595 let maxy = maxi * nfs in
3596 let p, h = scrollph y maxy in
3597 conf.scrollbw, p, h
3599 method modehash = modehash
3600 end;;
3602 class outlinelistview ~source =
3603 object (self)
3604 inherit listview
3605 ~source:(source :> lvsource)
3606 ~trusted:false
3607 ~modehash:(findkeyhash conf "outline")
3608 as super
3610 method key key mask =
3611 let calcfirst first active =
3612 if active > first
3613 then
3614 let rows = active - first in
3615 let maxrows =
3616 if String.length state.text = 0
3617 then fstate.maxrows
3618 else fstate.maxrows - 2
3620 if rows > maxrows then active - maxrows else first
3621 else active
3623 let navigate incr =
3624 let active = m_active + incr in
3625 let active = bound active 0 (source#getitemcount - 1) in
3626 let first = calcfirst m_first active in
3627 G.postRedisplay "outline navigate";
3628 coe {< m_active = active; m_first = first >}
3630 let ctrl = Wsi.withctrl mask in
3631 match key with
3632 | 110 when ctrl -> (* ctrl-n *)
3633 source#narrow m_qsearch;
3634 G.postRedisplay "outline ctrl-n";
3635 coe {< m_first = 0; m_active = 0 >}
3637 | 117 when ctrl -> (* ctrl-u *)
3638 source#denarrow;
3639 G.postRedisplay "outline ctrl-u";
3640 state.text <- "";
3641 coe {< m_first = 0; m_active = 0 >}
3643 | 108 when ctrl -> (* ctrl-l *)
3644 let first = max 0 (m_active - (fstate.maxrows / 2)) in
3645 G.postRedisplay "outline ctrl-l";
3646 coe {< m_first = first >}
3648 | 0xff9f | 0xffff -> (* (kp) delete *)
3649 source#remove m_active;
3650 G.postRedisplay "outline delete";
3651 let active = max 0 (m_active-1) in
3652 coe {< m_first = firstof m_first active;
3653 m_active = active >}
3655 | 0xff52 | 0xff97 -> navigate ~-1 (* (kp) up *)
3656 | 0xff54 | 0xff99 -> navigate 1 (* (kp) down *)
3657 | 0xff55 | 0xff9a -> (* (kp) prior *)
3658 navigate ~-(fstate.maxrows)
3659 | 0xff56 | 0xff9b -> (* (kp) next *)
3660 navigate fstate.maxrows
3662 | 0xff53 | 0xff98 -> (* [ctrl-] (kp) right *)
3663 let o =
3664 if ctrl
3665 then (
3666 G.postRedisplay "outline ctrl right";
3667 {< m_pan = m_pan + 1 >}
3669 else self#updownlevel 1
3671 coe o
3673 | 0xff51 | 0xff96 -> (* [ctrl-] (kp) left *)
3674 let o =
3675 if ctrl
3676 then (
3677 G.postRedisplay "outline ctrl left";
3678 {< m_pan = m_pan - 1 >}
3680 else self#updownlevel ~-1
3682 coe o
3684 | 0xff50 | 0xff95 -> (* (kp) home *)
3685 G.postRedisplay "outline home";
3686 coe {< m_first = 0; m_active = 0 >}
3688 | 0xff57 | 0xff9c -> (* (kp) end *)
3689 let active = source#getitemcount - 1 in
3690 let first = max 0 (active - fstate.maxrows) in
3691 G.postRedisplay "outline end";
3692 coe {< m_active = active; m_first = first >}
3694 | _ -> super#key key mask
3697 let outlinesource usebookmarks =
3698 let empty = [||] in
3699 (object
3700 inherit lvsourcebase
3701 val mutable m_items = empty
3702 val mutable m_orig_items = empty
3703 val mutable m_prev_items = empty
3704 val mutable m_narrow_pattern = ""
3705 val mutable m_hadremovals = false
3707 method getitemcount =
3708 Array.length m_items + (if m_hadremovals then 1 else 0)
3710 method getitem n =
3711 if n == Array.length m_items && m_hadremovals
3712 then
3713 ("[Confirm removal]", 0)
3714 else
3715 let s, n, _ = m_items.(n) in
3716 (s, n)
3718 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3719 ignore (uioh, first, qsearch);
3720 let confrimremoval = m_hadremovals && active = Array.length m_items in
3721 let items =
3722 if String.length m_narrow_pattern = 0
3723 then m_orig_items
3724 else m_items
3726 if not cancel
3727 then (
3728 if not confrimremoval
3729 then(
3730 let _, _, anchor = m_items.(active) in
3731 gotoghyll (getanchory anchor);
3732 m_items <- items;
3734 else (
3735 state.bookmarks <- Array.to_list m_items;
3736 m_orig_items <- m_items;
3739 else m_items <- items;
3740 m_pan <- pan;
3741 None
3743 method hasaction _ = true
3745 method greetmsg =
3746 if Array.length m_items != Array.length m_orig_items
3747 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3748 else ""
3750 method narrow pattern =
3751 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3752 match reopt with
3753 | None -> ()
3754 | Some re ->
3755 let rec loop accu n =
3756 if n = -1
3757 then (
3758 m_narrow_pattern <- pattern;
3759 m_items <- Array.of_list accu
3761 else
3762 let (s, _, _) as o = m_items.(n) in
3763 let accu =
3764 if (try ignore (Str.search_forward re s 0); true
3765 with Not_found -> false)
3766 then o :: accu
3767 else accu
3769 loop accu (n-1)
3771 loop [] (Array.length m_items - 1)
3773 method denarrow =
3774 m_orig_items <- (
3775 if usebookmarks
3776 then Array.of_list state.bookmarks
3777 else state.outlines
3779 m_items <- m_orig_items
3781 method remove m =
3782 if usebookmarks
3783 then
3784 if m >= 0 && m < Array.length m_items
3785 then (
3786 m_hadremovals <- true;
3787 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3788 let n = if n >= m then n+1 else n in
3789 m_items.(n)
3793 method reset anchor items =
3794 m_hadremovals <- false;
3795 if m_orig_items == empty || m_prev_items != items
3796 then (
3797 m_orig_items <- items;
3798 if String.length m_narrow_pattern = 0
3799 then m_items <- items;
3801 m_prev_items <- items;
3802 let rely = getanchory anchor in
3803 let active =
3804 let rec loop n best bestd =
3805 if n = Array.length m_items
3806 then best
3807 else
3808 let (_, _, anchor) = m_items.(n) in
3809 let orely = getanchory anchor in
3810 let d = abs (orely - rely) in
3811 if d < bestd
3812 then loop (n+1) n d
3813 else loop (n+1) best bestd
3815 loop 0 ~-1 max_int
3817 m_active <- active;
3818 m_first <- firstof m_first active
3819 end)
3822 let enterselector usebookmarks =
3823 let source = outlinesource usebookmarks in
3824 fun errmsg ->
3825 let outlines =
3826 if usebookmarks
3827 then Array.of_list state.bookmarks
3828 else state.outlines
3830 if Array.length outlines = 0
3831 then (
3832 showtext ' ' errmsg;
3834 else (
3835 state.text <- source#greetmsg;
3836 Wsi.setcursor Wsi.CURSOR_INHERIT;
3837 let anchor = getanchor () in
3838 source#reset anchor outlines;
3839 state.uioh <- coe (new outlinelistview ~source);
3840 G.postRedisplay "enter selector";
3844 let enteroutlinemode =
3845 let f = enterselector false in
3846 fun ()-> f "Document has no outline";
3849 let enterbookmarkmode =
3850 let f = enterselector true in
3851 fun () -> f "Document has no bookmarks (yet)";
3854 let color_of_string s =
3855 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3856 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3860 let color_to_string (r, g, b) =
3861 let r = truncate (r *. 256.0)
3862 and g = truncate (g *. 256.0)
3863 and b = truncate (b *. 256.0) in
3864 Printf.sprintf "%d/%d/%d" r g b
3867 let irect_of_string s =
3868 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3871 let irect_to_string (x0,y0,x1,y1) =
3872 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3875 let makecheckers () =
3876 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3877 following to say:
3878 converted by Issac Trotts. July 25, 2002 *)
3879 let image = GlPix.create `ubyte ~format:`luminance ~width:2 ~height:2 in
3880 Raw.sets_string (GlPix.to_raw image) ~pos:0 "\255\200\200\255";
3881 let id = GlTex.gen_texture () in
3882 GlTex.bind_texture `texture_2d id;
3883 GlPix.store (`unpack_alignment 1);
3884 GlTex.image2d image;
3885 List.iter (GlTex.parameter ~target:`texture_2d)
3886 [ `wrap_s `repeat;
3887 `wrap_t `repeat;
3888 `mag_filter `nearest;
3889 `min_filter `nearest ];
3893 let setcheckers enabled =
3894 match state.texid with
3895 | None ->
3896 if enabled then state.texid <- Some (makecheckers ())
3898 | Some texid ->
3899 if not enabled
3900 then (
3901 GlTex.delete_texture texid;
3902 state.texid <- None;
3906 let int_of_string_with_suffix s =
3907 let l = String.length s in
3908 let s1, shift =
3909 if l > 1
3910 then
3911 let suffix = Char.lowercase s.[l-1] in
3912 match suffix with
3913 | 'k' -> String.sub s 0 (l-1), 10
3914 | 'm' -> String.sub s 0 (l-1), 20
3915 | 'g' -> String.sub s 0 (l-1), 30
3916 | _ -> s, 0
3917 else s, 0
3919 let n = int_of_string s1 in
3920 let m = n lsl shift in
3921 if m < 0 || m < n
3922 then raise (Failure "value too large")
3923 else m
3926 let string_with_suffix_of_int n =
3927 if n = 0
3928 then "0"
3929 else
3930 let n, s =
3931 if n land ((1 lsl 30) - 1) = 0
3932 then n lsr 30, "G"
3933 else (
3934 if n land ((1 lsl 20) - 1) = 0
3935 then n lsr 20, "M"
3936 else (
3937 if n land ((1 lsl 10) - 1) = 0
3938 then n lsr 10, "K"
3939 else n, ""
3943 let rec loop s n =
3944 let h = n mod 1000 in
3945 let n = n / 1000 in
3946 if n = 0
3947 then string_of_int h ^ s
3948 else (
3949 let s = Printf.sprintf "_%03d%s" h s in
3950 loop s n
3953 loop "" n ^ s;
3956 let defghyllscroll = (40, 8, 32);;
3957 let ghyllscroll_of_string s =
3958 let (n, a, b) as nab =
3959 if s = "default"
3960 then defghyllscroll
3961 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3963 if n <= a || n <= b || a >= b
3964 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3965 nab;
3968 let ghyllscroll_to_string ((n, a, b) as nab) =
3969 if nab = defghyllscroll
3970 then "default"
3971 else Printf.sprintf "%d,%d,%d" n a b;
3974 let describe_location () =
3975 let f (fn, _) l =
3976 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3978 let fn, ln = List.fold_left f (-1, -1) state.layout in
3979 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3980 let percent =
3981 if maxy <= 0
3982 then 100.
3983 else (100. *. (float state.y /. float maxy))
3985 if fn = ln
3986 then
3987 Printf.sprintf "page %d of %d [%.2f%%]"
3988 (fn+1) state.pagecount percent
3989 else
3990 Printf.sprintf
3991 "pages %d-%d of %d [%.2f%%]"
3992 (fn+1) (ln+1) state.pagecount percent
3995 let setpresentationmode v =
3996 let (n, _, _) = getanchor () in
3997 let _, h = getpageyh n in
3998 let ips = if conf.presentation then calcips h else conf.interpagespace in
3999 state.anchor <- (n, 0.0, float ips);
4000 conf.presentation <- v;
4001 if conf.presentation
4002 then (
4003 if not conf.scrollbarinpm
4004 then state.scrollw <- 0;
4006 else state.scrollw <- conf.scrollbw;
4007 represent ();
4010 let enterinfomode =
4011 let btos b = if b then "\xe2\x88\x9a" else "" in
4012 let showextended = ref false in
4013 let leave mode = function
4014 | Confirm -> state.mode <- mode
4015 | Cancel -> state.mode <- mode in
4016 let src =
4017 (object
4018 val mutable m_first_time = true
4019 val mutable m_l = []
4020 val mutable m_a = [||]
4021 val mutable m_prev_uioh = nouioh
4022 val mutable m_prev_mode = View
4024 inherit lvsourcebase
4026 method reset prev_mode prev_uioh =
4027 m_a <- Array.of_list (List.rev m_l);
4028 m_l <- [];
4029 m_prev_mode <- prev_mode;
4030 m_prev_uioh <- prev_uioh;
4031 if m_first_time
4032 then (
4033 let rec loop n =
4034 if n >= Array.length m_a
4035 then ()
4036 else
4037 match m_a.(n) with
4038 | _, _, _, Action _ -> m_active <- n
4039 | _ -> loop (n+1)
4041 loop 0;
4042 m_first_time <- false;
4045 method int name get set =
4046 m_l <-
4047 (name, `int get, 1, Action (
4048 fun u ->
4049 let ondone s =
4050 try set (int_of_string s)
4051 with exn ->
4052 state.text <- Printf.sprintf "bad integer `%s': %s"
4053 s (Printexc.to_string exn)
4055 state.text <- "";
4056 let te = name ^ ": ", "", None, intentry, ondone, true in
4057 state.mode <- Textentry (te, leave m_prev_mode);
4059 )) :: m_l
4061 method int_with_suffix name get set =
4062 m_l <-
4063 (name, `intws get, 1, Action (
4064 fun u ->
4065 let ondone s =
4066 try set (int_of_string_with_suffix s)
4067 with exn ->
4068 state.text <- Printf.sprintf "bad integer `%s': %s"
4069 s (Printexc.to_string exn)
4071 state.text <- "";
4072 let te =
4073 name ^ ": ", "", None, intentry_with_suffix, ondone, true
4075 state.mode <- Textentry (te, leave m_prev_mode);
4077 )) :: m_l
4079 method bool ?(offset=1) ?(btos=btos) name get set =
4080 m_l <-
4081 (name, `bool (btos, get), offset, Action (
4082 fun u ->
4083 let v = get () in
4084 set (not v);
4086 )) :: m_l
4088 method color name get set =
4089 m_l <-
4090 (name, `color get, 1, Action (
4091 fun u ->
4092 let invalid = (nan, nan, nan) in
4093 let ondone s =
4094 let c =
4095 try color_of_string s
4096 with exn ->
4097 state.text <- Printf.sprintf "bad color `%s': %s"
4098 s (Printexc.to_string exn);
4099 invalid
4101 if c <> invalid
4102 then set c;
4104 let te = name ^ ": ", "", None, textentry, ondone, true in
4105 state.text <- color_to_string (get ());
4106 state.mode <- Textentry (te, leave m_prev_mode);
4108 )) :: m_l
4110 method string name get set =
4111 m_l <-
4112 (name, `string get, 1, Action (
4113 fun u ->
4114 let ondone s = set s in
4115 let te = name ^ ": ", "", None, textentry, ondone, true in
4116 state.mode <- Textentry (te, leave m_prev_mode);
4118 )) :: m_l
4120 method colorspace name get set =
4121 m_l <-
4122 (name, `string get, 1, Action (
4123 fun _ ->
4124 let source =
4125 let vals = [| "rgb"; "bgr"; "gray" |] in
4126 (object
4127 inherit lvsourcebase
4129 initializer
4130 m_active <- int_of_colorspace conf.colorspace;
4131 m_first <- 0;
4133 method getitemcount = Array.length vals
4134 method getitem n = (vals.(n), 0)
4135 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4136 ignore (uioh, first, pan, qsearch);
4137 if not cancel then set active;
4138 None
4139 method hasaction _ = true
4140 end)
4142 state.text <- "";
4143 let modehash = findkeyhash conf "info" in
4144 coe (new listview ~source ~trusted:true ~modehash)
4145 )) :: m_l
4147 method caption s offset =
4148 m_l <- (s, `empty, offset, Noaction) :: m_l
4150 method caption2 s f offset =
4151 m_l <- (s, `string f, offset, Noaction) :: m_l
4153 method getitemcount = Array.length m_a
4155 method getitem n =
4156 let tostr = function
4157 | `int f -> string_of_int (f ())
4158 | `intws f -> string_with_suffix_of_int (f ())
4159 | `string f -> f ()
4160 | `color f -> color_to_string (f ())
4161 | `bool (btos, f) -> btos (f ())
4162 | `empty -> ""
4164 let name, t, offset, _ = m_a.(n) in
4165 ((let s = tostr t in
4166 if String.length s > 0
4167 then Printf.sprintf "%s\t%s" name s
4168 else name),
4169 offset)
4171 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4172 let uiohopt =
4173 if not cancel
4174 then (
4175 m_qsearch <- qsearch;
4176 let uioh =
4177 match m_a.(active) with
4178 | _, _, _, Action f -> f uioh
4179 | _ -> uioh
4181 Some uioh
4183 else None
4185 m_active <- active;
4186 m_first <- first;
4187 m_pan <- pan;
4188 uiohopt
4190 method hasaction n =
4191 match m_a.(n) with
4192 | _, _, _, Action _ -> true
4193 | _ -> false
4194 end)
4196 let rec fillsrc prevmode prevuioh =
4197 let sep () = src#caption "" 0 in
4198 let colorp name get set =
4199 src#string name
4200 (fun () -> color_to_string (get ()))
4201 (fun v ->
4203 let c = color_of_string v in
4204 set c
4205 with exn ->
4206 state.text <- Printf.sprintf "bad color `%s': %s"
4207 v (Printexc.to_string exn);
4210 let oldmode = state.mode in
4211 let birdseye = isbirdseye state.mode in
4213 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
4215 src#bool "presentation mode"
4216 (fun () -> conf.presentation)
4217 (fun v -> setpresentationmode v);
4219 src#bool "ignore case in searches"
4220 (fun () -> conf.icase)
4221 (fun v -> conf.icase <- v);
4223 src#bool "preload"
4224 (fun () -> conf.preload)
4225 (fun v -> conf.preload <- v);
4227 src#bool "highlight links"
4228 (fun () -> conf.hlinks)
4229 (fun v -> conf.hlinks <- v);
4231 src#bool "under info"
4232 (fun () -> conf.underinfo)
4233 (fun v -> conf.underinfo <- v);
4235 src#bool "persistent bookmarks"
4236 (fun () -> conf.savebmarks)
4237 (fun v -> conf.savebmarks <- v);
4239 src#bool "proportional display"
4240 (fun () -> conf.proportional)
4241 (fun v -> reqlayout conf.angle v);
4243 src#bool "trim margins"
4244 (fun () -> conf.trimmargins)
4245 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
4247 src#bool "persistent location"
4248 (fun () -> conf.jumpback)
4249 (fun v -> conf.jumpback <- v);
4251 sep ();
4252 src#int "inter-page space"
4253 (fun () -> conf.interpagespace)
4254 (fun n ->
4255 conf.interpagespace <- n;
4256 docolumns conf.columns;
4257 let pageno, py =
4258 match state.layout with
4259 | [] -> 0, 0
4260 | l :: _ ->
4261 l.pageno, l.pagey
4263 state.maxy <- calcheight ();
4264 let y = getpagey pageno in
4265 gotoy (y + py)
4268 src#int "page bias"
4269 (fun () -> conf.pagebias)
4270 (fun v -> conf.pagebias <- v);
4272 src#int "scroll step"
4273 (fun () -> conf.scrollstep)
4274 (fun n -> conf.scrollstep <- n);
4276 src#int "horizontal scroll step"
4277 (fun () -> conf.hscrollstep)
4278 (fun v -> conf.hscrollstep <- v);
4280 src#int "auto scroll step"
4281 (fun () ->
4282 match state.autoscroll with
4283 | Some step -> step
4284 | _ -> conf.autoscrollstep)
4285 (fun n ->
4286 if state.autoscroll <> None
4287 then state.autoscroll <- Some n;
4288 conf.autoscrollstep <- n);
4290 src#int "zoom"
4291 (fun () -> truncate (conf.zoom *. 100.))
4292 (fun v -> setzoom ((float v) /. 100.));
4294 src#int "rotation"
4295 (fun () -> conf.angle)
4296 (fun v -> reqlayout v conf.proportional);
4298 src#int "scroll bar width"
4299 (fun () -> state.scrollw)
4300 (fun v ->
4301 state.scrollw <- v;
4302 conf.scrollbw <- v;
4303 reshape conf.winw conf.winh;
4306 src#int "scroll handle height"
4307 (fun () -> conf.scrollh)
4308 (fun v -> conf.scrollh <- v;);
4310 src#int "thumbnail width"
4311 (fun () -> conf.thumbw)
4312 (fun v ->
4313 conf.thumbw <- min 4096 v;
4314 match oldmode with
4315 | Birdseye beye ->
4316 leavebirdseye beye false;
4317 enterbirdseye ()
4318 | _ -> ()
4321 let mode = state.mode in
4322 src#string "columns"
4323 (fun () ->
4324 match conf.columns with
4325 | Csingle _ -> "1"
4326 | Cmulti (multi, _) -> multicolumns_to_string multi
4327 | Csplit (count, _) -> "-" ^ string_of_int count
4329 (fun v ->
4330 let n, a, b = multicolumns_of_string v in
4331 setcolumns mode n a b);
4333 sep ();
4334 src#caption "Presentation mode" 0;
4335 src#bool "scrollbar visible"
4336 (fun () -> conf.scrollbarinpm)
4337 (fun v ->
4338 if v != conf.scrollbarinpm
4339 then (
4340 conf.scrollbarinpm <- v;
4341 if conf.presentation
4342 then (
4343 state.scrollw <- if v then conf.scrollbw else 0;
4344 reshape conf.winw conf.winh;
4349 sep ();
4350 src#caption "Pixmap cache" 0;
4351 src#int_with_suffix "size (advisory)"
4352 (fun () -> conf.memlimit)
4353 (fun v -> conf.memlimit <- v);
4355 src#caption2 "used"
4356 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4357 (string_with_suffix_of_int state.memused)
4358 (Hashtbl.length state.tilemap)) 1;
4360 sep ();
4361 src#caption "Layout" 0;
4362 src#caption2 "Dimension"
4363 (fun () ->
4364 Printf.sprintf "%dx%d (virtual %dx%d)"
4365 conf.winw conf.winh
4366 state.w state.maxy)
4368 if conf.debug
4369 then
4370 src#caption2 "Position" (fun () ->
4371 Printf.sprintf "%dx%d" state.x state.y
4373 else
4374 src#caption2 "Visible" (fun () -> describe_location ()) 1
4377 sep ();
4378 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4379 "Save these parameters as global defaults at exit"
4380 (fun () -> conf.bedefault)
4381 (fun v -> conf.bedefault <- v)
4384 sep ();
4385 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4386 src#bool ~offset:0 ~btos "Extended parameters"
4387 (fun () -> !showextended)
4388 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4389 if !showextended
4390 then (
4391 src#bool "checkers"
4392 (fun () -> conf.checkers)
4393 (fun v -> conf.checkers <- v; setcheckers v);
4394 src#bool "update cursor"
4395 (fun () -> conf.updatecurs)
4396 (fun v -> conf.updatecurs <- v);
4397 src#bool "verbose"
4398 (fun () -> conf.verbose)
4399 (fun v -> conf.verbose <- v);
4400 src#bool "invert colors"
4401 (fun () -> conf.invert)
4402 (fun v -> conf.invert <- v);
4403 src#bool "max fit"
4404 (fun () -> conf.maxhfit)
4405 (fun v -> conf.maxhfit <- v);
4406 src#bool "redirect stderr"
4407 (fun () -> conf.redirectstderr)
4408 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4409 src#string "uri launcher"
4410 (fun () -> conf.urilauncher)
4411 (fun v -> conf.urilauncher <- v);
4412 src#string "path launcher"
4413 (fun () -> conf.pathlauncher)
4414 (fun v -> conf.pathlauncher <- v);
4415 src#string "tile size"
4416 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4417 (fun v ->
4419 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4420 conf.tilew <- max 64 w;
4421 conf.tileh <- max 64 h;
4422 flushtiles ();
4423 with exn ->
4424 state.text <- Printf.sprintf "bad tile size `%s': %s"
4425 v (Printexc.to_string exn));
4426 src#int "texture count"
4427 (fun () -> conf.texcount)
4428 (fun v ->
4429 if realloctexts v
4430 then conf.texcount <- v
4431 else showtext '!' " Failed to set texture count please retry later"
4433 src#int "slice height"
4434 (fun () -> conf.sliceheight)
4435 (fun v ->
4436 conf.sliceheight <- v;
4437 wcmd "sliceh %d" conf.sliceheight;
4439 src#int "anti-aliasing level"
4440 (fun () -> conf.aalevel)
4441 (fun v ->
4442 conf.aalevel <- bound v 0 8;
4443 state.anchor <- getanchor ();
4444 opendoc state.path state.password state.nameddest;
4446 src#string "page scroll scaling factor"
4447 (fun () -> string_of_float conf.pgscale)
4448 (fun v ->
4450 let s = float_of_string v in
4451 conf.pgscale <- s
4452 with exn ->
4453 state.text <- Printf.sprintf
4454 "bad page scroll scaling factor `%s': %s"
4455 v (Printexc.to_string exn)
4458 src#int "ui font size"
4459 (fun () -> fstate.fontsize)
4460 (fun v -> setfontsize (bound v 5 100));
4461 src#int "hint font size"
4462 (fun () -> conf.hfsize)
4463 (fun v -> conf.hfsize <- bound v 5 100);
4464 colorp "background color"
4465 (fun () -> conf.bgcolor)
4466 (fun v -> conf.bgcolor <- v);
4467 src#bool "crop hack"
4468 (fun () -> conf.crophack)
4469 (fun v -> conf.crophack <- v);
4470 src#string "trim fuzz"
4471 (fun () -> irect_to_string conf.trimfuzz)
4472 (fun v ->
4474 conf.trimfuzz <- irect_of_string v;
4475 if conf.trimmargins
4476 then settrim true conf.trimfuzz;
4477 with exn ->
4478 state.text <- Printf.sprintf "bad irect `%s': %s"
4479 v (Printexc.to_string exn)
4481 src#string "throttle"
4482 (fun () ->
4483 match conf.maxwait with
4484 | None -> "show place holder if page is not ready"
4485 | Some time ->
4486 if time = infinity
4487 then "wait for page to fully render"
4488 else
4489 "wait " ^ string_of_float time
4490 ^ " seconds before showing placeholder"
4492 (fun v ->
4494 let f = float_of_string v in
4495 if f <= 0.0
4496 then conf.maxwait <- None
4497 else conf.maxwait <- Some f
4498 with exn ->
4499 state.text <- Printf.sprintf "bad time `%s': %s"
4500 v (Printexc.to_string exn)
4502 src#string "ghyll scroll"
4503 (fun () ->
4504 match conf.ghyllscroll with
4505 | None -> ""
4506 | Some nab -> ghyllscroll_to_string nab
4508 (fun v ->
4510 let gs =
4511 if String.length v = 0
4512 then None
4513 else Some (ghyllscroll_of_string v)
4515 conf.ghyllscroll <- gs
4516 with exn ->
4517 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4518 v (Printexc.to_string exn)
4520 src#string "selection command"
4521 (fun () -> conf.selcmd)
4522 (fun v -> conf.selcmd <- v);
4523 src#colorspace "color space"
4524 (fun () -> colorspace_to_string conf.colorspace)
4525 (fun v ->
4526 conf.colorspace <- colorspace_of_int v;
4527 wcmd "cs %d" v;
4528 load state.layout;
4530 if pbousable ()
4531 then
4532 src#bool "use PBO"
4533 (fun () -> conf.usepbo)
4534 (fun v -> conf.usepbo <- v);
4535 src#bool "mouse wheel scrolls pages"
4536 (fun () -> conf.wheelbypage)
4537 (fun v -> conf.wheelbypage <- v);
4540 sep ();
4541 src#caption "Document" 0;
4542 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4543 src#caption2 "Pages"
4544 (fun () -> string_of_int state.pagecount) 1;
4545 src#caption2 "Dimensions"
4546 (fun () -> string_of_int (List.length state.pdims)) 1;
4547 if conf.trimmargins
4548 then (
4549 sep ();
4550 src#caption "Trimmed margins" 0;
4551 src#caption2 "Dimensions"
4552 (fun () -> string_of_int (List.length state.pdims)) 1;
4555 sep ();
4556 src#caption "OpenGL" 0;
4557 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
4558 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
4559 src#reset prevmode prevuioh;
4561 fun () ->
4562 state.text <- "";
4563 let prevmode = state.mode
4564 and prevuioh = state.uioh in
4565 fillsrc prevmode prevuioh;
4566 let source = (src :> lvsource) in
4567 let modehash = findkeyhash conf "info" in
4568 state.uioh <- coe (object (self)
4569 inherit listview ~source ~trusted:true ~modehash as super
4570 val mutable m_prevmemused = 0
4571 method infochanged = function
4572 | Memused ->
4573 if m_prevmemused != state.memused
4574 then (
4575 m_prevmemused <- state.memused;
4576 G.postRedisplay "memusedchanged";
4578 | Pdim -> G.postRedisplay "pdimchanged"
4579 | Docinfo -> fillsrc prevmode prevuioh
4581 method key key mask =
4582 if not (Wsi.withctrl mask)
4583 then
4584 match key with
4585 | 0xff51 | 0xff96 -> coe (self#updownlevel ~-1) (* (kp) left *)
4586 | 0xff53 | 0xff98 -> coe (self#updownlevel 1) (* (kp) right *)
4587 | _ -> super#key key mask
4588 else super#key key mask
4589 end);
4590 G.postRedisplay "info";
4593 let enterhelpmode =
4594 let source =
4595 (object
4596 inherit lvsourcebase
4597 method getitemcount = Array.length state.help
4598 method getitem n =
4599 let s, l, _ = state.help.(n) in
4600 (s, l)
4602 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4603 let optuioh =
4604 if not cancel
4605 then (
4606 m_qsearch <- qsearch;
4607 match state.help.(active) with
4608 | _, _, Action f -> Some (f uioh)
4609 | _ -> Some (uioh)
4611 else None
4613 m_active <- active;
4614 m_first <- first;
4615 m_pan <- pan;
4616 optuioh
4618 method hasaction n =
4619 match state.help.(n) with
4620 | _, _, Action _ -> true
4621 | _ -> false
4623 initializer
4624 m_active <- -1
4625 end)
4626 in fun () ->
4627 let modehash = findkeyhash conf "help" in
4628 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4629 G.postRedisplay "help";
4632 let entermsgsmode =
4633 let msgsource =
4634 let re = Str.regexp "[\r\n]" in
4635 (object
4636 inherit lvsourcebase
4637 val mutable m_items = [||]
4639 method getitemcount = 1 + Array.length m_items
4641 method getitem n =
4642 if n = 0
4643 then "[Clear]", 0
4644 else m_items.(n-1), 0
4646 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4647 ignore uioh;
4648 if not cancel
4649 then (
4650 if active = 0
4651 then Buffer.clear state.errmsgs;
4652 m_qsearch <- qsearch;
4654 m_active <- active;
4655 m_first <- first;
4656 m_pan <- pan;
4657 None
4659 method hasaction n =
4660 n = 0
4662 method reset =
4663 state.newerrmsgs <- false;
4664 let l = Str.split re (Buffer.contents state.errmsgs) in
4665 m_items <- Array.of_list l
4667 initializer
4668 m_active <- 0
4669 end)
4670 in fun () ->
4671 state.text <- "";
4672 msgsource#reset;
4673 let source = (msgsource :> lvsource) in
4674 let modehash = findkeyhash conf "listview" in
4675 state.uioh <- coe (object
4676 inherit listview ~source ~trusted:false ~modehash as super
4677 method display =
4678 if state.newerrmsgs
4679 then msgsource#reset;
4680 super#display
4681 end);
4682 G.postRedisplay "msgs";
4685 let quickbookmark ?title () =
4686 match state.layout with
4687 | [] -> ()
4688 | l :: _ ->
4689 let title =
4690 match title with
4691 | None ->
4692 let sec = Unix.gettimeofday () in
4693 let tm = Unix.localtime sec in
4694 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4695 (l.pageno+1)
4696 tm.Unix.tm_mday
4697 tm.Unix.tm_mon
4698 (tm.Unix.tm_year + 1900)
4699 tm.Unix.tm_hour
4700 tm.Unix.tm_min
4701 | Some title -> title
4703 state.bookmarks <- (title, 0, getanchor1 l) :: state.bookmarks
4706 let doreshape w h =
4707 state.fullscreen <- None;
4708 Wsi.reshape w h;
4711 let setautoscrollspeed step goingdown =
4712 let incr = max 1 ((abs step) / 2) in
4713 let incr = if goingdown then incr else -incr in
4714 let astep = step + incr in
4715 state.autoscroll <- Some astep;
4718 let gotounder = function
4719 | Ulinkgoto (pageno, top) ->
4720 if pageno >= 0
4721 then (
4722 addnav ();
4723 gotopage1 pageno top;
4726 | Ulinkuri s ->
4727 gotouri s
4729 | Uremote (filename, pageno) ->
4730 let path =
4731 if Sys.file_exists filename
4732 then filename
4733 else
4734 let dir = Filename.dirname state.path in
4735 let path = Filename.concat dir filename in
4736 if Sys.file_exists path
4737 then path
4738 else ""
4740 if String.length path > 0
4741 then (
4742 let anchor = getanchor () in
4743 let ranchor = state.path, state.password, anchor in
4744 state.anchor <- (pageno, 0.0, 0.0);
4745 state.ranchors <- ranchor :: state.ranchors;
4746 opendoc path "" "";
4748 else showtext '!' ("Could not find " ^ filename)
4750 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4753 let canpan () =
4754 match conf.columns with
4755 | Csplit _ -> true
4756 | _ -> conf.zoom > 1.0
4759 let existsinrow pageno (columns, coverA, coverB) p =
4760 let last = ((pageno - coverA) mod columns) + columns in
4761 let rec any = function
4762 | [] -> false
4763 | l :: rest ->
4764 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
4765 then p l
4766 else (
4767 if not (p l)
4768 then (if l.pageno = last then false else any rest)
4769 else true
4772 any state.layout
4775 let nextpage () =
4776 match state.layout with
4777 | [] -> ()
4778 | l :: rest ->
4779 match conf.columns with
4780 | Csingle _ ->
4781 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4782 then
4783 let y = clamp (pgscale conf.winh) in
4784 gotoghyll y
4785 else
4786 let pageno = min (l.pageno+1) (state.pagecount-1) in
4787 gotoghyll (getpagey pageno)
4788 | Cmulti ((c, _, _) as cl, _) ->
4789 if conf.presentation
4790 && (existsinrow l.pageno cl
4791 (fun l -> l.pageh > l.pagey + l.pagevh))
4792 then
4793 let y = clamp (pgscale conf.winh) in
4794 gotoghyll y
4795 else
4796 let pageno = min (l.pageno+c) (state.pagecount-1) in
4797 gotoghyll (getpagey pageno)
4798 | Csplit (n, _) ->
4799 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
4800 then
4801 let pagey, pageh = getpageyh l.pageno in
4802 let pagey = pagey + pageh * l.pagecol in
4803 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
4804 gotoghyll (pagey + pageh + ips)
4807 let prevpage () =
4808 match state.layout with
4809 | [] -> ()
4810 | l :: _ ->
4811 match conf.columns with
4812 | Csingle _ ->
4813 if conf.presentation && l.pagey != 0
4814 then
4815 gotoghyll (clamp (pgscale ~-(conf.winh)))
4816 else
4817 let pageno = max 0 (l.pageno-1) in
4818 gotoghyll (getpagey pageno)
4819 | Cmulti ((c, _, coverB) as cl, _) ->
4820 if conf.presentation &&
4821 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
4822 then
4823 gotoghyll (clamp (pgscale ~-(conf.winh)))
4824 else
4825 let decr =
4826 if l.pageno = state.pagecount - coverB
4827 then 1
4828 else c
4830 let pageno = max 0 (l.pageno-decr) in
4831 gotoghyll (getpagey pageno)
4832 | Csplit (n, _) ->
4833 let y =
4834 if l.pagecol = 0
4835 then
4836 if l.pageno = 0
4837 then l.pagey
4838 else
4839 let pageno = max 0 (l.pageno-1) in
4840 let pagey, pageh = getpageyh pageno in
4841 pagey + (n-1)*pageh
4842 else
4843 let pagey, pageh = getpageyh l.pageno in
4844 pagey + pageh * (l.pagecol-1) - conf.interpagespace
4846 gotoghyll y
4849 let viewkeyboard key mask =
4850 let enttext te =
4851 let mode = state.mode in
4852 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4853 state.text <- "";
4854 enttext ();
4855 G.postRedisplay "view:enttext"
4857 let ctrl = Wsi.withctrl mask in
4858 let key =
4859 if key >= 0xffb0 && key < 0xffb9 then key - 0xffb0 + 48 else key
4861 match key with
4862 | 81 -> (* Q *)
4863 exit 0
4865 | 0xff63 -> (* insert *)
4866 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
4867 then (
4868 state.mode <- LinkNav (Ltgendir 0);
4869 gotoy state.y;
4871 else showtext '!' "Keyboard link navigation does not work under rotation"
4873 | 0xff1b | 113 -> (* escape / q *)
4874 begin match state.mstate with
4875 | Mzoomrect _ ->
4876 state.mstate <- Mnone;
4877 Wsi.setcursor Wsi.CURSOR_INHERIT;
4878 G.postRedisplay "kill zoom rect";
4879 | _ ->
4880 begin match state.mode with
4881 | LinkNav _ ->
4882 state.mode <- View;
4883 G.postRedisplay "esc leave linknav"
4884 | _ ->
4885 match state.ranchors with
4886 | [] -> raise Quit
4887 | (path, password, anchor) :: rest ->
4888 state.ranchors <- rest;
4889 state.anchor <- anchor;
4890 opendoc path password ""
4891 end;
4892 end;
4894 | 0xff08 -> (* backspace *)
4895 gotoghyll (getnav ~-1)
4897 | 111 -> (* o *)
4898 enteroutlinemode ()
4900 | 117 -> (* u *)
4901 state.rects <- [];
4902 state.text <- "";
4903 G.postRedisplay "dehighlight";
4905 | 47 | 63 -> (* / ? *)
4906 let ondone isforw s =
4907 cbput state.hists.pat s;
4908 state.searchpattern <- s;
4909 search s isforw
4911 let s = String.create 1 in
4912 s.[0] <- Char.chr key;
4913 enttext (s, "", Some (onhist state.hists.pat),
4914 textentry, ondone (key = 47), true)
4916 | 43 | 0xffab | 61 when ctrl -> (* ctrl-+ or ctrl-= *)
4917 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4918 setzoom (conf.zoom +. incr)
4920 | 43 | 0xffab -> (* + *)
4921 let ondone s =
4922 let n =
4923 try int_of_string s with exc ->
4924 state.text <- Printf.sprintf "bad integer `%s': %s"
4925 s (Printexc.to_string exc);
4926 max_int
4928 if n != max_int
4929 then (
4930 conf.pagebias <- n;
4931 state.text <- "page bias is now " ^ string_of_int n;
4934 enttext ("page bias: ", "", None, intentry, ondone, true)
4936 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4937 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4938 setzoom (max 0.01 (conf.zoom -. decr))
4940 | 45 | 0xffad -> (* - *)
4941 let ondone msg = state.text <- msg in
4942 enttext (
4943 "option [acfhilpstvxACFPRSZTIS]: ", "", None,
4944 optentry state.mode, ondone, true
4947 | 48 when ctrl -> (* ctrl-0 *)
4948 setzoom 1.0
4950 | 49 when ctrl -> (* ctrl-1 *)
4951 let cols =
4952 match conf.columns with
4953 | Csingle _ | Cmulti _ -> 1
4954 | Csplit (n, _) -> n
4956 let zoom = zoomforh conf.winw conf.winh state.scrollw cols in
4957 if zoom < 1.0
4958 then setzoom zoom
4960 | 0xffc6 -> (* f9 *)
4961 togglebirdseye ()
4963 | 57 when ctrl -> (* ctrl-9 *)
4964 togglebirdseye ()
4966 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4967 when not ctrl -> (* 0..9 *)
4968 let ondone s =
4969 let n =
4970 try int_of_string s with exc ->
4971 state.text <- Printf.sprintf "bad integer `%s': %s"
4972 s (Printexc.to_string exc);
4975 if n >= 0
4976 then (
4977 addnav ();
4978 cbput state.hists.pag (string_of_int n);
4979 gotopage1 (n + conf.pagebias - 1) 0;
4982 let pageentry text key =
4983 match Char.unsafe_chr key with
4984 | 'g' -> TEdone text
4985 | _ -> intentry text key
4987 let text = "x" in text.[0] <- Char.chr key;
4988 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone, true)
4990 | 98 -> (* b *)
4991 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4992 reshape conf.winw conf.winh;
4994 | 108 -> (* l *)
4995 conf.hlinks <- not conf.hlinks;
4996 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4997 G.postRedisplay "toggle highlightlinks";
4999 | 70 -> (* F *)
5000 state.glinks <- true;
5001 let mode = state.mode in
5002 state.mode <- Textentry (
5003 (":", "", None, linknentry, linkndone gotounder, false),
5004 (fun _ ->
5005 state.glinks <- false;
5006 state.mode <- mode)
5008 state.text <- "";
5009 G.postRedisplay "view:linkent(F)"
5011 | 121 -> (* y *)
5012 state.glinks <- true;
5013 let mode = state.mode in
5014 state.mode <- Textentry (
5015 (":", "", None, linknentry, linkndone (fun under ->
5016 match Ne.pipe () with
5017 | Ne.Exn exn ->
5018 showtext '!' (Printf.sprintf "pipe failed: %s"
5019 (Printexc.to_string exn));
5020 | Ne.Res (r, w) ->
5021 let popened =
5022 try popen conf.selcmd [r, 0; w, -1]; true
5023 with exn ->
5024 showtext '!'
5025 (Printf.sprintf "failed to execute %s: %s"
5026 conf.selcmd (Printexc.to_string exn));
5027 false
5029 let clo cap fd =
5030 Ne.clo fd (fun msg ->
5031 showtext '!' (Printf.sprintf "failed to close %s: %s" cap msg)
5034 let s = undertext under in
5035 if popened
5036 then
5037 (try
5038 let l = String.length s in
5039 let n = tempfailureretry (Unix.write w s 0) l in
5040 if n != l
5041 then
5042 showtext '!'
5043 (Printf.sprintf
5044 "failed to write %d characters to sel pipe, wrote %d"
5047 with exn ->
5048 showtext '!'
5049 (Printf.sprintf "failed to write to sel pipe: %s"
5050 (Printexc.to_string exn)
5053 else dolog "%s" s;
5054 clo "pipe/r" r;
5055 clo "pipe/w" w;
5056 ), false
5058 fun _ ->
5059 state.glinks <- false;
5060 state.mode <- mode
5062 state.text <- "";
5063 G.postRedisplay "view:linkent"
5065 | 97 -> (* a *)
5066 begin match state.autoscroll with
5067 | Some step ->
5068 conf.autoscrollstep <- step;
5069 state.autoscroll <- None
5070 | None ->
5071 if conf.autoscrollstep = 0
5072 then state.autoscroll <- Some 1
5073 else state.autoscroll <- Some conf.autoscrollstep
5076 | 112 when ctrl -> (* ctrl-p *)
5077 launchpath ()
5079 | 80 -> (* P *)
5080 setpresentationmode (not conf.presentation);
5081 showtext ' ' ("presentation mode " ^
5082 if conf.presentation then "on" else "off");
5084 | 102 -> (* f *)
5085 begin match state.fullscreen with
5086 | None ->
5087 state.fullscreen <- Some (conf.winw, conf.winh);
5088 Wsi.fullscreen ()
5089 | Some (w, h) ->
5090 state.fullscreen <- None;
5091 doreshape w h
5094 | 112 | 78 -> (* p|N *)
5095 search state.searchpattern false
5097 | 110 | 0xffc0 -> (* n|F3 *)
5098 search state.searchpattern true
5100 | 116 -> (* t *)
5101 begin match state.layout with
5102 | [] -> ()
5103 | l :: _ ->
5104 gotoy_and_clear_text (getpagey l.pageno)
5107 | 32 -> (* space *)
5108 nextpage ()
5110 | 0xff9f | 0xffff -> (* delete *)
5111 prevpage ()
5113 | 61 -> (* = *)
5114 showtext ' ' (describe_location ());
5116 | 119 -> (* w *)
5117 begin match state.layout with
5118 | [] -> ()
5119 | l :: _ ->
5120 doreshape (l.pagew + state.scrollw) l.pageh;
5121 G.postRedisplay "w"
5124 | 39 -> (* ' *)
5125 enterbookmarkmode ()
5127 | 104 | 0xffbe -> (* h|F1 *)
5128 enterhelpmode ()
5130 | 105 -> (* i *)
5131 enterinfomode ()
5133 | 101 when Buffer.length state.errmsgs > 0 -> (* e *)
5134 entermsgsmode ()
5136 | 109 -> (* m *)
5137 let ondone s =
5138 match state.layout with
5139 | l :: _ ->
5140 if String.length s > 0
5141 then
5142 state.bookmarks <- (s, 0, getanchor1 l) :: state.bookmarks
5143 | _ -> ()
5145 enttext ("bookmark: ", "", None, textentry, ondone, true)
5147 | 126 -> (* ~ *)
5148 quickbookmark ();
5149 showtext ' ' "Quick bookmark added";
5151 | 122 -> (* z *)
5152 begin match state.layout with
5153 | l :: _ ->
5154 let rect = getpdimrect l.pagedimno in
5155 let w, h =
5156 if conf.crophack
5157 then
5158 (truncate (1.8 *. (rect.(1) -. rect.(0))),
5159 truncate (1.2 *. (rect.(3) -. rect.(0))))
5160 else
5161 (truncate (rect.(1) -. rect.(0)),
5162 truncate (rect.(3) -. rect.(0)))
5164 let w = truncate ((float w)*.conf.zoom)
5165 and h = truncate ((float h)*.conf.zoom) in
5166 if w != 0 && h != 0
5167 then (
5168 state.anchor <- getanchor ();
5169 doreshape (w + state.scrollw) (h + conf.interpagespace)
5171 G.postRedisplay "z";
5173 | [] -> ()
5176 | 50 when ctrl -> (* ctrl-2 *)
5177 let maxw = getmaxw () in
5178 if maxw > 0.0
5179 then setzoom (maxw /. float conf.winw)
5181 | 60 | 62 -> (* < > *)
5182 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
5184 | 91 | 93 -> (* [ ] *)
5185 conf.colorscale <-
5186 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
5188 G.postRedisplay "brightness";
5190 | 99 when state.mode = View -> (* c *)
5191 let (c, a, b), z =
5192 match state.prevcolumns with
5193 | None -> (1, 0, 0), 1.0
5194 | Some (columns, z) ->
5195 let cab =
5196 match columns with
5197 | Csplit (c, _) -> -c, 0, 0
5198 | Cmulti ((c, a, b), _) -> c, a, b
5199 | Csingle _ -> 1, 0, 0
5201 cab, z
5203 setcolumns View c a b;
5204 setzoom z;
5206 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
5207 setzoom state.prevzoom
5209 | 107 | 0xff52 | 0xff97 -> (* k (kp) up *)
5210 begin match state.autoscroll with
5211 | None ->
5212 begin match state.mode with
5213 | Birdseye beye -> upbirdseye 1 beye
5214 | _ ->
5215 if ctrl
5216 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
5217 else gotoy_and_clear_text (clamp (-conf.scrollstep))
5219 | Some n ->
5220 setautoscrollspeed n false
5223 | 106 | 0xff54 | 0xff99 -> (* j (kp) down *)
5224 begin match state.autoscroll with
5225 | None ->
5226 begin match state.mode with
5227 | Birdseye beye -> downbirdseye 1 beye
5228 | _ ->
5229 if ctrl
5230 then gotoy_and_clear_text (clamp (conf.winh/2))
5231 else gotoy_and_clear_text (clamp conf.scrollstep)
5233 | Some n ->
5234 setautoscrollspeed n true
5237 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
5238 if canpan ()
5239 then
5240 let dx =
5241 if ctrl
5242 then conf.winw / 2
5243 else conf.hscrollstep
5245 let dx = if key = 0xff51 then dx else -dx in
5246 state.x <- state.x + dx;
5247 gotoy_and_clear_text state.y
5248 else (
5249 state.text <- "";
5250 G.postRedisplay "lef/right"
5253 | 0xff55 | 0xff9a -> (* (kp) prior *)
5254 let y =
5255 if ctrl
5256 then
5257 match state.layout with
5258 | [] -> state.y
5259 | l :: _ -> state.y - l.pagey
5260 else
5261 clamp (pgscale (-conf.winh))
5263 gotoghyll y
5265 | 0xff56 | 0xff9b -> (* (kp) next *)
5266 let y =
5267 if ctrl
5268 then
5269 match List.rev state.layout with
5270 | [] -> state.y
5271 | l :: _ -> getpagey l.pageno
5272 else
5273 clamp (pgscale conf.winh)
5275 gotoghyll y
5277 | 103 | 0xff50 | 0xff95 -> (* g (kp) home *)
5278 gotoghyll 0
5279 | 71 | 0xff57 | 0xff9c -> (* G end *)
5280 gotoghyll (clamp state.maxy)
5282 | 0xff53 when Wsi.withalt mask -> (* alt-right *)
5283 gotoghyll (getnav 1)
5284 | 0xff51 when Wsi.withalt mask -> (* alt-left *)
5285 gotoghyll (getnav ~-1)
5287 | 114 -> (* r *)
5288 reload ()
5290 | 118 when conf.debug -> (* v *)
5291 state.rects <- [];
5292 List.iter (fun l ->
5293 match getopaque l.pageno with
5294 | None -> ()
5295 | Some opaque ->
5296 let x0, y0, x1, y1 = pagebbox opaque in
5297 let a,b = float x0, float y0 in
5298 let c,d = float x1, float y0 in
5299 let e,f = float x1, float y1 in
5300 let h,j = float x0, float y1 in
5301 let rect = (a,b,c,d,e,f,h,j) in
5302 debugrect rect;
5303 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
5304 ) state.layout;
5305 G.postRedisplay "v";
5307 | _ ->
5308 vlog "huh? %s" (Wsi.keyname key)
5311 let linknavkeyboard key mask linknav =
5312 let getpage pageno =
5313 let rec loop = function
5314 | [] -> None
5315 | l :: _ when l.pageno = pageno -> Some l
5316 | _ :: rest -> loop rest
5317 in loop state.layout
5319 let doexact (pageno, n) =
5320 match getopaque pageno, getpage pageno with
5321 | Some opaque, Some l ->
5322 if key = 0xff0d || key = 0xff8d (* (kp)enter *)
5323 then
5324 let under = getlink opaque n in
5325 G.postRedisplay "link gotounder";
5326 gotounder under;
5327 state.mode <- View;
5328 else
5329 let opt, dir =
5330 match key with
5331 | 0xff50 -> (* home *)
5332 Some (findlink opaque LDfirst), -1
5334 | 0xff57 -> (* end *)
5335 Some (findlink opaque LDlast), 1
5337 | 0xff51 -> (* left *)
5338 Some (findlink opaque (LDleft n)), -1
5340 | 0xff53 -> (* right *)
5341 Some (findlink opaque (LDright n)), 1
5343 | 0xff52 -> (* up *)
5344 Some (findlink opaque (LDup n)), -1
5346 | 0xff54 -> (* down *)
5347 Some (findlink opaque (LDdown n)), 1
5349 | _ -> None, 0
5351 let pwl l dir =
5352 begin match findpwl l.pageno dir with
5353 | Pwlnotfound -> ()
5354 | Pwl pageno ->
5355 let notfound dir =
5356 state.mode <- LinkNav (Ltgendir dir);
5357 let y, h = getpageyh pageno in
5358 let y =
5359 if dir < 0
5360 then y + h - conf.winh
5361 else y
5363 gotoy y
5365 begin match getopaque pageno, getpage pageno with
5366 | Some opaque, Some _ ->
5367 let link =
5368 let ld = if dir > 0 then LDfirst else LDlast in
5369 findlink opaque ld
5371 begin match link with
5372 | Lfound m ->
5373 showlinktype (getlink opaque m);
5374 state.mode <- LinkNav (Ltexact (pageno, m));
5375 G.postRedisplay "linknav jpage";
5376 | _ -> notfound dir
5377 end;
5378 | _ -> notfound dir
5379 end;
5380 end;
5382 begin match opt with
5383 | Some Lnotfound -> pwl l dir;
5384 | Some (Lfound m) ->
5385 if m = n
5386 then pwl l dir
5387 else (
5388 let _, y0, _, y1 = getlinkrect opaque m in
5389 if y0 < l.pagey
5390 then gotopage1 l.pageno y0
5391 else (
5392 let d = fstate.fontsize + 1 in
5393 if y1 - l.pagey > l.pagevh - d
5394 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh + d)
5395 else G.postRedisplay "linknav";
5397 showlinktype (getlink opaque m);
5398 state.mode <- LinkNav (Ltexact (l.pageno, m));
5401 | None -> viewkeyboard key mask
5402 end;
5403 | _ -> viewkeyboard key mask
5405 if key = 0xff63
5406 then (
5407 state.mode <- View;
5408 G.postRedisplay "leave linknav"
5410 else
5411 match linknav with
5412 | Ltgendir _ -> viewkeyboard key mask
5413 | Ltexact exact -> doexact exact
5416 let keyboard key mask =
5417 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5418 then wcmd "interrupt"
5419 else state.uioh <- state.uioh#key key mask
5422 let birdseyekeyboard key mask
5423 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5424 let incr =
5425 match conf.columns with
5426 | Csingle _ -> 1
5427 | Cmulti ((c, _, _), _) -> c
5428 | Csplit _ -> failwith "bird's eye split mode"
5430 let pgh layout = List.fold_left (fun m l -> max l.pageh m) conf.winh layout in
5431 match key with
5432 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5433 let y, h = getpageyh pageno in
5434 let top = (conf.winh - h) / 2 in
5435 gotoy (max 0 (y - top))
5436 | 0xff0d (* enter *)
5437 | 0xff8d -> leavebirdseye beye false (* kp enter *)
5438 | 0xff1b -> leavebirdseye beye true (* escape *)
5439 | 0xff52 -> upbirdseye incr beye (* up *)
5440 | 0xff54 -> downbirdseye incr beye (* down *)
5441 | 0xff51 -> upbirdseye 1 beye (* left *)
5442 | 0xff53 -> downbirdseye 1 beye (* right *)
5444 | 0xff55 -> (* prior *)
5445 begin match state.layout with
5446 | l :: _ ->
5447 if l.pagey != 0
5448 then (
5449 state.mode <- Birdseye (
5450 oconf, leftx, l.pageno, hooverpageno, anchor
5452 gotopage1 l.pageno 0;
5454 else (
5455 let layout = layout (state.y-conf.winh) (pgh state.layout) in
5456 match layout with
5457 | [] -> gotoy (clamp (-conf.winh))
5458 | l :: _ ->
5459 state.mode <- Birdseye (
5460 oconf, leftx, l.pageno, hooverpageno, anchor
5462 gotopage1 l.pageno 0
5465 | [] -> gotoy (clamp (-conf.winh))
5466 end;
5468 | 0xff56 -> (* next *)
5469 begin match List.rev state.layout with
5470 | l :: _ ->
5471 let layout = layout (state.y + (pgh state.layout)) conf.winh in
5472 begin match layout with
5473 | [] ->
5474 let incr = l.pageh - l.pagevh in
5475 if incr = 0
5476 then (
5477 state.mode <-
5478 Birdseye (
5479 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5481 G.postRedisplay "birdseye pagedown";
5483 else gotoy (clamp (incr + conf.interpagespace*2));
5485 | l :: _ ->
5486 state.mode <-
5487 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5488 gotopage1 l.pageno 0;
5491 | [] -> gotoy (clamp conf.winh)
5492 end;
5494 | 0xff50 -> (* home *)
5495 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5496 gotopage1 0 0
5498 | 0xff57 -> (* end *)
5499 let pageno = state.pagecount - 1 in
5500 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5501 if not (pagevisible state.layout pageno)
5502 then
5503 let h =
5504 match List.rev state.pdims with
5505 | [] -> conf.winh
5506 | (_, _, h, _) :: _ -> h
5508 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
5509 else G.postRedisplay "birdseye end";
5510 | _ -> viewkeyboard key mask
5513 let drawpage l linkindexbase =
5514 let color =
5515 match state.mode with
5516 | Textentry _ -> scalecolor 0.4
5517 | LinkNav _
5518 | View -> scalecolor 1.0
5519 | Birdseye (_, _, pageno, hooverpageno, _) ->
5520 if l.pageno = hooverpageno
5521 then scalecolor 0.9
5522 else (
5523 if l.pageno = pageno
5524 then scalecolor 1.0
5525 else scalecolor 0.8
5528 drawtiles l color;
5529 begin match getopaque l.pageno with
5530 | Some opaque ->
5531 if tileready l l.pagex l.pagey
5532 then
5533 let x = l.pagedispx - l.pagex
5534 and y = l.pagedispy - l.pagey in
5535 let hlmask =
5536 match conf.columns with
5537 | Csingle _ | Cmulti _ ->
5538 (if conf.hlinks then 1 else 0)
5539 + (if state.glinks
5540 && not (isbirdseye state.mode) then 2 else 0)
5541 | _ -> 0
5543 let s =
5544 match state.mode with
5545 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5546 | _ -> ""
5548 postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize);
5549 else 0
5551 | _ -> 0
5552 end;
5555 let scrollindicator () =
5556 let sbw, ph, sh = state.uioh#scrollph in
5557 let sbh, pw, sw = state.uioh#scrollpw in
5559 GlDraw.color (0.64, 0.64, 0.64);
5560 GlDraw.rect
5561 (float (conf.winw - sbw), 0.)
5562 (float conf.winw, float conf.winh)
5564 GlDraw.rect
5565 (0., float (conf.winh - sbh))
5566 (float (conf.winw - state.scrollw - 1), float conf.winh)
5568 GlDraw.color (0.0, 0.0, 0.0);
5570 GlDraw.rect
5571 (float (conf.winw - sbw), ph)
5572 (float conf.winw, ph +. sh)
5574 GlDraw.rect
5575 (pw, float (conf.winh - sbh))
5576 (pw +. sw, float conf.winh)
5580 let showsel () =
5581 match state.mstate with
5582 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5585 | Msel ((x0, y0), (x1, y1)) ->
5586 let rec loop = function
5587 | l :: ls ->
5588 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5589 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5590 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5591 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5592 then
5593 match getopaque l.pageno with
5594 | Some opaque ->
5595 let x0, y0 = pagetranslatepoint l x0 y0 in
5596 let x1, y1 = pagetranslatepoint l x1 y1 in
5597 seltext opaque (x0, y0, x1, y1);
5598 | _ -> ()
5599 else loop ls
5600 | [] -> ()
5602 loop state.layout
5605 let showrects rects =
5606 Gl.enable `blend;
5607 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5608 GlDraw.polygon_mode `both `fill;
5609 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5610 List.iter
5611 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5612 List.iter (fun l ->
5613 if l.pageno = pageno
5614 then (
5615 let dx = float (l.pagedispx - l.pagex) in
5616 let dy = float (l.pagedispy - l.pagey) in
5617 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5618 GlDraw.begins `quads;
5620 GlDraw.vertex2 (x0+.dx, y0+.dy);
5621 GlDraw.vertex2 (x1+.dx, y1+.dy);
5622 GlDraw.vertex2 (x2+.dx, y2+.dy);
5623 GlDraw.vertex2 (x3+.dx, y3+.dy);
5625 GlDraw.ends ();
5627 ) state.layout
5628 ) rects
5630 Gl.disable `blend;
5633 let display () =
5634 GlClear.color (scalecolor2 conf.bgcolor);
5635 GlClear.clear [`color];
5636 let rec loop linkindexbase = function
5637 | l :: rest ->
5638 let linkindexbase = linkindexbase + drawpage l linkindexbase in
5639 loop linkindexbase rest
5640 | [] -> ()
5642 loop 0 state.layout;
5643 let rects =
5644 match state.mode with
5645 | LinkNav (Ltexact (pageno, linkno)) ->
5646 begin match getopaque pageno with
5647 | Some opaque ->
5648 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5649 (pageno, 5, (
5650 float x0, float y0,
5651 float x1, float y0,
5652 float x1, float y1,
5653 float x0, float y1)
5654 ) :: state.rects
5655 | None -> state.rects
5657 | _ -> state.rects
5659 showrects rects;
5660 showsel ();
5661 state.uioh#display;
5662 begin match state.mstate with
5663 | Mzoomrect ((x0, y0), (x1, y1)) ->
5664 Gl.enable `blend;
5665 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5666 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5667 GlDraw.rect (float x0, float y0)
5668 (float x1, float y1);
5669 Gl.disable `blend;
5670 | _ -> ()
5671 end;
5672 enttext ();
5673 scrollindicator ();
5674 if not state.wthack then Wsi.swapb ();
5677 let zoomrect x y x1 y1 =
5678 let x0 = min x x1
5679 and x1 = max x x1
5680 and y0 = min y y1 in
5681 gotoy (state.y + y0);
5682 state.anchor <- getanchor ();
5683 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5684 let margin =
5685 if state.w < conf.winw - state.scrollw
5686 then (conf.winw - state.scrollw - state.w) / 2
5687 else 0
5689 state.x <- (state.x + margin) - x0;
5690 setzoom zoom;
5691 Wsi.setcursor Wsi.CURSOR_INHERIT;
5692 state.mstate <- Mnone;
5695 let scrollx x =
5696 let winw = conf.winw - state.scrollw - 1 in
5697 let s = float x /. float winw in
5698 let destx = truncate (float (state.w + winw) *. s) in
5699 state.x <- winw - destx;
5700 gotoy_and_clear_text state.y;
5701 state.mstate <- Mscrollx;
5704 let scrolly y =
5705 let s = float y /. float conf.winh in
5706 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5707 gotoy_and_clear_text desty;
5708 state.mstate <- Mscrolly;
5711 let viewmouse button down x y mask =
5712 match button with
5713 | n when (n == 4 || n == 5) && not down ->
5714 if Wsi.withctrl mask
5715 then (
5716 match state.mstate with
5717 | Mzoom (oldn, i) ->
5718 if oldn = n
5719 then (
5720 if i = 2
5721 then
5722 let incr =
5723 match n with
5724 | 5 ->
5725 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5726 | _ ->
5727 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5729 let zoom = conf.zoom -. incr in
5730 setzoom zoom;
5731 state.mstate <- Mzoom (n, 0);
5732 else
5733 state.mstate <- Mzoom (n, i+1);
5735 else state.mstate <- Mzoom (n, 0)
5737 | _ -> state.mstate <- Mzoom (n, 0)
5739 else (
5740 match state.autoscroll with
5741 | Some step -> setautoscrollspeed step (n=4)
5742 | None ->
5743 if conf.wheelbypage
5744 then (
5745 if n = 4
5746 then nextpage ()
5747 else prevpage ()
5749 else
5750 let incr =
5751 if n = 4
5752 then -conf.scrollstep
5753 else conf.scrollstep
5755 let incr = incr * 2 in
5756 let y = clamp incr in
5757 gotoy_and_clear_text y
5760 | n when (n = 6 || n = 7) && not down && canpan () ->
5761 state.x <- state.x + (if n = 7 then -2 else 2) * conf.hscrollstep;
5762 gotoy_and_clear_text state.y
5764 | 1 when Wsi.withctrl mask ->
5765 if down
5766 then (
5767 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5768 state.mstate <- Mpan (x, y)
5770 else
5771 state.mstate <- Mnone
5773 | 3 ->
5774 if down
5775 then (
5776 Wsi.setcursor Wsi.CURSOR_CYCLE;
5777 let p = (x, y) in
5778 state.mstate <- Mzoomrect (p, p)
5780 else (
5781 match state.mstate with
5782 | Mzoomrect ((x0, y0), _) ->
5783 if abs (x-x0) > 10 && abs (y - y0) > 10
5784 then zoomrect x0 y0 x y
5785 else (
5786 state.mstate <- Mnone;
5787 Wsi.setcursor Wsi.CURSOR_INHERIT;
5788 G.postRedisplay "kill accidental zoom rect";
5790 | _ ->
5791 Wsi.setcursor Wsi.CURSOR_INHERIT;
5792 state.mstate <- Mnone
5795 | 1 when x > conf.winw - state.scrollw ->
5796 if down
5797 then
5798 let _, position, sh = state.uioh#scrollph in
5799 if y > truncate position && y < truncate (position +. sh)
5800 then state.mstate <- Mscrolly
5801 else scrolly y
5802 else
5803 state.mstate <- Mnone
5805 | 1 when y > conf.winh - state.hscrollh ->
5806 if down
5807 then
5808 let _, position, sw = state.uioh#scrollpw in
5809 if x > truncate position && x < truncate (position +. sw)
5810 then state.mstate <- Mscrollx
5811 else scrollx x
5812 else
5813 state.mstate <- Mnone
5815 | 1 ->
5816 let dest = if down then getunder x y else Unone in
5817 begin match dest with
5818 | Ulinkgoto _
5819 | Ulinkuri _
5820 | Uremote _
5821 | Uunexpected _ | Ulaunch _ | Unamed _ ->
5822 gotounder dest
5824 | Unone when down ->
5825 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5826 state.mstate <- Mpan (x, y);
5828 | Unone | Utext _ ->
5829 if down
5830 then (
5831 if conf.angle mod 360 = 0
5832 then (
5833 state.mstate <- Msel ((x, y), (x, y));
5834 G.postRedisplay "mouse select";
5837 else (
5838 match state.mstate with
5839 | Mnone -> ()
5841 | Mzoom _ | Mscrollx | Mscrolly ->
5842 state.mstate <- Mnone
5844 | Mzoomrect ((x0, y0), _) ->
5845 zoomrect x0 y0 x y
5847 | Mpan _ ->
5848 Wsi.setcursor Wsi.CURSOR_INHERIT;
5849 state.mstate <- Mnone
5851 | Msel ((x0, y0), (x1, y1)) ->
5852 let rec loop = function
5853 | [] -> ()
5854 | l :: rest ->
5855 let inside =
5856 let a0 = l.pagedispy in
5857 let a1 = a0 + l.pagevh in
5858 let b0 = l.pagedispx in
5859 let b1 = b0 + l.pagevw in
5860 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
5861 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
5863 if inside
5864 then
5865 match getopaque l.pageno with
5866 | Some opaque ->
5867 begin
5868 match Ne.pipe () with
5869 | Ne.Exn exn ->
5870 showtext '!'
5871 (Printf.sprintf
5872 "can not create sel pipe: %s"
5873 (Printexc.to_string exn));
5874 | Ne.Res (r, w) ->
5875 let doclose what fd =
5876 Ne.clo fd (fun msg ->
5877 dolog "%s close failed: %s" what msg)
5880 popen conf.selcmd [r, 0; w, -1];
5881 copysel w opaque;
5882 doclose "pipe/r" r;
5883 G.postRedisplay "copysel";
5884 with exn ->
5885 dolog "can not execute %S: %s"
5886 conf.selcmd (Printexc.to_string exn);
5887 doclose "pipe/r" r;
5888 doclose "pipe/w" w;
5890 | None -> ()
5891 else loop rest
5893 loop state.layout;
5894 Wsi.setcursor Wsi.CURSOR_INHERIT;
5895 state.mstate <- Mnone;
5899 | _ -> ()
5902 let birdseyemouse button down x y mask
5903 (conf, leftx, _, hooverpageno, anchor) =
5904 match button with
5905 | 1 when down ->
5906 let rec loop = function
5907 | [] -> ()
5908 | l :: rest ->
5909 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5910 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5911 then (
5912 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5914 else loop rest
5916 loop state.layout
5917 | 3 -> ()
5918 | _ -> viewmouse button down x y mask
5921 let mouse button down x y mask =
5922 state.uioh <- state.uioh#button button down x y mask;
5925 let motion ~x ~y =
5926 state.uioh <- state.uioh#motion x y
5929 let pmotion ~x ~y =
5930 state.uioh <- state.uioh#pmotion x y;
5933 let uioh = object
5934 method display = ()
5936 method key key mask =
5937 begin match state.mode with
5938 | Textentry textentry -> textentrykeyboard key mask textentry
5939 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5940 | View -> viewkeyboard key mask
5941 | LinkNav linknav -> linknavkeyboard key mask linknav
5942 end;
5943 state.uioh
5945 method button button bstate x y mask =
5946 begin match state.mode with
5947 | LinkNav _
5948 | View -> viewmouse button bstate x y mask
5949 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5950 | Textentry _ -> ()
5951 end;
5952 state.uioh
5954 method motion x y =
5955 begin match state.mode with
5956 | Textentry _ -> ()
5957 | View | Birdseye _ | LinkNav _ ->
5958 match state.mstate with
5959 | Mzoom _ | Mnone -> ()
5961 | Mpan (x0, y0) ->
5962 let dx = x - x0
5963 and dy = y0 - y in
5964 state.mstate <- Mpan (x, y);
5965 if canpan ()
5966 then state.x <- state.x + dx;
5967 let y = clamp dy in
5968 gotoy_and_clear_text y
5970 | Msel (a, _) ->
5971 state.mstate <- Msel (a, (x, y));
5972 G.postRedisplay "motion select";
5974 | Mscrolly ->
5975 let y = min conf.winh (max 0 y) in
5976 scrolly y
5978 | Mscrollx ->
5979 let x = min conf.winw (max 0 x) in
5980 scrollx x
5982 | Mzoomrect (p0, _) ->
5983 state.mstate <- Mzoomrect (p0, (x, y));
5984 G.postRedisplay "motion zoomrect";
5985 end;
5986 state.uioh
5988 method pmotion x y =
5989 begin match state.mode with
5990 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5991 let rec loop = function
5992 | [] ->
5993 if hooverpageno != -1
5994 then (
5995 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5996 G.postRedisplay "pmotion birdseye no hoover";
5998 | l :: rest ->
5999 if y > l.pagedispy && y < l.pagedispy + l.pagevh
6000 && x > l.pagedispx && x < l.pagedispx + l.pagevw
6001 then (
6002 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
6003 G.postRedisplay "pmotion birdseye hoover";
6005 else loop rest
6007 loop state.layout
6009 | Textentry _ -> ()
6011 | LinkNav _
6012 | View ->
6013 match state.mstate with
6014 | Mnone -> updateunder x y
6015 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
6017 end;
6018 state.uioh
6020 method infochanged _ = ()
6022 method scrollph =
6023 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
6024 let p, h = scrollph state.y maxy in
6025 state.scrollw, p, h
6027 method scrollpw =
6028 let winw = conf.winw - state.scrollw - 1 in
6029 let fwinw = float winw in
6030 let sw =
6031 let sw = fwinw /. float state.w in
6032 let sw = fwinw *. sw in
6033 max sw (float conf.scrollh)
6035 let position, sw =
6036 let f = state.w+winw in
6037 let r = float (winw-state.x) /. float f in
6038 let p = fwinw *. r in
6039 p-.sw/.2., sw
6041 let sw =
6042 if position +. sw > fwinw
6043 then fwinw -. position
6044 else sw
6046 state.hscrollh, position, sw
6048 method modehash =
6049 let modename =
6050 match state.mode with
6051 | LinkNav _ -> "links"
6052 | Textentry _ -> "textentry"
6053 | Birdseye _ -> "birdseye"
6054 | View -> "view"
6056 findkeyhash conf modename
6057 end;;
6059 module Config =
6060 struct
6061 open Parser
6063 let fontpath = ref "";;
6065 module KeyMap =
6066 Map.Make (struct type t = (int * int) let compare = compare end);;
6068 let unent s =
6069 let l = String.length s in
6070 let b = Buffer.create l in
6071 unent b s 0 l;
6072 Buffer.contents b;
6075 let home =
6076 try Sys.getenv "HOME"
6077 with exn ->
6078 prerr_endline
6079 ("Can not determine home directory location: " ^
6080 Printexc.to_string exn);
6084 let modifier_of_string = function
6085 | "alt" -> Wsi.altmask
6086 | "shift" -> Wsi.shiftmask
6087 | "ctrl" | "control" -> Wsi.ctrlmask
6088 | "meta" -> Wsi.metamask
6089 | _ -> 0
6092 let key_of_string =
6093 let r = Str.regexp "-" in
6094 fun s ->
6095 let elems = Str.full_split r s in
6096 let f n k m =
6097 let g s =
6098 let m1 = modifier_of_string s in
6099 if m1 = 0
6100 then (Wsi.namekey s, m)
6101 else (k, m lor m1)
6102 in function
6103 | Str.Delim s when n land 1 = 0 -> g s
6104 | Str.Text s -> g s
6105 | Str.Delim _ -> (k, m)
6107 let rec loop n k m = function
6108 | [] -> (k, m)
6109 | x :: xs ->
6110 let k, m = f n k m x in
6111 loop (n+1) k m xs
6113 loop 0 0 0 elems
6116 let keys_of_string =
6117 let r = Str.regexp "[ \t]" in
6118 fun s ->
6119 let elems = Str.split r s in
6120 List.map key_of_string elems
6123 let copykeyhashes c =
6124 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
6127 let config_of c attrs =
6128 let apply c k v =
6130 match k with
6131 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
6132 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
6133 | "case-insensitive-search" -> { c with icase = bool_of_string v }
6134 | "preload" -> { c with preload = bool_of_string v }
6135 | "page-bias" -> { c with pagebias = int_of_string v }
6136 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
6137 | "horizontal-scroll-step" ->
6138 { c with hscrollstep = max (int_of_string v) 1 }
6139 | "auto-scroll-step" ->
6140 { c with autoscrollstep = max 0 (int_of_string v) }
6141 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
6142 | "crop-hack" -> { c with crophack = bool_of_string v }
6143 | "throttle" ->
6144 let mw =
6145 match String.lowercase v with
6146 | "true" -> Some infinity
6147 | "false" -> None
6148 | f -> Some (float_of_string f)
6150 { c with maxwait = mw}
6151 | "highlight-links" -> { c with hlinks = bool_of_string v }
6152 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
6153 | "vertical-margin" ->
6154 { c with interpagespace = max 0 (int_of_string v) }
6155 | "zoom" ->
6156 let zoom = float_of_string v /. 100. in
6157 let zoom = max zoom 0.0 in
6158 { c with zoom = zoom }
6159 | "presentation" -> { c with presentation = bool_of_string v }
6160 | "rotation-angle" -> { c with angle = int_of_string v }
6161 | "width" -> { c with winw = max 20 (int_of_string v) }
6162 | "height" -> { c with winh = max 20 (int_of_string v) }
6163 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
6164 | "proportional-display" -> { c with proportional = bool_of_string v }
6165 | "pixmap-cache-size" ->
6166 { c with memlimit = max 2 (int_of_string_with_suffix v) }
6167 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
6168 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
6169 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
6170 | "persistent-location" -> { c with jumpback = bool_of_string v }
6171 | "background-color" -> { c with bgcolor = color_of_string v }
6172 | "scrollbar-in-presentation" ->
6173 { c with scrollbarinpm = bool_of_string v }
6174 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
6175 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
6176 | "mupdf-store-size" ->
6177 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
6178 | "checkers" -> { c with checkers = bool_of_string v }
6179 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
6180 | "trim-margins" -> { c with trimmargins = bool_of_string v }
6181 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
6182 | "uri-launcher" -> { c with urilauncher = unent v }
6183 | "path-launcher" -> { c with pathlauncher = unent v }
6184 | "color-space" -> { c with colorspace = colorspace_of_string v }
6185 | "invert-colors" -> { c with invert = bool_of_string v }
6186 | "brightness" -> { c with colorscale = float_of_string v }
6187 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
6188 | "ghyllscroll" ->
6189 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
6190 | "columns" ->
6191 let (n, _, _) as nab = multicolumns_of_string v in
6192 if n < 0
6193 then { c with columns = Csplit (-n, [||]) }
6194 else { c with columns = Cmulti (nab, [||]) }
6195 | "birds-eye-columns" ->
6196 { c with beyecolumns = Some (max (int_of_string v) 2) }
6197 | "selection-command" -> { c with selcmd = unent v }
6198 | "update-cursor" -> { c with updatecurs = bool_of_string v }
6199 | "hint-font-size" -> { c with hfsize = bound (int_of_string v) 5 100 }
6200 | "page-scroll-scale" -> { c with pgscale = float_of_string v }
6201 | "use-pbo" -> { c with usepbo = bool_of_string v }
6202 | "wheel-scrolls-pages" -> { c with wheelbypage = bool_of_string v }
6203 | _ -> c
6204 with exn ->
6205 prerr_endline ("Error processing attribute (`" ^
6206 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
6209 let rec fold c = function
6210 | [] -> c
6211 | (k, v) :: rest ->
6212 let c = apply c k v in
6213 fold c rest
6215 fold { c with keyhashes = copykeyhashes c } attrs;
6218 let fromstring f pos n v d =
6219 try f v
6220 with exn ->
6221 dolog "Error processing attribute (%S=%S) at %d\n%s"
6222 n v pos (Printexc.to_string exn)
6227 let bookmark_of attrs =
6228 let rec fold title page rely visy = function
6229 | ("title", v) :: rest -> fold v page rely visy rest
6230 | ("page", v) :: rest -> fold title v rely visy rest
6231 | ("rely", v) :: rest -> fold title page v visy rest
6232 | ("visy", v) :: rest -> fold title page rely v rest
6233 | _ :: rest -> fold title page rely visy rest
6234 | [] -> title, page, rely, visy
6236 fold "invalid" "0" "0" "0" attrs
6239 let doc_of attrs =
6240 let rec fold path page rely pan visy = function
6241 | ("path", v) :: rest -> fold v page rely pan visy rest
6242 | ("page", v) :: rest -> fold path v rely pan visy rest
6243 | ("rely", v) :: rest -> fold path page v pan visy rest
6244 | ("pan", v) :: rest -> fold path page rely v visy rest
6245 | ("visy", v) :: rest -> fold path page rely pan v rest
6246 | _ :: rest -> fold path page rely pan visy rest
6247 | [] -> path, page, rely, pan, visy
6249 fold "" "0" "0" "0" "0" attrs
6252 let map_of attrs =
6253 let rec fold rs ls = function
6254 | ("out", v) :: rest -> fold v ls rest
6255 | ("in", v) :: rest -> fold rs v rest
6256 | _ :: rest -> fold ls rs rest
6257 | [] -> ls, rs
6259 fold "" "" attrs
6262 let setconf dst src =
6263 dst.scrollbw <- src.scrollbw;
6264 dst.scrollh <- src.scrollh;
6265 dst.icase <- src.icase;
6266 dst.preload <- src.preload;
6267 dst.pagebias <- src.pagebias;
6268 dst.verbose <- src.verbose;
6269 dst.scrollstep <- src.scrollstep;
6270 dst.maxhfit <- src.maxhfit;
6271 dst.crophack <- src.crophack;
6272 dst.autoscrollstep <- src.autoscrollstep;
6273 dst.maxwait <- src.maxwait;
6274 dst.hlinks <- src.hlinks;
6275 dst.underinfo <- src.underinfo;
6276 dst.interpagespace <- src.interpagespace;
6277 dst.zoom <- src.zoom;
6278 dst.presentation <- src.presentation;
6279 dst.angle <- src.angle;
6280 dst.winw <- src.winw;
6281 dst.winh <- src.winh;
6282 dst.savebmarks <- src.savebmarks;
6283 dst.memlimit <- src.memlimit;
6284 dst.proportional <- src.proportional;
6285 dst.texcount <- src.texcount;
6286 dst.sliceheight <- src.sliceheight;
6287 dst.thumbw <- src.thumbw;
6288 dst.jumpback <- src.jumpback;
6289 dst.bgcolor <- src.bgcolor;
6290 dst.scrollbarinpm <- src.scrollbarinpm;
6291 dst.tilew <- src.tilew;
6292 dst.tileh <- src.tileh;
6293 dst.mustoresize <- src.mustoresize;
6294 dst.checkers <- src.checkers;
6295 dst.aalevel <- src.aalevel;
6296 dst.trimmargins <- src.trimmargins;
6297 dst.trimfuzz <- src.trimfuzz;
6298 dst.urilauncher <- src.urilauncher;
6299 dst.colorspace <- src.colorspace;
6300 dst.invert <- src.invert;
6301 dst.colorscale <- src.colorscale;
6302 dst.redirectstderr <- src.redirectstderr;
6303 dst.ghyllscroll <- src.ghyllscroll;
6304 dst.columns <- src.columns;
6305 dst.beyecolumns <- src.beyecolumns;
6306 dst.selcmd <- src.selcmd;
6307 dst.updatecurs <- src.updatecurs;
6308 dst.pathlauncher <- src.pathlauncher;
6309 dst.keyhashes <- copykeyhashes src;
6310 dst.hfsize <- src.hfsize;
6311 dst.hscrollstep <- src.hscrollstep;
6312 dst.pgscale <- src.pgscale;
6313 dst.usepbo <- src.usepbo;
6314 dst.wheelbypage <- src.wheelbypage;
6317 let get s =
6318 let h = Hashtbl.create 10 in
6319 let dc = { defconf with angle = defconf.angle } in
6320 let rec toplevel v t spos _ =
6321 match t with
6322 | Vdata | Vcdata | Vend -> v
6323 | Vopen ("llppconfig", _, closed) ->
6324 if closed
6325 then v
6326 else { v with f = llppconfig }
6327 | Vopen _ ->
6328 error "unexpected subelement at top level" s spos
6329 | Vclose _ -> error "unexpected close at top level" s spos
6331 and llppconfig v t spos _ =
6332 match t with
6333 | Vdata | Vcdata -> v
6334 | Vend -> error "unexpected end of input in llppconfig" s spos
6335 | Vopen ("defaults", attrs, closed) ->
6336 let c = config_of dc attrs in
6337 setconf dc c;
6338 if closed
6339 then v
6340 else { v with f = defaults }
6342 | Vopen ("ui-font", attrs, closed) ->
6343 let rec getsize size = function
6344 | [] -> size
6345 | ("size", v) :: rest ->
6346 let size =
6347 fromstring int_of_string spos "size" v fstate.fontsize in
6348 getsize size rest
6349 | l -> getsize size l
6351 fstate.fontsize <- getsize fstate.fontsize attrs;
6352 if closed
6353 then v
6354 else { v with f = uifont (Buffer.create 10) }
6356 | Vopen ("doc", attrs, closed) ->
6357 let pathent, spage, srely, span, svisy = doc_of attrs in
6358 let path = unent pathent
6359 and pageno = fromstring int_of_string spos "page" spage 0
6360 and rely = fromstring float_of_string spos "rely" srely 0.0
6361 and pan = fromstring int_of_string spos "pan" span 0
6362 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6363 let c = config_of dc attrs in
6364 let anchor = (pageno, rely, visy) in
6365 if closed
6366 then (Hashtbl.add h path (c, [], pan, anchor); v)
6367 else { v with f = doc path pan anchor c [] }
6369 | Vopen _ ->
6370 error "unexpected subelement in llppconfig" s spos
6372 | Vclose "llppconfig" -> { v with f = toplevel }
6373 | Vclose _ -> error "unexpected close in llppconfig" s spos
6375 and defaults v t spos _ =
6376 match t with
6377 | Vdata | Vcdata -> v
6378 | Vend -> error "unexpected end of input in defaults" s spos
6379 | Vopen ("keymap", attrs, closed) ->
6380 let modename =
6381 try List.assoc "mode" attrs
6382 with Not_found -> "global" in
6383 if closed
6384 then v
6385 else
6386 let ret keymap =
6387 let h = findkeyhash dc modename in
6388 KeyMap.iter (Hashtbl.replace h) keymap;
6389 defaults
6391 { v with f = pkeymap ret KeyMap.empty }
6393 | Vopen (_, _, _) ->
6394 error "unexpected subelement in defaults" s spos
6396 | Vclose "defaults" ->
6397 { v with f = llppconfig }
6399 | Vclose _ -> error "unexpected close in defaults" s spos
6401 and uifont b v t spos epos =
6402 match t with
6403 | Vdata | Vcdata ->
6404 Buffer.add_substring b s spos (epos - spos);
6406 | Vopen (_, _, _) ->
6407 error "unexpected subelement in ui-font" s spos
6408 | Vclose "ui-font" ->
6409 if String.length !fontpath = 0
6410 then fontpath := Buffer.contents b;
6411 { v with f = llppconfig }
6412 | Vclose _ -> error "unexpected close in ui-font" s spos
6413 | Vend -> error "unexpected end of input in ui-font" s spos
6415 and doc path pan anchor c bookmarks v t spos _ =
6416 match t with
6417 | Vdata | Vcdata -> v
6418 | Vend -> error "unexpected end of input in doc" s spos
6419 | Vopen ("bookmarks", _, closed) ->
6420 if closed
6421 then v
6422 else { v with f = pbookmarks path pan anchor c bookmarks }
6424 | Vopen ("keymap", attrs, closed) ->
6425 let modename =
6426 try List.assoc "mode" attrs
6427 with Not_found -> "global"
6429 if closed
6430 then v
6431 else
6432 let ret keymap =
6433 let h = findkeyhash c modename in
6434 KeyMap.iter (Hashtbl.replace h) keymap;
6435 doc path pan anchor c bookmarks
6437 { v with f = pkeymap ret KeyMap.empty }
6439 | Vopen (_, _, _) ->
6440 error "unexpected subelement in doc" s spos
6442 | Vclose "doc" ->
6443 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
6444 { v with f = llppconfig }
6446 | Vclose _ -> error "unexpected close in doc" s spos
6448 and pkeymap ret keymap v t spos _ =
6449 match t with
6450 | Vdata | Vcdata -> v
6451 | Vend -> error "unexpected end of input in keymap" s spos
6452 | Vopen ("map", attrs, closed) ->
6453 let r, l = map_of attrs in
6454 let kss = fromstring keys_of_string spos "in" r [] in
6455 let lss = fromstring keys_of_string spos "out" l [] in
6456 let keymap =
6457 match kss with
6458 | [] -> keymap
6459 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
6460 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
6462 if closed
6463 then { v with f = pkeymap ret keymap }
6464 else
6465 let f () = v in
6466 { v with f = skip "map" f }
6468 | Vopen _ ->
6469 error "unexpected subelement in keymap" s spos
6471 | Vclose "keymap" ->
6472 { v with f = ret keymap }
6474 | Vclose _ -> error "unexpected close in keymap" s spos
6476 and pbookmarks path pan anchor c bookmarks v t spos _ =
6477 match t with
6478 | Vdata | Vcdata -> v
6479 | Vend -> error "unexpected end of input in bookmarks" s spos
6480 | Vopen ("item", attrs, closed) ->
6481 let titleent, spage, srely, svisy = bookmark_of attrs in
6482 let page = fromstring int_of_string spos "page" spage 0
6483 and rely = fromstring float_of_string spos "rely" srely 0.0
6484 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6485 let bookmarks =
6486 (unent titleent, 0, (page, rely, visy)) :: bookmarks
6488 if closed
6489 then { v with f = pbookmarks path pan anchor c bookmarks }
6490 else
6491 let f () = v in
6492 { v with f = skip "item" f }
6494 | Vopen _ ->
6495 error "unexpected subelement in bookmarks" s spos
6497 | Vclose "bookmarks" ->
6498 { v with f = doc path pan anchor c bookmarks }
6500 | Vclose _ -> error "unexpected close in bookmarks" s spos
6502 and skip tag f v t spos _ =
6503 match t with
6504 | Vdata | Vcdata -> v
6505 | Vend ->
6506 error ("unexpected end of input in skipped " ^ tag) s spos
6507 | Vopen (tag', _, closed) ->
6508 if closed
6509 then v
6510 else
6511 let f' () = { v with f = skip tag f } in
6512 { v with f = skip tag' f' }
6513 | Vclose ctag ->
6514 if tag = ctag
6515 then f ()
6516 else error ("unexpected close in skipped " ^ tag) s spos
6519 parse { f = toplevel; accu = () } s;
6520 h, dc;
6523 let do_load f ic =
6525 let len = in_channel_length ic in
6526 let s = String.create len in
6527 really_input ic s 0 len;
6528 f s;
6529 with
6530 | Parse_error (msg, s, pos) ->
6531 let subs = subs s pos in
6532 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6533 failwith ("parse error: " ^ s)
6535 | exn ->
6536 failwith ("config load error: " ^ Printexc.to_string exn)
6539 let defconfpath =
6540 let dir =
6542 let dir = Filename.concat home ".config" in
6543 if Sys.is_directory dir then dir else home
6544 with _ -> home
6546 Filename.concat dir "llpp.conf"
6549 let confpath = ref defconfpath;;
6551 let load1 f =
6552 if Sys.file_exists !confpath
6553 then
6554 match
6555 (try Some (open_in_bin !confpath)
6556 with exn ->
6557 prerr_endline
6558 ("Error opening configuation file `" ^ !confpath ^ "': " ^
6559 Printexc.to_string exn);
6560 None
6562 with
6563 | Some ic ->
6564 let success =
6566 f (do_load get ic)
6567 with exn ->
6568 prerr_endline
6569 ("Error loading configuation from `" ^ !confpath ^ "': " ^
6570 Printexc.to_string exn);
6571 false
6573 close_in ic;
6574 success
6576 | None -> false
6577 else
6578 f (Hashtbl.create 0, defconf)
6581 let load () =
6582 let f (h, dc) =
6583 let pc, pb, px, pa =
6585 Hashtbl.find h (Filename.basename state.path)
6586 with Not_found -> dc, [], 0, emptyanchor
6588 setconf defconf dc;
6589 setconf conf pc;
6590 state.bookmarks <- pb;
6591 state.x <- px;
6592 state.scrollw <- conf.scrollbw;
6593 if conf.jumpback
6594 then state.anchor <- pa;
6595 cbput state.hists.nav pa;
6596 true
6598 load1 f
6601 let add_attrs bb always dc c =
6602 let ob s a b =
6603 if always || a != b
6604 then Printf.bprintf bb "\n %s='%b'" s a
6605 and oi s a b =
6606 if always || a != b
6607 then Printf.bprintf bb "\n %s='%d'" s a
6608 and oI s a b =
6609 if always || a != b
6610 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6611 and oz s a b =
6612 if always || a <> b
6613 then Printf.bprintf bb "\n %s='%g'" s (a*.100.)
6614 and oF s a b =
6615 if always || a <> b
6616 then Printf.bprintf bb "\n %s='%f'" s a
6617 and oc s a b =
6618 if always || a <> b
6619 then
6620 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6621 and oC s a b =
6622 if always || a <> b
6623 then
6624 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6625 and oR s a b =
6626 if always || a <> b
6627 then
6628 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6629 and os s a b =
6630 if always || a <> b
6631 then
6632 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6633 and og s a b =
6634 if always || a <> b
6635 then
6636 match a with
6637 | None -> ()
6638 | Some (_N, _A, _B) ->
6639 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6640 and oW s a b =
6641 if always || a <> b
6642 then
6643 let v =
6644 match a with
6645 | None -> "false"
6646 | Some f ->
6647 if f = infinity
6648 then "true"
6649 else string_of_float f
6651 Printf.bprintf bb "\n %s='%s'" s v
6652 and oco s a b =
6653 if always || a <> b
6654 then
6655 match a with
6656 | Cmulti ((n, a, b), _) when n > 1 ->
6657 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6658 | Csplit (n, _) when n > 1 ->
6659 Printf.bprintf bb "\n %s='%d'" s ~-n
6660 | _ -> ()
6661 and obeco s a b =
6662 if always || a <> b
6663 then
6664 match a with
6665 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6666 | _ -> ()
6668 let w, h =
6669 if always
6670 then dc.winw, dc.winh
6671 else
6672 match state.fullscreen with
6673 | Some wh -> wh
6674 | None -> c.winw, c.winh
6676 oi "width" w dc.winw;
6677 oi "height" h dc.winh;
6678 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6679 oi "scroll-handle-height" c.scrollh dc.scrollh;
6680 ob "case-insensitive-search" c.icase dc.icase;
6681 ob "preload" c.preload dc.preload;
6682 oi "page-bias" c.pagebias dc.pagebias;
6683 oi "scroll-step" c.scrollstep dc.scrollstep;
6684 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6685 ob "max-height-fit" c.maxhfit dc.maxhfit;
6686 ob "crop-hack" c.crophack dc.crophack;
6687 oW "throttle" c.maxwait dc.maxwait;
6688 ob "highlight-links" c.hlinks dc.hlinks;
6689 ob "under-cursor-info" c.underinfo dc.underinfo;
6690 oi "vertical-margin" c.interpagespace dc.interpagespace;
6691 oz "zoom" c.zoom dc.zoom;
6692 ob "presentation" c.presentation dc.presentation;
6693 oi "rotation-angle" c.angle dc.angle;
6694 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6695 ob "proportional-display" c.proportional dc.proportional;
6696 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6697 oi "tex-count" c.texcount dc.texcount;
6698 oi "slice-height" c.sliceheight dc.sliceheight;
6699 oi "thumbnail-width" c.thumbw dc.thumbw;
6700 ob "persistent-location" c.jumpback dc.jumpback;
6701 oc "background-color" c.bgcolor dc.bgcolor;
6702 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6703 oi "tile-width" c.tilew dc.tilew;
6704 oi "tile-height" c.tileh dc.tileh;
6705 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6706 ob "checkers" c.checkers dc.checkers;
6707 oi "aalevel" c.aalevel dc.aalevel;
6708 ob "trim-margins" c.trimmargins dc.trimmargins;
6709 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6710 os "uri-launcher" c.urilauncher dc.urilauncher;
6711 os "path-launcher" c.pathlauncher dc.pathlauncher;
6712 oC "color-space" c.colorspace dc.colorspace;
6713 ob "invert-colors" c.invert dc.invert;
6714 oF "brightness" c.colorscale dc.colorscale;
6715 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6716 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6717 oco "columns" c.columns dc.columns;
6718 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6719 os "selection-command" c.selcmd dc.selcmd;
6720 ob "update-cursor" c.updatecurs dc.updatecurs;
6721 oi "hint-font-size" c.hfsize dc.hfsize;
6722 oi "horizontal-scroll-step" c.hscrollstep dc.hscrollstep;
6723 oF "page-scroll-scale" c.pgscale dc.pgscale;
6724 ob "use-pbo" c.usepbo dc.usepbo;
6725 ob "wheel-scrolls-pages" c.wheelbypage dc.wheelbypage;
6728 let keymapsbuf always dc c =
6729 let bb = Buffer.create 16 in
6730 let rec loop = function
6731 | [] -> ()
6732 | (modename, h) :: rest ->
6733 let dh = findkeyhash dc modename in
6734 if always || h <> dh
6735 then (
6736 if Hashtbl.length h > 0
6737 then (
6738 if Buffer.length bb > 0
6739 then Buffer.add_char bb '\n';
6740 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6741 Hashtbl.iter (fun i o ->
6742 let isdifferent = always ||
6744 let dO = Hashtbl.find dh i in
6745 dO <> o
6746 with Not_found -> true
6748 if isdifferent
6749 then
6750 let addkm (k, m) =
6751 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6752 if Wsi.withalt m then Buffer.add_string bb "alt-";
6753 if Wsi.withshift m then Buffer.add_string bb "shift-";
6754 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6755 Buffer.add_string bb (Wsi.keyname k);
6757 let addkms l =
6758 let rec loop = function
6759 | [] -> ()
6760 | km :: [] -> addkm km
6761 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6763 loop l
6765 Buffer.add_string bb "<map in='";
6766 addkm i;
6767 match o with
6768 | KMinsrt km ->
6769 Buffer.add_string bb "' out='";
6770 addkm km;
6771 Buffer.add_string bb "'/>\n"
6773 | KMinsrl kms ->
6774 Buffer.add_string bb "' out='";
6775 addkms kms;
6776 Buffer.add_string bb "'/>\n"
6778 | KMmulti (ins, kms) ->
6779 Buffer.add_char bb ' ';
6780 addkms ins;
6781 Buffer.add_string bb "' out='";
6782 addkms kms;
6783 Buffer.add_string bb "'/>\n"
6784 ) h;
6785 Buffer.add_string bb "</keymap>";
6788 loop rest
6790 loop c.keyhashes;
6794 let save () =
6795 let uifontsize = fstate.fontsize in
6796 let bb = Buffer.create 32768 in
6797 let f (h, dc) =
6798 let dc = if conf.bedefault then conf else dc in
6799 Buffer.add_string bb "<llppconfig>\n";
6801 if String.length !fontpath > 0
6802 then
6803 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6804 uifontsize
6805 !fontpath
6806 else (
6807 if uifontsize <> 14
6808 then
6809 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6812 Buffer.add_string bb "<defaults ";
6813 add_attrs bb true dc dc;
6814 let kb = keymapsbuf true dc dc in
6815 if Buffer.length kb > 0
6816 then (
6817 Buffer.add_string bb ">\n";
6818 Buffer.add_buffer bb kb;
6819 Buffer.add_string bb "\n</defaults>\n";
6821 else Buffer.add_string bb "/>\n";
6823 let adddoc path pan anchor c bookmarks =
6824 if bookmarks == [] && c = dc && anchor = emptyanchor
6825 then ()
6826 else (
6827 Printf.bprintf bb "<doc path='%s'"
6828 (enent path 0 (String.length path));
6830 if anchor <> emptyanchor
6831 then (
6832 let n, rely, visy = anchor in
6833 Printf.bprintf bb " page='%d'" n;
6834 if rely > 1e-6
6835 then
6836 Printf.bprintf bb " rely='%f'" rely
6838 if abs_float visy > 1e-6
6839 then
6840 Printf.bprintf bb " visy='%f'" visy
6844 if pan != 0
6845 then Printf.bprintf bb " pan='%d'" pan;
6847 add_attrs bb false dc c;
6848 let kb = keymapsbuf false dc c in
6850 begin match bookmarks with
6851 | [] ->
6852 if Buffer.length kb > 0
6853 then (
6854 Buffer.add_string bb ">\n";
6855 Buffer.add_buffer bb kb;
6856 Buffer.add_string bb "\n</doc>\n";
6858 else Buffer.add_string bb "/>\n"
6859 | _ ->
6860 Buffer.add_string bb ">\n<bookmarks>\n";
6861 List.iter (fun (title, _level, (page, rely, visy)) ->
6862 Printf.bprintf bb
6863 "<item title='%s' page='%d'"
6864 (enent title 0 (String.length title))
6865 page
6867 if rely > 1e-6
6868 then
6869 Printf.bprintf bb " rely='%f'" rely
6871 if abs_float visy > 1e-6
6872 then
6873 Printf.bprintf bb " visy='%f'" visy
6875 Buffer.add_string bb "/>\n";
6876 ) bookmarks;
6877 Buffer.add_string bb "</bookmarks>";
6878 if Buffer.length kb > 0
6879 then (
6880 Buffer.add_string bb "\n";
6881 Buffer.add_buffer bb kb;
6883 Buffer.add_string bb "\n</doc>\n";
6884 end;
6888 let pan, conf =
6889 match state.mode with
6890 | Birdseye (c, pan, _, _, _) ->
6891 let beyecolumns =
6892 match conf.columns with
6893 | Cmulti ((c, _, _), _) -> Some c
6894 | Csingle _ -> None
6895 | Csplit _ -> None
6896 and columns =
6897 match c.columns with
6898 | Cmulti (c, _) -> Cmulti (c, [||])
6899 | Csingle _ -> Csingle [||]
6900 | Csplit _ -> failwith "quit from bird's eye while split"
6902 pan, { c with beyecolumns = beyecolumns; columns = columns }
6903 | _ -> state.x, conf
6905 let basename = Filename.basename state.path in
6906 adddoc basename pan (getanchor ())
6907 (let conf =
6908 let autoscrollstep =
6909 match state.autoscroll with
6910 | Some step -> step
6911 | None -> conf.autoscrollstep
6913 match state.mode with
6914 | Birdseye (bc, _, _, _, _) ->
6915 { conf with
6916 zoom = bc.zoom;
6917 presentation = bc.presentation;
6918 interpagespace = bc.interpagespace;
6919 maxwait = bc.maxwait;
6920 autoscrollstep = autoscrollstep }
6921 | _ -> { conf with autoscrollstep = autoscrollstep }
6922 in conf)
6923 (if conf.savebmarks then state.bookmarks else []);
6925 Hashtbl.iter (fun path (c, bookmarks, x, anchor) ->
6926 if basename <> path
6927 then adddoc path x anchor c bookmarks
6928 ) h;
6929 Buffer.add_string bb "</llppconfig>\n";
6930 true;
6932 if load1 f && Buffer.length bb > 0
6933 then
6935 let tmp = !confpath ^ ".tmp" in
6936 let oc = open_out_bin tmp in
6937 Buffer.output_buffer oc bb;
6938 close_out oc;
6939 Unix.rename tmp !confpath;
6940 with exn ->
6941 prerr_endline
6942 ("error while saving configuration: " ^ Printexc.to_string exn)
6944 end;;
6946 let () =
6947 let trimcachepath = ref "" in
6948 Arg.parse
6949 (Arg.align
6950 [("-p", Arg.String (fun s -> state.password <- s) ,
6951 "<password> Set password");
6953 ("-f", Arg.String (fun s -> Config.fontpath := s),
6954 "<path> Set path to the user interface font");
6956 ("-c", Arg.String (fun s -> Config.confpath := s),
6957 "<path> Set path to the configuration file");
6959 ("-tcf", Arg.String (fun s -> trimcachepath := s),
6960 "<path> Set path to the trim cache file");
6962 ("-dest", Arg.String (fun s -> state.nameddest <- s),
6963 "<named destination> Set named destination");
6965 ("-v", Arg.Unit (fun () ->
6966 Printf.printf
6967 "%s\nconfiguration path: %s\n"
6968 (version ())
6969 Config.defconfpath
6971 exit 0), " Print version and exit");
6974 (fun s -> state.path <- s)
6975 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6977 if String.length state.path = 0
6978 then (prerr_endline "file name missing"; exit 1);
6980 if not (Config.load ())
6981 then prerr_endline "failed to load configuration";
6983 let globalkeyhash = findkeyhash conf "global" in
6984 let wsfd, winw, winh = Wsi.init (object
6985 method expose =
6986 state.wthack <- false;
6987 if nogeomcmds state.geomcmds || platform == Posx
6988 then display ()
6989 else (
6990 GlClear.color (scalecolor2 conf.bgcolor);
6991 GlClear.clear [`color];
6993 method display = display ()
6994 method reshape w h = reshape w h
6995 method mouse b d x y m = mouse b d x y m
6996 method motion x y = state.mpos <- (x, y); motion x y
6997 method pmotion x y = state.mpos <- (x, y); pmotion x y
6998 method key k m =
6999 let mascm = m land (
7000 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
7001 ) in
7002 match state.keystate with
7003 | KSnone ->
7004 let km = k, mascm in
7005 begin
7006 match
7007 let modehash = state.uioh#modehash in
7008 try Hashtbl.find modehash km
7009 with Not_found ->
7010 try Hashtbl.find globalkeyhash km
7011 with Not_found -> KMinsrt (k, m)
7012 with
7013 | KMinsrt (k, m) -> keyboard k m
7014 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
7015 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
7017 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
7018 List.iter (fun (k, m) -> keyboard k m) insrt;
7019 state.keystate <- KSnone
7020 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
7021 state.keystate <- KSinto (keys, insrt)
7022 | _ ->
7023 state.keystate <- KSnone
7025 method enter x y = state.mpos <- (x, y); pmotion x y
7026 method leave = state.mpos <- (-1, -1)
7027 method quit = raise Quit
7028 end) conf.winw conf.winh (platform = Posx) in
7030 state.wsfd <- wsfd;
7032 if not (
7033 List.exists GlMisc.check_extension
7034 [ "GL_ARB_texture_rectangle"
7035 ; "GL_EXT_texture_recangle"
7036 ; "GL_NV_texture_rectangle" ]
7038 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
7040 let cr, sw =
7041 match Ne.pipe () with
7042 | Ne.Exn exn ->
7043 Printf.eprintf "pipe/crsw failed: %s" (Printexc.to_string exn);
7044 exit 1
7045 | Ne.Res rw -> rw
7046 and sr, cw =
7047 match Ne.pipe () with
7048 | Ne.Exn exn ->
7049 Printf.eprintf "pipe/srcw failed: %s" (Printexc.to_string exn);
7050 exit 1
7051 | Ne.Res rw -> rw
7054 cloexec cr;
7055 cloexec sw;
7056 cloexec sr;
7057 cloexec cw;
7059 setcheckers conf.checkers;
7060 redirectstderr ();
7062 init (cr, cw) (
7063 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
7064 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
7065 !Config.fontpath, !trimcachepath,
7066 GlMisc.check_extension "GL_ARB_pixel_buffer_object"
7068 state.sr <- sr;
7069 state.sw <- sw;
7070 state.text <- "Opening " ^ (mbtoutf8 state.path);
7071 reshape winw winh;
7072 opendoc state.path state.password state.nameddest;
7073 state.uioh <- uioh;
7075 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
7077 let rec loop deadline =
7078 let r =
7079 match state.errfd with
7080 | None -> [state.sr; state.wsfd]
7081 | Some fd -> [state.sr; state.wsfd; fd]
7083 if state.redisplay
7084 then (
7085 state.redisplay <- false;
7086 display ();
7088 let timeout =
7089 let now = now () in
7090 if deadline > now
7091 then (
7092 if deadline = infinity
7093 then ~-.1.0
7094 else max 0.0 (deadline -. now)
7096 else 0.0
7098 let r, _, _ =
7099 try tempfailureretry (Unix.select r [] []) timeout
7100 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
7102 begin match r with
7103 | [] ->
7104 state.ghyll None;
7105 let newdeadline =
7106 if state.ghyll == noghyll
7107 then
7108 match state.autoscroll with
7109 | Some step when step != 0 ->
7110 let y = state.y + step in
7111 let y =
7112 if y < 0
7113 then state.maxy
7114 else if y >= state.maxy then 0 else y
7116 gotoy y;
7117 if state.mode = View
7118 then state.text <- "";
7119 deadline +. 0.01
7120 | _ -> infinity
7121 else deadline +. 0.01
7123 loop newdeadline
7125 | l ->
7126 let rec checkfds = function
7127 | [] -> ()
7128 | fd :: rest when fd = state.sr ->
7129 let cmd = readcmd state.sr in
7130 act cmd;
7131 checkfds rest
7133 | fd :: rest when fd = state.wsfd ->
7134 Wsi.readresp fd;
7135 checkfds rest
7137 | fd :: rest ->
7138 let s = String.create 80 in
7139 let n = tempfailureretry (Unix.read fd s 0) 80 in
7140 if conf.redirectstderr
7141 then (
7142 Buffer.add_substring state.errmsgs s 0 n;
7143 state.newerrmsgs <- true;
7144 state.redisplay <- true;
7146 else (
7147 prerr_string (String.sub s 0 n);
7148 flush stderr;
7150 checkfds rest
7152 checkfds l;
7153 let newdeadline =
7154 let deadline1 =
7155 if deadline = infinity
7156 then now () +. 0.01
7157 else deadline
7159 match state.autoscroll with
7160 | Some step when step != 0 -> deadline1
7161 | _ -> if state.ghyll == noghyll then infinity else deadline1
7163 loop newdeadline
7164 end;
7167 loop infinity;
7168 with Quit ->
7169 Config.save ();