Cheap and dirty way to avoid text stomping over scroll area
[llpp.git] / main.ml
blob4b5cd9a81affbdb1ac7fc611047017b01ea1c101
1 exception Quit;;
3 type under =
4 | Unone
5 | Ulinkuri of string
6 | Ulinkgoto of (int * int)
7 | Utext of facename
8 | Uunexpected of string
9 | Ulaunch of string
10 | Unamed of string
11 | Uremote of (string * int)
12 and facename = string;;
14 let dolog fmt = Printf.kprintf prerr_endline fmt;;
15 let now = Unix.gettimeofday;;
17 type params = (angle * proportional * trimparams
18 * texcount * sliceheight * memsize
19 * colorspace * fontpath)
20 and pageno = int
21 and width = int
22 and height = int
23 and leftx = int
24 and opaque = string
25 and recttype = int
26 and pixmapsize = int
27 and angle = int
28 and proportional = bool
29 and trimmargins = bool
30 and interpagespace = int
31 and texcount = int
32 and sliceheight = int
33 and gen = int
34 and top = float
35 and fontpath = string
36 and memsize = int
37 and aalevel = int
38 and irect = (int * int * int * int)
39 and trimparams = (trimmargins * irect)
40 and colorspace = | Rgb | Bgr | Gray
43 type link =
44 | Lnotfound
45 | Lfound of (int * int * int * int * int)
46 and linkdir =
47 | LDfirst
48 | LDlast
49 | LDfirstvisible of (int * int * int)
50 | LDleft of int
51 | LDright of int
52 | LDdown of int
53 | LDup of int
56 type pagewithlinks =
57 | Pwlnotfound
58 | Pwl of int
61 type keymap =
62 | KMinsrt of key
63 | KMinsrl of key list
64 | KMmulti of key list * key list
65 and key = int * int
66 and keyhash = (key, keymap) Hashtbl.t
67 and keystate =
68 | KSnone
69 | KSinto of (key list * key list)
72 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
73 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
75 type pipe = (Unix.file_descr * Unix.file_descr);;
77 external init : pipe -> params -> unit = "ml_init";;
78 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
79 external copysel : string -> opaque -> unit = "ml_copysel";;
80 external getpdimrect : int -> float array = "ml_getpdimrect";;
81 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
82 external zoomforh : int -> int -> int -> float = "ml_zoom_for_height";;
83 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
84 external measurestr : int -> string -> float = "ml_measure_string";;
85 external getmaxw : unit -> float = "ml_getmaxw";;
86 external postprocess : opaque -> bool -> int -> int -> unit = "ml_postprocess";;
87 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
88 external platform : unit -> platform = "ml_platform";;
89 external setaalevel : int -> unit = "ml_setaalevel";;
90 external realloctexts : int -> bool = "ml_realloctexts";;
91 external cloexec : Unix.file_descr -> unit = "ml_cloexec";;
92 external findlink : opaque -> linkdir -> link = "ml_findlink";;
93 external getlink : opaque -> int -> under = "ml_getlink";;
94 external findpwl: int -> int -> pagewithlinks = "ml_find_page_with_links"
96 let platform_to_string = function
97 | Punknown -> "unknown"
98 | Plinux -> "Linux"
99 | Posx -> "OSX"
100 | Psun -> "Sun"
101 | Pfreebsd -> "FreeBSD"
102 | Pdragonflybsd -> "DragonflyBSD"
103 | Popenbsd -> "OpenBSD"
104 | Pnetbsd -> "NetBSD"
105 | Pcygwin -> "Cygwin"
108 let platform = platform ();;
110 type x = int
111 and y = int
112 and tilex = int
113 and tiley = int
114 and tileparams = (x * y * width * height * tilex * tiley)
117 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
119 type mpos = int * int
120 and mstate =
121 | Msel of (mpos * mpos)
122 | Mpan of mpos
123 | Mscrolly | Mscrollx
124 | Mzoom of (int * int)
125 | Mzoomrect of (mpos * mpos)
126 | Mnone
129 type textentry = string * string * onhist option * onkey * ondone
130 and onkey = string -> int -> te
131 and ondone = string -> unit
132 and histcancel = unit -> unit
133 and onhist = ((histcmd -> string) * histcancel)
134 and histcmd = HCnext | HCprev | HCfirst | HClast
135 and te =
136 | TEstop
137 | TEdone of string
138 | TEcont of string
139 | TEswitch of textentry
142 type 'a circbuf =
143 { store : 'a array
144 ; mutable rc : int
145 ; mutable wc : int
146 ; mutable len : int
150 let bound v minv maxv =
151 max minv (min maxv v);
154 let cbnew n v =
155 { store = Array.create n v
156 ; rc = 0
157 ; wc = 0
158 ; len = 0
162 let drawstring size x y s =
163 Gl.enable `blend;
164 Gl.enable `texture_2d;
165 ignore (drawstr size x y s);
166 Gl.disable `blend;
167 Gl.disable `texture_2d;
170 let drawstring1 size x y s =
171 drawstr size x y s;
174 let drawstring2 size x y fmt =
175 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
178 let cbcap b = Array.length b.store;;
180 let cbput b v =
181 let cap = cbcap b in
182 b.store.(b.wc) <- v;
183 b.wc <- (b.wc + 1) mod cap;
184 b.rc <- b.wc;
185 b.len <- min (b.len + 1) cap;
188 let cbempty b = b.len = 0;;
190 let cbgetg b circular dir =
191 if cbempty b
192 then b.store.(0)
193 else
194 let rc = b.rc + dir in
195 let rc =
196 if circular
197 then (
198 if rc = -1
199 then b.len-1
200 else (
201 if rc = b.len
202 then 0
203 else rc
206 else max 0 (min rc (b.len-1))
208 b.rc <- rc;
209 b.store.(rc);
212 let cbget b = cbgetg b false;;
213 let cbgetc b = cbgetg b true;;
215 type page =
216 { pageno : int
217 ; pagedimno : int
218 ; pagew : int
219 ; pageh : int
220 ; pagex : int
221 ; pagey : int
222 ; pagevw : int
223 ; pagevh : int
224 ; pagedispx : int
225 ; pagedispy : int
229 let debugl l =
230 dolog "l %d dim=%d {" l.pageno l.pagedimno;
231 dolog " WxH %dx%d" l.pagew l.pageh;
232 dolog " vWxH %dx%d" l.pagevw l.pagevh;
233 dolog " pagex,y %d,%d" l.pagex l.pagey;
234 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
235 dolog "}";
238 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
239 dolog "rect {";
240 dolog " x0,y0=(% f, % f)" x0 y0;
241 dolog " x1,y1=(% f, % f)" x1 y1;
242 dolog " x2,y2=(% f, % f)" x2 y2;
243 dolog " x3,y3=(% f, % f)" x3 y3;
244 dolog "}";
247 type columns =
248 multicol * ((pdimno * x * y * (pageno * width * height * leftx)) array)
249 and multicol = columncount * covercount * covercount
250 and pdimno = int
251 and columncount = int
252 and covercount = int;;
254 type conf =
255 { mutable scrollbw : int
256 ; mutable scrollh : int
257 ; mutable icase : bool
258 ; mutable preload : bool
259 ; mutable pagebias : int
260 ; mutable verbose : bool
261 ; mutable debug : bool
262 ; mutable scrollstep : int
263 ; mutable maxhfit : bool
264 ; mutable crophack : bool
265 ; mutable autoscrollstep : int
266 ; mutable maxwait : float option
267 ; mutable hlinks : bool
268 ; mutable underinfo : bool
269 ; mutable interpagespace : interpagespace
270 ; mutable zoom : float
271 ; mutable presentation : bool
272 ; mutable angle : angle
273 ; mutable winw : int
274 ; mutable winh : int
275 ; mutable savebmarks : bool
276 ; mutable proportional : proportional
277 ; mutable trimmargins : trimmargins
278 ; mutable trimfuzz : irect
279 ; mutable memlimit : memsize
280 ; mutable texcount : texcount
281 ; mutable sliceheight : sliceheight
282 ; mutable thumbw : width
283 ; mutable jumpback : bool
284 ; mutable bgcolor : float * float * float
285 ; mutable bedefault : bool
286 ; mutable scrollbarinpm : bool
287 ; mutable tilew : int
288 ; mutable tileh : int
289 ; mutable mustoresize : memsize
290 ; mutable checkers : bool
291 ; mutable aalevel : int
292 ; mutable urilauncher : string
293 ; mutable pathlauncher : string
294 ; mutable colorspace : colorspace
295 ; mutable invert : bool
296 ; mutable colorscale : float
297 ; mutable redirectstderr : bool
298 ; mutable ghyllscroll : (int * int * int) option
299 ; mutable columns : columns option
300 ; mutable beyecolumns : columncount option
301 ; mutable selcmd : string
302 ; mutable updatecurs : bool
303 ; mutable keyhashes : (string * keyhash) list
307 type anchor = pageno * top;;
309 type outline = string * int * anchor;;
311 type rect = float * float * float * float * float * float * float * float;;
313 type tile = opaque * pixmapsize * elapsed
314 and elapsed = float;;
315 type pagemapkey = pageno * gen;;
316 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
317 and row = int
318 and col = int;;
320 let emptyanchor = (0, 0.0);;
322 type infochange = | Memused | Docinfo | Pdim;;
324 class type uioh = object
325 method display : unit
326 method key : int -> int -> uioh
327 method button : int -> bool -> int -> int -> int -> uioh
328 method motion : int -> int -> uioh
329 method pmotion : int -> int -> uioh
330 method infochanged : infochange -> unit
331 method scrollpw : (int * float * float)
332 method scrollph : (int * float * float)
333 method modehash : keyhash
334 end;;
336 type mode =
337 | Birdseye of (conf * leftx * pageno * pageno * anchor)
338 | Textentry of (textentry * onleave)
339 | View
340 | LinkNav of linktarget
341 and onleave = leavetextentrystatus -> unit
342 and leavetextentrystatus = | Cancel | Confirm
343 and helpitem = string * int * action
344 and action =
345 | Noaction
346 | Action of (uioh -> uioh)
347 and linktarget =
348 | Ltexact of ((pageno * int) * (int * int * int * int))
349 | Ltgendir of int
352 let isbirdseye = function Birdseye _ -> true | _ -> false;;
353 let istextentry = function Textentry _ -> true | _ -> false;;
355 type currently =
356 | Idle
357 | Loading of (page * gen)
358 | Tiling of (
359 page * opaque * colorspace * angle * gen * col * row * width * height
361 | Outlining of outline list
364 let emptykeyhash = Hashtbl.create 0;;
365 let nouioh : uioh = object (self)
366 method display = ()
367 method key _ _ = self
368 method button _ _ _ _ _ = self
369 method motion _ _ = self
370 method pmotion _ _ = self
371 method infochanged _ = ()
372 method scrollpw = (0, nan, nan)
373 method scrollph = (0, nan, nan)
374 method modehash = emptykeyhash
375 end;;
377 type state =
378 { mutable sr : Unix.file_descr
379 ; mutable sw : Unix.file_descr
380 ; mutable wsfd : Unix.file_descr
381 ; mutable errfd : Unix.file_descr option
382 ; mutable stderr : Unix.file_descr
383 ; mutable errmsgs : Buffer.t
384 ; mutable newerrmsgs : bool
385 ; mutable w : int
386 ; mutable x : int
387 ; mutable y : int
388 ; mutable scrollw : int
389 ; mutable hscrollh : int
390 ; mutable anchor : anchor
391 ; mutable ranchors : (string * string * anchor) list
392 ; mutable maxy : int
393 ; mutable layout : page list
394 ; pagemap : (pagemapkey, opaque) Hashtbl.t
395 ; tilemap : (tilemapkey, tile) Hashtbl.t
396 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
397 ; mutable pdims : (pageno * width * height * leftx) list
398 ; mutable pagecount : int
399 ; mutable currently : currently
400 ; mutable mstate : mstate
401 ; mutable searchpattern : string
402 ; mutable rects : (pageno * recttype * rect) list
403 ; mutable rects1 : (pageno * recttype * rect) list
404 ; mutable text : string
405 ; mutable fullscreen : (width * height) option
406 ; mutable mode : mode
407 ; mutable uioh : uioh
408 ; mutable outlines : outline array
409 ; mutable bookmarks : outline list
410 ; mutable path : string
411 ; mutable password : string
412 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
413 ; mutable memused : memsize
414 ; mutable gen : gen
415 ; mutable throttle : (page list * int * float) option
416 ; mutable autoscroll : int option
417 ; mutable ghyll : (int option -> unit)
418 ; mutable help : helpitem array
419 ; mutable docinfo : (int * string) list
420 ; mutable texid : GlTex.texture_id option
421 ; hists : hists
422 ; mutable prevzoom : float
423 ; mutable progress : float
424 ; mutable redisplay : bool
425 ; mutable mpos : mpos
426 ; mutable keystate : keystate
428 and hists =
429 { pat : string circbuf
430 ; pag : string circbuf
431 ; nav : anchor circbuf
432 ; sel : string circbuf
436 let defconf =
437 { scrollbw = 7
438 ; scrollh = 12
439 ; icase = true
440 ; preload = true
441 ; pagebias = 0
442 ; verbose = false
443 ; debug = false
444 ; scrollstep = 24
445 ; maxhfit = true
446 ; crophack = false
447 ; autoscrollstep = 2
448 ; maxwait = None
449 ; hlinks = false
450 ; underinfo = false
451 ; interpagespace = 2
452 ; zoom = 1.0
453 ; presentation = false
454 ; angle = 0
455 ; winw = 900
456 ; winh = 900
457 ; savebmarks = true
458 ; proportional = true
459 ; trimmargins = false
460 ; trimfuzz = (0,0,0,0)
461 ; memlimit = 32 lsl 20
462 ; texcount = 256
463 ; sliceheight = 24
464 ; thumbw = 76
465 ; jumpback = true
466 ; bgcolor = (0.5, 0.5, 0.5)
467 ; bedefault = false
468 ; scrollbarinpm = true
469 ; tilew = 2048
470 ; tileh = 2048
471 ; mustoresize = 128 lsl 20
472 ; checkers = true
473 ; aalevel = 8
474 ; urilauncher =
475 (match platform with
476 | Plinux | Pfreebsd | Pdragonflybsd
477 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
478 | Posx -> "open \"%s\""
479 | Pcygwin -> "cygstart %s"
480 | Punknown -> "echo %s")
481 ; pathlauncher = "lp \"%s\""
482 ; selcmd =
483 (match platform with
484 | Plinux | Pfreebsd | Pdragonflybsd
485 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
486 | Posx -> "pbcopy"
487 | Pcygwin -> "wsel"
488 | Punknown -> "cat")
489 ; colorspace = Rgb
490 ; invert = false
491 ; colorscale = 1.0
492 ; redirectstderr = false
493 ; ghyllscroll = None
494 ; columns = None
495 ; beyecolumns = None
496 ; updatecurs = false
497 ; keyhashes =
498 let mk n = (n, Hashtbl.create 1) in
499 [ mk "global"
500 ; mk "info"
501 ; mk "help"
502 ; mk "outline"
503 ; mk "listview"
504 ; mk "birdseye"
505 ; mk "textentry"
506 ; mk "links"
511 let findkeyhash c name =
512 try List.assoc name c.keyhashes
513 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
516 let conf = { defconf with angle = defconf.angle };;
518 type fontstate =
519 { mutable fontsize : int
520 ; mutable wwidth : float
521 ; mutable maxrows : int
525 let fstate =
526 { fontsize = 14
527 ; wwidth = nan
528 ; maxrows = -1
532 let setfontsize n =
533 fstate.fontsize <- n;
534 fstate.wwidth <- measurestr fstate.fontsize "w";
535 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
538 let geturl s =
539 let colonpos = try String.index s ':' with Not_found -> -1 in
540 let len = String.length s in
541 if colonpos >= 0 && colonpos + 3 < len
542 then (
543 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
544 then
545 let schemestartpos =
546 try String.rindex_from s colonpos ' '
547 with Not_found -> -1
549 let scheme =
550 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
552 match scheme with
553 | "http" | "ftp" | "mailto" ->
554 let epos =
555 try String.index_from s colonpos ' '
556 with Not_found -> len
558 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
559 | _ -> ""
560 else ""
562 else ""
565 let popen =
566 let shell, farg = "/bin/sh", "-c" in
567 fun s ->
568 let args = [|shell; farg; s|] in
569 ignore (Unix.create_process shell args Unix.stdin Unix.stdout Unix.stderr)
572 let gotouri uri =
573 if String.length conf.urilauncher = 0
574 then print_endline uri
575 else (
576 let url = geturl uri in
577 if String.length url = 0
578 then print_endline uri
579 else
580 let re = Str.regexp "%s" in
581 let command = Str.global_replace re url conf.urilauncher in
582 try popen command
583 with exn ->
584 Printf.eprintf
585 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
586 flush stderr;
590 let version () =
591 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
592 (platform_to_string platform) Sys.word_size Sys.ocaml_version
595 let makehelp () =
596 let strings = version () :: "" :: Help.keys in
597 Array.of_list (
598 List.map (fun s ->
599 let url = geturl s in
600 if String.length url > 0
601 then (s, 0, Action (fun u -> gotouri url; u))
602 else (s, 0, Noaction)
603 ) strings);
606 let noghyll _ = ();;
608 let state =
609 { sr = Unix.stdin
610 ; sw = Unix.stdin
611 ; wsfd = Unix.stdin
612 ; errfd = None
613 ; stderr = Unix.stderr
614 ; errmsgs = Buffer.create 0
615 ; newerrmsgs = false
616 ; x = 0
617 ; y = 0
618 ; w = 0
619 ; scrollw = 0
620 ; hscrollh = 0
621 ; anchor = emptyanchor
622 ; ranchors = []
623 ; layout = []
624 ; maxy = max_int
625 ; tilelru = Queue.create ()
626 ; pagemap = Hashtbl.create 10
627 ; tilemap = Hashtbl.create 10
628 ; pdims = []
629 ; pagecount = 0
630 ; currently = Idle
631 ; mstate = Mnone
632 ; rects = []
633 ; rects1 = []
634 ; text = ""
635 ; mode = View
636 ; fullscreen = None
637 ; searchpattern = ""
638 ; outlines = [||]
639 ; bookmarks = []
640 ; path = ""
641 ; password = ""
642 ; geomcmds = "", []
643 ; hists =
644 { nav = cbnew 10 (0, 0.0)
645 ; pat = cbnew 10 ""
646 ; pag = cbnew 10 ""
647 ; sel = cbnew 10 ""
649 ; memused = 0
650 ; gen = 0
651 ; throttle = None
652 ; autoscroll = None
653 ; ghyll = noghyll
654 ; help = makehelp ()
655 ; docinfo = []
656 ; texid = None
657 ; prevzoom = 1.0
658 ; progress = -1.0
659 ; uioh = nouioh
660 ; redisplay = false
661 ; mpos = (-1, -1)
662 ; keystate = KSnone
666 let vlog fmt =
667 if conf.verbose
668 then
669 Printf.kprintf prerr_endline fmt
670 else
671 Printf.kprintf ignore fmt
674 let launchpath () =
675 if String.length conf.pathlauncher = 0
676 then print_endline state.path
677 else (
678 let re = Str.regexp "%s" in
679 let command = Str.global_replace re state.path conf.pathlauncher in
680 try popen command
681 with exn ->
682 Printf.eprintf
683 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
684 flush stderr;
688 let redirectstderr () =
689 if conf.redirectstderr
690 then
691 let rfd, wfd = Unix.pipe () in
692 state.stderr <- Unix.dup Unix.stderr;
693 state.errfd <- Some rfd;
694 Unix.dup2 wfd Unix.stderr;
695 else (
696 state.newerrmsgs <- false;
697 begin match state.errfd with
698 | Some fd ->
699 Unix.close fd;
700 Unix.dup2 state.stderr Unix.stderr;
701 state.errfd <- None;
702 | None -> ()
703 end;
704 prerr_string (Buffer.contents state.errmsgs);
705 flush stderr;
706 Buffer.clear state.errmsgs;
710 module G =
711 struct
712 let postRedisplay who =
713 if conf.verbose
714 then prerr_endline ("redisplay for " ^ who);
715 state.redisplay <- true;
717 end;;
719 let getopaque pageno =
720 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
721 with Not_found -> None
724 let putopaque pageno opaque =
725 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
728 let pagetranslatepoint l x y =
729 let dy = y - l.pagedispy in
730 let y = dy + l.pagey in
731 let dx = x - l.pagedispx in
732 let x = dx + l.pagex in
733 (x, y);
736 let getunder x y =
737 let rec f = function
738 | l :: rest ->
739 begin match getopaque l.pageno with
740 | Some opaque ->
741 let x0 = l.pagedispx in
742 let x1 = x0 + l.pagevw in
743 let y0 = l.pagedispy in
744 let y1 = y0 + l.pagevh in
745 if y >= y0 && y <= y1 && x >= x0 && x <= x1
746 then
747 let px, py = pagetranslatepoint l x y in
748 match whatsunder opaque px py with
749 | Unone -> f rest
750 | under -> under
751 else f rest
752 | _ ->
753 f rest
755 | [] -> Unone
757 f state.layout
760 let showtext c s =
761 state.text <- Printf.sprintf "%c%s" c s;
762 G.postRedisplay "showtext";
765 let updateunder x y =
766 match getunder x y with
767 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
768 | Ulinkuri uri ->
769 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
770 Wsi.setcursor Wsi.CURSOR_INFO
771 | Ulinkgoto (page, _) ->
772 if conf.underinfo
773 then showtext 'p' ("age: " ^ string_of_int (page+1));
774 Wsi.setcursor Wsi.CURSOR_INFO
775 | Utext s ->
776 if conf.underinfo then showtext 'f' ("ont: " ^ s);
777 Wsi.setcursor Wsi.CURSOR_TEXT
778 | Uunexpected s ->
779 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
780 Wsi.setcursor Wsi.CURSOR_INHERIT
781 | Ulaunch s ->
782 if conf.underinfo then showtext 'l' ("launch: " ^ s);
783 Wsi.setcursor Wsi.CURSOR_INHERIT
784 | Unamed s ->
785 if conf.underinfo then showtext 'n' ("named: " ^ s);
786 Wsi.setcursor Wsi.CURSOR_INHERIT
787 | Uremote (filename, pageno) ->
788 if conf.underinfo then showtext 'r'
789 (Printf.sprintf "emote: %s (%d)" filename pageno);
790 Wsi.setcursor Wsi.CURSOR_INFO
793 let addchar s c =
794 let b = Buffer.create (String.length s + 1) in
795 Buffer.add_string b s;
796 Buffer.add_char b c;
797 Buffer.contents b;
800 let colorspace_of_string s =
801 match String.lowercase s with
802 | "rgb" -> Rgb
803 | "bgr" -> Bgr
804 | "gray" -> Gray
805 | _ -> failwith "invalid colorspace"
808 let int_of_colorspace = function
809 | Rgb -> 0
810 | Bgr -> 1
811 | Gray -> 2
814 let colorspace_of_int = function
815 | 0 -> Rgb
816 | 1 -> Bgr
817 | 2 -> Gray
818 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
821 let colorspace_to_string = function
822 | Rgb -> "rgb"
823 | Bgr -> "bgr"
824 | Gray -> "gray"
827 let intentry_with_suffix text key =
828 let c =
829 if key >= 32 && key < 127
830 then Char.chr key
831 else '\000'
833 match Char.lowercase c with
834 | '0' .. '9' ->
835 let text = addchar text c in
836 TEcont text
838 | 'k' | 'm' | 'g' ->
839 let text = addchar text c in
840 TEcont text
842 | _ ->
843 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
844 TEcont text
847 let columns_to_string (n, a, b) =
848 if a = 0 && b = 0
849 then Printf.sprintf "%d" n
850 else Printf.sprintf "%d,%d,%d" n a b;
853 let columns_of_string s =
855 (int_of_string s, 0, 0)
856 with _ ->
857 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
860 let readcmd fd =
861 let s = "xxxx" in
862 let n = Unix.read fd s 0 4 in
863 if n != 4 then failwith "incomplete read(len)";
864 let len = 0
865 lor (Char.code s.[0] lsl 24)
866 lor (Char.code s.[1] lsl 16)
867 lor (Char.code s.[2] lsl 8)
868 lor (Char.code s.[3] lsl 0)
870 let s = String.create len in
871 let n = Unix.read fd s 0 len in
872 if n != len then failwith "incomplete read(data)";
876 let btod b = if b then 1 else 0;;
878 let wcmd fmt =
879 let b = Buffer.create 16 in
880 Buffer.add_string b "llll";
881 Printf.kbprintf
882 (fun b ->
883 let s = Buffer.contents b in
884 let n = String.length s in
885 let len = n - 4 in
886 (* dolog "wcmd %S" (String.sub s 4 len); *)
887 s.[0] <- Char.chr ((len lsr 24) land 0xff);
888 s.[1] <- Char.chr ((len lsr 16) land 0xff);
889 s.[2] <- Char.chr ((len lsr 8) land 0xff);
890 s.[3] <- Char.chr (len land 0xff);
891 let n' = Unix.write state.sw s 0 n in
892 if n' != n then failwith "write failed";
893 ) b fmt;
896 let calcips h =
897 if conf.presentation
898 then
899 let d = conf.winh - h in
900 max 0 ((d + 1) / 2)
901 else
902 conf.interpagespace
905 let calcheight () =
906 let rec f pn ph pi fh l =
907 match l with
908 | (n, _, h, _) :: rest ->
909 let ips = calcips h in
910 let fh =
911 if conf.presentation
912 then fh+ips
913 else (
914 if isbirdseye state.mode && pn = 0
915 then fh + ips
916 else fh
919 let fh = fh + ((n - pn) * (ph + pi)) in
920 f n h ips fh rest;
922 | [] ->
923 let inc =
924 if conf.presentation || (isbirdseye state.mode && pn = 0)
925 then 0
926 else -pi
928 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
929 max 0 fh
931 let fh = f 0 0 0 0 state.pdims in
935 let calcheight () =
936 match conf.columns with
937 | None -> calcheight ()
938 | Some (_, b) ->
939 if Array.length b > 0
940 then
941 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
942 y + h
943 else 0
946 let getpageyh pageno =
947 let rec f pn ph pi y l =
948 match l with
949 | (n, _, h, _) :: rest ->
950 let ips = calcips h in
951 if n >= pageno
952 then
953 let h = if n = pageno then h else ph in
954 if conf.presentation && n = pageno
955 then
956 y + (pageno - pn) * (ph + pi) + pi, h
957 else
958 y + (pageno - pn) * (ph + pi), h
959 else
960 let y = y + (if conf.presentation then pi else 0) in
961 let y = y + (n - pn) * (ph + pi) in
962 f n h ips y rest
964 | [] ->
965 y + (pageno - pn) * (ph + pi), ph
967 f 0 0 0 0 state.pdims
970 let getpageyh pageno =
971 match conf.columns with
972 | None -> getpageyh pageno
973 | Some (_, b) ->
974 let (_, _, y, (_, _, h, _)) = b.(pageno) in
975 y, h
978 let getpagedim pageno =
979 let rec f ppdim l =
980 match l with
981 | (n, _, _, _) as pdim :: rest ->
982 if n >= pageno
983 then (if n = pageno then pdim else ppdim)
984 else f pdim rest
986 | [] -> ppdim
988 f (-1, -1, -1, -1) state.pdims
991 let getpagey pageno = fst (getpageyh pageno);;
993 let nogeomcmds cmds =
994 match cmds with
995 | _, [] -> true
996 | _ -> false
999 let layout1 y sh =
1000 let sh = sh - state.hscrollh in
1001 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
1002 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
1003 match pdims with
1004 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
1005 let ips = calcips h in
1006 let yinc =
1007 if conf.presentation || (isbirdseye state.mode && pageno = 0)
1008 then ips
1009 else 0
1011 (w, h, ips, xoff), rest, pdimno + 1, yinc
1012 | _ ->
1013 prev, pdims, pdimno, 0
1015 let dy = dy + yinc in
1016 let py = py + yinc in
1017 if pageno = state.pagecount || dy >= sh
1018 then
1019 accu
1020 else
1021 let vy = y + dy in
1022 if py + h <= vy - yinc
1023 then
1024 let py = py + h + ips in
1025 let dy = max 0 (py - y) in
1026 f ~pageno:(pageno+1)
1027 ~pdimno
1028 ~prev:curr
1031 ~pdims:rest
1032 ~accu
1033 else
1034 let pagey = vy - py in
1035 let pagevh = h - pagey in
1036 let pagevh = min (sh - dy) pagevh in
1037 let off = if yinc > 0 then py - vy else 0 in
1038 let py = py + h + ips in
1039 let pagex, dx =
1040 let xoff = xoff +
1041 if state.w < conf.winw - state.scrollw
1042 then (conf.winw - state.scrollw - state.w) / 2
1043 else 0
1045 let dispx = xoff + state.x in
1046 if dispx < 0
1047 then (-dispx, 0)
1048 else (0, dispx)
1050 let pagevw =
1051 let lw = w - pagex in
1052 min lw (conf.winw - state.scrollw)
1054 let e =
1055 { pageno = pageno
1056 ; pagedimno = pdimno
1057 ; pagew = w
1058 ; pageh = h
1059 ; pagex = pagex
1060 ; pagey = pagey + off
1061 ; pagevw = pagevw
1062 ; pagevh = pagevh - off
1063 ; pagedispx = dx
1064 ; pagedispy = dy + off
1067 let accu = e :: accu in
1068 f ~pageno:(pageno+1)
1069 ~pdimno
1070 ~prev:curr
1072 ~dy:(dy+pagevh+ips)
1073 ~pdims:rest
1074 ~accu
1076 if nogeomcmds state.geomcmds
1077 then (
1078 let accu =
1080 ~pageno:0
1081 ~pdimno:~-1
1082 ~prev:(0,0,0,0)
1083 ~py:0
1084 ~dy:0
1085 ~pdims:state.pdims
1086 ~accu:[]
1088 List.rev accu
1090 else
1094 let layoutN ((columns, coverA, coverB), b) y sh =
1095 let sh = sh - state.hscrollh in
1096 let rec fold accu n =
1097 if n = Array.length b
1098 then accu
1099 else
1100 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1101 if (vy - y) > sh &&
1102 (n = coverA - 1
1103 || n = state.pagecount - coverB
1104 || (n - coverA) mod columns = columns - 1)
1105 then accu
1106 else
1107 let accu =
1108 if vy + h > y
1109 then
1110 let pagey = max 0 (y - vy) in
1111 let pagedispy = if pagey > 0 then 0 else vy - y in
1112 let pagedispx, pagex, pagevw =
1113 let pdx =
1114 if n = coverA - 1 || n = state.pagecount - coverB
1115 then state.x + (conf.winw - state.scrollw - w) / 2
1116 else dx + xoff + state.x
1118 if pdx < 0
1119 then 0, -pdx, w + pdx
1120 else pdx, 0, min (conf.winw - state.scrollw) w
1122 let pagevh = min (h - pagey) (sh - pagedispy) in
1123 if pagedispx < conf.winw - state.scrollw && pagevw > 0 && pagevh > 0
1124 then
1125 let e =
1126 { pageno = n
1127 ; pagedimno = pdimno
1128 ; pagew = w
1129 ; pageh = h
1130 ; pagex = pagex
1131 ; pagey = pagey
1132 ; pagevw = pagevw
1133 ; pagevh = pagevh
1134 ; pagedispx = pagedispx
1135 ; pagedispy = pagedispy
1138 e :: accu
1139 else
1140 accu
1141 else
1142 accu
1144 fold accu (n+1)
1146 if nogeomcmds state.geomcmds
1147 then List.rev (fold [] 0)
1148 else []
1151 let layout y sh =
1152 match conf.columns with
1153 | None -> layout1 y sh
1154 | Some c -> layoutN c y sh
1157 let clamp incr =
1158 let y = state.y + incr in
1159 let y = max 0 y in
1160 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1164 let itertiles l f =
1165 let tilex = l.pagex mod conf.tilew in
1166 let tiley = l.pagey mod conf.tileh in
1168 let col = l.pagex / conf.tilew in
1169 let row = l.pagey / conf.tileh in
1171 let vw =
1172 let a = l.pagew - l.pagex in
1173 let b = conf.winw - state.scrollw in
1174 min a b
1175 and vh = l.pagevh in
1177 let rec rowloop row y0 dispy h =
1178 if h = 0
1179 then ()
1180 else (
1181 let dh = conf.tileh - y0 in
1182 let dh = min h dh in
1183 let rec colloop col x0 dispx w =
1184 if w = 0
1185 then ()
1186 else (
1187 let dw = conf.tilew - x0 in
1188 let dw = min w dw in
1190 f col row dispx dispy x0 y0 dw dh;
1191 colloop (col+1) 0 (dispx+dw) (w-dw)
1194 colloop col tilex l.pagedispx vw;
1195 rowloop (row+1) 0 (dispy+dh) (h-dh)
1198 if vw > 0 && vh > 0
1199 then rowloop row tiley l.pagedispy vh;
1202 let gettileopaque l col row =
1203 let key =
1204 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1206 try Some (Hashtbl.find state.tilemap key)
1207 with Not_found -> None
1210 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1211 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1212 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1215 let drawtiles l color =
1216 GlDraw.color color;
1217 let f col row x y tilex tiley w h =
1218 match gettileopaque l col row with
1219 | Some (opaque, _, t) ->
1220 let params = x, y, w, h, tilex, tiley in
1221 if conf.invert
1222 then (
1223 Gl.enable `blend;
1224 GlFunc.blend_func `zero `one_minus_src_color;
1226 drawtile params opaque;
1227 if conf.invert
1228 then Gl.disable `blend;
1229 if conf.debug
1230 then (
1231 let s = Printf.sprintf
1232 "%d[%d,%d] %f sec"
1233 l.pageno col row t
1235 let w = measurestr fstate.fontsize s in
1236 GlMisc.push_attrib [`current];
1237 GlDraw.color (0.0, 0.0, 0.0);
1238 GlDraw.rect
1239 (float (x-2), float (y-2))
1240 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1241 GlDraw.color (1.0, 1.0, 1.0);
1242 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1243 GlMisc.pop_attrib ();
1246 | _ ->
1247 let w =
1248 let lw = conf.winw - state.scrollw - x in
1249 min lw w
1250 and h =
1251 let lh = conf.winh - y in
1252 min lh h
1254 Gl.enable `texture_2d;
1255 begin match state.texid with
1256 | Some id ->
1257 GlTex.bind_texture `texture_2d id;
1258 let x0 = float x
1259 and y0 = float y
1260 and x1 = float (x+w)
1261 and y1 = float (y+h) in
1263 let tw = float w /. 64.0
1264 and th = float h /. 64.0 in
1265 let tx0 = float tilex /. 64.0
1266 and ty0 = float tiley /. 64.0 in
1267 let tx1 = tx0 +. tw
1268 and ty1 = ty0 +. th in
1269 GlDraw.begins `quads;
1270 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1271 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1272 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1273 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1274 GlDraw.ends ();
1276 Gl.disable `texture_2d;
1277 | None ->
1278 GlDraw.color (1.0, 1.0, 1.0);
1279 GlDraw.rect
1280 (float x, float y)
1281 (float (x+w), float (y+h));
1282 end;
1283 if w > 128 && h > fstate.fontsize + 10
1284 then (
1285 GlDraw.color (0.0, 0.0, 0.0);
1286 let c, r =
1287 if conf.verbose
1288 then (col*conf.tilew, row*conf.tileh)
1289 else col, row
1291 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1293 GlDraw.color color;
1295 itertiles l f
1298 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1300 let tilevisible1 l x y =
1301 let ax0 = l.pagex
1302 and ax1 = l.pagex + l.pagevw
1303 and ay0 = l.pagey
1304 and ay1 = l.pagey + l.pagevh in
1306 let bx0 = x
1307 and by0 = y in
1308 let bx1 = min (bx0 + conf.tilew) l.pagew
1309 and by1 = min (by0 + conf.tileh) l.pageh in
1311 let rx0 = max ax0 bx0
1312 and ry0 = max ay0 by0
1313 and rx1 = min ax1 bx1
1314 and ry1 = min ay1 by1 in
1316 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1317 nonemptyintersection
1320 let tilevisible layout n x y =
1321 let rec findpageinlayout = function
1322 | l :: _ when l.pageno = n -> tilevisible1 l x y
1323 | _ :: rest -> findpageinlayout rest
1324 | [] -> false
1326 findpageinlayout layout
1329 let tileready l x y =
1330 tilevisible1 l x y &&
1331 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1334 let tilepage n p layout =
1335 let rec loop = function
1336 | l :: rest ->
1337 if l.pageno = n
1338 then
1339 let f col row _ _ _ _ _ _ =
1340 if state.currently = Idle
1341 then
1342 match gettileopaque l col row with
1343 | Some _ -> ()
1344 | None ->
1345 let x = col*conf.tilew
1346 and y = row*conf.tileh in
1347 let w =
1348 let w = l.pagew - x in
1349 min w conf.tilew
1351 let h =
1352 let h = l.pageh - y in
1353 min h conf.tileh
1355 wcmd "tile %s %d %d %d %d" p x y w h;
1356 state.currently <-
1357 Tiling (
1358 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1359 conf.tilew, conf.tileh
1362 itertiles l f;
1363 else
1364 loop rest
1366 | [] -> ()
1368 if nogeomcmds state.geomcmds
1369 then loop layout;
1372 let preloadlayout visiblepages =
1373 let presentation = conf.presentation in
1374 let interpagespace = conf.interpagespace in
1375 let maxy = state.maxy in
1376 conf.presentation <- false;
1377 conf.interpagespace <- 0;
1378 state.maxy <- calcheight ();
1379 let y =
1380 match visiblepages with
1381 | [] -> 0
1382 | l :: _ -> getpagey l.pageno + l.pagey
1384 let y = if y < conf.winh then 0 else y - conf.winh in
1385 let h = state.y - y + conf.winh*3 in
1386 let pages = layout y h in
1387 conf.presentation <- presentation;
1388 conf.interpagespace <- interpagespace;
1389 state.maxy <- maxy;
1390 pages;
1393 let load pages =
1394 let rec loop pages =
1395 if state.currently != Idle
1396 then ()
1397 else
1398 match pages with
1399 | l :: rest ->
1400 begin match getopaque l.pageno with
1401 | None ->
1402 wcmd "page %d %d" l.pageno l.pagedimno;
1403 state.currently <- Loading (l, state.gen);
1404 | Some opaque ->
1405 tilepage l.pageno opaque pages;
1406 loop rest
1407 end;
1408 | _ -> ()
1410 if nogeomcmds state.geomcmds
1411 then loop pages
1414 let preload pages =
1415 load pages;
1416 if conf.preload && state.currently = Idle
1417 then load (preloadlayout pages);
1420 let layoutready layout =
1421 let rec fold all ls =
1422 all && match ls with
1423 | l :: rest ->
1424 let seen = ref false in
1425 let allvisible = ref true in
1426 let foo col row _ _ _ _ _ _ =
1427 seen := true;
1428 allvisible := !allvisible &&
1429 begin match gettileopaque l col row with
1430 | Some _ -> true
1431 | None -> false
1434 itertiles l foo;
1435 fold (!seen && !allvisible) rest
1436 | [] -> true
1438 let alltilesvisible = fold true layout in
1439 alltilesvisible;
1442 let gotoy y =
1443 let y = bound y 0 state.maxy in
1444 let y, layout, proceed =
1445 match conf.maxwait with
1446 | Some time when state.ghyll == noghyll ->
1447 begin match state.throttle with
1448 | None ->
1449 let layout = layout y conf.winh in
1450 let ready = layoutready layout in
1451 if not ready
1452 then (
1453 load layout;
1454 state.throttle <- Some (layout, y, now ());
1456 else G.postRedisplay "gotoy showall (None)";
1457 y, layout, ready
1458 | Some (_, _, started) ->
1459 let dt = now () -. started in
1460 if dt > time
1461 then (
1462 state.throttle <- None;
1463 let layout = layout y conf.winh in
1464 load layout;
1465 G.postRedisplay "maxwait";
1466 y, layout, true
1468 else -1, [], false
1471 | _ ->
1472 let layout = layout y conf.winh in
1473 if true || layoutready layout
1474 then G.postRedisplay "gotoy ready";
1475 y, layout, true
1477 if proceed
1478 then (
1479 state.y <- y;
1480 state.layout <- layout;
1481 begin match state.mode with
1482 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1483 if not (pagevisible layout pageno)
1484 then (
1485 match state.layout with
1486 | [] -> ()
1487 | l :: _ ->
1488 state.mode <- Birdseye (
1489 conf, leftx, l.pageno, hooverpageno, anchor
1492 | LinkNav (Ltgendir dir as lt) ->
1493 let linknav =
1494 let rec loop = function
1495 | [] -> lt
1496 | l :: rest ->
1497 match getopaque l.pageno with
1498 | None -> loop rest
1499 | Some opaque ->
1500 let link =
1501 let ld =
1502 if dir = 0
1503 then LDfirstvisible (l.pagex, l.pagey, dir)
1504 else (
1505 if dir > 0 then LDfirst else LDlast
1508 findlink opaque ld
1510 match link with
1511 | Lnotfound -> loop rest
1512 | Lfound (n, x0, y0, x1, y1) ->
1513 Ltexact ((l.pageno, n), (x0, y0, x1, y1))
1515 loop state.layout
1517 state.mode <- LinkNav linknav
1518 | _ -> ()
1519 end;
1520 preload layout;
1522 state.ghyll <- noghyll;
1525 let conttiling pageno opaque =
1526 tilepage pageno opaque
1527 (if conf.preload then preloadlayout state.layout else state.layout)
1530 let gotoy_and_clear_text y =
1531 gotoy y;
1532 if not conf.verbose then state.text <- "";
1535 let getanchor () =
1536 match state.layout with
1537 | [] -> emptyanchor
1538 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1541 let getanchory (n, top) =
1542 let y, h = getpageyh n in
1543 y + (truncate (top *. float h));
1546 let gotoanchor anchor =
1547 gotoy (getanchory anchor);
1550 let addnav () =
1551 cbput state.hists.nav (getanchor ());
1554 let getnav dir =
1555 let anchor = cbgetc state.hists.nav dir in
1556 getanchory anchor;
1559 let gotoghyll y =
1560 let rec scroll f n a b =
1561 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1562 let snake f a b =
1563 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1564 if f < a
1565 then s (float f /. float a)
1566 else (
1567 if f > b
1568 then 1.0 -. s ((float (f-b) /. float (n-b)))
1569 else 1.0
1572 snake f a b
1573 and summa f n a b =
1574 (* courtesy:
1575 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1576 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1577 let iv1 = iv f in
1578 let ins = float a *. iv1
1579 and outs = float (n-b) *. iv1 in
1580 let ones = b - a in
1581 ins +. outs +. float ones
1583 let rec set (_N, _A, _B) y sy =
1584 let sum = summa 1.0 _N _A _B in
1585 let dy = float (y - sy) in
1586 state.ghyll <- (
1587 let rec gf n y1 o =
1588 if n >= _N
1589 then state.ghyll <- noghyll
1590 else
1591 let go n =
1592 let s = scroll n _N _A _B in
1593 let y1 = y1 +. ((s *. dy) /. sum) in
1594 gotoy_and_clear_text (truncate y1);
1595 state.ghyll <- gf (n+1) y1;
1597 match o with
1598 | None -> go n
1599 | Some y' -> set (_N/2, 0, 0) y' state.y
1601 gf 0 (float state.y)
1604 match conf.ghyllscroll with
1605 | None ->
1606 gotoy_and_clear_text y
1607 | Some nab ->
1608 if state.ghyll == noghyll
1609 then set nab y state.y
1610 else state.ghyll (Some y)
1613 let gotopage n top =
1614 let y, h = getpageyh n in
1615 let y = y + (truncate (top *. float h)) in
1616 gotoghyll y
1619 let gotopage1 n top =
1620 let y = getpagey n in
1621 let y = y + top in
1622 gotoghyll y
1625 let invalidate s f =
1626 state.layout <- [];
1627 state.pdims <- [];
1628 state.rects <- [];
1629 state.rects1 <- [];
1630 match state.geomcmds with
1631 | ps, [] when String.length ps = 0 ->
1632 f ();
1633 state.geomcmds <- s, [];
1635 | ps, [] ->
1636 state.geomcmds <- ps, [s, f];
1638 | ps, (s', _) :: rest when s' = s ->
1639 state.geomcmds <- ps, ((s, f) :: rest);
1641 | ps, cmds ->
1642 state.geomcmds <- ps, ((s, f) :: cmds);
1645 let opendoc path password =
1646 state.path <- path;
1647 state.password <- password;
1648 state.gen <- state.gen + 1;
1649 state.docinfo <- [];
1651 setaalevel conf.aalevel;
1652 Wsi.settitle ("llpp " ^ Filename.basename path);
1653 wcmd "open %s\000%s\000" path password;
1654 invalidate "reqlayout"
1655 (fun () ->
1656 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1659 let scalecolor c =
1660 let c = c *. conf.colorscale in
1661 (c, c, c);
1664 let scalecolor2 (r, g, b) =
1665 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1668 let represent () =
1669 let docolumns = function
1670 | None -> ()
1671 | Some ((columns, coverA, coverB), _) ->
1672 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1673 let rec loop pageno pdimno pdim x y rowh pdims =
1674 if pageno = state.pagecount
1675 then ()
1676 else
1677 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1678 match pdims with
1679 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1680 pdimno+1, pdim, rest
1681 | _ ->
1682 pdimno, pdim, pdims
1684 let x, y, rowh' =
1685 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1686 then (
1687 (conf.winw - state.scrollw - w) / 2,
1688 y + rowh + conf.interpagespace, h
1690 else (
1691 if (pageno - coverA) mod columns = 0
1692 then 0, y + rowh + conf.interpagespace, h
1693 else x, y, max rowh h
1696 let rec fixrow m = if m = pageno then () else
1697 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1698 if h < rowh
1699 then (
1700 let y = y + (rowh - h) / 2 in
1701 a.(m) <- (pdimno, x, y, pdim);
1703 fixrow (m+1)
1705 if pageno > 1 && (pageno - coverA) mod columns = 0
1706 then fixrow (pageno - columns);
1707 a.(pageno) <- (pdimno, x, y, pdim);
1708 let x = x + w + xoff*2 + conf.interpagespace in
1709 loop (pageno+1) pdimno pdim x y rowh' pdims
1711 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1712 conf.columns <- Some ((columns, coverA, coverB), a);
1714 docolumns conf.columns;
1715 state.maxy <- calcheight ();
1716 state.hscrollh <-
1717 if state.w <= conf.winw - state.scrollw
1718 then 0
1719 else state.scrollw
1721 match state.mode with
1722 | Birdseye (_, _, pageno, _, _) ->
1723 let y, h = getpageyh pageno in
1724 let top = (conf.winh - h) / 2 in
1725 gotoy (max 0 (y - top))
1726 | _ -> gotoanchor state.anchor
1729 let reshape =
1730 let firsttime = ref true in
1731 fun ~w ~h ->
1732 GlDraw.viewport 0 0 w h;
1733 if nogeomcmds state.geomcmds && not !firsttime
1734 then state.anchor <- getanchor ();
1736 firsttime := false;
1737 conf.winw <- w;
1738 let w = truncate (float w *. conf.zoom) - state.scrollw in
1739 let w = max w 2 in
1740 conf.winh <- h;
1741 setfontsize fstate.fontsize;
1742 GlMat.mode `modelview;
1743 GlMat.load_identity ();
1745 GlMat.mode `projection;
1746 GlMat.load_identity ();
1747 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1748 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1749 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1751 invalidate "geometry"
1752 (fun () ->
1753 state.w <- w;
1754 let w =
1755 match conf.columns with
1756 | None -> w
1757 | Some ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1759 wcmd "geometry %d %d" w h);
1762 let enttext () =
1763 let len = String.length state.text in
1764 let drawstring s =
1765 let hscrollh =
1766 match state.mode with
1767 | View -> state.hscrollh
1768 | _ -> 0
1770 let rect x w =
1771 GlDraw.rect
1772 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1773 (x+.w, float (conf.winh - hscrollh))
1776 let w = float (conf.winw - state.scrollw - 1) in
1777 if state.progress >= 0.0 && state.progress < 1.0
1778 then (
1779 GlDraw.color (0.3, 0.3, 0.3);
1780 let w1 = w *. state.progress in
1781 rect 0.0 w1;
1782 GlDraw.color (0.0, 0.0, 0.0);
1783 rect w1 (w-.w1)
1785 else (
1786 GlDraw.color (0.0, 0.0, 0.0);
1787 rect 0.0 w;
1790 GlDraw.color (1.0, 1.0, 1.0);
1791 drawstring fstate.fontsize
1792 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1794 let s =
1795 match state.mode with
1796 | Textentry ((prefix, text, _, _, _), _) ->
1797 let s =
1798 if len > 0
1799 then
1800 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1801 else
1802 Printf.sprintf "%s%s_" prefix text
1806 | _ -> state.text
1808 let s =
1809 if state.newerrmsgs
1810 then (
1811 if not (istextentry state.mode)
1812 then
1813 let s1 = "(press 'e' to review error messasges)" in
1814 if String.length s > 0 then s ^ " " ^ s1 else s1
1815 else s
1817 else s
1819 if String.length s > 0
1820 then drawstring s
1823 let gctiles () =
1824 let len = Queue.length state.tilelru in
1825 let rec loop qpos =
1826 if state.memused <= conf.memlimit
1827 then ()
1828 else (
1829 if qpos < len
1830 then
1831 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1832 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1833 let (_, pw, ph, _) = getpagedim n in
1835 gen = state.gen
1836 && colorspace = conf.colorspace
1837 && angle = conf.angle
1838 && pagew = pw
1839 && pageh = ph
1840 && (
1841 let layout =
1842 match state.throttle with
1843 | None ->
1844 if conf.preload
1845 then preloadlayout state.layout
1846 else state.layout
1847 | Some (layout, _, _) ->
1848 layout
1850 let x = col*conf.tilew
1851 and y = row*conf.tileh in
1852 tilevisible layout n x y
1854 then Queue.push lruitem state.tilelru
1855 else (
1856 wcmd "freetile %s" p;
1857 state.memused <- state.memused - s;
1858 state.uioh#infochanged Memused;
1859 Hashtbl.remove state.tilemap k;
1861 loop (qpos+1)
1864 loop 0
1867 let flushtiles () =
1868 Queue.iter (fun (k, p, s) ->
1869 wcmd "freetile %s" p;
1870 state.memused <- state.memused - s;
1871 state.uioh#infochanged Memused;
1872 Hashtbl.remove state.tilemap k;
1873 ) state.tilelru;
1874 Queue.clear state.tilelru;
1875 load state.layout;
1878 let logcurrently = function
1879 | Idle -> dolog "Idle"
1880 | Loading (l, gen) ->
1881 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1882 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1883 dolog
1884 "Tiling %d[%d,%d] page=%s cs=%s angle"
1885 l.pageno col row pageopaque
1886 (colorspace_to_string colorspace)
1888 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1889 angle gen conf.angle state.gen
1890 tilew tileh
1891 conf.tilew conf.tileh
1893 | Outlining _ ->
1894 dolog "outlining"
1897 let act cmds =
1898 (* dolog "%S" cmds; *)
1899 let op, args =
1900 let spacepos =
1901 try String.index cmds ' '
1902 with Not_found -> -1
1904 if spacepos = -1
1905 then cmds, ""
1906 else
1907 let l = String.length cmds in
1908 let op = String.sub cmds 0 spacepos in
1909 op, begin
1910 if l - spacepos < 2 then ""
1911 else String.sub cmds (spacepos+1) (l-spacepos-1)
1914 match op with
1915 | "clear" ->
1916 state.uioh#infochanged Pdim;
1917 state.pdims <- [];
1919 | "clearrects" ->
1920 state.rects <- state.rects1;
1921 G.postRedisplay "clearrects";
1923 | "continue" ->
1924 let n =
1925 try Scanf.sscanf args "%u" (fun n -> n)
1926 with exn ->
1927 dolog "error processing 'continue' %S: %s"
1928 cmds (Printexc.to_string exn);
1929 exit 1;
1931 state.pagecount <- n;
1932 begin match state.currently with
1933 | Outlining l ->
1934 state.currently <- Idle;
1935 state.outlines <- Array.of_list (List.rev l)
1936 | _ -> ()
1937 end;
1939 let cur, cmds = state.geomcmds in
1940 if String.length cur = 0
1941 then failwith "umpossible";
1943 begin match List.rev cmds with
1944 | [] ->
1945 state.geomcmds <- "", [];
1946 represent ();
1947 | (s, f) :: rest ->
1948 f ();
1949 state.geomcmds <- s, List.rev rest;
1950 end;
1951 if conf.maxwait = None
1952 then G.postRedisplay "continue";
1954 | "title" ->
1955 Wsi.settitle args
1957 | "msg" ->
1958 showtext ' ' args
1960 | "vmsg" ->
1961 if conf.verbose
1962 then showtext ' ' args
1964 | "progress" ->
1965 let progress, text =
1967 Scanf.sscanf args "%f %n"
1968 (fun f pos ->
1969 f, String.sub args pos (String.length args - pos))
1970 with exn ->
1971 dolog "error processing 'progress' %S: %s"
1972 cmds (Printexc.to_string exn);
1973 exit 1;
1975 state.text <- text;
1976 state.progress <- progress;
1977 G.postRedisplay "progress"
1979 | "firstmatch" ->
1980 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1982 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1983 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1984 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1985 with exn ->
1986 dolog "error processing 'firstmatch' %S: %s"
1987 cmds (Printexc.to_string exn);
1988 exit 1;
1990 let y = (getpagey pageno) + truncate y0 in
1991 addnav ();
1992 gotoy y;
1993 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1995 | "match" ->
1996 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1998 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1999 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2000 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2001 with exn ->
2002 dolog "error processing 'match' %S: %s"
2003 cmds (Printexc.to_string exn);
2004 exit 1;
2006 state.rects1 <-
2007 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2009 | "page" ->
2010 let pageopaque, t =
2012 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2013 with exn ->
2014 dolog "error processing 'page' %S: %s"
2015 cmds (Printexc.to_string exn);
2016 exit 1;
2018 begin match state.currently with
2019 | Loading (l, gen) ->
2020 vlog "page %d took %f sec" l.pageno t;
2021 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2022 begin match state.throttle with
2023 | None ->
2024 let preloadedpages =
2025 if conf.preload
2026 then preloadlayout state.layout
2027 else state.layout
2029 let evict () =
2030 let module IntSet =
2031 Set.Make (struct type t = int let compare = (-) end) in
2032 let set =
2033 List.fold_left (fun s l -> IntSet.add l.pageno s)
2034 IntSet.empty preloadedpages
2036 let evictedpages =
2037 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2038 if not (IntSet.mem pageno set)
2039 then (
2040 wcmd "freepage %s" opaque;
2041 key :: accu
2043 else accu
2044 ) state.pagemap []
2046 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2048 evict ();
2049 state.currently <- Idle;
2050 if gen = state.gen
2051 then (
2052 tilepage l.pageno pageopaque state.layout;
2053 load state.layout;
2054 load preloadedpages;
2055 if pagevisible state.layout l.pageno
2056 && layoutready state.layout
2057 then G.postRedisplay "page";
2060 | Some (layout, _, _) ->
2061 state.currently <- Idle;
2062 tilepage l.pageno pageopaque layout;
2063 load state.layout
2064 end;
2066 | _ ->
2067 dolog "Inconsistent loading state";
2068 logcurrently state.currently;
2069 exit 1
2072 | "tile" ->
2073 let (x, y, opaque, size, t) =
2075 Scanf.sscanf args "%u %u %s %u %f"
2076 (fun x y p size t -> (x, y, p, size, t))
2077 with exn ->
2078 dolog "error processing 'tile' %S: %s"
2079 cmds (Printexc.to_string exn);
2080 exit 1;
2082 begin match state.currently with
2083 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2084 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2086 if tilew != conf.tilew || tileh != conf.tileh
2087 then (
2088 wcmd "freetile %s" opaque;
2089 state.currently <- Idle;
2090 load state.layout;
2092 else (
2093 puttileopaque l col row gen cs angle opaque size t;
2094 state.memused <- state.memused + size;
2095 state.uioh#infochanged Memused;
2096 gctiles ();
2097 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2098 opaque, size) state.tilelru;
2100 let layout =
2101 match state.throttle with
2102 | None -> state.layout
2103 | Some (layout, _, _) -> layout
2106 state.currently <- Idle;
2107 if gen = state.gen
2108 && conf.colorspace = cs
2109 && conf.angle = angle
2110 && tilevisible layout l.pageno x y
2111 then conttiling l.pageno pageopaque;
2113 begin match state.throttle with
2114 | None ->
2115 preload state.layout;
2116 if gen = state.gen
2117 && conf.colorspace = cs
2118 && conf.angle = angle
2119 && tilevisible state.layout l.pageno x y
2120 then G.postRedisplay "tile nothrottle";
2122 | Some (layout, y, _) ->
2123 let ready = layoutready layout in
2124 if ready
2125 then (
2126 state.y <- y;
2127 state.layout <- layout;
2128 state.throttle <- None;
2129 G.postRedisplay "throttle";
2131 else load layout;
2132 end;
2135 | _ ->
2136 dolog "Inconsistent tiling state";
2137 logcurrently state.currently;
2138 exit 1
2141 | "pdim" ->
2142 let pdim =
2144 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2145 with exn ->
2146 dolog "error processing 'pdim' %S: %s"
2147 cmds (Printexc.to_string exn);
2148 exit 1;
2150 state.uioh#infochanged Pdim;
2151 state.pdims <- pdim :: state.pdims
2153 | "o" ->
2154 let (l, n, t, h, pos) =
2156 Scanf.sscanf args "%u %u %d %u %n"
2157 (fun l n t h pos -> l, n, t, h, pos)
2158 with exn ->
2159 dolog "error processing 'o' %S: %s"
2160 cmds (Printexc.to_string exn);
2161 exit 1;
2163 let s = String.sub args pos (String.length args - pos) in
2164 let outline = (s, l, (n, float t /. float h)) in
2165 begin match state.currently with
2166 | Outlining outlines ->
2167 state.currently <- Outlining (outline :: outlines)
2168 | Idle ->
2169 state.currently <- Outlining [outline]
2170 | currently ->
2171 dolog "invalid outlining state";
2172 logcurrently currently
2175 | "info" ->
2176 state.docinfo <- (1, args) :: state.docinfo
2178 | "infoend" ->
2179 state.uioh#infochanged Docinfo;
2180 state.docinfo <- List.rev state.docinfo
2182 | _ ->
2183 dolog "unknown cmd `%S'" cmds
2186 let onhist cb =
2187 let rc = cb.rc in
2188 let action = function
2189 | HCprev -> cbget cb ~-1
2190 | HCnext -> cbget cb 1
2191 | HCfirst -> cbget cb ~-(cb.rc)
2192 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2193 and cancel () = cb.rc <- rc
2194 in (action, cancel)
2197 let search pattern forward =
2198 if String.length pattern > 0
2199 then
2200 let pn, py =
2201 match state.layout with
2202 | [] -> 0, 0
2203 | l :: _ ->
2204 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2206 wcmd "search %d %d %d %d,%s\000"
2207 (btod conf.icase) pn py (btod forward) pattern;
2210 let intentry text key =
2211 let c =
2212 if key >= 32 && key < 127
2213 then Char.chr key
2214 else '\000'
2216 match c with
2217 | '0' .. '9' ->
2218 let text = addchar text c in
2219 TEcont text
2221 | _ ->
2222 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2223 TEcont text
2226 let textentry text key =
2227 if key land 0xff00 = 0xff00
2228 then TEcont text
2229 else TEcont (text ^ Wsi.toutf8 key)
2232 let reqlayout angle proportional =
2233 match state.throttle with
2234 | None ->
2235 if nogeomcmds state.geomcmds
2236 then state.anchor <- getanchor ();
2237 conf.angle <- angle mod 360;
2238 if conf.angle != 0
2239 then (
2240 match state.mode with
2241 | LinkNav _ -> state.mode <- View
2242 | _ -> ()
2244 conf.proportional <- proportional;
2245 invalidate "reqlayout"
2246 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2247 | _ -> ()
2250 let settrim trimmargins trimfuzz =
2251 if nogeomcmds state.geomcmds
2252 then state.anchor <- getanchor ();
2253 conf.trimmargins <- trimmargins;
2254 conf.trimfuzz <- trimfuzz;
2255 let x0, y0, x1, y1 = trimfuzz in
2256 invalidate "settrim"
2257 (fun () ->
2258 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2259 Hashtbl.iter (fun _ opaque ->
2260 wcmd "freepage %s" opaque;
2261 ) state.pagemap;
2262 Hashtbl.clear state.pagemap;
2265 let setzoom zoom =
2266 match state.throttle with
2267 | None ->
2268 let zoom = max 0.01 zoom in
2269 if zoom <> conf.zoom
2270 then (
2271 state.prevzoom <- conf.zoom;
2272 let relx =
2273 if zoom <= 1.0
2274 then (state.x <- 0; 0.0)
2275 else float state.x /. float state.w
2277 conf.zoom <- zoom;
2278 reshape conf.winw conf.winh;
2279 if zoom > 1.0
2280 then (
2281 let x = relx *. float state.w in
2282 state.x <- truncate x;
2284 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2287 | Some (layout, y, started) ->
2288 let time =
2289 match conf.maxwait with
2290 | None -> 0.0
2291 | Some t -> t
2293 let dt = now () -. started in
2294 if dt > time
2295 then (
2296 state.y <- y;
2297 load layout;
2301 let setcolumns columns coverA coverB =
2302 if columns < 2
2303 then (
2304 conf.columns <- None;
2305 state.x <- 0;
2306 setzoom 1.0;
2308 else (
2309 conf.columns <- Some ((columns, coverA, coverB), [||]);
2310 conf.zoom <- 1.0;
2312 reshape conf.winw conf.winh;
2315 let enterbirdseye () =
2316 let zoom = float conf.thumbw /. float conf.winw in
2317 let birdseyepageno =
2318 let cy = conf.winh / 2 in
2319 let fold = function
2320 | [] -> 0
2321 | l :: rest ->
2322 let rec fold best = function
2323 | [] -> best.pageno
2324 | l :: rest ->
2325 let d = cy - (l.pagedispy + l.pagevh/2)
2326 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2327 if abs d < abs dbest
2328 then fold l rest
2329 else best.pageno
2330 in fold l rest
2332 fold state.layout
2334 state.mode <- Birdseye (
2335 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2337 conf.zoom <- zoom;
2338 conf.presentation <- false;
2339 conf.interpagespace <- 10;
2340 conf.hlinks <- false;
2341 state.x <- 0;
2342 state.mstate <- Mnone;
2343 conf.maxwait <- None;
2344 conf.columns <- (
2345 match conf.beyecolumns with
2346 | Some c ->
2347 conf.zoom <- 1.0;
2348 Some ((c, 0, 0), [||])
2349 | None -> None
2351 Wsi.setcursor Wsi.CURSOR_INHERIT;
2352 if conf.verbose
2353 then
2354 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2355 (100.0*.zoom)
2356 else
2357 state.text <- ""
2359 reshape conf.winw conf.winh;
2362 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2363 state.mode <- View;
2364 conf.zoom <- c.zoom;
2365 conf.presentation <- c.presentation;
2366 conf.interpagespace <- c.interpagespace;
2367 conf.maxwait <- c.maxwait;
2368 conf.hlinks <- c.hlinks;
2369 conf.beyecolumns <- (
2370 match conf.columns with
2371 | Some ((c, _, _), _) -> Some c
2372 | None -> None
2374 conf.columns <- (
2375 match c.columns with
2376 | Some (c, _) -> Some (c, [||])
2377 | None -> None
2379 state.x <- leftx;
2380 if conf.verbose
2381 then
2382 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2383 (100.0*.conf.zoom)
2385 reshape conf.winw conf.winh;
2386 state.anchor <- if goback then anchor else (pageno, 0.0);
2389 let togglebirdseye () =
2390 match state.mode with
2391 | Birdseye vals -> leavebirdseye vals true
2392 | View -> enterbirdseye ()
2393 | _ -> ()
2396 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2397 let pageno = max 0 (pageno - incr) in
2398 let rec loop = function
2399 | [] -> gotopage1 pageno 0
2400 | l :: _ when l.pageno = pageno ->
2401 if l.pagedispy >= 0 && l.pagey = 0
2402 then G.postRedisplay "upbirdseye"
2403 else gotopage1 pageno 0
2404 | _ :: rest -> loop rest
2406 loop state.layout;
2407 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2410 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2411 let pageno = min (state.pagecount - 1) (pageno + incr) in
2412 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2413 let rec loop = function
2414 | [] ->
2415 let y, h = getpageyh pageno in
2416 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2417 gotoy (clamp dy)
2418 | l :: _ when l.pageno = pageno ->
2419 if l.pagevh != l.pageh
2420 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2421 else G.postRedisplay "downbirdseye"
2422 | _ :: rest -> loop rest
2424 loop state.layout
2427 let optentry mode _ key =
2428 let btos b = if b then "on" else "off" in
2429 if key >= 32 && key < 127
2430 then
2431 let c = Char.chr key in
2432 match c with
2433 | 's' ->
2434 let ondone s =
2435 try conf.scrollstep <- int_of_string s with exc ->
2436 state.text <- Printf.sprintf "bad integer `%s': %s"
2437 s (Printexc.to_string exc)
2439 TEswitch ("scroll step: ", "", None, intentry, ondone)
2441 | 'A' ->
2442 let ondone s =
2444 conf.autoscrollstep <- int_of_string s;
2445 if state.autoscroll <> None
2446 then state.autoscroll <- Some conf.autoscrollstep
2447 with exc ->
2448 state.text <- Printf.sprintf "bad integer `%s': %s"
2449 s (Printexc.to_string exc)
2451 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2453 | 'C' ->
2454 let ondone s =
2456 let n, a, b = columns_of_string s in
2457 setcolumns n a b;
2458 with exc ->
2459 state.text <- Printf.sprintf "bad columns `%s': %s"
2460 s (Printexc.to_string exc)
2462 TEswitch ("columns: ", "", None, textentry, ondone)
2464 | 'Z' ->
2465 let ondone s =
2467 let zoom = float (int_of_string s) /. 100.0 in
2468 setzoom zoom
2469 with exc ->
2470 state.text <- Printf.sprintf "bad integer `%s': %s"
2471 s (Printexc.to_string exc)
2473 TEswitch ("zoom: ", "", None, intentry, ondone)
2475 | 't' ->
2476 let ondone s =
2478 conf.thumbw <- bound (int_of_string s) 2 4096;
2479 state.text <-
2480 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2481 begin match mode with
2482 | Birdseye beye ->
2483 leavebirdseye beye false;
2484 enterbirdseye ();
2485 | _ -> ();
2487 with exc ->
2488 state.text <- Printf.sprintf "bad integer `%s': %s"
2489 s (Printexc.to_string exc)
2491 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2493 | 'R' ->
2494 let ondone s =
2495 match try
2496 Some (int_of_string s)
2497 with exc ->
2498 state.text <- Printf.sprintf "bad integer `%s': %s"
2499 s (Printexc.to_string exc);
2500 None
2501 with
2502 | Some angle -> reqlayout angle conf.proportional
2503 | None -> ()
2505 TEswitch ("rotation: ", "", None, intentry, ondone)
2507 | 'i' ->
2508 conf.icase <- not conf.icase;
2509 TEdone ("case insensitive search " ^ (btos conf.icase))
2511 | 'p' ->
2512 conf.preload <- not conf.preload;
2513 gotoy state.y;
2514 TEdone ("preload " ^ (btos conf.preload))
2516 | 'v' ->
2517 conf.verbose <- not conf.verbose;
2518 TEdone ("verbose " ^ (btos conf.verbose))
2520 | 'd' ->
2521 conf.debug <- not conf.debug;
2522 TEdone ("debug " ^ (btos conf.debug))
2524 | 'h' ->
2525 conf.maxhfit <- not conf.maxhfit;
2526 state.maxy <-
2527 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2528 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2530 | 'c' ->
2531 conf.crophack <- not conf.crophack;
2532 TEdone ("crophack " ^ btos conf.crophack)
2534 | 'a' ->
2535 let s =
2536 match conf.maxwait with
2537 | None ->
2538 conf.maxwait <- Some infinity;
2539 "always wait for page to complete"
2540 | Some _ ->
2541 conf.maxwait <- None;
2542 "show placeholder if page is not ready"
2544 TEdone s
2546 | 'f' ->
2547 conf.underinfo <- not conf.underinfo;
2548 TEdone ("underinfo " ^ btos conf.underinfo)
2550 | 'P' ->
2551 conf.savebmarks <- not conf.savebmarks;
2552 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2554 | 'S' ->
2555 let ondone s =
2557 let pageno, py =
2558 match state.layout with
2559 | [] -> 0, 0
2560 | l :: _ ->
2561 l.pageno, l.pagey
2563 conf.interpagespace <- int_of_string s;
2564 state.maxy <- calcheight ();
2565 let y = getpagey pageno in
2566 gotoy (y + py)
2567 with exc ->
2568 state.text <- Printf.sprintf "bad integer `%s': %s"
2569 s (Printexc.to_string exc)
2571 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2573 | 'l' ->
2574 reqlayout conf.angle (not conf.proportional);
2575 TEdone ("proportional display " ^ btos conf.proportional)
2577 | 'T' ->
2578 settrim (not conf.trimmargins) conf.trimfuzz;
2579 TEdone ("trim margins " ^ btos conf.trimmargins)
2581 | 'I' ->
2582 conf.invert <- not conf.invert;
2583 TEdone ("invert colors " ^ btos conf.invert)
2585 | 'x' ->
2586 let ondone s =
2587 cbput state.hists.sel s;
2588 conf.selcmd <- s;
2590 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2591 textentry, ondone)
2593 | _ ->
2594 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2595 TEstop
2596 else
2597 TEcont state.text
2600 class type lvsource = object
2601 method getitemcount : int
2602 method getitem : int -> (string * int)
2603 method hasaction : int -> bool
2604 method exit :
2605 uioh:uioh ->
2606 cancel:bool ->
2607 active:int ->
2608 first:int ->
2609 pan:int ->
2610 qsearch:string ->
2611 uioh option
2612 method getactive : int
2613 method getfirst : int
2614 method getqsearch : string
2615 method setqsearch : string -> unit
2616 method getpan : int
2617 end;;
2619 class virtual lvsourcebase = object
2620 val mutable m_active = 0
2621 val mutable m_first = 0
2622 val mutable m_qsearch = ""
2623 val mutable m_pan = 0
2624 method getactive = m_active
2625 method getfirst = m_first
2626 method getqsearch = m_qsearch
2627 method getpan = m_pan
2628 method setqsearch s = m_qsearch <- s
2629 end;;
2631 let withoutlastutf8 s =
2632 let len = String.length s in
2633 if len = 0
2634 then s
2635 else
2636 let rec find pos =
2637 if pos = 0
2638 then pos
2639 else
2640 let b = Char.code s.[pos] in
2641 if b land 0b110000 = 0b11000000
2642 then find (pos-1)
2643 else pos-1
2645 let first =
2646 if Char.code s.[len-1] land 0x80 = 0
2647 then len-1
2648 else find (len-1)
2650 String.sub s 0 first;
2653 let textentrykeyboard key _mask ((c, text, opthist, onkey, ondone), onleave) =
2654 let enttext te =
2655 state.mode <- Textentry (te, onleave);
2656 state.text <- "";
2657 enttext ();
2658 G.postRedisplay "textentrykeyboard enttext";
2660 let histaction cmd =
2661 match opthist with
2662 | None -> ()
2663 | Some (action, _) ->
2664 state.mode <- Textentry (
2665 (c, action cmd, opthist, onkey, ondone), onleave
2667 G.postRedisplay "textentry histaction"
2669 match key with
2670 | 0xff08 -> (* backspace *)
2671 let s = withoutlastutf8 text in
2672 let len = String.length s in
2673 if len = 0
2674 then (
2675 onleave Cancel;
2676 G.postRedisplay "textentrykeyboard after cancel";
2678 else (
2679 enttext (c, s, opthist, onkey, ondone)
2682 | 0xff0d ->
2683 ondone text;
2684 onleave Confirm;
2685 G.postRedisplay "textentrykeyboard after confirm"
2687 | 0xff52 -> histaction HCprev
2688 | 0xff54 -> histaction HCnext
2689 | 0xff50 -> histaction HCfirst
2690 | 0xff57 -> histaction HClast
2692 | 0xff1b -> (* escape*)
2693 if String.length text = 0
2694 then (
2695 begin match opthist with
2696 | None -> ()
2697 | Some (_, onhistcancel) -> onhistcancel ()
2698 end;
2699 onleave Cancel;
2700 state.text <- "";
2701 G.postRedisplay "textentrykeyboard after cancel2"
2703 else (
2704 enttext (c, "", opthist, onkey, ondone)
2707 | 0xff9f | 0xffff -> () (* delete *)
2709 | _ when key != 0 && key land 0xff00 != 0xff00 ->
2710 begin match onkey text key with
2711 | TEdone text ->
2712 ondone text;
2713 onleave Confirm;
2714 G.postRedisplay "textentrykeyboard after confirm2";
2716 | TEcont text ->
2717 enttext (c, text, opthist, onkey, ondone);
2719 | TEstop ->
2720 onleave Cancel;
2721 G.postRedisplay "textentrykeyboard after cancel3"
2723 | TEswitch te ->
2724 state.mode <- Textentry (te, onleave);
2725 G.postRedisplay "textentrykeyboard switch";
2726 end;
2728 | _ ->
2729 vlog "unhandled key %s" (Wsi.keyname key)
2732 let firstof first active =
2733 if first > active || abs (first - active) > fstate.maxrows - 1
2734 then max 0 (active - (fstate.maxrows/2))
2735 else first
2738 let calcfirst first active =
2739 if active > first
2740 then
2741 let rows = active - first in
2742 if rows > fstate.maxrows then active - fstate.maxrows else first
2743 else active
2746 let scrollph y maxy =
2747 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2748 let sh = float conf.winh /. sh in
2749 let sh = max sh (float conf.scrollh) in
2751 let percent =
2752 if y = state.maxy
2753 then 1.0
2754 else float y /. float maxy
2756 let position = (float conf.winh -. sh) *. percent in
2758 let position =
2759 if position +. sh > float conf.winh
2760 then float conf.winh -. sh
2761 else position
2763 position, sh;
2766 let coe s = (s :> uioh);;
2768 class listview ~(source:lvsource) ~trusted ~modehash =
2769 object (self)
2770 val m_pan = source#getpan
2771 val m_first = source#getfirst
2772 val m_active = source#getactive
2773 val m_qsearch = source#getqsearch
2774 val m_prev_uioh = state.uioh
2776 method private elemunder y =
2777 let n = y / (fstate.fontsize+1) in
2778 if m_first + n < source#getitemcount
2779 then (
2780 if source#hasaction (m_first + n)
2781 then Some (m_first + n)
2782 else None
2784 else None
2786 method display =
2787 Gl.enable `blend;
2788 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2789 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2790 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2791 GlDraw.color (1., 1., 1.);
2792 Gl.enable `texture_2d;
2793 let fs = fstate.fontsize in
2794 let nfs = fs + 1 in
2795 let ww = fstate.wwidth in
2796 let tabw = 30.0*.ww in
2797 let itemcount = source#getitemcount in
2798 let rec loop row =
2799 if (row - m_first) * nfs > conf.winh
2800 then ()
2801 else (
2802 if row >= 0 && row < itemcount
2803 then (
2804 let (s, level) = source#getitem row in
2805 let y = (row - m_first) * nfs in
2806 let x = 5.0 +. float (level + m_pan) *. ww in
2807 if row = m_active
2808 then (
2809 Gl.disable `texture_2d;
2810 GlDraw.polygon_mode `both `line;
2811 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2812 GlDraw.rect (1., float (y + 1))
2813 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2814 GlDraw.polygon_mode `both `fill;
2815 GlDraw.color (1., 1., 1.);
2816 Gl.enable `texture_2d;
2819 let drawtabularstring s =
2820 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2821 if trusted
2822 then
2823 let tabpos = try String.index s '\t' with Not_found -> -1 in
2824 if tabpos > 0
2825 then
2826 let len = String.length s - tabpos - 1 in
2827 let s1 = String.sub s 0 tabpos
2828 and s2 = String.sub s (tabpos + 1) len in
2829 let nx = drawstr x s1 in
2830 let sw = nx -. x in
2831 let x = x +. (max tabw sw) in
2832 drawstr x s2
2833 else
2834 drawstr x s
2835 else
2836 drawstr x s
2838 let _ = drawtabularstring s in
2839 loop (row+1)
2843 loop m_first;
2844 Gl.disable `blend;
2845 Gl.disable `texture_2d;
2847 method updownlevel incr =
2848 let len = source#getitemcount in
2849 let curlevel =
2850 if m_active >= 0 && m_active < len
2851 then snd (source#getitem m_active)
2852 else -1
2854 let rec flow i =
2855 if i = len then i-1 else if i = -1 then 0 else
2856 let _, l = source#getitem i in
2857 if l != curlevel then i else flow (i+incr)
2859 let active = flow m_active in
2860 let first = calcfirst m_first active in
2861 G.postRedisplay "outline updownlevel";
2862 {< m_active = active; m_first = first >}
2864 method private key1 key mask =
2865 let set1 active first qsearch =
2866 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2868 let search active pattern incr =
2869 let dosearch re =
2870 let rec loop n =
2871 if n >= 0 && n < source#getitemcount
2872 then (
2873 let s, _ = source#getitem n in
2875 (try ignore (Str.search_forward re s 0); true
2876 with Not_found -> false)
2877 then Some n
2878 else loop (n + incr)
2880 else None
2882 loop active
2885 let re = Str.regexp_case_fold pattern in
2886 dosearch re
2887 with Failure s ->
2888 state.text <- s;
2889 None
2891 let itemcount = source#getitemcount in
2892 let find start incr =
2893 let rec find i =
2894 if i = -1 || i = itemcount
2895 then -1
2896 else (
2897 if source#hasaction i
2898 then i
2899 else find (i + incr)
2902 find start
2904 let set active first =
2905 let first = bound first 0 (itemcount - fstate.maxrows) in
2906 state.text <- "";
2907 coe {< m_active = active; m_first = first >}
2909 let navigate incr =
2910 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2911 let active, first =
2912 let incr1 = if incr > 0 then 1 else -1 in
2913 if isvisible m_first m_active
2914 then
2915 let next =
2916 let next = m_active + incr in
2917 let next =
2918 if next < 0 || next >= itemcount
2919 then -1
2920 else find next incr1
2922 if next = -1 || abs (m_active - next) > fstate.maxrows
2923 then -1
2924 else next
2926 if next = -1
2927 then
2928 let first = m_first + incr in
2929 let first = bound first 0 (itemcount - 1) in
2930 let next =
2931 let next = m_active + incr in
2932 let next = bound next 0 (itemcount - 1) in
2933 find next ~-incr1
2935 let active = if next = -1 then m_active else next in
2936 active, first
2937 else
2938 let first = min next m_first in
2939 let first =
2940 if abs (next - first) > fstate.maxrows
2941 then first + incr
2942 else first
2944 next, first
2945 else
2946 let first = m_first + incr in
2947 let first = bound first 0 (itemcount - 1) in
2948 let active =
2949 let next = m_active + incr in
2950 let next = bound next 0 (itemcount - 1) in
2951 let next = find next incr1 in
2952 let active =
2953 if next = -1 || abs (m_active - first) > fstate.maxrows
2954 then (
2955 let active = if m_active = -1 then next else m_active in
2956 active
2958 else next
2960 if isvisible first active
2961 then active
2962 else -1
2964 active, first
2966 G.postRedisplay "listview navigate";
2967 set active first;
2969 match key with
2970 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
2971 let incr = if key = 0x72 then -1 else 1 in
2972 let active, first =
2973 match search (m_active + incr) m_qsearch incr with
2974 | None ->
2975 state.text <- m_qsearch ^ " [not found]";
2976 m_active, m_first
2977 | Some active ->
2978 state.text <- m_qsearch;
2979 active, firstof m_first active
2981 G.postRedisplay "listview ctrl-r/s";
2982 set1 active first m_qsearch;
2984 | 0xff08 -> (* backspace *)
2985 if String.length m_qsearch = 0
2986 then coe self
2987 else (
2988 let qsearch = withoutlastutf8 m_qsearch in
2989 let len = String.length qsearch in
2990 if len = 0
2991 then (
2992 state.text <- "";
2993 G.postRedisplay "listview empty qsearch";
2994 set1 m_active m_first "";
2996 else
2997 let active, first =
2998 match search m_active qsearch ~-1 with
2999 | None ->
3000 state.text <- qsearch ^ " [not found]";
3001 m_active, m_first
3002 | Some active ->
3003 state.text <- qsearch;
3004 active, firstof m_first active
3006 G.postRedisplay "listview backspace qsearch";
3007 set1 active first qsearch
3010 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3011 let pattern = m_qsearch ^ Wsi.toutf8 key in
3012 let active, first =
3013 match search m_active pattern 1 with
3014 | None ->
3015 state.text <- pattern ^ " [not found]";
3016 m_active, m_first
3017 | Some active ->
3018 state.text <- pattern;
3019 active, firstof m_first active
3021 G.postRedisplay "listview qsearch add";
3022 set1 active first pattern;
3024 | 0xff1b -> (* escape *)
3025 state.text <- "";
3026 if String.length m_qsearch = 0
3027 then (
3028 G.postRedisplay "list view escape";
3029 begin
3030 match
3031 source#exit (coe self) true m_active m_first m_pan m_qsearch
3032 with
3033 | None -> m_prev_uioh
3034 | Some uioh -> uioh
3037 else (
3038 G.postRedisplay "list view kill qsearch";
3039 source#setqsearch "";
3040 coe {< m_qsearch = "" >}
3043 | 0xff0d -> (* return *)
3044 state.text <- "";
3045 let self = {< m_qsearch = "" >} in
3046 source#setqsearch "";
3047 let opt =
3048 G.postRedisplay "listview enter";
3049 if m_active >= 0 && m_active < source#getitemcount
3050 then (
3051 source#exit (coe self) false m_active m_first m_pan "";
3053 else (
3054 source#exit (coe self) true m_active m_first m_pan "";
3057 begin match opt with
3058 | None -> m_prev_uioh
3059 | Some uioh -> uioh
3062 | 0xff9f | 0xffff -> (* delete *)
3063 coe self
3065 | 0xff52 -> navigate ~-1 (* up *)
3066 | 0xff54 -> navigate 1 (* down *)
3067 | 0xff55 -> navigate ~-(fstate.maxrows) (* prior *)
3068 | 0xff56 -> navigate fstate.maxrows (* next *)
3070 | 0xff53 -> (* right *)
3071 state.text <- "";
3072 G.postRedisplay "listview right";
3073 coe {< m_pan = m_pan - 1 >}
3075 | 0xff51 -> (* left *)
3076 state.text <- "";
3077 G.postRedisplay "listview left";
3078 coe {< m_pan = m_pan + 1 >}
3080 | 0xff50 -> (* home *)
3081 let active = find 0 1 in
3082 G.postRedisplay "listview home";
3083 set active 0;
3085 | 0xff57 -> (* end *)
3086 let first = max 0 (itemcount - fstate.maxrows) in
3087 let active = find (itemcount - 1) ~-1 in
3088 G.postRedisplay "listview end";
3089 set active first;
3091 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3092 coe self
3094 | _ ->
3095 dolog "listview unknown key %#x" key; coe self
3097 method key key mask =
3098 match state.mode with
3099 | Textentry te -> textentrykeyboard key mask te; coe self
3100 | _ -> self#key1 key mask
3102 method button button down x y _ =
3103 let opt =
3104 match button with
3105 | 1 when x > conf.winw - conf.scrollbw ->
3106 G.postRedisplay "listview scroll";
3107 if down
3108 then
3109 let _, position, sh = self#scrollph in
3110 if y > truncate position && y < truncate (position +. sh)
3111 then (
3112 state.mstate <- Mscrolly;
3113 Some (coe self)
3115 else
3116 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3117 let first = truncate (s *. float source#getitemcount) in
3118 let first = min source#getitemcount first in
3119 Some (coe {< m_first = first; m_active = first >})
3120 else (
3121 state.mstate <- Mnone;
3122 Some (coe self);
3124 | 1 when not down ->
3125 begin match self#elemunder y with
3126 | Some n ->
3127 G.postRedisplay "listview click";
3128 source#exit
3129 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3130 | _ ->
3131 Some (coe self)
3133 | n when (n == 4 || n == 5) && not down ->
3134 let len = source#getitemcount in
3135 let first =
3136 if n = 5 && m_first + fstate.maxrows >= len
3137 then
3138 m_first
3139 else
3140 let first = m_first + (if n == 4 then -1 else 1) in
3141 bound first 0 (len - 1)
3143 G.postRedisplay "listview wheel";
3144 Some (coe {< m_first = first >})
3145 | _ ->
3146 Some (coe self)
3148 match opt with
3149 | None -> m_prev_uioh
3150 | Some uioh -> uioh
3152 method motion _ y =
3153 match state.mstate with
3154 | Mscrolly ->
3155 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3156 let first = truncate (s *. float source#getitemcount) in
3157 let first = min source#getitemcount first in
3158 G.postRedisplay "listview motion";
3159 coe {< m_first = first; m_active = first >}
3160 | _ -> coe self
3162 method pmotion x y =
3163 if x < conf.winw - conf.scrollbw
3164 then
3165 let n =
3166 match self#elemunder y with
3167 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3168 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3170 let o =
3171 if n != m_active
3172 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3173 else self
3175 coe o
3176 else (
3177 Wsi.setcursor Wsi.CURSOR_INHERIT;
3178 coe self
3181 method infochanged _ = ()
3183 method scrollpw = (0, 0.0, 0.0)
3184 method scrollph =
3185 let nfs = fstate.fontsize + 1 in
3186 let y = m_first * nfs in
3187 let itemcount = source#getitemcount in
3188 let maxi = max 0 (itemcount - fstate.maxrows) in
3189 let maxy = maxi * nfs in
3190 let p, h = scrollph y maxy in
3191 conf.scrollbw, p, h
3193 method modehash = modehash
3194 end;;
3196 class outlinelistview ~source =
3197 object (self)
3198 inherit listview
3199 ~source:(source :> lvsource)
3200 ~trusted:false
3201 ~modehash:(findkeyhash conf "outline")
3202 as super
3204 method key key mask =
3205 let calcfirst first active =
3206 if active > first
3207 then
3208 let rows = active - first in
3209 if rows > fstate.maxrows then active - fstate.maxrows else first
3210 else active
3212 let navigate incr =
3213 let active = m_active + incr in
3214 let active = bound active 0 (source#getitemcount - 1) in
3215 let first = calcfirst m_first active in
3216 G.postRedisplay "outline navigate";
3217 coe {< m_active = active; m_first = first >}
3219 let ctrl = Wsi.withctrl mask in
3220 match key with
3221 | 110 when ctrl -> (* ctrl-n *)
3222 source#narrow m_qsearch;
3223 G.postRedisplay "outline ctrl-n";
3224 coe {< m_first = 0; m_active = 0 >}
3226 | 117 when ctrl -> (* ctrl-u *)
3227 source#denarrow;
3228 G.postRedisplay "outline ctrl-u";
3229 state.text <- "";
3230 coe {< m_first = 0; m_active = 0 >}
3232 | 108 when ctrl -> (* ctrl-l *)
3233 let first = m_active - (fstate.maxrows / 2) in
3234 G.postRedisplay "outline ctrl-l";
3235 coe {< m_first = first >}
3237 | 0xff9f | 0xffff -> (* delete *)
3238 source#remove m_active;
3239 G.postRedisplay "outline delete";
3240 let active = max 0 (m_active-1) in
3241 coe {< m_first = firstof m_first active;
3242 m_active = active >}
3244 | 0xff52 -> navigate ~-1 (* up *)
3245 | 0xff54 -> navigate 1 (* down *)
3246 | 0xff55 -> (* prior *)
3247 navigate ~-(fstate.maxrows)
3248 | 0xff56 -> (* next *)
3249 navigate fstate.maxrows
3251 | 0xff53 -> (* [ctrl-]right *)
3252 let o =
3253 if ctrl
3254 then (
3255 G.postRedisplay "outline ctrl right";
3256 {< m_pan = m_pan + 1 >}
3258 else self#updownlevel 1
3260 coe o
3262 | 0xff51 -> (* [ctrl-]left *)
3263 let o =
3264 if ctrl
3265 then (
3266 G.postRedisplay "outline ctrl left";
3267 {< m_pan = m_pan - 1 >}
3269 else self#updownlevel ~-1
3271 coe o
3273 | 0xff50 -> (* home *)
3274 G.postRedisplay "outline home";
3275 coe {< m_first = 0; m_active = 0 >}
3277 | 0xff57 -> (* end *)
3278 let active = source#getitemcount - 1 in
3279 let first = max 0 (active - fstate.maxrows) in
3280 G.postRedisplay "outline end";
3281 coe {< m_active = active; m_first = first >}
3283 | _ -> super#key key mask
3286 let outlinesource usebookmarks =
3287 let empty = [||] in
3288 (object
3289 inherit lvsourcebase
3290 val mutable m_items = empty
3291 val mutable m_orig_items = empty
3292 val mutable m_prev_items = empty
3293 val mutable m_narrow_pattern = ""
3294 val mutable m_hadremovals = false
3296 method getitemcount =
3297 Array.length m_items + (if m_hadremovals then 1 else 0)
3299 method getitem n =
3300 if n == Array.length m_items && m_hadremovals
3301 then
3302 ("[Confirm removal]", 0)
3303 else
3304 let s, n, _ = m_items.(n) in
3305 (s, n)
3307 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3308 ignore (uioh, first, qsearch);
3309 let confrimremoval = m_hadremovals && active = Array.length m_items in
3310 let items =
3311 if String.length m_narrow_pattern = 0
3312 then m_orig_items
3313 else m_items
3315 if not cancel
3316 then (
3317 if not confrimremoval
3318 then(
3319 let _, _, anchor = m_items.(active) in
3320 gotoanchor anchor;
3321 m_items <- items;
3323 else (
3324 state.bookmarks <- Array.to_list m_items;
3325 m_orig_items <- m_items;
3328 else m_items <- items;
3329 m_pan <- pan;
3330 None
3332 method hasaction _ = true
3334 method greetmsg =
3335 if Array.length m_items != Array.length m_orig_items
3336 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3337 else ""
3339 method narrow pattern =
3340 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3341 match reopt with
3342 | None -> ()
3343 | Some re ->
3344 let rec loop accu n =
3345 if n = -1
3346 then (
3347 m_narrow_pattern <- pattern;
3348 m_items <- Array.of_list accu
3350 else
3351 let (s, _, _) as o = m_items.(n) in
3352 let accu =
3353 if (try ignore (Str.search_forward re s 0); true
3354 with Not_found -> false)
3355 then o :: accu
3356 else accu
3358 loop accu (n-1)
3360 loop [] (Array.length m_items - 1)
3362 method denarrow =
3363 m_orig_items <- (
3364 if usebookmarks
3365 then Array.of_list state.bookmarks
3366 else state.outlines
3368 m_items <- m_orig_items
3370 method remove m =
3371 if usebookmarks
3372 then
3373 if m >= 0 && m < Array.length m_items
3374 then (
3375 m_hadremovals <- true;
3376 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3377 let n = if n >= m then n+1 else n in
3378 m_items.(n)
3382 method reset anchor items =
3383 m_hadremovals <- false;
3384 if m_orig_items == empty || m_prev_items != items
3385 then (
3386 m_orig_items <- items;
3387 if String.length m_narrow_pattern = 0
3388 then m_items <- items;
3390 m_prev_items <- items;
3391 let rely = getanchory anchor in
3392 let active =
3393 let rec loop n best bestd =
3394 if n = Array.length m_items
3395 then best
3396 else
3397 let (_, _, anchor) = m_items.(n) in
3398 let orely = getanchory anchor in
3399 let d = abs (orely - rely) in
3400 if d < bestd
3401 then loop (n+1) n d
3402 else loop (n+1) best bestd
3404 loop 0 ~-1 max_int
3406 m_active <- active;
3407 m_first <- firstof m_first active
3408 end)
3411 let enterselector usebookmarks =
3412 let source = outlinesource usebookmarks in
3413 fun errmsg ->
3414 let outlines =
3415 if usebookmarks
3416 then Array.of_list state.bookmarks
3417 else state.outlines
3419 if Array.length outlines = 0
3420 then (
3421 showtext ' ' errmsg;
3423 else (
3424 state.text <- source#greetmsg;
3425 Wsi.setcursor Wsi.CURSOR_INHERIT;
3426 let anchor = getanchor () in
3427 source#reset anchor outlines;
3428 state.uioh <- coe (new outlinelistview ~source);
3429 G.postRedisplay "enter selector";
3433 let enteroutlinemode =
3434 let f = enterselector false in
3435 fun ()-> f "Document has no outline";
3438 let enterbookmarkmode =
3439 let f = enterselector true in
3440 fun () -> f "Document has no bookmarks (yet)";
3443 let color_of_string s =
3444 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3445 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3449 let color_to_string (r, g, b) =
3450 let r = truncate (r *. 256.0)
3451 and g = truncate (g *. 256.0)
3452 and b = truncate (b *. 256.0) in
3453 Printf.sprintf "%d/%d/%d" r g b
3456 let irect_of_string s =
3457 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3460 let irect_to_string (x0,y0,x1,y1) =
3461 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3464 let makecheckers () =
3465 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3466 following to say:
3467 converted by Issac Trotts. July 25, 2002 *)
3468 let image_height = 64
3469 and image_width = 64 in
3471 let make_image () =
3472 let image =
3473 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3475 for i = 0 to image_width - 1 do
3476 for j = 0 to image_height - 1 do
3477 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3478 (if (i land 8 ) lxor (j land 8) = 0
3479 then [|255;255;255|] else [|200;200;200|])
3480 done
3481 done;
3482 image
3484 let image = make_image () in
3485 let id = GlTex.gen_texture () in
3486 GlTex.bind_texture `texture_2d id;
3487 GlPix.store (`unpack_alignment 1);
3488 GlTex.image2d image;
3489 List.iter (GlTex.parameter ~target:`texture_2d)
3490 [ `wrap_s `repeat;
3491 `wrap_t `repeat;
3492 `mag_filter `nearest;
3493 `min_filter `nearest ];
3497 let setcheckers enabled =
3498 match state.texid with
3499 | None ->
3500 if enabled then state.texid <- Some (makecheckers ())
3502 | Some texid ->
3503 if not enabled
3504 then (
3505 GlTex.delete_texture texid;
3506 state.texid <- None;
3510 let int_of_string_with_suffix s =
3511 let l = String.length s in
3512 let s1, shift =
3513 if l > 1
3514 then
3515 let suffix = Char.lowercase s.[l-1] in
3516 match suffix with
3517 | 'k' -> String.sub s 0 (l-1), 10
3518 | 'm' -> String.sub s 0 (l-1), 20
3519 | 'g' -> String.sub s 0 (l-1), 30
3520 | _ -> s, 0
3521 else s, 0
3523 let n = int_of_string s1 in
3524 let m = n lsl shift in
3525 if m < 0 || m < n
3526 then raise (Failure "value too large")
3527 else m
3530 let string_with_suffix_of_int n =
3531 if n = 0
3532 then "0"
3533 else
3534 let n, s =
3535 if n = 0
3536 then 0, ""
3537 else (
3538 if n land ((1 lsl 20) - 1) = 0
3539 then n lsr 20, "M"
3540 else (
3541 if n land ((1 lsl 10) - 1) = 0
3542 then n lsr 10, "K"
3543 else n, ""
3547 let rec loop s n =
3548 let h = n mod 1000 in
3549 let n = n / 1000 in
3550 if n = 0
3551 then string_of_int h ^ s
3552 else (
3553 let s = Printf.sprintf "_%03d%s" h s in
3554 loop s n
3557 loop "" n ^ s;
3560 let defghyllscroll = (40, 8, 32);;
3561 let ghyllscroll_of_string s =
3562 let (n, a, b) as nab =
3563 if s = "default"
3564 then defghyllscroll
3565 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3567 if n <= a || n <= b || a >= b
3568 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3569 nab;
3572 let ghyllscroll_to_string ((n, a, b) as nab) =
3573 if nab = defghyllscroll
3574 then "default"
3575 else Printf.sprintf "%d,%d,%d" n a b;
3578 let describe_location () =
3579 let f (fn, _) l =
3580 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3582 let fn, ln = List.fold_left f (-1, -1) state.layout in
3583 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3584 let percent =
3585 if maxy <= 0
3586 then 100.
3587 else (100. *. (float state.y /. float maxy))
3589 if fn = ln
3590 then
3591 Printf.sprintf "page %d of %d [%.2f%%]"
3592 (fn+1) state.pagecount percent
3593 else
3594 Printf.sprintf
3595 "pages %d-%d of %d [%.2f%%]"
3596 (fn+1) (ln+1) state.pagecount percent
3599 let enterinfomode =
3600 let btos b = if b then "\xe2\x88\x9a" else "" in
3601 let showextended = ref false in
3602 let leave mode = function
3603 | Confirm -> state.mode <- mode
3604 | Cancel -> state.mode <- mode in
3605 let src =
3606 (object
3607 val mutable m_first_time = true
3608 val mutable m_l = []
3609 val mutable m_a = [||]
3610 val mutable m_prev_uioh = nouioh
3611 val mutable m_prev_mode = View
3613 inherit lvsourcebase
3615 method reset prev_mode prev_uioh =
3616 m_a <- Array.of_list (List.rev m_l);
3617 m_l <- [];
3618 m_prev_mode <- prev_mode;
3619 m_prev_uioh <- prev_uioh;
3620 if m_first_time
3621 then (
3622 let rec loop n =
3623 if n >= Array.length m_a
3624 then ()
3625 else
3626 match m_a.(n) with
3627 | _, _, _, Action _ -> m_active <- n
3628 | _ -> loop (n+1)
3630 loop 0;
3631 m_first_time <- false;
3634 method int name get set =
3635 m_l <-
3636 (name, `int get, 1, Action (
3637 fun u ->
3638 let ondone s =
3639 try set (int_of_string s)
3640 with exn ->
3641 state.text <- Printf.sprintf "bad integer `%s': %s"
3642 s (Printexc.to_string exn)
3644 state.text <- "";
3645 let te = name ^ ": ", "", None, intentry, ondone in
3646 state.mode <- Textentry (te, leave m_prev_mode);
3648 )) :: m_l
3650 method int_with_suffix name get set =
3651 m_l <-
3652 (name, `intws get, 1, Action (
3653 fun u ->
3654 let ondone s =
3655 try set (int_of_string_with_suffix s)
3656 with exn ->
3657 state.text <- Printf.sprintf "bad integer `%s': %s"
3658 s (Printexc.to_string exn)
3660 state.text <- "";
3661 let te =
3662 name ^ ": ", "", None, intentry_with_suffix, ondone
3664 state.mode <- Textentry (te, leave m_prev_mode);
3666 )) :: m_l
3668 method bool ?(offset=1) ?(btos=btos) name get set =
3669 m_l <-
3670 (name, `bool (btos, get), offset, Action (
3671 fun u ->
3672 let v = get () in
3673 set (not v);
3675 )) :: m_l
3677 method color name get set =
3678 m_l <-
3679 (name, `color get, 1, Action (
3680 fun u ->
3681 let invalid = (nan, nan, nan) in
3682 let ondone s =
3683 let c =
3684 try color_of_string s
3685 with exn ->
3686 state.text <- Printf.sprintf "bad color `%s': %s"
3687 s (Printexc.to_string exn);
3688 invalid
3690 if c <> invalid
3691 then set c;
3693 let te = name ^ ": ", "", None, textentry, ondone in
3694 state.text <- color_to_string (get ());
3695 state.mode <- Textentry (te, leave m_prev_mode);
3697 )) :: m_l
3699 method string name get set =
3700 m_l <-
3701 (name, `string get, 1, Action (
3702 fun u ->
3703 let ondone s = set s in
3704 let te = name ^ ": ", "", None, textentry, ondone in
3705 state.mode <- Textentry (te, leave m_prev_mode);
3707 )) :: m_l
3709 method colorspace name get set =
3710 m_l <-
3711 (name, `string get, 1, Action (
3712 fun _ ->
3713 let source =
3714 let vals = [| "rgb"; "bgr"; "gray" |] in
3715 (object
3716 inherit lvsourcebase
3718 initializer
3719 m_active <- int_of_colorspace conf.colorspace;
3720 m_first <- 0;
3722 method getitemcount = Array.length vals
3723 method getitem n = (vals.(n), 0)
3724 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3725 ignore (uioh, first, pan, qsearch);
3726 if not cancel then set active;
3727 None
3728 method hasaction _ = true
3729 end)
3731 state.text <- "";
3732 let modehash = findkeyhash conf "info" in
3733 coe (new listview ~source ~trusted:true ~modehash)
3734 )) :: m_l
3736 method caption s offset =
3737 m_l <- (s, `empty, offset, Noaction) :: m_l
3739 method caption2 s f offset =
3740 m_l <- (s, `string f, offset, Noaction) :: m_l
3742 method getitemcount = Array.length m_a
3744 method getitem n =
3745 let tostr = function
3746 | `int f -> string_of_int (f ())
3747 | `intws f -> string_with_suffix_of_int (f ())
3748 | `string f -> f ()
3749 | `color f -> color_to_string (f ())
3750 | `bool (btos, f) -> btos (f ())
3751 | `empty -> ""
3753 let name, t, offset, _ = m_a.(n) in
3754 ((let s = tostr t in
3755 if String.length s > 0
3756 then Printf.sprintf "%s\t%s" name s
3757 else name),
3758 offset)
3760 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3761 let uiohopt =
3762 if not cancel
3763 then (
3764 m_qsearch <- qsearch;
3765 let uioh =
3766 match m_a.(active) with
3767 | _, _, _, Action f -> f uioh
3768 | _ -> uioh
3770 Some uioh
3772 else None
3774 m_active <- active;
3775 m_first <- first;
3776 m_pan <- pan;
3777 uiohopt
3779 method hasaction n =
3780 match m_a.(n) with
3781 | _, _, _, Action _ -> true
3782 | _ -> false
3783 end)
3785 let rec fillsrc prevmode prevuioh =
3786 let sep () = src#caption "" 0 in
3787 let colorp name get set =
3788 src#string name
3789 (fun () -> color_to_string (get ()))
3790 (fun v ->
3792 let c = color_of_string v in
3793 set c
3794 with exn ->
3795 state.text <- Printf.sprintf "bad color `%s': %s"
3796 v (Printexc.to_string exn);
3799 let oldmode = state.mode in
3800 let birdseye = isbirdseye state.mode in
3802 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3804 src#bool "presentation mode"
3805 (fun () -> conf.presentation)
3806 (fun v ->
3807 conf.presentation <- v;
3808 state.anchor <- getanchor ();
3809 represent ());
3811 src#bool "ignore case in searches"
3812 (fun () -> conf.icase)
3813 (fun v -> conf.icase <- v);
3815 src#bool "preload"
3816 (fun () -> conf.preload)
3817 (fun v -> conf.preload <- v);
3819 src#bool "highlight links"
3820 (fun () -> conf.hlinks)
3821 (fun v -> conf.hlinks <- v);
3823 src#bool "under info"
3824 (fun () -> conf.underinfo)
3825 (fun v -> conf.underinfo <- v);
3827 src#bool "persistent bookmarks"
3828 (fun () -> conf.savebmarks)
3829 (fun v -> conf.savebmarks <- v);
3831 src#bool "proportional display"
3832 (fun () -> conf.proportional)
3833 (fun v -> reqlayout conf.angle v);
3835 src#bool "trim margins"
3836 (fun () -> conf.trimmargins)
3837 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3839 src#bool "persistent location"
3840 (fun () -> conf.jumpback)
3841 (fun v -> conf.jumpback <- v);
3843 sep ();
3844 src#int "inter-page space"
3845 (fun () -> conf.interpagespace)
3846 (fun n ->
3847 conf.interpagespace <- n;
3848 let pageno, py =
3849 match state.layout with
3850 | [] -> 0, 0
3851 | l :: _ ->
3852 l.pageno, l.pagey
3854 state.maxy <- calcheight ();
3855 let y = getpagey pageno in
3856 gotoy (y + py)
3859 src#int "page bias"
3860 (fun () -> conf.pagebias)
3861 (fun v -> conf.pagebias <- v);
3863 src#int "scroll step"
3864 (fun () -> conf.scrollstep)
3865 (fun n -> conf.scrollstep <- n);
3867 src#int "auto scroll step"
3868 (fun () ->
3869 match state.autoscroll with
3870 | Some step -> step
3871 | _ -> conf.autoscrollstep)
3872 (fun n ->
3873 if state.autoscroll <> None
3874 then state.autoscroll <- Some n;
3875 conf.autoscrollstep <- n);
3877 src#int "zoom"
3878 (fun () -> truncate (conf.zoom *. 100.))
3879 (fun v -> setzoom ((float v) /. 100.));
3881 src#int "rotation"
3882 (fun () -> conf.angle)
3883 (fun v -> reqlayout v conf.proportional);
3885 src#int "scroll bar width"
3886 (fun () -> state.scrollw)
3887 (fun v ->
3888 state.scrollw <- v;
3889 conf.scrollbw <- v;
3890 reshape conf.winw conf.winh;
3893 src#int "scroll handle height"
3894 (fun () -> conf.scrollh)
3895 (fun v -> conf.scrollh <- v;);
3897 src#int "thumbnail width"
3898 (fun () -> conf.thumbw)
3899 (fun v ->
3900 conf.thumbw <- min 4096 v;
3901 match oldmode with
3902 | Birdseye beye ->
3903 leavebirdseye beye false;
3904 enterbirdseye ()
3905 | _ -> ()
3908 src#string "columns"
3909 (fun () ->
3910 match conf.columns with
3911 | None -> "1"
3912 | Some (multicol, _) -> columns_to_string multicol)
3913 (fun v ->
3914 let n, a, b = columns_of_string v in
3915 setcolumns n a b);
3917 sep ();
3918 src#caption "Presentation mode" 0;
3919 src#bool "scrollbar visible"
3920 (fun () -> conf.scrollbarinpm)
3921 (fun v ->
3922 if v != conf.scrollbarinpm
3923 then (
3924 conf.scrollbarinpm <- v;
3925 if conf.presentation
3926 then (
3927 state.scrollw <- if v then conf.scrollbw else 0;
3928 reshape conf.winw conf.winh;
3933 sep ();
3934 src#caption "Pixmap cache" 0;
3935 src#int_with_suffix "size (advisory)"
3936 (fun () -> conf.memlimit)
3937 (fun v -> conf.memlimit <- v);
3939 src#caption2 "used"
3940 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3941 (string_with_suffix_of_int state.memused)
3942 (Hashtbl.length state.tilemap)) 1;
3944 sep ();
3945 src#caption "Layout" 0;
3946 src#caption2 "Dimension"
3947 (fun () ->
3948 Printf.sprintf "%dx%d (virtual %dx%d)"
3949 conf.winw conf.winh
3950 state.w state.maxy)
3952 if conf.debug
3953 then
3954 src#caption2 "Position" (fun () ->
3955 Printf.sprintf "%dx%d" state.x state.y
3957 else
3958 src#caption2 "Visible" (fun () -> describe_location ()) 1
3961 sep ();
3962 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3963 "Save these parameters as global defaults at exit"
3964 (fun () -> conf.bedefault)
3965 (fun v -> conf.bedefault <- v)
3968 sep ();
3969 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3970 src#bool ~offset:0 ~btos "Extended parameters"
3971 (fun () -> !showextended)
3972 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3973 if !showextended
3974 then (
3975 src#bool "checkers"
3976 (fun () -> conf.checkers)
3977 (fun v -> conf.checkers <- v; setcheckers v);
3978 src#bool "update cursor"
3979 (fun () -> conf.updatecurs)
3980 (fun v -> conf.updatecurs <- v);
3981 src#bool "verbose"
3982 (fun () -> conf.verbose)
3983 (fun v -> conf.verbose <- v);
3984 src#bool "invert colors"
3985 (fun () -> conf.invert)
3986 (fun v -> conf.invert <- v);
3987 src#bool "max fit"
3988 (fun () -> conf.maxhfit)
3989 (fun v -> conf.maxhfit <- v);
3990 src#bool "redirect stderr"
3991 (fun () -> conf.redirectstderr)
3992 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3993 src#string "uri launcher"
3994 (fun () -> conf.urilauncher)
3995 (fun v -> conf.urilauncher <- v);
3996 src#string "path launcher"
3997 (fun () -> conf.pathlauncher)
3998 (fun v -> conf.pathlauncher <- v);
3999 src#string "tile size"
4000 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4001 (fun v ->
4003 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4004 conf.tileh <- max 64 w;
4005 conf.tilew <- max 64 h;
4006 flushtiles ();
4007 with exn ->
4008 state.text <- Printf.sprintf "bad tile size `%s': %s"
4009 v (Printexc.to_string exn));
4010 src#int "texture count"
4011 (fun () -> conf.texcount)
4012 (fun v ->
4013 if realloctexts v
4014 then conf.texcount <- v
4015 else showtext '!' " Failed to set texture count please retry later"
4017 src#int "slice height"
4018 (fun () -> conf.sliceheight)
4019 (fun v ->
4020 conf.sliceheight <- v;
4021 wcmd "sliceh %d" conf.sliceheight;
4023 src#int "anti-aliasing level"
4024 (fun () -> conf.aalevel)
4025 (fun v ->
4026 conf.aalevel <- bound v 0 8;
4027 state.anchor <- getanchor ();
4028 opendoc state.path state.password;
4030 src#int "ui font size"
4031 (fun () -> fstate.fontsize)
4032 (fun v -> setfontsize (bound v 5 100));
4033 colorp "background color"
4034 (fun () -> conf.bgcolor)
4035 (fun v -> conf.bgcolor <- v);
4036 src#bool "crop hack"
4037 (fun () -> conf.crophack)
4038 (fun v -> conf.crophack <- v);
4039 src#string "trim fuzz"
4040 (fun () -> irect_to_string conf.trimfuzz)
4041 (fun v ->
4043 conf.trimfuzz <- irect_of_string v;
4044 if conf.trimmargins
4045 then settrim true conf.trimfuzz;
4046 with exn ->
4047 state.text <- Printf.sprintf "bad irect `%s': %s"
4048 v (Printexc.to_string exn)
4050 src#string "throttle"
4051 (fun () ->
4052 match conf.maxwait with
4053 | None -> "show place holder if page is not ready"
4054 | Some time ->
4055 if time = infinity
4056 then "wait for page to fully render"
4057 else
4058 "wait " ^ string_of_float time
4059 ^ " seconds before showing placeholder"
4061 (fun v ->
4063 let f = float_of_string v in
4064 if f <= 0.0
4065 then conf.maxwait <- None
4066 else conf.maxwait <- Some f
4067 with exn ->
4068 state.text <- Printf.sprintf "bad time `%s': %s"
4069 v (Printexc.to_string exn)
4071 src#string "ghyll scroll"
4072 (fun () ->
4073 match conf.ghyllscroll with
4074 | None -> ""
4075 | Some nab -> ghyllscroll_to_string nab
4077 (fun v ->
4079 let gs =
4080 if String.length v = 0
4081 then None
4082 else Some (ghyllscroll_of_string v)
4084 conf.ghyllscroll <- gs
4085 with exn ->
4086 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4087 v (Printexc.to_string exn)
4089 src#string "selection command"
4090 (fun () -> conf.selcmd)
4091 (fun v -> conf.selcmd <- v);
4092 src#colorspace "color space"
4093 (fun () -> colorspace_to_string conf.colorspace)
4094 (fun v ->
4095 conf.colorspace <- colorspace_of_int v;
4096 wcmd "cs %d" v;
4097 load state.layout;
4101 sep ();
4102 src#caption "Document" 0;
4103 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4104 src#caption2 "Pages"
4105 (fun () -> string_of_int state.pagecount) 1;
4106 src#caption2 "Dimensions"
4107 (fun () -> string_of_int (List.length state.pdims)) 1;
4108 if conf.trimmargins
4109 then (
4110 sep ();
4111 src#caption "Trimmed margins" 0;
4112 src#caption2 "Dimensions"
4113 (fun () -> string_of_int (List.length state.pdims)) 1;
4116 src#reset prevmode prevuioh;
4118 fun () ->
4119 state.text <- "";
4120 let prevmode = state.mode
4121 and prevuioh = state.uioh in
4122 fillsrc prevmode prevuioh;
4123 let source = (src :> lvsource) in
4124 let modehash = findkeyhash conf "info" in
4125 state.uioh <- coe (object (self)
4126 inherit listview ~source ~trusted:true ~modehash as super
4127 val mutable m_prevmemused = 0
4128 method infochanged = function
4129 | Memused ->
4130 if m_prevmemused != state.memused
4131 then (
4132 m_prevmemused <- state.memused;
4133 G.postRedisplay "memusedchanged";
4135 | Pdim -> G.postRedisplay "pdimchanged"
4136 | Docinfo -> fillsrc prevmode prevuioh
4138 method key key mask =
4139 if not (Wsi.withctrl mask)
4140 then
4141 match key with
4142 | 0xff51 -> coe (self#updownlevel ~-1)
4143 | 0xff53 -> coe (self#updownlevel 1)
4144 | _ -> super#key key mask
4145 else super#key key mask
4146 end);
4147 G.postRedisplay "info";
4150 let enterhelpmode =
4151 let source =
4152 (object
4153 inherit lvsourcebase
4154 method getitemcount = Array.length state.help
4155 method getitem n =
4156 let s, n, _ = state.help.(n) in
4157 (s, n)
4159 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4160 let optuioh =
4161 if not cancel
4162 then (
4163 m_qsearch <- qsearch;
4164 match state.help.(active) with
4165 | _, _, Action f -> Some (f uioh)
4166 | _ -> Some (uioh)
4168 else None
4170 m_active <- active;
4171 m_first <- first;
4172 m_pan <- pan;
4173 optuioh
4175 method hasaction n =
4176 match state.help.(n) with
4177 | _, _, Action _ -> true
4178 | _ -> false
4180 initializer
4181 m_active <- -1
4182 end)
4183 in fun () ->
4184 let modehash = findkeyhash conf "help" in
4185 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4186 G.postRedisplay "help";
4189 let entermsgsmode =
4190 let msgsource =
4191 let re = Str.regexp "[\r\n]" in
4192 (object
4193 inherit lvsourcebase
4194 val mutable m_items = [||]
4196 method getitemcount = 1 + Array.length m_items
4198 method getitem n =
4199 if n = 0
4200 then "[Clear]", 0
4201 else m_items.(n-1), 0
4203 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4204 ignore uioh;
4205 if not cancel
4206 then (
4207 if active = 0
4208 then Buffer.clear state.errmsgs;
4209 m_qsearch <- qsearch;
4211 m_active <- active;
4212 m_first <- first;
4213 m_pan <- pan;
4214 None
4216 method hasaction n =
4217 n = 0
4219 method reset =
4220 state.newerrmsgs <- false;
4221 let l = Str.split re (Buffer.contents state.errmsgs) in
4222 m_items <- Array.of_list l
4224 initializer
4225 m_active <- 0
4226 end)
4227 in fun () ->
4228 state.text <- "";
4229 msgsource#reset;
4230 let source = (msgsource :> lvsource) in
4231 let modehash = findkeyhash conf "listview" in
4232 state.uioh <- coe (object
4233 inherit listview ~source ~trusted:false ~modehash as super
4234 method display =
4235 if state.newerrmsgs
4236 then msgsource#reset;
4237 super#display
4238 end);
4239 G.postRedisplay "msgs";
4242 let quickbookmark ?title () =
4243 match state.layout with
4244 | [] -> ()
4245 | l :: _ ->
4246 let title =
4247 match title with
4248 | None ->
4249 let sec = Unix.gettimeofday () in
4250 let tm = Unix.localtime sec in
4251 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4252 (l.pageno+1)
4253 tm.Unix.tm_mday
4254 tm.Unix.tm_mon
4255 (tm.Unix.tm_year + 1900)
4256 tm.Unix.tm_hour
4257 tm.Unix.tm_min
4258 | Some title -> title
4260 state.bookmarks <-
4261 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4262 :: state.bookmarks
4265 let doreshape w h =
4266 state.fullscreen <- None;
4267 Wsi.reshape w h;
4270 let setautoscrollspeed step goingdown =
4271 let incr = max 1 ((abs step) / 2) in
4272 let incr = if goingdown then incr else -incr in
4273 let astep = step + incr in
4274 state.autoscroll <- Some astep;
4277 let viewkeyboard key mask =
4278 let enttext te =
4279 let mode = state.mode in
4280 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4281 state.text <- "";
4282 enttext ();
4283 G.postRedisplay "view:enttext"
4285 let ctrl = Wsi.withctrl mask in
4286 match key with
4287 | 81 -> (* Q *)
4288 exit 0
4290 | 0xff63 -> (* insert *)
4291 if conf.angle mod 360 = 0
4292 then (
4293 state.mode <- LinkNav (Ltgendir 0);
4294 gotoy state.y;
4296 else showtext '!' "Keyboard link naviagtion does not work under rotation"
4298 | 0xff1b | 113 -> (* escape / q *)
4299 begin match state.mstate with
4300 | Mzoomrect _ ->
4301 state.mstate <- Mnone;
4302 Wsi.setcursor Wsi.CURSOR_INHERIT;
4303 G.postRedisplay "kill zoom rect";
4304 | _ ->
4305 match state.ranchors with
4306 | [] -> raise Quit
4307 | (path, password, anchor) :: rest ->
4308 state.ranchors <- rest;
4309 state.anchor <- anchor;
4310 opendoc path password
4311 end;
4313 | 0xff08 -> (* backspace *)
4314 let y = getnav ~-1 in
4315 gotoy_and_clear_text y
4317 | 111 -> (* o *)
4318 enteroutlinemode ()
4320 | 117 -> (* u *)
4321 state.rects <- [];
4322 state.text <- "";
4323 G.postRedisplay "dehighlight";
4325 | 47 | 63 -> (* / ? *)
4326 let ondone isforw s =
4327 cbput state.hists.pat s;
4328 state.searchpattern <- s;
4329 search s isforw
4331 let s = String.create 1 in
4332 s.[0] <- Char.chr key;
4333 enttext (s, "", Some (onhist state.hists.pat),
4334 textentry, ondone (key = 47))
4336 | 43 | 0xffab when ctrl -> (* ctrl-+ *)
4337 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4338 setzoom (conf.zoom +. incr)
4340 | 43 | 0xffab -> (* + *)
4341 let ondone s =
4342 let n =
4343 try int_of_string s with exc ->
4344 state.text <- Printf.sprintf "bad integer `%s': %s"
4345 s (Printexc.to_string exc);
4346 max_int
4348 if n != max_int
4349 then (
4350 conf.pagebias <- n;
4351 state.text <- "page bias is now " ^ string_of_int n;
4354 enttext ("page bias: ", "", None, intentry, ondone)
4356 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4357 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4358 setzoom (max 0.01 (conf.zoom -. decr))
4360 | 45 | 0xffad -> (* - *)
4361 let ondone msg = state.text <- msg in
4362 enttext (
4363 "option [acfhilpstvxACPRSZTIS]: ", "", None,
4364 optentry state.mode, ondone
4367 | 48 when ctrl -> (* ctrl-0 *)
4368 setzoom 1.0
4370 | 49 when ctrl -> (* 1 *)
4371 let zoom = zoomforh conf.winw conf.winh state.scrollw in
4372 if zoom < 1.0
4373 then setzoom zoom
4375 | 0xffc6 -> (* f9 *)
4376 togglebirdseye ()
4378 | 57 when ctrl -> (* ctrl-9 *)
4379 togglebirdseye ()
4381 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4382 when not ctrl -> (* 0..9 *)
4383 let ondone s =
4384 let n =
4385 try int_of_string s with exc ->
4386 state.text <- Printf.sprintf "bad integer `%s': %s"
4387 s (Printexc.to_string exc);
4390 if n >= 0
4391 then (
4392 addnav ();
4393 cbput state.hists.pag (string_of_int n);
4394 gotopage1 (n + conf.pagebias - 1) 0;
4397 let pageentry text key =
4398 match Char.unsafe_chr key with
4399 | 'g' -> TEdone text
4400 | _ -> intentry text key
4402 let text = "x" in text.[0] <- Char.chr key;
4403 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
4405 | 98 -> (* b *)
4406 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4407 reshape conf.winw conf.winh;
4409 | 108 -> (* l *)
4410 conf.hlinks <- not conf.hlinks;
4411 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4412 G.postRedisplay "toggle highlightlinks";
4414 | 97 -> (* a *)
4415 begin match state.autoscroll with
4416 | Some step ->
4417 conf.autoscrollstep <- step;
4418 state.autoscroll <- None
4419 | None ->
4420 if conf.autoscrollstep = 0
4421 then state.autoscroll <- Some 1
4422 else state.autoscroll <- Some conf.autoscrollstep
4425 | 80 -> (* P *)
4426 conf.presentation <- not conf.presentation;
4427 if conf.presentation
4428 then (
4429 if not conf.scrollbarinpm
4430 then state.scrollw <- 0;
4432 else
4433 state.scrollw <- conf.scrollbw;
4435 showtext ' ' ("presentation mode " ^
4436 if conf.presentation then "on" else "off");
4437 state.anchor <- getanchor ();
4438 represent ()
4440 | 102 -> (* f *)
4441 begin match state.fullscreen with
4442 | None ->
4443 state.fullscreen <- Some (conf.winw, conf.winh);
4444 Wsi.fullscreen ()
4445 | Some (w, h) ->
4446 state.fullscreen <- None;
4447 doreshape w h
4450 | 103 -> (* g *)
4451 gotoy_and_clear_text 0
4453 | 71 -> (* G *)
4454 gotopage1 (state.pagecount - 1) 0
4456 | 112 | 78 -> (* p|N *)
4457 search state.searchpattern false
4459 | 110 | 0xffc0 -> (* n|F3 *)
4460 search state.searchpattern true
4462 | 116 -> (* t *)
4463 begin match state.layout with
4464 | [] -> ()
4465 | l :: _ ->
4466 gotoy_and_clear_text (getpagey l.pageno)
4469 | 32 -> (* ' ' *)
4470 begin match List.rev state.layout with
4471 | [] -> ()
4472 | l :: _ ->
4473 let pageno = min (l.pageno+1) (state.pagecount-1) in
4474 gotoy_and_clear_text (getpagey pageno)
4477 | 0xff9f | 0xffff -> (* delete *)
4478 begin match state.layout with
4479 | [] -> ()
4480 | l :: _ ->
4481 let pageno = max 0 (l.pageno-1) in
4482 gotoy_and_clear_text (getpagey pageno)
4485 | 61 -> (* = *)
4486 showtext ' ' (describe_location ());
4488 | 119 -> (* w *)
4489 begin match state.layout with
4490 | [] -> ()
4491 | l :: _ ->
4492 doreshape (l.pagew + state.scrollw) l.pageh;
4493 G.postRedisplay "w"
4496 | 39 -> (* ' *)
4497 enterbookmarkmode ()
4499 | 104 | 0xffbe -> (* h|F1 *)
4500 enterhelpmode ()
4502 | 105 -> (* i *)
4503 enterinfomode ()
4505 | 101 when conf.redirectstderr -> (* e *)
4506 entermsgsmode ()
4508 | 109 -> (* m *)
4509 let ondone s =
4510 match state.layout with
4511 | l :: _ ->
4512 state.bookmarks <-
4513 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4514 :: state.bookmarks
4515 | _ -> ()
4517 enttext ("bookmark: ", "", None, textentry, ondone)
4519 | 126 -> (* ~ *)
4520 quickbookmark ();
4521 showtext ' ' "Quick bookmark added";
4523 | 122 -> (* z *)
4524 begin match state.layout with
4525 | l :: _ ->
4526 let rect = getpdimrect l.pagedimno in
4527 let w, h =
4528 if conf.crophack
4529 then
4530 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4531 truncate (1.2 *. (rect.(3) -. rect.(0))))
4532 else
4533 (truncate (rect.(1) -. rect.(0)),
4534 truncate (rect.(3) -. rect.(0)))
4536 let w = truncate ((float w)*.conf.zoom)
4537 and h = truncate ((float h)*.conf.zoom) in
4538 if w != 0 && h != 0
4539 then (
4540 state.anchor <- getanchor ();
4541 doreshape (w + state.scrollw) (h + conf.interpagespace)
4543 G.postRedisplay "z";
4545 | [] -> ()
4548 | 50 when ctrl -> (* ctrl-2 *)
4549 let maxw = getmaxw () in
4550 if maxw > 0.0
4551 then setzoom (maxw /. float conf.winw)
4553 | 60 | 62 -> (* < > *)
4554 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
4556 | 91 | 93 -> (* [ ] *)
4557 conf.colorscale <-
4558 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
4560 G.postRedisplay "brightness";
4562 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
4563 setzoom state.prevzoom
4565 | 107 | 0xff52 -> (* k up *)
4566 begin match state.autoscroll with
4567 | None ->
4568 begin match state.mode with
4569 | Birdseye beye -> upbirdseye 1 beye
4570 | _ ->
4571 if ctrl
4572 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
4573 else gotoy_and_clear_text (clamp (-conf.scrollstep))
4575 | Some n ->
4576 setautoscrollspeed n false
4579 | 106 | 0xff54 -> (* j down *)
4580 begin match state.autoscroll with
4581 | None ->
4582 begin match state.mode with
4583 | Birdseye beye -> downbirdseye 1 beye
4584 | _ ->
4585 if ctrl
4586 then gotoy_and_clear_text (clamp (conf.winh/2))
4587 else gotoy_and_clear_text (clamp conf.scrollstep)
4589 | Some n ->
4590 setautoscrollspeed n true
4593 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
4594 if conf.zoom > 1.0
4595 then
4596 let dx =
4597 if ctrl
4598 then conf.winw / 2
4599 else 10
4601 let dx = if key = 0xff51 then dx else -dx in
4602 state.x <- state.x + dx;
4603 gotoy_and_clear_text state.y
4604 else (
4605 state.text <- "";
4606 G.postRedisplay "lef/right"
4609 | 0xff55 -> (* prior *)
4610 let y =
4611 if ctrl
4612 then
4613 match state.layout with
4614 | [] -> state.y
4615 | l :: _ -> state.y - l.pagey
4616 else
4617 clamp (-conf.winh)
4619 gotoghyll y
4621 | 0xff56 -> (* next *)
4622 let y =
4623 if ctrl
4624 then
4625 match List.rev state.layout with
4626 | [] -> state.y
4627 | l :: _ -> getpagey l.pageno
4628 else
4629 clamp conf.winh
4631 gotoghyll y
4633 | 0xff50 -> gotoghyll 0
4634 | 0xff57 -> gotoghyll (clamp state.maxy)
4635 | 0xff53 when Wsi.withalt mask ->
4636 gotoghyll (getnav ~-1)
4637 | 0xff51 when Wsi.withalt mask ->
4638 gotoghyll (getnav 1)
4640 | 114 -> (* r *)
4641 state.anchor <- getanchor ();
4642 opendoc state.path state.password
4644 | 76 -> (* L *)
4645 launchpath ()
4647 | 118 when conf.debug -> (* v *)
4648 state.rects <- [];
4649 List.iter (fun l ->
4650 match getopaque l.pageno with
4651 | None -> ()
4652 | Some opaque ->
4653 let x0, y0, x1, y1 = pagebbox opaque in
4654 let a,b = float x0, float y0 in
4655 let c,d = float x1, float y0 in
4656 let e,f = float x1, float y1 in
4657 let h,j = float x0, float y1 in
4658 let rect = (a,b,c,d,e,f,h,j) in
4659 debugrect rect;
4660 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4661 ) state.layout;
4662 G.postRedisplay "v";
4664 | _ ->
4665 vlog "huh? %s" (Wsi.keyname key)
4668 let gotounder = function
4669 | Ulinkgoto (pageno, top) ->
4670 if pageno >= 0
4671 then (
4672 addnav ();
4673 gotopage1 pageno top;
4676 | Ulinkuri s ->
4677 gotouri s
4679 | Uremote (filename, pageno) ->
4680 let path =
4681 if Sys.file_exists filename
4682 then filename
4683 else
4684 let dir = Filename.dirname state.path in
4685 let path = Filename.concat dir filename in
4686 if Sys.file_exists path
4687 then path
4688 else ""
4690 if String.length path > 0
4691 then (
4692 let anchor = getanchor () in
4693 let ranchor = state.path, state.password, anchor in
4694 state.anchor <- (pageno, 0.0);
4695 state.ranchors <- ranchor :: state.ranchors;
4696 opendoc path "";
4698 else showtext '!' ("Could not find " ^ filename)
4700 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4703 let linknavkeyboard key mask linknav =
4704 let getpage pageno =
4705 let rec loop = function
4706 | [] -> None
4707 | l :: _ when l.pageno = pageno -> Some l
4708 | _ :: rest -> loop rest
4709 in loop state.layout
4711 let doexact (pageno, n) =
4712 match getopaque pageno, getpage pageno with
4713 | Some opaque, Some l ->
4714 if key = 0xff0d
4715 then
4716 let under = getlink opaque n in
4717 G.postRedisplay "link gotounder";
4718 gotounder under;
4719 state.mode <- View;
4720 else
4721 let opt, dir =
4722 match key with
4723 | 0xff50 -> (* home *)
4724 Some (findlink opaque LDfirst), -1
4726 | 0xff57 -> (* end *)
4727 Some (findlink opaque LDlast), 1
4729 | 0xff51 -> (* left *)
4730 Some (findlink opaque (LDleft n)), -1
4732 | 0xff53 -> (* right *)
4733 Some (findlink opaque (LDright n)), 1
4735 | 0xff52 -> (* up *)
4736 Some (findlink opaque (LDup n)), -1
4738 | 0xff54 -> (* down *)
4739 Some (findlink opaque (LDdown n)), 1
4741 | _ -> None, 0
4743 let pwl l dir =
4744 begin match findpwl l.pageno dir with
4745 | Pwlnotfound -> ()
4746 | Pwl pageno ->
4747 let notfound dir =
4748 state.mode <- LinkNav (Ltgendir dir);
4749 let y, h = getpageyh pageno in
4750 let y =
4751 if dir < 0
4752 then y + h - conf.winh
4753 else y
4755 gotoy y
4757 begin match getopaque pageno, getpage pageno with
4758 | Some opaque, Some _ ->
4759 let link =
4760 let ld = if dir > 0 then LDfirst else LDlast in
4761 findlink opaque ld
4763 begin match link with
4764 | Lfound (m, x0, y0, x1, y1) ->
4765 let r = x0, y0, x1, y1 in
4766 state.mode <- LinkNav (Ltexact ((pageno, m), r));
4767 G.postRedisplay "linknav jpage";
4768 | _ -> notfound dir
4769 end;
4770 | _ -> notfound dir
4771 end;
4772 end;
4774 begin match opt with
4775 | Some Lnotfound -> pwl l dir;
4776 | Some (Lfound (m, x0, y0, x1, y1)) ->
4777 if m = n
4778 then pwl l dir
4779 else (
4780 if y0 < l.pagey
4781 then gotopage1 l.pageno y0
4782 else (
4783 if y1 - l.pagey > l.pagevh
4784 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh)
4785 else G.postRedisplay "linknav";
4787 let r = x0, y0, x1, y1 in
4788 state.mode <- LinkNav (Ltexact ((l.pageno, m), r));
4791 | None ->
4792 state.mode <- LinkNav (Ltgendir 0);
4793 viewkeyboard key mask
4794 end;
4795 | _ -> viewkeyboard key mask
4797 if key = 0xff63
4798 then (
4799 state.mode <- View;
4800 G.postRedisplay "leave linknav"
4802 else
4803 match linknav with
4804 | Ltgendir _ -> viewkeyboard key mask
4805 | Ltexact (exact, _) -> doexact exact
4808 let keyboard key mask =
4809 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
4810 then wcmd "interrupt"
4811 else state.uioh <- state.uioh#key key mask
4814 let birdseyekeyboard key mask
4815 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
4816 let incr =
4817 match conf.columns with
4818 | None -> 1
4819 | Some ((c, _, _), _) -> c
4821 match key with
4822 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
4823 let y, h = getpageyh pageno in
4824 let top = (conf.winh - h) / 2 in
4825 gotoy (max 0 (y - top))
4826 | 0xff0d -> leavebirdseye beye false
4827 | 0xff1b -> leavebirdseye beye true (* escape *)
4828 | 0xff52 -> upbirdseye incr beye (* prior *)
4829 | 0xff54 -> downbirdseye incr beye (* next *)
4830 | 0xff51 -> upbirdseye 1 beye (* up *)
4831 | 0xff53 -> downbirdseye 1 beye (* down *)
4833 | 0xff55 ->
4834 begin match state.layout with
4835 | l :: _ ->
4836 if l.pagey != 0
4837 then (
4838 state.mode <- Birdseye (
4839 oconf, leftx, l.pageno, hooverpageno, anchor
4841 gotopage1 l.pageno 0;
4843 else (
4844 let layout = layout (state.y-conf.winh) conf.winh in
4845 match layout with
4846 | [] -> gotoy (clamp (-conf.winh))
4847 | l :: _ ->
4848 state.mode <- Birdseye (
4849 oconf, leftx, l.pageno, hooverpageno, anchor
4851 gotopage1 l.pageno 0
4854 | [] -> gotoy (clamp (-conf.winh))
4855 end;
4857 | 0xff56 ->
4858 begin match List.rev state.layout with
4859 | l :: _ ->
4860 let layout = layout (state.y + conf.winh) conf.winh in
4861 begin match layout with
4862 | [] ->
4863 let incr = l.pageh - l.pagevh in
4864 if incr = 0
4865 then (
4866 state.mode <-
4867 Birdseye (
4868 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4870 G.postRedisplay "birdseye pagedown";
4872 else gotoy (clamp (incr + conf.interpagespace*2));
4874 | l :: _ ->
4875 state.mode <-
4876 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4877 gotopage1 l.pageno 0;
4880 | [] -> gotoy (clamp conf.winh)
4881 end;
4883 | 0xff50 ->
4884 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4885 gotopage1 0 0
4887 | 0xff57 ->
4888 let pageno = state.pagecount - 1 in
4889 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4890 if not (pagevisible state.layout pageno)
4891 then
4892 let h =
4893 match List.rev state.pdims with
4894 | [] -> conf.winh
4895 | (_, _, h, _) :: _ -> h
4897 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4898 else G.postRedisplay "birdseye end";
4899 | _ -> viewkeyboard key mask
4902 let drawpage l =
4903 let color =
4904 match state.mode with
4905 | Textentry _ -> scalecolor 0.4
4906 | LinkNav _
4907 | View -> scalecolor 1.0
4908 | Birdseye (_, _, pageno, hooverpageno, _) ->
4909 if l.pageno = hooverpageno
4910 then scalecolor 0.9
4911 else (
4912 if l.pageno = pageno
4913 then scalecolor 1.0
4914 else scalecolor 0.8
4917 drawtiles l color;
4918 begin match getopaque l.pageno with
4919 | Some opaque ->
4920 if tileready l l.pagex l.pagey
4921 then
4922 let x = l.pagedispx - l.pagex
4923 and y = l.pagedispy - l.pagey in
4924 postprocess opaque conf.hlinks x y;
4926 | _ -> ()
4927 end;
4930 let scrollindicator () =
4931 let sbw, ph, sh = state.uioh#scrollph in
4932 let sbh, pw, sw = state.uioh#scrollpw in
4934 GlDraw.color (0.64, 0.64, 0.64);
4935 GlDraw.rect
4936 (float (conf.winw - sbw), 0.)
4937 (float conf.winw, float conf.winh)
4939 GlDraw.rect
4940 (0., float (conf.winh - sbh))
4941 (float (conf.winw - state.scrollw - 1), float conf.winh)
4943 GlDraw.color (0.0, 0.0, 0.0);
4945 GlDraw.rect
4946 (float (conf.winw - sbw), ph)
4947 (float conf.winw, ph +. sh)
4949 GlDraw.rect
4950 (pw, float (conf.winh - sbh))
4951 (pw +. sw, float conf.winh)
4955 let showsel () =
4956 match state.mstate with
4957 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4960 | Msel ((x0, y0), (x1, y1)) ->
4961 let rec loop = function
4962 | l :: ls ->
4963 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4964 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
4965 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
4966 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
4967 then
4968 match getopaque l.pageno with
4969 | Some opaque ->
4970 let x0, y0 = pagetranslatepoint l x0 y0 in
4971 let x1, y1 = pagetranslatepoint l x1 y1 in
4972 seltext opaque (x0, y0, x1, y1);
4973 | _ -> ()
4974 else loop ls
4975 | [] -> ()
4977 loop state.layout
4980 let showrects rects =
4981 Gl.enable `blend;
4982 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4983 GlDraw.polygon_mode `both `fill;
4984 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4985 List.iter
4986 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4987 List.iter (fun l ->
4988 if l.pageno = pageno
4989 then (
4990 let dx = float (l.pagedispx - l.pagex) in
4991 let dy = float (l.pagedispy - l.pagey) in
4992 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4993 GlDraw.begins `quads;
4995 GlDraw.vertex2 (x0+.dx, y0+.dy);
4996 GlDraw.vertex2 (x1+.dx, y1+.dy);
4997 GlDraw.vertex2 (x2+.dx, y2+.dy);
4998 GlDraw.vertex2 (x3+.dx, y3+.dy);
5000 GlDraw.ends ();
5002 ) state.layout
5003 ) rects
5005 Gl.disable `blend;
5008 let display () =
5009 GlClear.color (scalecolor2 conf.bgcolor);
5010 GlClear.clear [`color];
5011 List.iter drawpage state.layout;
5012 let rects =
5013 match state.mode with
5014 | LinkNav (Ltexact ((pageno, _), (x0, y0, x1, y1))) ->
5015 (pageno, 5, (
5016 float x0, float y0,
5017 float x1, float y0,
5018 float x1, float y1,
5019 float x0, float y1)
5020 ) :: state.rects
5021 | _ -> state.rects
5023 showrects rects;
5024 showsel ();
5025 state.uioh#display;
5026 begin match state.mstate with
5027 | Mzoomrect ((x0, y0), (x1, y1)) ->
5028 Gl.enable `blend;
5029 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5030 GlDraw.polygon_mode `both `fill;
5031 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5032 GlDraw.rect (float x0, float y0)
5033 (float x1, float y1);
5034 Gl.disable `blend;
5035 | _ -> ()
5036 end;
5037 enttext ();
5038 scrollindicator ();
5039 if conf.updatecurs
5040 then (
5041 let mx, my = state.mpos in
5042 updateunder mx my;
5044 Wsi.swapb ();
5047 let display () =
5048 if nogeomcmds state.geomcmds
5049 then display ()
5050 else (
5051 GlFunc.draw_buffer `front;
5052 GlClear.color (scalecolor2 conf.bgcolor);
5053 GlClear.clear [`color];
5054 GlFunc.draw_buffer `back;
5058 let zoomrect x y x1 y1 =
5059 let x0 = min x x1
5060 and x1 = max x x1
5061 and y0 = min y y1 in
5062 gotoy (state.y + y0);
5063 state.anchor <- getanchor ();
5064 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5065 let margin =
5066 if state.w < conf.winw - state.scrollw
5067 then (conf.winw - state.scrollw - state.w) / 2
5068 else 0
5070 state.x <- (state.x + margin) - x0;
5071 setzoom zoom;
5072 Wsi.setcursor Wsi.CURSOR_INHERIT;
5073 state.mstate <- Mnone;
5076 let scrollx x =
5077 let winw = conf.winw - state.scrollw - 1 in
5078 let s = float x /. float winw in
5079 let destx = truncate (float (state.w + winw) *. s) in
5080 state.x <- winw - destx;
5081 gotoy_and_clear_text state.y;
5082 state.mstate <- Mscrollx;
5085 let scrolly y =
5086 let s = float y /. float conf.winh in
5087 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5088 gotoy_and_clear_text desty;
5089 state.mstate <- Mscrolly;
5092 let viewmouse button down x y mask =
5093 match button with
5094 | n when (n == 4 || n == 5) && not down ->
5095 if Wsi.withctrl mask
5096 then (
5097 match state.mstate with
5098 | Mzoom (oldn, i) ->
5099 if oldn = n
5100 then (
5101 if i = 2
5102 then
5103 let incr =
5104 match n with
5105 | 5 ->
5106 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5107 | _ ->
5108 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5110 let zoom = conf.zoom -. incr in
5111 setzoom zoom;
5112 state.mstate <- Mzoom (n, 0);
5113 else
5114 state.mstate <- Mzoom (n, i+1);
5116 else state.mstate <- Mzoom (n, 0)
5118 | _ -> state.mstate <- Mzoom (n, 0)
5120 else (
5121 match state.autoscroll with
5122 | Some step -> setautoscrollspeed step (n=4)
5123 | None ->
5124 let incr =
5125 if n = 4
5126 then -conf.scrollstep
5127 else conf.scrollstep
5129 let incr = incr * 2 in
5130 let y = clamp incr in
5131 gotoy_and_clear_text y
5134 | 1 when Wsi.withctrl mask ->
5135 if down
5136 then (
5137 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5138 state.mstate <- Mpan (x, y)
5140 else
5141 state.mstate <- Mnone
5143 | 3 ->
5144 if down
5145 then (
5146 Wsi.setcursor Wsi.CURSOR_CYCLE;
5147 let p = (x, y) in
5148 state.mstate <- Mzoomrect (p, p)
5150 else (
5151 match state.mstate with
5152 | Mzoomrect ((x0, y0), _) ->
5153 if abs (x-x0) > 10 && abs (y - y0) > 10
5154 then zoomrect x0 y0 x y
5155 else (
5156 state.mstate <- Mnone;
5157 Wsi.setcursor Wsi.CURSOR_INHERIT;
5158 G.postRedisplay "kill accidental zoom rect";
5160 | _ ->
5161 Wsi.setcursor Wsi.CURSOR_INHERIT;
5162 state.mstate <- Mnone
5165 | 1 when x > conf.winw - state.scrollw ->
5166 if down
5167 then
5168 let _, position, sh = state.uioh#scrollph in
5169 if y > truncate position && y < truncate (position +. sh)
5170 then state.mstate <- Mscrolly
5171 else scrolly y
5172 else
5173 state.mstate <- Mnone
5175 | 1 when y > conf.winh - state.hscrollh ->
5176 if down
5177 then
5178 let _, position, sw = state.uioh#scrollpw in
5179 if x > truncate position && x < truncate (position +. sw)
5180 then state.mstate <- Mscrollx
5181 else scrollx x
5182 else
5183 state.mstate <- Mnone
5185 | 1 ->
5186 let dest = if down then getunder x y else Unone in
5187 begin match dest with
5188 | Ulinkgoto (pageno, top) ->
5189 if pageno >= 0
5190 then (
5191 addnav ();
5192 gotopage1 pageno top;
5195 | Ulinkuri s ->
5196 gotouri s
5198 | Uremote (filename, pageno) ->
5199 let path =
5200 if Sys.file_exists filename
5201 then filename
5202 else
5203 let dir = Filename.dirname state.path in
5204 let path = Filename.concat dir filename in
5205 if Sys.file_exists path
5206 then path
5207 else ""
5209 if String.length path > 0
5210 then (
5211 let anchor = getanchor () in
5212 let ranchor = state.path, state.password, anchor in
5213 state.anchor <- (pageno, 0.0);
5214 state.ranchors <- ranchor :: state.ranchors;
5215 opendoc path "";
5217 else showtext '!' ("Could not find " ^ filename)
5219 | Uunexpected _ | Ulaunch _ | Unamed _ -> ()
5221 | Unone when down ->
5222 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5223 state.mstate <- Mpan (x, y);
5225 | Unone | Utext _ ->
5226 if down
5227 then (
5228 if conf.angle mod 360 = 0
5229 then (
5230 state.mstate <- Msel ((x, y), (x, y));
5231 G.postRedisplay "mouse select";
5234 else (
5235 match state.mstate with
5236 | Mnone -> ()
5238 | Mzoom _ | Mscrollx | Mscrolly ->
5239 state.mstate <- Mnone
5241 | Mzoomrect ((x0, y0), _) ->
5242 zoomrect x0 y0 x y
5244 | Mpan _ ->
5245 Wsi.setcursor Wsi.CURSOR_INHERIT;
5246 state.mstate <- Mnone
5248 | Msel ((_, y0), (_, y1)) ->
5249 let rec loop = function
5250 | [] -> ()
5251 | l :: rest ->
5252 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5253 || ((y1 >= l.pagedispy
5254 && y1 <= (l.pagedispy + l.pagevh)))
5255 then
5256 match getopaque l.pageno with
5257 | Some opaque ->
5258 copysel conf.selcmd opaque;
5259 G.postRedisplay "copysel"
5260 | _ -> ()
5261 else loop rest
5263 loop state.layout;
5264 Wsi.setcursor Wsi.CURSOR_INHERIT;
5265 state.mstate <- Mnone;
5269 | _ -> ()
5272 let birdseyemouse button down x y mask
5273 (conf, leftx, _, hooverpageno, anchor) =
5274 match button with
5275 | 1 when down ->
5276 let rec loop = function
5277 | [] -> ()
5278 | l :: rest ->
5279 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5280 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5281 then (
5282 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5284 else loop rest
5286 loop state.layout
5287 | 3 -> ()
5288 | _ -> viewmouse button down x y mask
5291 let mouse button down x y mask =
5292 state.uioh <- state.uioh#button button down x y mask;
5295 let motion ~x ~y =
5296 state.uioh <- state.uioh#motion x y
5299 let pmotion ~x ~y =
5300 state.uioh <- state.uioh#pmotion x y;
5303 let uioh = object
5304 method display = ()
5306 method key key mask =
5307 begin match state.mode with
5308 | Textentry textentry -> textentrykeyboard key mask textentry
5309 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5310 | View -> viewkeyboard key mask
5311 | LinkNav linknav -> linknavkeyboard key mask linknav
5312 end;
5313 state.uioh
5315 method button button bstate x y mask =
5316 begin match state.mode with
5317 | LinkNav _
5318 | View -> viewmouse button bstate x y mask
5319 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5320 | Textentry _ -> ()
5321 end;
5322 state.uioh
5324 method motion x y =
5325 begin match state.mode with
5326 | Textentry _ -> ()
5327 | View | Birdseye _ | LinkNav _ ->
5328 match state.mstate with
5329 | Mzoom _ | Mnone -> ()
5331 | Mpan (x0, y0) ->
5332 let dx = x - x0
5333 and dy = y0 - y in
5334 state.mstate <- Mpan (x, y);
5335 if conf.zoom > 1.0 then state.x <- state.x + dx;
5336 let y = clamp dy in
5337 gotoy_and_clear_text y
5339 | Msel (a, _) ->
5340 state.mstate <- Msel (a, (x, y));
5341 G.postRedisplay "motion select";
5343 | Mscrolly ->
5344 let y = min conf.winh (max 0 y) in
5345 scrolly y
5347 | Mscrollx ->
5348 let x = min conf.winw (max 0 x) in
5349 scrollx x
5351 | Mzoomrect (p0, _) ->
5352 state.mstate <- Mzoomrect (p0, (x, y));
5353 G.postRedisplay "motion zoomrect";
5354 end;
5355 state.uioh
5357 method pmotion x y =
5358 begin match state.mode with
5359 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5360 let rec loop = function
5361 | [] ->
5362 if hooverpageno != -1
5363 then (
5364 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5365 G.postRedisplay "pmotion birdseye no hoover";
5367 | l :: rest ->
5368 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5369 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5370 then (
5371 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5372 G.postRedisplay "pmotion birdseye hoover";
5374 else loop rest
5376 loop state.layout
5378 | Textentry _ -> ()
5380 | LinkNav _
5381 | View ->
5382 match state.mstate with
5383 | Mnone -> updateunder x y
5384 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5386 end;
5387 state.uioh
5389 method infochanged _ = ()
5391 method scrollph =
5392 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5393 let p, h = scrollph state.y maxy in
5394 state.scrollw, p, h
5396 method scrollpw =
5397 let winw = conf.winw - state.scrollw - 1 in
5398 let fwinw = float winw in
5399 let sw =
5400 let sw = fwinw /. float state.w in
5401 let sw = fwinw *. sw in
5402 max sw (float conf.scrollh)
5404 let position, sw =
5405 let f = state.w+winw in
5406 let r = float (winw-state.x) /. float f in
5407 let p = fwinw *. r in
5408 p-.sw/.2., sw
5410 let sw =
5411 if position +. sw > fwinw
5412 then fwinw -. position
5413 else sw
5415 state.hscrollh, position, sw
5417 method modehash =
5418 let modename =
5419 match state.mode with
5420 | LinkNav _ -> "links"
5421 | Textentry _ -> "textentry"
5422 | Birdseye _ -> "birdseye"
5423 | View -> "global"
5425 findkeyhash conf modename
5426 end;;
5428 module Config =
5429 struct
5430 open Parser
5432 let fontpath = ref "";;
5434 module KeyMap =
5435 Map.Make (struct type t = (int * int) let compare = compare end);;
5437 let unent s =
5438 let l = String.length s in
5439 let b = Buffer.create l in
5440 unent b s 0 l;
5441 Buffer.contents b;
5444 let home =
5445 try Sys.getenv "HOME"
5446 with exn ->
5447 prerr_endline
5448 ("Can not determine home directory location: " ^
5449 Printexc.to_string exn);
5453 let modifier_of_string = function
5454 | "alt" -> Wsi.altmask
5455 | "shift" -> Wsi.shiftmask
5456 | "ctrl" | "control" -> Wsi.ctrlmask
5457 | "meta" -> Wsi.metamask
5458 | _ -> 0
5461 let key_of_string =
5462 let r = Str.regexp "-" in
5463 fun s ->
5464 let elems = Str.full_split r s in
5465 let f n k m =
5466 let g s =
5467 let m1 = modifier_of_string s in
5468 if m1 = 0
5469 then (Wsi.namekey s, m)
5470 else (k, m lor m1)
5471 in function
5472 | Str.Delim s when n land 1 = 0 -> g s
5473 | Str.Text s -> g s
5474 | Str.Delim _ -> (k, m)
5476 let rec loop n k m = function
5477 | [] -> (k, m)
5478 | x :: xs ->
5479 let k, m = f n k m x in
5480 loop (n+1) k m xs
5482 loop 0 0 0 elems
5485 let keys_of_string =
5486 let r = Str.regexp "[ \t]" in
5487 fun s ->
5488 let elems = Str.split r s in
5489 List.map key_of_string elems
5492 let copykeyhashes c =
5493 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
5496 let config_of c attrs =
5497 let apply c k v =
5499 match k with
5500 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5501 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5502 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5503 | "preload" -> { c with preload = bool_of_string v }
5504 | "page-bias" -> { c with pagebias = int_of_string v }
5505 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5506 | "auto-scroll-step" ->
5507 { c with autoscrollstep = max 0 (int_of_string v) }
5508 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5509 | "crop-hack" -> { c with crophack = bool_of_string v }
5510 | "throttle" ->
5511 let mw =
5512 match String.lowercase v with
5513 | "true" -> Some infinity
5514 | "false" -> None
5515 | f -> Some (float_of_string f)
5517 { c with maxwait = mw}
5518 | "highlight-links" -> { c with hlinks = bool_of_string v }
5519 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5520 | "vertical-margin" ->
5521 { c with interpagespace = max 0 (int_of_string v) }
5522 | "zoom" ->
5523 let zoom = float_of_string v /. 100. in
5524 let zoom = max zoom 0.0 in
5525 { c with zoom = zoom }
5526 | "presentation" -> { c with presentation = bool_of_string v }
5527 | "rotation-angle" -> { c with angle = int_of_string v }
5528 | "width" -> { c with winw = max 20 (int_of_string v) }
5529 | "height" -> { c with winh = max 20 (int_of_string v) }
5530 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5531 | "proportional-display" -> { c with proportional = bool_of_string v }
5532 | "pixmap-cache-size" ->
5533 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5534 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5535 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5536 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5537 | "persistent-location" -> { c with jumpback = bool_of_string v }
5538 | "background-color" -> { c with bgcolor = color_of_string v }
5539 | "scrollbar-in-presentation" ->
5540 { c with scrollbarinpm = bool_of_string v }
5541 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5542 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5543 | "mupdf-store-size" ->
5544 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5545 | "checkers" -> { c with checkers = bool_of_string v }
5546 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5547 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5548 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5549 | "uri-launcher" -> { c with urilauncher = unent v }
5550 | "path-launcher" -> { c with pathlauncher = unent v }
5551 | "color-space" -> { c with colorspace = colorspace_of_string v }
5552 | "invert-colors" -> { c with invert = bool_of_string v }
5553 | "brightness" -> { c with colorscale = float_of_string v }
5554 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5555 | "ghyllscroll" ->
5556 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5557 | "columns" ->
5558 let nab = columns_of_string v in
5559 { c with columns = Some (nab, [||]) }
5560 | "birds-eye-columns" ->
5561 { c with beyecolumns = Some (max (int_of_string v) 2) }
5562 | "selection-command" -> { c with selcmd = unent v }
5563 | "update-cursor" -> { c with updatecurs = bool_of_string v }
5564 | _ -> c
5565 with exn ->
5566 prerr_endline ("Error processing attribute (`" ^
5567 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5570 let rec fold c = function
5571 | [] -> c
5572 | (k, v) :: rest ->
5573 let c = apply c k v in
5574 fold c rest
5576 fold { c with keyhashes = copykeyhashes c } attrs;
5579 let fromstring f pos n v d =
5580 try f v
5581 with exn ->
5582 dolog "Error processing attribute (%S=%S) at %d\n%s"
5583 n v pos (Printexc.to_string exn)
5588 let bookmark_of attrs =
5589 let rec fold title page rely = function
5590 | ("title", v) :: rest -> fold v page rely rest
5591 | ("page", v) :: rest -> fold title v rely rest
5592 | ("rely", v) :: rest -> fold title page v rest
5593 | _ :: rest -> fold title page rely rest
5594 | [] -> title, page, rely
5596 fold "invalid" "0" "0" attrs
5599 let doc_of attrs =
5600 let rec fold path page rely pan = function
5601 | ("path", v) :: rest -> fold v page rely pan rest
5602 | ("page", v) :: rest -> fold path v rely pan rest
5603 | ("rely", v) :: rest -> fold path page v pan rest
5604 | ("pan", v) :: rest -> fold path page rely v rest
5605 | _ :: rest -> fold path page rely pan rest
5606 | [] -> path, page, rely, pan
5608 fold "" "0" "0" "0" attrs
5611 let map_of attrs =
5612 let rec fold rs ls = function
5613 | ("out", v) :: rest -> fold v ls rest
5614 | ("in", v) :: rest -> fold rs v rest
5615 | _ :: rest -> fold ls rs rest
5616 | [] -> ls, rs
5618 fold "" "" attrs
5621 let setconf dst src =
5622 dst.scrollbw <- src.scrollbw;
5623 dst.scrollh <- src.scrollh;
5624 dst.icase <- src.icase;
5625 dst.preload <- src.preload;
5626 dst.pagebias <- src.pagebias;
5627 dst.verbose <- src.verbose;
5628 dst.scrollstep <- src.scrollstep;
5629 dst.maxhfit <- src.maxhfit;
5630 dst.crophack <- src.crophack;
5631 dst.autoscrollstep <- src.autoscrollstep;
5632 dst.maxwait <- src.maxwait;
5633 dst.hlinks <- src.hlinks;
5634 dst.underinfo <- src.underinfo;
5635 dst.interpagespace <- src.interpagespace;
5636 dst.zoom <- src.zoom;
5637 dst.presentation <- src.presentation;
5638 dst.angle <- src.angle;
5639 dst.winw <- src.winw;
5640 dst.winh <- src.winh;
5641 dst.savebmarks <- src.savebmarks;
5642 dst.memlimit <- src.memlimit;
5643 dst.proportional <- src.proportional;
5644 dst.texcount <- src.texcount;
5645 dst.sliceheight <- src.sliceheight;
5646 dst.thumbw <- src.thumbw;
5647 dst.jumpback <- src.jumpback;
5648 dst.bgcolor <- src.bgcolor;
5649 dst.scrollbarinpm <- src.scrollbarinpm;
5650 dst.tilew <- src.tilew;
5651 dst.tileh <- src.tileh;
5652 dst.mustoresize <- src.mustoresize;
5653 dst.checkers <- src.checkers;
5654 dst.aalevel <- src.aalevel;
5655 dst.trimmargins <- src.trimmargins;
5656 dst.trimfuzz <- src.trimfuzz;
5657 dst.urilauncher <- src.urilauncher;
5658 dst.colorspace <- src.colorspace;
5659 dst.invert <- src.invert;
5660 dst.colorscale <- src.colorscale;
5661 dst.redirectstderr <- src.redirectstderr;
5662 dst.ghyllscroll <- src.ghyllscroll;
5663 dst.columns <- src.columns;
5664 dst.beyecolumns <- src.beyecolumns;
5665 dst.selcmd <- src.selcmd;
5666 dst.updatecurs <- src.updatecurs;
5667 dst.pathlauncher <- src.pathlauncher;
5668 dst.keyhashes <- copykeyhashes src;
5671 let get s =
5672 let h = Hashtbl.create 10 in
5673 let dc = { defconf with angle = defconf.angle } in
5674 let rec toplevel v t spos _ =
5675 match t with
5676 | Vdata | Vcdata | Vend -> v
5677 | Vopen ("llppconfig", _, closed) ->
5678 if closed
5679 then v
5680 else { v with f = llppconfig }
5681 | Vopen _ ->
5682 error "unexpected subelement at top level" s spos
5683 | Vclose _ -> error "unexpected close at top level" s spos
5685 and llppconfig v t spos _ =
5686 match t with
5687 | Vdata | Vcdata -> v
5688 | Vend -> error "unexpected end of input in llppconfig" s spos
5689 | Vopen ("defaults", attrs, closed) ->
5690 let c = config_of dc attrs in
5691 setconf dc c;
5692 if closed
5693 then v
5694 else { v with f = defaults }
5696 | Vopen ("ui-font", attrs, closed) ->
5697 let rec getsize size = function
5698 | [] -> size
5699 | ("size", v) :: rest ->
5700 let size =
5701 fromstring int_of_string spos "size" v fstate.fontsize in
5702 getsize size rest
5703 | l -> getsize size l
5705 fstate.fontsize <- getsize fstate.fontsize attrs;
5706 if closed
5707 then v
5708 else { v with f = uifont (Buffer.create 10) }
5710 | Vopen ("doc", attrs, closed) ->
5711 let pathent, spage, srely, span = doc_of attrs in
5712 let path = unent pathent
5713 and pageno = fromstring int_of_string spos "page" spage 0
5714 and rely = fromstring float_of_string spos "rely" srely 0.0
5715 and pan = fromstring int_of_string spos "pan" span 0 in
5716 let c = config_of dc attrs in
5717 let anchor = (pageno, rely) in
5718 if closed
5719 then (Hashtbl.add h path (c, [], pan, anchor); v)
5720 else { v with f = doc path pan anchor c [] }
5722 | Vopen _ ->
5723 error "unexpected subelement in llppconfig" s spos
5725 | Vclose "llppconfig" -> { v with f = toplevel }
5726 | Vclose _ -> error "unexpected close in llppconfig" s spos
5728 and defaults v t spos _ =
5729 match t with
5730 | Vdata | Vcdata -> v
5731 | Vend -> error "unexpected end of input in defaults" s spos
5732 | Vopen ("keymap", attrs, closed) ->
5733 let modename =
5734 try List.assoc "mode" attrs
5735 with Not_found -> "global" in
5736 if closed
5737 then v
5738 else
5739 let ret keymap =
5740 let h = findkeyhash dc modename in
5741 KeyMap.iter (Hashtbl.replace h) keymap;
5742 defaults
5744 { v with f = pkeymap ret KeyMap.empty }
5746 | Vopen (_, _, _) ->
5747 error "unexpected subelement in defaults" s spos
5749 | Vclose "defaults" ->
5750 { v with f = llppconfig }
5752 | Vclose _ -> error "unexpected close in defaults" s spos
5754 and uifont b v t spos epos =
5755 match t with
5756 | Vdata | Vcdata ->
5757 Buffer.add_substring b s spos (epos - spos);
5759 | Vopen (_, _, _) ->
5760 error "unexpected subelement in ui-font" s spos
5761 | Vclose "ui-font" ->
5762 if String.length !fontpath = 0
5763 then fontpath := Buffer.contents b;
5764 { v with f = llppconfig }
5765 | Vclose _ -> error "unexpected close in ui-font" s spos
5766 | Vend -> error "unexpected end of input in ui-font" s spos
5768 and doc path pan anchor c bookmarks v t spos _ =
5769 match t with
5770 | Vdata | Vcdata -> v
5771 | Vend -> error "unexpected end of input in doc" s spos
5772 | Vopen ("bookmarks", _, closed) ->
5773 if closed
5774 then v
5775 else { v with f = pbookmarks path pan anchor c bookmarks }
5777 | Vopen ("keymap", attrs, closed) ->
5778 let modename =
5779 try List.assoc "mode" attrs
5780 with Not_found -> "global"
5782 if closed
5783 then v
5784 else
5785 let ret keymap =
5786 let h = findkeyhash c modename in
5787 KeyMap.iter (Hashtbl.replace h) keymap;
5788 doc path pan anchor c bookmarks
5790 { v with f = pkeymap ret KeyMap.empty }
5792 | Vopen (_, _, _) ->
5793 error "unexpected subelement in doc" s spos
5795 | Vclose "doc" ->
5796 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5797 { v with f = llppconfig }
5799 | Vclose _ -> error "unexpected close in doc" s spos
5801 and pkeymap ret keymap v t spos _ =
5802 match t with
5803 | Vdata | Vcdata -> v
5804 | Vend -> error "unexpected end of input in keymap" s spos
5805 | Vopen ("map", attrs, closed) ->
5806 let r, l = map_of attrs in
5807 let kss = fromstring keys_of_string spos "in" r [] in
5808 let lss = fromstring keys_of_string spos "out" l [] in
5809 let keymap =
5810 match kss with
5811 | [] -> keymap
5812 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
5813 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
5815 if closed
5816 then { v with f = pkeymap ret keymap }
5817 else
5818 let f () = v in
5819 { v with f = skip "map" f }
5821 | Vopen _ ->
5822 error "unexpected subelement in keymap" s spos
5824 | Vclose "keymap" ->
5825 { v with f = ret keymap }
5827 | Vclose _ -> error "unexpected close in keymap" s spos
5829 and pbookmarks path pan anchor c bookmarks v t spos _ =
5830 match t with
5831 | Vdata | Vcdata -> v
5832 | Vend -> error "unexpected end of input in bookmarks" s spos
5833 | Vopen ("item", attrs, closed) ->
5834 let titleent, spage, srely = bookmark_of attrs in
5835 let page = fromstring int_of_string spos "page" spage 0
5836 and rely = fromstring float_of_string spos "rely" srely 0.0 in
5837 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
5838 if closed
5839 then { v with f = pbookmarks path pan anchor c bookmarks }
5840 else
5841 let f () = v in
5842 { v with f = skip "item" f }
5844 | Vopen _ ->
5845 error "unexpected subelement in bookmarks" s spos
5847 | Vclose "bookmarks" ->
5848 { v with f = doc path pan anchor c bookmarks }
5850 | Vclose _ -> error "unexpected close in bookmarks" s spos
5852 and skip tag f v t spos _ =
5853 match t with
5854 | Vdata | Vcdata -> v
5855 | Vend ->
5856 error ("unexpected end of input in skipped " ^ tag) s spos
5857 | Vopen (tag', _, closed) ->
5858 if closed
5859 then v
5860 else
5861 let f' () = { v with f = skip tag f } in
5862 { v with f = skip tag' f' }
5863 | Vclose ctag ->
5864 if tag = ctag
5865 then f ()
5866 else error ("unexpected close in skipped " ^ tag) s spos
5869 parse { f = toplevel; accu = () } s;
5870 h, dc;
5873 let do_load f ic =
5875 let len = in_channel_length ic in
5876 let s = String.create len in
5877 really_input ic s 0 len;
5878 f s;
5879 with
5880 | Parse_error (msg, s, pos) ->
5881 let subs = subs s pos in
5882 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
5883 failwith ("parse error: " ^ s)
5885 | exn ->
5886 failwith ("config load error: " ^ Printexc.to_string exn)
5889 let defconfpath =
5890 let dir =
5892 let dir = Filename.concat home ".config" in
5893 if Sys.is_directory dir then dir else home
5894 with _ -> home
5896 Filename.concat dir "llpp.conf"
5899 let confpath = ref defconfpath;;
5901 let load1 f =
5902 if Sys.file_exists !confpath
5903 then
5904 match
5905 (try Some (open_in_bin !confpath)
5906 with exn ->
5907 prerr_endline
5908 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5909 Printexc.to_string exn);
5910 None
5912 with
5913 | Some ic ->
5914 begin try
5915 f (do_load get ic)
5916 with exn ->
5917 prerr_endline
5918 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5919 Printexc.to_string exn);
5920 end;
5921 close_in ic;
5923 | None -> ()
5924 else
5925 f (Hashtbl.create 0, defconf)
5928 let load () =
5929 let f (h, dc) =
5930 let pc, pb, px, pa =
5932 Hashtbl.find h (Filename.basename state.path)
5933 with Not_found -> dc, [], 0, (0, 0.0)
5935 setconf defconf dc;
5936 setconf conf pc;
5937 state.bookmarks <- pb;
5938 state.x <- px;
5939 state.scrollw <- conf.scrollbw;
5940 if conf.jumpback
5941 then state.anchor <- pa;
5942 cbput state.hists.nav pa;
5944 load1 f
5947 let add_attrs bb always dc c =
5948 let ob s a b =
5949 if always || a != b
5950 then Printf.bprintf bb "\n %s='%b'" s a
5951 and oi s a b =
5952 if always || a != b
5953 then Printf.bprintf bb "\n %s='%d'" s a
5954 and oI s a b =
5955 if always || a != b
5956 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5957 and oz s a b =
5958 if always || a <> b
5959 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5960 and oF s a b =
5961 if always || a <> b
5962 then Printf.bprintf bb "\n %s='%f'" s a
5963 and oc s a b =
5964 if always || a <> b
5965 then
5966 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5967 and oC s a b =
5968 if always || a <> b
5969 then
5970 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5971 and oR s a b =
5972 if always || a <> b
5973 then
5974 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5975 and os s a b =
5976 if always || a <> b
5977 then
5978 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5979 and og s a b =
5980 if always || a <> b
5981 then
5982 match a with
5983 | None -> ()
5984 | Some (_N, _A, _B) ->
5985 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
5986 and oW s a b =
5987 if always || a <> b
5988 then
5989 let v =
5990 match a with
5991 | None -> "false"
5992 | Some f ->
5993 if f = infinity
5994 then "true"
5995 else string_of_float f
5997 Printf.bprintf bb "\n %s='%s'" s v
5998 and oco s a b =
5999 if always || a <> b
6000 then
6001 match a with
6002 | Some ((n, a, b), _) when n > 1 ->
6003 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6004 | _ -> ()
6005 and obeco s a b =
6006 if always || a <> b
6007 then
6008 match a with
6009 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6010 | _ -> ()
6012 let w, h =
6013 if always
6014 then dc.winw, dc.winh
6015 else
6016 match state.fullscreen with
6017 | Some wh -> wh
6018 | None -> c.winw, c.winh
6020 let zoom, presentation, interpagespace, maxwait =
6021 if always
6022 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
6023 else
6024 match state.mode with
6025 | Birdseye (bc, _, _, _, _) ->
6026 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
6027 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
6029 oi "width" w dc.winw;
6030 oi "height" h dc.winh;
6031 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6032 oi "scroll-handle-height" c.scrollh dc.scrollh;
6033 ob "case-insensitive-search" c.icase dc.icase;
6034 ob "preload" c.preload dc.preload;
6035 oi "page-bias" c.pagebias dc.pagebias;
6036 oi "scroll-step" c.scrollstep dc.scrollstep;
6037 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6038 ob "max-height-fit" c.maxhfit dc.maxhfit;
6039 ob "crop-hack" c.crophack dc.crophack;
6040 oW "throttle" maxwait dc.maxwait;
6041 ob "highlight-links" c.hlinks dc.hlinks;
6042 ob "under-cursor-info" c.underinfo dc.underinfo;
6043 oi "vertical-margin" interpagespace dc.interpagespace;
6044 oz "zoom" zoom dc.zoom;
6045 ob "presentation" presentation dc.presentation;
6046 oi "rotation-angle" c.angle dc.angle;
6047 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6048 ob "proportional-display" c.proportional dc.proportional;
6049 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6050 oi "tex-count" c.texcount dc.texcount;
6051 oi "slice-height" c.sliceheight dc.sliceheight;
6052 oi "thumbnail-width" c.thumbw dc.thumbw;
6053 ob "persistent-location" c.jumpback dc.jumpback;
6054 oc "background-color" c.bgcolor dc.bgcolor;
6055 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6056 oi "tile-width" c.tilew dc.tilew;
6057 oi "tile-height" c.tileh dc.tileh;
6058 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6059 ob "checkers" c.checkers dc.checkers;
6060 oi "aalevel" c.aalevel dc.aalevel;
6061 ob "trim-margins" c.trimmargins dc.trimmargins;
6062 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6063 os "uri-launcher" c.urilauncher dc.urilauncher;
6064 os "path-launcher" c.pathlauncher dc.pathlauncher;
6065 oC "color-space" c.colorspace dc.colorspace;
6066 ob "invert-colors" c.invert dc.invert;
6067 oF "brightness" c.colorscale dc.colorscale;
6068 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6069 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6070 oco "columns" c.columns dc.columns;
6071 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6072 os "selection-command" c.selcmd dc.selcmd;
6073 ob "update-cursor" c.updatecurs dc.updatecurs;
6076 let keymapsbuf always dc c =
6077 let bb = Buffer.create 16 in
6078 let rec loop = function
6079 | [] -> ()
6080 | (modename, h) :: rest ->
6081 let dh = findkeyhash dc modename in
6082 if always || h <> dh
6083 then (
6084 if Hashtbl.length h > 0
6085 then (
6086 if Buffer.length bb > 0
6087 then Buffer.add_char bb '\n';
6088 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6089 Hashtbl.iter (fun i o ->
6090 let isdifferent = always ||
6092 let dO = Hashtbl.find dh i in
6093 dO <> o
6094 with Not_found -> true
6096 if isdifferent
6097 then
6098 let addkm (k, m) =
6099 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6100 if Wsi.withalt m then Buffer.add_string bb "alt-";
6101 if Wsi.withshift m then Buffer.add_string bb "shift-";
6102 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6103 Buffer.add_string bb (Wsi.keyname k);
6105 let addkms l =
6106 let rec loop = function
6107 | [] -> ()
6108 | km :: [] -> addkm km
6109 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6111 loop l
6113 Buffer.add_string bb "<map in='";
6114 addkm i;
6115 match o with
6116 | KMinsrt km ->
6117 Buffer.add_string bb "' out='";
6118 addkm km;
6119 Buffer.add_string bb "'/>\n"
6121 | KMinsrl kms ->
6122 Buffer.add_string bb "' out='";
6123 addkms kms;
6124 Buffer.add_string bb "'/>\n"
6126 | KMmulti (ins, kms) ->
6127 Buffer.add_char bb ' ';
6128 addkms ins;
6129 Buffer.add_string bb "' out='";
6130 addkms kms;
6131 Buffer.add_string bb "'/>\n"
6132 ) h;
6133 Buffer.add_string bb "</keymap>";
6136 loop rest
6138 loop c.keyhashes;
6142 let save () =
6143 let uifontsize = fstate.fontsize in
6144 let bb = Buffer.create 32768 in
6145 let f (h, dc) =
6146 let dc = if conf.bedefault then conf else dc in
6147 Buffer.add_string bb "<llppconfig>\n";
6149 if String.length !fontpath > 0
6150 then
6151 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6152 uifontsize
6153 !fontpath
6154 else (
6155 if uifontsize <> 14
6156 then
6157 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6160 Buffer.add_string bb "<defaults ";
6161 add_attrs bb true dc dc;
6162 let kb = keymapsbuf true dc dc in
6163 if Buffer.length kb > 0
6164 then (
6165 Buffer.add_string bb ">\n";
6166 Buffer.add_buffer bb kb;
6167 Buffer.add_string bb "\n</defaults>\n";
6169 else Buffer.add_string bb "/>\n";
6171 let adddoc path pan anchor c bookmarks =
6172 if bookmarks == [] && c = dc && anchor = emptyanchor
6173 then ()
6174 else (
6175 Printf.bprintf bb "<doc path='%s'"
6176 (enent path 0 (String.length path));
6178 if anchor <> emptyanchor
6179 then (
6180 let n, y = anchor in
6181 Printf.bprintf bb " page='%d'" n;
6182 if y > 1e-6
6183 then
6184 Printf.bprintf bb " rely='%f'" y
6188 if pan != 0
6189 then Printf.bprintf bb " pan='%d'" pan;
6191 add_attrs bb false dc c;
6192 let kb = keymapsbuf false dc c in
6194 begin match bookmarks with
6195 | [] ->
6196 if Buffer.length kb > 0
6197 then (
6198 Buffer.add_string bb ">\n";
6199 Buffer.add_buffer bb kb;
6200 Buffer.add_string bb "</doc>\n";
6202 else Buffer.add_string bb "/>\n"
6203 | _ ->
6204 Buffer.add_string bb ">\n<bookmarks>\n";
6205 List.iter (fun (title, _level, (page, rely)) ->
6206 Printf.bprintf bb
6207 "<item title='%s' page='%d'"
6208 (enent title 0 (String.length title))
6209 page
6211 if rely > 1e-6
6212 then
6213 Printf.bprintf bb " rely='%f'" rely
6215 Buffer.add_string bb "/>\n";
6216 ) bookmarks;
6217 Buffer.add_string bb "</bookmarks>";
6218 if Buffer.length kb > 0
6219 then (
6220 Buffer.add_string bb "\n";
6221 Buffer.add_buffer bb kb;
6223 Buffer.add_string bb "\n</doc>\n";
6224 end;
6228 let pan, conf =
6229 match state.mode with
6230 | Birdseye (c, pan, _, _, _) ->
6231 let beyecolumns =
6232 match conf.columns with
6233 | Some ((c, _, _), _) -> Some c
6234 | None -> None
6235 and columns =
6236 match c.columns with
6237 | Some (c, _) -> Some (c, [||])
6238 | None -> None
6240 pan, { c with beyecolumns = beyecolumns; columns = columns }
6241 | _ -> state.x, conf
6243 let basename = Filename.basename state.path in
6244 adddoc basename pan (getanchor ())
6245 { conf with
6246 autoscrollstep =
6247 match state.autoscroll with
6248 | Some step -> step
6249 | None -> conf.autoscrollstep }
6250 (if conf.savebmarks then state.bookmarks else []);
6252 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
6253 if basename <> path
6254 then adddoc path x y c bookmarks
6255 ) h;
6256 Buffer.add_string bb "</llppconfig>";
6258 load1 f;
6259 if Buffer.length bb > 0
6260 then
6262 let tmp = !confpath ^ ".tmp" in
6263 let oc = open_out_bin tmp in
6264 Buffer.output_buffer oc bb;
6265 close_out oc;
6266 Unix.rename tmp !confpath;
6267 with exn ->
6268 prerr_endline
6269 ("error while saving configuration: " ^ Printexc.to_string exn)
6271 end;;
6273 let () =
6274 Arg.parse
6275 (Arg.align
6276 [("-p", Arg.String (fun s -> state.password <- s) ,
6277 "<password> Set password");
6279 ("-f", Arg.String (fun s -> Config.fontpath := s),
6280 "<path> Set path to the user interface font");
6282 ("-c", Arg.String (fun s -> Config.confpath := s),
6283 "<path> Set path to the configuration file");
6285 ("-v", Arg.Unit (fun () ->
6286 Printf.printf
6287 "%s\nconfiguration path: %s\n"
6288 (version ())
6289 Config.defconfpath
6291 exit 0), " Print version and exit");
6294 (fun s -> state.path <- s)
6295 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6297 if String.length state.path = 0
6298 then (prerr_endline "file name missing"; exit 1);
6300 Config.load ();
6302 let globalkeyhash = findkeyhash conf "global" in
6303 state.wsfd <- Wsi.init (object
6304 method display = display ()
6305 method reshape w h = reshape w h
6306 method mouse b d x y m = mouse b d x y m
6307 method motion x y = state.mpos <- (x, y); motion x y
6308 method pmotion x y = state.mpos <- (x, y); pmotion x y
6309 method key k m =
6310 match state.keystate with
6311 | KSnone ->
6312 let km = k, m in
6313 begin
6314 match
6315 try Hashtbl.find globalkeyhash km
6316 with Not_found ->
6317 let modehash = state.uioh#modehash in
6318 try Hashtbl.find modehash km
6319 with Not_found -> KMinsrt (k, m)
6320 with
6321 | KMinsrt (k, m) -> keyboard k m
6322 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6323 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6325 | KSinto ((k', m') :: [], insrt) when k'=k && m' land m = m' ->
6326 List.iter (fun (k, m) -> keyboard k m) insrt;
6327 state.keystate <- KSnone
6328 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land m = m' ->
6329 state.keystate <- KSinto (keys, insrt)
6330 | _ ->
6331 state.keystate <- KSnone
6333 method enter x y = state.mpos <- (x, y); pmotion x y
6334 method leave = state.mpos <- (-1, -1)
6335 method quit = raise Quit
6336 end) conf.winw conf.winh;
6338 if not (
6339 List.exists GlMisc.check_extension
6340 [ "GL_ARB_texture_rectangle"
6341 ; "GL_EXT_texture_recangle"
6342 ; "GL_NV_texture_rectangle" ]
6344 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
6346 let cr, sw = Unix.pipe ()
6347 and sr, cw = Unix.pipe () in
6349 cloexec cr;
6350 cloexec sw;
6351 cloexec sr;
6352 cloexec cw;
6354 setcheckers conf.checkers;
6355 redirectstderr ();
6357 init (cr, cw) (
6358 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
6359 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6360 !Config.fontpath
6362 state.sr <- sr;
6363 state.sw <- sw;
6364 state.text <- "Opening " ^ state.path;
6365 opendoc state.path state.password;
6366 state.uioh <- uioh;
6367 setfontsize fstate.fontsize;
6368 doreshape conf.winw conf.winh;
6370 let rec loop deadline =
6371 let r =
6372 match state.errfd with
6373 | None -> [state.sr; state.wsfd]
6374 | Some fd -> [state.sr; state.wsfd; fd]
6376 if state.redisplay
6377 then (
6378 state.redisplay <- false;
6379 display ();
6381 let timeout =
6382 let now = now () in
6383 if deadline > now
6384 then (
6385 if deadline = infinity
6386 then ~-.1.0
6387 else max 0.0 (deadline -. now)
6389 else 0.0
6391 let r, _, _ =
6392 try Unix.select r [] [] timeout
6393 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
6395 begin match r with
6396 | [] ->
6397 state.ghyll None;
6398 let newdeadline =
6399 if state.ghyll == noghyll
6400 then
6401 match state.autoscroll with
6402 | Some step when step != 0 ->
6403 let y = state.y + step in
6404 let y =
6405 if y < 0
6406 then state.maxy
6407 else if y >= state.maxy then 0 else y
6409 gotoy y;
6410 if state.mode = View
6411 then state.text <- "";
6412 deadline +. 0.01
6413 | _ -> infinity
6414 else deadline +. 0.01
6416 loop newdeadline
6418 | l ->
6419 let rec checkfds = function
6420 | [] -> ()
6421 | fd :: rest when fd = state.sr ->
6422 let cmd = readcmd state.sr in
6423 act cmd;
6424 checkfds rest
6426 | fd :: rest when fd = state.wsfd ->
6427 Wsi.readresp fd;
6428 checkfds rest
6430 | fd :: rest ->
6431 let s = String.create 80 in
6432 let n = Unix.read fd s 0 80 in
6433 if conf.redirectstderr
6434 then (
6435 Buffer.add_substring state.errmsgs s 0 n;
6436 state.newerrmsgs <- true;
6437 state.redisplay <- true;
6439 else (
6440 prerr_string (String.sub s 0 n);
6441 flush stderr;
6443 checkfds rest
6445 checkfds l;
6446 let newdeadline =
6447 let deadline1 =
6448 if deadline = infinity
6449 then now () +. 0.01
6450 else deadline
6452 match state.autoscroll with
6453 | Some step when step != 0 -> deadline1
6454 | _ -> if state.ghyll == noghyll then infinity else deadline1
6456 loop newdeadline
6457 end;
6460 loop infinity;
6461 with Quit ->
6462 wcmd "quit";
6463 Config.save ();
6464 exit 0;