Fix some split issues
[llpp.git] / main.ml
blob8fe23b13a6f9fe75537dcf6861dc540d29087f2b
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 = 256 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 =
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
1155 else pdx, 0
1157 let pagevw =
1158 let vw = conf.winw - state.scrollw - pagedispx in
1159 let pw = w - pagex in
1160 min vw pw
1162 let pagevh = min (h - pagey) (sh - pagedispy) in
1163 if pagevw > 0 && pagevh > 0
1164 then
1165 let e =
1166 { pageno = n
1167 ; pagedimno = pdimno
1168 ; pagew = w
1169 ; pageh = h
1170 ; pagex = pagex
1171 ; pagey = pagey
1172 ; pagevw = pagevw
1173 ; pagevh = pagevh
1174 ; pagedispx = pagedispx
1175 ; pagedispy = pagedispy
1178 e :: accu
1179 else
1180 accu
1181 else
1182 accu
1184 fold accu (n+1)
1186 List.rev (fold [] 0)
1189 let layoutS (columns, b) y sh =
1190 let sh = sh - state.hscrollh in
1191 let rec fold accu n =
1192 if n = Array.length b
1193 then accu
1194 else
1195 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1196 if (vy - y) > sh
1197 then accu
1198 else
1199 let accu =
1200 if vy + pageh > y
1201 then
1202 let x = xoff + state.x in
1203 let pagey = max 0 (y - vy) in
1204 let pagedispy = if pagey > 0 then 0 else vy - y in
1205 let pagedispx, pagex =
1206 if x < 0
1207 then 0, max 0 (px + x)
1208 else x, px
1210 let pagevw =
1211 let vw = conf.winw - pagedispx - state.scrollw in
1212 let pw = pagew - pagex in
1213 min vw pw
1215 let pagevw = min pagevw (pagew/columns) in
1216 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1217 if pagevw > 0 && pagevh > 0
1218 then
1219 let e =
1220 { pageno = n/columns
1221 ; pagedimno = pdimno
1222 ; pagew = pagew
1223 ; pageh = pageh
1224 ; pagex = pagex
1225 ; pagey = pagey
1226 ; pagevw = pagevw
1227 ; pagevh = pagevh
1228 ; pagedispx = pagedispx
1229 ; pagedispy = pagedispy
1232 e :: accu
1233 else
1234 accu
1235 else
1236 accu
1238 fold accu (n+1)
1240 List.rev (fold [] 0)
1243 let layout y sh =
1244 if nogeomcmds state.geomcmds
1245 then
1246 match conf.columns with
1247 | Csingle -> layout1 y sh
1248 | Cmulti c -> layoutN c y sh
1249 | Csplit s -> layoutS s y sh
1250 else []
1253 let clamp incr =
1254 let y = state.y + incr in
1255 let y = max 0 y in
1256 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1260 let itertiles l f =
1261 let tilex = l.pagex mod conf.tilew in
1262 let tiley = l.pagey mod conf.tileh in
1264 let col = l.pagex / conf.tilew in
1265 let row = l.pagey / conf.tileh in
1267 let rec rowloop row y0 dispy h =
1268 if h = 0
1269 then ()
1270 else (
1271 let dh = conf.tileh - y0 in
1272 let dh = min h dh in
1273 let rec colloop col x0 dispx w =
1274 if w = 0
1275 then ()
1276 else (
1277 let dw = conf.tilew - x0 in
1278 let dw = min w dw in
1280 f col row dispx dispy x0 y0 dw dh;
1281 colloop (col+1) 0 (dispx+dw) (w-dw)
1284 colloop col tilex l.pagedispx l.pagevw;
1285 rowloop (row+1) 0 (dispy+dh) (h-dh)
1288 if l.pagevw > 0 && l.pagevh > 0
1289 then rowloop row tiley l.pagedispy l.pagevh;
1292 let gettileopaque l col row =
1293 let key =
1294 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1296 try Some (Hashtbl.find state.tilemap key)
1297 with Not_found -> None
1300 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1301 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1302 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1305 let drawtiles l color =
1306 GlDraw.color color;
1307 let f col row x y tilex tiley w h =
1308 match gettileopaque l col row with
1309 | Some (opaque, _, t) ->
1310 let params = x, y, w, h, tilex, tiley in
1311 if conf.invert
1312 then (
1313 Gl.enable `blend;
1314 GlFunc.blend_func `zero `one_minus_src_color;
1316 drawtile params opaque;
1317 if conf.invert
1318 then Gl.disable `blend;
1319 if conf.debug
1320 then (
1321 let s = Printf.sprintf
1322 "%d[%d,%d] %f sec"
1323 l.pageno col row t
1325 let w = measurestr fstate.fontsize s in
1326 GlMisc.push_attrib [`current];
1327 GlDraw.color (0.0, 0.0, 0.0);
1328 GlDraw.rect
1329 (float (x-2), float (y-2))
1330 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1331 GlDraw.color (1.0, 1.0, 1.0);
1332 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1333 GlMisc.pop_attrib ();
1336 | _ ->
1337 let w =
1338 let lw = conf.winw - state.scrollw - x in
1339 min lw w
1340 and h =
1341 let lh = conf.winh - y in
1342 min lh h
1344 Gl.enable `texture_2d;
1345 begin match state.texid with
1346 | Some id ->
1347 GlTex.bind_texture `texture_2d id;
1348 let x0 = float x
1349 and y0 = float y
1350 and x1 = float (x+w)
1351 and y1 = float (y+h) in
1353 let tw = float w /. 64.0
1354 and th = float h /. 64.0 in
1355 let tx0 = float tilex /. 64.0
1356 and ty0 = float tiley /. 64.0 in
1357 let tx1 = tx0 +. tw
1358 and ty1 = ty0 +. th in
1359 GlDraw.begins `quads;
1360 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1361 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1362 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1363 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1364 GlDraw.ends ();
1366 Gl.disable `texture_2d;
1367 | None ->
1368 GlDraw.color (1.0, 1.0, 1.0);
1369 GlDraw.rect
1370 (float x, float y)
1371 (float (x+w), float (y+h));
1372 end;
1373 if w > 128 && h > fstate.fontsize + 10
1374 then (
1375 GlDraw.color (0.0, 0.0, 0.0);
1376 let c, r =
1377 if conf.verbose
1378 then (col*conf.tilew, row*conf.tileh)
1379 else col, row
1381 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1383 GlDraw.color color;
1385 itertiles l f
1388 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1390 let tilevisible1 l x y =
1391 let ax0 = l.pagex
1392 and ax1 = l.pagex + l.pagevw
1393 and ay0 = l.pagey
1394 and ay1 = l.pagey + l.pagevh in
1396 let bx0 = x
1397 and by0 = y in
1398 let bx1 = min (bx0 + conf.tilew) l.pagew
1399 and by1 = min (by0 + conf.tileh) l.pageh in
1401 let rx0 = max ax0 bx0
1402 and ry0 = max ay0 by0
1403 and rx1 = min ax1 bx1
1404 and ry1 = min ay1 by1 in
1406 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1407 nonemptyintersection
1410 let tilevisible layout n x y =
1411 let rec findpageinlayout = function
1412 | l :: _ when l.pageno = n -> tilevisible1 l x y
1413 | _ :: rest -> findpageinlayout rest
1414 | [] -> false
1416 findpageinlayout layout
1419 let tileready l x y =
1420 tilevisible1 l x y &&
1421 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1424 let tilepage n p layout =
1425 let rec loop = function
1426 | l :: rest ->
1427 if l.pageno = n
1428 then
1429 let f col row _ _ _ _ _ _ =
1430 if state.currently = Idle
1431 then
1432 match gettileopaque l col row with
1433 | Some _ -> ()
1434 | None ->
1435 let x = col*conf.tilew
1436 and y = row*conf.tileh in
1437 let w =
1438 let w = l.pagew - x in
1439 min w conf.tilew
1441 let h =
1442 let h = l.pageh - y in
1443 min h conf.tileh
1445 wcmd "tile %s %d %d %d %d" p x y w h;
1446 state.currently <-
1447 Tiling (
1448 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1449 conf.tilew, conf.tileh
1452 itertiles l f;
1453 else
1454 loop rest
1456 | [] -> ()
1458 if nogeomcmds state.geomcmds
1459 then loop layout;
1462 let preloadlayout visiblepages =
1463 let presentation = conf.presentation in
1464 let interpagespace = conf.interpagespace in
1465 let maxy = state.maxy in
1466 conf.presentation <- false;
1467 conf.interpagespace <- 0;
1468 state.maxy <- calcheight ();
1469 let y =
1470 match visiblepages with
1471 | [] -> 0
1472 | l :: _ -> getpagey l.pageno + l.pagey
1474 let y = if y < conf.winh then 0 else y - conf.winh in
1475 let h = state.y - y + conf.winh*3 in
1476 let pages = layout y h in
1477 conf.presentation <- presentation;
1478 conf.interpagespace <- interpagespace;
1479 state.maxy <- maxy;
1480 pages;
1483 let load pages =
1484 let rec loop pages =
1485 if state.currently != Idle
1486 then ()
1487 else
1488 match pages with
1489 | l :: rest ->
1490 begin match getopaque l.pageno with
1491 | None ->
1492 wcmd "page %d %d" l.pageno l.pagedimno;
1493 state.currently <- Loading (l, state.gen);
1494 | Some opaque ->
1495 tilepage l.pageno opaque pages;
1496 loop rest
1497 end;
1498 | _ -> ()
1500 if nogeomcmds state.geomcmds
1501 then loop pages
1504 let preload pages =
1505 load pages;
1506 if conf.preload && state.currently = Idle
1507 then load (preloadlayout pages);
1510 let layoutready layout =
1511 let rec fold all ls =
1512 all && match ls with
1513 | l :: rest ->
1514 let seen = ref false in
1515 let allvisible = ref true in
1516 let foo col row _ _ _ _ _ _ =
1517 seen := true;
1518 allvisible := !allvisible &&
1519 begin match gettileopaque l col row with
1520 | Some _ -> true
1521 | None -> false
1524 itertiles l foo;
1525 fold (!seen && !allvisible) rest
1526 | [] -> true
1528 let alltilesvisible = fold true layout in
1529 alltilesvisible;
1532 let gotoy y =
1533 let y = bound y 0 state.maxy in
1534 let y, layout, proceed =
1535 match conf.maxwait with
1536 | Some time when state.ghyll == noghyll ->
1537 begin match state.throttle with
1538 | None ->
1539 let layout = layout y conf.winh in
1540 let ready = layoutready layout in
1541 if not ready
1542 then (
1543 load layout;
1544 state.throttle <- Some (layout, y, now ());
1546 else G.postRedisplay "gotoy showall (None)";
1547 y, layout, ready
1548 | Some (_, _, started) ->
1549 let dt = now () -. started in
1550 if dt > time
1551 then (
1552 state.throttle <- None;
1553 let layout = layout y conf.winh in
1554 load layout;
1555 G.postRedisplay "maxwait";
1556 y, layout, true
1558 else -1, [], false
1561 | _ ->
1562 let layout = layout y conf.winh in
1563 if true || layoutready layout
1564 then G.postRedisplay "gotoy ready";
1565 y, layout, true
1567 if proceed
1568 then (
1569 state.y <- y;
1570 state.layout <- layout;
1571 begin match state.mode with
1572 | LinkNav (Ltexact (pageno, linkno)) ->
1573 let rec loop = function
1574 | [] ->
1575 state.mode <- LinkNav (Ltgendir 0)
1576 | l :: _ when l.pageno = pageno ->
1577 begin match getopaque pageno with
1578 | None ->
1579 state.mode <- LinkNav (Ltgendir 0)
1580 | Some opaque ->
1581 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1582 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1583 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1584 then state.mode <- LinkNav (Ltgendir 0)
1586 | _ :: rest -> loop rest
1588 loop layout
1589 | _ -> ()
1590 end;
1591 begin match state.mode with
1592 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1593 if not (pagevisible layout pageno)
1594 then (
1595 match state.layout with
1596 | [] -> ()
1597 | l :: _ ->
1598 state.mode <- Birdseye (
1599 conf, leftx, l.pageno, hooverpageno, anchor
1602 | LinkNav (Ltgendir dir as lt) ->
1603 let linknav =
1604 let rec loop = function
1605 | [] -> lt
1606 | l :: rest ->
1607 match getopaque l.pageno with
1608 | None -> loop rest
1609 | Some opaque ->
1610 let link =
1611 let ld =
1612 if dir = 0
1613 then LDfirstvisible (l.pagex, l.pagey, dir)
1614 else (
1615 if dir > 0 then LDfirst else LDlast
1618 findlink opaque ld
1620 match link with
1621 | Lnotfound -> loop rest
1622 | Lfound n ->
1623 showlinktype (getlink opaque n);
1624 Ltexact (l.pageno, n)
1626 loop state.layout
1628 state.mode <- LinkNav linknav
1629 | _ -> ()
1630 end;
1631 preload layout;
1633 state.ghyll <- noghyll;
1634 if conf.updatecurs
1635 then (
1636 let mx, my = state.mpos in
1637 updateunder mx my;
1641 let conttiling pageno opaque =
1642 tilepage pageno opaque
1643 (if conf.preload then preloadlayout state.layout else state.layout)
1646 let gotoy_and_clear_text y =
1647 if not conf.verbose then state.text <- "";
1648 gotoy y;
1651 let getanchor () =
1652 match state.layout with
1653 | [] -> emptyanchor
1654 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1657 let getanchory (n, top) =
1658 let y, h = getpageyh n in
1659 y + (truncate (top *. float h));
1662 let gotoanchor anchor =
1663 gotoy (getanchory anchor);
1666 let addnav () =
1667 cbput state.hists.nav (getanchor ());
1670 let getnav dir =
1671 let anchor = cbgetc state.hists.nav dir in
1672 getanchory anchor;
1675 let gotoghyll y =
1676 let rec scroll f n a b =
1677 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1678 let snake f a b =
1679 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1680 if f < a
1681 then s (float f /. float a)
1682 else (
1683 if f > b
1684 then 1.0 -. s ((float (f-b) /. float (n-b)))
1685 else 1.0
1688 snake f a b
1689 and summa f n a b =
1690 (* courtesy:
1691 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1692 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1693 let iv1 = iv f in
1694 let ins = float a *. iv1
1695 and outs = float (n-b) *. iv1 in
1696 let ones = b - a in
1697 ins +. outs +. float ones
1699 let rec set (_N, _A, _B) y sy =
1700 let sum = summa 1.0 _N _A _B in
1701 let dy = float (y - sy) in
1702 state.ghyll <- (
1703 let rec gf n y1 o =
1704 if n >= _N
1705 then state.ghyll <- noghyll
1706 else
1707 let go n =
1708 let s = scroll n _N _A _B in
1709 let y1 = y1 +. ((s *. dy) /. sum) in
1710 gotoy_and_clear_text (truncate y1);
1711 state.ghyll <- gf (n+1) y1;
1713 match o with
1714 | None -> go n
1715 | Some y' -> set (_N/2, 0, 0) y' state.y
1717 gf 0 (float state.y)
1720 match conf.ghyllscroll with
1721 | None ->
1722 gotoy_and_clear_text y
1723 | Some nab ->
1724 if state.ghyll == noghyll
1725 then set nab y state.y
1726 else state.ghyll (Some y)
1729 let gotopage n top =
1730 let y, h = getpageyh n in
1731 let y = y + (truncate (top *. float h)) in
1732 gotoghyll y
1735 let gotopage1 n top =
1736 let y = getpagey n in
1737 let y = y + top in
1738 gotoghyll y
1741 let invalidate s f =
1742 state.layout <- [];
1743 state.pdims <- [];
1744 state.rects <- [];
1745 state.rects1 <- [];
1746 match state.geomcmds with
1747 | ps, [] when String.length ps = 0 ->
1748 f ();
1749 state.geomcmds <- s, [];
1751 | ps, [] ->
1752 state.geomcmds <- ps, [s, f];
1754 | ps, (s', _) :: rest when s' = s ->
1755 state.geomcmds <- ps, ((s, f) :: rest);
1757 | ps, cmds ->
1758 state.geomcmds <- ps, ((s, f) :: cmds);
1761 let opendoc path password =
1762 state.path <- path;
1763 state.password <- password;
1764 state.gen <- state.gen + 1;
1765 state.docinfo <- [];
1767 setaalevel conf.aalevel;
1768 Wsi.settitle ("llpp " ^ Filename.basename path);
1769 wcmd "open %s\000%s\000" path password;
1770 invalidate "reqlayout"
1771 (fun () ->
1772 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1775 let scalecolor c =
1776 let c = c *. conf.colorscale in
1777 (c, c, c);
1780 let scalecolor2 (r, g, b) =
1781 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1784 let represent () =
1785 let docolumns = function
1786 | Csingle -> ()
1788 | Cmulti ((columns, coverA, coverB), _) ->
1789 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1790 let rec loop pageno pdimno pdim x y rowh pdims =
1791 if pageno = state.pagecount
1792 then ()
1793 else
1794 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1795 match pdims with
1796 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1797 pdimno+1, pdim, rest
1798 | _ ->
1799 pdimno, pdim, pdims
1801 let x, y, rowh' =
1802 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1803 then (
1804 (conf.winw - state.scrollw - w) / 2,
1805 y + rowh + conf.interpagespace, h
1807 else (
1808 if (pageno - coverA) mod columns = 0
1809 then 0, y + rowh + conf.interpagespace, h
1810 else x, y, max rowh h
1813 let rec fixrow m = if m = pageno then () else
1814 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1815 if h < rowh
1816 then (
1817 let y = y + (rowh - h) / 2 in
1818 a.(m) <- (pdimno, x, y, pdim);
1820 fixrow (m+1)
1822 if pageno > 1 && (pageno - coverA) mod columns = 0
1823 then fixrow (pageno - columns);
1824 a.(pageno) <- (pdimno, x, y, pdim);
1825 let x = x + w + xoff*2 + conf.interpagespace in
1826 loop (pageno+1) pdimno pdim x y rowh' pdims
1828 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1829 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1831 | Csplit (c, _) ->
1832 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1833 let rec loop pageno pdimno pdim y pdims =
1834 if pageno = state.pagecount
1835 then ()
1836 else
1837 let pdimno, ((_, w, h, _) as pdim), pdims =
1838 match pdims with
1839 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1840 pdimno+1, pdim, rest
1841 | _ ->
1842 pdimno, pdim, pdims
1844 let cw = w / c in
1845 let rec loop1 n x y =
1846 if n = c then y else (
1847 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1848 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1851 let y = loop1 0 0 y in
1852 loop (pageno+1) pdimno pdim y pdims
1854 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1855 conf.columns <- Csplit (c, a);
1857 docolumns conf.columns;
1858 state.maxy <- calcheight ();
1859 state.hscrollh <-
1860 if state.w <= conf.winw - state.scrollw
1861 then 0
1862 else state.scrollw
1864 match state.mode with
1865 | Birdseye (_, _, pageno, _, _) ->
1866 let y, h = getpageyh pageno in
1867 let top = (conf.winh - h) / 2 in
1868 gotoy (max 0 (y - top))
1869 | _ -> gotoanchor state.anchor
1872 let reshape w h =
1873 GlDraw.viewport 0 0 w h;
1874 if state.geomcmds != firstgeomcmds && nogeomcmds state.geomcmds
1875 then state.anchor <- getanchor ();
1877 conf.winw <- w;
1878 let w = truncate (float w *. conf.zoom) - state.scrollw in
1879 let w = max w 2 in
1880 conf.winh <- h;
1881 setfontsize fstate.fontsize;
1882 GlMat.mode `modelview;
1883 GlMat.load_identity ();
1885 GlMat.mode `projection;
1886 GlMat.load_identity ();
1887 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1888 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1889 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1891 let relx =
1892 if conf.zoom <= 1.0
1893 then 0.0
1894 else float state.x /. float state.w
1896 invalidate "geometry"
1897 (fun () ->
1898 state.w <- w;
1899 state.x <- truncate (relx *. float w);
1900 let w =
1901 match conf.columns with
1902 | Csingle -> w
1903 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1904 | Csplit (c, _) -> w * c
1906 wcmd "geometry %d %d" w h);
1909 let enttext () =
1910 let len = String.length state.text in
1911 let drawstring s =
1912 let hscrollh =
1913 match state.mode with
1914 | Textentry _
1915 | View -> state.hscrollh
1916 | _ -> 0
1918 let rect x w =
1919 GlDraw.rect
1920 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1921 (x+.w, float (conf.winh - hscrollh))
1924 let w = float (conf.winw - state.scrollw - 1) in
1925 if state.progress >= 0.0 && state.progress < 1.0
1926 then (
1927 GlDraw.color (0.3, 0.3, 0.3);
1928 let w1 = w *. state.progress in
1929 rect 0.0 w1;
1930 GlDraw.color (0.0, 0.0, 0.0);
1931 rect w1 (w-.w1)
1933 else (
1934 GlDraw.color (0.0, 0.0, 0.0);
1935 rect 0.0 w;
1938 GlDraw.color (1.0, 1.0, 1.0);
1939 drawstring fstate.fontsize
1940 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1942 let s =
1943 match state.mode with
1944 | Textentry ((prefix, text, _, _, _), _) ->
1945 let s =
1946 if len > 0
1947 then
1948 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1949 else
1950 Printf.sprintf "%s%s_" prefix text
1954 | _ -> state.text
1956 let s =
1957 if state.newerrmsgs
1958 then (
1959 if not (istextentry state.mode)
1960 then
1961 let s1 = "(press 'e' to review error messasges)" in
1962 if String.length s > 0 then s ^ " " ^ s1 else s1
1963 else s
1965 else s
1967 if String.length s > 0
1968 then drawstring s
1971 let gctiles () =
1972 let len = Queue.length state.tilelru in
1973 let rec loop qpos =
1974 if state.memused <= conf.memlimit
1975 then ()
1976 else (
1977 if qpos < len
1978 then
1979 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1980 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1981 let (_, pw, ph, _) = getpagedim n in
1983 gen = state.gen
1984 && colorspace = conf.colorspace
1985 && angle = conf.angle
1986 && pagew = pw
1987 && pageh = ph
1988 && (
1989 let layout =
1990 match state.throttle with
1991 | None ->
1992 if conf.preload
1993 then preloadlayout state.layout
1994 else state.layout
1995 | Some (layout, _, _) ->
1996 layout
1998 let x = col*conf.tilew
1999 and y = row*conf.tileh in
2000 tilevisible layout n x y
2002 then Queue.push lruitem state.tilelru
2003 else (
2004 wcmd "freetile %s" p;
2005 state.memused <- state.memused - s;
2006 state.uioh#infochanged Memused;
2007 Hashtbl.remove state.tilemap k;
2009 loop (qpos+1)
2012 loop 0
2015 let flushtiles () =
2016 Queue.iter (fun (k, p, s) ->
2017 wcmd "freetile %s" p;
2018 state.memused <- state.memused - s;
2019 state.uioh#infochanged Memused;
2020 Hashtbl.remove state.tilemap k;
2021 ) state.tilelru;
2022 Queue.clear state.tilelru;
2023 load state.layout;
2026 let logcurrently = function
2027 | Idle -> dolog "Idle"
2028 | Loading (l, gen) ->
2029 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2030 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2031 dolog
2032 "Tiling %d[%d,%d] page=%s cs=%s angle"
2033 l.pageno col row pageopaque
2034 (colorspace_to_string colorspace)
2036 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2037 angle gen conf.angle state.gen
2038 tilew tileh
2039 conf.tilew conf.tileh
2041 | Outlining _ ->
2042 dolog "outlining"
2045 let act cmds =
2046 (* dolog "%S" cmds; *)
2047 let op, args =
2048 let spacepos =
2049 try String.index cmds ' '
2050 with Not_found -> -1
2052 if spacepos = -1
2053 then cmds, ""
2054 else
2055 let l = String.length cmds in
2056 let op = String.sub cmds 0 spacepos in
2057 op, begin
2058 if l - spacepos < 2 then ""
2059 else String.sub cmds (spacepos+1) (l-spacepos-1)
2062 match op with
2063 | "clear" ->
2064 state.uioh#infochanged Pdim;
2065 state.pdims <- [];
2067 | "clearrects" ->
2068 state.rects <- state.rects1;
2069 G.postRedisplay "clearrects";
2071 | "continue" ->
2072 let n =
2073 try Scanf.sscanf args "%u" (fun n -> n)
2074 with exn ->
2075 dolog "error processing 'continue' %S: %s"
2076 cmds (Printexc.to_string exn);
2077 exit 1;
2079 state.pagecount <- n;
2080 begin match state.currently with
2081 | Outlining l ->
2082 state.currently <- Idle;
2083 state.outlines <- Array.of_list (List.rev l)
2084 | _ -> ()
2085 end;
2087 let cur, cmds = state.geomcmds in
2088 if String.length cur = 0
2089 then failwith "umpossible";
2091 begin match List.rev cmds with
2092 | [] ->
2093 state.geomcmds <- "", [];
2094 represent ();
2095 | (s, f) :: rest ->
2096 f ();
2097 state.geomcmds <- s, List.rev rest;
2098 end;
2099 if conf.maxwait = None
2100 then G.postRedisplay "continue";
2102 | "title" ->
2103 Wsi.settitle args
2105 | "msg" ->
2106 showtext ' ' args
2108 | "vmsg" ->
2109 if conf.verbose
2110 then showtext ' ' args
2112 | "progress" ->
2113 let progress, text =
2115 Scanf.sscanf args "%f %n"
2116 (fun f pos ->
2117 f, String.sub args pos (String.length args - pos))
2118 with exn ->
2119 dolog "error processing 'progress' %S: %s"
2120 cmds (Printexc.to_string exn);
2121 exit 1;
2123 state.text <- text;
2124 state.progress <- progress;
2125 G.postRedisplay "progress"
2127 | "firstmatch" ->
2128 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2130 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2131 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2132 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2133 with exn ->
2134 dolog "error processing 'firstmatch' %S: %s"
2135 cmds (Printexc.to_string exn);
2136 exit 1;
2138 let y = (getpagey pageno) + truncate y0 in
2139 addnav ();
2140 gotoy y;
2141 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2143 | "match" ->
2144 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2146 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2147 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2148 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2149 with exn ->
2150 dolog "error processing 'match' %S: %s"
2151 cmds (Printexc.to_string exn);
2152 exit 1;
2154 state.rects1 <-
2155 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2157 | "page" ->
2158 let pageopaque, t =
2160 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2161 with exn ->
2162 dolog "error processing 'page' %S: %s"
2163 cmds (Printexc.to_string exn);
2164 exit 1;
2166 begin match state.currently with
2167 | Loading (l, gen) ->
2168 vlog "page %d took %f sec" l.pageno t;
2169 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2170 begin match state.throttle with
2171 | None ->
2172 let preloadedpages =
2173 if conf.preload
2174 then preloadlayout state.layout
2175 else state.layout
2177 let evict () =
2178 let module IntSet =
2179 Set.Make (struct type t = int let compare = (-) end) in
2180 let set =
2181 List.fold_left (fun s l -> IntSet.add l.pageno s)
2182 IntSet.empty preloadedpages
2184 let evictedpages =
2185 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2186 if not (IntSet.mem pageno set)
2187 then (
2188 wcmd "freepage %s" opaque;
2189 key :: accu
2191 else accu
2192 ) state.pagemap []
2194 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2196 evict ();
2197 state.currently <- Idle;
2198 if gen = state.gen
2199 then (
2200 tilepage l.pageno pageopaque state.layout;
2201 load state.layout;
2202 load preloadedpages;
2203 if pagevisible state.layout l.pageno
2204 && layoutready state.layout
2205 then G.postRedisplay "page";
2208 | Some (layout, _, _) ->
2209 state.currently <- Idle;
2210 tilepage l.pageno pageopaque layout;
2211 load state.layout
2212 end;
2214 | _ ->
2215 dolog "Inconsistent loading state";
2216 logcurrently state.currently;
2217 exit 1
2220 | "tile" ->
2221 let (x, y, opaque, size, t) =
2223 Scanf.sscanf args "%u %u %s %u %f"
2224 (fun x y p size t -> (x, y, p, size, t))
2225 with exn ->
2226 dolog "error processing 'tile' %S: %s"
2227 cmds (Printexc.to_string exn);
2228 exit 1;
2230 begin match state.currently with
2231 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2232 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2234 if tilew != conf.tilew || tileh != conf.tileh
2235 then (
2236 wcmd "freetile %s" opaque;
2237 state.currently <- Idle;
2238 load state.layout;
2240 else (
2241 puttileopaque l col row gen cs angle opaque size t;
2242 state.memused <- state.memused + size;
2243 state.uioh#infochanged Memused;
2244 gctiles ();
2245 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2246 opaque, size) state.tilelru;
2248 let layout =
2249 match state.throttle with
2250 | None -> state.layout
2251 | Some (layout, _, _) -> layout
2254 state.currently <- Idle;
2255 if gen = state.gen
2256 && conf.colorspace = cs
2257 && conf.angle = angle
2258 && tilevisible layout l.pageno x y
2259 then conttiling l.pageno pageopaque;
2261 begin match state.throttle with
2262 | None ->
2263 preload state.layout;
2264 if gen = state.gen
2265 && conf.colorspace = cs
2266 && conf.angle = angle
2267 && tilevisible state.layout l.pageno x y
2268 then G.postRedisplay "tile nothrottle";
2270 | Some (layout, y, _) ->
2271 let ready = layoutready layout in
2272 if ready
2273 then (
2274 state.y <- y;
2275 state.layout <- layout;
2276 state.throttle <- None;
2277 G.postRedisplay "throttle";
2279 else load layout;
2280 end;
2283 | _ ->
2284 dolog "Inconsistent tiling state";
2285 logcurrently state.currently;
2286 exit 1
2289 | "pdim" ->
2290 let pdim =
2292 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2293 with exn ->
2294 dolog "error processing 'pdim' %S: %s"
2295 cmds (Printexc.to_string exn);
2296 exit 1;
2298 state.uioh#infochanged Pdim;
2299 state.pdims <- pdim :: state.pdims
2301 | "o" ->
2302 let (l, n, t, h, pos) =
2304 Scanf.sscanf args "%u %u %d %u %n"
2305 (fun l n t h pos -> l, n, t, h, pos)
2306 with exn ->
2307 dolog "error processing 'o' %S: %s"
2308 cmds (Printexc.to_string exn);
2309 exit 1;
2311 let s = String.sub args pos (String.length args - pos) in
2312 let outline = (s, l, (n, float t /. float h)) in
2313 begin match state.currently with
2314 | Outlining outlines ->
2315 state.currently <- Outlining (outline :: outlines)
2316 | Idle ->
2317 state.currently <- Outlining [outline]
2318 | currently ->
2319 dolog "invalid outlining state";
2320 logcurrently currently
2323 | "info" ->
2324 state.docinfo <- (1, args) :: state.docinfo
2326 | "infoend" ->
2327 state.uioh#infochanged Docinfo;
2328 state.docinfo <- List.rev state.docinfo
2330 | _ ->
2331 dolog "unknown cmd `%S'" cmds
2334 let onhist cb =
2335 let rc = cb.rc in
2336 let action = function
2337 | HCprev -> cbget cb ~-1
2338 | HCnext -> cbget cb 1
2339 | HCfirst -> cbget cb ~-(cb.rc)
2340 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2341 and cancel () = cb.rc <- rc
2342 in (action, cancel)
2345 let search pattern forward =
2346 if String.length pattern > 0
2347 then
2348 let pn, py =
2349 match state.layout with
2350 | [] -> 0, 0
2351 | l :: _ ->
2352 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2354 wcmd "search %d %d %d %d,%s\000"
2355 (btod conf.icase) pn py (btod forward) pattern;
2358 let intentry text key =
2359 let c =
2360 if key >= 32 && key < 127
2361 then Char.chr key
2362 else '\000'
2364 match c with
2365 | '0' .. '9' ->
2366 let text = addchar text c in
2367 TEcont text
2369 | _ ->
2370 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2371 TEcont text
2374 let textentry text key =
2375 if key land 0xff00 = 0xff00
2376 then TEcont text
2377 else TEcont (text ^ Wsi.toutf8 key)
2380 let reqlayout angle proportional =
2381 match state.throttle with
2382 | None ->
2383 if nogeomcmds state.geomcmds
2384 then state.anchor <- getanchor ();
2385 conf.angle <- angle mod 360;
2386 if conf.angle != 0
2387 then (
2388 match state.mode with
2389 | LinkNav _ -> state.mode <- View
2390 | _ -> ()
2392 conf.proportional <- proportional;
2393 invalidate "reqlayout"
2394 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2395 | _ -> ()
2398 let settrim trimmargins trimfuzz =
2399 if nogeomcmds state.geomcmds
2400 then state.anchor <- getanchor ();
2401 conf.trimmargins <- trimmargins;
2402 conf.trimfuzz <- trimfuzz;
2403 let x0, y0, x1, y1 = trimfuzz in
2404 invalidate "settrim"
2405 (fun () ->
2406 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2407 Hashtbl.iter (fun _ opaque ->
2408 wcmd "freepage %s" opaque;
2409 ) state.pagemap;
2410 Hashtbl.clear state.pagemap;
2413 let setzoom zoom =
2414 match state.throttle with
2415 | None ->
2416 let zoom = max 0.01 zoom in
2417 if zoom <> conf.zoom
2418 then (
2419 state.prevzoom <- conf.zoom;
2420 conf.zoom <- zoom;
2421 reshape conf.winw conf.winh;
2422 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2425 | Some (layout, y, started) ->
2426 let time =
2427 match conf.maxwait with
2428 | None -> 0.0
2429 | Some t -> t
2431 let dt = now () -. started in
2432 if dt > time
2433 then (
2434 state.y <- y;
2435 load layout;
2439 let setcolumns mode columns coverA coverB =
2440 if columns < 0
2441 then (
2442 if isbirdseye mode
2443 then showtext '!' "split mode doesn't work in bird's eye"
2444 else (
2445 conf.columns <- Csplit (-columns, [||]);
2446 state.x <- 0;
2447 conf.zoom <- 1.0;
2450 else (
2451 if columns < 2
2452 then (
2453 conf.columns <- Csingle;
2454 state.x <- 0;
2455 setzoom 1.0;
2457 else (
2458 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2459 conf.zoom <- 1.0;
2462 reshape conf.winw conf.winh;
2465 let enterbirdseye () =
2466 let zoom = float conf.thumbw /. float conf.winw in
2467 let birdseyepageno =
2468 let cy = conf.winh / 2 in
2469 let fold = function
2470 | [] -> 0
2471 | l :: rest ->
2472 let rec fold best = function
2473 | [] -> best.pageno
2474 | l :: rest ->
2475 let d = cy - (l.pagedispy + l.pagevh/2)
2476 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2477 if abs d < abs dbest
2478 then fold l rest
2479 else best.pageno
2480 in fold l rest
2482 fold state.layout
2484 state.mode <- Birdseye (
2485 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2487 conf.zoom <- zoom;
2488 conf.presentation <- false;
2489 conf.interpagespace <- 10;
2490 conf.hlinks <- false;
2491 state.x <- 0;
2492 state.mstate <- Mnone;
2493 conf.maxwait <- None;
2494 conf.columns <- (
2495 match conf.beyecolumns with
2496 | Some c ->
2497 conf.zoom <- 1.0;
2498 Cmulti ((c, 0, 0), [||])
2499 | None -> Csingle
2501 Wsi.setcursor Wsi.CURSOR_INHERIT;
2502 if conf.verbose
2503 then
2504 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2505 (100.0*.zoom)
2506 else
2507 state.text <- ""
2509 reshape conf.winw conf.winh;
2512 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2513 state.mode <- View;
2514 conf.zoom <- c.zoom;
2515 conf.presentation <- c.presentation;
2516 conf.interpagespace <- c.interpagespace;
2517 conf.maxwait <- c.maxwait;
2518 conf.hlinks <- c.hlinks;
2519 conf.beyecolumns <- (
2520 match conf.columns with
2521 | Cmulti ((c, _, _), _) -> Some c
2522 | Csingle -> None
2523 | Csplit _ -> assert false
2525 conf.columns <- (
2526 match c.columns with
2527 | Cmulti (c, _) -> Cmulti (c, [||])
2528 | Csingle -> Csingle
2529 | Csplit _ -> failwith "leaving bird's eye split mode"
2531 state.x <- leftx;
2532 if conf.verbose
2533 then
2534 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2535 (100.0*.conf.zoom)
2537 reshape conf.winw conf.winh;
2538 state.anchor <- if goback then anchor else (pageno, 0.0);
2541 let togglebirdseye () =
2542 match state.mode with
2543 | Birdseye vals -> leavebirdseye vals true
2544 | View -> enterbirdseye ()
2545 | _ -> ()
2548 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2549 let pageno = max 0 (pageno - incr) in
2550 let rec loop = function
2551 | [] -> gotopage1 pageno 0
2552 | l :: _ when l.pageno = pageno ->
2553 if l.pagedispy >= 0 && l.pagey = 0
2554 then G.postRedisplay "upbirdseye"
2555 else gotopage1 pageno 0
2556 | _ :: rest -> loop rest
2558 loop state.layout;
2559 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2562 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2563 let pageno = min (state.pagecount - 1) (pageno + incr) in
2564 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2565 let rec loop = function
2566 | [] ->
2567 let y, h = getpageyh pageno in
2568 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2569 gotoy (clamp dy)
2570 | l :: _ when l.pageno = pageno ->
2571 if l.pagevh != l.pageh
2572 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2573 else G.postRedisplay "downbirdseye"
2574 | _ :: rest -> loop rest
2576 loop state.layout
2579 let optentry mode _ key =
2580 let btos b = if b then "on" else "off" in
2581 if key >= 32 && key < 127
2582 then
2583 let c = Char.chr key in
2584 match c with
2585 | 's' ->
2586 let ondone s =
2587 try conf.scrollstep <- int_of_string s with exc ->
2588 state.text <- Printf.sprintf "bad integer `%s': %s"
2589 s (Printexc.to_string exc)
2591 TEswitch ("scroll step: ", "", None, intentry, ondone)
2593 | 'A' ->
2594 let ondone s =
2596 conf.autoscrollstep <- int_of_string s;
2597 if state.autoscroll <> None
2598 then state.autoscroll <- Some conf.autoscrollstep
2599 with exc ->
2600 state.text <- Printf.sprintf "bad integer `%s': %s"
2601 s (Printexc.to_string exc)
2603 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2605 | 'C' ->
2606 let mode = state.mode in
2607 let ondone s =
2609 let n, a, b = multicolumns_of_string s in
2610 setcolumns mode n a b;
2611 with exc ->
2612 state.text <- Printf.sprintf "bad columns `%s': %s"
2613 s (Printexc.to_string exc)
2615 TEswitch ("columns: ", "", None, textentry, ondone)
2617 | 'Z' ->
2618 let ondone s =
2620 let zoom = float (int_of_string s) /. 100.0 in
2621 setzoom zoom
2622 with exc ->
2623 state.text <- Printf.sprintf "bad integer `%s': %s"
2624 s (Printexc.to_string exc)
2626 TEswitch ("zoom: ", "", None, intentry, ondone)
2628 | 't' ->
2629 let ondone s =
2631 conf.thumbw <- bound (int_of_string s) 2 4096;
2632 state.text <-
2633 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2634 begin match mode with
2635 | Birdseye beye ->
2636 leavebirdseye beye false;
2637 enterbirdseye ();
2638 | _ -> ();
2640 with exc ->
2641 state.text <- Printf.sprintf "bad integer `%s': %s"
2642 s (Printexc.to_string exc)
2644 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2646 | 'R' ->
2647 let ondone s =
2648 match try
2649 Some (int_of_string s)
2650 with exc ->
2651 state.text <- Printf.sprintf "bad integer `%s': %s"
2652 s (Printexc.to_string exc);
2653 None
2654 with
2655 | Some angle -> reqlayout angle conf.proportional
2656 | None -> ()
2658 TEswitch ("rotation: ", "", None, intentry, ondone)
2660 | 'i' ->
2661 conf.icase <- not conf.icase;
2662 TEdone ("case insensitive search " ^ (btos conf.icase))
2664 | 'p' ->
2665 conf.preload <- not conf.preload;
2666 gotoy state.y;
2667 TEdone ("preload " ^ (btos conf.preload))
2669 | 'v' ->
2670 conf.verbose <- not conf.verbose;
2671 TEdone ("verbose " ^ (btos conf.verbose))
2673 | 'd' ->
2674 conf.debug <- not conf.debug;
2675 TEdone ("debug " ^ (btos conf.debug))
2677 | 'h' ->
2678 conf.maxhfit <- not conf.maxhfit;
2679 state.maxy <-
2680 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2681 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2683 | 'c' ->
2684 conf.crophack <- not conf.crophack;
2685 TEdone ("crophack " ^ btos conf.crophack)
2687 | 'a' ->
2688 let s =
2689 match conf.maxwait with
2690 | None ->
2691 conf.maxwait <- Some infinity;
2692 "always wait for page to complete"
2693 | Some _ ->
2694 conf.maxwait <- None;
2695 "show placeholder if page is not ready"
2697 TEdone s
2699 | 'f' ->
2700 conf.underinfo <- not conf.underinfo;
2701 TEdone ("underinfo " ^ btos conf.underinfo)
2703 | 'P' ->
2704 conf.savebmarks <- not conf.savebmarks;
2705 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2707 | 'S' ->
2708 let ondone s =
2710 let pageno, py =
2711 match state.layout with
2712 | [] -> 0, 0
2713 | l :: _ ->
2714 l.pageno, l.pagey
2716 conf.interpagespace <- int_of_string s;
2717 state.maxy <- calcheight ();
2718 let y = getpagey pageno in
2719 gotoy (y + py)
2720 with exc ->
2721 state.text <- Printf.sprintf "bad integer `%s': %s"
2722 s (Printexc.to_string exc)
2724 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2726 | 'l' ->
2727 reqlayout conf.angle (not conf.proportional);
2728 TEdone ("proportional display " ^ btos conf.proportional)
2730 | 'T' ->
2731 settrim (not conf.trimmargins) conf.trimfuzz;
2732 TEdone ("trim margins " ^ btos conf.trimmargins)
2734 | 'I' ->
2735 conf.invert <- not conf.invert;
2736 TEdone ("invert colors " ^ btos conf.invert)
2738 | 'x' ->
2739 let ondone s =
2740 cbput state.hists.sel s;
2741 conf.selcmd <- s;
2743 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2744 textentry, ondone)
2746 | _ ->
2747 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2748 TEstop
2749 else
2750 TEcont state.text
2753 class type lvsource = object
2754 method getitemcount : int
2755 method getitem : int -> (string * int)
2756 method hasaction : int -> bool
2757 method exit :
2758 uioh:uioh ->
2759 cancel:bool ->
2760 active:int ->
2761 first:int ->
2762 pan:int ->
2763 qsearch:string ->
2764 uioh option
2765 method getactive : int
2766 method getfirst : int
2767 method getqsearch : string
2768 method setqsearch : string -> unit
2769 method getpan : int
2770 end;;
2772 class virtual lvsourcebase = object
2773 val mutable m_active = 0
2774 val mutable m_first = 0
2775 val mutable m_qsearch = ""
2776 val mutable m_pan = 0
2777 method getactive = m_active
2778 method getfirst = m_first
2779 method getqsearch = m_qsearch
2780 method getpan = m_pan
2781 method setqsearch s = m_qsearch <- s
2782 end;;
2784 let withoutlastutf8 s =
2785 let len = String.length s in
2786 if len = 0
2787 then s
2788 else
2789 let rec find pos =
2790 if pos = 0
2791 then pos
2792 else
2793 let b = Char.code s.[pos] in
2794 if b land 0b110000 = 0b11000000
2795 then find (pos-1)
2796 else pos-1
2798 let first =
2799 if Char.code s.[len-1] land 0x80 = 0
2800 then len-1
2801 else find (len-1)
2803 String.sub s 0 first;
2806 let textentrykeyboard key _mask ((c, text, opthist, onkey, ondone), onleave) =
2807 let enttext te =
2808 state.mode <- Textentry (te, onleave);
2809 state.text <- "";
2810 enttext ();
2811 G.postRedisplay "textentrykeyboard enttext";
2813 let histaction cmd =
2814 match opthist with
2815 | None -> ()
2816 | Some (action, _) ->
2817 state.mode <- Textentry (
2818 (c, action cmd, opthist, onkey, ondone), onleave
2820 G.postRedisplay "textentry histaction"
2822 match key with
2823 | 0xff08 -> (* backspace *)
2824 let s = withoutlastutf8 text in
2825 let len = String.length s in
2826 if len = 0
2827 then (
2828 onleave Cancel;
2829 G.postRedisplay "textentrykeyboard after cancel";
2831 else (
2832 enttext (c, s, opthist, onkey, ondone)
2835 | 0xff0d ->
2836 ondone text;
2837 onleave Confirm;
2838 G.postRedisplay "textentrykeyboard after confirm"
2840 | 0xff52 -> histaction HCprev
2841 | 0xff54 -> histaction HCnext
2842 | 0xff50 -> histaction HCfirst
2843 | 0xff57 -> histaction HClast
2845 | 0xff1b -> (* escape*)
2846 if String.length text = 0
2847 then (
2848 begin match opthist with
2849 | None -> ()
2850 | Some (_, onhistcancel) -> onhistcancel ()
2851 end;
2852 onleave Cancel;
2853 state.text <- "";
2854 G.postRedisplay "textentrykeyboard after cancel2"
2856 else (
2857 enttext (c, "", opthist, onkey, ondone)
2860 | 0xff9f | 0xffff -> () (* delete *)
2862 | _ when key != 0 && key land 0xff00 != 0xff00 ->
2863 begin match onkey text key with
2864 | TEdone text ->
2865 ondone text;
2866 onleave Confirm;
2867 G.postRedisplay "textentrykeyboard after confirm2";
2869 | TEcont text ->
2870 enttext (c, text, opthist, onkey, ondone);
2872 | TEstop ->
2873 onleave Cancel;
2874 G.postRedisplay "textentrykeyboard after cancel3"
2876 | TEswitch te ->
2877 state.mode <- Textentry (te, onleave);
2878 G.postRedisplay "textentrykeyboard switch";
2879 end;
2881 | _ ->
2882 vlog "unhandled key %s" (Wsi.keyname key)
2885 let firstof first active =
2886 if first > active || abs (first - active) > fstate.maxrows - 1
2887 then max 0 (active - (fstate.maxrows/2))
2888 else first
2891 let calcfirst first active =
2892 if active > first
2893 then
2894 let rows = active - first in
2895 if rows > fstate.maxrows then active - fstate.maxrows else first
2896 else active
2899 let scrollph y maxy =
2900 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2901 let sh = float conf.winh /. sh in
2902 let sh = max sh (float conf.scrollh) in
2904 let percent =
2905 if y = state.maxy
2906 then 1.0
2907 else float y /. float maxy
2909 let position = (float conf.winh -. sh) *. percent in
2911 let position =
2912 if position +. sh > float conf.winh
2913 then float conf.winh -. sh
2914 else position
2916 position, sh;
2919 let coe s = (s :> uioh);;
2921 class listview ~(source:lvsource) ~trusted ~modehash =
2922 object (self)
2923 val m_pan = source#getpan
2924 val m_first = source#getfirst
2925 val m_active = source#getactive
2926 val m_qsearch = source#getqsearch
2927 val m_prev_uioh = state.uioh
2929 method private elemunder y =
2930 let n = y / (fstate.fontsize+1) in
2931 if m_first + n < source#getitemcount
2932 then (
2933 if source#hasaction (m_first + n)
2934 then Some (m_first + n)
2935 else None
2937 else None
2939 method display =
2940 Gl.enable `blend;
2941 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2942 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2943 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2944 GlDraw.color (1., 1., 1.);
2945 Gl.enable `texture_2d;
2946 let fs = fstate.fontsize in
2947 let nfs = fs + 1 in
2948 let ww = fstate.wwidth in
2949 let tabw = 30.0*.ww in
2950 let itemcount = source#getitemcount in
2951 let rec loop row =
2952 if (row - m_first) * nfs > conf.winh
2953 then ()
2954 else (
2955 if row >= 0 && row < itemcount
2956 then (
2957 let (s, level) = source#getitem row in
2958 let y = (row - m_first) * nfs in
2959 let x = 5.0 +. float (level + m_pan) *. ww in
2960 if row = m_active
2961 then (
2962 Gl.disable `texture_2d;
2963 GlDraw.polygon_mode `both `line;
2964 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2965 GlDraw.rect (1., float (y + 1))
2966 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2967 GlDraw.polygon_mode `both `fill;
2968 GlDraw.color (1., 1., 1.);
2969 Gl.enable `texture_2d;
2972 let drawtabularstring s =
2973 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2974 if trusted
2975 then
2976 let tabpos = try String.index s '\t' with Not_found -> -1 in
2977 if tabpos > 0
2978 then
2979 let len = String.length s - tabpos - 1 in
2980 let s1 = String.sub s 0 tabpos
2981 and s2 = String.sub s (tabpos + 1) len in
2982 let nx = drawstr x s1 in
2983 let sw = nx -. x in
2984 let x = x +. (max tabw sw) in
2985 drawstr x s2
2986 else
2987 drawstr x s
2988 else
2989 drawstr x s
2991 let _ = drawtabularstring s in
2992 loop (row+1)
2996 loop m_first;
2997 Gl.disable `blend;
2998 Gl.disable `texture_2d;
3000 method updownlevel incr =
3001 let len = source#getitemcount in
3002 let curlevel =
3003 if m_active >= 0 && m_active < len
3004 then snd (source#getitem m_active)
3005 else -1
3007 let rec flow i =
3008 if i = len then i-1 else if i = -1 then 0 else
3009 let _, l = source#getitem i in
3010 if l != curlevel then i else flow (i+incr)
3012 let active = flow m_active in
3013 let first = calcfirst m_first active in
3014 G.postRedisplay "outline updownlevel";
3015 {< m_active = active; m_first = first >}
3017 method private key1 key mask =
3018 let set1 active first qsearch =
3019 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3021 let search active pattern incr =
3022 let dosearch re =
3023 let rec loop n =
3024 if n >= 0 && n < source#getitemcount
3025 then (
3026 let s, _ = source#getitem n in
3028 (try ignore (Str.search_forward re s 0); true
3029 with Not_found -> false)
3030 then Some n
3031 else loop (n + incr)
3033 else None
3035 loop active
3038 let re = Str.regexp_case_fold pattern in
3039 dosearch re
3040 with Failure s ->
3041 state.text <- s;
3042 None
3044 let itemcount = source#getitemcount in
3045 let find start incr =
3046 let rec find i =
3047 if i = -1 || i = itemcount
3048 then -1
3049 else (
3050 if source#hasaction i
3051 then i
3052 else find (i + incr)
3055 find start
3057 let set active first =
3058 let first = bound first 0 (itemcount - fstate.maxrows) in
3059 state.text <- "";
3060 coe {< m_active = active; m_first = first >}
3062 let navigate incr =
3063 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3064 let active, first =
3065 let incr1 = if incr > 0 then 1 else -1 in
3066 if isvisible m_first m_active
3067 then
3068 let next =
3069 let next = m_active + incr in
3070 let next =
3071 if next < 0 || next >= itemcount
3072 then -1
3073 else find next incr1
3075 if next = -1 || abs (m_active - next) > fstate.maxrows
3076 then -1
3077 else next
3079 if next = -1
3080 then
3081 let first = m_first + incr in
3082 let first = bound first 0 (itemcount - 1) in
3083 let next =
3084 let next = m_active + incr in
3085 let next = bound next 0 (itemcount - 1) in
3086 find next ~-incr1
3088 let active = if next = -1 then m_active else next in
3089 active, first
3090 else
3091 let first = min next m_first in
3092 let first =
3093 if abs (next - first) > fstate.maxrows
3094 then first + incr
3095 else first
3097 next, first
3098 else
3099 let first = m_first + incr in
3100 let first = bound first 0 (itemcount - 1) in
3101 let active =
3102 let next = m_active + incr in
3103 let next = bound next 0 (itemcount - 1) in
3104 let next = find next incr1 in
3105 let active =
3106 if next = -1 || abs (m_active - first) > fstate.maxrows
3107 then (
3108 let active = if m_active = -1 then next else m_active in
3109 active
3111 else next
3113 if isvisible first active
3114 then active
3115 else -1
3117 active, first
3119 G.postRedisplay "listview navigate";
3120 set active first;
3122 match key with
3123 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3124 let incr = if key = 0x72 then -1 else 1 in
3125 let active, first =
3126 match search (m_active + incr) m_qsearch incr with
3127 | None ->
3128 state.text <- m_qsearch ^ " [not found]";
3129 m_active, m_first
3130 | Some active ->
3131 state.text <- m_qsearch;
3132 active, firstof m_first active
3134 G.postRedisplay "listview ctrl-r/s";
3135 set1 active first m_qsearch;
3137 | 0xff08 -> (* backspace *)
3138 if String.length m_qsearch = 0
3139 then coe self
3140 else (
3141 let qsearch = withoutlastutf8 m_qsearch in
3142 let len = String.length qsearch in
3143 if len = 0
3144 then (
3145 state.text <- "";
3146 G.postRedisplay "listview empty qsearch";
3147 set1 m_active m_first "";
3149 else
3150 let active, first =
3151 match search m_active qsearch ~-1 with
3152 | None ->
3153 state.text <- qsearch ^ " [not found]";
3154 m_active, m_first
3155 | Some active ->
3156 state.text <- qsearch;
3157 active, firstof m_first active
3159 G.postRedisplay "listview backspace qsearch";
3160 set1 active first qsearch
3163 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3164 let pattern = m_qsearch ^ Wsi.toutf8 key in
3165 let active, first =
3166 match search m_active pattern 1 with
3167 | None ->
3168 state.text <- pattern ^ " [not found]";
3169 m_active, m_first
3170 | Some active ->
3171 state.text <- pattern;
3172 active, firstof m_first active
3174 G.postRedisplay "listview qsearch add";
3175 set1 active first pattern;
3177 | 0xff1b -> (* escape *)
3178 state.text <- "";
3179 if String.length m_qsearch = 0
3180 then (
3181 G.postRedisplay "list view escape";
3182 begin
3183 match
3184 source#exit (coe self) true m_active m_first m_pan m_qsearch
3185 with
3186 | None -> m_prev_uioh
3187 | Some uioh -> uioh
3190 else (
3191 G.postRedisplay "list view kill qsearch";
3192 source#setqsearch "";
3193 coe {< m_qsearch = "" >}
3196 | 0xff0d -> (* return *)
3197 state.text <- "";
3198 let self = {< m_qsearch = "" >} in
3199 source#setqsearch "";
3200 let opt =
3201 G.postRedisplay "listview enter";
3202 if m_active >= 0 && m_active < source#getitemcount
3203 then (
3204 source#exit (coe self) false m_active m_first m_pan "";
3206 else (
3207 source#exit (coe self) true m_active m_first m_pan "";
3210 begin match opt with
3211 | None -> m_prev_uioh
3212 | Some uioh -> uioh
3215 | 0xff9f | 0xffff -> (* delete *)
3216 coe self
3218 | 0xff52 -> navigate ~-1 (* up *)
3219 | 0xff54 -> navigate 1 (* down *)
3220 | 0xff55 -> navigate ~-(fstate.maxrows) (* prior *)
3221 | 0xff56 -> navigate fstate.maxrows (* next *)
3223 | 0xff53 -> (* right *)
3224 state.text <- "";
3225 G.postRedisplay "listview right";
3226 coe {< m_pan = m_pan - 1 >}
3228 | 0xff51 -> (* left *)
3229 state.text <- "";
3230 G.postRedisplay "listview left";
3231 coe {< m_pan = m_pan + 1 >}
3233 | 0xff50 -> (* home *)
3234 let active = find 0 1 in
3235 G.postRedisplay "listview home";
3236 set active 0;
3238 | 0xff57 -> (* end *)
3239 let first = max 0 (itemcount - fstate.maxrows) in
3240 let active = find (itemcount - 1) ~-1 in
3241 G.postRedisplay "listview end";
3242 set active first;
3244 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3245 coe self
3247 | _ ->
3248 dolog "listview unknown key %#x" key; coe self
3250 method key key mask =
3251 match state.mode with
3252 | Textentry te -> textentrykeyboard key mask te; coe self
3253 | _ -> self#key1 key mask
3255 method button button down x y _ =
3256 let opt =
3257 match button with
3258 | 1 when x > conf.winw - conf.scrollbw ->
3259 G.postRedisplay "listview scroll";
3260 if down
3261 then
3262 let _, position, sh = self#scrollph in
3263 if y > truncate position && y < truncate (position +. sh)
3264 then (
3265 state.mstate <- Mscrolly;
3266 Some (coe self)
3268 else
3269 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3270 let first = truncate (s *. float source#getitemcount) in
3271 let first = min source#getitemcount first in
3272 Some (coe {< m_first = first; m_active = first >})
3273 else (
3274 state.mstate <- Mnone;
3275 Some (coe self);
3277 | 1 when not down ->
3278 begin match self#elemunder y with
3279 | Some n ->
3280 G.postRedisplay "listview click";
3281 source#exit
3282 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3283 | _ ->
3284 Some (coe self)
3286 | n when (n == 4 || n == 5) && not down ->
3287 let len = source#getitemcount in
3288 let first =
3289 if n = 5 && m_first + fstate.maxrows >= len
3290 then
3291 m_first
3292 else
3293 let first = m_first + (if n == 4 then -1 else 1) in
3294 bound first 0 (len - 1)
3296 G.postRedisplay "listview wheel";
3297 Some (coe {< m_first = first >})
3298 | _ ->
3299 Some (coe self)
3301 match opt with
3302 | None -> m_prev_uioh
3303 | Some uioh -> uioh
3305 method motion _ y =
3306 match state.mstate with
3307 | Mscrolly ->
3308 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3309 let first = truncate (s *. float source#getitemcount) in
3310 let first = min source#getitemcount first in
3311 G.postRedisplay "listview motion";
3312 coe {< m_first = first; m_active = first >}
3313 | _ -> coe self
3315 method pmotion x y =
3316 if x < conf.winw - conf.scrollbw
3317 then
3318 let n =
3319 match self#elemunder y with
3320 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3321 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3323 let o =
3324 if n != m_active
3325 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3326 else self
3328 coe o
3329 else (
3330 Wsi.setcursor Wsi.CURSOR_INHERIT;
3331 coe self
3334 method infochanged _ = ()
3336 method scrollpw = (0, 0.0, 0.0)
3337 method scrollph =
3338 let nfs = fstate.fontsize + 1 in
3339 let y = m_first * nfs in
3340 let itemcount = source#getitemcount in
3341 let maxi = max 0 (itemcount - fstate.maxrows) in
3342 let maxy = maxi * nfs in
3343 let p, h = scrollph y maxy in
3344 conf.scrollbw, p, h
3346 method modehash = modehash
3347 end;;
3349 class outlinelistview ~source =
3350 object (self)
3351 inherit listview
3352 ~source:(source :> lvsource)
3353 ~trusted:false
3354 ~modehash:(findkeyhash conf "outline")
3355 as super
3357 method key key mask =
3358 let calcfirst first active =
3359 if active > first
3360 then
3361 let rows = active - first in
3362 if rows > fstate.maxrows then active - fstate.maxrows else first
3363 else active
3365 let navigate incr =
3366 let active = m_active + incr in
3367 let active = bound active 0 (source#getitemcount - 1) in
3368 let first = calcfirst m_first active in
3369 G.postRedisplay "outline navigate";
3370 coe {< m_active = active; m_first = first >}
3372 let ctrl = Wsi.withctrl mask in
3373 match key with
3374 | 110 when ctrl -> (* ctrl-n *)
3375 source#narrow m_qsearch;
3376 G.postRedisplay "outline ctrl-n";
3377 coe {< m_first = 0; m_active = 0 >}
3379 | 117 when ctrl -> (* ctrl-u *)
3380 source#denarrow;
3381 G.postRedisplay "outline ctrl-u";
3382 state.text <- "";
3383 coe {< m_first = 0; m_active = 0 >}
3385 | 108 when ctrl -> (* ctrl-l *)
3386 let first = m_active - (fstate.maxrows / 2) in
3387 G.postRedisplay "outline ctrl-l";
3388 coe {< m_first = first >}
3390 | 0xff9f | 0xffff -> (* delete *)
3391 source#remove m_active;
3392 G.postRedisplay "outline delete";
3393 let active = max 0 (m_active-1) in
3394 coe {< m_first = firstof m_first active;
3395 m_active = active >}
3397 | 0xff52 -> navigate ~-1 (* up *)
3398 | 0xff54 -> navigate 1 (* down *)
3399 | 0xff55 -> (* prior *)
3400 navigate ~-(fstate.maxrows)
3401 | 0xff56 -> (* next *)
3402 navigate fstate.maxrows
3404 | 0xff53 -> (* [ctrl-]right *)
3405 let o =
3406 if ctrl
3407 then (
3408 G.postRedisplay "outline ctrl right";
3409 {< m_pan = m_pan + 1 >}
3411 else self#updownlevel 1
3413 coe o
3415 | 0xff51 -> (* [ctrl-]left *)
3416 let o =
3417 if ctrl
3418 then (
3419 G.postRedisplay "outline ctrl left";
3420 {< m_pan = m_pan - 1 >}
3422 else self#updownlevel ~-1
3424 coe o
3426 | 0xff50 -> (* home *)
3427 G.postRedisplay "outline home";
3428 coe {< m_first = 0; m_active = 0 >}
3430 | 0xff57 -> (* end *)
3431 let active = source#getitemcount - 1 in
3432 let first = max 0 (active - fstate.maxrows) in
3433 G.postRedisplay "outline end";
3434 coe {< m_active = active; m_first = first >}
3436 | _ -> super#key key mask
3439 let outlinesource usebookmarks =
3440 let empty = [||] in
3441 (object
3442 inherit lvsourcebase
3443 val mutable m_items = empty
3444 val mutable m_orig_items = empty
3445 val mutable m_prev_items = empty
3446 val mutable m_narrow_pattern = ""
3447 val mutable m_hadremovals = false
3449 method getitemcount =
3450 Array.length m_items + (if m_hadremovals then 1 else 0)
3452 method getitem n =
3453 if n == Array.length m_items && m_hadremovals
3454 then
3455 ("[Confirm removal]", 0)
3456 else
3457 let s, n, _ = m_items.(n) in
3458 (s, n)
3460 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3461 ignore (uioh, first, qsearch);
3462 let confrimremoval = m_hadremovals && active = Array.length m_items in
3463 let items =
3464 if String.length m_narrow_pattern = 0
3465 then m_orig_items
3466 else m_items
3468 if not cancel
3469 then (
3470 if not confrimremoval
3471 then(
3472 let _, _, anchor = m_items.(active) in
3473 gotoanchor anchor;
3474 m_items <- items;
3476 else (
3477 state.bookmarks <- Array.to_list m_items;
3478 m_orig_items <- m_items;
3481 else m_items <- items;
3482 m_pan <- pan;
3483 None
3485 method hasaction _ = true
3487 method greetmsg =
3488 if Array.length m_items != Array.length m_orig_items
3489 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3490 else ""
3492 method narrow pattern =
3493 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3494 match reopt with
3495 | None -> ()
3496 | Some re ->
3497 let rec loop accu n =
3498 if n = -1
3499 then (
3500 m_narrow_pattern <- pattern;
3501 m_items <- Array.of_list accu
3503 else
3504 let (s, _, _) as o = m_items.(n) in
3505 let accu =
3506 if (try ignore (Str.search_forward re s 0); true
3507 with Not_found -> false)
3508 then o :: accu
3509 else accu
3511 loop accu (n-1)
3513 loop [] (Array.length m_items - 1)
3515 method denarrow =
3516 m_orig_items <- (
3517 if usebookmarks
3518 then Array.of_list state.bookmarks
3519 else state.outlines
3521 m_items <- m_orig_items
3523 method remove m =
3524 if usebookmarks
3525 then
3526 if m >= 0 && m < Array.length m_items
3527 then (
3528 m_hadremovals <- true;
3529 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3530 let n = if n >= m then n+1 else n in
3531 m_items.(n)
3535 method reset anchor items =
3536 m_hadremovals <- false;
3537 if m_orig_items == empty || m_prev_items != items
3538 then (
3539 m_orig_items <- items;
3540 if String.length m_narrow_pattern = 0
3541 then m_items <- items;
3543 m_prev_items <- items;
3544 let rely = getanchory anchor in
3545 let active =
3546 let rec loop n best bestd =
3547 if n = Array.length m_items
3548 then best
3549 else
3550 let (_, _, anchor) = m_items.(n) in
3551 let orely = getanchory anchor in
3552 let d = abs (orely - rely) in
3553 if d < bestd
3554 then loop (n+1) n d
3555 else loop (n+1) best bestd
3557 loop 0 ~-1 max_int
3559 m_active <- active;
3560 m_first <- firstof m_first active
3561 end)
3564 let enterselector usebookmarks =
3565 let source = outlinesource usebookmarks in
3566 fun errmsg ->
3567 let outlines =
3568 if usebookmarks
3569 then Array.of_list state.bookmarks
3570 else state.outlines
3572 if Array.length outlines = 0
3573 then (
3574 showtext ' ' errmsg;
3576 else (
3577 state.text <- source#greetmsg;
3578 Wsi.setcursor Wsi.CURSOR_INHERIT;
3579 let anchor = getanchor () in
3580 source#reset anchor outlines;
3581 state.uioh <- coe (new outlinelistview ~source);
3582 G.postRedisplay "enter selector";
3586 let enteroutlinemode =
3587 let f = enterselector false in
3588 fun ()-> f "Document has no outline";
3591 let enterbookmarkmode =
3592 let f = enterselector true in
3593 fun () -> f "Document has no bookmarks (yet)";
3596 let color_of_string s =
3597 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3598 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3602 let color_to_string (r, g, b) =
3603 let r = truncate (r *. 256.0)
3604 and g = truncate (g *. 256.0)
3605 and b = truncate (b *. 256.0) in
3606 Printf.sprintf "%d/%d/%d" r g b
3609 let irect_of_string s =
3610 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3613 let irect_to_string (x0,y0,x1,y1) =
3614 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3617 let makecheckers () =
3618 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3619 following to say:
3620 converted by Issac Trotts. July 25, 2002 *)
3621 let image_height = 64
3622 and image_width = 64 in
3624 let make_image () =
3625 let image =
3626 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3628 for i = 0 to image_width - 1 do
3629 for j = 0 to image_height - 1 do
3630 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3631 (if (i land 8 ) lxor (j land 8) = 0
3632 then [|255;255;255|] else [|200;200;200|])
3633 done
3634 done;
3635 image
3637 let image = make_image () in
3638 let id = GlTex.gen_texture () in
3639 GlTex.bind_texture `texture_2d id;
3640 GlPix.store (`unpack_alignment 1);
3641 GlTex.image2d image;
3642 List.iter (GlTex.parameter ~target:`texture_2d)
3643 [ `wrap_s `repeat;
3644 `wrap_t `repeat;
3645 `mag_filter `nearest;
3646 `min_filter `nearest ];
3650 let setcheckers enabled =
3651 match state.texid with
3652 | None ->
3653 if enabled then state.texid <- Some (makecheckers ())
3655 | Some texid ->
3656 if not enabled
3657 then (
3658 GlTex.delete_texture texid;
3659 state.texid <- None;
3663 let int_of_string_with_suffix s =
3664 let l = String.length s in
3665 let s1, shift =
3666 if l > 1
3667 then
3668 let suffix = Char.lowercase s.[l-1] in
3669 match suffix with
3670 | 'k' -> String.sub s 0 (l-1), 10
3671 | 'm' -> String.sub s 0 (l-1), 20
3672 | 'g' -> String.sub s 0 (l-1), 30
3673 | _ -> s, 0
3674 else s, 0
3676 let n = int_of_string s1 in
3677 let m = n lsl shift in
3678 if m < 0 || m < n
3679 then raise (Failure "value too large")
3680 else m
3683 let string_with_suffix_of_int n =
3684 if n = 0
3685 then "0"
3686 else
3687 let n, s =
3688 if n = 0
3689 then 0, ""
3690 else (
3691 if n land ((1 lsl 20) - 1) = 0
3692 then n lsr 20, "M"
3693 else (
3694 if n land ((1 lsl 10) - 1) = 0
3695 then n lsr 10, "K"
3696 else n, ""
3700 let rec loop s n =
3701 let h = n mod 1000 in
3702 let n = n / 1000 in
3703 if n = 0
3704 then string_of_int h ^ s
3705 else (
3706 let s = Printf.sprintf "_%03d%s" h s in
3707 loop s n
3710 loop "" n ^ s;
3713 let defghyllscroll = (40, 8, 32);;
3714 let ghyllscroll_of_string s =
3715 let (n, a, b) as nab =
3716 if s = "default"
3717 then defghyllscroll
3718 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3720 if n <= a || n <= b || a >= b
3721 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3722 nab;
3725 let ghyllscroll_to_string ((n, a, b) as nab) =
3726 if nab = defghyllscroll
3727 then "default"
3728 else Printf.sprintf "%d,%d,%d" n a b;
3731 let describe_location () =
3732 let f (fn, _) l =
3733 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3735 let fn, ln = List.fold_left f (-1, -1) state.layout in
3736 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3737 let percent =
3738 if maxy <= 0
3739 then 100.
3740 else (100. *. (float state.y /. float maxy))
3742 if fn = ln
3743 then
3744 Printf.sprintf "page %d of %d [%.2f%%]"
3745 (fn+1) state.pagecount percent
3746 else
3747 Printf.sprintf
3748 "pages %d-%d of %d [%.2f%%]"
3749 (fn+1) (ln+1) state.pagecount percent
3752 let enterinfomode =
3753 let btos b = if b then "\xe2\x88\x9a" else "" in
3754 let showextended = ref false in
3755 let leave mode = function
3756 | Confirm -> state.mode <- mode
3757 | Cancel -> state.mode <- mode in
3758 let src =
3759 (object
3760 val mutable m_first_time = true
3761 val mutable m_l = []
3762 val mutable m_a = [||]
3763 val mutable m_prev_uioh = nouioh
3764 val mutable m_prev_mode = View
3766 inherit lvsourcebase
3768 method reset prev_mode prev_uioh =
3769 m_a <- Array.of_list (List.rev m_l);
3770 m_l <- [];
3771 m_prev_mode <- prev_mode;
3772 m_prev_uioh <- prev_uioh;
3773 if m_first_time
3774 then (
3775 let rec loop n =
3776 if n >= Array.length m_a
3777 then ()
3778 else
3779 match m_a.(n) with
3780 | _, _, _, Action _ -> m_active <- n
3781 | _ -> loop (n+1)
3783 loop 0;
3784 m_first_time <- false;
3787 method int name get set =
3788 m_l <-
3789 (name, `int get, 1, Action (
3790 fun u ->
3791 let ondone s =
3792 try set (int_of_string s)
3793 with exn ->
3794 state.text <- Printf.sprintf "bad integer `%s': %s"
3795 s (Printexc.to_string exn)
3797 state.text <- "";
3798 let te = name ^ ": ", "", None, intentry, ondone in
3799 state.mode <- Textentry (te, leave m_prev_mode);
3801 )) :: m_l
3803 method int_with_suffix name get set =
3804 m_l <-
3805 (name, `intws get, 1, Action (
3806 fun u ->
3807 let ondone s =
3808 try set (int_of_string_with_suffix s)
3809 with exn ->
3810 state.text <- Printf.sprintf "bad integer `%s': %s"
3811 s (Printexc.to_string exn)
3813 state.text <- "";
3814 let te =
3815 name ^ ": ", "", None, intentry_with_suffix, ondone
3817 state.mode <- Textentry (te, leave m_prev_mode);
3819 )) :: m_l
3821 method bool ?(offset=1) ?(btos=btos) name get set =
3822 m_l <-
3823 (name, `bool (btos, get), offset, Action (
3824 fun u ->
3825 let v = get () in
3826 set (not v);
3828 )) :: m_l
3830 method color name get set =
3831 m_l <-
3832 (name, `color get, 1, Action (
3833 fun u ->
3834 let invalid = (nan, nan, nan) in
3835 let ondone s =
3836 let c =
3837 try color_of_string s
3838 with exn ->
3839 state.text <- Printf.sprintf "bad color `%s': %s"
3840 s (Printexc.to_string exn);
3841 invalid
3843 if c <> invalid
3844 then set c;
3846 let te = name ^ ": ", "", None, textentry, ondone in
3847 state.text <- color_to_string (get ());
3848 state.mode <- Textentry (te, leave m_prev_mode);
3850 )) :: m_l
3852 method string name get set =
3853 m_l <-
3854 (name, `string get, 1, Action (
3855 fun u ->
3856 let ondone s = set s in
3857 let te = name ^ ": ", "", None, textentry, ondone in
3858 state.mode <- Textentry (te, leave m_prev_mode);
3860 )) :: m_l
3862 method colorspace name get set =
3863 m_l <-
3864 (name, `string get, 1, Action (
3865 fun _ ->
3866 let source =
3867 let vals = [| "rgb"; "bgr"; "gray" |] in
3868 (object
3869 inherit lvsourcebase
3871 initializer
3872 m_active <- int_of_colorspace conf.colorspace;
3873 m_first <- 0;
3875 method getitemcount = Array.length vals
3876 method getitem n = (vals.(n), 0)
3877 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3878 ignore (uioh, first, pan, qsearch);
3879 if not cancel then set active;
3880 None
3881 method hasaction _ = true
3882 end)
3884 state.text <- "";
3885 let modehash = findkeyhash conf "info" in
3886 coe (new listview ~source ~trusted:true ~modehash)
3887 )) :: m_l
3889 method caption s offset =
3890 m_l <- (s, `empty, offset, Noaction) :: m_l
3892 method caption2 s f offset =
3893 m_l <- (s, `string f, offset, Noaction) :: m_l
3895 method getitemcount = Array.length m_a
3897 method getitem n =
3898 let tostr = function
3899 | `int f -> string_of_int (f ())
3900 | `intws f -> string_with_suffix_of_int (f ())
3901 | `string f -> f ()
3902 | `color f -> color_to_string (f ())
3903 | `bool (btos, f) -> btos (f ())
3904 | `empty -> ""
3906 let name, t, offset, _ = m_a.(n) in
3907 ((let s = tostr t in
3908 if String.length s > 0
3909 then Printf.sprintf "%s\t%s" name s
3910 else name),
3911 offset)
3913 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3914 let uiohopt =
3915 if not cancel
3916 then (
3917 m_qsearch <- qsearch;
3918 let uioh =
3919 match m_a.(active) with
3920 | _, _, _, Action f -> f uioh
3921 | _ -> uioh
3923 Some uioh
3925 else None
3927 m_active <- active;
3928 m_first <- first;
3929 m_pan <- pan;
3930 uiohopt
3932 method hasaction n =
3933 match m_a.(n) with
3934 | _, _, _, Action _ -> true
3935 | _ -> false
3936 end)
3938 let rec fillsrc prevmode prevuioh =
3939 let sep () = src#caption "" 0 in
3940 let colorp name get set =
3941 src#string name
3942 (fun () -> color_to_string (get ()))
3943 (fun v ->
3945 let c = color_of_string v in
3946 set c
3947 with exn ->
3948 state.text <- Printf.sprintf "bad color `%s': %s"
3949 v (Printexc.to_string exn);
3952 let oldmode = state.mode in
3953 let birdseye = isbirdseye state.mode in
3955 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3957 src#bool "presentation mode"
3958 (fun () -> conf.presentation)
3959 (fun v ->
3960 conf.presentation <- v;
3961 state.anchor <- getanchor ();
3962 represent ());
3964 src#bool "ignore case in searches"
3965 (fun () -> conf.icase)
3966 (fun v -> conf.icase <- v);
3968 src#bool "preload"
3969 (fun () -> conf.preload)
3970 (fun v -> conf.preload <- v);
3972 src#bool "highlight links"
3973 (fun () -> conf.hlinks)
3974 (fun v -> conf.hlinks <- v);
3976 src#bool "under info"
3977 (fun () -> conf.underinfo)
3978 (fun v -> conf.underinfo <- v);
3980 src#bool "persistent bookmarks"
3981 (fun () -> conf.savebmarks)
3982 (fun v -> conf.savebmarks <- v);
3984 src#bool "proportional display"
3985 (fun () -> conf.proportional)
3986 (fun v -> reqlayout conf.angle v);
3988 src#bool "trim margins"
3989 (fun () -> conf.trimmargins)
3990 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3992 src#bool "persistent location"
3993 (fun () -> conf.jumpback)
3994 (fun v -> conf.jumpback <- v);
3996 sep ();
3997 src#int "inter-page space"
3998 (fun () -> conf.interpagespace)
3999 (fun n ->
4000 conf.interpagespace <- n;
4001 let pageno, py =
4002 match state.layout with
4003 | [] -> 0, 0
4004 | l :: _ ->
4005 l.pageno, l.pagey
4007 state.maxy <- calcheight ();
4008 let y = getpagey pageno in
4009 gotoy (y + py)
4012 src#int "page bias"
4013 (fun () -> conf.pagebias)
4014 (fun v -> conf.pagebias <- v);
4016 src#int "scroll step"
4017 (fun () -> conf.scrollstep)
4018 (fun n -> conf.scrollstep <- n);
4020 src#int "auto scroll step"
4021 (fun () ->
4022 match state.autoscroll with
4023 | Some step -> step
4024 | _ -> conf.autoscrollstep)
4025 (fun n ->
4026 if state.autoscroll <> None
4027 then state.autoscroll <- Some n;
4028 conf.autoscrollstep <- n);
4030 src#int "zoom"
4031 (fun () -> truncate (conf.zoom *. 100.))
4032 (fun v -> setzoom ((float v) /. 100.));
4034 src#int "rotation"
4035 (fun () -> conf.angle)
4036 (fun v -> reqlayout v conf.proportional);
4038 src#int "scroll bar width"
4039 (fun () -> state.scrollw)
4040 (fun v ->
4041 state.scrollw <- v;
4042 conf.scrollbw <- v;
4043 reshape conf.winw conf.winh;
4046 src#int "scroll handle height"
4047 (fun () -> conf.scrollh)
4048 (fun v -> conf.scrollh <- v;);
4050 src#int "thumbnail width"
4051 (fun () -> conf.thumbw)
4052 (fun v ->
4053 conf.thumbw <- min 4096 v;
4054 match oldmode with
4055 | Birdseye beye ->
4056 leavebirdseye beye false;
4057 enterbirdseye ()
4058 | _ -> ()
4061 let mode = state.mode in
4062 src#string "columns"
4063 (fun () ->
4064 match conf.columns with
4065 | Csingle -> "1"
4066 | Cmulti (multi, _) -> multicolumns_to_string multi
4067 | Csplit (count, _) -> "-" ^ string_of_int count
4069 (fun v ->
4070 let n, a, b = multicolumns_of_string v in
4071 setcolumns mode n a b);
4073 sep ();
4074 src#caption "Presentation mode" 0;
4075 src#bool "scrollbar visible"
4076 (fun () -> conf.scrollbarinpm)
4077 (fun v ->
4078 if v != conf.scrollbarinpm
4079 then (
4080 conf.scrollbarinpm <- v;
4081 if conf.presentation
4082 then (
4083 state.scrollw <- if v then conf.scrollbw else 0;
4084 reshape conf.winw conf.winh;
4089 sep ();
4090 src#caption "Pixmap cache" 0;
4091 src#int_with_suffix "size (advisory)"
4092 (fun () -> conf.memlimit)
4093 (fun v -> conf.memlimit <- v);
4095 src#caption2 "used"
4096 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4097 (string_with_suffix_of_int state.memused)
4098 (Hashtbl.length state.tilemap)) 1;
4100 sep ();
4101 src#caption "Layout" 0;
4102 src#caption2 "Dimension"
4103 (fun () ->
4104 Printf.sprintf "%dx%d (virtual %dx%d)"
4105 conf.winw conf.winh
4106 state.w state.maxy)
4108 if conf.debug
4109 then
4110 src#caption2 "Position" (fun () ->
4111 Printf.sprintf "%dx%d" state.x state.y
4113 else
4114 src#caption2 "Visible" (fun () -> describe_location ()) 1
4117 sep ();
4118 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4119 "Save these parameters as global defaults at exit"
4120 (fun () -> conf.bedefault)
4121 (fun v -> conf.bedefault <- v)
4124 sep ();
4125 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4126 src#bool ~offset:0 ~btos "Extended parameters"
4127 (fun () -> !showextended)
4128 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4129 if !showextended
4130 then (
4131 src#bool "checkers"
4132 (fun () -> conf.checkers)
4133 (fun v -> conf.checkers <- v; setcheckers v);
4134 src#bool "update cursor"
4135 (fun () -> conf.updatecurs)
4136 (fun v -> conf.updatecurs <- v);
4137 src#bool "verbose"
4138 (fun () -> conf.verbose)
4139 (fun v -> conf.verbose <- v);
4140 src#bool "invert colors"
4141 (fun () -> conf.invert)
4142 (fun v -> conf.invert <- v);
4143 src#bool "max fit"
4144 (fun () -> conf.maxhfit)
4145 (fun v -> conf.maxhfit <- v);
4146 src#bool "redirect stderr"
4147 (fun () -> conf.redirectstderr)
4148 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4149 src#string "uri launcher"
4150 (fun () -> conf.urilauncher)
4151 (fun v -> conf.urilauncher <- v);
4152 src#string "path launcher"
4153 (fun () -> conf.pathlauncher)
4154 (fun v -> conf.pathlauncher <- v);
4155 src#string "tile size"
4156 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4157 (fun v ->
4159 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4160 conf.tileh <- max 64 w;
4161 conf.tilew <- max 64 h;
4162 flushtiles ();
4163 with exn ->
4164 state.text <- Printf.sprintf "bad tile size `%s': %s"
4165 v (Printexc.to_string exn));
4166 src#int "texture count"
4167 (fun () -> conf.texcount)
4168 (fun v ->
4169 if realloctexts v
4170 then conf.texcount <- v
4171 else showtext '!' " Failed to set texture count please retry later"
4173 src#int "slice height"
4174 (fun () -> conf.sliceheight)
4175 (fun v ->
4176 conf.sliceheight <- v;
4177 wcmd "sliceh %d" conf.sliceheight;
4179 src#int "anti-aliasing level"
4180 (fun () -> conf.aalevel)
4181 (fun v ->
4182 conf.aalevel <- bound v 0 8;
4183 state.anchor <- getanchor ();
4184 opendoc state.path state.password;
4186 src#int "ui font size"
4187 (fun () -> fstate.fontsize)
4188 (fun v -> setfontsize (bound v 5 100));
4189 colorp "background color"
4190 (fun () -> conf.bgcolor)
4191 (fun v -> conf.bgcolor <- v);
4192 src#bool "crop hack"
4193 (fun () -> conf.crophack)
4194 (fun v -> conf.crophack <- v);
4195 src#string "trim fuzz"
4196 (fun () -> irect_to_string conf.trimfuzz)
4197 (fun v ->
4199 conf.trimfuzz <- irect_of_string v;
4200 if conf.trimmargins
4201 then settrim true conf.trimfuzz;
4202 with exn ->
4203 state.text <- Printf.sprintf "bad irect `%s': %s"
4204 v (Printexc.to_string exn)
4206 src#string "throttle"
4207 (fun () ->
4208 match conf.maxwait with
4209 | None -> "show place holder if page is not ready"
4210 | Some time ->
4211 if time = infinity
4212 then "wait for page to fully render"
4213 else
4214 "wait " ^ string_of_float time
4215 ^ " seconds before showing placeholder"
4217 (fun v ->
4219 let f = float_of_string v in
4220 if f <= 0.0
4221 then conf.maxwait <- None
4222 else conf.maxwait <- Some f
4223 with exn ->
4224 state.text <- Printf.sprintf "bad time `%s': %s"
4225 v (Printexc.to_string exn)
4227 src#string "ghyll scroll"
4228 (fun () ->
4229 match conf.ghyllscroll with
4230 | None -> ""
4231 | Some nab -> ghyllscroll_to_string nab
4233 (fun v ->
4235 let gs =
4236 if String.length v = 0
4237 then None
4238 else Some (ghyllscroll_of_string v)
4240 conf.ghyllscroll <- gs
4241 with exn ->
4242 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4243 v (Printexc.to_string exn)
4245 src#string "selection command"
4246 (fun () -> conf.selcmd)
4247 (fun v -> conf.selcmd <- v);
4248 src#colorspace "color space"
4249 (fun () -> colorspace_to_string conf.colorspace)
4250 (fun v ->
4251 conf.colorspace <- colorspace_of_int v;
4252 wcmd "cs %d" v;
4253 load state.layout;
4257 sep ();
4258 src#caption "Document" 0;
4259 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4260 src#caption2 "Pages"
4261 (fun () -> string_of_int state.pagecount) 1;
4262 src#caption2 "Dimensions"
4263 (fun () -> string_of_int (List.length state.pdims)) 1;
4264 if conf.trimmargins
4265 then (
4266 sep ();
4267 src#caption "Trimmed margins" 0;
4268 src#caption2 "Dimensions"
4269 (fun () -> string_of_int (List.length state.pdims)) 1;
4272 src#reset prevmode prevuioh;
4274 fun () ->
4275 state.text <- "";
4276 let prevmode = state.mode
4277 and prevuioh = state.uioh in
4278 fillsrc prevmode prevuioh;
4279 let source = (src :> lvsource) in
4280 let modehash = findkeyhash conf "info" in
4281 state.uioh <- coe (object (self)
4282 inherit listview ~source ~trusted:true ~modehash as super
4283 val mutable m_prevmemused = 0
4284 method infochanged = function
4285 | Memused ->
4286 if m_prevmemused != state.memused
4287 then (
4288 m_prevmemused <- state.memused;
4289 G.postRedisplay "memusedchanged";
4291 | Pdim -> G.postRedisplay "pdimchanged"
4292 | Docinfo -> fillsrc prevmode prevuioh
4294 method key key mask =
4295 if not (Wsi.withctrl mask)
4296 then
4297 match key with
4298 | 0xff51 -> coe (self#updownlevel ~-1)
4299 | 0xff53 -> coe (self#updownlevel 1)
4300 | _ -> super#key key mask
4301 else super#key key mask
4302 end);
4303 G.postRedisplay "info";
4306 let enterhelpmode =
4307 let source =
4308 (object
4309 inherit lvsourcebase
4310 method getitemcount = Array.length state.help
4311 method getitem n =
4312 let s, n, _ = state.help.(n) in
4313 (s, n)
4315 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4316 let optuioh =
4317 if not cancel
4318 then (
4319 m_qsearch <- qsearch;
4320 match state.help.(active) with
4321 | _, _, Action f -> Some (f uioh)
4322 | _ -> Some (uioh)
4324 else None
4326 m_active <- active;
4327 m_first <- first;
4328 m_pan <- pan;
4329 optuioh
4331 method hasaction n =
4332 match state.help.(n) with
4333 | _, _, Action _ -> true
4334 | _ -> false
4336 initializer
4337 m_active <- -1
4338 end)
4339 in fun () ->
4340 let modehash = findkeyhash conf "help" in
4341 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4342 G.postRedisplay "help";
4345 let entermsgsmode =
4346 let msgsource =
4347 let re = Str.regexp "[\r\n]" in
4348 (object
4349 inherit lvsourcebase
4350 val mutable m_items = [||]
4352 method getitemcount = 1 + Array.length m_items
4354 method getitem n =
4355 if n = 0
4356 then "[Clear]", 0
4357 else m_items.(n-1), 0
4359 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4360 ignore uioh;
4361 if not cancel
4362 then (
4363 if active = 0
4364 then Buffer.clear state.errmsgs;
4365 m_qsearch <- qsearch;
4367 m_active <- active;
4368 m_first <- first;
4369 m_pan <- pan;
4370 None
4372 method hasaction n =
4373 n = 0
4375 method reset =
4376 state.newerrmsgs <- false;
4377 let l = Str.split re (Buffer.contents state.errmsgs) in
4378 m_items <- Array.of_list l
4380 initializer
4381 m_active <- 0
4382 end)
4383 in fun () ->
4384 state.text <- "";
4385 msgsource#reset;
4386 let source = (msgsource :> lvsource) in
4387 let modehash = findkeyhash conf "listview" in
4388 state.uioh <- coe (object
4389 inherit listview ~source ~trusted:false ~modehash as super
4390 method display =
4391 if state.newerrmsgs
4392 then msgsource#reset;
4393 super#display
4394 end);
4395 G.postRedisplay "msgs";
4398 let quickbookmark ?title () =
4399 match state.layout with
4400 | [] -> ()
4401 | l :: _ ->
4402 let title =
4403 match title with
4404 | None ->
4405 let sec = Unix.gettimeofday () in
4406 let tm = Unix.localtime sec in
4407 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4408 (l.pageno+1)
4409 tm.Unix.tm_mday
4410 tm.Unix.tm_mon
4411 (tm.Unix.tm_year + 1900)
4412 tm.Unix.tm_hour
4413 tm.Unix.tm_min
4414 | Some title -> title
4416 state.bookmarks <-
4417 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4418 :: state.bookmarks
4421 let doreshape w h =
4422 state.fullscreen <- None;
4423 Wsi.reshape w h;
4426 let setautoscrollspeed step goingdown =
4427 let incr = max 1 ((abs step) / 2) in
4428 let incr = if goingdown then incr else -incr in
4429 let astep = step + incr in
4430 state.autoscroll <- Some astep;
4433 let gotounder = function
4434 | Ulinkgoto (pageno, top) ->
4435 if pageno >= 0
4436 then (
4437 addnav ();
4438 gotopage1 pageno top;
4441 | Ulinkuri s ->
4442 gotouri s
4444 | Uremote (filename, pageno) ->
4445 let path =
4446 if Sys.file_exists filename
4447 then filename
4448 else
4449 let dir = Filename.dirname state.path in
4450 let path = Filename.concat dir filename in
4451 if Sys.file_exists path
4452 then path
4453 else ""
4455 if String.length path > 0
4456 then (
4457 let anchor = getanchor () in
4458 let ranchor = state.path, state.password, anchor in
4459 state.anchor <- (pageno, 0.0);
4460 state.ranchors <- ranchor :: state.ranchors;
4461 opendoc path "";
4463 else showtext '!' ("Could not find " ^ filename)
4465 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4468 let canpan () =
4469 match conf.columns with
4470 | Csplit _ -> true
4471 | _ -> conf.zoom > 1.0
4474 let viewkeyboard key mask =
4475 let enttext te =
4476 let mode = state.mode in
4477 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4478 state.text <- "";
4479 enttext ();
4480 G.postRedisplay "view:enttext"
4482 let ctrl = Wsi.withctrl mask in
4483 match key with
4484 | 81 -> (* Q *)
4485 exit 0
4487 | 0xff63 -> (* insert *)
4488 if conf.angle mod 360 = 0
4489 then (
4490 state.mode <- LinkNav (Ltgendir 0);
4491 gotoy state.y;
4493 else showtext '!' "Keyboard link naviagtion does not work under rotation"
4495 | 0xff1b | 113 -> (* escape / q *)
4496 begin match state.mstate with
4497 | Mzoomrect _ ->
4498 state.mstate <- Mnone;
4499 Wsi.setcursor Wsi.CURSOR_INHERIT;
4500 G.postRedisplay "kill zoom rect";
4501 | _ ->
4502 match state.ranchors with
4503 | [] -> raise Quit
4504 | (path, password, anchor) :: rest ->
4505 state.ranchors <- rest;
4506 state.anchor <- anchor;
4507 opendoc path password
4508 end;
4510 | 0xff08 -> (* backspace *)
4511 let y = getnav ~-1 in
4512 gotoy_and_clear_text y
4514 | 111 -> (* o *)
4515 enteroutlinemode ()
4517 | 117 -> (* u *)
4518 state.rects <- [];
4519 state.text <- "";
4520 G.postRedisplay "dehighlight";
4522 | 47 | 63 -> (* / ? *)
4523 let ondone isforw s =
4524 cbput state.hists.pat s;
4525 state.searchpattern <- s;
4526 search s isforw
4528 let s = String.create 1 in
4529 s.[0] <- Char.chr key;
4530 enttext (s, "", Some (onhist state.hists.pat),
4531 textentry, ondone (key = 47))
4533 | 43 | 0xffab when ctrl -> (* ctrl-+ *)
4534 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4535 setzoom (conf.zoom +. incr)
4537 | 43 | 0xffab -> (* + *)
4538 let ondone s =
4539 let n =
4540 try int_of_string s with exc ->
4541 state.text <- Printf.sprintf "bad integer `%s': %s"
4542 s (Printexc.to_string exc);
4543 max_int
4545 if n != max_int
4546 then (
4547 conf.pagebias <- n;
4548 state.text <- "page bias is now " ^ string_of_int n;
4551 enttext ("page bias: ", "", None, intentry, ondone)
4553 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4554 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4555 setzoom (max 0.01 (conf.zoom -. decr))
4557 | 45 | 0xffad -> (* - *)
4558 let ondone msg = state.text <- msg in
4559 enttext (
4560 "option [acfhilpstvxACPRSZTIS]: ", "", None,
4561 optentry state.mode, ondone
4564 | 48 when ctrl -> (* ctrl-0 *)
4565 setzoom 1.0
4567 | 49 when ctrl -> (* 1 *)
4568 let zoom = zoomforh conf.winw conf.winh state.scrollw in
4569 if zoom < 1.0
4570 then setzoom zoom
4572 | 0xffc6 -> (* f9 *)
4573 togglebirdseye ()
4575 | 57 when ctrl -> (* ctrl-9 *)
4576 togglebirdseye ()
4578 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4579 when not ctrl -> (* 0..9 *)
4580 let ondone s =
4581 let n =
4582 try int_of_string s with exc ->
4583 state.text <- Printf.sprintf "bad integer `%s': %s"
4584 s (Printexc.to_string exc);
4587 if n >= 0
4588 then (
4589 addnav ();
4590 cbput state.hists.pag (string_of_int n);
4591 gotopage1 (n + conf.pagebias - 1) 0;
4594 let pageentry text key =
4595 match Char.unsafe_chr key with
4596 | 'g' -> TEdone text
4597 | _ -> intentry text key
4599 let text = "x" in text.[0] <- Char.chr key;
4600 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
4602 | 98 -> (* b *)
4603 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4604 reshape conf.winw conf.winh;
4606 | 108 -> (* l *)
4607 conf.hlinks <- not conf.hlinks;
4608 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4609 G.postRedisplay "toggle highlightlinks";
4611 | 70 -> (* F *)
4612 state.glinks <- true;
4613 let ondone s =
4614 let n =
4615 try int_of_string s with exc ->
4616 state.text <- Printf.sprintf "bad integer `%s': %s"
4617 s (Printexc.to_string exc);
4620 if n >= 0
4621 then (
4622 let rec loop n = function
4623 | [] -> ()
4624 | l :: rest ->
4625 match getopaque l.pageno with
4626 | None -> loop n rest
4627 | Some opaque ->
4628 let m = getlinkcount opaque in
4629 if n < m
4630 then (
4631 let under = getlink opaque n in
4632 addnav ();
4633 gotounder under;
4635 else loop (n-m) rest
4637 loop n state.layout;
4640 let onkey text key =
4641 match Char.unsafe_chr key with
4642 | 'g' -> TEdone text
4643 | _ -> intentry text key
4645 let mode = state.mode in
4646 state.mode <- Textentry (
4647 (":", "", Some (onhist state.hists.pag), onkey, ondone),
4648 fun _ ->
4649 state.glinks <- false;
4650 state.mode <- mode
4652 state.text <- "";
4653 G.postRedisplay "view:enttext"
4655 | 97 -> (* a *)
4656 begin match state.autoscroll with
4657 | Some step ->
4658 conf.autoscrollstep <- step;
4659 state.autoscroll <- None
4660 | None ->
4661 if conf.autoscrollstep = 0
4662 then state.autoscroll <- Some 1
4663 else state.autoscroll <- Some conf.autoscrollstep
4666 | 112 when ctrl -> (* ctrl-p *)
4667 launchpath ()
4669 | 80 -> (* P *)
4670 conf.presentation <- not conf.presentation;
4671 if conf.presentation
4672 then (
4673 if not conf.scrollbarinpm
4674 then state.scrollw <- 0;
4676 else
4677 state.scrollw <- conf.scrollbw;
4679 showtext ' ' ("presentation mode " ^
4680 if conf.presentation then "on" else "off");
4681 state.anchor <- getanchor ();
4682 represent ()
4684 | 102 -> (* f *)
4685 begin match state.fullscreen with
4686 | None ->
4687 state.fullscreen <- Some (conf.winw, conf.winh);
4688 Wsi.fullscreen ()
4689 | Some (w, h) ->
4690 state.fullscreen <- None;
4691 doreshape w h
4694 | 103 -> (* g *)
4695 gotoy_and_clear_text 0
4697 | 71 -> (* G *)
4698 gotopage1 (state.pagecount - 1) 0
4700 | 112 | 78 -> (* p|N *)
4701 search state.searchpattern false
4703 | 110 | 0xffc0 -> (* n|F3 *)
4704 search state.searchpattern true
4706 | 116 -> (* t *)
4707 begin match state.layout with
4708 | [] -> ()
4709 | l :: _ ->
4710 gotoy_and_clear_text (getpagey l.pageno)
4713 | 32 -> (* ' ' *)
4714 begin match List.rev state.layout with
4715 | [] -> ()
4716 | l :: _ ->
4717 let pageno = min (l.pageno+1) (state.pagecount-1) in
4718 gotoy_and_clear_text (getpagey pageno)
4721 | 0xff9f | 0xffff -> (* delete *)
4722 begin match state.layout with
4723 | [] -> ()
4724 | l :: _ ->
4725 let pageno = max 0 (l.pageno-1) in
4726 gotoy_and_clear_text (getpagey pageno)
4729 | 61 -> (* = *)
4730 showtext ' ' (describe_location ());
4732 | 119 -> (* w *)
4733 begin match state.layout with
4734 | [] -> ()
4735 | l :: _ ->
4736 doreshape (l.pagew + state.scrollw) l.pageh;
4737 G.postRedisplay "w"
4740 | 39 -> (* ' *)
4741 enterbookmarkmode ()
4743 | 104 | 0xffbe -> (* h|F1 *)
4744 enterhelpmode ()
4746 | 105 -> (* i *)
4747 enterinfomode ()
4749 | 101 when conf.redirectstderr -> (* e *)
4750 entermsgsmode ()
4752 | 109 -> (* m *)
4753 let ondone s =
4754 match state.layout with
4755 | l :: _ ->
4756 state.bookmarks <-
4757 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4758 :: state.bookmarks
4759 | _ -> ()
4761 enttext ("bookmark: ", "", None, textentry, ondone)
4763 | 126 -> (* ~ *)
4764 quickbookmark ();
4765 showtext ' ' "Quick bookmark added";
4767 | 122 -> (* z *)
4768 begin match state.layout with
4769 | l :: _ ->
4770 let rect = getpdimrect l.pagedimno in
4771 let w, h =
4772 if conf.crophack
4773 then
4774 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4775 truncate (1.2 *. (rect.(3) -. rect.(0))))
4776 else
4777 (truncate (rect.(1) -. rect.(0)),
4778 truncate (rect.(3) -. rect.(0)))
4780 let w = truncate ((float w)*.conf.zoom)
4781 and h = truncate ((float h)*.conf.zoom) in
4782 if w != 0 && h != 0
4783 then (
4784 state.anchor <- getanchor ();
4785 doreshape (w + state.scrollw) (h + conf.interpagespace)
4787 G.postRedisplay "z";
4789 | [] -> ()
4792 | 50 when ctrl -> (* ctrl-2 *)
4793 let maxw = getmaxw () in
4794 if maxw > 0.0
4795 then setzoom (maxw /. float conf.winw)
4797 | 60 | 62 -> (* < > *)
4798 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
4800 | 91 | 93 -> (* [ ] *)
4801 conf.colorscale <-
4802 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
4804 G.postRedisplay "brightness";
4806 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
4807 setzoom state.prevzoom
4809 | 107 | 0xff52 -> (* k up *)
4810 begin match state.autoscroll with
4811 | None ->
4812 begin match state.mode with
4813 | Birdseye beye -> upbirdseye 1 beye
4814 | _ ->
4815 if ctrl
4816 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
4817 else gotoy_and_clear_text (clamp (-conf.scrollstep))
4819 | Some n ->
4820 setautoscrollspeed n false
4823 | 106 | 0xff54 -> (* j down *)
4824 begin match state.autoscroll with
4825 | None ->
4826 begin match state.mode with
4827 | Birdseye beye -> downbirdseye 1 beye
4828 | _ ->
4829 if ctrl
4830 then gotoy_and_clear_text (clamp (conf.winh/2))
4831 else gotoy_and_clear_text (clamp conf.scrollstep)
4833 | Some n ->
4834 setautoscrollspeed n true
4837 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
4838 if canpan ()
4839 then
4840 let dx =
4841 if ctrl
4842 then conf.winw / 2
4843 else 10
4845 let dx = if key = 0xff51 then dx else -dx in
4846 state.x <- state.x + dx;
4847 gotoy_and_clear_text state.y
4848 else (
4849 state.text <- "";
4850 G.postRedisplay "lef/right"
4853 | 0xff55 -> (* prior *)
4854 let y =
4855 if ctrl
4856 then
4857 match state.layout with
4858 | [] -> state.y
4859 | l :: _ -> state.y - l.pagey
4860 else
4861 clamp (-conf.winh)
4863 gotoghyll y
4865 | 0xff56 -> (* next *)
4866 let y =
4867 if ctrl
4868 then
4869 match List.rev state.layout with
4870 | [] -> state.y
4871 | l :: _ -> getpagey l.pageno
4872 else
4873 clamp conf.winh
4875 gotoghyll y
4877 | 0xff50 -> gotoghyll 0
4878 | 0xff57 -> gotoghyll (clamp state.maxy)
4879 | 0xff53 when Wsi.withalt mask ->
4880 gotoghyll (getnav ~-1)
4881 | 0xff51 when Wsi.withalt mask ->
4882 gotoghyll (getnav 1)
4884 | 114 -> (* r *)
4885 state.anchor <- getanchor ();
4886 opendoc state.path state.password
4888 | 118 when conf.debug -> (* v *)
4889 state.rects <- [];
4890 List.iter (fun l ->
4891 match getopaque l.pageno with
4892 | None -> ()
4893 | Some opaque ->
4894 let x0, y0, x1, y1 = pagebbox opaque in
4895 let a,b = float x0, float y0 in
4896 let c,d = float x1, float y0 in
4897 let e,f = float x1, float y1 in
4898 let h,j = float x0, float y1 in
4899 let rect = (a,b,c,d,e,f,h,j) in
4900 debugrect rect;
4901 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4902 ) state.layout;
4903 G.postRedisplay "v";
4905 | _ ->
4906 vlog "huh? %s" (Wsi.keyname key)
4909 let linknavkeyboard key mask linknav =
4910 let getpage pageno =
4911 let rec loop = function
4912 | [] -> None
4913 | l :: _ when l.pageno = pageno -> Some l
4914 | _ :: rest -> loop rest
4915 in loop state.layout
4917 let doexact (pageno, n) =
4918 match getopaque pageno, getpage pageno with
4919 | Some opaque, Some l ->
4920 if key = 0xff0d
4921 then
4922 let under = getlink opaque n in
4923 G.postRedisplay "link gotounder";
4924 gotounder under;
4925 state.mode <- View;
4926 else
4927 let opt, dir =
4928 match key with
4929 | 0xff50 -> (* home *)
4930 Some (findlink opaque LDfirst), -1
4932 | 0xff57 -> (* end *)
4933 Some (findlink opaque LDlast), 1
4935 | 0xff51 -> (* left *)
4936 Some (findlink opaque (LDleft n)), -1
4938 | 0xff53 -> (* right *)
4939 Some (findlink opaque (LDright n)), 1
4941 | 0xff52 -> (* up *)
4942 Some (findlink opaque (LDup n)), -1
4944 | 0xff54 -> (* down *)
4945 Some (findlink opaque (LDdown n)), 1
4947 | _ -> None, 0
4949 let pwl l dir =
4950 begin match findpwl l.pageno dir with
4951 | Pwlnotfound -> ()
4952 | Pwl pageno ->
4953 let notfound dir =
4954 state.mode <- LinkNav (Ltgendir dir);
4955 let y, h = getpageyh pageno in
4956 let y =
4957 if dir < 0
4958 then y + h - conf.winh
4959 else y
4961 gotoy y
4963 begin match getopaque pageno, getpage pageno with
4964 | Some opaque, Some _ ->
4965 let link =
4966 let ld = if dir > 0 then LDfirst else LDlast in
4967 findlink opaque ld
4969 begin match link with
4970 | Lfound m ->
4971 showlinktype (getlink opaque m);
4972 state.mode <- LinkNav (Ltexact (pageno, m));
4973 G.postRedisplay "linknav jpage";
4974 | _ -> notfound dir
4975 end;
4976 | _ -> notfound dir
4977 end;
4978 end;
4980 begin match opt with
4981 | Some Lnotfound -> pwl l dir;
4982 | Some (Lfound m) ->
4983 if m = n
4984 then pwl l dir
4985 else (
4986 let _, y0, _, y1 = getlinkrect opaque m in
4987 if y0 < l.pagey
4988 then gotopage1 l.pageno y0
4989 else (
4990 let d = fstate.fontsize + 1 in
4991 if y1 - l.pagey > l.pagevh - d
4992 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh + d)
4993 else G.postRedisplay "linknav";
4995 showlinktype (getlink opaque m);
4996 state.mode <- LinkNav (Ltexact (l.pageno, m));
4999 | None -> viewkeyboard key mask
5000 end;
5001 | _ -> viewkeyboard key mask
5003 if key = 0xff63
5004 then (
5005 state.mode <- View;
5006 G.postRedisplay "leave linknav"
5008 else
5009 match linknav with
5010 | Ltgendir _ -> viewkeyboard key mask
5011 | Ltexact exact -> doexact exact
5014 let keyboard key mask =
5015 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5016 then wcmd "interrupt"
5017 else state.uioh <- state.uioh#key key mask
5020 let birdseyekeyboard key mask
5021 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5022 let incr =
5023 match conf.columns with
5024 | Csingle -> 1
5025 | Cmulti ((c, _, _), _) -> c
5026 | Csplit _ -> failwith "bird's eye split mode"
5028 match key with
5029 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5030 let y, h = getpageyh pageno in
5031 let top = (conf.winh - h) / 2 in
5032 gotoy (max 0 (y - top))
5033 | 0xff0d -> leavebirdseye beye false
5034 | 0xff1b -> leavebirdseye beye true (* escape *)
5035 | 0xff52 -> upbirdseye incr beye (* prior *)
5036 | 0xff54 -> downbirdseye incr beye (* next *)
5037 | 0xff51 -> upbirdseye 1 beye (* up *)
5038 | 0xff53 -> downbirdseye 1 beye (* down *)
5040 | 0xff55 ->
5041 begin match state.layout with
5042 | l :: _ ->
5043 if l.pagey != 0
5044 then (
5045 state.mode <- Birdseye (
5046 oconf, leftx, l.pageno, hooverpageno, anchor
5048 gotopage1 l.pageno 0;
5050 else (
5051 let layout = layout (state.y-conf.winh) conf.winh in
5052 match layout with
5053 | [] -> gotoy (clamp (-conf.winh))
5054 | l :: _ ->
5055 state.mode <- Birdseye (
5056 oconf, leftx, l.pageno, hooverpageno, anchor
5058 gotopage1 l.pageno 0
5061 | [] -> gotoy (clamp (-conf.winh))
5062 end;
5064 | 0xff56 ->
5065 begin match List.rev state.layout with
5066 | l :: _ ->
5067 let layout = layout (state.y + conf.winh) conf.winh in
5068 begin match layout with
5069 | [] ->
5070 let incr = l.pageh - l.pagevh in
5071 if incr = 0
5072 then (
5073 state.mode <-
5074 Birdseye (
5075 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5077 G.postRedisplay "birdseye pagedown";
5079 else gotoy (clamp (incr + conf.interpagespace*2));
5081 | l :: _ ->
5082 state.mode <-
5083 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5084 gotopage1 l.pageno 0;
5087 | [] -> gotoy (clamp conf.winh)
5088 end;
5090 | 0xff50 ->
5091 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5092 gotopage1 0 0
5094 | 0xff57 ->
5095 let pageno = state.pagecount - 1 in
5096 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5097 if not (pagevisible state.layout pageno)
5098 then
5099 let h =
5100 match List.rev state.pdims with
5101 | [] -> conf.winh
5102 | (_, _, h, _) :: _ -> h
5104 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
5105 else G.postRedisplay "birdseye end";
5106 | _ -> viewkeyboard key mask
5109 let drawpage l linkindexbase =
5110 let color =
5111 match state.mode with
5112 | Textentry _ -> scalecolor 0.4
5113 | LinkNav _
5114 | View -> scalecolor 1.0
5115 | Birdseye (_, _, pageno, hooverpageno, _) ->
5116 if l.pageno = hooverpageno
5117 then scalecolor 0.9
5118 else (
5119 if l.pageno = pageno
5120 then scalecolor 1.0
5121 else scalecolor 0.8
5124 drawtiles l color;
5125 begin match getopaque l.pageno with
5126 | Some opaque ->
5127 if tileready l l.pagex l.pagey
5128 then
5129 let x = l.pagedispx - l.pagex
5130 and y = l.pagedispy - l.pagey in
5131 let hlmask = (if conf.hlinks then 1 else 0)
5132 + (if state.glinks && not (isbirdseye state.mode) then 2 else 0)
5134 postprocess opaque hlmask x y linkindexbase;
5135 else 0
5137 | _ -> 0
5138 end;
5141 let scrollindicator () =
5142 let sbw, ph, sh = state.uioh#scrollph in
5143 let sbh, pw, sw = state.uioh#scrollpw in
5145 GlDraw.color (0.64, 0.64, 0.64);
5146 GlDraw.rect
5147 (float (conf.winw - sbw), 0.)
5148 (float conf.winw, float conf.winh)
5150 GlDraw.rect
5151 (0., float (conf.winh - sbh))
5152 (float (conf.winw - state.scrollw - 1), float conf.winh)
5154 GlDraw.color (0.0, 0.0, 0.0);
5156 GlDraw.rect
5157 (float (conf.winw - sbw), ph)
5158 (float conf.winw, ph +. sh)
5160 GlDraw.rect
5161 (pw, float (conf.winh - sbh))
5162 (pw +. sw, float conf.winh)
5166 let showsel () =
5167 match state.mstate with
5168 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5171 | Msel ((x0, y0), (x1, y1)) ->
5172 let rec loop = function
5173 | l :: ls ->
5174 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5175 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5176 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5177 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5178 then
5179 match getopaque l.pageno with
5180 | Some opaque ->
5181 let x0, y0 = pagetranslatepoint l x0 y0 in
5182 let x1, y1 = pagetranslatepoint l x1 y1 in
5183 seltext opaque (x0, y0, x1, y1);
5184 | _ -> ()
5185 else loop ls
5186 | [] -> ()
5188 loop state.layout
5191 let showrects rects =
5192 Gl.enable `blend;
5193 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5194 GlDraw.polygon_mode `both `fill;
5195 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5196 List.iter
5197 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5198 List.iter (fun l ->
5199 if l.pageno = pageno
5200 then (
5201 let dx = float (l.pagedispx - l.pagex) in
5202 let dy = float (l.pagedispy - l.pagey) in
5203 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5204 GlDraw.begins `quads;
5206 GlDraw.vertex2 (x0+.dx, y0+.dy);
5207 GlDraw.vertex2 (x1+.dx, y1+.dy);
5208 GlDraw.vertex2 (x2+.dx, y2+.dy);
5209 GlDraw.vertex2 (x3+.dx, y3+.dy);
5211 GlDraw.ends ();
5213 ) state.layout
5214 ) rects
5216 Gl.disable `blend;
5219 let display () =
5220 GlClear.color (scalecolor2 conf.bgcolor);
5221 GlClear.clear [`color];
5222 let rec loop linkindexbase = function
5223 | l :: rest ->
5224 let linkindexbase = linkindexbase + drawpage l linkindexbase in
5225 loop linkindexbase rest
5226 | [] -> ()
5228 loop 0 state.layout;
5229 let rects =
5230 match state.mode with
5231 | LinkNav (Ltexact (pageno, linkno)) ->
5232 begin match getopaque pageno with
5233 | Some opaque ->
5234 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5235 (pageno, 5, (
5236 float x0, float y0,
5237 float x1, float y0,
5238 float x1, float y1,
5239 float x0, float y1)
5240 ) :: state.rects
5241 | None -> state.rects
5243 | _ -> state.rects
5245 showrects rects;
5246 showsel ();
5247 state.uioh#display;
5248 begin match state.mstate with
5249 | Mzoomrect ((x0, y0), (x1, y1)) ->
5250 Gl.enable `blend;
5251 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5252 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5253 GlDraw.rect (float x0, float y0)
5254 (float x1, float y1);
5255 Gl.disable `blend;
5256 | _ -> ()
5257 end;
5258 enttext ();
5259 scrollindicator ();
5260 Wsi.swapb ();
5263 let zoomrect x y x1 y1 =
5264 let x0 = min x x1
5265 and x1 = max x x1
5266 and y0 = min y y1 in
5267 gotoy (state.y + y0);
5268 state.anchor <- getanchor ();
5269 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5270 let margin =
5271 if state.w < conf.winw - state.scrollw
5272 then (conf.winw - state.scrollw - state.w) / 2
5273 else 0
5275 state.x <- (state.x + margin) - x0;
5276 setzoom zoom;
5277 Wsi.setcursor Wsi.CURSOR_INHERIT;
5278 state.mstate <- Mnone;
5281 let scrollx x =
5282 let winw = conf.winw - state.scrollw - 1 in
5283 let s = float x /. float winw in
5284 let destx = truncate (float (state.w + winw) *. s) in
5285 state.x <- winw - destx;
5286 gotoy_and_clear_text state.y;
5287 state.mstate <- Mscrollx;
5290 let scrolly y =
5291 let s = float y /. float conf.winh in
5292 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5293 gotoy_and_clear_text desty;
5294 state.mstate <- Mscrolly;
5297 let viewmouse button down x y mask =
5298 match button with
5299 | n when (n == 4 || n == 5) && not down ->
5300 if Wsi.withctrl mask
5301 then (
5302 match state.mstate with
5303 | Mzoom (oldn, i) ->
5304 if oldn = n
5305 then (
5306 if i = 2
5307 then
5308 let incr =
5309 match n with
5310 | 5 ->
5311 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5312 | _ ->
5313 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5315 let zoom = conf.zoom -. incr in
5316 setzoom zoom;
5317 state.mstate <- Mzoom (n, 0);
5318 else
5319 state.mstate <- Mzoom (n, i+1);
5321 else state.mstate <- Mzoom (n, 0)
5323 | _ -> state.mstate <- Mzoom (n, 0)
5325 else (
5326 match state.autoscroll with
5327 | Some step -> setautoscrollspeed step (n=4)
5328 | None ->
5329 let incr =
5330 if n = 4
5331 then -conf.scrollstep
5332 else conf.scrollstep
5334 let incr = incr * 2 in
5335 let y = clamp incr in
5336 gotoy_and_clear_text y
5339 | 1 when Wsi.withctrl mask ->
5340 if down
5341 then (
5342 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5343 state.mstate <- Mpan (x, y)
5345 else
5346 state.mstate <- Mnone
5348 | 3 ->
5349 if down
5350 then (
5351 Wsi.setcursor Wsi.CURSOR_CYCLE;
5352 let p = (x, y) in
5353 state.mstate <- Mzoomrect (p, p)
5355 else (
5356 match state.mstate with
5357 | Mzoomrect ((x0, y0), _) ->
5358 if abs (x-x0) > 10 && abs (y - y0) > 10
5359 then zoomrect x0 y0 x y
5360 else (
5361 state.mstate <- Mnone;
5362 Wsi.setcursor Wsi.CURSOR_INHERIT;
5363 G.postRedisplay "kill accidental zoom rect";
5365 | _ ->
5366 Wsi.setcursor Wsi.CURSOR_INHERIT;
5367 state.mstate <- Mnone
5370 | 1 when x > conf.winw - state.scrollw ->
5371 if down
5372 then
5373 let _, position, sh = state.uioh#scrollph in
5374 if y > truncate position && y < truncate (position +. sh)
5375 then state.mstate <- Mscrolly
5376 else scrolly y
5377 else
5378 state.mstate <- Mnone
5380 | 1 when y > conf.winh - state.hscrollh ->
5381 if down
5382 then
5383 let _, position, sw = state.uioh#scrollpw in
5384 if x > truncate position && x < truncate (position +. sw)
5385 then state.mstate <- Mscrollx
5386 else scrollx x
5387 else
5388 state.mstate <- Mnone
5390 | 1 ->
5391 let dest = if down then getunder x y else Unone in
5392 begin match dest with
5393 | Ulinkgoto _
5394 | Ulinkuri _
5395 | Uremote _
5396 | Uunexpected _ | Ulaunch _ | Unamed _ ->
5397 gotounder dest
5399 | Unone when down ->
5400 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5401 state.mstate <- Mpan (x, y);
5403 | Unone | Utext _ ->
5404 if down
5405 then (
5406 if conf.angle mod 360 = 0
5407 then (
5408 state.mstate <- Msel ((x, y), (x, y));
5409 G.postRedisplay "mouse select";
5412 else (
5413 match state.mstate with
5414 | Mnone -> ()
5416 | Mzoom _ | Mscrollx | Mscrolly ->
5417 state.mstate <- Mnone
5419 | Mzoomrect ((x0, y0), _) ->
5420 zoomrect x0 y0 x y
5422 | Mpan _ ->
5423 Wsi.setcursor Wsi.CURSOR_INHERIT;
5424 state.mstate <- Mnone
5426 | Msel ((_, y0), (_, y1)) ->
5427 let rec loop = function
5428 | [] -> ()
5429 | l :: rest ->
5430 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5431 || ((y1 >= l.pagedispy
5432 && y1 <= (l.pagedispy + l.pagevh)))
5433 then
5434 match getopaque l.pageno with
5435 | Some opaque ->
5436 copysel conf.selcmd opaque;
5437 G.postRedisplay "copysel"
5438 | _ -> ()
5439 else loop rest
5441 loop state.layout;
5442 Wsi.setcursor Wsi.CURSOR_INHERIT;
5443 state.mstate <- Mnone;
5447 | _ -> ()
5450 let birdseyemouse button down x y mask
5451 (conf, leftx, _, hooverpageno, anchor) =
5452 match button with
5453 | 1 when down ->
5454 let rec loop = function
5455 | [] -> ()
5456 | l :: rest ->
5457 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5458 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5459 then (
5460 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5462 else loop rest
5464 loop state.layout
5465 | 3 -> ()
5466 | _ -> viewmouse button down x y mask
5469 let mouse button down x y mask =
5470 state.uioh <- state.uioh#button button down x y mask;
5473 let motion ~x ~y =
5474 state.uioh <- state.uioh#motion x y
5477 let pmotion ~x ~y =
5478 state.uioh <- state.uioh#pmotion x y;
5481 let uioh = object
5482 method display = ()
5484 method key key mask =
5485 begin match state.mode with
5486 | Textentry textentry -> textentrykeyboard key mask textentry
5487 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5488 | View -> viewkeyboard key mask
5489 | LinkNav linknav -> linknavkeyboard key mask linknav
5490 end;
5491 state.uioh
5493 method button button bstate x y mask =
5494 begin match state.mode with
5495 | LinkNav _
5496 | View -> viewmouse button bstate x y mask
5497 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5498 | Textentry _ -> ()
5499 end;
5500 state.uioh
5502 method motion x y =
5503 begin match state.mode with
5504 | Textentry _ -> ()
5505 | View | Birdseye _ | LinkNav _ ->
5506 match state.mstate with
5507 | Mzoom _ | Mnone -> ()
5509 | Mpan (x0, y0) ->
5510 let dx = x - x0
5511 and dy = y0 - y in
5512 state.mstate <- Mpan (x, y);
5513 if canpan ()
5514 then state.x <- state.x + dx;
5515 let y = clamp dy in
5516 gotoy_and_clear_text y
5518 | Msel (a, _) ->
5519 state.mstate <- Msel (a, (x, y));
5520 G.postRedisplay "motion select";
5522 | Mscrolly ->
5523 let y = min conf.winh (max 0 y) in
5524 scrolly y
5526 | Mscrollx ->
5527 let x = min conf.winw (max 0 x) in
5528 scrollx x
5530 | Mzoomrect (p0, _) ->
5531 state.mstate <- Mzoomrect (p0, (x, y));
5532 G.postRedisplay "motion zoomrect";
5533 end;
5534 state.uioh
5536 method pmotion x y =
5537 begin match state.mode with
5538 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5539 let rec loop = function
5540 | [] ->
5541 if hooverpageno != -1
5542 then (
5543 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5544 G.postRedisplay "pmotion birdseye no hoover";
5546 | l :: rest ->
5547 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5548 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5549 then (
5550 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5551 G.postRedisplay "pmotion birdseye hoover";
5553 else loop rest
5555 loop state.layout
5557 | Textentry _ -> ()
5559 | LinkNav _
5560 | View ->
5561 match state.mstate with
5562 | Mnone -> updateunder x y
5563 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5565 end;
5566 state.uioh
5568 method infochanged _ = ()
5570 method scrollph =
5571 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5572 let p, h = scrollph state.y maxy in
5573 state.scrollw, p, h
5575 method scrollpw =
5576 let winw = conf.winw - state.scrollw - 1 in
5577 let fwinw = float winw in
5578 let sw =
5579 let sw = fwinw /. float state.w in
5580 let sw = fwinw *. sw in
5581 max sw (float conf.scrollh)
5583 let position, sw =
5584 let f = state.w+winw in
5585 let r = float (winw-state.x) /. float f in
5586 let p = fwinw *. r in
5587 p-.sw/.2., sw
5589 let sw =
5590 if position +. sw > fwinw
5591 then fwinw -. position
5592 else sw
5594 state.hscrollh, position, sw
5596 method modehash =
5597 let modename =
5598 match state.mode with
5599 | LinkNav _ -> "links"
5600 | Textentry _ -> "textentry"
5601 | Birdseye _ -> "birdseye"
5602 | View -> "global"
5604 findkeyhash conf modename
5605 end;;
5607 module Config =
5608 struct
5609 open Parser
5611 let fontpath = ref "";;
5613 module KeyMap =
5614 Map.Make (struct type t = (int * int) let compare = compare end);;
5616 let unent s =
5617 let l = String.length s in
5618 let b = Buffer.create l in
5619 unent b s 0 l;
5620 Buffer.contents b;
5623 let home =
5624 try Sys.getenv "HOME"
5625 with exn ->
5626 prerr_endline
5627 ("Can not determine home directory location: " ^
5628 Printexc.to_string exn);
5632 let modifier_of_string = function
5633 | "alt" -> Wsi.altmask
5634 | "shift" -> Wsi.shiftmask
5635 | "ctrl" | "control" -> Wsi.ctrlmask
5636 | "meta" -> Wsi.metamask
5637 | _ -> 0
5640 let key_of_string =
5641 let r = Str.regexp "-" in
5642 fun s ->
5643 let elems = Str.full_split r s in
5644 let f n k m =
5645 let g s =
5646 let m1 = modifier_of_string s in
5647 if m1 = 0
5648 then (Wsi.namekey s, m)
5649 else (k, m lor m1)
5650 in function
5651 | Str.Delim s when n land 1 = 0 -> g s
5652 | Str.Text s -> g s
5653 | Str.Delim _ -> (k, m)
5655 let rec loop n k m = function
5656 | [] -> (k, m)
5657 | x :: xs ->
5658 let k, m = f n k m x in
5659 loop (n+1) k m xs
5661 loop 0 0 0 elems
5664 let keys_of_string =
5665 let r = Str.regexp "[ \t]" in
5666 fun s ->
5667 let elems = Str.split r s in
5668 List.map key_of_string elems
5671 let copykeyhashes c =
5672 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
5675 let config_of c attrs =
5676 let apply c k v =
5678 match k with
5679 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5680 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5681 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5682 | "preload" -> { c with preload = bool_of_string v }
5683 | "page-bias" -> { c with pagebias = int_of_string v }
5684 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5685 | "auto-scroll-step" ->
5686 { c with autoscrollstep = max 0 (int_of_string v) }
5687 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5688 | "crop-hack" -> { c with crophack = bool_of_string v }
5689 | "throttle" ->
5690 let mw =
5691 match String.lowercase v with
5692 | "true" -> Some infinity
5693 | "false" -> None
5694 | f -> Some (float_of_string f)
5696 { c with maxwait = mw}
5697 | "highlight-links" -> { c with hlinks = bool_of_string v }
5698 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5699 | "vertical-margin" ->
5700 { c with interpagespace = max 0 (int_of_string v) }
5701 | "zoom" ->
5702 let zoom = float_of_string v /. 100. in
5703 let zoom = max zoom 0.0 in
5704 { c with zoom = zoom }
5705 | "presentation" -> { c with presentation = bool_of_string v }
5706 | "rotation-angle" -> { c with angle = int_of_string v }
5707 | "width" -> { c with winw = max 20 (int_of_string v) }
5708 | "height" -> { c with winh = max 20 (int_of_string v) }
5709 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5710 | "proportional-display" -> { c with proportional = bool_of_string v }
5711 | "pixmap-cache-size" ->
5712 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5713 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5714 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5715 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5716 | "persistent-location" -> { c with jumpback = bool_of_string v }
5717 | "background-color" -> { c with bgcolor = color_of_string v }
5718 | "scrollbar-in-presentation" ->
5719 { c with scrollbarinpm = bool_of_string v }
5720 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5721 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5722 | "mupdf-store-size" ->
5723 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5724 | "checkers" -> { c with checkers = bool_of_string v }
5725 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5726 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5727 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5728 | "uri-launcher" -> { c with urilauncher = unent v }
5729 | "path-launcher" -> { c with pathlauncher = unent v }
5730 | "color-space" -> { c with colorspace = colorspace_of_string v }
5731 | "invert-colors" -> { c with invert = bool_of_string v }
5732 | "brightness" -> { c with colorscale = float_of_string v }
5733 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5734 | "ghyllscroll" ->
5735 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5736 | "columns" ->
5737 let (n, _, _) as nab = multicolumns_of_string v in
5738 if n < 0
5739 then { c with columns = Csplit (-n, [||]) }
5740 else { c with columns = Cmulti (nab, [||]) }
5741 | "birds-eye-columns" ->
5742 { c with beyecolumns = Some (max (int_of_string v) 2) }
5743 | "selection-command" -> { c with selcmd = unent v }
5744 | "update-cursor" -> { c with updatecurs = bool_of_string v }
5745 | _ -> c
5746 with exn ->
5747 prerr_endline ("Error processing attribute (`" ^
5748 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5751 let rec fold c = function
5752 | [] -> c
5753 | (k, v) :: rest ->
5754 let c = apply c k v in
5755 fold c rest
5757 fold { c with keyhashes = copykeyhashes c } attrs;
5760 let fromstring f pos n v d =
5761 try f v
5762 with exn ->
5763 dolog "Error processing attribute (%S=%S) at %d\n%s"
5764 n v pos (Printexc.to_string exn)
5769 let bookmark_of attrs =
5770 let rec fold title page rely = function
5771 | ("title", v) :: rest -> fold v page rely rest
5772 | ("page", v) :: rest -> fold title v rely rest
5773 | ("rely", v) :: rest -> fold title page v rest
5774 | _ :: rest -> fold title page rely rest
5775 | [] -> title, page, rely
5777 fold "invalid" "0" "0" attrs
5780 let doc_of attrs =
5781 let rec fold path page rely pan = function
5782 | ("path", v) :: rest -> fold v page rely pan rest
5783 | ("page", v) :: rest -> fold path v rely pan rest
5784 | ("rely", v) :: rest -> fold path page v pan rest
5785 | ("pan", v) :: rest -> fold path page rely v rest
5786 | _ :: rest -> fold path page rely pan rest
5787 | [] -> path, page, rely, pan
5789 fold "" "0" "0" "0" attrs
5792 let map_of attrs =
5793 let rec fold rs ls = function
5794 | ("out", v) :: rest -> fold v ls rest
5795 | ("in", v) :: rest -> fold rs v rest
5796 | _ :: rest -> fold ls rs rest
5797 | [] -> ls, rs
5799 fold "" "" attrs
5802 let setconf dst src =
5803 dst.scrollbw <- src.scrollbw;
5804 dst.scrollh <- src.scrollh;
5805 dst.icase <- src.icase;
5806 dst.preload <- src.preload;
5807 dst.pagebias <- src.pagebias;
5808 dst.verbose <- src.verbose;
5809 dst.scrollstep <- src.scrollstep;
5810 dst.maxhfit <- src.maxhfit;
5811 dst.crophack <- src.crophack;
5812 dst.autoscrollstep <- src.autoscrollstep;
5813 dst.maxwait <- src.maxwait;
5814 dst.hlinks <- src.hlinks;
5815 dst.underinfo <- src.underinfo;
5816 dst.interpagespace <- src.interpagespace;
5817 dst.zoom <- src.zoom;
5818 dst.presentation <- src.presentation;
5819 dst.angle <- src.angle;
5820 dst.winw <- src.winw;
5821 dst.winh <- src.winh;
5822 dst.savebmarks <- src.savebmarks;
5823 dst.memlimit <- src.memlimit;
5824 dst.proportional <- src.proportional;
5825 dst.texcount <- src.texcount;
5826 dst.sliceheight <- src.sliceheight;
5827 dst.thumbw <- src.thumbw;
5828 dst.jumpback <- src.jumpback;
5829 dst.bgcolor <- src.bgcolor;
5830 dst.scrollbarinpm <- src.scrollbarinpm;
5831 dst.tilew <- src.tilew;
5832 dst.tileh <- src.tileh;
5833 dst.mustoresize <- src.mustoresize;
5834 dst.checkers <- src.checkers;
5835 dst.aalevel <- src.aalevel;
5836 dst.trimmargins <- src.trimmargins;
5837 dst.trimfuzz <- src.trimfuzz;
5838 dst.urilauncher <- src.urilauncher;
5839 dst.colorspace <- src.colorspace;
5840 dst.invert <- src.invert;
5841 dst.colorscale <- src.colorscale;
5842 dst.redirectstderr <- src.redirectstderr;
5843 dst.ghyllscroll <- src.ghyllscroll;
5844 dst.columns <- src.columns;
5845 dst.beyecolumns <- src.beyecolumns;
5846 dst.selcmd <- src.selcmd;
5847 dst.updatecurs <- src.updatecurs;
5848 dst.pathlauncher <- src.pathlauncher;
5849 dst.keyhashes <- copykeyhashes src;
5852 let get s =
5853 let h = Hashtbl.create 10 in
5854 let dc = { defconf with angle = defconf.angle } in
5855 let rec toplevel v t spos _ =
5856 match t with
5857 | Vdata | Vcdata | Vend -> v
5858 | Vopen ("llppconfig", _, closed) ->
5859 if closed
5860 then v
5861 else { v with f = llppconfig }
5862 | Vopen _ ->
5863 error "unexpected subelement at top level" s spos
5864 | Vclose _ -> error "unexpected close at top level" s spos
5866 and llppconfig v t spos _ =
5867 match t with
5868 | Vdata | Vcdata -> v
5869 | Vend -> error "unexpected end of input in llppconfig" s spos
5870 | Vopen ("defaults", attrs, closed) ->
5871 let c = config_of dc attrs in
5872 setconf dc c;
5873 if closed
5874 then v
5875 else { v with f = defaults }
5877 | Vopen ("ui-font", attrs, closed) ->
5878 let rec getsize size = function
5879 | [] -> size
5880 | ("size", v) :: rest ->
5881 let size =
5882 fromstring int_of_string spos "size" v fstate.fontsize in
5883 getsize size rest
5884 | l -> getsize size l
5886 fstate.fontsize <- getsize fstate.fontsize attrs;
5887 if closed
5888 then v
5889 else { v with f = uifont (Buffer.create 10) }
5891 | Vopen ("doc", attrs, closed) ->
5892 let pathent, spage, srely, span = doc_of attrs in
5893 let path = unent pathent
5894 and pageno = fromstring int_of_string spos "page" spage 0
5895 and rely = fromstring float_of_string spos "rely" srely 0.0
5896 and pan = fromstring int_of_string spos "pan" span 0 in
5897 let c = config_of dc attrs in
5898 let anchor = (pageno, rely) in
5899 if closed
5900 then (Hashtbl.add h path (c, [], pan, anchor); v)
5901 else { v with f = doc path pan anchor c [] }
5903 | Vopen _ ->
5904 error "unexpected subelement in llppconfig" s spos
5906 | Vclose "llppconfig" -> { v with f = toplevel }
5907 | Vclose _ -> error "unexpected close in llppconfig" s spos
5909 and defaults v t spos _ =
5910 match t with
5911 | Vdata | Vcdata -> v
5912 | Vend -> error "unexpected end of input in defaults" s spos
5913 | Vopen ("keymap", attrs, closed) ->
5914 let modename =
5915 try List.assoc "mode" attrs
5916 with Not_found -> "global" in
5917 if closed
5918 then v
5919 else
5920 let ret keymap =
5921 let h = findkeyhash dc modename in
5922 KeyMap.iter (Hashtbl.replace h) keymap;
5923 defaults
5925 { v with f = pkeymap ret KeyMap.empty }
5927 | Vopen (_, _, _) ->
5928 error "unexpected subelement in defaults" s spos
5930 | Vclose "defaults" ->
5931 { v with f = llppconfig }
5933 | Vclose _ -> error "unexpected close in defaults" s spos
5935 and uifont b v t spos epos =
5936 match t with
5937 | Vdata | Vcdata ->
5938 Buffer.add_substring b s spos (epos - spos);
5940 | Vopen (_, _, _) ->
5941 error "unexpected subelement in ui-font" s spos
5942 | Vclose "ui-font" ->
5943 if String.length !fontpath = 0
5944 then fontpath := Buffer.contents b;
5945 { v with f = llppconfig }
5946 | Vclose _ -> error "unexpected close in ui-font" s spos
5947 | Vend -> error "unexpected end of input in ui-font" s spos
5949 and doc path pan anchor c bookmarks v t spos _ =
5950 match t with
5951 | Vdata | Vcdata -> v
5952 | Vend -> error "unexpected end of input in doc" s spos
5953 | Vopen ("bookmarks", _, closed) ->
5954 if closed
5955 then v
5956 else { v with f = pbookmarks path pan anchor c bookmarks }
5958 | Vopen ("keymap", attrs, closed) ->
5959 let modename =
5960 try List.assoc "mode" attrs
5961 with Not_found -> "global"
5963 if closed
5964 then v
5965 else
5966 let ret keymap =
5967 let h = findkeyhash c modename in
5968 KeyMap.iter (Hashtbl.replace h) keymap;
5969 doc path pan anchor c bookmarks
5971 { v with f = pkeymap ret KeyMap.empty }
5973 | Vopen (_, _, _) ->
5974 error "unexpected subelement in doc" s spos
5976 | Vclose "doc" ->
5977 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5978 { v with f = llppconfig }
5980 | Vclose _ -> error "unexpected close in doc" s spos
5982 and pkeymap ret keymap v t spos _ =
5983 match t with
5984 | Vdata | Vcdata -> v
5985 | Vend -> error "unexpected end of input in keymap" s spos
5986 | Vopen ("map", attrs, closed) ->
5987 let r, l = map_of attrs in
5988 let kss = fromstring keys_of_string spos "in" r [] in
5989 let lss = fromstring keys_of_string spos "out" l [] in
5990 let keymap =
5991 match kss with
5992 | [] -> keymap
5993 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
5994 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
5996 if closed
5997 then { v with f = pkeymap ret keymap }
5998 else
5999 let f () = v in
6000 { v with f = skip "map" f }
6002 | Vopen _ ->
6003 error "unexpected subelement in keymap" s spos
6005 | Vclose "keymap" ->
6006 { v with f = ret keymap }
6008 | Vclose _ -> error "unexpected close in keymap" s spos
6010 and pbookmarks path pan anchor c bookmarks v t spos _ =
6011 match t with
6012 | Vdata | Vcdata -> v
6013 | Vend -> error "unexpected end of input in bookmarks" s spos
6014 | Vopen ("item", attrs, closed) ->
6015 let titleent, spage, srely = bookmark_of attrs in
6016 let page = fromstring int_of_string spos "page" spage 0
6017 and rely = fromstring float_of_string spos "rely" srely 0.0 in
6018 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
6019 if closed
6020 then { v with f = pbookmarks path pan anchor c bookmarks }
6021 else
6022 let f () = v in
6023 { v with f = skip "item" f }
6025 | Vopen _ ->
6026 error "unexpected subelement in bookmarks" s spos
6028 | Vclose "bookmarks" ->
6029 { v with f = doc path pan anchor c bookmarks }
6031 | Vclose _ -> error "unexpected close in bookmarks" s spos
6033 and skip tag f v t spos _ =
6034 match t with
6035 | Vdata | Vcdata -> v
6036 | Vend ->
6037 error ("unexpected end of input in skipped " ^ tag) s spos
6038 | Vopen (tag', _, closed) ->
6039 if closed
6040 then v
6041 else
6042 let f' () = { v with f = skip tag f } in
6043 { v with f = skip tag' f' }
6044 | Vclose ctag ->
6045 if tag = ctag
6046 then f ()
6047 else error ("unexpected close in skipped " ^ tag) s spos
6050 parse { f = toplevel; accu = () } s;
6051 h, dc;
6054 let do_load f ic =
6056 let len = in_channel_length ic in
6057 let s = String.create len in
6058 really_input ic s 0 len;
6059 f s;
6060 with
6061 | Parse_error (msg, s, pos) ->
6062 let subs = subs s pos in
6063 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6064 failwith ("parse error: " ^ s)
6066 | exn ->
6067 failwith ("config load error: " ^ Printexc.to_string exn)
6070 let defconfpath =
6071 let dir =
6073 let dir = Filename.concat home ".config" in
6074 if Sys.is_directory dir then dir else home
6075 with _ -> home
6077 Filename.concat dir "llpp.conf"
6080 let confpath = ref defconfpath;;
6082 let load1 f =
6083 if Sys.file_exists !confpath
6084 then
6085 match
6086 (try Some (open_in_bin !confpath)
6087 with exn ->
6088 prerr_endline
6089 ("Error opening configuation file `" ^ !confpath ^ "': " ^
6090 Printexc.to_string exn);
6091 None
6093 with
6094 | Some ic ->
6095 begin try
6096 f (do_load get ic)
6097 with exn ->
6098 prerr_endline
6099 ("Error loading configuation from `" ^ !confpath ^ "': " ^
6100 Printexc.to_string exn);
6101 end;
6102 close_in ic;
6104 | None -> ()
6105 else
6106 f (Hashtbl.create 0, defconf)
6109 let load () =
6110 let f (h, dc) =
6111 let pc, pb, px, pa =
6113 Hashtbl.find h (Filename.basename state.path)
6114 with Not_found -> dc, [], 0, (0, 0.0)
6116 setconf defconf dc;
6117 setconf conf pc;
6118 state.bookmarks <- pb;
6119 state.x <- px;
6120 state.scrollw <- conf.scrollbw;
6121 if conf.jumpback
6122 then state.anchor <- pa;
6123 cbput state.hists.nav pa;
6125 load1 f
6128 let add_attrs bb always dc c =
6129 let ob s a b =
6130 if always || a != b
6131 then Printf.bprintf bb "\n %s='%b'" s a
6132 and oi s a b =
6133 if always || a != b
6134 then Printf.bprintf bb "\n %s='%d'" s a
6135 and oI s a b =
6136 if always || a != b
6137 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6138 and oz s a b =
6139 if always || a <> b
6140 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
6141 and oF s a b =
6142 if always || a <> b
6143 then Printf.bprintf bb "\n %s='%f'" s a
6144 and oc s a b =
6145 if always || a <> b
6146 then
6147 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6148 and oC s a b =
6149 if always || a <> b
6150 then
6151 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6152 and oR s a b =
6153 if always || a <> b
6154 then
6155 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6156 and os s a b =
6157 if always || a <> b
6158 then
6159 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6160 and og s a b =
6161 if always || a <> b
6162 then
6163 match a with
6164 | None -> ()
6165 | Some (_N, _A, _B) ->
6166 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6167 and oW s a b =
6168 if always || a <> b
6169 then
6170 let v =
6171 match a with
6172 | None -> "false"
6173 | Some f ->
6174 if f = infinity
6175 then "true"
6176 else string_of_float f
6178 Printf.bprintf bb "\n %s='%s'" s v
6179 and oco s a b =
6180 if always || a <> b
6181 then
6182 match a with
6183 | Cmulti ((n, a, b), _) when n > 1 ->
6184 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6185 | Csplit (n, _) when n > 1 ->
6186 Printf.bprintf bb "\n %s='%d'" s ~-n
6187 | _ -> ()
6188 and obeco s a b =
6189 if always || a <> b
6190 then
6191 match a with
6192 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6193 | _ -> ()
6195 let w, h =
6196 if always
6197 then dc.winw, dc.winh
6198 else
6199 match state.fullscreen with
6200 | Some wh -> wh
6201 | None -> c.winw, c.winh
6203 let zoom, presentation, interpagespace, maxwait =
6204 if always
6205 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
6206 else
6207 match state.mode with
6208 | Birdseye (bc, _, _, _, _) ->
6209 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
6210 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
6212 oi "width" w dc.winw;
6213 oi "height" h dc.winh;
6214 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6215 oi "scroll-handle-height" c.scrollh dc.scrollh;
6216 ob "case-insensitive-search" c.icase dc.icase;
6217 ob "preload" c.preload dc.preload;
6218 oi "page-bias" c.pagebias dc.pagebias;
6219 oi "scroll-step" c.scrollstep dc.scrollstep;
6220 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6221 ob "max-height-fit" c.maxhfit dc.maxhfit;
6222 ob "crop-hack" c.crophack dc.crophack;
6223 oW "throttle" maxwait dc.maxwait;
6224 ob "highlight-links" c.hlinks dc.hlinks;
6225 ob "under-cursor-info" c.underinfo dc.underinfo;
6226 oi "vertical-margin" interpagespace dc.interpagespace;
6227 oz "zoom" zoom dc.zoom;
6228 ob "presentation" presentation dc.presentation;
6229 oi "rotation-angle" c.angle dc.angle;
6230 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6231 ob "proportional-display" c.proportional dc.proportional;
6232 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6233 oi "tex-count" c.texcount dc.texcount;
6234 oi "slice-height" c.sliceheight dc.sliceheight;
6235 oi "thumbnail-width" c.thumbw dc.thumbw;
6236 ob "persistent-location" c.jumpback dc.jumpback;
6237 oc "background-color" c.bgcolor dc.bgcolor;
6238 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6239 oi "tile-width" c.tilew dc.tilew;
6240 oi "tile-height" c.tileh dc.tileh;
6241 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6242 ob "checkers" c.checkers dc.checkers;
6243 oi "aalevel" c.aalevel dc.aalevel;
6244 ob "trim-margins" c.trimmargins dc.trimmargins;
6245 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6246 os "uri-launcher" c.urilauncher dc.urilauncher;
6247 os "path-launcher" c.pathlauncher dc.pathlauncher;
6248 oC "color-space" c.colorspace dc.colorspace;
6249 ob "invert-colors" c.invert dc.invert;
6250 oF "brightness" c.colorscale dc.colorscale;
6251 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6252 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6253 oco "columns" c.columns dc.columns;
6254 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6255 os "selection-command" c.selcmd dc.selcmd;
6256 ob "update-cursor" c.updatecurs dc.updatecurs;
6259 let keymapsbuf always dc c =
6260 let bb = Buffer.create 16 in
6261 let rec loop = function
6262 | [] -> ()
6263 | (modename, h) :: rest ->
6264 let dh = findkeyhash dc modename in
6265 if always || h <> dh
6266 then (
6267 if Hashtbl.length h > 0
6268 then (
6269 if Buffer.length bb > 0
6270 then Buffer.add_char bb '\n';
6271 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6272 Hashtbl.iter (fun i o ->
6273 let isdifferent = always ||
6275 let dO = Hashtbl.find dh i in
6276 dO <> o
6277 with Not_found -> true
6279 if isdifferent
6280 then
6281 let addkm (k, m) =
6282 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6283 if Wsi.withalt m then Buffer.add_string bb "alt-";
6284 if Wsi.withshift m then Buffer.add_string bb "shift-";
6285 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6286 Buffer.add_string bb (Wsi.keyname k);
6288 let addkms l =
6289 let rec loop = function
6290 | [] -> ()
6291 | km :: [] -> addkm km
6292 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6294 loop l
6296 Buffer.add_string bb "<map in='";
6297 addkm i;
6298 match o with
6299 | KMinsrt km ->
6300 Buffer.add_string bb "' out='";
6301 addkm km;
6302 Buffer.add_string bb "'/>\n"
6304 | KMinsrl kms ->
6305 Buffer.add_string bb "' out='";
6306 addkms kms;
6307 Buffer.add_string bb "'/>\n"
6309 | KMmulti (ins, kms) ->
6310 Buffer.add_char bb ' ';
6311 addkms ins;
6312 Buffer.add_string bb "' out='";
6313 addkms kms;
6314 Buffer.add_string bb "'/>\n"
6315 ) h;
6316 Buffer.add_string bb "</keymap>";
6319 loop rest
6321 loop c.keyhashes;
6325 let save () =
6326 let uifontsize = fstate.fontsize in
6327 let bb = Buffer.create 32768 in
6328 let f (h, dc) =
6329 let dc = if conf.bedefault then conf else dc in
6330 Buffer.add_string bb "<llppconfig>\n";
6332 if String.length !fontpath > 0
6333 then
6334 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6335 uifontsize
6336 !fontpath
6337 else (
6338 if uifontsize <> 14
6339 then
6340 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6343 Buffer.add_string bb "<defaults ";
6344 add_attrs bb true dc dc;
6345 let kb = keymapsbuf true dc dc in
6346 if Buffer.length kb > 0
6347 then (
6348 Buffer.add_string bb ">\n";
6349 Buffer.add_buffer bb kb;
6350 Buffer.add_string bb "\n</defaults>\n";
6352 else Buffer.add_string bb "/>\n";
6354 let adddoc path pan anchor c bookmarks =
6355 if bookmarks == [] && c = dc && anchor = emptyanchor
6356 then ()
6357 else (
6358 Printf.bprintf bb "<doc path='%s'"
6359 (enent path 0 (String.length path));
6361 if anchor <> emptyanchor
6362 then (
6363 let n, y = anchor in
6364 Printf.bprintf bb " page='%d'" n;
6365 if y > 1e-6
6366 then
6367 Printf.bprintf bb " rely='%f'" y
6371 if pan != 0
6372 then Printf.bprintf bb " pan='%d'" pan;
6374 add_attrs bb false dc c;
6375 let kb = keymapsbuf false dc c in
6377 begin match bookmarks with
6378 | [] ->
6379 if Buffer.length kb > 0
6380 then (
6381 Buffer.add_string bb ">\n";
6382 Buffer.add_buffer bb kb;
6383 Buffer.add_string bb "</doc>\n";
6385 else Buffer.add_string bb "/>\n"
6386 | _ ->
6387 Buffer.add_string bb ">\n<bookmarks>\n";
6388 List.iter (fun (title, _level, (page, rely)) ->
6389 Printf.bprintf bb
6390 "<item title='%s' page='%d'"
6391 (enent title 0 (String.length title))
6392 page
6394 if rely > 1e-6
6395 then
6396 Printf.bprintf bb " rely='%f'" rely
6398 Buffer.add_string bb "/>\n";
6399 ) bookmarks;
6400 Buffer.add_string bb "</bookmarks>";
6401 if Buffer.length kb > 0
6402 then (
6403 Buffer.add_string bb "\n";
6404 Buffer.add_buffer bb kb;
6406 Buffer.add_string bb "\n</doc>\n";
6407 end;
6411 let pan, conf =
6412 match state.mode with
6413 | Birdseye (c, pan, _, _, _) ->
6414 let beyecolumns =
6415 match conf.columns with
6416 | Cmulti ((c, _, _), _) -> Some c
6417 | Csingle -> None
6418 | Csplit _ -> None
6419 and columns =
6420 match c.columns with
6421 | Cmulti (c, _) -> Cmulti (c, [||])
6422 | Csingle -> Csingle
6423 | Csplit _ -> failwith "quit from bird's eye while split"
6425 pan, { c with beyecolumns = beyecolumns; columns = columns }
6426 | _ -> state.x, conf
6428 let basename = Filename.basename state.path in
6429 adddoc basename pan (getanchor ())
6430 { conf with
6431 autoscrollstep =
6432 match state.autoscroll with
6433 | Some step -> step
6434 | None -> conf.autoscrollstep }
6435 (if conf.savebmarks then state.bookmarks else []);
6437 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
6438 if basename <> path
6439 then adddoc path x y c bookmarks
6440 ) h;
6441 Buffer.add_string bb "</llppconfig>";
6443 load1 f;
6444 if Buffer.length bb > 0
6445 then
6447 let tmp = !confpath ^ ".tmp" in
6448 let oc = open_out_bin tmp in
6449 Buffer.output_buffer oc bb;
6450 close_out oc;
6451 Unix.rename tmp !confpath;
6452 with exn ->
6453 prerr_endline
6454 ("error while saving configuration: " ^ Printexc.to_string exn)
6456 end;;
6458 let () =
6459 Arg.parse
6460 (Arg.align
6461 [("-p", Arg.String (fun s -> state.password <- s) ,
6462 "<password> Set password");
6464 ("-f", Arg.String (fun s -> Config.fontpath := s),
6465 "<path> Set path to the user interface font");
6467 ("-c", Arg.String (fun s -> Config.confpath := s),
6468 "<path> Set path to the configuration file");
6470 ("-v", Arg.Unit (fun () ->
6471 Printf.printf
6472 "%s\nconfiguration path: %s\n"
6473 (version ())
6474 Config.defconfpath
6476 exit 0), " Print version and exit");
6479 (fun s -> state.path <- s)
6480 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6482 if String.length state.path = 0
6483 then (prerr_endline "file name missing"; exit 1);
6485 Config.load ();
6487 let globalkeyhash = findkeyhash conf "global" in
6488 let wsfd, winw, winh = Wsi.init (object
6489 method expose =
6490 if nogeomcmds state.geomcmds
6491 then display ()
6492 method display = display ()
6493 method reshape w h = reshape w h
6494 method mouse b d x y m = mouse b d x y m
6495 method motion x y = state.mpos <- (x, y); motion x y
6496 method pmotion x y = state.mpos <- (x, y); pmotion x y
6497 method key k m =
6498 let mascm = m land (
6499 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
6500 ) in
6501 match state.keystate with
6502 | KSnone ->
6503 let km = k, mascm in
6504 begin
6505 match
6506 try Hashtbl.find globalkeyhash km
6507 with Not_found ->
6508 let modehash = state.uioh#modehash in
6509 try Hashtbl.find modehash km
6510 with Not_found -> KMinsrt (k, m)
6511 with
6512 | KMinsrt (k, m) -> keyboard k m
6513 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6514 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6516 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
6517 List.iter (fun (k, m) -> keyboard k m) insrt;
6518 state.keystate <- KSnone
6519 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
6520 state.keystate <- KSinto (keys, insrt)
6521 | _ ->
6522 state.keystate <- KSnone
6524 method enter x y = state.mpos <- (x, y); pmotion x y
6525 method leave = state.mpos <- (-1, -1)
6526 method quit = raise Quit
6527 end) conf.winw conf.winh (platform = Posx) in
6529 state.wsfd <- wsfd;
6531 if not (
6532 List.exists GlMisc.check_extension
6533 [ "GL_ARB_texture_rectangle"
6534 ; "GL_EXT_texture_recangle"
6535 ; "GL_NV_texture_rectangle" ]
6537 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
6539 let cr, sw = Unix.pipe ()
6540 and sr, cw = Unix.pipe () in
6542 cloexec cr;
6543 cloexec sw;
6544 cloexec sr;
6545 cloexec cw;
6547 setcheckers conf.checkers;
6548 redirectstderr ();
6550 init (cr, cw) (
6551 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
6552 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6553 !Config.fontpath
6555 state.sr <- sr;
6556 state.sw <- sw;
6557 state.text <- "Opening " ^ state.path;
6558 reshape winw winh;
6559 opendoc state.path state.password;
6560 state.uioh <- uioh;
6562 let rec loop deadline =
6563 let r =
6564 match state.errfd with
6565 | None -> [state.sr; state.wsfd]
6566 | Some fd -> [state.sr; state.wsfd; fd]
6568 if state.redisplay
6569 then (
6570 state.redisplay <- false;
6571 display ();
6573 let timeout =
6574 let now = now () in
6575 if deadline > now
6576 then (
6577 if deadline = infinity
6578 then ~-.1.0
6579 else max 0.0 (deadline -. now)
6581 else 0.0
6583 let r, _, _ =
6584 try Unix.select r [] [] timeout
6585 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
6587 begin match r with
6588 | [] ->
6589 state.ghyll None;
6590 let newdeadline =
6591 if state.ghyll == noghyll
6592 then
6593 match state.autoscroll with
6594 | Some step when step != 0 ->
6595 let y = state.y + step in
6596 let y =
6597 if y < 0
6598 then state.maxy
6599 else if y >= state.maxy then 0 else y
6601 gotoy y;
6602 if state.mode = View
6603 then state.text <- "";
6604 deadline +. 0.01
6605 | _ -> infinity
6606 else deadline +. 0.01
6608 loop newdeadline
6610 | l ->
6611 let rec checkfds = function
6612 | [] -> ()
6613 | fd :: rest when fd = state.sr ->
6614 let cmd = readcmd state.sr in
6615 act cmd;
6616 checkfds rest
6618 | fd :: rest when fd = state.wsfd ->
6619 Wsi.readresp fd;
6620 checkfds rest
6622 | fd :: rest ->
6623 let s = String.create 80 in
6624 let n = Unix.read fd s 0 80 in
6625 if conf.redirectstderr
6626 then (
6627 Buffer.add_substring state.errmsgs s 0 n;
6628 state.newerrmsgs <- true;
6629 state.redisplay <- true;
6631 else (
6632 prerr_string (String.sub s 0 n);
6633 flush stderr;
6635 checkfds rest
6637 checkfds l;
6638 let newdeadline =
6639 let deadline1 =
6640 if deadline = infinity
6641 then now () +. 0.01
6642 else deadline
6644 match state.autoscroll with
6645 | Some step when step != 0 -> deadline1
6646 | _ -> if state.ghyll == noghyll then infinity else deadline1
6648 loop newdeadline
6649 end;
6652 loop infinity;
6653 with Quit ->
6654 Config.save ();
6655 exit 0;