Split view
[llpp.git] / main.ml
blobd1679ba48a5d5b2d3d7dd16d5328f7ffeec7444b
1 exception Quit;;
3 type under =
4 | Unone
5 | Ulinkuri of string
6 | Ulinkgoto of (int * int)
7 | Utext of facename
8 | Uunexpected of string
9 | Ulaunch of string
10 | Unamed of string
11 | Uremote of (string * int)
12 and facename = string;;
14 let dolog fmt = Printf.kprintf prerr_endline fmt;;
15 let now = Unix.gettimeofday;;
17 type params = (angle * proportional * trimparams
18 * texcount * sliceheight * memsize
19 * colorspace * fontpath)
20 and pageno = int
21 and width = int
22 and height = int
23 and leftx = int
24 and opaque = string
25 and recttype = int
26 and pixmapsize = int
27 and angle = int
28 and proportional = bool
29 and trimmargins = bool
30 and interpagespace = int
31 and texcount = int
32 and sliceheight = int
33 and gen = int
34 and top = float
35 and fontpath = string
36 and memsize = int
37 and aalevel = int
38 and irect = (int * int * int * int)
39 and trimparams = (trimmargins * irect)
40 and colorspace = | Rgb | Bgr | Gray
43 type link =
44 | Lnotfound
45 | Lfound of int
46 and linkdir =
47 | LDfirst
48 | LDlast
49 | LDfirstvisible of (int * int * int)
50 | LDleft of int
51 | LDright of int
52 | LDdown of int
53 | LDup of int
56 type pagewithlinks =
57 | Pwlnotfound
58 | Pwl of int
61 type keymap =
62 | KMinsrt of key
63 | KMinsrl of key list
64 | KMmulti of key list * key list
65 and key = int * int
66 and keyhash = (key, keymap) Hashtbl.t
67 and keystate =
68 | KSnone
69 | KSinto of (key list * key list)
72 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
73 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
75 type pipe = (Unix.file_descr * Unix.file_descr);;
77 external init : pipe -> params -> unit = "ml_init";;
78 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
79 external copysel : 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 -> int -> int -> int -> int -> int =
87 "ml_postprocess";;
88 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
89 external platform : unit -> platform = "ml_platform";;
90 external setaalevel : int -> unit = "ml_setaalevel";;
91 external realloctexts : int -> bool = "ml_realloctexts";;
92 external cloexec : Unix.file_descr -> unit = "ml_cloexec";;
93 external findlink : opaque -> linkdir -> link = "ml_findlink";;
94 external getlink : opaque -> int -> under = "ml_getlink";;
95 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
96 external getlinkcount : opaque -> int = "ml_getlinkcount";;
97 external findpwl: int -> int -> pagewithlinks = "ml_find_page_with_links"
99 let platform_to_string = function
100 | Punknown -> "unknown"
101 | Plinux -> "Linux"
102 | Posx -> "OSX"
103 | Psun -> "Sun"
104 | Pfreebsd -> "FreeBSD"
105 | Pdragonflybsd -> "DragonflyBSD"
106 | Popenbsd -> "OpenBSD"
107 | Pnetbsd -> "NetBSD"
108 | Pcygwin -> "Cygwin"
111 let platform = platform ();;
113 type x = int
114 and y = int
115 and tilex = int
116 and tiley = int
117 and tileparams = (x * y * width * height * tilex * tiley)
120 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
122 type mpos = int * int
123 and mstate =
124 | Msel of (mpos * mpos)
125 | Mpan of mpos
126 | Mscrolly | Mscrollx
127 | Mzoom of (int * int)
128 | Mzoomrect of (mpos * mpos)
129 | Mnone
132 type textentry = string * string * onhist option * onkey * ondone
133 and onkey = string -> int -> te
134 and ondone = string -> unit
135 and histcancel = unit -> unit
136 and onhist = ((histcmd -> string) * histcancel)
137 and histcmd = HCnext | HCprev | HCfirst | HClast
138 and te =
139 | TEstop
140 | TEdone of string
141 | TEcont of string
142 | TEswitch of textentry
145 type 'a circbuf =
146 { store : 'a array
147 ; mutable rc : int
148 ; mutable wc : int
149 ; mutable len : int
153 let bound v minv maxv =
154 max minv (min maxv v);
157 let cbnew n v =
158 { store = Array.create n v
159 ; rc = 0
160 ; wc = 0
161 ; len = 0
165 let drawstring size x y s =
166 Gl.enable `blend;
167 Gl.enable `texture_2d;
168 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
169 ignore (drawstr size x y s);
170 Gl.disable `blend;
171 Gl.disable `texture_2d;
174 let drawstring1 size x y s =
175 drawstr size x y s;
178 let drawstring2 size x y fmt =
179 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
182 let cbcap b = Array.length b.store;;
184 let cbput b v =
185 let cap = cbcap b in
186 b.store.(b.wc) <- v;
187 b.wc <- (b.wc + 1) mod cap;
188 b.rc <- b.wc;
189 b.len <- min (b.len + 1) cap;
192 let cbempty b = b.len = 0;;
194 let cbgetg b circular dir =
195 if cbempty b
196 then b.store.(0)
197 else
198 let rc = b.rc + dir in
199 let rc =
200 if circular
201 then (
202 if rc = -1
203 then b.len-1
204 else (
205 if rc = b.len
206 then 0
207 else rc
210 else max 0 (min rc (b.len-1))
212 b.rc <- rc;
213 b.store.(rc);
216 let cbget b = cbgetg b false;;
217 let cbgetc b = cbgetg b true;;
219 type page =
220 { pageno : int
221 ; pagedimno : int
222 ; pagew : int
223 ; pageh : int
224 ; pagex : int
225 ; pagey : int
226 ; pagevw : int
227 ; pagevh : int
228 ; pagedispx : int
229 ; pagedispy : int
233 let debugl l =
234 dolog "l %d dim=%d {" l.pageno l.pagedimno;
235 dolog " WxH %dx%d" l.pagew l.pageh;
236 dolog " vWxH %dx%d" l.pagevw l.pagevh;
237 dolog " pagex,y %d,%d" l.pagex l.pagey;
238 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
239 dolog "}";
242 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
243 dolog "rect {";
244 dolog " x0,y0=(% f, % f)" x0 y0;
245 dolog " x1,y1=(% f, % f)" x1 y1;
246 dolog " x2,y2=(% f, % f)" x2 y2;
247 dolog " x3,y3=(% f, % f)" x3 y3;
248 dolog "}";
251 type multicolumns = multicol * pagegeom
252 and splitcolumns = columncount * pagegeom
253 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
254 and multicol = columncount * covercount * covercount
255 and pdimno = int
256 and columncount = int
257 and covercount = int;;
259 type conf =
260 { mutable scrollbw : int
261 ; mutable scrollh : int
262 ; mutable icase : bool
263 ; mutable preload : bool
264 ; mutable pagebias : int
265 ; mutable verbose : bool
266 ; mutable debug : bool
267 ; mutable scrollstep : int
268 ; mutable maxhfit : bool
269 ; mutable crophack : bool
270 ; mutable autoscrollstep : int
271 ; mutable maxwait : float option
272 ; mutable hlinks : bool
273 ; mutable underinfo : bool
274 ; mutable interpagespace : interpagespace
275 ; mutable zoom : float
276 ; mutable presentation : bool
277 ; mutable angle : angle
278 ; mutable winw : int
279 ; mutable winh : int
280 ; mutable savebmarks : bool
281 ; mutable proportional : proportional
282 ; mutable trimmargins : trimmargins
283 ; mutable trimfuzz : irect
284 ; mutable memlimit : memsize
285 ; mutable texcount : texcount
286 ; mutable sliceheight : sliceheight
287 ; mutable thumbw : width
288 ; mutable jumpback : bool
289 ; mutable bgcolor : float * float * float
290 ; mutable bedefault : bool
291 ; mutable scrollbarinpm : bool
292 ; mutable tilew : int
293 ; mutable tileh : int
294 ; mutable mustoresize : memsize
295 ; mutable checkers : bool
296 ; mutable aalevel : int
297 ; mutable urilauncher : string
298 ; mutable pathlauncher : string
299 ; mutable colorspace : colorspace
300 ; mutable invert : bool
301 ; mutable colorscale : float
302 ; mutable redirectstderr : bool
303 ; mutable ghyllscroll : (int * int * int) option
304 ; mutable columns : columns
305 ; mutable beyecolumns : columncount option
306 ; mutable selcmd : string
307 ; mutable updatecurs : bool
308 ; mutable keyhashes : (string * keyhash) list
310 and columns =
311 | Csingle
312 | Cmulti of multicolumns
313 | Csplit of splitcolumns
316 type anchor = pageno * top;;
318 type outline = string * int * anchor;;
320 type rect = float * float * float * float * float * float * float * float;;
322 type tile = opaque * pixmapsize * elapsed
323 and elapsed = float;;
324 type pagemapkey = pageno * gen;;
325 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
326 and row = int
327 and col = int;;
329 let emptyanchor = (0, 0.0);;
331 type infochange = | Memused | Docinfo | Pdim;;
333 class type uioh = object
334 method display : unit
335 method key : int -> int -> uioh
336 method button : int -> bool -> int -> int -> int -> uioh
337 method motion : int -> int -> uioh
338 method pmotion : int -> int -> uioh
339 method infochanged : infochange -> unit
340 method scrollpw : (int * float * float)
341 method scrollph : (int * float * float)
342 method modehash : keyhash
343 end;;
345 type mode =
346 | Birdseye of (conf * leftx * pageno * pageno * anchor)
347 | Textentry of (textentry * onleave)
348 | View
349 | LinkNav of linktarget
350 and onleave = leavetextentrystatus -> unit
351 and leavetextentrystatus = | Cancel | Confirm
352 and helpitem = string * int * action
353 and action =
354 | Noaction
355 | Action of (uioh -> uioh)
356 and linktarget =
357 | Ltexact of (pageno * int)
358 | Ltgendir of int
361 let isbirdseye = function Birdseye _ -> true | _ -> false;;
362 let istextentry = function Textentry _ -> true | _ -> false;;
364 type currently =
365 | Idle
366 | Loading of (page * gen)
367 | Tiling of (
368 page * opaque * colorspace * angle * gen * col * row * width * height
370 | Outlining of outline list
373 let emptykeyhash = Hashtbl.create 0;;
374 let nouioh : uioh = object (self)
375 method display = ()
376 method key _ _ = self
377 method button _ _ _ _ _ = self
378 method motion _ _ = self
379 method pmotion _ _ = self
380 method infochanged _ = ()
381 method scrollpw = (0, nan, nan)
382 method scrollph = (0, nan, nan)
383 method modehash = emptykeyhash
384 end;;
386 type state =
387 { mutable sr : Unix.file_descr
388 ; mutable sw : Unix.file_descr
389 ; mutable wsfd : Unix.file_descr
390 ; mutable errfd : Unix.file_descr option
391 ; mutable stderr : Unix.file_descr
392 ; mutable errmsgs : Buffer.t
393 ; mutable newerrmsgs : bool
394 ; mutable w : int
395 ; mutable x : int
396 ; mutable y : int
397 ; mutable scrollw : int
398 ; mutable hscrollh : int
399 ; mutable anchor : anchor
400 ; mutable ranchors : (string * string * anchor) list
401 ; mutable maxy : int
402 ; mutable layout : page list
403 ; pagemap : (pagemapkey, opaque) Hashtbl.t
404 ; tilemap : (tilemapkey, tile) Hashtbl.t
405 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
406 ; mutable pdims : (pageno * width * height * leftx) list
407 ; mutable pagecount : int
408 ; mutable currently : currently
409 ; mutable mstate : mstate
410 ; mutable searchpattern : string
411 ; mutable rects : (pageno * recttype * rect) list
412 ; mutable rects1 : (pageno * recttype * rect) list
413 ; mutable text : string
414 ; mutable fullscreen : (width * height) option
415 ; mutable mode : mode
416 ; mutable uioh : uioh
417 ; mutable outlines : outline array
418 ; mutable bookmarks : outline list
419 ; mutable path : string
420 ; mutable password : string
421 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
422 ; mutable memused : memsize
423 ; mutable gen : gen
424 ; mutable throttle : (page list * int * float) option
425 ; mutable autoscroll : int option
426 ; mutable ghyll : (int option -> unit)
427 ; mutable help : helpitem array
428 ; mutable docinfo : (int * string) list
429 ; mutable texid : GlTex.texture_id option
430 ; hists : hists
431 ; mutable prevzoom : float
432 ; mutable progress : float
433 ; mutable redisplay : bool
434 ; mutable mpos : mpos
435 ; mutable keystate : keystate
436 ; mutable glinks : bool
438 and hists =
439 { pat : string circbuf
440 ; pag : string circbuf
441 ; nav : anchor circbuf
442 ; sel : string circbuf
446 let defconf =
447 { scrollbw = 7
448 ; scrollh = 12
449 ; icase = true
450 ; preload = true
451 ; pagebias = 0
452 ; verbose = false
453 ; debug = false
454 ; scrollstep = 24
455 ; maxhfit = true
456 ; crophack = false
457 ; autoscrollstep = 2
458 ; maxwait = None
459 ; hlinks = false
460 ; underinfo = false
461 ; interpagespace = 2
462 ; zoom = 1.0
463 ; presentation = false
464 ; angle = 0
465 ; winw = 900
466 ; winh = 900
467 ; savebmarks = true
468 ; proportional = true
469 ; trimmargins = false
470 ; trimfuzz = (0,0,0,0)
471 ; memlimit = 32 lsl 20
472 ; texcount = 256
473 ; sliceheight = 24
474 ; thumbw = 76
475 ; jumpback = true
476 ; bgcolor = (0.5, 0.5, 0.5)
477 ; bedefault = false
478 ; scrollbarinpm = true
479 ; tilew = 2048
480 ; tileh = 2048
481 ; mustoresize = 128 lsl 20
482 ; checkers = true
483 ; aalevel = 8
484 ; urilauncher =
485 (match platform with
486 | Plinux | Pfreebsd | Pdragonflybsd
487 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
488 | Posx -> "open \"%s\""
489 | Pcygwin -> "cygstart %s"
490 | Punknown -> "echo %s")
491 ; pathlauncher = "lp \"%s\""
492 ; selcmd =
493 (match platform with
494 | Plinux | Pfreebsd | Pdragonflybsd
495 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
496 | Posx -> "pbcopy"
497 | Pcygwin -> "wsel"
498 | Punknown -> "cat")
499 ; colorspace = Rgb
500 ; invert = false
501 ; colorscale = 1.0
502 ; redirectstderr = false
503 ; ghyllscroll = None
504 ; columns = Csingle
505 ; beyecolumns = None
506 ; updatecurs = false
507 ; keyhashes =
508 let mk n = (n, Hashtbl.create 1) in
509 [ mk "global"
510 ; mk "info"
511 ; mk "help"
512 ; mk "outline"
513 ; mk "listview"
514 ; mk "birdseye"
515 ; mk "textentry"
516 ; mk "links"
521 let findkeyhash c name =
522 try List.assoc name c.keyhashes
523 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
526 let conf = { defconf with angle = defconf.angle };;
528 type fontstate =
529 { mutable fontsize : int
530 ; mutable wwidth : float
531 ; mutable maxrows : int
535 let fstate =
536 { fontsize = 14
537 ; wwidth = nan
538 ; maxrows = -1
542 let setfontsize n =
543 fstate.fontsize <- n;
544 fstate.wwidth <- measurestr fstate.fontsize "w";
545 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
548 let geturl s =
549 let colonpos = try String.index s ':' with Not_found -> -1 in
550 let len = String.length s in
551 if colonpos >= 0 && colonpos + 3 < len
552 then (
553 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
554 then
555 let schemestartpos =
556 try String.rindex_from s colonpos ' '
557 with Not_found -> -1
559 let scheme =
560 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
562 match scheme with
563 | "http" | "ftp" | "mailto" ->
564 let epos =
565 try String.index_from s colonpos ' '
566 with Not_found -> len
568 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
569 | _ -> ""
570 else ""
572 else ""
575 let popen =
576 let shell, farg = "/bin/sh", "-c" in
577 fun s ->
578 let args = [|shell; farg; s|] in
579 ignore (Unix.create_process shell args Unix.stdin Unix.stdout Unix.stderr)
582 let gotouri uri =
583 if String.length conf.urilauncher = 0
584 then print_endline uri
585 else (
586 let url = geturl uri in
587 if String.length url = 0
588 then print_endline uri
589 else
590 let re = Str.regexp "%s" in
591 let command = Str.global_replace re url conf.urilauncher in
592 try popen command
593 with exn ->
594 Printf.eprintf
595 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
596 flush stderr;
600 let version () =
601 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
602 (platform_to_string platform) Sys.word_size Sys.ocaml_version
605 let makehelp () =
606 let strings = version () :: "" :: Help.keys in
607 Array.of_list (
608 List.map (fun s ->
609 let url = geturl s in
610 if String.length url > 0
611 then (s, 0, Action (fun u -> gotouri url; u))
612 else (s, 0, Noaction)
613 ) strings);
616 let noghyll _ = ();;
617 let firstgeomcmds = "", [];;
619 let state =
620 { sr = Unix.stdin
621 ; sw = Unix.stdin
622 ; wsfd = Unix.stdin
623 ; errfd = None
624 ; stderr = Unix.stderr
625 ; errmsgs = Buffer.create 0
626 ; newerrmsgs = false
627 ; x = 0
628 ; y = 0
629 ; w = 0
630 ; scrollw = 0
631 ; hscrollh = 0
632 ; anchor = emptyanchor
633 ; ranchors = []
634 ; layout = []
635 ; maxy = max_int
636 ; tilelru = Queue.create ()
637 ; pagemap = Hashtbl.create 10
638 ; tilemap = Hashtbl.create 10
639 ; pdims = []
640 ; pagecount = 0
641 ; currently = Idle
642 ; mstate = Mnone
643 ; rects = []
644 ; rects1 = []
645 ; text = ""
646 ; mode = View
647 ; fullscreen = None
648 ; searchpattern = ""
649 ; outlines = [||]
650 ; bookmarks = []
651 ; path = ""
652 ; password = ""
653 ; geomcmds = firstgeomcmds
654 ; hists =
655 { nav = cbnew 10 (0, 0.0)
656 ; pat = cbnew 10 ""
657 ; pag = cbnew 10 ""
658 ; sel = cbnew 10 ""
660 ; memused = 0
661 ; gen = 0
662 ; throttle = None
663 ; autoscroll = None
664 ; ghyll = noghyll
665 ; help = makehelp ()
666 ; docinfo = []
667 ; texid = None
668 ; prevzoom = 1.0
669 ; progress = -1.0
670 ; uioh = nouioh
671 ; redisplay = true
672 ; mpos = (-1, -1)
673 ; keystate = KSnone
674 ; glinks = false
678 let vlog fmt =
679 if conf.verbose
680 then
681 Printf.kprintf prerr_endline fmt
682 else
683 Printf.kprintf ignore fmt
686 let launchpath () =
687 if String.length conf.pathlauncher = 0
688 then print_endline state.path
689 else (
690 let re = Str.regexp "%s" in
691 let command = Str.global_replace re state.path conf.pathlauncher in
692 try popen command
693 with exn ->
694 Printf.eprintf
695 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
696 flush stderr;
700 let redirectstderr () =
701 if conf.redirectstderr
702 then
703 let rfd, wfd = Unix.pipe () in
704 state.stderr <- Unix.dup Unix.stderr;
705 state.errfd <- Some rfd;
706 Unix.dup2 wfd Unix.stderr;
707 else (
708 state.newerrmsgs <- false;
709 begin match state.errfd with
710 | Some fd ->
711 Unix.close fd;
712 Unix.dup2 state.stderr Unix.stderr;
713 state.errfd <- None;
714 | None -> ()
715 end;
716 prerr_string (Buffer.contents state.errmsgs);
717 flush stderr;
718 Buffer.clear state.errmsgs;
722 module G =
723 struct
724 let postRedisplay who =
725 if conf.verbose
726 then prerr_endline ("redisplay for " ^ who);
727 state.redisplay <- true;
729 end;;
731 let getopaque pageno =
732 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
733 with Not_found -> None
736 let putopaque pageno opaque =
737 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
740 let pagetranslatepoint l x y =
741 let dy = y - l.pagedispy in
742 let y = dy + l.pagey in
743 let dx = x - l.pagedispx in
744 let x = dx + l.pagex in
745 (x, y);
748 let getunder x y =
749 let rec f = function
750 | l :: rest ->
751 begin match getopaque l.pageno with
752 | Some opaque ->
753 let x0 = l.pagedispx in
754 let x1 = x0 + l.pagevw in
755 let y0 = l.pagedispy in
756 let y1 = y0 + l.pagevh in
757 if y >= y0 && y <= y1 && x >= x0 && x <= x1
758 then
759 let px, py = pagetranslatepoint l x y in
760 match whatsunder opaque px py with
761 | Unone -> f rest
762 | under -> under
763 else f rest
764 | _ ->
765 f rest
767 | [] -> Unone
769 f state.layout
772 let showtext c s =
773 state.text <- Printf.sprintf "%c%s" c s;
774 G.postRedisplay "showtext";
777 let updateunder x y =
778 match getunder x y with
779 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
780 | Ulinkuri uri ->
781 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
782 Wsi.setcursor Wsi.CURSOR_INFO
783 | Ulinkgoto (page, _) ->
784 if conf.underinfo
785 then showtext 'p' ("age: " ^ string_of_int (page+1));
786 Wsi.setcursor Wsi.CURSOR_INFO
787 | Utext s ->
788 if conf.underinfo then showtext 'f' ("ont: " ^ s);
789 Wsi.setcursor Wsi.CURSOR_TEXT
790 | Uunexpected s ->
791 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
792 Wsi.setcursor Wsi.CURSOR_INHERIT
793 | Ulaunch s ->
794 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
795 Wsi.setcursor Wsi.CURSOR_INHERIT
796 | Unamed s ->
797 if conf.underinfo then showtext 'n' ("amed: " ^ s);
798 Wsi.setcursor Wsi.CURSOR_INHERIT
799 | Uremote (filename, pageno) ->
800 if conf.underinfo then showtext 'r'
801 (Printf.sprintf "emote: %s (%d)" filename pageno);
802 Wsi.setcursor Wsi.CURSOR_INFO
805 let showlinktype under =
806 if conf.underinfo
807 then
808 match under with
809 | Unone -> ()
810 | Ulinkuri uri ->
811 showtext 'u' ("ri: " ^ uri)
812 | Ulinkgoto (page, _) ->
813 showtext 'p' ("age: " ^ string_of_int (page+1));
814 | Utext s ->
815 showtext 'f' ("ont: " ^ s);
816 | Uunexpected s ->
817 showtext 'u' ("nexpected: " ^ s);
818 | Ulaunch s ->
819 showtext 'l' ("aunch: " ^ s);
820 | Unamed s ->
821 showtext 'n' ("amed: " ^ s);
822 | Uremote (filename, pageno) ->
823 showtext 'r' (Printf.sprintf "emote: %s (%d)" filename pageno);
826 let addchar s c =
827 let b = Buffer.create (String.length s + 1) in
828 Buffer.add_string b s;
829 Buffer.add_char b c;
830 Buffer.contents b;
833 let colorspace_of_string s =
834 match String.lowercase s with
835 | "rgb" -> Rgb
836 | "bgr" -> Bgr
837 | "gray" -> Gray
838 | _ -> failwith "invalid colorspace"
841 let int_of_colorspace = function
842 | Rgb -> 0
843 | Bgr -> 1
844 | Gray -> 2
847 let colorspace_of_int = function
848 | 0 -> Rgb
849 | 1 -> Bgr
850 | 2 -> Gray
851 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
854 let colorspace_to_string = function
855 | Rgb -> "rgb"
856 | Bgr -> "bgr"
857 | Gray -> "gray"
860 let intentry_with_suffix text key =
861 let c =
862 if key >= 32 && key < 127
863 then Char.chr key
864 else '\000'
866 match Char.lowercase c with
867 | '0' .. '9' ->
868 let text = addchar text c in
869 TEcont text
871 | 'k' | 'm' | 'g' ->
872 let text = addchar text c in
873 TEcont text
875 | _ ->
876 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
877 TEcont text
880 let multicolumns_to_string (n, a, b) =
881 if a = 0 && b = 0
882 then Printf.sprintf "%d" n
883 else Printf.sprintf "%d,%d,%d" n a b;
886 let multicolumns_of_string s =
888 (int_of_string s, 0, 0)
889 with _ ->
890 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
893 let readcmd fd =
894 let s = "xxxx" in
895 let n = Unix.read fd s 0 4 in
896 if n != 4 then failwith "incomplete read(len)";
897 let len = 0
898 lor (Char.code s.[0] lsl 24)
899 lor (Char.code s.[1] lsl 16)
900 lor (Char.code s.[2] lsl 8)
901 lor (Char.code s.[3] lsl 0)
903 let s = String.create len in
904 let n = Unix.read fd s 0 len in
905 if n != len then failwith "incomplete read(data)";
909 let btod b = if b then 1 else 0;;
911 let wcmd fmt =
912 let b = Buffer.create 16 in
913 Buffer.add_string b "llll";
914 Printf.kbprintf
915 (fun b ->
916 let s = Buffer.contents b in
917 let n = String.length s in
918 let len = n - 4 in
919 (* dolog "wcmd %S" (String.sub s 4 len); *)
920 s.[0] <- Char.chr ((len lsr 24) land 0xff);
921 s.[1] <- Char.chr ((len lsr 16) land 0xff);
922 s.[2] <- Char.chr ((len lsr 8) land 0xff);
923 s.[3] <- Char.chr (len land 0xff);
924 let n' = Unix.write state.sw s 0 n in
925 if n' != n then failwith "write failed";
926 ) b fmt;
929 let calcips h =
930 if conf.presentation
931 then
932 let d = conf.winh - h in
933 max 0 ((d + 1) / 2)
934 else
935 conf.interpagespace
938 let calcheight () =
939 let rec f pn ph pi fh l =
940 match l with
941 | (n, _, h, _) :: rest ->
942 let ips = calcips h in
943 let fh =
944 if conf.presentation
945 then fh+ips
946 else (
947 if isbirdseye state.mode && pn = 0
948 then fh + ips
949 else fh
952 let fh = fh + ((n - pn) * (ph + pi)) in
953 f n h ips fh rest;
955 | [] ->
956 let inc =
957 if conf.presentation || (isbirdseye state.mode && pn = 0)
958 then 0
959 else -pi
961 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
962 max 0 fh
964 let fh = f 0 0 0 0 state.pdims in
968 let calcheight () =
969 match conf.columns with
970 | Csingle -> calcheight ()
971 | Cmulti (_, b) ->
972 if Array.length b > 0
973 then
974 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
975 y + h
976 else 0
977 | Csplit (_, b) ->
978 if Array.length b > 0
979 then
980 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
981 y + h
982 else 0
985 let getpageyh pageno =
986 let rec f pn ph pi y l =
987 match l with
988 | (n, _, h, _) :: rest ->
989 let ips = calcips h in
990 if n >= pageno
991 then
992 let h = if n = pageno then h else ph in
993 if conf.presentation && n = pageno
994 then
995 y + (pageno - pn) * (ph + pi) + pi, h
996 else
997 y + (pageno - pn) * (ph + pi), h
998 else
999 let y = y + (if conf.presentation then pi else 0) in
1000 let y = y + (n - pn) * (ph + pi) in
1001 f n h ips y rest
1003 | [] ->
1004 y + (pageno - pn) * (ph + pi), ph
1006 f 0 0 0 0 state.pdims
1009 let getpageyh pageno =
1010 match conf.columns with
1011 | Csingle -> getpageyh pageno
1012 | Cmulti (_, b) ->
1013 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1014 y, h
1015 | Csplit _ -> getpageyh pageno
1018 let getpagedim pageno =
1019 let rec f ppdim l =
1020 match l with
1021 | (n, _, _, _) as pdim :: rest ->
1022 if n >= pageno
1023 then (if n = pageno then pdim else ppdim)
1024 else f pdim rest
1026 | [] -> ppdim
1028 f (-1, -1, -1, -1) state.pdims
1031 let getpagey pageno = fst (getpageyh pageno);;
1033 let nogeomcmds cmds =
1034 match cmds with
1035 | s, [] -> String.length s = 0
1036 | _ -> false
1039 let layout1 y sh =
1040 let sh = sh - state.hscrollh in
1041 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
1042 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
1043 match pdims with
1044 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
1045 let ips = calcips h in
1046 let yinc =
1047 if conf.presentation || (isbirdseye state.mode && pageno = 0)
1048 then ips
1049 else 0
1051 (w, h, ips, xoff), rest, pdimno + 1, yinc
1052 | _ ->
1053 prev, pdims, pdimno, 0
1055 let dy = dy + yinc in
1056 let py = py + yinc in
1057 if pageno = state.pagecount || dy >= sh
1058 then
1059 accu
1060 else
1061 let vy = y + dy in
1062 if py + h <= vy - yinc
1063 then
1064 let py = py + h + ips in
1065 let dy = max 0 (py - y) in
1066 f ~pageno:(pageno+1)
1067 ~pdimno
1068 ~prev:curr
1071 ~pdims:rest
1072 ~accu
1073 else
1074 let pagey = vy - py in
1075 let pagevh = h - pagey in
1076 let pagevh = min (sh - dy) pagevh in
1077 let off = if yinc > 0 then py - vy else 0 in
1078 let py = py + h + ips in
1079 let pagex, dx =
1080 let xoff = xoff +
1081 if state.w < conf.winw - state.scrollw
1082 then (conf.winw - state.scrollw - state.w) / 2
1083 else 0
1085 let dispx = xoff + state.x in
1086 if dispx < 0
1087 then (-dispx, 0)
1088 else (0, dispx)
1090 let pagevw =
1091 let lw = w - pagex in
1092 min lw (conf.winw - state.scrollw)
1094 let e =
1095 { pageno = pageno
1096 ; pagedimno = pdimno
1097 ; pagew = w
1098 ; pageh = h
1099 ; pagex = pagex
1100 ; pagey = pagey + off
1101 ; pagevw = pagevw
1102 ; pagevh = pagevh - off
1103 ; pagedispx = dx
1104 ; pagedispy = dy + off
1107 let accu = e :: accu in
1108 f ~pageno:(pageno+1)
1109 ~pdimno
1110 ~prev:curr
1112 ~dy:(dy+pagevh+ips)
1113 ~pdims:rest
1114 ~accu
1116 let accu =
1118 ~pageno:0
1119 ~pdimno:~-1
1120 ~prev:(0,0,0,0)
1121 ~py:0
1122 ~dy:0
1123 ~pdims:state.pdims
1124 ~accu:[]
1126 List.rev accu
1129 let layoutN ((columns, coverA, coverB), b) y sh =
1130 let sh = sh - state.hscrollh in
1131 let rec fold accu n =
1132 if n = Array.length b
1133 then accu
1134 else
1135 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1136 if (vy - y) > sh &&
1137 (n = coverA - 1
1138 || n = state.pagecount - coverB
1139 || (n - coverA) mod columns = columns - 1)
1140 then accu
1141 else
1142 let accu =
1143 if vy + h > y
1144 then
1145 let pagey = max 0 (y - vy) in
1146 let pagedispy = if pagey > 0 then 0 else vy - y in
1147 let pagedispx, pagex, pagevw =
1148 let pdx =
1149 if n = coverA - 1 || n = state.pagecount - coverB
1150 then state.x + (conf.winw - state.scrollw - w) / 2
1151 else dx + xoff + state.x
1153 if pdx < 0
1154 then 0, -pdx, w + pdx
1155 else pdx, 0, min (conf.winw - state.scrollw) w
1157 let pagevh = min (h - pagey) (sh - pagedispy) in
1158 if pagedispx < conf.winw - state.scrollw && pagevw > 0 && pagevh > 0
1159 then
1160 let e =
1161 { pageno = n
1162 ; pagedimno = pdimno
1163 ; pagew = w
1164 ; pageh = h
1165 ; pagex = pagex
1166 ; pagey = pagey
1167 ; pagevw = pagevw
1168 ; pagevh = pagevh
1169 ; pagedispx = pagedispx
1170 ; pagedispy = pagedispy
1173 e :: accu
1174 else
1175 accu
1176 else
1177 accu
1179 fold accu (n+1)
1181 List.rev (fold [] 0)
1184 let layoutS (columns, b) y sh =
1185 let sh = sh - state.hscrollh in
1186 let rec fold accu n =
1187 if n = Array.length b
1188 then accu
1189 else
1190 let pdimno, px, vy, (_, pw, h, xoff) = b.(n) in
1191 if (vy - y) > sh
1192 then accu
1193 else
1194 let accu =
1195 if vy + h > y
1196 then
1197 let x = xoff + state.x in
1198 let pagey = max 0 (y - vy) in
1199 let pagedispy = if pagey > 0 then 0 else vy - y in
1200 let pagedispx, pagex =
1201 if x < 0
1202 then 0, px - x
1203 else x, px
1205 let w = pw/columns in
1206 let pagevw = min (w - (pagex mod w)) (conf.winw - state.scrollw) in
1207 let pagevh = min (h - pagey) (sh - pagedispy) in
1208 if pagevw > 0 && pagevh > 0
1209 then
1210 let e =
1211 { pageno = n/columns
1212 ; pagedimno = pdimno
1213 ; pagew = pw
1214 ; pageh = h
1215 ; pagex = pagex
1216 ; pagey = pagey
1217 ; pagevw = pagevw
1218 ; pagevh = pagevh
1219 ; pagedispx = pagedispx
1220 ; pagedispy = pagedispy
1223 e :: accu
1224 else
1225 accu
1226 else
1227 accu
1229 fold accu (n+1)
1231 List.rev (fold [] 0)
1234 let layout y sh =
1235 if nogeomcmds state.geomcmds
1236 then
1237 match conf.columns with
1238 | Csingle -> layout1 y sh
1239 | Cmulti c -> layoutN c y sh
1240 | Csplit s -> layoutS s y sh
1241 else []
1244 let clamp incr =
1245 let y = state.y + incr in
1246 let y = max 0 y in
1247 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1251 let itertiles l f =
1252 let tilex = l.pagex mod conf.tilew in
1253 let tiley = l.pagey mod conf.tileh in
1255 let col = l.pagex / conf.tilew in
1256 let row = l.pagey / conf.tileh in
1258 let vw =
1259 let a = l.pagew - l.pagex in
1260 let b = conf.winw - state.scrollw in
1261 min a b
1262 and vh = l.pagevh in
1264 let rec rowloop row y0 dispy h =
1265 if h = 0
1266 then ()
1267 else (
1268 let dh = conf.tileh - y0 in
1269 let dh = min h dh in
1270 let rec colloop col x0 dispx w =
1271 if w = 0
1272 then ()
1273 else (
1274 let dw = conf.tilew - x0 in
1275 let dw = min w dw in
1277 f col row dispx dispy x0 y0 dw dh;
1278 colloop (col+1) 0 (dispx+dw) (w-dw)
1281 colloop col tilex l.pagedispx vw;
1282 rowloop (row+1) 0 (dispy+dh) (h-dh)
1285 if vw > 0 && vh > 0
1286 then rowloop row tiley l.pagedispy vh;
1289 let gettileopaque l col row =
1290 let key =
1291 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1293 try Some (Hashtbl.find state.tilemap key)
1294 with Not_found -> None
1297 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1298 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1299 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1302 let drawtiles l color =
1303 GlDraw.color color;
1304 let f col row x y tilex tiley w h =
1305 match gettileopaque l col row with
1306 | Some (opaque, _, t) ->
1307 let params = x, y, w, h, tilex, tiley in
1308 if conf.invert
1309 then (
1310 Gl.enable `blend;
1311 GlFunc.blend_func `zero `one_minus_src_color;
1313 drawtile params opaque;
1314 if conf.invert
1315 then Gl.disable `blend;
1316 if conf.debug
1317 then (
1318 let s = Printf.sprintf
1319 "%d[%d,%d] %f sec"
1320 l.pageno col row t
1322 let w = measurestr fstate.fontsize s in
1323 GlMisc.push_attrib [`current];
1324 GlDraw.color (0.0, 0.0, 0.0);
1325 GlDraw.rect
1326 (float (x-2), float (y-2))
1327 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1328 GlDraw.color (1.0, 1.0, 1.0);
1329 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1330 GlMisc.pop_attrib ();
1333 | _ ->
1334 let w =
1335 let lw = conf.winw - state.scrollw - x in
1336 min lw w
1337 and h =
1338 let lh = conf.winh - y in
1339 min lh h
1341 Gl.enable `texture_2d;
1342 begin match state.texid with
1343 | Some id ->
1344 GlTex.bind_texture `texture_2d id;
1345 let x0 = float x
1346 and y0 = float y
1347 and x1 = float (x+w)
1348 and y1 = float (y+h) in
1350 let tw = float w /. 64.0
1351 and th = float h /. 64.0 in
1352 let tx0 = float tilex /. 64.0
1353 and ty0 = float tiley /. 64.0 in
1354 let tx1 = tx0 +. tw
1355 and ty1 = ty0 +. th in
1356 GlDraw.begins `quads;
1357 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1358 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1359 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1360 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1361 GlDraw.ends ();
1363 Gl.disable `texture_2d;
1364 | None ->
1365 GlDraw.color (1.0, 1.0, 1.0);
1366 GlDraw.rect
1367 (float x, float y)
1368 (float (x+w), float (y+h));
1369 end;
1370 if w > 128 && h > fstate.fontsize + 10
1371 then (
1372 GlDraw.color (0.0, 0.0, 0.0);
1373 let c, r =
1374 if conf.verbose
1375 then (col*conf.tilew, row*conf.tileh)
1376 else col, row
1378 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1380 GlDraw.color color;
1382 itertiles l f
1385 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1387 let tilevisible1 l x y =
1388 let ax0 = l.pagex
1389 and ax1 = l.pagex + l.pagevw
1390 and ay0 = l.pagey
1391 and ay1 = l.pagey + l.pagevh in
1393 let bx0 = x
1394 and by0 = y in
1395 let bx1 = min (bx0 + conf.tilew) l.pagew
1396 and by1 = min (by0 + conf.tileh) l.pageh in
1398 let rx0 = max ax0 bx0
1399 and ry0 = max ay0 by0
1400 and rx1 = min ax1 bx1
1401 and ry1 = min ay1 by1 in
1403 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1404 nonemptyintersection
1407 let tilevisible layout n x y =
1408 let rec findpageinlayout = function
1409 | l :: _ when l.pageno = n -> tilevisible1 l x y
1410 | _ :: rest -> findpageinlayout rest
1411 | [] -> false
1413 findpageinlayout layout
1416 let tileready l x y =
1417 tilevisible1 l x y &&
1418 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1421 let tilepage n p layout =
1422 let rec loop = function
1423 | l :: rest ->
1424 if l.pageno = n
1425 then
1426 let f col row _ _ _ _ _ _ =
1427 if state.currently = Idle
1428 then
1429 match gettileopaque l col row with
1430 | Some _ -> ()
1431 | None ->
1432 let x = col*conf.tilew
1433 and y = row*conf.tileh in
1434 let w =
1435 let w = l.pagew - x in
1436 min w conf.tilew
1438 let h =
1439 let h = l.pageh - y in
1440 min h conf.tileh
1442 wcmd "tile %s %d %d %d %d" p x y w h;
1443 state.currently <-
1444 Tiling (
1445 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1446 conf.tilew, conf.tileh
1449 itertiles l f;
1450 else
1451 loop rest
1453 | [] -> ()
1455 if nogeomcmds state.geomcmds
1456 then loop layout;
1459 let preloadlayout visiblepages =
1460 let presentation = conf.presentation in
1461 let interpagespace = conf.interpagespace in
1462 let maxy = state.maxy in
1463 conf.presentation <- false;
1464 conf.interpagespace <- 0;
1465 state.maxy <- calcheight ();
1466 let y =
1467 match visiblepages with
1468 | [] -> 0
1469 | l :: _ -> getpagey l.pageno + l.pagey
1471 let y = if y < conf.winh then 0 else y - conf.winh in
1472 let h = state.y - y + conf.winh*3 in
1473 let pages = layout y h in
1474 conf.presentation <- presentation;
1475 conf.interpagespace <- interpagespace;
1476 state.maxy <- maxy;
1477 pages;
1480 let load pages =
1481 let rec loop pages =
1482 if state.currently != Idle
1483 then ()
1484 else
1485 match pages with
1486 | l :: rest ->
1487 begin match getopaque l.pageno with
1488 | None ->
1489 wcmd "page %d %d" l.pageno l.pagedimno;
1490 state.currently <- Loading (l, state.gen);
1491 | Some opaque ->
1492 tilepage l.pageno opaque pages;
1493 loop rest
1494 end;
1495 | _ -> ()
1497 if nogeomcmds state.geomcmds
1498 then loop pages
1501 let preload pages =
1502 load pages;
1503 if conf.preload && state.currently = Idle
1504 then load (preloadlayout pages);
1507 let layoutready layout =
1508 let rec fold all ls =
1509 all && match ls with
1510 | l :: rest ->
1511 let seen = ref false in
1512 let allvisible = ref true in
1513 let foo col row _ _ _ _ _ _ =
1514 seen := true;
1515 allvisible := !allvisible &&
1516 begin match gettileopaque l col row with
1517 | Some _ -> true
1518 | None -> false
1521 itertiles l foo;
1522 fold (!seen && !allvisible) rest
1523 | [] -> true
1525 let alltilesvisible = fold true layout in
1526 alltilesvisible;
1529 let gotoy y =
1530 let y = bound y 0 state.maxy in
1531 let y, layout, proceed =
1532 match conf.maxwait with
1533 | Some time when state.ghyll == noghyll ->
1534 begin match state.throttle with
1535 | None ->
1536 let layout = layout y conf.winh in
1537 let ready = layoutready layout in
1538 if not ready
1539 then (
1540 load layout;
1541 state.throttle <- Some (layout, y, now ());
1543 else G.postRedisplay "gotoy showall (None)";
1544 y, layout, ready
1545 | Some (_, _, started) ->
1546 let dt = now () -. started in
1547 if dt > time
1548 then (
1549 state.throttle <- None;
1550 let layout = layout y conf.winh in
1551 load layout;
1552 G.postRedisplay "maxwait";
1553 y, layout, true
1555 else -1, [], false
1558 | _ ->
1559 let layout = layout y conf.winh in
1560 if true || layoutready layout
1561 then G.postRedisplay "gotoy ready";
1562 y, layout, true
1564 if proceed
1565 then (
1566 state.y <- y;
1567 state.layout <- layout;
1568 begin match state.mode with
1569 | LinkNav (Ltexact (pageno, linkno)) ->
1570 let rec loop = function
1571 | [] ->
1572 state.mode <- LinkNav (Ltgendir 0)
1573 | l :: _ when l.pageno = pageno ->
1574 begin match getopaque pageno with
1575 | None ->
1576 state.mode <- LinkNav (Ltgendir 0)
1577 | Some opaque ->
1578 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1579 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1580 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1581 then state.mode <- LinkNav (Ltgendir 0)
1583 | _ :: rest -> loop rest
1585 loop layout
1586 | _ -> ()
1587 end;
1588 begin match state.mode with
1589 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1590 if not (pagevisible layout pageno)
1591 then (
1592 match state.layout with
1593 | [] -> ()
1594 | l :: _ ->
1595 state.mode <- Birdseye (
1596 conf, leftx, l.pageno, hooverpageno, anchor
1599 | LinkNav (Ltgendir dir as lt) ->
1600 let linknav =
1601 let rec loop = function
1602 | [] -> lt
1603 | l :: rest ->
1604 match getopaque l.pageno with
1605 | None -> loop rest
1606 | Some opaque ->
1607 let link =
1608 let ld =
1609 if dir = 0
1610 then LDfirstvisible (l.pagex, l.pagey, dir)
1611 else (
1612 if dir > 0 then LDfirst else LDlast
1615 findlink opaque ld
1617 match link with
1618 | Lnotfound -> loop rest
1619 | Lfound n ->
1620 showlinktype (getlink opaque n);
1621 Ltexact (l.pageno, n)
1623 loop state.layout
1625 state.mode <- LinkNav linknav
1626 | _ -> ()
1627 end;
1628 preload layout;
1630 state.ghyll <- noghyll;
1631 if conf.updatecurs
1632 then (
1633 let mx, my = state.mpos in
1634 updateunder mx my;
1638 let conttiling pageno opaque =
1639 tilepage pageno opaque
1640 (if conf.preload then preloadlayout state.layout else state.layout)
1643 let gotoy_and_clear_text y =
1644 if not conf.verbose then state.text <- "";
1645 gotoy y;
1648 let getanchor () =
1649 match state.layout with
1650 | [] -> emptyanchor
1651 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1654 let getanchory (n, top) =
1655 let y, h = getpageyh n in
1656 y + (truncate (top *. float h));
1659 let gotoanchor anchor =
1660 gotoy (getanchory anchor);
1663 let addnav () =
1664 cbput state.hists.nav (getanchor ());
1667 let getnav dir =
1668 let anchor = cbgetc state.hists.nav dir in
1669 getanchory anchor;
1672 let gotoghyll y =
1673 let rec scroll f n a b =
1674 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1675 let snake f a b =
1676 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1677 if f < a
1678 then s (float f /. float a)
1679 else (
1680 if f > b
1681 then 1.0 -. s ((float (f-b) /. float (n-b)))
1682 else 1.0
1685 snake f a b
1686 and summa f n a b =
1687 (* courtesy:
1688 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1689 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1690 let iv1 = iv f in
1691 let ins = float a *. iv1
1692 and outs = float (n-b) *. iv1 in
1693 let ones = b - a in
1694 ins +. outs +. float ones
1696 let rec set (_N, _A, _B) y sy =
1697 let sum = summa 1.0 _N _A _B in
1698 let dy = float (y - sy) in
1699 state.ghyll <- (
1700 let rec gf n y1 o =
1701 if n >= _N
1702 then state.ghyll <- noghyll
1703 else
1704 let go n =
1705 let s = scroll n _N _A _B in
1706 let y1 = y1 +. ((s *. dy) /. sum) in
1707 gotoy_and_clear_text (truncate y1);
1708 state.ghyll <- gf (n+1) y1;
1710 match o with
1711 | None -> go n
1712 | Some y' -> set (_N/2, 0, 0) y' state.y
1714 gf 0 (float state.y)
1717 match conf.ghyllscroll with
1718 | None ->
1719 gotoy_and_clear_text y
1720 | Some nab ->
1721 if state.ghyll == noghyll
1722 then set nab y state.y
1723 else state.ghyll (Some y)
1726 let gotopage n top =
1727 let y, h = getpageyh n in
1728 let y = y + (truncate (top *. float h)) in
1729 gotoghyll y
1732 let gotopage1 n top =
1733 let y = getpagey n in
1734 let y = y + top in
1735 gotoghyll y
1738 let invalidate s f =
1739 state.layout <- [];
1740 state.pdims <- [];
1741 state.rects <- [];
1742 state.rects1 <- [];
1743 match state.geomcmds with
1744 | ps, [] when String.length ps = 0 ->
1745 f ();
1746 state.geomcmds <- s, [];
1748 | ps, [] ->
1749 state.geomcmds <- ps, [s, f];
1751 | ps, (s', _) :: rest when s' = s ->
1752 state.geomcmds <- ps, ((s, f) :: rest);
1754 | ps, cmds ->
1755 state.geomcmds <- ps, ((s, f) :: cmds);
1758 let opendoc path password =
1759 state.path <- path;
1760 state.password <- password;
1761 state.gen <- state.gen + 1;
1762 state.docinfo <- [];
1764 setaalevel conf.aalevel;
1765 Wsi.settitle ("llpp " ^ Filename.basename path);
1766 wcmd "open %s\000%s\000" path password;
1767 invalidate "reqlayout"
1768 (fun () ->
1769 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1772 let scalecolor c =
1773 let c = c *. conf.colorscale in
1774 (c, c, c);
1777 let scalecolor2 (r, g, b) =
1778 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1781 let represent () =
1782 let docolumns = function
1783 | Csingle -> ()
1785 | Cmulti ((columns, coverA, coverB), _) ->
1786 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1787 let rec loop pageno pdimno pdim x y rowh pdims =
1788 if pageno = state.pagecount
1789 then ()
1790 else
1791 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1792 match pdims with
1793 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1794 pdimno+1, pdim, rest
1795 | _ ->
1796 pdimno, pdim, pdims
1798 let x, y, rowh' =
1799 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1800 then (
1801 (conf.winw - state.scrollw - w) / 2,
1802 y + rowh + conf.interpagespace, h
1804 else (
1805 if (pageno - coverA) mod columns = 0
1806 then 0, y + rowh + conf.interpagespace, h
1807 else x, y, max rowh h
1810 let rec fixrow m = if m = pageno then () else
1811 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1812 if h < rowh
1813 then (
1814 let y = y + (rowh - h) / 2 in
1815 a.(m) <- (pdimno, x, y, pdim);
1817 fixrow (m+1)
1819 if pageno > 1 && (pageno - coverA) mod columns = 0
1820 then fixrow (pageno - columns);
1821 a.(pageno) <- (pdimno, x, y, pdim);
1822 let x = x + w + xoff*2 + conf.interpagespace in
1823 loop (pageno+1) pdimno pdim x y rowh' pdims
1825 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1826 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1828 | Csplit (c, _) ->
1829 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1830 let rec loop pageno pdimno pdim y pdims =
1831 if pageno = state.pagecount
1832 then ()
1833 else
1834 let pdimno, ((_, w, h, _) as pdim), pdims =
1835 match pdims with
1836 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1837 pdimno+1, pdim, rest
1838 | _ ->
1839 pdimno, pdim, pdims
1841 let cw = w / c in
1842 let rec loop1 n x y =
1843 if n = c then y else (
1844 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1845 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1848 let y = loop1 0 0 y in
1849 loop (pageno+1) pdimno pdim y pdims
1851 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1852 conf.columns <- Csplit (c, a);
1854 docolumns conf.columns;
1855 state.maxy <- calcheight ();
1856 state.hscrollh <-
1857 if state.w <= conf.winw - state.scrollw
1858 then 0
1859 else state.scrollw
1861 match state.mode with
1862 | Birdseye (_, _, pageno, _, _) ->
1863 let y, h = getpageyh pageno in
1864 let top = (conf.winh - h) / 2 in
1865 gotoy (max 0 (y - top))
1866 | _ -> gotoanchor state.anchor
1869 let reshape w h =
1870 GlDraw.viewport 0 0 w h;
1871 if state.geomcmds != firstgeomcmds && nogeomcmds state.geomcmds
1872 then state.anchor <- getanchor ();
1874 conf.winw <- w;
1875 let w = truncate (float w *. conf.zoom) - state.scrollw in
1876 let w = max w 2 in
1877 conf.winh <- h;
1878 setfontsize fstate.fontsize;
1879 GlMat.mode `modelview;
1880 GlMat.load_identity ();
1882 GlMat.mode `projection;
1883 GlMat.load_identity ();
1884 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1885 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1886 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1888 let relx =
1889 if conf.zoom <= 1.0
1890 then 0.0
1891 else float state.x /. float state.w
1893 invalidate "geometry"
1894 (fun () ->
1895 state.w <- w;
1896 state.x <- truncate (relx *. float w);
1897 let w =
1898 match conf.columns with
1899 | Csingle -> w
1900 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1901 | Csplit (c, _) -> w * c
1903 wcmd "geometry %d %d" w h);
1906 let enttext () =
1907 let len = String.length state.text in
1908 let drawstring s =
1909 let hscrollh =
1910 match state.mode with
1911 | Textentry _
1912 | View -> state.hscrollh
1913 | _ -> 0
1915 let rect x w =
1916 GlDraw.rect
1917 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1918 (x+.w, float (conf.winh - hscrollh))
1921 let w = float (conf.winw - state.scrollw - 1) in
1922 if state.progress >= 0.0 && state.progress < 1.0
1923 then (
1924 GlDraw.color (0.3, 0.3, 0.3);
1925 let w1 = w *. state.progress in
1926 rect 0.0 w1;
1927 GlDraw.color (0.0, 0.0, 0.0);
1928 rect w1 (w-.w1)
1930 else (
1931 GlDraw.color (0.0, 0.0, 0.0);
1932 rect 0.0 w;
1935 GlDraw.color (1.0, 1.0, 1.0);
1936 drawstring fstate.fontsize
1937 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1939 let s =
1940 match state.mode with
1941 | Textentry ((prefix, text, _, _, _), _) ->
1942 let s =
1943 if len > 0
1944 then
1945 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1946 else
1947 Printf.sprintf "%s%s_" prefix text
1951 | _ -> state.text
1953 let s =
1954 if state.newerrmsgs
1955 then (
1956 if not (istextentry state.mode)
1957 then
1958 let s1 = "(press 'e' to review error messasges)" in
1959 if String.length s > 0 then s ^ " " ^ s1 else s1
1960 else s
1962 else s
1964 if String.length s > 0
1965 then drawstring s
1968 let gctiles () =
1969 let len = Queue.length state.tilelru in
1970 let rec loop qpos =
1971 if state.memused <= conf.memlimit
1972 then ()
1973 else (
1974 if qpos < len
1975 then
1976 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1977 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1978 let (_, pw, ph, _) = getpagedim n in
1980 gen = state.gen
1981 && colorspace = conf.colorspace
1982 && angle = conf.angle
1983 && pagew = pw
1984 && pageh = ph
1985 && (
1986 let layout =
1987 match state.throttle with
1988 | None ->
1989 if conf.preload
1990 then preloadlayout state.layout
1991 else state.layout
1992 | Some (layout, _, _) ->
1993 layout
1995 let x = col*conf.tilew
1996 and y = row*conf.tileh in
1997 tilevisible layout n x y
1999 then Queue.push lruitem state.tilelru
2000 else (
2001 wcmd "freetile %s" p;
2002 state.memused <- state.memused - s;
2003 state.uioh#infochanged Memused;
2004 Hashtbl.remove state.tilemap k;
2006 loop (qpos+1)
2009 loop 0
2012 let flushtiles () =
2013 Queue.iter (fun (k, p, s) ->
2014 wcmd "freetile %s" p;
2015 state.memused <- state.memused - s;
2016 state.uioh#infochanged Memused;
2017 Hashtbl.remove state.tilemap k;
2018 ) state.tilelru;
2019 Queue.clear state.tilelru;
2020 load state.layout;
2023 let logcurrently = function
2024 | Idle -> dolog "Idle"
2025 | Loading (l, gen) ->
2026 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2027 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2028 dolog
2029 "Tiling %d[%d,%d] page=%s cs=%s angle"
2030 l.pageno col row pageopaque
2031 (colorspace_to_string colorspace)
2033 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2034 angle gen conf.angle state.gen
2035 tilew tileh
2036 conf.tilew conf.tileh
2038 | Outlining _ ->
2039 dolog "outlining"
2042 let act cmds =
2043 (* dolog "%S" cmds; *)
2044 let op, args =
2045 let spacepos =
2046 try String.index cmds ' '
2047 with Not_found -> -1
2049 if spacepos = -1
2050 then cmds, ""
2051 else
2052 let l = String.length cmds in
2053 let op = String.sub cmds 0 spacepos in
2054 op, begin
2055 if l - spacepos < 2 then ""
2056 else String.sub cmds (spacepos+1) (l-spacepos-1)
2059 match op with
2060 | "clear" ->
2061 state.uioh#infochanged Pdim;
2062 state.pdims <- [];
2064 | "clearrects" ->
2065 state.rects <- state.rects1;
2066 G.postRedisplay "clearrects";
2068 | "continue" ->
2069 let n =
2070 try Scanf.sscanf args "%u" (fun n -> n)
2071 with exn ->
2072 dolog "error processing 'continue' %S: %s"
2073 cmds (Printexc.to_string exn);
2074 exit 1;
2076 state.pagecount <- n;
2077 begin match state.currently with
2078 | Outlining l ->
2079 state.currently <- Idle;
2080 state.outlines <- Array.of_list (List.rev l)
2081 | _ -> ()
2082 end;
2084 let cur, cmds = state.geomcmds in
2085 if String.length cur = 0
2086 then failwith "umpossible";
2088 begin match List.rev cmds with
2089 | [] ->
2090 state.geomcmds <- "", [];
2091 represent ();
2092 | (s, f) :: rest ->
2093 f ();
2094 state.geomcmds <- s, List.rev rest;
2095 end;
2096 if conf.maxwait = None
2097 then G.postRedisplay "continue";
2099 | "title" ->
2100 Wsi.settitle args
2102 | "msg" ->
2103 showtext ' ' args
2105 | "vmsg" ->
2106 if conf.verbose
2107 then showtext ' ' args
2109 | "progress" ->
2110 let progress, text =
2112 Scanf.sscanf args "%f %n"
2113 (fun f pos ->
2114 f, String.sub args pos (String.length args - pos))
2115 with exn ->
2116 dolog "error processing 'progress' %S: %s"
2117 cmds (Printexc.to_string exn);
2118 exit 1;
2120 state.text <- text;
2121 state.progress <- progress;
2122 G.postRedisplay "progress"
2124 | "firstmatch" ->
2125 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2127 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2128 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2129 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2130 with exn ->
2131 dolog "error processing 'firstmatch' %S: %s"
2132 cmds (Printexc.to_string exn);
2133 exit 1;
2135 let y = (getpagey pageno) + truncate y0 in
2136 addnav ();
2137 gotoy y;
2138 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2140 | "match" ->
2141 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2143 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2144 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2145 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2146 with exn ->
2147 dolog "error processing 'match' %S: %s"
2148 cmds (Printexc.to_string exn);
2149 exit 1;
2151 state.rects1 <-
2152 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2154 | "page" ->
2155 let pageopaque, t =
2157 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2158 with exn ->
2159 dolog "error processing 'page' %S: %s"
2160 cmds (Printexc.to_string exn);
2161 exit 1;
2163 begin match state.currently with
2164 | Loading (l, gen) ->
2165 vlog "page %d took %f sec" l.pageno t;
2166 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2167 begin match state.throttle with
2168 | None ->
2169 let preloadedpages =
2170 if conf.preload
2171 then preloadlayout state.layout
2172 else state.layout
2174 let evict () =
2175 let module IntSet =
2176 Set.Make (struct type t = int let compare = (-) end) in
2177 let set =
2178 List.fold_left (fun s l -> IntSet.add l.pageno s)
2179 IntSet.empty preloadedpages
2181 let evictedpages =
2182 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2183 if not (IntSet.mem pageno set)
2184 then (
2185 wcmd "freepage %s" opaque;
2186 key :: accu
2188 else accu
2189 ) state.pagemap []
2191 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2193 evict ();
2194 state.currently <- Idle;
2195 if gen = state.gen
2196 then (
2197 tilepage l.pageno pageopaque state.layout;
2198 load state.layout;
2199 load preloadedpages;
2200 if pagevisible state.layout l.pageno
2201 && layoutready state.layout
2202 then G.postRedisplay "page";
2205 | Some (layout, _, _) ->
2206 state.currently <- Idle;
2207 tilepage l.pageno pageopaque layout;
2208 load state.layout
2209 end;
2211 | _ ->
2212 dolog "Inconsistent loading state";
2213 logcurrently state.currently;
2214 exit 1
2217 | "tile" ->
2218 let (x, y, opaque, size, t) =
2220 Scanf.sscanf args "%u %u %s %u %f"
2221 (fun x y p size t -> (x, y, p, size, t))
2222 with exn ->
2223 dolog "error processing 'tile' %S: %s"
2224 cmds (Printexc.to_string exn);
2225 exit 1;
2227 begin match state.currently with
2228 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2229 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2231 if tilew != conf.tilew || tileh != conf.tileh
2232 then (
2233 wcmd "freetile %s" opaque;
2234 state.currently <- Idle;
2235 load state.layout;
2237 else (
2238 puttileopaque l col row gen cs angle opaque size t;
2239 state.memused <- state.memused + size;
2240 state.uioh#infochanged Memused;
2241 gctiles ();
2242 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2243 opaque, size) state.tilelru;
2245 let layout =
2246 match state.throttle with
2247 | None -> state.layout
2248 | Some (layout, _, _) -> layout
2251 state.currently <- Idle;
2252 if gen = state.gen
2253 && conf.colorspace = cs
2254 && conf.angle = angle
2255 && tilevisible layout l.pageno x y
2256 then conttiling l.pageno pageopaque;
2258 begin match state.throttle with
2259 | None ->
2260 preload state.layout;
2261 if gen = state.gen
2262 && conf.colorspace = cs
2263 && conf.angle = angle
2264 && tilevisible state.layout l.pageno x y
2265 then G.postRedisplay "tile nothrottle";
2267 | Some (layout, y, _) ->
2268 let ready = layoutready layout in
2269 if ready
2270 then (
2271 state.y <- y;
2272 state.layout <- layout;
2273 state.throttle <- None;
2274 G.postRedisplay "throttle";
2276 else load layout;
2277 end;
2280 | _ ->
2281 dolog "Inconsistent tiling state";
2282 logcurrently state.currently;
2283 exit 1
2286 | "pdim" ->
2287 let pdim =
2289 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2290 with exn ->
2291 dolog "error processing 'pdim' %S: %s"
2292 cmds (Printexc.to_string exn);
2293 exit 1;
2295 state.uioh#infochanged Pdim;
2296 state.pdims <- pdim :: state.pdims
2298 | "o" ->
2299 let (l, n, t, h, pos) =
2301 Scanf.sscanf args "%u %u %d %u %n"
2302 (fun l n t h pos -> l, n, t, h, pos)
2303 with exn ->
2304 dolog "error processing 'o' %S: %s"
2305 cmds (Printexc.to_string exn);
2306 exit 1;
2308 let s = String.sub args pos (String.length args - pos) in
2309 let outline = (s, l, (n, float t /. float h)) in
2310 begin match state.currently with
2311 | Outlining outlines ->
2312 state.currently <- Outlining (outline :: outlines)
2313 | Idle ->
2314 state.currently <- Outlining [outline]
2315 | currently ->
2316 dolog "invalid outlining state";
2317 logcurrently currently
2320 | "info" ->
2321 state.docinfo <- (1, args) :: state.docinfo
2323 | "infoend" ->
2324 state.uioh#infochanged Docinfo;
2325 state.docinfo <- List.rev state.docinfo
2327 | _ ->
2328 dolog "unknown cmd `%S'" cmds
2331 let onhist cb =
2332 let rc = cb.rc in
2333 let action = function
2334 | HCprev -> cbget cb ~-1
2335 | HCnext -> cbget cb 1
2336 | HCfirst -> cbget cb ~-(cb.rc)
2337 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2338 and cancel () = cb.rc <- rc
2339 in (action, cancel)
2342 let search pattern forward =
2343 if String.length pattern > 0
2344 then
2345 let pn, py =
2346 match state.layout with
2347 | [] -> 0, 0
2348 | l :: _ ->
2349 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2351 wcmd "search %d %d %d %d,%s\000"
2352 (btod conf.icase) pn py (btod forward) pattern;
2355 let intentry text key =
2356 let c =
2357 if key >= 32 && key < 127
2358 then Char.chr key
2359 else '\000'
2361 match c with
2362 | '0' .. '9' ->
2363 let text = addchar text c in
2364 TEcont text
2366 | _ ->
2367 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2368 TEcont text
2371 let textentry text key =
2372 if key land 0xff00 = 0xff00
2373 then TEcont text
2374 else TEcont (text ^ Wsi.toutf8 key)
2377 let reqlayout angle proportional =
2378 match state.throttle with
2379 | None ->
2380 if nogeomcmds state.geomcmds
2381 then state.anchor <- getanchor ();
2382 conf.angle <- angle mod 360;
2383 if conf.angle != 0
2384 then (
2385 match state.mode with
2386 | LinkNav _ -> state.mode <- View
2387 | _ -> ()
2389 conf.proportional <- proportional;
2390 invalidate "reqlayout"
2391 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2392 | _ -> ()
2395 let settrim trimmargins trimfuzz =
2396 if nogeomcmds state.geomcmds
2397 then state.anchor <- getanchor ();
2398 conf.trimmargins <- trimmargins;
2399 conf.trimfuzz <- trimfuzz;
2400 let x0, y0, x1, y1 = trimfuzz in
2401 invalidate "settrim"
2402 (fun () ->
2403 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2404 Hashtbl.iter (fun _ opaque ->
2405 wcmd "freepage %s" opaque;
2406 ) state.pagemap;
2407 Hashtbl.clear state.pagemap;
2410 let setzoom zoom =
2411 match state.throttle with
2412 | None ->
2413 let zoom = max 0.01 zoom in
2414 if zoom <> conf.zoom
2415 then (
2416 state.prevzoom <- conf.zoom;
2417 conf.zoom <- zoom;
2418 reshape conf.winw conf.winh;
2419 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2422 | Some (layout, y, started) ->
2423 let time =
2424 match conf.maxwait with
2425 | None -> 0.0
2426 | Some t -> t
2428 let dt = now () -. started in
2429 if dt > time
2430 then (
2431 state.y <- y;
2432 load layout;
2436 let setcolumns mode columns coverA coverB =
2437 if columns < 0
2438 then (
2439 if isbirdseye mode
2440 then showtext '!' "split mode doesn't work in bird's eye"
2441 else (
2442 conf.columns <- Csplit (-columns, [||]);
2443 state.x <- 0;
2444 conf.zoom <- 1.0;
2447 else (
2448 if columns < 2
2449 then (
2450 conf.columns <- Csingle;
2451 state.x <- 0;
2452 setzoom 1.0;
2454 else (
2455 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2456 conf.zoom <- 1.0;
2459 reshape conf.winw conf.winh;
2462 let enterbirdseye () =
2463 let zoom = float conf.thumbw /. float conf.winw in
2464 let birdseyepageno =
2465 let cy = conf.winh / 2 in
2466 let fold = function
2467 | [] -> 0
2468 | l :: rest ->
2469 let rec fold best = function
2470 | [] -> best.pageno
2471 | l :: rest ->
2472 let d = cy - (l.pagedispy + l.pagevh/2)
2473 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2474 if abs d < abs dbest
2475 then fold l rest
2476 else best.pageno
2477 in fold l rest
2479 fold state.layout
2481 state.mode <- Birdseye (
2482 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2484 conf.zoom <- zoom;
2485 conf.presentation <- false;
2486 conf.interpagespace <- 10;
2487 conf.hlinks <- false;
2488 state.x <- 0;
2489 state.mstate <- Mnone;
2490 conf.maxwait <- None;
2491 conf.columns <- (
2492 match conf.beyecolumns with
2493 | Some c ->
2494 conf.zoom <- 1.0;
2495 Cmulti ((c, 0, 0), [||])
2496 | None -> Csingle
2498 Wsi.setcursor Wsi.CURSOR_INHERIT;
2499 if conf.verbose
2500 then
2501 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2502 (100.0*.zoom)
2503 else
2504 state.text <- ""
2506 reshape conf.winw conf.winh;
2509 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2510 state.mode <- View;
2511 conf.zoom <- c.zoom;
2512 conf.presentation <- c.presentation;
2513 conf.interpagespace <- c.interpagespace;
2514 conf.maxwait <- c.maxwait;
2515 conf.hlinks <- c.hlinks;
2516 conf.beyecolumns <- (
2517 match conf.columns with
2518 | Cmulti ((c, _, _), _) -> Some c
2519 | Csingle -> None
2520 | Csplit _ -> assert false
2522 conf.columns <- (
2523 match c.columns with
2524 | Cmulti (c, _) -> Cmulti (c, [||])
2525 | Csingle -> Csingle
2526 | Csplit _ -> failwith "leaving bird's eye split mode"
2528 state.x <- leftx;
2529 if conf.verbose
2530 then
2531 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2532 (100.0*.conf.zoom)
2534 reshape conf.winw conf.winh;
2535 state.anchor <- if goback then anchor else (pageno, 0.0);
2538 let togglebirdseye () =
2539 match state.mode with
2540 | Birdseye vals -> leavebirdseye vals true
2541 | View -> enterbirdseye ()
2542 | _ -> ()
2545 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2546 let pageno = max 0 (pageno - incr) in
2547 let rec loop = function
2548 | [] -> gotopage1 pageno 0
2549 | l :: _ when l.pageno = pageno ->
2550 if l.pagedispy >= 0 && l.pagey = 0
2551 then G.postRedisplay "upbirdseye"
2552 else gotopage1 pageno 0
2553 | _ :: rest -> loop rest
2555 loop state.layout;
2556 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2559 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2560 let pageno = min (state.pagecount - 1) (pageno + incr) in
2561 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2562 let rec loop = function
2563 | [] ->
2564 let y, h = getpageyh pageno in
2565 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2566 gotoy (clamp dy)
2567 | l :: _ when l.pageno = pageno ->
2568 if l.pagevh != l.pageh
2569 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2570 else G.postRedisplay "downbirdseye"
2571 | _ :: rest -> loop rest
2573 loop state.layout
2576 let optentry mode _ key =
2577 let btos b = if b then "on" else "off" in
2578 if key >= 32 && key < 127
2579 then
2580 let c = Char.chr key in
2581 match c with
2582 | 's' ->
2583 let ondone s =
2584 try conf.scrollstep <- int_of_string s with exc ->
2585 state.text <- Printf.sprintf "bad integer `%s': %s"
2586 s (Printexc.to_string exc)
2588 TEswitch ("scroll step: ", "", None, intentry, ondone)
2590 | 'A' ->
2591 let ondone s =
2593 conf.autoscrollstep <- int_of_string s;
2594 if state.autoscroll <> None
2595 then state.autoscroll <- Some conf.autoscrollstep
2596 with exc ->
2597 state.text <- Printf.sprintf "bad integer `%s': %s"
2598 s (Printexc.to_string exc)
2600 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2602 | 'C' ->
2603 let mode = state.mode in
2604 let ondone s =
2606 let n, a, b = multicolumns_of_string s in
2607 setcolumns mode n a b;
2608 with exc ->
2609 state.text <- Printf.sprintf "bad columns `%s': %s"
2610 s (Printexc.to_string exc)
2612 TEswitch ("columns: ", "", None, textentry, ondone)
2614 | 'Z' ->
2615 let ondone s =
2617 let zoom = float (int_of_string s) /. 100.0 in
2618 setzoom zoom
2619 with exc ->
2620 state.text <- Printf.sprintf "bad integer `%s': %s"
2621 s (Printexc.to_string exc)
2623 TEswitch ("zoom: ", "", None, intentry, ondone)
2625 | 't' ->
2626 let ondone s =
2628 conf.thumbw <- bound (int_of_string s) 2 4096;
2629 state.text <-
2630 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2631 begin match mode with
2632 | Birdseye beye ->
2633 leavebirdseye beye false;
2634 enterbirdseye ();
2635 | _ -> ();
2637 with exc ->
2638 state.text <- Printf.sprintf "bad integer `%s': %s"
2639 s (Printexc.to_string exc)
2641 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2643 | 'R' ->
2644 let ondone s =
2645 match try
2646 Some (int_of_string s)
2647 with exc ->
2648 state.text <- Printf.sprintf "bad integer `%s': %s"
2649 s (Printexc.to_string exc);
2650 None
2651 with
2652 | Some angle -> reqlayout angle conf.proportional
2653 | None -> ()
2655 TEswitch ("rotation: ", "", None, intentry, ondone)
2657 | 'i' ->
2658 conf.icase <- not conf.icase;
2659 TEdone ("case insensitive search " ^ (btos conf.icase))
2661 | 'p' ->
2662 conf.preload <- not conf.preload;
2663 gotoy state.y;
2664 TEdone ("preload " ^ (btos conf.preload))
2666 | 'v' ->
2667 conf.verbose <- not conf.verbose;
2668 TEdone ("verbose " ^ (btos conf.verbose))
2670 | 'd' ->
2671 conf.debug <- not conf.debug;
2672 TEdone ("debug " ^ (btos conf.debug))
2674 | 'h' ->
2675 conf.maxhfit <- not conf.maxhfit;
2676 state.maxy <-
2677 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2678 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2680 | 'c' ->
2681 conf.crophack <- not conf.crophack;
2682 TEdone ("crophack " ^ btos conf.crophack)
2684 | 'a' ->
2685 let s =
2686 match conf.maxwait with
2687 | None ->
2688 conf.maxwait <- Some infinity;
2689 "always wait for page to complete"
2690 | Some _ ->
2691 conf.maxwait <- None;
2692 "show placeholder if page is not ready"
2694 TEdone s
2696 | 'f' ->
2697 conf.underinfo <- not conf.underinfo;
2698 TEdone ("underinfo " ^ btos conf.underinfo)
2700 | 'P' ->
2701 conf.savebmarks <- not conf.savebmarks;
2702 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2704 | 'S' ->
2705 let ondone s =
2707 let pageno, py =
2708 match state.layout with
2709 | [] -> 0, 0
2710 | l :: _ ->
2711 l.pageno, l.pagey
2713 conf.interpagespace <- int_of_string s;
2714 state.maxy <- calcheight ();
2715 let y = getpagey pageno in
2716 gotoy (y + py)
2717 with exc ->
2718 state.text <- Printf.sprintf "bad integer `%s': %s"
2719 s (Printexc.to_string exc)
2721 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2723 | 'l' ->
2724 reqlayout conf.angle (not conf.proportional);
2725 TEdone ("proportional display " ^ btos conf.proportional)
2727 | 'T' ->
2728 settrim (not conf.trimmargins) conf.trimfuzz;
2729 TEdone ("trim margins " ^ btos conf.trimmargins)
2731 | 'I' ->
2732 conf.invert <- not conf.invert;
2733 TEdone ("invert colors " ^ btos conf.invert)
2735 | 'x' ->
2736 let ondone s =
2737 cbput state.hists.sel s;
2738 conf.selcmd <- s;
2740 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2741 textentry, ondone)
2743 | _ ->
2744 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2745 TEstop
2746 else
2747 TEcont state.text
2750 class type lvsource = object
2751 method getitemcount : int
2752 method getitem : int -> (string * int)
2753 method hasaction : int -> bool
2754 method exit :
2755 uioh:uioh ->
2756 cancel:bool ->
2757 active:int ->
2758 first:int ->
2759 pan:int ->
2760 qsearch:string ->
2761 uioh option
2762 method getactive : int
2763 method getfirst : int
2764 method getqsearch : string
2765 method setqsearch : string -> unit
2766 method getpan : int
2767 end;;
2769 class virtual lvsourcebase = object
2770 val mutable m_active = 0
2771 val mutable m_first = 0
2772 val mutable m_qsearch = ""
2773 val mutable m_pan = 0
2774 method getactive = m_active
2775 method getfirst = m_first
2776 method getqsearch = m_qsearch
2777 method getpan = m_pan
2778 method setqsearch s = m_qsearch <- s
2779 end;;
2781 let withoutlastutf8 s =
2782 let len = String.length s in
2783 if len = 0
2784 then s
2785 else
2786 let rec find pos =
2787 if pos = 0
2788 then pos
2789 else
2790 let b = Char.code s.[pos] in
2791 if b land 0b110000 = 0b11000000
2792 then find (pos-1)
2793 else pos-1
2795 let first =
2796 if Char.code s.[len-1] land 0x80 = 0
2797 then len-1
2798 else find (len-1)
2800 String.sub s 0 first;
2803 let textentrykeyboard key _mask ((c, text, opthist, onkey, ondone), onleave) =
2804 let enttext te =
2805 state.mode <- Textentry (te, onleave);
2806 state.text <- "";
2807 enttext ();
2808 G.postRedisplay "textentrykeyboard enttext";
2810 let histaction cmd =
2811 match opthist with
2812 | None -> ()
2813 | Some (action, _) ->
2814 state.mode <- Textentry (
2815 (c, action cmd, opthist, onkey, ondone), onleave
2817 G.postRedisplay "textentry histaction"
2819 match key with
2820 | 0xff08 -> (* backspace *)
2821 let s = withoutlastutf8 text in
2822 let len = String.length s in
2823 if len = 0
2824 then (
2825 onleave Cancel;
2826 G.postRedisplay "textentrykeyboard after cancel";
2828 else (
2829 enttext (c, s, opthist, onkey, ondone)
2832 | 0xff0d ->
2833 ondone text;
2834 onleave Confirm;
2835 G.postRedisplay "textentrykeyboard after confirm"
2837 | 0xff52 -> histaction HCprev
2838 | 0xff54 -> histaction HCnext
2839 | 0xff50 -> histaction HCfirst
2840 | 0xff57 -> histaction HClast
2842 | 0xff1b -> (* escape*)
2843 if String.length text = 0
2844 then (
2845 begin match opthist with
2846 | None -> ()
2847 | Some (_, onhistcancel) -> onhistcancel ()
2848 end;
2849 onleave Cancel;
2850 state.text <- "";
2851 G.postRedisplay "textentrykeyboard after cancel2"
2853 else (
2854 enttext (c, "", opthist, onkey, ondone)
2857 | 0xff9f | 0xffff -> () (* delete *)
2859 | _ when key != 0 && key land 0xff00 != 0xff00 ->
2860 begin match onkey text key with
2861 | TEdone text ->
2862 ondone text;
2863 onleave Confirm;
2864 G.postRedisplay "textentrykeyboard after confirm2";
2866 | TEcont text ->
2867 enttext (c, text, opthist, onkey, ondone);
2869 | TEstop ->
2870 onleave Cancel;
2871 G.postRedisplay "textentrykeyboard after cancel3"
2873 | TEswitch te ->
2874 state.mode <- Textentry (te, onleave);
2875 G.postRedisplay "textentrykeyboard switch";
2876 end;
2878 | _ ->
2879 vlog "unhandled key %s" (Wsi.keyname key)
2882 let firstof first active =
2883 if first > active || abs (first - active) > fstate.maxrows - 1
2884 then max 0 (active - (fstate.maxrows/2))
2885 else first
2888 let calcfirst first active =
2889 if active > first
2890 then
2891 let rows = active - first in
2892 if rows > fstate.maxrows then active - fstate.maxrows else first
2893 else active
2896 let scrollph y maxy =
2897 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2898 let sh = float conf.winh /. sh in
2899 let sh = max sh (float conf.scrollh) in
2901 let percent =
2902 if y = state.maxy
2903 then 1.0
2904 else float y /. float maxy
2906 let position = (float conf.winh -. sh) *. percent in
2908 let position =
2909 if position +. sh > float conf.winh
2910 then float conf.winh -. sh
2911 else position
2913 position, sh;
2916 let coe s = (s :> uioh);;
2918 class listview ~(source:lvsource) ~trusted ~modehash =
2919 object (self)
2920 val m_pan = source#getpan
2921 val m_first = source#getfirst
2922 val m_active = source#getactive
2923 val m_qsearch = source#getqsearch
2924 val m_prev_uioh = state.uioh
2926 method private elemunder y =
2927 let n = y / (fstate.fontsize+1) in
2928 if m_first + n < source#getitemcount
2929 then (
2930 if source#hasaction (m_first + n)
2931 then Some (m_first + n)
2932 else None
2934 else None
2936 method display =
2937 Gl.enable `blend;
2938 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2939 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2940 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2941 GlDraw.color (1., 1., 1.);
2942 Gl.enable `texture_2d;
2943 let fs = fstate.fontsize in
2944 let nfs = fs + 1 in
2945 let ww = fstate.wwidth in
2946 let tabw = 30.0*.ww in
2947 let itemcount = source#getitemcount in
2948 let rec loop row =
2949 if (row - m_first) * nfs > conf.winh
2950 then ()
2951 else (
2952 if row >= 0 && row < itemcount
2953 then (
2954 let (s, level) = source#getitem row in
2955 let y = (row - m_first) * nfs in
2956 let x = 5.0 +. float (level + m_pan) *. ww in
2957 if row = m_active
2958 then (
2959 Gl.disable `texture_2d;
2960 GlDraw.polygon_mode `both `line;
2961 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2962 GlDraw.rect (1., float (y + 1))
2963 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2964 GlDraw.polygon_mode `both `fill;
2965 GlDraw.color (1., 1., 1.);
2966 Gl.enable `texture_2d;
2969 let drawtabularstring s =
2970 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2971 if trusted
2972 then
2973 let tabpos = try String.index s '\t' with Not_found -> -1 in
2974 if tabpos > 0
2975 then
2976 let len = String.length s - tabpos - 1 in
2977 let s1 = String.sub s 0 tabpos
2978 and s2 = String.sub s (tabpos + 1) len in
2979 let nx = drawstr x s1 in
2980 let sw = nx -. x in
2981 let x = x +. (max tabw sw) in
2982 drawstr x s2
2983 else
2984 drawstr x s
2985 else
2986 drawstr x s
2988 let _ = drawtabularstring s in
2989 loop (row+1)
2993 loop m_first;
2994 Gl.disable `blend;
2995 Gl.disable `texture_2d;
2997 method updownlevel incr =
2998 let len = source#getitemcount in
2999 let curlevel =
3000 if m_active >= 0 && m_active < len
3001 then snd (source#getitem m_active)
3002 else -1
3004 let rec flow i =
3005 if i = len then i-1 else if i = -1 then 0 else
3006 let _, l = source#getitem i in
3007 if l != curlevel then i else flow (i+incr)
3009 let active = flow m_active in
3010 let first = calcfirst m_first active in
3011 G.postRedisplay "outline updownlevel";
3012 {< m_active = active; m_first = first >}
3014 method private key1 key mask =
3015 let set1 active first qsearch =
3016 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3018 let search active pattern incr =
3019 let dosearch re =
3020 let rec loop n =
3021 if n >= 0 && n < source#getitemcount
3022 then (
3023 let s, _ = source#getitem n in
3025 (try ignore (Str.search_forward re s 0); true
3026 with Not_found -> false)
3027 then Some n
3028 else loop (n + incr)
3030 else None
3032 loop active
3035 let re = Str.regexp_case_fold pattern in
3036 dosearch re
3037 with Failure s ->
3038 state.text <- s;
3039 None
3041 let itemcount = source#getitemcount in
3042 let find start incr =
3043 let rec find i =
3044 if i = -1 || i = itemcount
3045 then -1
3046 else (
3047 if source#hasaction i
3048 then i
3049 else find (i + incr)
3052 find start
3054 let set active first =
3055 let first = bound first 0 (itemcount - fstate.maxrows) in
3056 state.text <- "";
3057 coe {< m_active = active; m_first = first >}
3059 let navigate incr =
3060 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3061 let active, first =
3062 let incr1 = if incr > 0 then 1 else -1 in
3063 if isvisible m_first m_active
3064 then
3065 let next =
3066 let next = m_active + incr in
3067 let next =
3068 if next < 0 || next >= itemcount
3069 then -1
3070 else find next incr1
3072 if next = -1 || abs (m_active - next) > fstate.maxrows
3073 then -1
3074 else next
3076 if next = -1
3077 then
3078 let first = m_first + incr in
3079 let first = bound first 0 (itemcount - 1) in
3080 let next =
3081 let next = m_active + incr in
3082 let next = bound next 0 (itemcount - 1) in
3083 find next ~-incr1
3085 let active = if next = -1 then m_active else next in
3086 active, first
3087 else
3088 let first = min next m_first in
3089 let first =
3090 if abs (next - first) > fstate.maxrows
3091 then first + incr
3092 else first
3094 next, first
3095 else
3096 let first = m_first + incr in
3097 let first = bound first 0 (itemcount - 1) in
3098 let active =
3099 let next = m_active + incr in
3100 let next = bound next 0 (itemcount - 1) in
3101 let next = find next incr1 in
3102 let active =
3103 if next = -1 || abs (m_active - first) > fstate.maxrows
3104 then (
3105 let active = if m_active = -1 then next else m_active in
3106 active
3108 else next
3110 if isvisible first active
3111 then active
3112 else -1
3114 active, first
3116 G.postRedisplay "listview navigate";
3117 set active first;
3119 match key with
3120 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3121 let incr = if key = 0x72 then -1 else 1 in
3122 let active, first =
3123 match search (m_active + incr) m_qsearch incr with
3124 | None ->
3125 state.text <- m_qsearch ^ " [not found]";
3126 m_active, m_first
3127 | Some active ->
3128 state.text <- m_qsearch;
3129 active, firstof m_first active
3131 G.postRedisplay "listview ctrl-r/s";
3132 set1 active first m_qsearch;
3134 | 0xff08 -> (* backspace *)
3135 if String.length m_qsearch = 0
3136 then coe self
3137 else (
3138 let qsearch = withoutlastutf8 m_qsearch in
3139 let len = String.length qsearch in
3140 if len = 0
3141 then (
3142 state.text <- "";
3143 G.postRedisplay "listview empty qsearch";
3144 set1 m_active m_first "";
3146 else
3147 let active, first =
3148 match search m_active qsearch ~-1 with
3149 | None ->
3150 state.text <- qsearch ^ " [not found]";
3151 m_active, m_first
3152 | Some active ->
3153 state.text <- qsearch;
3154 active, firstof m_first active
3156 G.postRedisplay "listview backspace qsearch";
3157 set1 active first qsearch
3160 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3161 let pattern = m_qsearch ^ Wsi.toutf8 key in
3162 let active, first =
3163 match search m_active pattern 1 with
3164 | None ->
3165 state.text <- pattern ^ " [not found]";
3166 m_active, m_first
3167 | Some active ->
3168 state.text <- pattern;
3169 active, firstof m_first active
3171 G.postRedisplay "listview qsearch add";
3172 set1 active first pattern;
3174 | 0xff1b -> (* escape *)
3175 state.text <- "";
3176 if String.length m_qsearch = 0
3177 then (
3178 G.postRedisplay "list view escape";
3179 begin
3180 match
3181 source#exit (coe self) true m_active m_first m_pan m_qsearch
3182 with
3183 | None -> m_prev_uioh
3184 | Some uioh -> uioh
3187 else (
3188 G.postRedisplay "list view kill qsearch";
3189 source#setqsearch "";
3190 coe {< m_qsearch = "" >}
3193 | 0xff0d -> (* return *)
3194 state.text <- "";
3195 let self = {< m_qsearch = "" >} in
3196 source#setqsearch "";
3197 let opt =
3198 G.postRedisplay "listview enter";
3199 if m_active >= 0 && m_active < source#getitemcount
3200 then (
3201 source#exit (coe self) false m_active m_first m_pan "";
3203 else (
3204 source#exit (coe self) true m_active m_first m_pan "";
3207 begin match opt with
3208 | None -> m_prev_uioh
3209 | Some uioh -> uioh
3212 | 0xff9f | 0xffff -> (* delete *)
3213 coe self
3215 | 0xff52 -> navigate ~-1 (* up *)
3216 | 0xff54 -> navigate 1 (* down *)
3217 | 0xff55 -> navigate ~-(fstate.maxrows) (* prior *)
3218 | 0xff56 -> navigate fstate.maxrows (* next *)
3220 | 0xff53 -> (* right *)
3221 state.text <- "";
3222 G.postRedisplay "listview right";
3223 coe {< m_pan = m_pan - 1 >}
3225 | 0xff51 -> (* left *)
3226 state.text <- "";
3227 G.postRedisplay "listview left";
3228 coe {< m_pan = m_pan + 1 >}
3230 | 0xff50 -> (* home *)
3231 let active = find 0 1 in
3232 G.postRedisplay "listview home";
3233 set active 0;
3235 | 0xff57 -> (* end *)
3236 let first = max 0 (itemcount - fstate.maxrows) in
3237 let active = find (itemcount - 1) ~-1 in
3238 G.postRedisplay "listview end";
3239 set active first;
3241 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3242 coe self
3244 | _ ->
3245 dolog "listview unknown key %#x" key; coe self
3247 method key key mask =
3248 match state.mode with
3249 | Textentry te -> textentrykeyboard key mask te; coe self
3250 | _ -> self#key1 key mask
3252 method button button down x y _ =
3253 let opt =
3254 match button with
3255 | 1 when x > conf.winw - conf.scrollbw ->
3256 G.postRedisplay "listview scroll";
3257 if down
3258 then
3259 let _, position, sh = self#scrollph in
3260 if y > truncate position && y < truncate (position +. sh)
3261 then (
3262 state.mstate <- Mscrolly;
3263 Some (coe self)
3265 else
3266 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3267 let first = truncate (s *. float source#getitemcount) in
3268 let first = min source#getitemcount first in
3269 Some (coe {< m_first = first; m_active = first >})
3270 else (
3271 state.mstate <- Mnone;
3272 Some (coe self);
3274 | 1 when not down ->
3275 begin match self#elemunder y with
3276 | Some n ->
3277 G.postRedisplay "listview click";
3278 source#exit
3279 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3280 | _ ->
3281 Some (coe self)
3283 | n when (n == 4 || n == 5) && not down ->
3284 let len = source#getitemcount in
3285 let first =
3286 if n = 5 && m_first + fstate.maxrows >= len
3287 then
3288 m_first
3289 else
3290 let first = m_first + (if n == 4 then -1 else 1) in
3291 bound first 0 (len - 1)
3293 G.postRedisplay "listview wheel";
3294 Some (coe {< m_first = first >})
3295 | _ ->
3296 Some (coe self)
3298 match opt with
3299 | None -> m_prev_uioh
3300 | Some uioh -> uioh
3302 method motion _ y =
3303 match state.mstate with
3304 | Mscrolly ->
3305 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3306 let first = truncate (s *. float source#getitemcount) in
3307 let first = min source#getitemcount first in
3308 G.postRedisplay "listview motion";
3309 coe {< m_first = first; m_active = first >}
3310 | _ -> coe self
3312 method pmotion x y =
3313 if x < conf.winw - conf.scrollbw
3314 then
3315 let n =
3316 match self#elemunder y with
3317 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3318 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3320 let o =
3321 if n != m_active
3322 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3323 else self
3325 coe o
3326 else (
3327 Wsi.setcursor Wsi.CURSOR_INHERIT;
3328 coe self
3331 method infochanged _ = ()
3333 method scrollpw = (0, 0.0, 0.0)
3334 method scrollph =
3335 let nfs = fstate.fontsize + 1 in
3336 let y = m_first * nfs in
3337 let itemcount = source#getitemcount in
3338 let maxi = max 0 (itemcount - fstate.maxrows) in
3339 let maxy = maxi * nfs in
3340 let p, h = scrollph y maxy in
3341 conf.scrollbw, p, h
3343 method modehash = modehash
3344 end;;
3346 class outlinelistview ~source =
3347 object (self)
3348 inherit listview
3349 ~source:(source :> lvsource)
3350 ~trusted:false
3351 ~modehash:(findkeyhash conf "outline")
3352 as super
3354 method key key mask =
3355 let calcfirst first active =
3356 if active > first
3357 then
3358 let rows = active - first in
3359 if rows > fstate.maxrows then active - fstate.maxrows else first
3360 else active
3362 let navigate incr =
3363 let active = m_active + incr in
3364 let active = bound active 0 (source#getitemcount - 1) in
3365 let first = calcfirst m_first active in
3366 G.postRedisplay "outline navigate";
3367 coe {< m_active = active; m_first = first >}
3369 let ctrl = Wsi.withctrl mask in
3370 match key with
3371 | 110 when ctrl -> (* ctrl-n *)
3372 source#narrow m_qsearch;
3373 G.postRedisplay "outline ctrl-n";
3374 coe {< m_first = 0; m_active = 0 >}
3376 | 117 when ctrl -> (* ctrl-u *)
3377 source#denarrow;
3378 G.postRedisplay "outline ctrl-u";
3379 state.text <- "";
3380 coe {< m_first = 0; m_active = 0 >}
3382 | 108 when ctrl -> (* ctrl-l *)
3383 let first = m_active - (fstate.maxrows / 2) in
3384 G.postRedisplay "outline ctrl-l";
3385 coe {< m_first = first >}
3387 | 0xff9f | 0xffff -> (* delete *)
3388 source#remove m_active;
3389 G.postRedisplay "outline delete";
3390 let active = max 0 (m_active-1) in
3391 coe {< m_first = firstof m_first active;
3392 m_active = active >}
3394 | 0xff52 -> navigate ~-1 (* up *)
3395 | 0xff54 -> navigate 1 (* down *)
3396 | 0xff55 -> (* prior *)
3397 navigate ~-(fstate.maxrows)
3398 | 0xff56 -> (* next *)
3399 navigate fstate.maxrows
3401 | 0xff53 -> (* [ctrl-]right *)
3402 let o =
3403 if ctrl
3404 then (
3405 G.postRedisplay "outline ctrl right";
3406 {< m_pan = m_pan + 1 >}
3408 else self#updownlevel 1
3410 coe o
3412 | 0xff51 -> (* [ctrl-]left *)
3413 let o =
3414 if ctrl
3415 then (
3416 G.postRedisplay "outline ctrl left";
3417 {< m_pan = m_pan - 1 >}
3419 else self#updownlevel ~-1
3421 coe o
3423 | 0xff50 -> (* home *)
3424 G.postRedisplay "outline home";
3425 coe {< m_first = 0; m_active = 0 >}
3427 | 0xff57 -> (* end *)
3428 let active = source#getitemcount - 1 in
3429 let first = max 0 (active - fstate.maxrows) in
3430 G.postRedisplay "outline end";
3431 coe {< m_active = active; m_first = first >}
3433 | _ -> super#key key mask
3436 let outlinesource usebookmarks =
3437 let empty = [||] in
3438 (object
3439 inherit lvsourcebase
3440 val mutable m_items = empty
3441 val mutable m_orig_items = empty
3442 val mutable m_prev_items = empty
3443 val mutable m_narrow_pattern = ""
3444 val mutable m_hadremovals = false
3446 method getitemcount =
3447 Array.length m_items + (if m_hadremovals then 1 else 0)
3449 method getitem n =
3450 if n == Array.length m_items && m_hadremovals
3451 then
3452 ("[Confirm removal]", 0)
3453 else
3454 let s, n, _ = m_items.(n) in
3455 (s, n)
3457 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3458 ignore (uioh, first, qsearch);
3459 let confrimremoval = m_hadremovals && active = Array.length m_items in
3460 let items =
3461 if String.length m_narrow_pattern = 0
3462 then m_orig_items
3463 else m_items
3465 if not cancel
3466 then (
3467 if not confrimremoval
3468 then(
3469 let _, _, anchor = m_items.(active) in
3470 gotoanchor anchor;
3471 m_items <- items;
3473 else (
3474 state.bookmarks <- Array.to_list m_items;
3475 m_orig_items <- m_items;
3478 else m_items <- items;
3479 m_pan <- pan;
3480 None
3482 method hasaction _ = true
3484 method greetmsg =
3485 if Array.length m_items != Array.length m_orig_items
3486 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3487 else ""
3489 method narrow pattern =
3490 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3491 match reopt with
3492 | None -> ()
3493 | Some re ->
3494 let rec loop accu n =
3495 if n = -1
3496 then (
3497 m_narrow_pattern <- pattern;
3498 m_items <- Array.of_list accu
3500 else
3501 let (s, _, _) as o = m_items.(n) in
3502 let accu =
3503 if (try ignore (Str.search_forward re s 0); true
3504 with Not_found -> false)
3505 then o :: accu
3506 else accu
3508 loop accu (n-1)
3510 loop [] (Array.length m_items - 1)
3512 method denarrow =
3513 m_orig_items <- (
3514 if usebookmarks
3515 then Array.of_list state.bookmarks
3516 else state.outlines
3518 m_items <- m_orig_items
3520 method remove m =
3521 if usebookmarks
3522 then
3523 if m >= 0 && m < Array.length m_items
3524 then (
3525 m_hadremovals <- true;
3526 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3527 let n = if n >= m then n+1 else n in
3528 m_items.(n)
3532 method reset anchor items =
3533 m_hadremovals <- false;
3534 if m_orig_items == empty || m_prev_items != items
3535 then (
3536 m_orig_items <- items;
3537 if String.length m_narrow_pattern = 0
3538 then m_items <- items;
3540 m_prev_items <- items;
3541 let rely = getanchory anchor in
3542 let active =
3543 let rec loop n best bestd =
3544 if n = Array.length m_items
3545 then best
3546 else
3547 let (_, _, anchor) = m_items.(n) in
3548 let orely = getanchory anchor in
3549 let d = abs (orely - rely) in
3550 if d < bestd
3551 then loop (n+1) n d
3552 else loop (n+1) best bestd
3554 loop 0 ~-1 max_int
3556 m_active <- active;
3557 m_first <- firstof m_first active
3558 end)
3561 let enterselector usebookmarks =
3562 let source = outlinesource usebookmarks in
3563 fun errmsg ->
3564 let outlines =
3565 if usebookmarks
3566 then Array.of_list state.bookmarks
3567 else state.outlines
3569 if Array.length outlines = 0
3570 then (
3571 showtext ' ' errmsg;
3573 else (
3574 state.text <- source#greetmsg;
3575 Wsi.setcursor Wsi.CURSOR_INHERIT;
3576 let anchor = getanchor () in
3577 source#reset anchor outlines;
3578 state.uioh <- coe (new outlinelistview ~source);
3579 G.postRedisplay "enter selector";
3583 let enteroutlinemode =
3584 let f = enterselector false in
3585 fun ()-> f "Document has no outline";
3588 let enterbookmarkmode =
3589 let f = enterselector true in
3590 fun () -> f "Document has no bookmarks (yet)";
3593 let color_of_string s =
3594 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3595 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3599 let color_to_string (r, g, b) =
3600 let r = truncate (r *. 256.0)
3601 and g = truncate (g *. 256.0)
3602 and b = truncate (b *. 256.0) in
3603 Printf.sprintf "%d/%d/%d" r g b
3606 let irect_of_string s =
3607 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3610 let irect_to_string (x0,y0,x1,y1) =
3611 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3614 let makecheckers () =
3615 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3616 following to say:
3617 converted by Issac Trotts. July 25, 2002 *)
3618 let image_height = 64
3619 and image_width = 64 in
3621 let make_image () =
3622 let image =
3623 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3625 for i = 0 to image_width - 1 do
3626 for j = 0 to image_height - 1 do
3627 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3628 (if (i land 8 ) lxor (j land 8) = 0
3629 then [|255;255;255|] else [|200;200;200|])
3630 done
3631 done;
3632 image
3634 let image = make_image () in
3635 let id = GlTex.gen_texture () in
3636 GlTex.bind_texture `texture_2d id;
3637 GlPix.store (`unpack_alignment 1);
3638 GlTex.image2d image;
3639 List.iter (GlTex.parameter ~target:`texture_2d)
3640 [ `wrap_s `repeat;
3641 `wrap_t `repeat;
3642 `mag_filter `nearest;
3643 `min_filter `nearest ];
3647 let setcheckers enabled =
3648 match state.texid with
3649 | None ->
3650 if enabled then state.texid <- Some (makecheckers ())
3652 | Some texid ->
3653 if not enabled
3654 then (
3655 GlTex.delete_texture texid;
3656 state.texid <- None;
3660 let int_of_string_with_suffix s =
3661 let l = String.length s in
3662 let s1, shift =
3663 if l > 1
3664 then
3665 let suffix = Char.lowercase s.[l-1] in
3666 match suffix with
3667 | 'k' -> String.sub s 0 (l-1), 10
3668 | 'm' -> String.sub s 0 (l-1), 20
3669 | 'g' -> String.sub s 0 (l-1), 30
3670 | _ -> s, 0
3671 else s, 0
3673 let n = int_of_string s1 in
3674 let m = n lsl shift in
3675 if m < 0 || m < n
3676 then raise (Failure "value too large")
3677 else m
3680 let string_with_suffix_of_int n =
3681 if n = 0
3682 then "0"
3683 else
3684 let n, s =
3685 if n = 0
3686 then 0, ""
3687 else (
3688 if n land ((1 lsl 20) - 1) = 0
3689 then n lsr 20, "M"
3690 else (
3691 if n land ((1 lsl 10) - 1) = 0
3692 then n lsr 10, "K"
3693 else n, ""
3697 let rec loop s n =
3698 let h = n mod 1000 in
3699 let n = n / 1000 in
3700 if n = 0
3701 then string_of_int h ^ s
3702 else (
3703 let s = Printf.sprintf "_%03d%s" h s in
3704 loop s n
3707 loop "" n ^ s;
3710 let defghyllscroll = (40, 8, 32);;
3711 let ghyllscroll_of_string s =
3712 let (n, a, b) as nab =
3713 if s = "default"
3714 then defghyllscroll
3715 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3717 if n <= a || n <= b || a >= b
3718 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3719 nab;
3722 let ghyllscroll_to_string ((n, a, b) as nab) =
3723 if nab = defghyllscroll
3724 then "default"
3725 else Printf.sprintf "%d,%d,%d" n a b;
3728 let describe_location () =
3729 let f (fn, _) l =
3730 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3732 let fn, ln = List.fold_left f (-1, -1) state.layout in
3733 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3734 let percent =
3735 if maxy <= 0
3736 then 100.
3737 else (100. *. (float state.y /. float maxy))
3739 if fn = ln
3740 then
3741 Printf.sprintf "page %d of %d [%.2f%%]"
3742 (fn+1) state.pagecount percent
3743 else
3744 Printf.sprintf
3745 "pages %d-%d of %d [%.2f%%]"
3746 (fn+1) (ln+1) state.pagecount percent
3749 let enterinfomode =
3750 let btos b = if b then "\xe2\x88\x9a" else "" in
3751 let showextended = ref false in
3752 let leave mode = function
3753 | Confirm -> state.mode <- mode
3754 | Cancel -> state.mode <- mode in
3755 let src =
3756 (object
3757 val mutable m_first_time = true
3758 val mutable m_l = []
3759 val mutable m_a = [||]
3760 val mutable m_prev_uioh = nouioh
3761 val mutable m_prev_mode = View
3763 inherit lvsourcebase
3765 method reset prev_mode prev_uioh =
3766 m_a <- Array.of_list (List.rev m_l);
3767 m_l <- [];
3768 m_prev_mode <- prev_mode;
3769 m_prev_uioh <- prev_uioh;
3770 if m_first_time
3771 then (
3772 let rec loop n =
3773 if n >= Array.length m_a
3774 then ()
3775 else
3776 match m_a.(n) with
3777 | _, _, _, Action _ -> m_active <- n
3778 | _ -> loop (n+1)
3780 loop 0;
3781 m_first_time <- false;
3784 method int name get set =
3785 m_l <-
3786 (name, `int get, 1, Action (
3787 fun u ->
3788 let ondone s =
3789 try set (int_of_string s)
3790 with exn ->
3791 state.text <- Printf.sprintf "bad integer `%s': %s"
3792 s (Printexc.to_string exn)
3794 state.text <- "";
3795 let te = name ^ ": ", "", None, intentry, ondone in
3796 state.mode <- Textentry (te, leave m_prev_mode);
3798 )) :: m_l
3800 method int_with_suffix name get set =
3801 m_l <-
3802 (name, `intws get, 1, Action (
3803 fun u ->
3804 let ondone s =
3805 try set (int_of_string_with_suffix s)
3806 with exn ->
3807 state.text <- Printf.sprintf "bad integer `%s': %s"
3808 s (Printexc.to_string exn)
3810 state.text <- "";
3811 let te =
3812 name ^ ": ", "", None, intentry_with_suffix, ondone
3814 state.mode <- Textentry (te, leave m_prev_mode);
3816 )) :: m_l
3818 method bool ?(offset=1) ?(btos=btos) name get set =
3819 m_l <-
3820 (name, `bool (btos, get), offset, Action (
3821 fun u ->
3822 let v = get () in
3823 set (not v);
3825 )) :: m_l
3827 method color name get set =
3828 m_l <-
3829 (name, `color get, 1, Action (
3830 fun u ->
3831 let invalid = (nan, nan, nan) in
3832 let ondone s =
3833 let c =
3834 try color_of_string s
3835 with exn ->
3836 state.text <- Printf.sprintf "bad color `%s': %s"
3837 s (Printexc.to_string exn);
3838 invalid
3840 if c <> invalid
3841 then set c;
3843 let te = name ^ ": ", "", None, textentry, ondone in
3844 state.text <- color_to_string (get ());
3845 state.mode <- Textentry (te, leave m_prev_mode);
3847 )) :: m_l
3849 method string name get set =
3850 m_l <-
3851 (name, `string get, 1, Action (
3852 fun u ->
3853 let ondone s = set s in
3854 let te = name ^ ": ", "", None, textentry, ondone in
3855 state.mode <- Textentry (te, leave m_prev_mode);
3857 )) :: m_l
3859 method colorspace name get set =
3860 m_l <-
3861 (name, `string get, 1, Action (
3862 fun _ ->
3863 let source =
3864 let vals = [| "rgb"; "bgr"; "gray" |] in
3865 (object
3866 inherit lvsourcebase
3868 initializer
3869 m_active <- int_of_colorspace conf.colorspace;
3870 m_first <- 0;
3872 method getitemcount = Array.length vals
3873 method getitem n = (vals.(n), 0)
3874 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3875 ignore (uioh, first, pan, qsearch);
3876 if not cancel then set active;
3877 None
3878 method hasaction _ = true
3879 end)
3881 state.text <- "";
3882 let modehash = findkeyhash conf "info" in
3883 coe (new listview ~source ~trusted:true ~modehash)
3884 )) :: m_l
3886 method caption s offset =
3887 m_l <- (s, `empty, offset, Noaction) :: m_l
3889 method caption2 s f offset =
3890 m_l <- (s, `string f, offset, Noaction) :: m_l
3892 method getitemcount = Array.length m_a
3894 method getitem n =
3895 let tostr = function
3896 | `int f -> string_of_int (f ())
3897 | `intws f -> string_with_suffix_of_int (f ())
3898 | `string f -> f ()
3899 | `color f -> color_to_string (f ())
3900 | `bool (btos, f) -> btos (f ())
3901 | `empty -> ""
3903 let name, t, offset, _ = m_a.(n) in
3904 ((let s = tostr t in
3905 if String.length s > 0
3906 then Printf.sprintf "%s\t%s" name s
3907 else name),
3908 offset)
3910 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3911 let uiohopt =
3912 if not cancel
3913 then (
3914 m_qsearch <- qsearch;
3915 let uioh =
3916 match m_a.(active) with
3917 | _, _, _, Action f -> f uioh
3918 | _ -> uioh
3920 Some uioh
3922 else None
3924 m_active <- active;
3925 m_first <- first;
3926 m_pan <- pan;
3927 uiohopt
3929 method hasaction n =
3930 match m_a.(n) with
3931 | _, _, _, Action _ -> true
3932 | _ -> false
3933 end)
3935 let rec fillsrc prevmode prevuioh =
3936 let sep () = src#caption "" 0 in
3937 let colorp name get set =
3938 src#string name
3939 (fun () -> color_to_string (get ()))
3940 (fun v ->
3942 let c = color_of_string v in
3943 set c
3944 with exn ->
3945 state.text <- Printf.sprintf "bad color `%s': %s"
3946 v (Printexc.to_string exn);
3949 let oldmode = state.mode in
3950 let birdseye = isbirdseye state.mode in
3952 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3954 src#bool "presentation mode"
3955 (fun () -> conf.presentation)
3956 (fun v ->
3957 conf.presentation <- v;
3958 state.anchor <- getanchor ();
3959 represent ());
3961 src#bool "ignore case in searches"
3962 (fun () -> conf.icase)
3963 (fun v -> conf.icase <- v);
3965 src#bool "preload"
3966 (fun () -> conf.preload)
3967 (fun v -> conf.preload <- v);
3969 src#bool "highlight links"
3970 (fun () -> conf.hlinks)
3971 (fun v -> conf.hlinks <- v);
3973 src#bool "under info"
3974 (fun () -> conf.underinfo)
3975 (fun v -> conf.underinfo <- v);
3977 src#bool "persistent bookmarks"
3978 (fun () -> conf.savebmarks)
3979 (fun v -> conf.savebmarks <- v);
3981 src#bool "proportional display"
3982 (fun () -> conf.proportional)
3983 (fun v -> reqlayout conf.angle v);
3985 src#bool "trim margins"
3986 (fun () -> conf.trimmargins)
3987 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3989 src#bool "persistent location"
3990 (fun () -> conf.jumpback)
3991 (fun v -> conf.jumpback <- v);
3993 sep ();
3994 src#int "inter-page space"
3995 (fun () -> conf.interpagespace)
3996 (fun n ->
3997 conf.interpagespace <- n;
3998 let pageno, py =
3999 match state.layout with
4000 | [] -> 0, 0
4001 | l :: _ ->
4002 l.pageno, l.pagey
4004 state.maxy <- calcheight ();
4005 let y = getpagey pageno in
4006 gotoy (y + py)
4009 src#int "page bias"
4010 (fun () -> conf.pagebias)
4011 (fun v -> conf.pagebias <- v);
4013 src#int "scroll step"
4014 (fun () -> conf.scrollstep)
4015 (fun n -> conf.scrollstep <- n);
4017 src#int "auto scroll step"
4018 (fun () ->
4019 match state.autoscroll with
4020 | Some step -> step
4021 | _ -> conf.autoscrollstep)
4022 (fun n ->
4023 if state.autoscroll <> None
4024 then state.autoscroll <- Some n;
4025 conf.autoscrollstep <- n);
4027 src#int "zoom"
4028 (fun () -> truncate (conf.zoom *. 100.))
4029 (fun v -> setzoom ((float v) /. 100.));
4031 src#int "rotation"
4032 (fun () -> conf.angle)
4033 (fun v -> reqlayout v conf.proportional);
4035 src#int "scroll bar width"
4036 (fun () -> state.scrollw)
4037 (fun v ->
4038 state.scrollw <- v;
4039 conf.scrollbw <- v;
4040 reshape conf.winw conf.winh;
4043 src#int "scroll handle height"
4044 (fun () -> conf.scrollh)
4045 (fun v -> conf.scrollh <- v;);
4047 src#int "thumbnail width"
4048 (fun () -> conf.thumbw)
4049 (fun v ->
4050 conf.thumbw <- min 4096 v;
4051 match oldmode with
4052 | Birdseye beye ->
4053 leavebirdseye beye false;
4054 enterbirdseye ()
4055 | _ -> ()
4058 let mode = state.mode in
4059 src#string "columns"
4060 (fun () ->
4061 match conf.columns with
4062 | Csingle -> "1"
4063 | Cmulti (multi, _) -> multicolumns_to_string multi
4064 | Csplit (count, _) -> string_of_int count
4066 (fun v ->
4067 let n, a, b = multicolumns_of_string v in
4068 setcolumns mode n a b);
4070 sep ();
4071 src#caption "Presentation mode" 0;
4072 src#bool "scrollbar visible"
4073 (fun () -> conf.scrollbarinpm)
4074 (fun v ->
4075 if v != conf.scrollbarinpm
4076 then (
4077 conf.scrollbarinpm <- v;
4078 if conf.presentation
4079 then (
4080 state.scrollw <- if v then conf.scrollbw else 0;
4081 reshape conf.winw conf.winh;
4086 sep ();
4087 src#caption "Pixmap cache" 0;
4088 src#int_with_suffix "size (advisory)"
4089 (fun () -> conf.memlimit)
4090 (fun v -> conf.memlimit <- v);
4092 src#caption2 "used"
4093 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4094 (string_with_suffix_of_int state.memused)
4095 (Hashtbl.length state.tilemap)) 1;
4097 sep ();
4098 src#caption "Layout" 0;
4099 src#caption2 "Dimension"
4100 (fun () ->
4101 Printf.sprintf "%dx%d (virtual %dx%d)"
4102 conf.winw conf.winh
4103 state.w state.maxy)
4105 if conf.debug
4106 then
4107 src#caption2 "Position" (fun () ->
4108 Printf.sprintf "%dx%d" state.x state.y
4110 else
4111 src#caption2 "Visible" (fun () -> describe_location ()) 1
4114 sep ();
4115 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4116 "Save these parameters as global defaults at exit"
4117 (fun () -> conf.bedefault)
4118 (fun v -> conf.bedefault <- v)
4121 sep ();
4122 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4123 src#bool ~offset:0 ~btos "Extended parameters"
4124 (fun () -> !showextended)
4125 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4126 if !showextended
4127 then (
4128 src#bool "checkers"
4129 (fun () -> conf.checkers)
4130 (fun v -> conf.checkers <- v; setcheckers v);
4131 src#bool "update cursor"
4132 (fun () -> conf.updatecurs)
4133 (fun v -> conf.updatecurs <- v);
4134 src#bool "verbose"
4135 (fun () -> conf.verbose)
4136 (fun v -> conf.verbose <- v);
4137 src#bool "invert colors"
4138 (fun () -> conf.invert)
4139 (fun v -> conf.invert <- v);
4140 src#bool "max fit"
4141 (fun () -> conf.maxhfit)
4142 (fun v -> conf.maxhfit <- v);
4143 src#bool "redirect stderr"
4144 (fun () -> conf.redirectstderr)
4145 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4146 src#string "uri launcher"
4147 (fun () -> conf.urilauncher)
4148 (fun v -> conf.urilauncher <- v);
4149 src#string "path launcher"
4150 (fun () -> conf.pathlauncher)
4151 (fun v -> conf.pathlauncher <- v);
4152 src#string "tile size"
4153 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4154 (fun v ->
4156 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4157 conf.tileh <- max 64 w;
4158 conf.tilew <- max 64 h;
4159 flushtiles ();
4160 with exn ->
4161 state.text <- Printf.sprintf "bad tile size `%s': %s"
4162 v (Printexc.to_string exn));
4163 src#int "texture count"
4164 (fun () -> conf.texcount)
4165 (fun v ->
4166 if realloctexts v
4167 then conf.texcount <- v
4168 else showtext '!' " Failed to set texture count please retry later"
4170 src#int "slice height"
4171 (fun () -> conf.sliceheight)
4172 (fun v ->
4173 conf.sliceheight <- v;
4174 wcmd "sliceh %d" conf.sliceheight;
4176 src#int "anti-aliasing level"
4177 (fun () -> conf.aalevel)
4178 (fun v ->
4179 conf.aalevel <- bound v 0 8;
4180 state.anchor <- getanchor ();
4181 opendoc state.path state.password;
4183 src#int "ui font size"
4184 (fun () -> fstate.fontsize)
4185 (fun v -> setfontsize (bound v 5 100));
4186 colorp "background color"
4187 (fun () -> conf.bgcolor)
4188 (fun v -> conf.bgcolor <- v);
4189 src#bool "crop hack"
4190 (fun () -> conf.crophack)
4191 (fun v -> conf.crophack <- v);
4192 src#string "trim fuzz"
4193 (fun () -> irect_to_string conf.trimfuzz)
4194 (fun v ->
4196 conf.trimfuzz <- irect_of_string v;
4197 if conf.trimmargins
4198 then settrim true conf.trimfuzz;
4199 with exn ->
4200 state.text <- Printf.sprintf "bad irect `%s': %s"
4201 v (Printexc.to_string exn)
4203 src#string "throttle"
4204 (fun () ->
4205 match conf.maxwait with
4206 | None -> "show place holder if page is not ready"
4207 | Some time ->
4208 if time = infinity
4209 then "wait for page to fully render"
4210 else
4211 "wait " ^ string_of_float time
4212 ^ " seconds before showing placeholder"
4214 (fun v ->
4216 let f = float_of_string v in
4217 if f <= 0.0
4218 then conf.maxwait <- None
4219 else conf.maxwait <- Some f
4220 with exn ->
4221 state.text <- Printf.sprintf "bad time `%s': %s"
4222 v (Printexc.to_string exn)
4224 src#string "ghyll scroll"
4225 (fun () ->
4226 match conf.ghyllscroll with
4227 | None -> ""
4228 | Some nab -> ghyllscroll_to_string nab
4230 (fun v ->
4232 let gs =
4233 if String.length v = 0
4234 then None
4235 else Some (ghyllscroll_of_string v)
4237 conf.ghyllscroll <- gs
4238 with exn ->
4239 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4240 v (Printexc.to_string exn)
4242 src#string "selection command"
4243 (fun () -> conf.selcmd)
4244 (fun v -> conf.selcmd <- v);
4245 src#colorspace "color space"
4246 (fun () -> colorspace_to_string conf.colorspace)
4247 (fun v ->
4248 conf.colorspace <- colorspace_of_int v;
4249 wcmd "cs %d" v;
4250 load state.layout;
4254 sep ();
4255 src#caption "Document" 0;
4256 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4257 src#caption2 "Pages"
4258 (fun () -> string_of_int state.pagecount) 1;
4259 src#caption2 "Dimensions"
4260 (fun () -> string_of_int (List.length state.pdims)) 1;
4261 if conf.trimmargins
4262 then (
4263 sep ();
4264 src#caption "Trimmed margins" 0;
4265 src#caption2 "Dimensions"
4266 (fun () -> string_of_int (List.length state.pdims)) 1;
4269 src#reset prevmode prevuioh;
4271 fun () ->
4272 state.text <- "";
4273 let prevmode = state.mode
4274 and prevuioh = state.uioh in
4275 fillsrc prevmode prevuioh;
4276 let source = (src :> lvsource) in
4277 let modehash = findkeyhash conf "info" in
4278 state.uioh <- coe (object (self)
4279 inherit listview ~source ~trusted:true ~modehash as super
4280 val mutable m_prevmemused = 0
4281 method infochanged = function
4282 | Memused ->
4283 if m_prevmemused != state.memused
4284 then (
4285 m_prevmemused <- state.memused;
4286 G.postRedisplay "memusedchanged";
4288 | Pdim -> G.postRedisplay "pdimchanged"
4289 | Docinfo -> fillsrc prevmode prevuioh
4291 method key key mask =
4292 if not (Wsi.withctrl mask)
4293 then
4294 match key with
4295 | 0xff51 -> coe (self#updownlevel ~-1)
4296 | 0xff53 -> coe (self#updownlevel 1)
4297 | _ -> super#key key mask
4298 else super#key key mask
4299 end);
4300 G.postRedisplay "info";
4303 let enterhelpmode =
4304 let source =
4305 (object
4306 inherit lvsourcebase
4307 method getitemcount = Array.length state.help
4308 method getitem n =
4309 let s, n, _ = state.help.(n) in
4310 (s, n)
4312 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4313 let optuioh =
4314 if not cancel
4315 then (
4316 m_qsearch <- qsearch;
4317 match state.help.(active) with
4318 | _, _, Action f -> Some (f uioh)
4319 | _ -> Some (uioh)
4321 else None
4323 m_active <- active;
4324 m_first <- first;
4325 m_pan <- pan;
4326 optuioh
4328 method hasaction n =
4329 match state.help.(n) with
4330 | _, _, Action _ -> true
4331 | _ -> false
4333 initializer
4334 m_active <- -1
4335 end)
4336 in fun () ->
4337 let modehash = findkeyhash conf "help" in
4338 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4339 G.postRedisplay "help";
4342 let entermsgsmode =
4343 let msgsource =
4344 let re = Str.regexp "[\r\n]" in
4345 (object
4346 inherit lvsourcebase
4347 val mutable m_items = [||]
4349 method getitemcount = 1 + Array.length m_items
4351 method getitem n =
4352 if n = 0
4353 then "[Clear]", 0
4354 else m_items.(n-1), 0
4356 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4357 ignore uioh;
4358 if not cancel
4359 then (
4360 if active = 0
4361 then Buffer.clear state.errmsgs;
4362 m_qsearch <- qsearch;
4364 m_active <- active;
4365 m_first <- first;
4366 m_pan <- pan;
4367 None
4369 method hasaction n =
4370 n = 0
4372 method reset =
4373 state.newerrmsgs <- false;
4374 let l = Str.split re (Buffer.contents state.errmsgs) in
4375 m_items <- Array.of_list l
4377 initializer
4378 m_active <- 0
4379 end)
4380 in fun () ->
4381 state.text <- "";
4382 msgsource#reset;
4383 let source = (msgsource :> lvsource) in
4384 let modehash = findkeyhash conf "listview" in
4385 state.uioh <- coe (object
4386 inherit listview ~source ~trusted:false ~modehash as super
4387 method display =
4388 if state.newerrmsgs
4389 then msgsource#reset;
4390 super#display
4391 end);
4392 G.postRedisplay "msgs";
4395 let quickbookmark ?title () =
4396 match state.layout with
4397 | [] -> ()
4398 | l :: _ ->
4399 let title =
4400 match title with
4401 | None ->
4402 let sec = Unix.gettimeofday () in
4403 let tm = Unix.localtime sec in
4404 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4405 (l.pageno+1)
4406 tm.Unix.tm_mday
4407 tm.Unix.tm_mon
4408 (tm.Unix.tm_year + 1900)
4409 tm.Unix.tm_hour
4410 tm.Unix.tm_min
4411 | Some title -> title
4413 state.bookmarks <-
4414 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4415 :: state.bookmarks
4418 let doreshape w h =
4419 state.fullscreen <- None;
4420 Wsi.reshape w h;
4423 let setautoscrollspeed step goingdown =
4424 let incr = max 1 ((abs step) / 2) in
4425 let incr = if goingdown then incr else -incr in
4426 let astep = step + incr in
4427 state.autoscroll <- Some astep;
4430 let gotounder = function
4431 | Ulinkgoto (pageno, top) ->
4432 if pageno >= 0
4433 then (
4434 addnav ();
4435 gotopage1 pageno top;
4438 | Ulinkuri s ->
4439 gotouri s
4441 | Uremote (filename, pageno) ->
4442 let path =
4443 if Sys.file_exists filename
4444 then filename
4445 else
4446 let dir = Filename.dirname state.path in
4447 let path = Filename.concat dir filename in
4448 if Sys.file_exists path
4449 then path
4450 else ""
4452 if String.length path > 0
4453 then (
4454 let anchor = getanchor () in
4455 let ranchor = state.path, state.password, anchor in
4456 state.anchor <- (pageno, 0.0);
4457 state.ranchors <- ranchor :: state.ranchors;
4458 opendoc path "";
4460 else showtext '!' ("Could not find " ^ filename)
4462 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4465 let canpan () =
4466 match conf.columns with
4467 | Csplit _ -> true
4468 | _ -> conf.zoom > 1.0
4471 let viewkeyboard key mask =
4472 let enttext te =
4473 let mode = state.mode in
4474 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4475 state.text <- "";
4476 enttext ();
4477 G.postRedisplay "view:enttext"
4479 let ctrl = Wsi.withctrl mask in
4480 match key with
4481 | 81 -> (* Q *)
4482 exit 0
4484 | 0xff63 -> (* insert *)
4485 if conf.angle mod 360 = 0
4486 then (
4487 state.mode <- LinkNav (Ltgendir 0);
4488 gotoy state.y;
4490 else showtext '!' "Keyboard link naviagtion does not work under rotation"
4492 | 0xff1b | 113 -> (* escape / q *)
4493 begin match state.mstate with
4494 | Mzoomrect _ ->
4495 state.mstate <- Mnone;
4496 Wsi.setcursor Wsi.CURSOR_INHERIT;
4497 G.postRedisplay "kill zoom rect";
4498 | _ ->
4499 match state.ranchors with
4500 | [] -> raise Quit
4501 | (path, password, anchor) :: rest ->
4502 state.ranchors <- rest;
4503 state.anchor <- anchor;
4504 opendoc path password
4505 end;
4507 | 0xff08 -> (* backspace *)
4508 let y = getnav ~-1 in
4509 gotoy_and_clear_text y
4511 | 111 -> (* o *)
4512 enteroutlinemode ()
4514 | 117 -> (* u *)
4515 state.rects <- [];
4516 state.text <- "";
4517 G.postRedisplay "dehighlight";
4519 | 47 | 63 -> (* / ? *)
4520 let ondone isforw s =
4521 cbput state.hists.pat s;
4522 state.searchpattern <- s;
4523 search s isforw
4525 let s = String.create 1 in
4526 s.[0] <- Char.chr key;
4527 enttext (s, "", Some (onhist state.hists.pat),
4528 textentry, ondone (key = 47))
4530 | 43 | 0xffab when ctrl -> (* ctrl-+ *)
4531 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4532 setzoom (conf.zoom +. incr)
4534 | 43 | 0xffab -> (* + *)
4535 let ondone s =
4536 let n =
4537 try int_of_string s with exc ->
4538 state.text <- Printf.sprintf "bad integer `%s': %s"
4539 s (Printexc.to_string exc);
4540 max_int
4542 if n != max_int
4543 then (
4544 conf.pagebias <- n;
4545 state.text <- "page bias is now " ^ string_of_int n;
4548 enttext ("page bias: ", "", None, intentry, ondone)
4550 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4551 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4552 setzoom (max 0.01 (conf.zoom -. decr))
4554 | 45 | 0xffad -> (* - *)
4555 let ondone msg = state.text <- msg in
4556 enttext (
4557 "option [acfhilpstvxACPRSZTIS]: ", "", None,
4558 optentry state.mode, ondone
4561 | 48 when ctrl -> (* ctrl-0 *)
4562 setzoom 1.0
4564 | 49 when ctrl -> (* 1 *)
4565 let zoom = zoomforh conf.winw conf.winh state.scrollw in
4566 if zoom < 1.0
4567 then setzoom zoom
4569 | 0xffc6 -> (* f9 *)
4570 togglebirdseye ()
4572 | 57 when ctrl -> (* ctrl-9 *)
4573 togglebirdseye ()
4575 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4576 when not ctrl -> (* 0..9 *)
4577 let ondone s =
4578 let n =
4579 try int_of_string s with exc ->
4580 state.text <- Printf.sprintf "bad integer `%s': %s"
4581 s (Printexc.to_string exc);
4584 if n >= 0
4585 then (
4586 addnav ();
4587 cbput state.hists.pag (string_of_int n);
4588 gotopage1 (n + conf.pagebias - 1) 0;
4591 let pageentry text key =
4592 match Char.unsafe_chr key with
4593 | 'g' -> TEdone text
4594 | _ -> intentry text key
4596 let text = "x" in text.[0] <- Char.chr key;
4597 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
4599 | 98 -> (* b *)
4600 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4601 reshape conf.winw conf.winh;
4603 | 108 -> (* l *)
4604 conf.hlinks <- not conf.hlinks;
4605 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4606 G.postRedisplay "toggle highlightlinks";
4608 | 70 -> (* F *)
4609 state.glinks <- true;
4610 let ondone s =
4611 let n =
4612 try int_of_string s with exc ->
4613 state.text <- Printf.sprintf "bad integer `%s': %s"
4614 s (Printexc.to_string exc);
4617 if n >= 0
4618 then (
4619 let rec loop n = function
4620 | [] -> ()
4621 | l :: rest ->
4622 match getopaque l.pageno with
4623 | None -> loop n rest
4624 | Some opaque ->
4625 let m = getlinkcount opaque in
4626 if n < m
4627 then (
4628 let under = getlink opaque n in
4629 addnav ();
4630 gotounder under;
4632 else loop (n-m) rest
4634 loop n state.layout;
4637 let onkey text key =
4638 match Char.unsafe_chr key with
4639 | 'g' -> TEdone text
4640 | _ -> intentry text key
4642 let mode = state.mode in
4643 state.mode <- Textentry (
4644 (":", "", Some (onhist state.hists.pag), onkey, ondone),
4645 fun _ ->
4646 state.glinks <- false;
4647 state.mode <- mode
4649 state.text <- "";
4650 G.postRedisplay "view:enttext"
4652 | 97 -> (* a *)
4653 begin match state.autoscroll with
4654 | Some step ->
4655 conf.autoscrollstep <- step;
4656 state.autoscroll <- None
4657 | None ->
4658 if conf.autoscrollstep = 0
4659 then state.autoscroll <- Some 1
4660 else state.autoscroll <- Some conf.autoscrollstep
4663 | 112 when ctrl -> (* ctrl-p *)
4664 launchpath ()
4666 | 80 -> (* P *)
4667 conf.presentation <- not conf.presentation;
4668 if conf.presentation
4669 then (
4670 if not conf.scrollbarinpm
4671 then state.scrollw <- 0;
4673 else
4674 state.scrollw <- conf.scrollbw;
4676 showtext ' ' ("presentation mode " ^
4677 if conf.presentation then "on" else "off");
4678 state.anchor <- getanchor ();
4679 represent ()
4681 | 102 -> (* f *)
4682 begin match state.fullscreen with
4683 | None ->
4684 state.fullscreen <- Some (conf.winw, conf.winh);
4685 Wsi.fullscreen ()
4686 | Some (w, h) ->
4687 state.fullscreen <- None;
4688 doreshape w h
4691 | 103 -> (* g *)
4692 gotoy_and_clear_text 0
4694 | 71 -> (* G *)
4695 gotopage1 (state.pagecount - 1) 0
4697 | 112 | 78 -> (* p|N *)
4698 search state.searchpattern false
4700 | 110 | 0xffc0 -> (* n|F3 *)
4701 search state.searchpattern true
4703 | 116 -> (* t *)
4704 begin match state.layout with
4705 | [] -> ()
4706 | l :: _ ->
4707 gotoy_and_clear_text (getpagey l.pageno)
4710 | 32 -> (* ' ' *)
4711 begin match List.rev state.layout with
4712 | [] -> ()
4713 | l :: _ ->
4714 let pageno = min (l.pageno+1) (state.pagecount-1) in
4715 gotoy_and_clear_text (getpagey pageno)
4718 | 0xff9f | 0xffff -> (* delete *)
4719 begin match state.layout with
4720 | [] -> ()
4721 | l :: _ ->
4722 let pageno = max 0 (l.pageno-1) in
4723 gotoy_and_clear_text (getpagey pageno)
4726 | 61 -> (* = *)
4727 showtext ' ' (describe_location ());
4729 | 119 -> (* w *)
4730 begin match state.layout with
4731 | [] -> ()
4732 | l :: _ ->
4733 doreshape (l.pagew + state.scrollw) l.pageh;
4734 G.postRedisplay "w"
4737 | 39 -> (* ' *)
4738 enterbookmarkmode ()
4740 | 104 | 0xffbe -> (* h|F1 *)
4741 enterhelpmode ()
4743 | 105 -> (* i *)
4744 enterinfomode ()
4746 | 101 when conf.redirectstderr -> (* e *)
4747 entermsgsmode ()
4749 | 109 -> (* m *)
4750 let ondone s =
4751 match state.layout with
4752 | l :: _ ->
4753 state.bookmarks <-
4754 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4755 :: state.bookmarks
4756 | _ -> ()
4758 enttext ("bookmark: ", "", None, textentry, ondone)
4760 | 126 -> (* ~ *)
4761 quickbookmark ();
4762 showtext ' ' "Quick bookmark added";
4764 | 122 -> (* z *)
4765 begin match state.layout with
4766 | l :: _ ->
4767 let rect = getpdimrect l.pagedimno in
4768 let w, h =
4769 if conf.crophack
4770 then
4771 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4772 truncate (1.2 *. (rect.(3) -. rect.(0))))
4773 else
4774 (truncate (rect.(1) -. rect.(0)),
4775 truncate (rect.(3) -. rect.(0)))
4777 let w = truncate ((float w)*.conf.zoom)
4778 and h = truncate ((float h)*.conf.zoom) in
4779 if w != 0 && h != 0
4780 then (
4781 state.anchor <- getanchor ();
4782 doreshape (w + state.scrollw) (h + conf.interpagespace)
4784 G.postRedisplay "z";
4786 | [] -> ()
4789 | 50 when ctrl -> (* ctrl-2 *)
4790 let maxw = getmaxw () in
4791 if maxw > 0.0
4792 then setzoom (maxw /. float conf.winw)
4794 | 60 | 62 -> (* < > *)
4795 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
4797 | 91 | 93 -> (* [ ] *)
4798 conf.colorscale <-
4799 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
4801 G.postRedisplay "brightness";
4803 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
4804 setzoom state.prevzoom
4806 | 107 | 0xff52 -> (* k up *)
4807 begin match state.autoscroll with
4808 | None ->
4809 begin match state.mode with
4810 | Birdseye beye -> upbirdseye 1 beye
4811 | _ ->
4812 if ctrl
4813 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
4814 else gotoy_and_clear_text (clamp (-conf.scrollstep))
4816 | Some n ->
4817 setautoscrollspeed n false
4820 | 106 | 0xff54 -> (* j down *)
4821 begin match state.autoscroll with
4822 | None ->
4823 begin match state.mode with
4824 | Birdseye beye -> downbirdseye 1 beye
4825 | _ ->
4826 if ctrl
4827 then gotoy_and_clear_text (clamp (conf.winh/2))
4828 else gotoy_and_clear_text (clamp conf.scrollstep)
4830 | Some n ->
4831 setautoscrollspeed n true
4834 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
4835 if canpan ()
4836 then
4837 let dx =
4838 if ctrl
4839 then conf.winw / 2
4840 else 10
4842 let dx = if key = 0xff51 then dx else -dx in
4843 state.x <- state.x + dx;
4844 gotoy_and_clear_text state.y
4845 else (
4846 state.text <- "";
4847 G.postRedisplay "lef/right"
4850 | 0xff55 -> (* prior *)
4851 let y =
4852 if ctrl
4853 then
4854 match state.layout with
4855 | [] -> state.y
4856 | l :: _ -> state.y - l.pagey
4857 else
4858 clamp (-conf.winh)
4860 gotoghyll y
4862 | 0xff56 -> (* next *)
4863 let y =
4864 if ctrl
4865 then
4866 match List.rev state.layout with
4867 | [] -> state.y
4868 | l :: _ -> getpagey l.pageno
4869 else
4870 clamp conf.winh
4872 gotoghyll y
4874 | 0xff50 -> gotoghyll 0
4875 | 0xff57 -> gotoghyll (clamp state.maxy)
4876 | 0xff53 when Wsi.withalt mask ->
4877 gotoghyll (getnav ~-1)
4878 | 0xff51 when Wsi.withalt mask ->
4879 gotoghyll (getnav 1)
4881 | 114 -> (* r *)
4882 state.anchor <- getanchor ();
4883 opendoc state.path state.password
4885 | 118 when conf.debug -> (* v *)
4886 state.rects <- [];
4887 List.iter (fun l ->
4888 match getopaque l.pageno with
4889 | None -> ()
4890 | Some opaque ->
4891 let x0, y0, x1, y1 = pagebbox opaque in
4892 let a,b = float x0, float y0 in
4893 let c,d = float x1, float y0 in
4894 let e,f = float x1, float y1 in
4895 let h,j = float x0, float y1 in
4896 let rect = (a,b,c,d,e,f,h,j) in
4897 debugrect rect;
4898 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4899 ) state.layout;
4900 G.postRedisplay "v";
4902 | _ ->
4903 vlog "huh? %s" (Wsi.keyname key)
4906 let linknavkeyboard key mask linknav =
4907 let getpage pageno =
4908 let rec loop = function
4909 | [] -> None
4910 | l :: _ when l.pageno = pageno -> Some l
4911 | _ :: rest -> loop rest
4912 in loop state.layout
4914 let doexact (pageno, n) =
4915 match getopaque pageno, getpage pageno with
4916 | Some opaque, Some l ->
4917 if key = 0xff0d
4918 then
4919 let under = getlink opaque n in
4920 G.postRedisplay "link gotounder";
4921 gotounder under;
4922 state.mode <- View;
4923 else
4924 let opt, dir =
4925 match key with
4926 | 0xff50 -> (* home *)
4927 Some (findlink opaque LDfirst), -1
4929 | 0xff57 -> (* end *)
4930 Some (findlink opaque LDlast), 1
4932 | 0xff51 -> (* left *)
4933 Some (findlink opaque (LDleft n)), -1
4935 | 0xff53 -> (* right *)
4936 Some (findlink opaque (LDright n)), 1
4938 | 0xff52 -> (* up *)
4939 Some (findlink opaque (LDup n)), -1
4941 | 0xff54 -> (* down *)
4942 Some (findlink opaque (LDdown n)), 1
4944 | _ -> None, 0
4946 let pwl l dir =
4947 begin match findpwl l.pageno dir with
4948 | Pwlnotfound -> ()
4949 | Pwl pageno ->
4950 let notfound dir =
4951 state.mode <- LinkNav (Ltgendir dir);
4952 let y, h = getpageyh pageno in
4953 let y =
4954 if dir < 0
4955 then y + h - conf.winh
4956 else y
4958 gotoy y
4960 begin match getopaque pageno, getpage pageno with
4961 | Some opaque, Some _ ->
4962 let link =
4963 let ld = if dir > 0 then LDfirst else LDlast in
4964 findlink opaque ld
4966 begin match link with
4967 | Lfound m ->
4968 showlinktype (getlink opaque m);
4969 state.mode <- LinkNav (Ltexact (pageno, m));
4970 G.postRedisplay "linknav jpage";
4971 | _ -> notfound dir
4972 end;
4973 | _ -> notfound dir
4974 end;
4975 end;
4977 begin match opt with
4978 | Some Lnotfound -> pwl l dir;
4979 | Some (Lfound m) ->
4980 if m = n
4981 then pwl l dir
4982 else (
4983 let _, y0, _, y1 = getlinkrect opaque m in
4984 if y0 < l.pagey
4985 then gotopage1 l.pageno y0
4986 else (
4987 let d = fstate.fontsize + 1 in
4988 if y1 - l.pagey > l.pagevh - d
4989 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh + d)
4990 else G.postRedisplay "linknav";
4992 showlinktype (getlink opaque m);
4993 state.mode <- LinkNav (Ltexact (l.pageno, m));
4996 | None -> viewkeyboard key mask
4997 end;
4998 | _ -> viewkeyboard key mask
5000 if key = 0xff63
5001 then (
5002 state.mode <- View;
5003 G.postRedisplay "leave linknav"
5005 else
5006 match linknav with
5007 | Ltgendir _ -> viewkeyboard key mask
5008 | Ltexact exact -> doexact exact
5011 let keyboard key mask =
5012 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5013 then wcmd "interrupt"
5014 else state.uioh <- state.uioh#key key mask
5017 let birdseyekeyboard key mask
5018 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5019 let incr =
5020 match conf.columns with
5021 | Csingle -> 1
5022 | Cmulti ((c, _, _), _) -> c
5023 | Csplit _ -> failwith "bird's eye split mode"
5025 match key with
5026 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5027 let y, h = getpageyh pageno in
5028 let top = (conf.winh - h) / 2 in
5029 gotoy (max 0 (y - top))
5030 | 0xff0d -> leavebirdseye beye false
5031 | 0xff1b -> leavebirdseye beye true (* escape *)
5032 | 0xff52 -> upbirdseye incr beye (* prior *)
5033 | 0xff54 -> downbirdseye incr beye (* next *)
5034 | 0xff51 -> upbirdseye 1 beye (* up *)
5035 | 0xff53 -> downbirdseye 1 beye (* down *)
5037 | 0xff55 ->
5038 begin match state.layout with
5039 | l :: _ ->
5040 if l.pagey != 0
5041 then (
5042 state.mode <- Birdseye (
5043 oconf, leftx, l.pageno, hooverpageno, anchor
5045 gotopage1 l.pageno 0;
5047 else (
5048 let layout = layout (state.y-conf.winh) conf.winh in
5049 match layout with
5050 | [] -> gotoy (clamp (-conf.winh))
5051 | l :: _ ->
5052 state.mode <- Birdseye (
5053 oconf, leftx, l.pageno, hooverpageno, anchor
5055 gotopage1 l.pageno 0
5058 | [] -> gotoy (clamp (-conf.winh))
5059 end;
5061 | 0xff56 ->
5062 begin match List.rev state.layout with
5063 | l :: _ ->
5064 let layout = layout (state.y + conf.winh) conf.winh in
5065 begin match layout with
5066 | [] ->
5067 let incr = l.pageh - l.pagevh in
5068 if incr = 0
5069 then (
5070 state.mode <-
5071 Birdseye (
5072 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5074 G.postRedisplay "birdseye pagedown";
5076 else gotoy (clamp (incr + conf.interpagespace*2));
5078 | l :: _ ->
5079 state.mode <-
5080 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5081 gotopage1 l.pageno 0;
5084 | [] -> gotoy (clamp conf.winh)
5085 end;
5087 | 0xff50 ->
5088 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5089 gotopage1 0 0
5091 | 0xff57 ->
5092 let pageno = state.pagecount - 1 in
5093 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5094 if not (pagevisible state.layout pageno)
5095 then
5096 let h =
5097 match List.rev state.pdims with
5098 | [] -> conf.winh
5099 | (_, _, h, _) :: _ -> h
5101 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
5102 else G.postRedisplay "birdseye end";
5103 | _ -> viewkeyboard key mask
5106 let drawpage l linkindexbase =
5107 let color =
5108 match state.mode with
5109 | Textentry _ -> scalecolor 0.4
5110 | LinkNav _
5111 | View -> scalecolor 1.0
5112 | Birdseye (_, _, pageno, hooverpageno, _) ->
5113 if l.pageno = hooverpageno
5114 then scalecolor 0.9
5115 else (
5116 if l.pageno = pageno
5117 then scalecolor 1.0
5118 else scalecolor 0.8
5121 drawtiles l color;
5122 begin match getopaque l.pageno with
5123 | Some opaque ->
5124 if tileready l l.pagex l.pagey
5125 then
5126 let x = l.pagedispx - l.pagex
5127 and y = l.pagedispy - l.pagey in
5128 let hlmask = (if conf.hlinks then 1 else 0)
5129 + (if state.glinks && not (isbirdseye state.mode) then 2 else 0)
5131 postprocess opaque hlmask x y linkindexbase;
5132 else 0
5134 | _ -> 0
5135 end;
5138 let scrollindicator () =
5139 let sbw, ph, sh = state.uioh#scrollph in
5140 let sbh, pw, sw = state.uioh#scrollpw in
5142 GlDraw.color (0.64, 0.64, 0.64);
5143 GlDraw.rect
5144 (float (conf.winw - sbw), 0.)
5145 (float conf.winw, float conf.winh)
5147 GlDraw.rect
5148 (0., float (conf.winh - sbh))
5149 (float (conf.winw - state.scrollw - 1), float conf.winh)
5151 GlDraw.color (0.0, 0.0, 0.0);
5153 GlDraw.rect
5154 (float (conf.winw - sbw), ph)
5155 (float conf.winw, ph +. sh)
5157 GlDraw.rect
5158 (pw, float (conf.winh - sbh))
5159 (pw +. sw, float conf.winh)
5163 let showsel () =
5164 match state.mstate with
5165 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5168 | Msel ((x0, y0), (x1, y1)) ->
5169 let rec loop = function
5170 | l :: ls ->
5171 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5172 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5173 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5174 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5175 then
5176 match getopaque l.pageno with
5177 | Some opaque ->
5178 let x0, y0 = pagetranslatepoint l x0 y0 in
5179 let x1, y1 = pagetranslatepoint l x1 y1 in
5180 seltext opaque (x0, y0, x1, y1);
5181 | _ -> ()
5182 else loop ls
5183 | [] -> ()
5185 loop state.layout
5188 let showrects rects =
5189 Gl.enable `blend;
5190 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5191 GlDraw.polygon_mode `both `fill;
5192 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5193 List.iter
5194 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5195 List.iter (fun l ->
5196 if l.pageno = pageno
5197 then (
5198 let dx = float (l.pagedispx - l.pagex) in
5199 let dy = float (l.pagedispy - l.pagey) in
5200 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5201 GlDraw.begins `quads;
5203 GlDraw.vertex2 (x0+.dx, y0+.dy);
5204 GlDraw.vertex2 (x1+.dx, y1+.dy);
5205 GlDraw.vertex2 (x2+.dx, y2+.dy);
5206 GlDraw.vertex2 (x3+.dx, y3+.dy);
5208 GlDraw.ends ();
5210 ) state.layout
5211 ) rects
5213 Gl.disable `blend;
5216 let display () =
5217 GlClear.color (scalecolor2 conf.bgcolor);
5218 GlClear.clear [`color];
5219 let rec loop linkindexbase = function
5220 | l :: rest ->
5221 let linkindexbase = linkindexbase + drawpage l linkindexbase in
5222 loop linkindexbase rest
5223 | [] -> ()
5225 loop 0 state.layout;
5226 let rects =
5227 match state.mode with
5228 | LinkNav (Ltexact (pageno, linkno)) ->
5229 begin match getopaque pageno with
5230 | Some opaque ->
5231 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5232 (pageno, 5, (
5233 float x0, float y0,
5234 float x1, float y0,
5235 float x1, float y1,
5236 float x0, float y1)
5237 ) :: state.rects
5238 | None -> state.rects
5240 | _ -> state.rects
5242 showrects rects;
5243 showsel ();
5244 state.uioh#display;
5245 begin match state.mstate with
5246 | Mzoomrect ((x0, y0), (x1, y1)) ->
5247 Gl.enable `blend;
5248 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5249 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5250 GlDraw.rect (float x0, float y0)
5251 (float x1, float y1);
5252 Gl.disable `blend;
5253 | _ -> ()
5254 end;
5255 enttext ();
5256 scrollindicator ();
5257 Wsi.swapb ();
5260 let zoomrect x y x1 y1 =
5261 let x0 = min x x1
5262 and x1 = max x x1
5263 and y0 = min y y1 in
5264 gotoy (state.y + y0);
5265 state.anchor <- getanchor ();
5266 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5267 let margin =
5268 if state.w < conf.winw - state.scrollw
5269 then (conf.winw - state.scrollw - state.w) / 2
5270 else 0
5272 state.x <- (state.x + margin) - x0;
5273 setzoom zoom;
5274 Wsi.setcursor Wsi.CURSOR_INHERIT;
5275 state.mstate <- Mnone;
5278 let scrollx x =
5279 let winw = conf.winw - state.scrollw - 1 in
5280 let s = float x /. float winw in
5281 let destx = truncate (float (state.w + winw) *. s) in
5282 state.x <- winw - destx;
5283 gotoy_and_clear_text state.y;
5284 state.mstate <- Mscrollx;
5287 let scrolly y =
5288 let s = float y /. float conf.winh in
5289 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5290 gotoy_and_clear_text desty;
5291 state.mstate <- Mscrolly;
5294 let viewmouse button down x y mask =
5295 match button with
5296 | n when (n == 4 || n == 5) && not down ->
5297 if Wsi.withctrl mask
5298 then (
5299 match state.mstate with
5300 | Mzoom (oldn, i) ->
5301 if oldn = n
5302 then (
5303 if i = 2
5304 then
5305 let incr =
5306 match n with
5307 | 5 ->
5308 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5309 | _ ->
5310 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5312 let zoom = conf.zoom -. incr in
5313 setzoom zoom;
5314 state.mstate <- Mzoom (n, 0);
5315 else
5316 state.mstate <- Mzoom (n, i+1);
5318 else state.mstate <- Mzoom (n, 0)
5320 | _ -> state.mstate <- Mzoom (n, 0)
5322 else (
5323 match state.autoscroll with
5324 | Some step -> setautoscrollspeed step (n=4)
5325 | None ->
5326 let incr =
5327 if n = 4
5328 then -conf.scrollstep
5329 else conf.scrollstep
5331 let incr = incr * 2 in
5332 let y = clamp incr in
5333 gotoy_and_clear_text y
5336 | 1 when Wsi.withctrl mask ->
5337 if down
5338 then (
5339 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5340 state.mstate <- Mpan (x, y)
5342 else
5343 state.mstate <- Mnone
5345 | 3 ->
5346 if down
5347 then (
5348 Wsi.setcursor Wsi.CURSOR_CYCLE;
5349 let p = (x, y) in
5350 state.mstate <- Mzoomrect (p, p)
5352 else (
5353 match state.mstate with
5354 | Mzoomrect ((x0, y0), _) ->
5355 if abs (x-x0) > 10 && abs (y - y0) > 10
5356 then zoomrect x0 y0 x y
5357 else (
5358 state.mstate <- Mnone;
5359 Wsi.setcursor Wsi.CURSOR_INHERIT;
5360 G.postRedisplay "kill accidental zoom rect";
5362 | _ ->
5363 Wsi.setcursor Wsi.CURSOR_INHERIT;
5364 state.mstate <- Mnone
5367 | 1 when x > conf.winw - state.scrollw ->
5368 if down
5369 then
5370 let _, position, sh = state.uioh#scrollph in
5371 if y > truncate position && y < truncate (position +. sh)
5372 then state.mstate <- Mscrolly
5373 else scrolly y
5374 else
5375 state.mstate <- Mnone
5377 | 1 when y > conf.winh - state.hscrollh ->
5378 if down
5379 then
5380 let _, position, sw = state.uioh#scrollpw in
5381 if x > truncate position && x < truncate (position +. sw)
5382 then state.mstate <- Mscrollx
5383 else scrollx x
5384 else
5385 state.mstate <- Mnone
5387 | 1 ->
5388 let dest = if down then getunder x y else Unone in
5389 begin match dest with
5390 | Ulinkgoto _
5391 | Ulinkuri _
5392 | Uremote _
5393 | Uunexpected _ | Ulaunch _ | Unamed _ ->
5394 gotounder dest
5396 | Unone when down ->
5397 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5398 state.mstate <- Mpan (x, y);
5400 | Unone | Utext _ ->
5401 if down
5402 then (
5403 if conf.angle mod 360 = 0
5404 then (
5405 state.mstate <- Msel ((x, y), (x, y));
5406 G.postRedisplay "mouse select";
5409 else (
5410 match state.mstate with
5411 | Mnone -> ()
5413 | Mzoom _ | Mscrollx | Mscrolly ->
5414 state.mstate <- Mnone
5416 | Mzoomrect ((x0, y0), _) ->
5417 zoomrect x0 y0 x y
5419 | Mpan _ ->
5420 Wsi.setcursor Wsi.CURSOR_INHERIT;
5421 state.mstate <- Mnone
5423 | Msel ((_, y0), (_, y1)) ->
5424 let rec loop = function
5425 | [] -> ()
5426 | l :: rest ->
5427 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5428 || ((y1 >= l.pagedispy
5429 && y1 <= (l.pagedispy + l.pagevh)))
5430 then
5431 match getopaque l.pageno with
5432 | Some opaque ->
5433 copysel conf.selcmd opaque;
5434 G.postRedisplay "copysel"
5435 | _ -> ()
5436 else loop rest
5438 loop state.layout;
5439 Wsi.setcursor Wsi.CURSOR_INHERIT;
5440 state.mstate <- Mnone;
5444 | _ -> ()
5447 let birdseyemouse button down x y mask
5448 (conf, leftx, _, hooverpageno, anchor) =
5449 match button with
5450 | 1 when down ->
5451 let rec loop = function
5452 | [] -> ()
5453 | l :: rest ->
5454 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5455 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5456 then (
5457 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5459 else loop rest
5461 loop state.layout
5462 | 3 -> ()
5463 | _ -> viewmouse button down x y mask
5466 let mouse button down x y mask =
5467 state.uioh <- state.uioh#button button down x y mask;
5470 let motion ~x ~y =
5471 state.uioh <- state.uioh#motion x y
5474 let pmotion ~x ~y =
5475 state.uioh <- state.uioh#pmotion x y;
5478 let uioh = object
5479 method display = ()
5481 method key key mask =
5482 begin match state.mode with
5483 | Textentry textentry -> textentrykeyboard key mask textentry
5484 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5485 | View -> viewkeyboard key mask
5486 | LinkNav linknav -> linknavkeyboard key mask linknav
5487 end;
5488 state.uioh
5490 method button button bstate x y mask =
5491 begin match state.mode with
5492 | LinkNav _
5493 | View -> viewmouse button bstate x y mask
5494 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5495 | Textentry _ -> ()
5496 end;
5497 state.uioh
5499 method motion x y =
5500 begin match state.mode with
5501 | Textentry _ -> ()
5502 | View | Birdseye _ | LinkNav _ ->
5503 match state.mstate with
5504 | Mzoom _ | Mnone -> ()
5506 | Mpan (x0, y0) ->
5507 let dx = x - x0
5508 and dy = y0 - y in
5509 state.mstate <- Mpan (x, y);
5510 if canpan ()
5511 then state.x <- state.x + dx;
5512 let y = clamp dy in
5513 gotoy_and_clear_text y
5515 | Msel (a, _) ->
5516 state.mstate <- Msel (a, (x, y));
5517 G.postRedisplay "motion select";
5519 | Mscrolly ->
5520 let y = min conf.winh (max 0 y) in
5521 scrolly y
5523 | Mscrollx ->
5524 let x = min conf.winw (max 0 x) in
5525 scrollx x
5527 | Mzoomrect (p0, _) ->
5528 state.mstate <- Mzoomrect (p0, (x, y));
5529 G.postRedisplay "motion zoomrect";
5530 end;
5531 state.uioh
5533 method pmotion x y =
5534 begin match state.mode with
5535 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5536 let rec loop = function
5537 | [] ->
5538 if hooverpageno != -1
5539 then (
5540 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5541 G.postRedisplay "pmotion birdseye no hoover";
5543 | l :: rest ->
5544 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5545 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5546 then (
5547 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5548 G.postRedisplay "pmotion birdseye hoover";
5550 else loop rest
5552 loop state.layout
5554 | Textentry _ -> ()
5556 | LinkNav _
5557 | View ->
5558 match state.mstate with
5559 | Mnone -> updateunder x y
5560 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5562 end;
5563 state.uioh
5565 method infochanged _ = ()
5567 method scrollph =
5568 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5569 let p, h = scrollph state.y maxy in
5570 state.scrollw, p, h
5572 method scrollpw =
5573 let winw = conf.winw - state.scrollw - 1 in
5574 let fwinw = float winw in
5575 let sw =
5576 let sw = fwinw /. float state.w in
5577 let sw = fwinw *. sw in
5578 max sw (float conf.scrollh)
5580 let position, sw =
5581 let f = state.w+winw in
5582 let r = float (winw-state.x) /. float f in
5583 let p = fwinw *. r in
5584 p-.sw/.2., sw
5586 let sw =
5587 if position +. sw > fwinw
5588 then fwinw -. position
5589 else sw
5591 state.hscrollh, position, sw
5593 method modehash =
5594 let modename =
5595 match state.mode with
5596 | LinkNav _ -> "links"
5597 | Textentry _ -> "textentry"
5598 | Birdseye _ -> "birdseye"
5599 | View -> "global"
5601 findkeyhash conf modename
5602 end;;
5604 module Config =
5605 struct
5606 open Parser
5608 let fontpath = ref "";;
5610 module KeyMap =
5611 Map.Make (struct type t = (int * int) let compare = compare end);;
5613 let unent s =
5614 let l = String.length s in
5615 let b = Buffer.create l in
5616 unent b s 0 l;
5617 Buffer.contents b;
5620 let home =
5621 try Sys.getenv "HOME"
5622 with exn ->
5623 prerr_endline
5624 ("Can not determine home directory location: " ^
5625 Printexc.to_string exn);
5629 let modifier_of_string = function
5630 | "alt" -> Wsi.altmask
5631 | "shift" -> Wsi.shiftmask
5632 | "ctrl" | "control" -> Wsi.ctrlmask
5633 | "meta" -> Wsi.metamask
5634 | _ -> 0
5637 let key_of_string =
5638 let r = Str.regexp "-" in
5639 fun s ->
5640 let elems = Str.full_split r s in
5641 let f n k m =
5642 let g s =
5643 let m1 = modifier_of_string s in
5644 if m1 = 0
5645 then (Wsi.namekey s, m)
5646 else (k, m lor m1)
5647 in function
5648 | Str.Delim s when n land 1 = 0 -> g s
5649 | Str.Text s -> g s
5650 | Str.Delim _ -> (k, m)
5652 let rec loop n k m = function
5653 | [] -> (k, m)
5654 | x :: xs ->
5655 let k, m = f n k m x in
5656 loop (n+1) k m xs
5658 loop 0 0 0 elems
5661 let keys_of_string =
5662 let r = Str.regexp "[ \t]" in
5663 fun s ->
5664 let elems = Str.split r s in
5665 List.map key_of_string elems
5668 let copykeyhashes c =
5669 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
5672 let config_of c attrs =
5673 let apply c k v =
5675 match k with
5676 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5677 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5678 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5679 | "preload" -> { c with preload = bool_of_string v }
5680 | "page-bias" -> { c with pagebias = int_of_string v }
5681 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5682 | "auto-scroll-step" ->
5683 { c with autoscrollstep = max 0 (int_of_string v) }
5684 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5685 | "crop-hack" -> { c with crophack = bool_of_string v }
5686 | "throttle" ->
5687 let mw =
5688 match String.lowercase v with
5689 | "true" -> Some infinity
5690 | "false" -> None
5691 | f -> Some (float_of_string f)
5693 { c with maxwait = mw}
5694 | "highlight-links" -> { c with hlinks = bool_of_string v }
5695 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5696 | "vertical-margin" ->
5697 { c with interpagespace = max 0 (int_of_string v) }
5698 | "zoom" ->
5699 let zoom = float_of_string v /. 100. in
5700 let zoom = max zoom 0.0 in
5701 { c with zoom = zoom }
5702 | "presentation" -> { c with presentation = bool_of_string v }
5703 | "rotation-angle" -> { c with angle = int_of_string v }
5704 | "width" -> { c with winw = max 20 (int_of_string v) }
5705 | "height" -> { c with winh = max 20 (int_of_string v) }
5706 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5707 | "proportional-display" -> { c with proportional = bool_of_string v }
5708 | "pixmap-cache-size" ->
5709 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5710 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5711 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5712 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5713 | "persistent-location" -> { c with jumpback = bool_of_string v }
5714 | "background-color" -> { c with bgcolor = color_of_string v }
5715 | "scrollbar-in-presentation" ->
5716 { c with scrollbarinpm = bool_of_string v }
5717 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5718 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5719 | "mupdf-store-size" ->
5720 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5721 | "checkers" -> { c with checkers = bool_of_string v }
5722 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5723 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5724 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5725 | "uri-launcher" -> { c with urilauncher = unent v }
5726 | "path-launcher" -> { c with pathlauncher = unent v }
5727 | "color-space" -> { c with colorspace = colorspace_of_string v }
5728 | "invert-colors" -> { c with invert = bool_of_string v }
5729 | "brightness" -> { c with colorscale = float_of_string v }
5730 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5731 | "ghyllscroll" ->
5732 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5733 | "columns" ->
5734 let (n, _, _) as nab = multicolumns_of_string v in
5735 if n < 0
5736 then { c with columns = Csplit (-n, [||]) }
5737 else { c with columns = Cmulti (nab, [||]) }
5738 | "birds-eye-columns" ->
5739 { c with beyecolumns = Some (max (int_of_string v) 2) }
5740 | "selection-command" -> { c with selcmd = unent v }
5741 | "update-cursor" -> { c with updatecurs = bool_of_string v }
5742 | _ -> c
5743 with exn ->
5744 prerr_endline ("Error processing attribute (`" ^
5745 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5748 let rec fold c = function
5749 | [] -> c
5750 | (k, v) :: rest ->
5751 let c = apply c k v in
5752 fold c rest
5754 fold { c with keyhashes = copykeyhashes c } attrs;
5757 let fromstring f pos n v d =
5758 try f v
5759 with exn ->
5760 dolog "Error processing attribute (%S=%S) at %d\n%s"
5761 n v pos (Printexc.to_string exn)
5766 let bookmark_of attrs =
5767 let rec fold title page rely = function
5768 | ("title", v) :: rest -> fold v page rely rest
5769 | ("page", v) :: rest -> fold title v rely rest
5770 | ("rely", v) :: rest -> fold title page v rest
5771 | _ :: rest -> fold title page rely rest
5772 | [] -> title, page, rely
5774 fold "invalid" "0" "0" attrs
5777 let doc_of attrs =
5778 let rec fold path page rely pan = function
5779 | ("path", v) :: rest -> fold v page rely pan rest
5780 | ("page", v) :: rest -> fold path v rely pan rest
5781 | ("rely", v) :: rest -> fold path page v pan rest
5782 | ("pan", v) :: rest -> fold path page rely v rest
5783 | _ :: rest -> fold path page rely pan rest
5784 | [] -> path, page, rely, pan
5786 fold "" "0" "0" "0" attrs
5789 let map_of attrs =
5790 let rec fold rs ls = function
5791 | ("out", v) :: rest -> fold v ls rest
5792 | ("in", v) :: rest -> fold rs v rest
5793 | _ :: rest -> fold ls rs rest
5794 | [] -> ls, rs
5796 fold "" "" attrs
5799 let setconf dst src =
5800 dst.scrollbw <- src.scrollbw;
5801 dst.scrollh <- src.scrollh;
5802 dst.icase <- src.icase;
5803 dst.preload <- src.preload;
5804 dst.pagebias <- src.pagebias;
5805 dst.verbose <- src.verbose;
5806 dst.scrollstep <- src.scrollstep;
5807 dst.maxhfit <- src.maxhfit;
5808 dst.crophack <- src.crophack;
5809 dst.autoscrollstep <- src.autoscrollstep;
5810 dst.maxwait <- src.maxwait;
5811 dst.hlinks <- src.hlinks;
5812 dst.underinfo <- src.underinfo;
5813 dst.interpagespace <- src.interpagespace;
5814 dst.zoom <- src.zoom;
5815 dst.presentation <- src.presentation;
5816 dst.angle <- src.angle;
5817 dst.winw <- src.winw;
5818 dst.winh <- src.winh;
5819 dst.savebmarks <- src.savebmarks;
5820 dst.memlimit <- src.memlimit;
5821 dst.proportional <- src.proportional;
5822 dst.texcount <- src.texcount;
5823 dst.sliceheight <- src.sliceheight;
5824 dst.thumbw <- src.thumbw;
5825 dst.jumpback <- src.jumpback;
5826 dst.bgcolor <- src.bgcolor;
5827 dst.scrollbarinpm <- src.scrollbarinpm;
5828 dst.tilew <- src.tilew;
5829 dst.tileh <- src.tileh;
5830 dst.mustoresize <- src.mustoresize;
5831 dst.checkers <- src.checkers;
5832 dst.aalevel <- src.aalevel;
5833 dst.trimmargins <- src.trimmargins;
5834 dst.trimfuzz <- src.trimfuzz;
5835 dst.urilauncher <- src.urilauncher;
5836 dst.colorspace <- src.colorspace;
5837 dst.invert <- src.invert;
5838 dst.colorscale <- src.colorscale;
5839 dst.redirectstderr <- src.redirectstderr;
5840 dst.ghyllscroll <- src.ghyllscroll;
5841 dst.columns <- src.columns;
5842 dst.beyecolumns <- src.beyecolumns;
5843 dst.selcmd <- src.selcmd;
5844 dst.updatecurs <- src.updatecurs;
5845 dst.pathlauncher <- src.pathlauncher;
5846 dst.keyhashes <- copykeyhashes src;
5849 let get s =
5850 let h = Hashtbl.create 10 in
5851 let dc = { defconf with angle = defconf.angle } in
5852 let rec toplevel v t spos _ =
5853 match t with
5854 | Vdata | Vcdata | Vend -> v
5855 | Vopen ("llppconfig", _, closed) ->
5856 if closed
5857 then v
5858 else { v with f = llppconfig }
5859 | Vopen _ ->
5860 error "unexpected subelement at top level" s spos
5861 | Vclose _ -> error "unexpected close at top level" s spos
5863 and llppconfig v t spos _ =
5864 match t with
5865 | Vdata | Vcdata -> v
5866 | Vend -> error "unexpected end of input in llppconfig" s spos
5867 | Vopen ("defaults", attrs, closed) ->
5868 let c = config_of dc attrs in
5869 setconf dc c;
5870 if closed
5871 then v
5872 else { v with f = defaults }
5874 | Vopen ("ui-font", attrs, closed) ->
5875 let rec getsize size = function
5876 | [] -> size
5877 | ("size", v) :: rest ->
5878 let size =
5879 fromstring int_of_string spos "size" v fstate.fontsize in
5880 getsize size rest
5881 | l -> getsize size l
5883 fstate.fontsize <- getsize fstate.fontsize attrs;
5884 if closed
5885 then v
5886 else { v with f = uifont (Buffer.create 10) }
5888 | Vopen ("doc", attrs, closed) ->
5889 let pathent, spage, srely, span = doc_of attrs in
5890 let path = unent pathent
5891 and pageno = fromstring int_of_string spos "page" spage 0
5892 and rely = fromstring float_of_string spos "rely" srely 0.0
5893 and pan = fromstring int_of_string spos "pan" span 0 in
5894 let c = config_of dc attrs in
5895 let anchor = (pageno, rely) in
5896 if closed
5897 then (Hashtbl.add h path (c, [], pan, anchor); v)
5898 else { v with f = doc path pan anchor c [] }
5900 | Vopen _ ->
5901 error "unexpected subelement in llppconfig" s spos
5903 | Vclose "llppconfig" -> { v with f = toplevel }
5904 | Vclose _ -> error "unexpected close in llppconfig" s spos
5906 and defaults v t spos _ =
5907 match t with
5908 | Vdata | Vcdata -> v
5909 | Vend -> error "unexpected end of input in defaults" s spos
5910 | Vopen ("keymap", attrs, closed) ->
5911 let modename =
5912 try List.assoc "mode" attrs
5913 with Not_found -> "global" in
5914 if closed
5915 then v
5916 else
5917 let ret keymap =
5918 let h = findkeyhash dc modename in
5919 KeyMap.iter (Hashtbl.replace h) keymap;
5920 defaults
5922 { v with f = pkeymap ret KeyMap.empty }
5924 | Vopen (_, _, _) ->
5925 error "unexpected subelement in defaults" s spos
5927 | Vclose "defaults" ->
5928 { v with f = llppconfig }
5930 | Vclose _ -> error "unexpected close in defaults" s spos
5932 and uifont b v t spos epos =
5933 match t with
5934 | Vdata | Vcdata ->
5935 Buffer.add_substring b s spos (epos - spos);
5937 | Vopen (_, _, _) ->
5938 error "unexpected subelement in ui-font" s spos
5939 | Vclose "ui-font" ->
5940 if String.length !fontpath = 0
5941 then fontpath := Buffer.contents b;
5942 { v with f = llppconfig }
5943 | Vclose _ -> error "unexpected close in ui-font" s spos
5944 | Vend -> error "unexpected end of input in ui-font" s spos
5946 and doc path pan anchor c bookmarks v t spos _ =
5947 match t with
5948 | Vdata | Vcdata -> v
5949 | Vend -> error "unexpected end of input in doc" s spos
5950 | Vopen ("bookmarks", _, closed) ->
5951 if closed
5952 then v
5953 else { v with f = pbookmarks path pan anchor c bookmarks }
5955 | Vopen ("keymap", attrs, closed) ->
5956 let modename =
5957 try List.assoc "mode" attrs
5958 with Not_found -> "global"
5960 if closed
5961 then v
5962 else
5963 let ret keymap =
5964 let h = findkeyhash c modename in
5965 KeyMap.iter (Hashtbl.replace h) keymap;
5966 doc path pan anchor c bookmarks
5968 { v with f = pkeymap ret KeyMap.empty }
5970 | Vopen (_, _, _) ->
5971 error "unexpected subelement in doc" s spos
5973 | Vclose "doc" ->
5974 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5975 { v with f = llppconfig }
5977 | Vclose _ -> error "unexpected close in doc" s spos
5979 and pkeymap ret keymap v t spos _ =
5980 match t with
5981 | Vdata | Vcdata -> v
5982 | Vend -> error "unexpected end of input in keymap" s spos
5983 | Vopen ("map", attrs, closed) ->
5984 let r, l = map_of attrs in
5985 let kss = fromstring keys_of_string spos "in" r [] in
5986 let lss = fromstring keys_of_string spos "out" l [] in
5987 let keymap =
5988 match kss with
5989 | [] -> keymap
5990 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
5991 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
5993 if closed
5994 then { v with f = pkeymap ret keymap }
5995 else
5996 let f () = v in
5997 { v with f = skip "map" f }
5999 | Vopen _ ->
6000 error "unexpected subelement in keymap" s spos
6002 | Vclose "keymap" ->
6003 { v with f = ret keymap }
6005 | Vclose _ -> error "unexpected close in keymap" s spos
6007 and pbookmarks path pan anchor c bookmarks v t spos _ =
6008 match t with
6009 | Vdata | Vcdata -> v
6010 | Vend -> error "unexpected end of input in bookmarks" s spos
6011 | Vopen ("item", attrs, closed) ->
6012 let titleent, spage, srely = bookmark_of attrs in
6013 let page = fromstring int_of_string spos "page" spage 0
6014 and rely = fromstring float_of_string spos "rely" srely 0.0 in
6015 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
6016 if closed
6017 then { v with f = pbookmarks path pan anchor c bookmarks }
6018 else
6019 let f () = v in
6020 { v with f = skip "item" f }
6022 | Vopen _ ->
6023 error "unexpected subelement in bookmarks" s spos
6025 | Vclose "bookmarks" ->
6026 { v with f = doc path pan anchor c bookmarks }
6028 | Vclose _ -> error "unexpected close in bookmarks" s spos
6030 and skip tag f v t spos _ =
6031 match t with
6032 | Vdata | Vcdata -> v
6033 | Vend ->
6034 error ("unexpected end of input in skipped " ^ tag) s spos
6035 | Vopen (tag', _, closed) ->
6036 if closed
6037 then v
6038 else
6039 let f' () = { v with f = skip tag f } in
6040 { v with f = skip tag' f' }
6041 | Vclose ctag ->
6042 if tag = ctag
6043 then f ()
6044 else error ("unexpected close in skipped " ^ tag) s spos
6047 parse { f = toplevel; accu = () } s;
6048 h, dc;
6051 let do_load f ic =
6053 let len = in_channel_length ic in
6054 let s = String.create len in
6055 really_input ic s 0 len;
6056 f s;
6057 with
6058 | Parse_error (msg, s, pos) ->
6059 let subs = subs s pos in
6060 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6061 failwith ("parse error: " ^ s)
6063 | exn ->
6064 failwith ("config load error: " ^ Printexc.to_string exn)
6067 let defconfpath =
6068 let dir =
6070 let dir = Filename.concat home ".config" in
6071 if Sys.is_directory dir then dir else home
6072 with _ -> home
6074 Filename.concat dir "llpp.conf"
6077 let confpath = ref defconfpath;;
6079 let load1 f =
6080 if Sys.file_exists !confpath
6081 then
6082 match
6083 (try Some (open_in_bin !confpath)
6084 with exn ->
6085 prerr_endline
6086 ("Error opening configuation file `" ^ !confpath ^ "': " ^
6087 Printexc.to_string exn);
6088 None
6090 with
6091 | Some ic ->
6092 begin try
6093 f (do_load get ic)
6094 with exn ->
6095 prerr_endline
6096 ("Error loading configuation from `" ^ !confpath ^ "': " ^
6097 Printexc.to_string exn);
6098 end;
6099 close_in ic;
6101 | None -> ()
6102 else
6103 f (Hashtbl.create 0, defconf)
6106 let load () =
6107 let f (h, dc) =
6108 let pc, pb, px, pa =
6110 Hashtbl.find h (Filename.basename state.path)
6111 with Not_found -> dc, [], 0, (0, 0.0)
6113 setconf defconf dc;
6114 setconf conf pc;
6115 state.bookmarks <- pb;
6116 state.x <- px;
6117 state.scrollw <- conf.scrollbw;
6118 if conf.jumpback
6119 then state.anchor <- pa;
6120 cbput state.hists.nav pa;
6122 load1 f
6125 let add_attrs bb always dc c =
6126 let ob s a b =
6127 if always || a != b
6128 then Printf.bprintf bb "\n %s='%b'" s a
6129 and oi s a b =
6130 if always || a != b
6131 then Printf.bprintf bb "\n %s='%d'" s a
6132 and oI s a b =
6133 if always || a != b
6134 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6135 and oz s a b =
6136 if always || a <> b
6137 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
6138 and oF s a b =
6139 if always || a <> b
6140 then Printf.bprintf bb "\n %s='%f'" s a
6141 and oc s a b =
6142 if always || a <> b
6143 then
6144 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6145 and oC s a b =
6146 if always || a <> b
6147 then
6148 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6149 and oR s a b =
6150 if always || a <> b
6151 then
6152 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6153 and os s a b =
6154 if always || a <> b
6155 then
6156 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6157 and og s a b =
6158 if always || a <> b
6159 then
6160 match a with
6161 | None -> ()
6162 | Some (_N, _A, _B) ->
6163 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6164 and oW s a b =
6165 if always || a <> b
6166 then
6167 let v =
6168 match a with
6169 | None -> "false"
6170 | Some f ->
6171 if f = infinity
6172 then "true"
6173 else string_of_float f
6175 Printf.bprintf bb "\n %s='%s'" s v
6176 and oco s a b =
6177 if always || a <> b
6178 then
6179 match a with
6180 | Cmulti ((n, a, b), _) when n > 1 ->
6181 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6182 | Csplit (n, _) when n > 1 ->
6183 Printf.bprintf bb "\n %s='%d'" s ~-n
6184 | _ -> ()
6185 and obeco s a b =
6186 if always || a <> b
6187 then
6188 match a with
6189 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6190 | _ -> ()
6192 let w, h =
6193 if always
6194 then dc.winw, dc.winh
6195 else
6196 match state.fullscreen with
6197 | Some wh -> wh
6198 | None -> c.winw, c.winh
6200 let zoom, presentation, interpagespace, maxwait =
6201 if always
6202 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
6203 else
6204 match state.mode with
6205 | Birdseye (bc, _, _, _, _) ->
6206 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
6207 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
6209 oi "width" w dc.winw;
6210 oi "height" h dc.winh;
6211 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6212 oi "scroll-handle-height" c.scrollh dc.scrollh;
6213 ob "case-insensitive-search" c.icase dc.icase;
6214 ob "preload" c.preload dc.preload;
6215 oi "page-bias" c.pagebias dc.pagebias;
6216 oi "scroll-step" c.scrollstep dc.scrollstep;
6217 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6218 ob "max-height-fit" c.maxhfit dc.maxhfit;
6219 ob "crop-hack" c.crophack dc.crophack;
6220 oW "throttle" maxwait dc.maxwait;
6221 ob "highlight-links" c.hlinks dc.hlinks;
6222 ob "under-cursor-info" c.underinfo dc.underinfo;
6223 oi "vertical-margin" interpagespace dc.interpagespace;
6224 oz "zoom" zoom dc.zoom;
6225 ob "presentation" presentation dc.presentation;
6226 oi "rotation-angle" c.angle dc.angle;
6227 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6228 ob "proportional-display" c.proportional dc.proportional;
6229 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6230 oi "tex-count" c.texcount dc.texcount;
6231 oi "slice-height" c.sliceheight dc.sliceheight;
6232 oi "thumbnail-width" c.thumbw dc.thumbw;
6233 ob "persistent-location" c.jumpback dc.jumpback;
6234 oc "background-color" c.bgcolor dc.bgcolor;
6235 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6236 oi "tile-width" c.tilew dc.tilew;
6237 oi "tile-height" c.tileh dc.tileh;
6238 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6239 ob "checkers" c.checkers dc.checkers;
6240 oi "aalevel" c.aalevel dc.aalevel;
6241 ob "trim-margins" c.trimmargins dc.trimmargins;
6242 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6243 os "uri-launcher" c.urilauncher dc.urilauncher;
6244 os "path-launcher" c.pathlauncher dc.pathlauncher;
6245 oC "color-space" c.colorspace dc.colorspace;
6246 ob "invert-colors" c.invert dc.invert;
6247 oF "brightness" c.colorscale dc.colorscale;
6248 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6249 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6250 oco "columns" c.columns dc.columns;
6251 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6252 os "selection-command" c.selcmd dc.selcmd;
6253 ob "update-cursor" c.updatecurs dc.updatecurs;
6256 let keymapsbuf always dc c =
6257 let bb = Buffer.create 16 in
6258 let rec loop = function
6259 | [] -> ()
6260 | (modename, h) :: rest ->
6261 let dh = findkeyhash dc modename in
6262 if always || h <> dh
6263 then (
6264 if Hashtbl.length h > 0
6265 then (
6266 if Buffer.length bb > 0
6267 then Buffer.add_char bb '\n';
6268 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6269 Hashtbl.iter (fun i o ->
6270 let isdifferent = always ||
6272 let dO = Hashtbl.find dh i in
6273 dO <> o
6274 with Not_found -> true
6276 if isdifferent
6277 then
6278 let addkm (k, m) =
6279 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6280 if Wsi.withalt m then Buffer.add_string bb "alt-";
6281 if Wsi.withshift m then Buffer.add_string bb "shift-";
6282 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6283 Buffer.add_string bb (Wsi.keyname k);
6285 let addkms l =
6286 let rec loop = function
6287 | [] -> ()
6288 | km :: [] -> addkm km
6289 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6291 loop l
6293 Buffer.add_string bb "<map in='";
6294 addkm i;
6295 match o with
6296 | KMinsrt km ->
6297 Buffer.add_string bb "' out='";
6298 addkm km;
6299 Buffer.add_string bb "'/>\n"
6301 | KMinsrl kms ->
6302 Buffer.add_string bb "' out='";
6303 addkms kms;
6304 Buffer.add_string bb "'/>\n"
6306 | KMmulti (ins, kms) ->
6307 Buffer.add_char bb ' ';
6308 addkms ins;
6309 Buffer.add_string bb "' out='";
6310 addkms kms;
6311 Buffer.add_string bb "'/>\n"
6312 ) h;
6313 Buffer.add_string bb "</keymap>";
6316 loop rest
6318 loop c.keyhashes;
6322 let save () =
6323 let uifontsize = fstate.fontsize in
6324 let bb = Buffer.create 32768 in
6325 let f (h, dc) =
6326 let dc = if conf.bedefault then conf else dc in
6327 Buffer.add_string bb "<llppconfig>\n";
6329 if String.length !fontpath > 0
6330 then
6331 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6332 uifontsize
6333 !fontpath
6334 else (
6335 if uifontsize <> 14
6336 then
6337 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6340 Buffer.add_string bb "<defaults ";
6341 add_attrs bb true dc dc;
6342 let kb = keymapsbuf true dc dc in
6343 if Buffer.length kb > 0
6344 then (
6345 Buffer.add_string bb ">\n";
6346 Buffer.add_buffer bb kb;
6347 Buffer.add_string bb "\n</defaults>\n";
6349 else Buffer.add_string bb "/>\n";
6351 let adddoc path pan anchor c bookmarks =
6352 if bookmarks == [] && c = dc && anchor = emptyanchor
6353 then ()
6354 else (
6355 Printf.bprintf bb "<doc path='%s'"
6356 (enent path 0 (String.length path));
6358 if anchor <> emptyanchor
6359 then (
6360 let n, y = anchor in
6361 Printf.bprintf bb " page='%d'" n;
6362 if y > 1e-6
6363 then
6364 Printf.bprintf bb " rely='%f'" y
6368 if pan != 0
6369 then Printf.bprintf bb " pan='%d'" pan;
6371 add_attrs bb false dc c;
6372 let kb = keymapsbuf false dc c in
6374 begin match bookmarks with
6375 | [] ->
6376 if Buffer.length kb > 0
6377 then (
6378 Buffer.add_string bb ">\n";
6379 Buffer.add_buffer bb kb;
6380 Buffer.add_string bb "</doc>\n";
6382 else Buffer.add_string bb "/>\n"
6383 | _ ->
6384 Buffer.add_string bb ">\n<bookmarks>\n";
6385 List.iter (fun (title, _level, (page, rely)) ->
6386 Printf.bprintf bb
6387 "<item title='%s' page='%d'"
6388 (enent title 0 (String.length title))
6389 page
6391 if rely > 1e-6
6392 then
6393 Printf.bprintf bb " rely='%f'" rely
6395 Buffer.add_string bb "/>\n";
6396 ) bookmarks;
6397 Buffer.add_string bb "</bookmarks>";
6398 if Buffer.length kb > 0
6399 then (
6400 Buffer.add_string bb "\n";
6401 Buffer.add_buffer bb kb;
6403 Buffer.add_string bb "\n</doc>\n";
6404 end;
6408 let pan, conf =
6409 match state.mode with
6410 | Birdseye (c, pan, _, _, _) ->
6411 let beyecolumns =
6412 match conf.columns with
6413 | Cmulti ((c, _, _), _) -> Some c
6414 | Csingle -> None
6415 | Csplit _ -> None
6416 and columns =
6417 match c.columns with
6418 | Cmulti (c, _) -> Cmulti (c, [||])
6419 | Csingle -> Csingle
6420 | Csplit _ -> failwith "quit from bird's eye while split"
6422 pan, { c with beyecolumns = beyecolumns; columns = columns }
6423 | _ -> state.x, conf
6425 let basename = Filename.basename state.path in
6426 adddoc basename pan (getanchor ())
6427 { conf with
6428 autoscrollstep =
6429 match state.autoscroll with
6430 | Some step -> step
6431 | None -> conf.autoscrollstep }
6432 (if conf.savebmarks then state.bookmarks else []);
6434 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
6435 if basename <> path
6436 then adddoc path x y c bookmarks
6437 ) h;
6438 Buffer.add_string bb "</llppconfig>";
6440 load1 f;
6441 if Buffer.length bb > 0
6442 then
6444 let tmp = !confpath ^ ".tmp" in
6445 let oc = open_out_bin tmp in
6446 Buffer.output_buffer oc bb;
6447 close_out oc;
6448 Unix.rename tmp !confpath;
6449 with exn ->
6450 prerr_endline
6451 ("error while saving configuration: " ^ Printexc.to_string exn)
6453 end;;
6455 let () =
6456 Arg.parse
6457 (Arg.align
6458 [("-p", Arg.String (fun s -> state.password <- s) ,
6459 "<password> Set password");
6461 ("-f", Arg.String (fun s -> Config.fontpath := s),
6462 "<path> Set path to the user interface font");
6464 ("-c", Arg.String (fun s -> Config.confpath := s),
6465 "<path> Set path to the configuration file");
6467 ("-v", Arg.Unit (fun () ->
6468 Printf.printf
6469 "%s\nconfiguration path: %s\n"
6470 (version ())
6471 Config.defconfpath
6473 exit 0), " Print version and exit");
6476 (fun s -> state.path <- s)
6477 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6479 if String.length state.path = 0
6480 then (prerr_endline "file name missing"; exit 1);
6482 Config.load ();
6484 let globalkeyhash = findkeyhash conf "global" in
6485 let wsfd, winw, winh = Wsi.init (object
6486 method expose =
6487 if nogeomcmds state.geomcmds
6488 then display ()
6489 method display = display ()
6490 method reshape w h = reshape w h
6491 method mouse b d x y m = mouse b d x y m
6492 method motion x y = state.mpos <- (x, y); motion x y
6493 method pmotion x y = state.mpos <- (x, y); pmotion x y
6494 method key k m =
6495 let mascm = m land (
6496 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
6497 ) in
6498 match state.keystate with
6499 | KSnone ->
6500 let km = k, mascm in
6501 begin
6502 match
6503 try Hashtbl.find globalkeyhash km
6504 with Not_found ->
6505 let modehash = state.uioh#modehash in
6506 try Hashtbl.find modehash km
6507 with Not_found -> KMinsrt (k, m)
6508 with
6509 | KMinsrt (k, m) -> keyboard k m
6510 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6511 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6513 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
6514 List.iter (fun (k, m) -> keyboard k m) insrt;
6515 state.keystate <- KSnone
6516 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
6517 state.keystate <- KSinto (keys, insrt)
6518 | _ ->
6519 state.keystate <- KSnone
6521 method enter x y = state.mpos <- (x, y); pmotion x y
6522 method leave = state.mpos <- (-1, -1)
6523 method quit = raise Quit
6524 end) conf.winw conf.winh (platform = Posx) in
6526 state.wsfd <- wsfd;
6528 if not (
6529 List.exists GlMisc.check_extension
6530 [ "GL_ARB_texture_rectangle"
6531 ; "GL_EXT_texture_recangle"
6532 ; "GL_NV_texture_rectangle" ]
6534 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
6536 let cr, sw = Unix.pipe ()
6537 and sr, cw = Unix.pipe () in
6539 cloexec cr;
6540 cloexec sw;
6541 cloexec sr;
6542 cloexec cw;
6544 setcheckers conf.checkers;
6545 redirectstderr ();
6547 init (cr, cw) (
6548 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
6549 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6550 !Config.fontpath
6552 state.sr <- sr;
6553 state.sw <- sw;
6554 state.text <- "Opening " ^ state.path;
6555 reshape winw winh;
6556 opendoc state.path state.password;
6557 state.uioh <- uioh;
6559 let rec loop deadline =
6560 let r =
6561 match state.errfd with
6562 | None -> [state.sr; state.wsfd]
6563 | Some fd -> [state.sr; state.wsfd; fd]
6565 if state.redisplay
6566 then (
6567 state.redisplay <- false;
6568 display ();
6570 let timeout =
6571 let now = now () in
6572 if deadline > now
6573 then (
6574 if deadline = infinity
6575 then ~-.1.0
6576 else max 0.0 (deadline -. now)
6578 else 0.0
6580 let r, _, _ =
6581 try Unix.select r [] [] timeout
6582 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
6584 begin match r with
6585 | [] ->
6586 state.ghyll None;
6587 let newdeadline =
6588 if state.ghyll == noghyll
6589 then
6590 match state.autoscroll with
6591 | Some step when step != 0 ->
6592 let y = state.y + step in
6593 let y =
6594 if y < 0
6595 then state.maxy
6596 else if y >= state.maxy then 0 else y
6598 gotoy y;
6599 if state.mode = View
6600 then state.text <- "";
6601 deadline +. 0.01
6602 | _ -> infinity
6603 else deadline +. 0.01
6605 loop newdeadline
6607 | l ->
6608 let rec checkfds = function
6609 | [] -> ()
6610 | fd :: rest when fd = state.sr ->
6611 let cmd = readcmd state.sr in
6612 act cmd;
6613 checkfds rest
6615 | fd :: rest when fd = state.wsfd ->
6616 Wsi.readresp fd;
6617 checkfds rest
6619 | fd :: rest ->
6620 let s = String.create 80 in
6621 let n = Unix.read fd s 0 80 in
6622 if conf.redirectstderr
6623 then (
6624 Buffer.add_substring state.errmsgs s 0 n;
6625 state.newerrmsgs <- true;
6626 state.redisplay <- true;
6628 else (
6629 prerr_string (String.sub s 0 n);
6630 flush stderr;
6632 checkfds rest
6634 checkfds l;
6635 let newdeadline =
6636 let deadline1 =
6637 if deadline = infinity
6638 then now () +. 0.01
6639 else deadline
6641 match state.autoscroll with
6642 | Some step when step != 0 -> deadline1
6643 | _ -> if state.ghyll == noghyll then infinity else deadline1
6645 loop newdeadline
6646 end;
6649 loop infinity;
6650 with Quit ->
6651 Config.save ();
6652 exit 0;