Give presentation mode fine positioning some love
[llpp.git] / main.ml
blobad330b99ca209a0ecc16c20448108af485f870aa
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 * trimcachepath)
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 dtop = float
36 and fontpath = string
37 and trimcachepath = string
38 and memsize = int
39 and aalevel = int
40 and irect = (int * int * int * int)
41 and trimparams = (trimmargins * irect)
42 and colorspace = | Rgb | Bgr | Gray
45 type link =
46 | Lnotfound
47 | Lfound of int
48 and linkdir =
49 | LDfirst
50 | LDlast
51 | LDfirstvisible of (int * int * int)
52 | LDleft of int
53 | LDright of int
54 | LDdown of int
55 | LDup of int
58 type pagewithlinks =
59 | Pwlnotfound
60 | Pwl of int
63 type keymap =
64 | KMinsrt of key
65 | KMinsrl of key list
66 | KMmulti of key list * key list
67 and key = int * int
68 and keyhash = (key, keymap) Hashtbl.t
69 and keystate =
70 | KSnone
71 | KSinto of (key list * key list)
74 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
75 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
77 type pipe = (Unix.file_descr * Unix.file_descr);;
79 external init : pipe -> params -> unit = "ml_init";;
80 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
81 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
82 external getpdimrect : int -> float array = "ml_getpdimrect";;
83 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
84 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
85 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
86 external measurestr : int -> string -> float = "ml_measure_string";;
87 external getmaxw : unit -> float = "ml_getmaxw";;
88 external postprocess :
89 opaque -> int -> int -> int -> (int * string * int) -> int = "ml_postprocess";;
90 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
91 external platform : unit -> platform = "ml_platform";;
92 external setaalevel : int -> unit = "ml_setaalevel";;
93 external realloctexts : int -> bool = "ml_realloctexts";;
94 external cloexec : Unix.file_descr -> unit = "ml_cloexec";;
95 external findlink : opaque -> linkdir -> link = "ml_findlink";;
96 external getlink : opaque -> int -> under = "ml_getlink";;
97 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
98 external getlinkcount : opaque -> int = "ml_getlinkcount";;
99 external findpwl: int -> int -> pagewithlinks = "ml_find_page_with_links"
100 external popen : string -> (Unix.file_descr * int) list -> unit = "ml_popen";;
102 let platform_to_string = function
103 | Punknown -> "unknown"
104 | Plinux -> "Linux"
105 | Posx -> "OSX"
106 | Psun -> "Sun"
107 | Pfreebsd -> "FreeBSD"
108 | Pdragonflybsd -> "DragonflyBSD"
109 | Popenbsd -> "OpenBSD"
110 | Pnetbsd -> "NetBSD"
111 | Pcygwin -> "Cygwin"
114 let platform = platform ();;
116 let popen cmd fda =
117 if platform = Pcygwin
118 then (
119 let sh = "/bin/sh" in
120 let args = [|sh; "-c"; cmd|] in
121 let rec std si so se = function
122 | [] -> si, so, se
123 | (fd, 0) :: rest -> std fd so se rest
124 | (fd, -1) :: rest ->
125 Unix.set_close_on_exec fd;
126 std si so se rest
127 | (_, n) :: _ ->
128 failwith ("unexpected fdn in cygwin popen " ^ string_of_int n)
130 let si, so, se = std Unix.stdin Unix.stdout Unix.stderr fda in
131 ignore (Unix.create_process sh args si so se)
133 else popen cmd fda;
136 type x = int
137 and y = int
138 and tilex = int
139 and tiley = int
140 and tileparams = (x * y * width * height * tilex * tiley)
143 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
145 type mpos = int * int
146 and mstate =
147 | Msel of (mpos * mpos)
148 | Mpan of mpos
149 | Mscrolly | Mscrollx
150 | Mzoom of (int * int)
151 | Mzoomrect of (mpos * mpos)
152 | Mnone
155 type textentry = string * string * onhist option * onkey * ondone * cancelonempty
156 and onkey = string -> int -> te
157 and ondone = string -> unit
158 and histcancel = unit -> unit
159 and onhist = ((histcmd -> string) * histcancel)
160 and histcmd = HCnext | HCprev | HCfirst | HClast
161 and cancelonempty = bool
162 and te =
163 | TEstop
164 | TEdone of string
165 | TEcont of string
166 | TEswitch of textentry
169 type 'a circbuf =
170 { store : 'a array
171 ; mutable rc : int
172 ; mutable wc : int
173 ; mutable len : int
177 let bound v minv maxv =
178 max minv (min maxv v);
181 let cbnew n v =
182 { store = Array.create n v
183 ; rc = 0
184 ; wc = 0
185 ; len = 0
189 let drawstring size x y s =
190 Gl.enable `blend;
191 Gl.enable `texture_2d;
192 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
193 ignore (drawstr size x y s);
194 Gl.disable `blend;
195 Gl.disable `texture_2d;
198 let drawstring1 size x y s =
199 drawstr size x y s;
202 let drawstring2 size x y fmt =
203 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
206 let cbcap b = Array.length b.store;;
208 let cbput b v =
209 let cap = cbcap b in
210 b.store.(b.wc) <- v;
211 b.wc <- (b.wc + 1) mod cap;
212 b.rc <- b.wc;
213 b.len <- min (b.len + 1) cap;
216 let cbempty b = b.len = 0;;
218 let cbgetg b circular dir =
219 if cbempty b
220 then b.store.(0)
221 else
222 let rc = b.rc + dir in
223 let rc =
224 if circular
225 then (
226 if rc = -1
227 then b.len-1
228 else (
229 if rc = b.len
230 then 0
231 else rc
234 else max 0 (min rc (b.len-1))
236 b.rc <- rc;
237 b.store.(rc);
240 let cbget b = cbgetg b false;;
241 let cbgetc b = cbgetg b true;;
243 type page =
244 { pageno : int
245 ; pagedimno : int
246 ; pagew : int
247 ; pageh : int
248 ; pagex : int
249 ; pagey : int
250 ; pagevw : int
251 ; pagevh : int
252 ; pagedispx : int
253 ; pagedispy : int
254 ; pagecol : int
258 let debugl l =
259 dolog "l %d dim=%d {" l.pageno l.pagedimno;
260 dolog " WxH %dx%d" l.pagew l.pageh;
261 dolog " vWxH %dx%d" l.pagevw l.pagevh;
262 dolog " pagex,y %d,%d" l.pagex l.pagey;
263 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
264 dolog " column %d" l.pagecol;
265 dolog "}";
268 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
269 dolog "rect {";
270 dolog " x0,y0=(% f, % f)" x0 y0;
271 dolog " x1,y1=(% f, % f)" x1 y1;
272 dolog " x2,y2=(% f, % f)" x2 y2;
273 dolog " x3,y3=(% f, % f)" x3 y3;
274 dolog "}";
277 type multicolumns = multicol * pagegeom
278 and singlecolumn = pagegeom
279 and splitcolumns = columncount * pagegeom
280 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
281 and multicol = columncount * covercount * covercount
282 and pdimno = int
283 and columncount = int
284 and covercount = int;;
286 type conf =
287 { mutable scrollbw : int
288 ; mutable scrollh : int
289 ; mutable icase : bool
290 ; mutable preload : bool
291 ; mutable pagebias : int
292 ; mutable verbose : bool
293 ; mutable debug : bool
294 ; mutable scrollstep : int
295 ; mutable hscrollstep : int
296 ; mutable maxhfit : bool
297 ; mutable crophack : bool
298 ; mutable autoscrollstep : int
299 ; mutable maxwait : float option
300 ; mutable hlinks : bool
301 ; mutable underinfo : bool
302 ; mutable interpagespace : interpagespace
303 ; mutable zoom : float
304 ; mutable presentation : bool
305 ; mutable angle : angle
306 ; mutable winw : int
307 ; mutable winh : int
308 ; mutable savebmarks : bool
309 ; mutable proportional : proportional
310 ; mutable trimmargins : trimmargins
311 ; mutable trimfuzz : irect
312 ; mutable memlimit : memsize
313 ; mutable texcount : texcount
314 ; mutable sliceheight : sliceheight
315 ; mutable thumbw : width
316 ; mutable jumpback : bool
317 ; mutable bgcolor : float * float * float
318 ; mutable bedefault : bool
319 ; mutable scrollbarinpm : bool
320 ; mutable tilew : int
321 ; mutable tileh : int
322 ; mutable mustoresize : memsize
323 ; mutable checkers : bool
324 ; mutable aalevel : int
325 ; mutable urilauncher : string
326 ; mutable pathlauncher : string
327 ; mutable colorspace : colorspace
328 ; mutable invert : bool
329 ; mutable colorscale : float
330 ; mutable redirectstderr : bool
331 ; mutable ghyllscroll : (int * int * int) option
332 ; mutable columns : columns
333 ; mutable beyecolumns : columncount option
334 ; mutable selcmd : string
335 ; mutable updatecurs : bool
336 ; mutable keyhashes : (string * keyhash) list
337 ; mutable hfsize : int
338 ; mutable pgscale : float
340 and columns =
341 | Csingle of singlecolumn
342 | Cmulti of multicolumns
343 | Csplit of splitcolumns
346 type anchor = pageno * top * dtop;;
348 type outline = string * int * anchor;;
350 type rect = float * float * float * float * float * float * float * float;;
352 type tile = opaque * pixmapsize * elapsed
353 and elapsed = float;;
354 type pagemapkey = pageno * gen;;
355 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
356 and row = int
357 and col = int;;
359 let emptyanchor = (0, 0.0, 0.0);;
361 type infochange = | Memused | Docinfo | Pdim;;
363 class type uioh = object
364 method display : unit
365 method key : int -> int -> uioh
366 method button : int -> bool -> int -> int -> int -> uioh
367 method motion : int -> int -> uioh
368 method pmotion : int -> int -> uioh
369 method infochanged : infochange -> unit
370 method scrollpw : (int * float * float)
371 method scrollph : (int * float * float)
372 method modehash : keyhash
373 end;;
375 type mode =
376 | Birdseye of (conf * leftx * pageno * pageno * anchor)
377 | Textentry of (textentry * onleave)
378 | View
379 | LinkNav of linktarget
380 and onleave = leavetextentrystatus -> unit
381 and leavetextentrystatus = | Cancel | Confirm
382 and helpitem = string * int * action
383 and action =
384 | Noaction
385 | Action of (uioh -> uioh)
386 and linktarget =
387 | Ltexact of (pageno * int)
388 | Ltgendir of int
391 let isbirdseye = function Birdseye _ -> true | _ -> false;;
392 let istextentry = function Textentry _ -> true | _ -> false;;
394 type currently =
395 | Idle
396 | Loading of (page * gen)
397 | Tiling of (
398 page * opaque * colorspace * angle * gen * col * row * width * height
400 | Outlining of outline list
403 let emptykeyhash = Hashtbl.create 0;;
404 let nouioh : uioh = object (self)
405 method display = ()
406 method key _ _ = self
407 method button _ _ _ _ _ = self
408 method motion _ _ = self
409 method pmotion _ _ = self
410 method infochanged _ = ()
411 method scrollpw = (0, nan, nan)
412 method scrollph = (0, nan, nan)
413 method modehash = emptykeyhash
414 end;;
416 type state =
417 { mutable sr : Unix.file_descr
418 ; mutable sw : Unix.file_descr
419 ; mutable wsfd : Unix.file_descr
420 ; mutable errfd : Unix.file_descr option
421 ; mutable stderr : Unix.file_descr
422 ; mutable errmsgs : Buffer.t
423 ; mutable newerrmsgs : bool
424 ; mutable w : int
425 ; mutable x : int
426 ; mutable y : int
427 ; mutable scrollw : int
428 ; mutable hscrollh : int
429 ; mutable anchor : anchor
430 ; mutable ranchors : (string * string * anchor) list
431 ; mutable maxy : int
432 ; mutable layout : page list
433 ; pagemap : (pagemapkey, opaque) Hashtbl.t
434 ; tilemap : (tilemapkey, tile) Hashtbl.t
435 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
436 ; mutable pdims : (pageno * width * height * leftx) list
437 ; mutable pagecount : int
438 ; mutable currently : currently
439 ; mutable mstate : mstate
440 ; mutable searchpattern : string
441 ; mutable rects : (pageno * recttype * rect) list
442 ; mutable rects1 : (pageno * recttype * rect) list
443 ; mutable text : string
444 ; mutable fullscreen : (width * height) option
445 ; mutable mode : mode
446 ; mutable uioh : uioh
447 ; mutable outlines : outline array
448 ; mutable bookmarks : outline list
449 ; mutable path : string
450 ; mutable password : string
451 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
452 ; mutable memused : memsize
453 ; mutable gen : gen
454 ; mutable throttle : (page list * int * float) option
455 ; mutable autoscroll : int option
456 ; mutable ghyll : (int option -> unit)
457 ; mutable help : helpitem array
458 ; mutable docinfo : (int * string) list
459 ; mutable texid : GlTex.texture_id option
460 ; hists : hists
461 ; mutable prevzoom : float
462 ; mutable progress : float
463 ; mutable redisplay : bool
464 ; mutable mpos : mpos
465 ; mutable keystate : keystate
466 ; mutable glinks : bool
467 ; mutable prevcolumns : (columns * float) option
469 and hists =
470 { pat : string circbuf
471 ; pag : string circbuf
472 ; nav : anchor circbuf
473 ; sel : string circbuf
477 let defconf =
478 { scrollbw = 7
479 ; scrollh = 12
480 ; icase = true
481 ; preload = true
482 ; pagebias = 0
483 ; verbose = false
484 ; debug = false
485 ; scrollstep = 24
486 ; hscrollstep = 24
487 ; maxhfit = true
488 ; crophack = false
489 ; autoscrollstep = 2
490 ; maxwait = None
491 ; hlinks = false
492 ; underinfo = false
493 ; interpagespace = 2
494 ; zoom = 1.0
495 ; presentation = false
496 ; angle = 0
497 ; winw = 900
498 ; winh = 900
499 ; savebmarks = true
500 ; proportional = true
501 ; trimmargins = false
502 ; trimfuzz = (0,0,0,0)
503 ; memlimit = 32 lsl 20
504 ; texcount = 256
505 ; sliceheight = 24
506 ; thumbw = 76
507 ; jumpback = true
508 ; bgcolor = (0.5, 0.5, 0.5)
509 ; bedefault = false
510 ; scrollbarinpm = true
511 ; tilew = 2048
512 ; tileh = 2048
513 ; mustoresize = 256 lsl 20
514 ; checkers = true
515 ; aalevel = 8
516 ; urilauncher =
517 (match platform with
518 | Plinux | Pfreebsd | Pdragonflybsd
519 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
520 | Posx -> "open \"%s\""
521 | Pcygwin -> "cygstart \"%s\""
522 | Punknown -> "echo %s")
523 ; pathlauncher = "lp \"%s\""
524 ; selcmd =
525 (match platform with
526 | Plinux | Pfreebsd | Pdragonflybsd
527 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
528 | Posx -> "pbcopy"
529 | Pcygwin -> "wsel"
530 | Punknown -> "cat")
531 ; colorspace = Rgb
532 ; invert = false
533 ; colorscale = 1.0
534 ; redirectstderr = false
535 ; ghyllscroll = None
536 ; columns = Csingle [||]
537 ; beyecolumns = None
538 ; updatecurs = false
539 ; hfsize = 12
540 ; pgscale = 1.0
541 ; keyhashes =
542 let mk n = (n, Hashtbl.create 1) in
543 [ mk "global"
544 ; mk "info"
545 ; mk "help"
546 ; mk "outline"
547 ; mk "listview"
548 ; mk "birdseye"
549 ; mk "textentry"
550 ; mk "links"
551 ; mk "view"
556 let findkeyhash c name =
557 try List.assoc name c.keyhashes
558 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
561 let conf = { defconf with angle = defconf.angle };;
563 let pgscale h = truncate (float h *. conf.pgscale);;
565 type fontstate =
566 { mutable fontsize : int
567 ; mutable wwidth : float
568 ; mutable maxrows : int
572 let fstate =
573 { fontsize = 14
574 ; wwidth = nan
575 ; maxrows = -1
579 let setfontsize n =
580 fstate.fontsize <- n;
581 fstate.wwidth <- measurestr fstate.fontsize "w";
582 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
585 let geturl s =
586 let colonpos = try String.index s ':' with Not_found -> -1 in
587 let len = String.length s in
588 if colonpos >= 0 && colonpos + 3 < len
589 then (
590 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
591 then
592 let schemestartpos =
593 try String.rindex_from s colonpos ' '
594 with Not_found -> -1
596 let scheme =
597 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
599 match scheme with
600 | "http" | "ftp" | "mailto" ->
601 let epos =
602 try String.index_from s colonpos ' '
603 with Not_found -> len
605 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
606 | _ -> ""
607 else ""
609 else ""
612 let gotouri uri =
613 if String.length conf.urilauncher = 0
614 then print_endline uri
615 else (
616 let url = geturl uri in
617 if String.length url = 0
618 then print_endline uri
619 else
620 let re = Str.regexp "%s" in
621 let command = Str.global_replace re url conf.urilauncher in
622 try popen command []
623 with exn ->
624 Printf.eprintf
625 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
626 flush stderr;
630 let version () =
631 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
632 (platform_to_string platform) Sys.word_size Sys.ocaml_version
635 let makehelp () =
636 let strings = version () :: "" :: Help.keys in
637 Array.of_list (
638 List.map (fun s ->
639 let url = geturl s in
640 if String.length url > 0
641 then (s, 0, Action (fun u -> gotouri url; u))
642 else (s, 0, Noaction)
643 ) strings);
646 let noghyll _ = ();;
647 let firstgeomcmds = "", [];;
649 let state =
650 { sr = Unix.stdin
651 ; sw = Unix.stdin
652 ; wsfd = Unix.stdin
653 ; errfd = None
654 ; stderr = Unix.stderr
655 ; errmsgs = Buffer.create 0
656 ; newerrmsgs = false
657 ; x = 0
658 ; y = 0
659 ; w = 0
660 ; scrollw = 0
661 ; hscrollh = 0
662 ; anchor = emptyanchor
663 ; ranchors = []
664 ; layout = []
665 ; maxy = max_int
666 ; tilelru = Queue.create ()
667 ; pagemap = Hashtbl.create 10
668 ; tilemap = Hashtbl.create 10
669 ; pdims = []
670 ; pagecount = 0
671 ; currently = Idle
672 ; mstate = Mnone
673 ; rects = []
674 ; rects1 = []
675 ; text = ""
676 ; mode = View
677 ; fullscreen = None
678 ; searchpattern = ""
679 ; outlines = [||]
680 ; bookmarks = []
681 ; path = ""
682 ; password = ""
683 ; geomcmds = firstgeomcmds
684 ; hists =
685 { nav = cbnew 10 emptyanchor
686 ; pat = cbnew 10 ""
687 ; pag = cbnew 10 ""
688 ; sel = cbnew 10 ""
690 ; memused = 0
691 ; gen = 0
692 ; throttle = None
693 ; autoscroll = None
694 ; ghyll = noghyll
695 ; help = makehelp ()
696 ; docinfo = []
697 ; texid = None
698 ; prevzoom = 1.0
699 ; progress = -1.0
700 ; uioh = nouioh
701 ; redisplay = true
702 ; mpos = (-1, -1)
703 ; keystate = KSnone
704 ; glinks = false
705 ; prevcolumns = None
709 let vlog fmt =
710 if conf.verbose
711 then
712 Printf.kprintf prerr_endline fmt
713 else
714 Printf.kprintf ignore fmt
717 let launchpath () =
718 if String.length conf.pathlauncher = 0
719 then print_endline state.path
720 else (
721 let re = Str.regexp "%s" in
722 let command = Str.global_replace re state.path conf.pathlauncher in
723 try popen command []
724 with exn ->
725 Printf.eprintf
726 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
727 flush stderr;
731 module Ne = struct
732 type 'a t = | Res of 'a | Exn of exn;;
734 let pipe () =
735 try Res (Unix.pipe ())
736 with exn -> Exn exn
739 let clo fd f =
740 try Unix.close fd
741 with exn -> f (Printexc.to_string exn)
744 let dup fd =
745 try Res (Unix.dup fd)
746 with exn -> Exn exn
749 let dup2 fd1 fd2 =
750 try Res (Unix.dup2 fd1 fd2)
751 with exn -> Exn exn
753 end;;
755 let redirectstderr () =
756 let clofail what errmsg = dolog "failed to close %s: %s" what errmsg in
757 if conf.redirectstderr
758 then
759 match Ne.pipe () with
760 | Ne.Exn exn ->
761 dolog "failed to create stderr redirection pipes: %s"
762 (Printexc.to_string exn)
764 | Ne.Res (r, w) ->
765 begin match Ne.dup Unix.stderr with
766 | Ne.Exn exn ->
767 dolog "failed to dup stderr: %s" (Printexc.to_string exn);
768 Ne.clo r (clofail "pipe/r");
769 Ne.clo w (clofail "pipe/w");
771 | Ne.Res dupstderr ->
772 begin match Ne.dup2 w Unix.stderr with
773 | Ne.Exn exn ->
774 dolog "failed to dup2 to stderr: %s"
775 (Printexc.to_string exn);
776 Ne.clo dupstderr (clofail "stderr duplicate");
777 Ne.clo r (clofail "redir pipe/r");
778 Ne.clo w (clofail "redir pipe/w");
780 | Ne.Res () ->
781 state.stderr <- dupstderr;
782 state.errfd <- Some r;
783 end;
785 else (
786 state.newerrmsgs <- false;
787 begin match state.errfd with
788 | Some fd ->
789 begin match Ne.dup2 state.stderr Unix.stderr with
790 | Ne.Exn exn ->
791 dolog "failed to dup2 original stderr: %s"
792 (Printexc.to_string exn)
793 | Ne.Res () ->
794 Ne.clo fd (clofail "dup of stderr");
795 Unix.dup2 state.stderr Unix.stderr;
796 state.errfd <- None;
797 end;
798 | None -> ()
799 end;
800 prerr_string (Buffer.contents state.errmsgs);
801 flush stderr;
802 Buffer.clear state.errmsgs;
806 module G =
807 struct
808 let postRedisplay who =
809 if conf.verbose
810 then prerr_endline ("redisplay for " ^ who);
811 state.redisplay <- true;
813 end;;
815 let getopaque pageno =
816 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
817 with Not_found -> None
820 let putopaque pageno opaque =
821 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
824 let pagetranslatepoint l x y =
825 let dy = y - l.pagedispy in
826 let y = dy + l.pagey in
827 let dx = x - l.pagedispx in
828 let x = dx + l.pagex in
829 (x, y);
832 let getunder x y =
833 let rec f = function
834 | l :: rest ->
835 begin match getopaque l.pageno with
836 | Some opaque ->
837 let x0 = l.pagedispx in
838 let x1 = x0 + l.pagevw in
839 let y0 = l.pagedispy in
840 let y1 = y0 + l.pagevh in
841 if y >= y0 && y <= y1 && x >= x0 && x <= x1
842 then
843 let px, py = pagetranslatepoint l x y in
844 match whatsunder opaque px py with
845 | Unone -> f rest
846 | under -> under
847 else f rest
848 | _ ->
849 f rest
851 | [] -> Unone
853 f state.layout
856 let showtext c s =
857 state.text <- Printf.sprintf "%c%s" c s;
858 G.postRedisplay "showtext";
861 let undertext = function
862 | Unone -> "none"
863 | Ulinkuri s -> s
864 | Ulinkgoto (pageno, _) -> Printf.sprintf "%s: page %d" state.path (pageno+1)
865 | Utext s -> "font: " ^ s
866 | Uunexpected s -> "unexpected: " ^ s
867 | Ulaunch s -> "launch: " ^ s
868 | Unamed s -> "named: " ^ s
869 | Uremote (filename, pageno) ->
870 Printf.sprintf "%s: page %d" filename (pageno+1)
873 let updateunder x y =
874 match getunder x y with
875 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
876 | Ulinkuri uri ->
877 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
878 Wsi.setcursor Wsi.CURSOR_INFO
879 | Ulinkgoto (pageno, _) ->
880 if conf.underinfo
881 then showtext 'p' ("age: " ^ string_of_int (pageno+1));
882 Wsi.setcursor Wsi.CURSOR_INFO
883 | Utext s ->
884 if conf.underinfo then showtext 'f' ("ont: " ^ s);
885 Wsi.setcursor Wsi.CURSOR_TEXT
886 | Uunexpected s ->
887 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
888 Wsi.setcursor Wsi.CURSOR_INHERIT
889 | Ulaunch s ->
890 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
891 Wsi.setcursor Wsi.CURSOR_INHERIT
892 | Unamed s ->
893 if conf.underinfo then showtext 'n' ("amed: " ^ s);
894 Wsi.setcursor Wsi.CURSOR_INHERIT
895 | Uremote (filename, pageno) ->
896 if conf.underinfo then showtext 'r'
897 (Printf.sprintf "emote: %s (%d)" filename (pageno+1));
898 Wsi.setcursor Wsi.CURSOR_INFO
901 let showlinktype under =
902 if conf.underinfo
903 then
904 match under with
905 | Unone -> ()
906 | under ->
907 let s = undertext under in
908 showtext ' ' s
911 let addchar s c =
912 let b = Buffer.create (String.length s + 1) in
913 Buffer.add_string b s;
914 Buffer.add_char b c;
915 Buffer.contents b;
918 let colorspace_of_string s =
919 match String.lowercase s with
920 | "rgb" -> Rgb
921 | "bgr" -> Bgr
922 | "gray" -> Gray
923 | _ -> failwith "invalid colorspace"
926 let int_of_colorspace = function
927 | Rgb -> 0
928 | Bgr -> 1
929 | Gray -> 2
932 let colorspace_of_int = function
933 | 0 -> Rgb
934 | 1 -> Bgr
935 | 2 -> Gray
936 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
939 let colorspace_to_string = function
940 | Rgb -> "rgb"
941 | Bgr -> "bgr"
942 | Gray -> "gray"
945 let intentry_with_suffix text key =
946 let c =
947 if key >= 32 && key < 127
948 then Char.chr key
949 else '\000'
951 match Char.lowercase c with
952 | '0' .. '9' ->
953 let text = addchar text c in
954 TEcont text
956 | 'k' | 'm' | 'g' ->
957 let text = addchar text c in
958 TEcont text
960 | _ ->
961 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
962 TEcont text
965 let multicolumns_to_string (n, a, b) =
966 if a = 0 && b = 0
967 then Printf.sprintf "%d" n
968 else Printf.sprintf "%d,%d,%d" n a b;
971 let multicolumns_of_string s =
973 (int_of_string s, 0, 0)
974 with _ ->
975 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
978 let readcmd fd =
979 let s = "xxxx" in
980 let n = Unix.read fd s 0 4 in
981 if n != 4 then failwith "incomplete read(len)";
982 let len = 0
983 lor (Char.code s.[0] lsl 24)
984 lor (Char.code s.[1] lsl 16)
985 lor (Char.code s.[2] lsl 8)
986 lor (Char.code s.[3] lsl 0)
988 let s = String.create len in
989 let n = Unix.read fd s 0 len in
990 if n != len then failwith "incomplete read(data)";
994 let btod b = if b then 1 else 0;;
996 let wcmd fmt =
997 let b = Buffer.create 16 in
998 Buffer.add_string b "llll";
999 Printf.kbprintf
1000 (fun b ->
1001 let s = Buffer.contents b in
1002 let n = String.length s in
1003 let len = n - 4 in
1004 (* dolog "wcmd %S" (String.sub s 4 len); *)
1005 s.[0] <- Char.chr ((len lsr 24) land 0xff);
1006 s.[1] <- Char.chr ((len lsr 16) land 0xff);
1007 s.[2] <- Char.chr ((len lsr 8) land 0xff);
1008 s.[3] <- Char.chr (len land 0xff);
1009 let n' = Unix.write state.sw s 0 n in
1010 if n' != n then failwith "write failed";
1011 ) b fmt;
1014 let calcips h =
1015 if conf.presentation
1016 then
1017 let d = conf.winh - h in
1018 max conf.interpagespace ((d + 1) / 2)
1019 else
1020 conf.interpagespace
1023 let calcheight () =
1024 match conf.columns with
1025 | Cmulti ((c, _, _), b) ->
1026 let rec loop y h n =
1027 if n < 0
1028 then loop y h (n+1)
1029 else (
1030 if n = Array.length b
1031 then y + h
1032 else
1033 let (_, _, y', (_, _, h', _)) = b.(n) in
1034 let y = min y y'
1035 and h = max h h' in
1036 loop y h (n+1)
1039 loop max_int 0 (((Array.length b - 1) / c) * c)
1040 | Csingle b ->
1041 if Array.length b > 0
1042 then
1043 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1044 y + h + (if conf.presentation then calcips h else 0)
1045 else 0
1046 | Csplit (_, b) ->
1047 if Array.length b > 0
1048 then
1049 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1050 y + h
1051 else 0
1054 let getpageyh pageno =
1055 let pageno = bound pageno 0 state.pagecount in
1056 match conf.columns with
1057 | Csingle b ->
1058 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1059 let y =
1060 if conf.presentation
1061 then y - calcips h
1062 else y
1064 y, h
1065 | Cmulti (_, b) ->
1066 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1067 y, h
1068 | Csplit (c, b) ->
1069 let n = pageno*c in
1070 let (_, _, y, (_, _, h, _)) = b.(n) in
1071 y, h
1074 let getpagedim pageno =
1075 let rec f ppdim l =
1076 match l with
1077 | (n, _, _, _) as pdim :: rest ->
1078 if n >= pageno
1079 then (if n = pageno then pdim else ppdim)
1080 else f pdim rest
1082 | [] -> ppdim
1084 f (-1, -1, -1, -1) state.pdims
1087 let getpagey pageno = fst (getpageyh pageno);;
1089 let nogeomcmds cmds =
1090 match cmds with
1091 | s, [] -> String.length s = 0
1092 | _ -> false
1095 let layoutN ((columns, coverA, coverB), b) y sh =
1096 let sh = sh - state.hscrollh in
1097 let rec fold accu n =
1098 if n = Array.length b
1099 then accu
1100 else
1101 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1102 if (vy - y) > sh &&
1103 (n = coverA - 1
1104 || n = state.pagecount - coverB
1105 || (n - coverA) mod columns = columns - 1)
1106 then accu
1107 else
1108 let accu =
1109 if vy + h > y
1110 then
1111 let pagey = max 0 (y - vy) in
1112 let pagedispy = if pagey > 0 then 0 else vy - y in
1113 let pagedispx, pagex =
1114 let pdx =
1115 if n = coverA - 1 || n = state.pagecount - coverB
1116 then state.x + (conf.winw - state.scrollw - w) / 2
1117 else dx + xoff + state.x
1119 if pdx < 0
1120 then 0, -pdx
1121 else pdx, 0
1123 let pagevw =
1124 let vw = conf.winw - state.scrollw - pagedispx in
1125 let pw = w - pagex in
1126 min vw pw
1128 let pagevh = min (h - pagey) (sh - pagedispy) in
1129 if pagevw > 0 && pagevh > 0
1130 then
1131 let e =
1132 { pageno = n
1133 ; pagedimno = pdimno
1134 ; pagew = w
1135 ; pageh = h
1136 ; pagex = pagex
1137 ; pagey = pagey
1138 ; pagevw = pagevw
1139 ; pagevh = pagevh
1140 ; pagedispx = pagedispx
1141 ; pagedispy = pagedispy
1142 ; pagecol = 0
1145 e :: accu
1146 else
1147 accu
1148 else
1149 accu
1151 fold accu (n+1)
1153 List.rev (fold [] 0)
1156 let layoutS (columns, b) y sh =
1157 let sh = sh - state.hscrollh in
1158 let rec fold accu n =
1159 if n = Array.length b
1160 then accu
1161 else
1162 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1163 if (vy - y) > sh
1164 then accu
1165 else
1166 let accu =
1167 if vy + pageh > y
1168 then
1169 let x = xoff + state.x in
1170 let pagey = max 0 (y - vy) in
1171 let pagedispy = if pagey > 0 then 0 else vy - y in
1172 let pagedispx, pagex =
1173 if px = 0
1174 then (
1175 if x < 0
1176 then 0, -x
1177 else x, 0
1179 else (
1180 let px = px - x in
1181 if px < 0
1182 then -px, 0
1183 else 0, px
1186 let pagecolw = pagew/columns in
1187 let pagedispx =
1188 if pagecolw < conf.winw
1189 then pagedispx + ((conf.winw - state.scrollw - pagecolw) / 2)
1190 else pagedispx
1192 let pagevw =
1193 let vw = conf.winw - pagedispx - state.scrollw in
1194 let pw = pagew - pagex in
1195 min vw pw
1197 let pagevw = min pagevw pagecolw in
1198 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1199 if pagevw > 0 && pagevh > 0
1200 then
1201 let e =
1202 { pageno = n/columns
1203 ; pagedimno = pdimno
1204 ; pagew = pagew
1205 ; pageh = pageh
1206 ; pagex = pagex
1207 ; pagey = pagey
1208 ; pagevw = pagevw
1209 ; pagevh = pagevh
1210 ; pagedispx = pagedispx
1211 ; pagedispy = pagedispy
1212 ; pagecol = n mod columns
1215 e :: accu
1216 else
1217 accu
1218 else
1219 accu
1221 fold accu (n+1)
1223 List.rev (fold [] 0)
1226 let layout y sh =
1227 if nogeomcmds state.geomcmds
1228 then
1229 match conf.columns with
1230 | Csingle b -> layoutN ((1, 0, 0), b) y sh
1231 | Cmulti c -> layoutN c y sh
1232 | Csplit s -> layoutS s y sh
1233 else []
1236 let clamp incr =
1237 let y = state.y + incr in
1238 let y = max 0 y in
1239 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1243 let itertiles l f =
1244 let tilex = l.pagex mod conf.tilew in
1245 let tiley = l.pagey mod conf.tileh in
1247 let col = l.pagex / conf.tilew in
1248 let row = l.pagey / conf.tileh in
1250 let rec rowloop row y0 dispy h =
1251 if h = 0
1252 then ()
1253 else (
1254 let dh = conf.tileh - y0 in
1255 let dh = min h dh in
1256 let rec colloop col x0 dispx w =
1257 if w = 0
1258 then ()
1259 else (
1260 let dw = conf.tilew - x0 in
1261 let dw = min w dw in
1263 f col row dispx dispy x0 y0 dw dh;
1264 colloop (col+1) 0 (dispx+dw) (w-dw)
1267 colloop col tilex l.pagedispx l.pagevw;
1268 rowloop (row+1) 0 (dispy+dh) (h-dh)
1271 if l.pagevw > 0 && l.pagevh > 0
1272 then rowloop row tiley l.pagedispy l.pagevh;
1275 let gettileopaque l col row =
1276 let key =
1277 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1279 try Some (Hashtbl.find state.tilemap key)
1280 with Not_found -> None
1283 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1284 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1285 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1288 let drawtiles l color =
1289 GlDraw.color color;
1290 let f col row x y tilex tiley w h =
1291 match gettileopaque l col row with
1292 | Some (opaque, _, t) ->
1293 let params = x, y, w, h, tilex, tiley in
1294 if conf.invert
1295 then (
1296 Gl.enable `blend;
1297 GlFunc.blend_func `zero `one_minus_src_color;
1299 drawtile params opaque;
1300 if conf.invert
1301 then Gl.disable `blend;
1302 if conf.debug
1303 then (
1304 let s = Printf.sprintf
1305 "%d[%d,%d] %f sec"
1306 l.pageno col row t
1308 let w = measurestr fstate.fontsize s in
1309 GlMisc.push_attrib [`current];
1310 GlDraw.color (0.0, 0.0, 0.0);
1311 GlDraw.rect
1312 (float (x-2), float (y-2))
1313 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1314 GlDraw.color (1.0, 1.0, 1.0);
1315 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1316 GlMisc.pop_attrib ();
1319 | _ ->
1320 let w =
1321 let lw = conf.winw - state.scrollw - x in
1322 min lw w
1323 and h =
1324 let lh = conf.winh - y in
1325 min lh h
1327 begin match state.texid with
1328 | Some id ->
1329 Gl.enable `texture_2d;
1330 GlTex.bind_texture `texture_2d id;
1331 let x0 = float x
1332 and y0 = float y
1333 and x1 = float (x+w)
1334 and y1 = float (y+h) in
1336 let tw = float w /. 64.0
1337 and th = float h /. 64.0 in
1338 let tx0 = float tilex /. 64.0
1339 and ty0 = float tiley /. 64.0 in
1340 let tx1 = tx0 +. tw
1341 and ty1 = ty0 +. th in
1342 GlDraw.begins `quads;
1343 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1344 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1345 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1346 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1347 GlDraw.ends ();
1349 Gl.disable `texture_2d;
1350 | None ->
1351 GlDraw.color (1.0, 1.0, 1.0);
1352 GlDraw.rect
1353 (float x, float y)
1354 (float (x+w), float (y+h));
1355 end;
1356 if w > 128 && h > fstate.fontsize + 10
1357 then (
1358 GlDraw.color (0.0, 0.0, 0.0);
1359 let c, r =
1360 if conf.verbose
1361 then (col*conf.tilew, row*conf.tileh)
1362 else col, row
1364 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1366 GlDraw.color color;
1368 itertiles l f
1371 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1373 let tilevisible1 l x y =
1374 let ax0 = l.pagex
1375 and ax1 = l.pagex + l.pagevw
1376 and ay0 = l.pagey
1377 and ay1 = l.pagey + l.pagevh in
1379 let bx0 = x
1380 and by0 = y in
1381 let bx1 = min (bx0 + conf.tilew) l.pagew
1382 and by1 = min (by0 + conf.tileh) l.pageh in
1384 let rx0 = max ax0 bx0
1385 and ry0 = max ay0 by0
1386 and rx1 = min ax1 bx1
1387 and ry1 = min ay1 by1 in
1389 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1390 nonemptyintersection
1393 let tilevisible layout n x y =
1394 let rec findpageinlayout m = function
1395 | l :: rest when l.pageno = n ->
1396 tilevisible1 l x y || (
1397 match conf.columns with
1398 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
1399 | _ -> false
1401 | _ :: rest -> findpageinlayout 0 rest
1402 | [] -> false
1404 findpageinlayout 0 layout;
1407 let tileready l x y =
1408 tilevisible1 l x y &&
1409 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1412 let tilepage n p layout =
1413 let rec loop = function
1414 | l :: rest ->
1415 if l.pageno = n
1416 then
1417 let f col row _ _ _ _ _ _ =
1418 if state.currently = Idle
1419 then
1420 match gettileopaque l col row with
1421 | Some _ -> ()
1422 | None ->
1423 let x = col*conf.tilew
1424 and y = row*conf.tileh in
1425 let w =
1426 let w = l.pagew - x in
1427 min w conf.tilew
1429 let h =
1430 let h = l.pageh - y in
1431 min h conf.tileh
1433 wcmd "tile %s %d %d %d %d" p x y w h;
1434 state.currently <-
1435 Tiling (
1436 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1437 conf.tilew, conf.tileh
1440 itertiles l f;
1441 else
1442 loop rest
1444 | [] -> ()
1446 if nogeomcmds state.geomcmds
1447 then loop layout;
1450 let preloadlayout visiblepages =
1451 let presentation = conf.presentation in
1452 let interpagespace = conf.interpagespace in
1453 conf.presentation <- false;
1454 conf.interpagespace <- 0;
1455 let y =
1456 match visiblepages with
1457 | [] -> if state.y >= state.maxy then state.maxy else 0
1458 | l :: _ -> getpagey l.pageno + (l.pagey - min 0 l.pagedispy)
1460 let y = if y < conf.winh then 0 else y - conf.winh in
1461 let h = conf.winh*3 in
1462 let pages = layout y h in
1463 conf.presentation <- presentation;
1464 conf.interpagespace <- interpagespace;
1465 pages;
1468 let load pages =
1469 let rec loop pages =
1470 if state.currently != Idle
1471 then ()
1472 else
1473 match pages with
1474 | l :: rest ->
1475 begin match getopaque l.pageno with
1476 | None ->
1477 wcmd "page %d %d" l.pageno l.pagedimno;
1478 state.currently <- Loading (l, state.gen);
1479 | Some opaque ->
1480 tilepage l.pageno opaque pages;
1481 loop rest
1482 end;
1483 | _ -> ()
1485 if nogeomcmds state.geomcmds
1486 then loop pages
1489 let preload pages =
1490 load pages;
1491 if conf.preload && state.currently = Idle
1492 then load (preloadlayout pages);
1495 let layoutready layout =
1496 let rec fold all ls =
1497 all && match ls with
1498 | l :: rest ->
1499 let seen = ref false in
1500 let allvisible = ref true in
1501 let foo col row _ _ _ _ _ _ =
1502 seen := true;
1503 allvisible := !allvisible &&
1504 begin match gettileopaque l col row with
1505 | Some _ -> true
1506 | None -> false
1509 itertiles l foo;
1510 fold (!seen && !allvisible) rest
1511 | [] -> true
1513 let alltilesvisible = fold true layout in
1514 alltilesvisible;
1517 let gotoy y =
1518 let y = bound y 0 state.maxy in
1519 let y, layout, proceed =
1520 match conf.maxwait with
1521 | Some time when state.ghyll == noghyll ->
1522 begin match state.throttle with
1523 | None ->
1524 let layout = layout y conf.winh in
1525 let ready = layoutready layout in
1526 if not ready
1527 then (
1528 load layout;
1529 state.throttle <- Some (layout, y, now ());
1531 else G.postRedisplay "gotoy showall (None)";
1532 y, layout, ready
1533 | Some (_, _, started) ->
1534 let dt = now () -. started in
1535 if dt > time
1536 then (
1537 state.throttle <- None;
1538 let layout = layout y conf.winh in
1539 load layout;
1540 G.postRedisplay "maxwait";
1541 y, layout, true
1543 else -1, [], false
1546 | _ ->
1547 let layout = layout y conf.winh in
1548 if true || layoutready layout
1549 then G.postRedisplay "gotoy ready";
1550 y, layout, true
1552 if proceed
1553 then (
1554 state.y <- y;
1555 state.layout <- layout;
1556 begin match state.mode with
1557 | LinkNav (Ltexact (pageno, linkno)) ->
1558 let rec loop = function
1559 | [] ->
1560 state.mode <- LinkNav (Ltgendir 0)
1561 | l :: _ when l.pageno = pageno ->
1562 begin match getopaque pageno with
1563 | None ->
1564 state.mode <- LinkNav (Ltgendir 0)
1565 | Some opaque ->
1566 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1567 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1568 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1569 then state.mode <- LinkNav (Ltgendir 0)
1571 | _ :: rest -> loop rest
1573 loop layout
1574 | _ -> ()
1575 end;
1576 begin match state.mode with
1577 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1578 if not (pagevisible layout pageno)
1579 then (
1580 match state.layout with
1581 | [] -> ()
1582 | l :: _ ->
1583 state.mode <- Birdseye (
1584 conf, leftx, l.pageno, hooverpageno, anchor
1587 | LinkNav (Ltgendir dir as lt) ->
1588 let linknav =
1589 let rec loop = function
1590 | [] -> lt
1591 | l :: rest ->
1592 match getopaque l.pageno with
1593 | None -> loop rest
1594 | Some opaque ->
1595 let link =
1596 let ld =
1597 if dir = 0
1598 then LDfirstvisible (l.pagex, l.pagey, dir)
1599 else (
1600 if dir > 0 then LDfirst else LDlast
1603 findlink opaque ld
1605 match link with
1606 | Lnotfound -> loop rest
1607 | Lfound n ->
1608 showlinktype (getlink opaque n);
1609 Ltexact (l.pageno, n)
1611 loop state.layout
1613 state.mode <- LinkNav linknav
1614 | _ -> ()
1615 end;
1616 preload layout;
1618 state.ghyll <- noghyll;
1619 if conf.updatecurs
1620 then (
1621 let mx, my = state.mpos in
1622 updateunder mx my;
1626 let conttiling pageno opaque =
1627 tilepage pageno opaque
1628 (if conf.preload then preloadlayout state.layout else state.layout)
1631 let gotoy_and_clear_text y =
1632 if not conf.verbose then state.text <- "";
1633 gotoy y;
1636 let getanchor1 l =
1637 let top =
1638 let coloff = l.pagecol * l.pageh in
1639 float (l.pagey + coloff) /. float l.pageh
1641 let dtop =
1642 if l.pagedispy = 0
1643 then
1645 else
1646 if conf.presentation
1647 then float l.pagedispy /. float (calcips l.pageh)
1648 else float l.pagedispy /. float conf.interpagespace
1650 (l.pageno, top, dtop)
1653 let getanchor () =
1654 match state.layout with
1655 | [] -> emptyanchor
1656 | l :: _ -> getanchor1 l
1659 let getanchory (n, top, dtop) =
1660 let y, h = getpageyh n in
1661 if conf.presentation
1662 then
1663 let ips = calcips h in
1664 y + truncate (top*.float h -. dtop*.float ips) + ips;
1665 else
1666 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
1669 let gotoanchor anchor =
1670 gotoy (getanchory anchor);
1673 let addnav () =
1674 cbput state.hists.nav (getanchor ());
1677 let getnav dir =
1678 let anchor = cbgetc state.hists.nav dir in
1679 getanchory anchor;
1682 let gotoghyll y =
1683 let scroll f n a b =
1684 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1685 let snake f a b =
1686 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1687 if f < a
1688 then s (float f /. float a)
1689 else (
1690 if f > b
1691 then 1.0 -. s ((float (f-b) /. float (n-b)))
1692 else 1.0
1695 snake f a b
1696 and summa f n a b =
1697 (* courtesy:
1698 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1699 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1700 let iv1 = iv f in
1701 let ins = float a *. iv1
1702 and outs = float (n-b) *. iv1 in
1703 let ones = b - a in
1704 ins +. outs +. float ones
1706 let rec set (_N, _A, _B) y sy =
1707 let sum = summa 1.0 _N _A _B in
1708 let dy = float (y - sy) in
1709 state.ghyll <- (
1710 let rec gf n y1 o =
1711 if n >= _N
1712 then state.ghyll <- noghyll
1713 else
1714 let go n =
1715 let s = scroll n _N _A _B in
1716 let y1 = y1 +. ((s *. dy) /. sum) in
1717 gotoy_and_clear_text (truncate y1);
1718 state.ghyll <- gf (n+1) y1;
1720 match o with
1721 | None -> go n
1722 | Some y' -> set (_N/2, 0, 0) y' state.y
1724 gf 0 (float state.y)
1727 match conf.ghyllscroll with
1728 | None ->
1729 gotoy_and_clear_text y
1730 | Some nab ->
1731 if state.ghyll == noghyll
1732 then set nab y state.y
1733 else state.ghyll (Some y)
1736 let gotopage n top =
1737 let y, h = getpageyh n in
1738 let y = y + (truncate (top *. float h)) in
1739 gotoghyll y
1742 let gotopage1 n top =
1743 let y = getpagey n in
1744 let y = y + top in
1745 gotoghyll y
1748 let invalidate s f =
1749 state.layout <- [];
1750 state.pdims <- [];
1751 state.rects <- [];
1752 state.rects1 <- [];
1753 match state.geomcmds with
1754 | ps, [] when String.length ps = 0 ->
1755 f ();
1756 state.geomcmds <- s, [];
1758 | ps, [] ->
1759 state.geomcmds <- ps, [s, f];
1761 | ps, (s', _) :: rest when s' = s ->
1762 state.geomcmds <- ps, ((s, f) :: rest);
1764 | ps, cmds ->
1765 state.geomcmds <- ps, ((s, f) :: cmds);
1768 let opendoc path password =
1769 state.path <- path;
1770 state.password <- password;
1771 state.gen <- state.gen + 1;
1772 state.docinfo <- [];
1774 setaalevel conf.aalevel;
1775 Wsi.settitle ("llpp " ^ Filename.basename path);
1776 wcmd "open %s\000%s\000" path password;
1777 invalidate "reqlayout"
1778 (fun () ->
1779 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1782 let scalecolor c =
1783 let c = c *. conf.colorscale in
1784 (c, c, c);
1787 let scalecolor2 (r, g, b) =
1788 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1791 let docolumns = function
1792 | Csingle _ ->
1793 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1794 let rec loop pageno pdimno pdim y ph pdims =
1795 if pageno = state.pagecount
1796 then ()
1797 else
1798 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1799 match pdims with
1800 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1801 pdimno+1, pdim, rest
1802 | _ ->
1803 pdimno, pdim, pdims
1805 let x = max 0 (((conf.winw - state.scrollw - w) / 2) - xoff) in
1806 let y = y +
1807 (if conf.presentation
1808 then (if pageno = 0 then calcips h else calcips ph + calcips h)
1809 else (if pageno = 0 then 0 else calcips h)
1812 a.(pageno) <- (pdimno, x, y, pdim);
1813 loop (pageno+1) pdimno pdim (y + h) h pdims
1815 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
1816 conf.columns <- Csingle a;
1818 | Cmulti ((columns, coverA, coverB), _) ->
1819 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1820 let rec loop pageno pdimno pdim x y rowh pdims =
1821 let rec fixrow m = if m = pageno then () else
1822 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1823 if h < rowh
1824 then (
1825 let y = y + (rowh - h) / 2 in
1826 a.(m) <- (pdimno, x, y, pdim);
1828 fixrow (m+1)
1830 if pageno = state.pagecount
1831 then fixrow (((pageno - 1) / columns) * columns)
1832 else
1833 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1834 match pdims with
1835 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1836 pdimno+1, pdim, rest
1837 | _ ->
1838 pdimno, pdim, pdims
1840 let x, y, rowh' =
1841 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1842 then (
1843 (conf.winw - state.scrollw - w) / 2,
1844 y + rowh + conf.interpagespace, h
1846 else (
1847 if (pageno - coverA) mod columns = 0
1848 then (
1849 (conf.winw - state.scrollw - state.w) / 2,
1850 y + rowh + (if pageno = 0 then 0 else conf.interpagespace), h
1852 else x, y, max rowh h
1855 if pageno > 1 && (pageno - coverA) mod columns = 0
1856 then fixrow (pageno - columns);
1857 a.(pageno) <- (pdimno, x, y, pdim);
1858 let x = x + w + xoff*2 + conf.interpagespace in
1859 loop (pageno+1) pdimno pdim x y rowh' pdims
1861 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1862 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1864 | Csplit (c, _) ->
1865 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1866 let rec loop pageno pdimno pdim y pdims =
1867 if pageno = state.pagecount
1868 then ()
1869 else
1870 let pdimno, ((_, w, h, _) as pdim), pdims =
1871 match pdims with
1872 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1873 pdimno+1, pdim, rest
1874 | _ ->
1875 pdimno, pdim, pdims
1877 let cw = w / c in
1878 let rec loop1 n x y =
1879 if n = c then y else (
1880 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1881 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1884 let y = loop1 0 0 y in
1885 loop (pageno+1) pdimno pdim y pdims
1887 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1888 conf.columns <- Csplit (c, a);
1891 let represent () =
1892 docolumns conf.columns;
1893 state.maxy <- calcheight ();
1894 state.hscrollh <-
1895 if state.w <= conf.winw - state.scrollw
1896 then 0
1897 else state.scrollw
1899 match state.mode with
1900 | Birdseye (_, _, pageno, _, _) ->
1901 let y, h = getpageyh pageno in
1902 let top = (conf.winh - h) / 2 in
1903 gotoy (max 0 (y - top))
1904 | _ -> gotoanchor state.anchor
1907 let reshape w h =
1908 GlDraw.viewport 0 0 w h;
1909 let firsttime = state.geomcmds == firstgeomcmds in
1910 if not firsttime && nogeomcmds state.geomcmds
1911 then state.anchor <- getanchor ();
1913 conf.winw <- w;
1914 let w = truncate (float w *. conf.zoom) - state.scrollw in
1915 let w = max w 2 in
1916 conf.winh <- h;
1917 setfontsize fstate.fontsize;
1918 GlMat.mode `modelview;
1919 GlMat.load_identity ();
1921 GlMat.mode `projection;
1922 GlMat.load_identity ();
1923 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1924 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1925 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1927 let relx =
1928 if conf.zoom <= 1.0
1929 then 0.0
1930 else float state.x /. float state.w
1932 invalidate "geometry"
1933 (fun () ->
1934 state.w <- w;
1935 if not firsttime
1936 then state.x <- truncate (relx *. float w);
1937 let w =
1938 match conf.columns with
1939 | Csingle _ -> w
1940 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1941 | Csplit (c, _) -> w * c
1943 wcmd "geometry %d %d" w h);
1946 let enttext () =
1947 let len = String.length state.text in
1948 let drawstring s =
1949 let hscrollh =
1950 match state.mode with
1951 | Textentry _
1952 | View ->
1953 let h, _, _ = state.uioh#scrollpw in
1955 | _ -> 0
1957 let rect x w =
1958 GlDraw.rect
1959 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1960 (x+.w, float (conf.winh - hscrollh))
1963 let w = float (conf.winw - state.scrollw - 1) in
1964 if state.progress >= 0.0 && state.progress < 1.0
1965 then (
1966 GlDraw.color (0.3, 0.3, 0.3);
1967 let w1 = w *. state.progress in
1968 rect 0.0 w1;
1969 GlDraw.color (0.0, 0.0, 0.0);
1970 rect w1 (w-.w1)
1972 else (
1973 GlDraw.color (0.0, 0.0, 0.0);
1974 rect 0.0 w;
1977 GlDraw.color (1.0, 1.0, 1.0);
1978 drawstring fstate.fontsize
1979 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1981 let s =
1982 match state.mode with
1983 | Textentry ((prefix, text, _, _, _, _), _) ->
1984 let s =
1985 if len > 0
1986 then
1987 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1988 else
1989 Printf.sprintf "%s%s_" prefix text
1993 | _ -> state.text
1995 let s =
1996 if state.newerrmsgs
1997 then (
1998 if not (istextentry state.mode)
1999 then
2000 let s1 = "(press 'e' to review error messasges)" in
2001 if String.length s > 0 then s ^ " " ^ s1 else s1
2002 else s
2004 else s
2006 if String.length s > 0
2007 then drawstring s
2010 let gctiles () =
2011 let len = Queue.length state.tilelru in
2012 let layout = lazy (
2013 match state.throttle with
2014 | None ->
2015 if conf.preload
2016 then preloadlayout state.layout
2017 else state.layout
2018 | Some (layout, _, _) ->
2019 layout
2020 ) in
2021 let rec loop qpos =
2022 if state.memused <= conf.memlimit
2023 then ()
2024 else (
2025 if qpos < len
2026 then
2027 let (k, p, s) as lruitem = Queue.pop state.tilelru in
2028 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
2029 let (_, pw, ph, _) = getpagedim n in
2031 gen = state.gen
2032 && colorspace = conf.colorspace
2033 && angle = conf.angle
2034 && pagew = pw
2035 && pageh = ph
2036 && (
2037 let x = col*conf.tilew
2038 and y = row*conf.tileh in
2039 tilevisible (Lazy.force_val layout) n x y
2041 then Queue.push lruitem state.tilelru
2042 else (
2043 wcmd "freetile %s" p;
2044 state.memused <- state.memused - s;
2045 state.uioh#infochanged Memused;
2046 Hashtbl.remove state.tilemap k;
2048 loop (qpos+1)
2051 loop 0
2054 let flushtiles () =
2055 Queue.iter (fun (k, p, s) ->
2056 wcmd "freetile %s" p;
2057 state.memused <- state.memused - s;
2058 state.uioh#infochanged Memused;
2059 Hashtbl.remove state.tilemap k;
2060 ) state.tilelru;
2061 Queue.clear state.tilelru;
2062 load state.layout;
2065 let logcurrently = function
2066 | Idle -> dolog "Idle"
2067 | Loading (l, gen) ->
2068 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2069 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2070 dolog
2071 "Tiling %d[%d,%d] page=%s cs=%s angle"
2072 l.pageno col row pageopaque
2073 (colorspace_to_string colorspace)
2075 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2076 angle gen conf.angle state.gen
2077 tilew tileh
2078 conf.tilew conf.tileh
2080 | Outlining _ ->
2081 dolog "outlining"
2084 let act cmds =
2085 (* dolog "%S" cmds; *)
2086 let op, args =
2087 let spacepos =
2088 try String.index cmds ' '
2089 with Not_found -> -1
2091 if spacepos = -1
2092 then cmds, ""
2093 else
2094 let l = String.length cmds in
2095 let op = String.sub cmds 0 spacepos in
2096 op, begin
2097 if l - spacepos < 2 then ""
2098 else String.sub cmds (spacepos+1) (l-spacepos-1)
2101 match op with
2102 | "clear" ->
2103 state.uioh#infochanged Pdim;
2104 state.pdims <- [];
2106 | "clearrects" ->
2107 state.rects <- state.rects1;
2108 G.postRedisplay "clearrects";
2110 | "continue" ->
2111 let n =
2112 try Scanf.sscanf args "%u" (fun n -> n)
2113 with exn ->
2114 dolog "error processing 'continue' %S: %s"
2115 cmds (Printexc.to_string exn);
2116 exit 1;
2118 state.pagecount <- n;
2119 begin match state.currently with
2120 | Outlining l ->
2121 state.currently <- Idle;
2122 state.outlines <- Array.of_list (List.rev l)
2123 | _ -> ()
2124 end;
2126 let cur, cmds = state.geomcmds in
2127 if String.length cur = 0
2128 then failwith "umpossible";
2130 begin match List.rev cmds with
2131 | [] ->
2132 state.geomcmds <- "", [];
2133 represent ();
2134 | (s, f) :: rest ->
2135 f ();
2136 state.geomcmds <- s, List.rev rest;
2137 end;
2138 if conf.maxwait = None
2139 then G.postRedisplay "continue";
2141 | "title" ->
2142 Wsi.settitle args
2144 | "msg" ->
2145 showtext ' ' args
2147 | "vmsg" ->
2148 if conf.verbose
2149 then showtext ' ' args
2151 | "progress" ->
2152 let progress, text =
2154 Scanf.sscanf args "%f %n"
2155 (fun f pos ->
2156 f, String.sub args pos (String.length args - pos))
2157 with exn ->
2158 dolog "error processing 'progress' %S: %s"
2159 cmds (Printexc.to_string exn);
2160 exit 1;
2162 state.text <- text;
2163 state.progress <- progress;
2164 G.postRedisplay "progress"
2166 | "firstmatch" ->
2167 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2169 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2170 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2171 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2172 with exn ->
2173 dolog "error processing 'firstmatch' %S: %s"
2174 cmds (Printexc.to_string exn);
2175 exit 1;
2177 let y = (getpagey pageno) + truncate y0 in
2178 addnav ();
2179 gotoy y;
2180 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2182 | "match" ->
2183 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2185 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2186 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2187 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2188 with exn ->
2189 dolog "error processing 'match' %S: %s"
2190 cmds (Printexc.to_string exn);
2191 exit 1;
2193 state.rects1 <-
2194 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2196 | "page" ->
2197 let pageopaque, t =
2199 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2200 with exn ->
2201 dolog "error processing 'page' %S: %s"
2202 cmds (Printexc.to_string exn);
2203 exit 1;
2205 begin match state.currently with
2206 | Loading (l, gen) ->
2207 vlog "page %d took %f sec" l.pageno t;
2208 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2209 begin match state.throttle with
2210 | None ->
2211 let preloadedpages =
2212 if conf.preload
2213 then preloadlayout state.layout
2214 else state.layout
2216 let evict () =
2217 let module IntSet =
2218 Set.Make (struct type t = int let compare = (-) end) in
2219 let set =
2220 List.fold_left (fun s l -> IntSet.add l.pageno s)
2221 IntSet.empty preloadedpages
2223 let evictedpages =
2224 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2225 if not (IntSet.mem pageno set)
2226 then (
2227 wcmd "freepage %s" opaque;
2228 key :: accu
2230 else accu
2231 ) state.pagemap []
2233 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2235 evict ();
2236 state.currently <- Idle;
2237 if gen = state.gen
2238 then (
2239 tilepage l.pageno pageopaque state.layout;
2240 load state.layout;
2241 load preloadedpages;
2242 if pagevisible state.layout l.pageno
2243 && layoutready state.layout
2244 then G.postRedisplay "page";
2247 | Some (layout, _, _) ->
2248 state.currently <- Idle;
2249 tilepage l.pageno pageopaque layout;
2250 load state.layout
2251 end;
2253 | _ ->
2254 dolog "Inconsistent loading state";
2255 logcurrently state.currently;
2256 exit 1
2259 | "tile" ->
2260 let (x, y, opaque, size, t) =
2262 Scanf.sscanf args "%u %u %s %u %f"
2263 (fun x y p size t -> (x, y, p, size, t))
2264 with exn ->
2265 dolog "error processing 'tile' %S: %s"
2266 cmds (Printexc.to_string exn);
2267 exit 1;
2269 begin match state.currently with
2270 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2271 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2273 if tilew != conf.tilew || tileh != conf.tileh
2274 then (
2275 wcmd "freetile %s" opaque;
2276 state.currently <- Idle;
2277 load state.layout;
2279 else (
2280 puttileopaque l col row gen cs angle opaque size t;
2281 state.memused <- state.memused + size;
2282 state.uioh#infochanged Memused;
2283 gctiles ();
2284 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2285 opaque, size) state.tilelru;
2287 let layout =
2288 match state.throttle with
2289 | None -> state.layout
2290 | Some (layout, _, _) -> layout
2293 state.currently <- Idle;
2294 if gen = state.gen
2295 && conf.colorspace = cs
2296 && conf.angle = angle
2297 && tilevisible layout l.pageno x y
2298 then conttiling l.pageno pageopaque;
2300 begin match state.throttle with
2301 | None ->
2302 preload state.layout;
2303 if gen = state.gen
2304 && conf.colorspace = cs
2305 && conf.angle = angle
2306 && tilevisible state.layout l.pageno x y
2307 then G.postRedisplay "tile nothrottle";
2309 | Some (layout, y, _) ->
2310 let ready = layoutready layout in
2311 if ready
2312 then (
2313 state.y <- y;
2314 state.layout <- layout;
2315 state.throttle <- None;
2316 G.postRedisplay "throttle";
2318 else load layout;
2319 end;
2322 | _ ->
2323 dolog "Inconsistent tiling state";
2324 logcurrently state.currently;
2325 exit 1
2328 | "pdim" ->
2329 let pdim =
2331 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2332 with exn ->
2333 dolog "error processing 'pdim' %S: %s"
2334 cmds (Printexc.to_string exn);
2335 exit 1;
2337 state.uioh#infochanged Pdim;
2338 state.pdims <- pdim :: state.pdims
2340 | "o" ->
2341 let (l, n, t, h, pos) =
2343 Scanf.sscanf args "%u %u %d %u %n"
2344 (fun l n t h pos -> l, n, t, h, pos)
2345 with exn ->
2346 dolog "error processing 'o' %S: %s"
2347 cmds (Printexc.to_string exn);
2348 exit 1;
2350 let s = String.sub args pos (String.length args - pos) in
2351 let outline = (s, l, (n, float t /. float h, 0.0)) in
2352 begin match state.currently with
2353 | Outlining outlines ->
2354 state.currently <- Outlining (outline :: outlines)
2355 | Idle ->
2356 state.currently <- Outlining [outline]
2357 | currently ->
2358 dolog "invalid outlining state";
2359 logcurrently currently
2362 | "info" ->
2363 state.docinfo <- (1, args) :: state.docinfo
2365 | "infoend" ->
2366 state.uioh#infochanged Docinfo;
2367 state.docinfo <- List.rev state.docinfo
2369 | _ ->
2370 dolog "unknown cmd `%S'" cmds
2373 let onhist cb =
2374 let rc = cb.rc in
2375 let action = function
2376 | HCprev -> cbget cb ~-1
2377 | HCnext -> cbget cb 1
2378 | HCfirst -> cbget cb ~-(cb.rc)
2379 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2380 and cancel () = cb.rc <- rc
2381 in (action, cancel)
2384 let search pattern forward =
2385 if String.length pattern > 0
2386 then
2387 let pn, py =
2388 match state.layout with
2389 | [] -> 0, 0
2390 | l :: _ ->
2391 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2393 wcmd "search %d %d %d %d,%s\000"
2394 (btod conf.icase) pn py (btod forward) pattern;
2397 let intentry text key =
2398 let c =
2399 if key >= 32 && key < 127
2400 then Char.chr key
2401 else '\000'
2403 match c with
2404 | '0' .. '9' ->
2405 let text = addchar text c in
2406 TEcont text
2408 | _ ->
2409 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2410 TEcont text
2413 let linknentry text key =
2414 let c =
2415 if key >= 32 && key < 127
2416 then Char.chr key
2417 else '\000'
2419 match c with
2420 | 'a' .. 'z' ->
2421 let text = addchar text c in
2422 TEcont text
2424 | _ ->
2425 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2426 TEcont text
2429 let linkndone f s =
2430 if String.length s > 0
2431 then (
2432 let n =
2433 let l = String.length s in
2434 let rec loop pos n = if pos = l then n else
2435 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
2436 loop (pos+1) (n*26 + m)
2437 in loop 0 0
2439 let rec loop n = function
2440 | [] -> ()
2441 | l :: rest ->
2442 match getopaque l.pageno with
2443 | None -> loop n rest
2444 | Some opaque ->
2445 let m = getlinkcount opaque in
2446 if n < m
2447 then (
2448 let under = getlink opaque n in
2449 f under
2451 else loop (n-m) rest
2453 loop n state.layout;
2457 let textentry text key =
2458 if key land 0xff00 = 0xff00
2459 then TEcont text
2460 else TEcont (text ^ Wsi.toutf8 key)
2463 let reqlayout angle proportional =
2464 match state.throttle with
2465 | None ->
2466 if nogeomcmds state.geomcmds
2467 then state.anchor <- getanchor ();
2468 conf.angle <- angle mod 360;
2469 if conf.angle != 0
2470 then (
2471 match state.mode with
2472 | LinkNav _ -> state.mode <- View
2473 | _ -> ()
2475 conf.proportional <- proportional;
2476 invalidate "reqlayout"
2477 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2478 | _ -> ()
2481 let settrim trimmargins trimfuzz =
2482 if nogeomcmds state.geomcmds
2483 then state.anchor <- getanchor ();
2484 conf.trimmargins <- trimmargins;
2485 conf.trimfuzz <- trimfuzz;
2486 let x0, y0, x1, y1 = trimfuzz in
2487 invalidate "settrim"
2488 (fun () ->
2489 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2490 Hashtbl.iter (fun _ opaque ->
2491 wcmd "freepage %s" opaque;
2492 ) state.pagemap;
2493 Hashtbl.clear state.pagemap;
2496 let setzoom zoom =
2497 match state.throttle with
2498 | None ->
2499 let zoom = max 0.01 zoom in
2500 if zoom <> conf.zoom
2501 then (
2502 state.prevzoom <- conf.zoom;
2503 conf.zoom <- zoom;
2504 reshape conf.winw conf.winh;
2505 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2508 | Some (layout, y, started) ->
2509 let time =
2510 match conf.maxwait with
2511 | None -> 0.0
2512 | Some t -> t
2514 let dt = now () -. started in
2515 if dt > time
2516 then (
2517 state.y <- y;
2518 load layout;
2522 let setcolumns mode columns coverA coverB =
2523 state.prevcolumns <- Some (conf.columns, conf.zoom);
2524 if columns < 0
2525 then (
2526 if isbirdseye mode
2527 then showtext '!' "split mode doesn't work in bird's eye"
2528 else (
2529 conf.columns <- Csplit (-columns, [||]);
2530 state.x <- 0;
2531 conf.zoom <- 1.0;
2534 else (
2535 if columns < 2
2536 then (
2537 conf.columns <- Csingle [||];
2538 state.x <- 0;
2539 setzoom 1.0;
2541 else (
2542 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2543 conf.zoom <- 1.0;
2546 reshape conf.winw conf.winh;
2549 let enterbirdseye () =
2550 let zoom = float conf.thumbw /. float conf.winw in
2551 let birdseyepageno =
2552 let cy = conf.winh / 2 in
2553 let fold = function
2554 | [] -> 0
2555 | l :: rest ->
2556 let rec fold best = function
2557 | [] -> best.pageno
2558 | l :: rest ->
2559 let d = cy - (l.pagedispy + l.pagevh/2)
2560 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2561 if abs d < abs dbest
2562 then fold l rest
2563 else best.pageno
2564 in fold l rest
2566 fold state.layout
2568 state.mode <- Birdseye (
2569 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2571 conf.zoom <- zoom;
2572 conf.presentation <- false;
2573 conf.interpagespace <- 10;
2574 conf.hlinks <- false;
2575 state.x <- 0;
2576 state.mstate <- Mnone;
2577 conf.maxwait <- None;
2578 conf.columns <- (
2579 match conf.beyecolumns with
2580 | Some c ->
2581 conf.zoom <- 1.0;
2582 Cmulti ((c, 0, 0), [||])
2583 | None -> Csingle [||]
2585 Wsi.setcursor Wsi.CURSOR_INHERIT;
2586 if conf.verbose
2587 then
2588 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2589 (100.0*.zoom)
2590 else
2591 state.text <- ""
2593 reshape conf.winw conf.winh;
2596 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2597 state.mode <- View;
2598 conf.zoom <- c.zoom;
2599 conf.presentation <- c.presentation;
2600 conf.interpagespace <- c.interpagespace;
2601 conf.maxwait <- c.maxwait;
2602 conf.hlinks <- c.hlinks;
2603 conf.beyecolumns <- (
2604 match conf.columns with
2605 | Cmulti ((c, _, _), _) -> Some c
2606 | Csingle _ -> None
2607 | Csplit _ -> failwith "leaving bird's eye split mode"
2609 conf.columns <- (
2610 match c.columns with
2611 | Cmulti (c, _) -> Cmulti (c, [||])
2612 | Csingle _ -> Csingle [||]
2613 | Csplit (c, _) -> Csplit (c, [||])
2615 state.x <- leftx;
2616 if conf.verbose
2617 then
2618 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2619 (100.0*.conf.zoom)
2621 reshape conf.winw conf.winh;
2622 state.anchor <- if goback then anchor else (pageno, 0.0, 0.0);
2625 let togglebirdseye () =
2626 match state.mode with
2627 | Birdseye vals -> leavebirdseye vals true
2628 | View -> enterbirdseye ()
2629 | _ -> ()
2632 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2633 let pageno = max 0 (pageno - incr) in
2634 let rec loop = function
2635 | [] -> gotopage1 pageno 0
2636 | l :: _ when l.pageno = pageno ->
2637 if l.pagedispy >= 0 && l.pagey = 0
2638 then G.postRedisplay "upbirdseye"
2639 else gotopage1 pageno 0
2640 | _ :: rest -> loop rest
2642 loop state.layout;
2643 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2646 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2647 let pageno = min (state.pagecount - 1) (pageno + incr) in
2648 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2649 let rec loop = function
2650 | [] ->
2651 let y, h = getpageyh pageno in
2652 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2653 gotoy (clamp dy)
2654 | l :: _ when l.pageno = pageno ->
2655 if l.pagevh != l.pageh
2656 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2657 else G.postRedisplay "downbirdseye"
2658 | _ :: rest -> loop rest
2660 loop state.layout
2663 let optentry mode _ key =
2664 let btos b = if b then "on" else "off" in
2665 if key >= 32 && key < 127
2666 then
2667 let c = Char.chr key in
2668 match c with
2669 | 's' ->
2670 let ondone s =
2671 try conf.scrollstep <- int_of_string s with exc ->
2672 state.text <- Printf.sprintf "bad integer `%s': %s"
2673 s (Printexc.to_string exc)
2675 TEswitch ("scroll step: ", "", None, intentry, ondone, true)
2677 | 'A' ->
2678 let ondone s =
2680 conf.autoscrollstep <- int_of_string s;
2681 if state.autoscroll <> None
2682 then state.autoscroll <- Some conf.autoscrollstep
2683 with exc ->
2684 state.text <- Printf.sprintf "bad integer `%s': %s"
2685 s (Printexc.to_string exc)
2687 TEswitch ("auto scroll step: ", "", None, intentry, ondone, true)
2689 | 'C' ->
2690 let ondone s =
2692 let n, a, b = multicolumns_of_string s in
2693 setcolumns mode n a b;
2694 with exc ->
2695 state.text <- Printf.sprintf "bad columns `%s': %s"
2696 s (Printexc.to_string exc)
2698 TEswitch ("columns: ", "", None, textentry, ondone, true)
2700 | 'Z' ->
2701 let ondone s =
2703 let zoom = float (int_of_string s) /. 100.0 in
2704 setzoom zoom
2705 with exc ->
2706 state.text <- Printf.sprintf "bad integer `%s': %s"
2707 s (Printexc.to_string exc)
2709 TEswitch ("zoom: ", "", None, intentry, ondone, true)
2711 | 't' ->
2712 let ondone s =
2714 conf.thumbw <- bound (int_of_string s) 2 4096;
2715 state.text <-
2716 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2717 begin match mode with
2718 | Birdseye beye ->
2719 leavebirdseye beye false;
2720 enterbirdseye ();
2721 | _ -> ();
2723 with exc ->
2724 state.text <- Printf.sprintf "bad integer `%s': %s"
2725 s (Printexc.to_string exc)
2727 TEswitch ("thumbnail width: ", "", None, intentry, ondone, true)
2729 | 'R' ->
2730 let ondone s =
2731 match try
2732 Some (int_of_string s)
2733 with exc ->
2734 state.text <- Printf.sprintf "bad integer `%s': %s"
2735 s (Printexc.to_string exc);
2736 None
2737 with
2738 | Some angle -> reqlayout angle conf.proportional
2739 | None -> ()
2741 TEswitch ("rotation: ", "", None, intentry, ondone, true)
2743 | 'i' ->
2744 conf.icase <- not conf.icase;
2745 TEdone ("case insensitive search " ^ (btos conf.icase))
2747 | 'p' ->
2748 conf.preload <- not conf.preload;
2749 gotoy state.y;
2750 TEdone ("preload " ^ (btos conf.preload))
2752 | 'v' ->
2753 conf.verbose <- not conf.verbose;
2754 TEdone ("verbose " ^ (btos conf.verbose))
2756 | 'd' ->
2757 conf.debug <- not conf.debug;
2758 TEdone ("debug " ^ (btos conf.debug))
2760 | 'h' ->
2761 conf.maxhfit <- not conf.maxhfit;
2762 state.maxy <- calcheight ();
2763 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2765 | 'c' ->
2766 conf.crophack <- not conf.crophack;
2767 TEdone ("crophack " ^ btos conf.crophack)
2769 | 'a' ->
2770 let s =
2771 match conf.maxwait with
2772 | None ->
2773 conf.maxwait <- Some infinity;
2774 "always wait for page to complete"
2775 | Some _ ->
2776 conf.maxwait <- None;
2777 "show placeholder if page is not ready"
2779 TEdone s
2781 | 'f' ->
2782 conf.underinfo <- not conf.underinfo;
2783 TEdone ("underinfo " ^ btos conf.underinfo)
2785 | 'P' ->
2786 conf.savebmarks <- not conf.savebmarks;
2787 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2789 | 'S' ->
2790 let ondone s =
2792 let pageno, py =
2793 match state.layout with
2794 | [] -> 0, 0
2795 | l :: _ ->
2796 l.pageno, l.pagey
2798 conf.interpagespace <- int_of_string s;
2799 docolumns conf.columns;
2800 state.maxy <- calcheight ();
2801 let y = getpagey pageno in
2802 gotoy (y + py)
2803 with exc ->
2804 state.text <- Printf.sprintf "bad integer `%s': %s"
2805 s (Printexc.to_string exc)
2807 TEswitch ("vertical margin: ", "", None, intentry, ondone, true)
2809 | 'l' ->
2810 reqlayout conf.angle (not conf.proportional);
2811 TEdone ("proportional display " ^ btos conf.proportional)
2813 | 'T' ->
2814 settrim (not conf.trimmargins) conf.trimfuzz;
2815 TEdone ("trim margins " ^ btos conf.trimmargins)
2817 | 'I' ->
2818 conf.invert <- not conf.invert;
2819 TEdone ("invert colors " ^ btos conf.invert)
2821 | 'x' ->
2822 let ondone s =
2823 cbput state.hists.sel s;
2824 conf.selcmd <- s;
2826 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2827 textentry, ondone, true)
2829 | _ ->
2830 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2831 TEstop
2832 else
2833 TEcont state.text
2836 class type lvsource = object
2837 method getitemcount : int
2838 method getitem : int -> (string * int)
2839 method hasaction : int -> bool
2840 method exit :
2841 uioh:uioh ->
2842 cancel:bool ->
2843 active:int ->
2844 first:int ->
2845 pan:int ->
2846 qsearch:string ->
2847 uioh option
2848 method getactive : int
2849 method getfirst : int
2850 method getqsearch : string
2851 method setqsearch : string -> unit
2852 method getpan : int
2853 end;;
2855 class virtual lvsourcebase = object
2856 val mutable m_active = 0
2857 val mutable m_first = 0
2858 val mutable m_qsearch = ""
2859 val mutable m_pan = 0
2860 method getactive = m_active
2861 method getfirst = m_first
2862 method getqsearch = m_qsearch
2863 method getpan = m_pan
2864 method setqsearch s = m_qsearch <- s
2865 end;;
2867 let withoutlastutf8 s =
2868 let len = String.length s in
2869 if len = 0
2870 then s
2871 else
2872 let rec find pos =
2873 if pos = 0
2874 then pos
2875 else
2876 let b = Char.code s.[pos] in
2877 if b land 0b110000 = 0b11000000
2878 then find (pos-1)
2879 else pos-1
2881 let first =
2882 if Char.code s.[len-1] land 0x80 = 0
2883 then len-1
2884 else find (len-1)
2886 String.sub s 0 first;
2889 let textentrykeyboard
2890 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
2891 let enttext te =
2892 state.mode <- Textentry (te, onleave);
2893 state.text <- "";
2894 enttext ();
2895 G.postRedisplay "textentrykeyboard enttext";
2897 let histaction cmd =
2898 match opthist with
2899 | None -> ()
2900 | Some (action, _) ->
2901 state.mode <- Textentry (
2902 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
2904 G.postRedisplay "textentry histaction"
2906 match key with
2907 | 0xff08 -> (* backspace *)
2908 let s = withoutlastutf8 text in
2909 let len = String.length s in
2910 if cancelonempty && len = 0
2911 then (
2912 onleave Cancel;
2913 G.postRedisplay "textentrykeyboard after cancel";
2915 else (
2916 enttext (c, s, opthist, onkey, ondone, cancelonempty)
2919 | 0xff0d ->
2920 ondone text;
2921 onleave Confirm;
2922 G.postRedisplay "textentrykeyboard after confirm"
2924 | 0xff52 -> histaction HCprev
2925 | 0xff54 -> histaction HCnext
2926 | 0xff50 -> histaction HCfirst
2927 | 0xff57 -> histaction HClast
2929 | 0xff1b -> (* escape*)
2930 if String.length text = 0
2931 then (
2932 begin match opthist with
2933 | None -> ()
2934 | Some (_, onhistcancel) -> onhistcancel ()
2935 end;
2936 onleave Cancel;
2937 state.text <- "";
2938 G.postRedisplay "textentrykeyboard after cancel2"
2940 else (
2941 enttext (c, "", opthist, onkey, ondone, cancelonempty)
2944 | 0xff9f | 0xffff -> () (* delete *)
2946 | _ when key != 0 && key land 0xff00 != 0xff00 ->
2947 begin match onkey text key with
2948 | TEdone text ->
2949 ondone text;
2950 onleave Confirm;
2951 G.postRedisplay "textentrykeyboard after confirm2";
2953 | TEcont text ->
2954 enttext (c, text, opthist, onkey, ondone, cancelonempty);
2956 | TEstop ->
2957 onleave Cancel;
2958 G.postRedisplay "textentrykeyboard after cancel3"
2960 | TEswitch te ->
2961 state.mode <- Textentry (te, onleave);
2962 G.postRedisplay "textentrykeyboard switch";
2963 end;
2965 | _ ->
2966 vlog "unhandled key %s" (Wsi.keyname key)
2969 let firstof first active =
2970 if first > active || abs (first - active) > fstate.maxrows - 1
2971 then max 0 (active - (fstate.maxrows/2))
2972 else first
2975 let calcfirst first active =
2976 if active > first
2977 then
2978 let rows = active - first in
2979 if rows > fstate.maxrows then active - fstate.maxrows else first
2980 else active
2983 let scrollph y maxy =
2984 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2985 let sh = float conf.winh /. sh in
2986 let sh = max sh (float conf.scrollh) in
2988 let percent =
2989 if y = state.maxy
2990 then 1.0
2991 else float y /. float maxy
2993 let position = (float conf.winh -. sh) *. percent in
2995 let position =
2996 if position +. sh > float conf.winh
2997 then float conf.winh -. sh
2998 else position
3000 position, sh;
3003 let coe s = (s :> uioh);;
3005 class listview ~(source:lvsource) ~trusted ~modehash =
3006 object (self)
3007 val m_pan = source#getpan
3008 val m_first = source#getfirst
3009 val m_active = source#getactive
3010 val m_qsearch = source#getqsearch
3011 val m_prev_uioh = state.uioh
3013 method private elemunder y =
3014 let n = y / (fstate.fontsize+1) in
3015 if m_first + n < source#getitemcount
3016 then (
3017 if source#hasaction (m_first + n)
3018 then Some (m_first + n)
3019 else None
3021 else None
3023 method display =
3024 Gl.enable `blend;
3025 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3026 GlDraw.color (0., 0., 0.) ~alpha:0.85;
3027 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
3028 GlDraw.color (1., 1., 1.);
3029 Gl.enable `texture_2d;
3030 let fs = fstate.fontsize in
3031 let nfs = fs + 1 in
3032 let ww = fstate.wwidth in
3033 let tabw = 30.0*.ww in
3034 let itemcount = source#getitemcount in
3035 let rec loop row =
3036 if (row - m_first) * nfs > conf.winh
3037 then ()
3038 else (
3039 if row >= 0 && row < itemcount
3040 then (
3041 let (s, level) = source#getitem row in
3042 let y = (row - m_first) * nfs in
3043 let x = 5.0 +. float (level + m_pan) *. ww in
3044 if row = m_active
3045 then (
3046 Gl.disable `texture_2d;
3047 GlDraw.polygon_mode `both `line;
3048 GlDraw.color (1., 1., 1.) ~alpha:0.9;
3049 GlDraw.rect (1., float (y + 1))
3050 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
3051 GlDraw.polygon_mode `both `fill;
3052 GlDraw.color (1., 1., 1.);
3053 Gl.enable `texture_2d;
3056 let drawtabularstring s =
3057 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
3058 if trusted
3059 then
3060 let tabpos = try String.index s '\t' with Not_found -> -1 in
3061 if tabpos > 0
3062 then
3063 let len = String.length s - tabpos - 1 in
3064 let s1 = String.sub s 0 tabpos
3065 and s2 = String.sub s (tabpos + 1) len in
3066 let nx = drawstr x s1 in
3067 let sw = nx -. x in
3068 let x = x +. (max tabw sw) in
3069 drawstr x s2
3070 else
3071 drawstr x s
3072 else
3073 drawstr x s
3075 let _ = drawtabularstring s in
3076 loop (row+1)
3080 loop m_first;
3081 Gl.disable `blend;
3082 Gl.disable `texture_2d;
3084 method updownlevel incr =
3085 let len = source#getitemcount in
3086 let curlevel =
3087 if m_active >= 0 && m_active < len
3088 then snd (source#getitem m_active)
3089 else -1
3091 let rec flow i =
3092 if i = len then i-1 else if i = -1 then 0 else
3093 let _, l = source#getitem i in
3094 if l != curlevel then i else flow (i+incr)
3096 let active = flow m_active in
3097 let first = calcfirst m_first active in
3098 G.postRedisplay "outline updownlevel";
3099 {< m_active = active; m_first = first >}
3101 method private key1 key mask =
3102 let set1 active first qsearch =
3103 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3105 let search active pattern incr =
3106 let dosearch re =
3107 let rec loop n =
3108 if n >= 0 && n < source#getitemcount
3109 then (
3110 let s, _ = source#getitem n in
3112 (try ignore (Str.search_forward re s 0); true
3113 with Not_found -> false)
3114 then Some n
3115 else loop (n + incr)
3117 else None
3119 loop active
3122 let re = Str.regexp_case_fold pattern in
3123 dosearch re
3124 with Failure s ->
3125 state.text <- s;
3126 None
3128 let itemcount = source#getitemcount in
3129 let find start incr =
3130 let rec find i =
3131 if i = -1 || i = itemcount
3132 then -1
3133 else (
3134 if source#hasaction i
3135 then i
3136 else find (i + incr)
3139 find start
3141 let set active first =
3142 let first = bound first 0 (itemcount - fstate.maxrows) in
3143 state.text <- "";
3144 coe {< m_active = active; m_first = first >}
3146 let navigate incr =
3147 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3148 let active, first =
3149 let incr1 = if incr > 0 then 1 else -1 in
3150 if isvisible m_first m_active
3151 then
3152 let next =
3153 let next = m_active + incr in
3154 let next =
3155 if next < 0 || next >= itemcount
3156 then -1
3157 else find next incr1
3159 if next = -1 || abs (m_active - next) > fstate.maxrows
3160 then -1
3161 else next
3163 if next = -1
3164 then
3165 let first = m_first + incr in
3166 let first = bound first 0 (itemcount - 1) in
3167 let next =
3168 let next = m_active + incr in
3169 let next = bound next 0 (itemcount - 1) in
3170 find next ~-incr1
3172 let active = if next = -1 then m_active else next in
3173 active, first
3174 else
3175 let first = min next m_first in
3176 let first =
3177 if abs (next - first) > fstate.maxrows
3178 then first + incr
3179 else first
3181 next, first
3182 else
3183 let first = m_first + incr in
3184 let first = bound first 0 (itemcount - 1) in
3185 let active =
3186 let next = m_active + incr in
3187 let next = bound next 0 (itemcount - 1) in
3188 let next = find next incr1 in
3189 let active =
3190 if next = -1 || abs (m_active - first) > fstate.maxrows
3191 then (
3192 let active = if m_active = -1 then next else m_active in
3193 active
3195 else next
3197 if isvisible first active
3198 then active
3199 else -1
3201 active, first
3203 G.postRedisplay "listview navigate";
3204 set active first;
3206 match key with
3207 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3208 let incr = if key = 0x72 then -1 else 1 in
3209 let active, first =
3210 match search (m_active + incr) m_qsearch incr with
3211 | None ->
3212 state.text <- m_qsearch ^ " [not found]";
3213 m_active, m_first
3214 | Some active ->
3215 state.text <- m_qsearch;
3216 active, firstof m_first active
3218 G.postRedisplay "listview ctrl-r/s";
3219 set1 active first m_qsearch;
3221 | 0xff08 -> (* backspace *)
3222 if String.length m_qsearch = 0
3223 then coe self
3224 else (
3225 let qsearch = withoutlastutf8 m_qsearch in
3226 let len = String.length qsearch in
3227 if len = 0
3228 then (
3229 state.text <- "";
3230 G.postRedisplay "listview empty qsearch";
3231 set1 m_active m_first "";
3233 else
3234 let active, first =
3235 match search m_active qsearch ~-1 with
3236 | None ->
3237 state.text <- qsearch ^ " [not found]";
3238 m_active, m_first
3239 | Some active ->
3240 state.text <- qsearch;
3241 active, firstof m_first active
3243 G.postRedisplay "listview backspace qsearch";
3244 set1 active first qsearch
3247 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3248 let pattern = m_qsearch ^ Wsi.toutf8 key in
3249 let active, first =
3250 match search m_active pattern 1 with
3251 | None ->
3252 state.text <- pattern ^ " [not found]";
3253 m_active, m_first
3254 | Some active ->
3255 state.text <- pattern;
3256 active, firstof m_first active
3258 G.postRedisplay "listview qsearch add";
3259 set1 active first pattern;
3261 | 0xff1b -> (* escape *)
3262 state.text <- "";
3263 if String.length m_qsearch = 0
3264 then (
3265 G.postRedisplay "list view escape";
3266 begin
3267 match
3268 source#exit (coe self) true m_active m_first m_pan m_qsearch
3269 with
3270 | None -> m_prev_uioh
3271 | Some uioh -> uioh
3274 else (
3275 G.postRedisplay "list view kill qsearch";
3276 source#setqsearch "";
3277 coe {< m_qsearch = "" >}
3280 | 0xff0d -> (* return *)
3281 state.text <- "";
3282 let self = {< m_qsearch = "" >} in
3283 source#setqsearch "";
3284 let opt =
3285 G.postRedisplay "listview enter";
3286 if m_active >= 0 && m_active < source#getitemcount
3287 then (
3288 source#exit (coe self) false m_active m_first m_pan "";
3290 else (
3291 source#exit (coe self) true m_active m_first m_pan "";
3294 begin match opt with
3295 | None -> m_prev_uioh
3296 | Some uioh -> uioh
3299 | 0xff9f | 0xffff -> (* delete *)
3300 coe self
3302 | 0xff52 -> navigate ~-1 (* up *)
3303 | 0xff54 -> navigate 1 (* down *)
3304 | 0xff55 -> navigate ~-(fstate.maxrows) (* prior *)
3305 | 0xff56 -> navigate fstate.maxrows (* next *)
3307 | 0xff53 -> (* right *)
3308 state.text <- "";
3309 G.postRedisplay "listview right";
3310 coe {< m_pan = m_pan - 1 >}
3312 | 0xff51 -> (* left *)
3313 state.text <- "";
3314 G.postRedisplay "listview left";
3315 coe {< m_pan = m_pan + 1 >}
3317 | 0xff50 -> (* home *)
3318 let active = find 0 1 in
3319 G.postRedisplay "listview home";
3320 set active 0;
3322 | 0xff57 -> (* end *)
3323 let first = max 0 (itemcount - fstate.maxrows) in
3324 let active = find (itemcount - 1) ~-1 in
3325 G.postRedisplay "listview end";
3326 set active first;
3328 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3329 coe self
3331 | _ ->
3332 dolog "listview unknown key %#x" key; coe self
3334 method key key mask =
3335 match state.mode with
3336 | Textentry te -> textentrykeyboard key mask te; coe self
3337 | _ -> self#key1 key mask
3339 method button button down x y _ =
3340 let opt =
3341 match button with
3342 | 1 when x > conf.winw - conf.scrollbw ->
3343 G.postRedisplay "listview scroll";
3344 if down
3345 then
3346 let _, position, sh = self#scrollph in
3347 if y > truncate position && y < truncate (position +. sh)
3348 then (
3349 state.mstate <- Mscrolly;
3350 Some (coe self)
3352 else
3353 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3354 let first = truncate (s *. float source#getitemcount) in
3355 let first = min source#getitemcount first in
3356 Some (coe {< m_first = first; m_active = first >})
3357 else (
3358 state.mstate <- Mnone;
3359 Some (coe self);
3361 | 1 when not down ->
3362 begin match self#elemunder y with
3363 | Some n ->
3364 G.postRedisplay "listview click";
3365 source#exit
3366 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3367 | _ ->
3368 Some (coe self)
3370 | n when (n == 4 || n == 5) && not down ->
3371 let len = source#getitemcount in
3372 let first =
3373 if n = 5 && m_first + fstate.maxrows >= len
3374 then
3375 m_first
3376 else
3377 let first = m_first + (if n == 4 then -1 else 1) in
3378 bound first 0 (len - 1)
3380 G.postRedisplay "listview wheel";
3381 Some (coe {< m_first = first >})
3382 | n when (n = 6 || n = 7) && not down ->
3383 let inc = m_first + (if n = 7 then -1 else 1) in
3384 G.postRedisplay "listview hwheel";
3385 Some (coe {< m_pan = m_pan + inc >})
3386 | _ ->
3387 Some (coe self)
3389 match opt with
3390 | None -> m_prev_uioh
3391 | Some uioh -> uioh
3393 method motion _ y =
3394 match state.mstate with
3395 | Mscrolly ->
3396 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3397 let first = truncate (s *. float source#getitemcount) in
3398 let first = min source#getitemcount first in
3399 G.postRedisplay "listview motion";
3400 coe {< m_first = first; m_active = first >}
3401 | _ -> coe self
3403 method pmotion x y =
3404 if x < conf.winw - conf.scrollbw
3405 then
3406 let n =
3407 match self#elemunder y with
3408 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3409 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3411 let o =
3412 if n != m_active
3413 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3414 else self
3416 coe o
3417 else (
3418 Wsi.setcursor Wsi.CURSOR_INHERIT;
3419 coe self
3422 method infochanged _ = ()
3424 method scrollpw = (0, 0.0, 0.0)
3425 method scrollph =
3426 let nfs = fstate.fontsize + 1 in
3427 let y = m_first * nfs in
3428 let itemcount = source#getitemcount in
3429 let maxi = max 0 (itemcount - fstate.maxrows) in
3430 let maxy = maxi * nfs in
3431 let p, h = scrollph y maxy in
3432 conf.scrollbw, p, h
3434 method modehash = modehash
3435 end;;
3437 class outlinelistview ~source =
3438 object (self)
3439 inherit listview
3440 ~source:(source :> lvsource)
3441 ~trusted:false
3442 ~modehash:(findkeyhash conf "outline")
3443 as super
3445 method key key mask =
3446 let calcfirst first active =
3447 if active > first
3448 then
3449 let rows = active - first in
3450 let maxrows =
3451 if String.length state.text = 0
3452 then fstate.maxrows
3453 else fstate.maxrows - 2
3455 if rows > maxrows then active - maxrows else first
3456 else active
3458 let navigate incr =
3459 let active = m_active + incr in
3460 let active = bound active 0 (source#getitemcount - 1) in
3461 let first = calcfirst m_first active in
3462 G.postRedisplay "outline navigate";
3463 coe {< m_active = active; m_first = first >}
3465 let ctrl = Wsi.withctrl mask in
3466 match key with
3467 | 110 when ctrl -> (* ctrl-n *)
3468 source#narrow m_qsearch;
3469 G.postRedisplay "outline ctrl-n";
3470 coe {< m_first = 0; m_active = 0 >}
3472 | 117 when ctrl -> (* ctrl-u *)
3473 source#denarrow;
3474 G.postRedisplay "outline ctrl-u";
3475 state.text <- "";
3476 coe {< m_first = 0; m_active = 0 >}
3478 | 108 when ctrl -> (* ctrl-l *)
3479 let first = m_active - (fstate.maxrows / 2) in
3480 G.postRedisplay "outline ctrl-l";
3481 coe {< m_first = first >}
3483 | 0xff9f | 0xffff -> (* delete *)
3484 source#remove m_active;
3485 G.postRedisplay "outline delete";
3486 let active = max 0 (m_active-1) in
3487 coe {< m_first = firstof m_first active;
3488 m_active = active >}
3490 | 0xff52 -> navigate ~-1 (* up *)
3491 | 0xff54 -> navigate 1 (* down *)
3492 | 0xff55 -> (* prior *)
3493 navigate ~-(fstate.maxrows)
3494 | 0xff56 -> (* next *)
3495 navigate fstate.maxrows
3497 | 0xff53 -> (* [ctrl-]right *)
3498 let o =
3499 if ctrl
3500 then (
3501 G.postRedisplay "outline ctrl right";
3502 {< m_pan = m_pan + 1 >}
3504 else self#updownlevel 1
3506 coe o
3508 | 0xff51 -> (* [ctrl-]left *)
3509 let o =
3510 if ctrl
3511 then (
3512 G.postRedisplay "outline ctrl left";
3513 {< m_pan = m_pan - 1 >}
3515 else self#updownlevel ~-1
3517 coe o
3519 | 0xff50 -> (* home *)
3520 G.postRedisplay "outline home";
3521 coe {< m_first = 0; m_active = 0 >}
3523 | 0xff57 -> (* end *)
3524 let active = source#getitemcount - 1 in
3525 let first = max 0 (active - fstate.maxrows) in
3526 G.postRedisplay "outline end";
3527 coe {< m_active = active; m_first = first >}
3529 | _ -> super#key key mask
3532 let outlinesource usebookmarks =
3533 let empty = [||] in
3534 (object
3535 inherit lvsourcebase
3536 val mutable m_items = empty
3537 val mutable m_orig_items = empty
3538 val mutable m_prev_items = empty
3539 val mutable m_narrow_pattern = ""
3540 val mutable m_hadremovals = false
3542 method getitemcount =
3543 Array.length m_items + (if m_hadremovals then 1 else 0)
3545 method getitem n =
3546 if n == Array.length m_items && m_hadremovals
3547 then
3548 ("[Confirm removal]", 0)
3549 else
3550 let s, n, _ = m_items.(n) in
3551 (s, n)
3553 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3554 ignore (uioh, first, qsearch);
3555 let confrimremoval = m_hadremovals && active = Array.length m_items in
3556 let items =
3557 if String.length m_narrow_pattern = 0
3558 then m_orig_items
3559 else m_items
3561 if not cancel
3562 then (
3563 if not confrimremoval
3564 then(
3565 let _, _, anchor = m_items.(active) in
3566 gotoanchor anchor;
3567 m_items <- items;
3569 else (
3570 state.bookmarks <- Array.to_list m_items;
3571 m_orig_items <- m_items;
3574 else m_items <- items;
3575 m_pan <- pan;
3576 None
3578 method hasaction _ = true
3580 method greetmsg =
3581 if Array.length m_items != Array.length m_orig_items
3582 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3583 else ""
3585 method narrow pattern =
3586 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3587 match reopt with
3588 | None -> ()
3589 | Some re ->
3590 let rec loop accu n =
3591 if n = -1
3592 then (
3593 m_narrow_pattern <- pattern;
3594 m_items <- Array.of_list accu
3596 else
3597 let (s, _, _) as o = m_items.(n) in
3598 let accu =
3599 if (try ignore (Str.search_forward re s 0); true
3600 with Not_found -> false)
3601 then o :: accu
3602 else accu
3604 loop accu (n-1)
3606 loop [] (Array.length m_items - 1)
3608 method denarrow =
3609 m_orig_items <- (
3610 if usebookmarks
3611 then Array.of_list state.bookmarks
3612 else state.outlines
3614 m_items <- m_orig_items
3616 method remove m =
3617 if usebookmarks
3618 then
3619 if m >= 0 && m < Array.length m_items
3620 then (
3621 m_hadremovals <- true;
3622 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3623 let n = if n >= m then n+1 else n in
3624 m_items.(n)
3628 method reset anchor items =
3629 m_hadremovals <- false;
3630 if m_orig_items == empty || m_prev_items != items
3631 then (
3632 m_orig_items <- items;
3633 if String.length m_narrow_pattern = 0
3634 then m_items <- items;
3636 m_prev_items <- items;
3637 let rely = getanchory anchor in
3638 let active =
3639 let rec loop n best bestd =
3640 if n = Array.length m_items
3641 then best
3642 else
3643 let (_, _, anchor) = m_items.(n) in
3644 let orely = getanchory anchor in
3645 let d = abs (orely - rely) in
3646 if d < bestd
3647 then loop (n+1) n d
3648 else loop (n+1) best bestd
3650 loop 0 ~-1 max_int
3652 m_active <- active;
3653 m_first <- firstof m_first active
3654 end)
3657 let enterselector usebookmarks =
3658 let source = outlinesource usebookmarks in
3659 fun errmsg ->
3660 let outlines =
3661 if usebookmarks
3662 then Array.of_list state.bookmarks
3663 else state.outlines
3665 if Array.length outlines = 0
3666 then (
3667 showtext ' ' errmsg;
3669 else (
3670 state.text <- source#greetmsg;
3671 Wsi.setcursor Wsi.CURSOR_INHERIT;
3672 let anchor = getanchor () in
3673 source#reset anchor outlines;
3674 state.uioh <- coe (new outlinelistview ~source);
3675 G.postRedisplay "enter selector";
3679 let enteroutlinemode =
3680 let f = enterselector false in
3681 fun ()-> f "Document has no outline";
3684 let enterbookmarkmode =
3685 let f = enterselector true in
3686 fun () -> f "Document has no bookmarks (yet)";
3689 let color_of_string s =
3690 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3691 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3695 let color_to_string (r, g, b) =
3696 let r = truncate (r *. 256.0)
3697 and g = truncate (g *. 256.0)
3698 and b = truncate (b *. 256.0) in
3699 Printf.sprintf "%d/%d/%d" r g b
3702 let irect_of_string s =
3703 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3706 let irect_to_string (x0,y0,x1,y1) =
3707 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3710 let makecheckers () =
3711 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3712 following to say:
3713 converted by Issac Trotts. July 25, 2002 *)
3714 let image_height = 64
3715 and image_width = 64 in
3717 let make_image () =
3718 let image =
3719 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3721 for i = 0 to image_width - 1 do
3722 for j = 0 to image_height - 1 do
3723 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3724 (if (i land 8 ) lxor (j land 8) = 0
3725 then [|255;255;255|] else [|200;200;200|])
3726 done
3727 done;
3728 image
3730 let image = make_image () in
3731 let id = GlTex.gen_texture () in
3732 GlTex.bind_texture `texture_2d id;
3733 GlPix.store (`unpack_alignment 1);
3734 GlTex.image2d image;
3735 List.iter (GlTex.parameter ~target:`texture_2d)
3736 [ `wrap_s `repeat;
3737 `wrap_t `repeat;
3738 `mag_filter `nearest;
3739 `min_filter `nearest ];
3743 let setcheckers enabled =
3744 match state.texid with
3745 | None ->
3746 if enabled then state.texid <- Some (makecheckers ())
3748 | Some texid ->
3749 if not enabled
3750 then (
3751 GlTex.delete_texture texid;
3752 state.texid <- None;
3756 let int_of_string_with_suffix s =
3757 let l = String.length s in
3758 let s1, shift =
3759 if l > 1
3760 then
3761 let suffix = Char.lowercase s.[l-1] in
3762 match suffix with
3763 | 'k' -> String.sub s 0 (l-1), 10
3764 | 'm' -> String.sub s 0 (l-1), 20
3765 | 'g' -> String.sub s 0 (l-1), 30
3766 | _ -> s, 0
3767 else s, 0
3769 let n = int_of_string s1 in
3770 let m = n lsl shift in
3771 if m < 0 || m < n
3772 then raise (Failure "value too large")
3773 else m
3776 let string_with_suffix_of_int n =
3777 if n = 0
3778 then "0"
3779 else
3780 let n, s =
3781 if n land ((1 lsl 20) - 1) = 0
3782 then n lsr 20, "M"
3783 else (
3784 if n land ((1 lsl 10) - 1) = 0
3785 then n lsr 10, "K"
3786 else n, ""
3789 let rec loop s n =
3790 let h = n mod 1000 in
3791 let n = n / 1000 in
3792 if n = 0
3793 then string_of_int h ^ s
3794 else (
3795 let s = Printf.sprintf "_%03d%s" h s in
3796 loop s n
3799 loop "" n ^ s;
3802 let defghyllscroll = (40, 8, 32);;
3803 let ghyllscroll_of_string s =
3804 let (n, a, b) as nab =
3805 if s = "default"
3806 then defghyllscroll
3807 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3809 if n <= a || n <= b || a >= b
3810 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3811 nab;
3814 let ghyllscroll_to_string ((n, a, b) as nab) =
3815 if nab = defghyllscroll
3816 then "default"
3817 else Printf.sprintf "%d,%d,%d" n a b;
3820 let describe_location () =
3821 let f (fn, _) l =
3822 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3824 let fn, ln = List.fold_left f (-1, -1) state.layout in
3825 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3826 let percent =
3827 if maxy <= 0
3828 then 100.
3829 else (100. *. (float state.y /. float maxy))
3831 if fn = ln
3832 then
3833 Printf.sprintf "page %d of %d [%.2f%%]"
3834 (fn+1) state.pagecount percent
3835 else
3836 Printf.sprintf
3837 "pages %d-%d of %d [%.2f%%]"
3838 (fn+1) (ln+1) state.pagecount percent
3841 let enterinfomode =
3842 let btos b = if b then "\xe2\x88\x9a" else "" in
3843 let showextended = ref false in
3844 let leave mode = function
3845 | Confirm -> state.mode <- mode
3846 | Cancel -> state.mode <- mode in
3847 let src =
3848 (object
3849 val mutable m_first_time = true
3850 val mutable m_l = []
3851 val mutable m_a = [||]
3852 val mutable m_prev_uioh = nouioh
3853 val mutable m_prev_mode = View
3855 inherit lvsourcebase
3857 method reset prev_mode prev_uioh =
3858 m_a <- Array.of_list (List.rev m_l);
3859 m_l <- [];
3860 m_prev_mode <- prev_mode;
3861 m_prev_uioh <- prev_uioh;
3862 if m_first_time
3863 then (
3864 let rec loop n =
3865 if n >= Array.length m_a
3866 then ()
3867 else
3868 match m_a.(n) with
3869 | _, _, _, Action _ -> m_active <- n
3870 | _ -> loop (n+1)
3872 loop 0;
3873 m_first_time <- false;
3876 method int name get set =
3877 m_l <-
3878 (name, `int get, 1, Action (
3879 fun u ->
3880 let ondone s =
3881 try set (int_of_string s)
3882 with exn ->
3883 state.text <- Printf.sprintf "bad integer `%s': %s"
3884 s (Printexc.to_string exn)
3886 state.text <- "";
3887 let te = name ^ ": ", "", None, intentry, ondone, true in
3888 state.mode <- Textentry (te, leave m_prev_mode);
3890 )) :: m_l
3892 method int_with_suffix name get set =
3893 m_l <-
3894 (name, `intws get, 1, Action (
3895 fun u ->
3896 let ondone s =
3897 try set (int_of_string_with_suffix s)
3898 with exn ->
3899 state.text <- Printf.sprintf "bad integer `%s': %s"
3900 s (Printexc.to_string exn)
3902 state.text <- "";
3903 let te =
3904 name ^ ": ", "", None, intentry_with_suffix, ondone, true
3906 state.mode <- Textentry (te, leave m_prev_mode);
3908 )) :: m_l
3910 method bool ?(offset=1) ?(btos=btos) name get set =
3911 m_l <-
3912 (name, `bool (btos, get), offset, Action (
3913 fun u ->
3914 let v = get () in
3915 set (not v);
3917 )) :: m_l
3919 method color name get set =
3920 m_l <-
3921 (name, `color get, 1, Action (
3922 fun u ->
3923 let invalid = (nan, nan, nan) in
3924 let ondone s =
3925 let c =
3926 try color_of_string s
3927 with exn ->
3928 state.text <- Printf.sprintf "bad color `%s': %s"
3929 s (Printexc.to_string exn);
3930 invalid
3932 if c <> invalid
3933 then set c;
3935 let te = name ^ ": ", "", None, textentry, ondone, true in
3936 state.text <- color_to_string (get ());
3937 state.mode <- Textentry (te, leave m_prev_mode);
3939 )) :: m_l
3941 method string name get set =
3942 m_l <-
3943 (name, `string get, 1, Action (
3944 fun u ->
3945 let ondone s = set s in
3946 let te = name ^ ": ", "", None, textentry, ondone, true in
3947 state.mode <- Textentry (te, leave m_prev_mode);
3949 )) :: m_l
3951 method colorspace name get set =
3952 m_l <-
3953 (name, `string get, 1, Action (
3954 fun _ ->
3955 let source =
3956 let vals = [| "rgb"; "bgr"; "gray" |] in
3957 (object
3958 inherit lvsourcebase
3960 initializer
3961 m_active <- int_of_colorspace conf.colorspace;
3962 m_first <- 0;
3964 method getitemcount = Array.length vals
3965 method getitem n = (vals.(n), 0)
3966 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3967 ignore (uioh, first, pan, qsearch);
3968 if not cancel then set active;
3969 None
3970 method hasaction _ = true
3971 end)
3973 state.text <- "";
3974 let modehash = findkeyhash conf "info" in
3975 coe (new listview ~source ~trusted:true ~modehash)
3976 )) :: m_l
3978 method caption s offset =
3979 m_l <- (s, `empty, offset, Noaction) :: m_l
3981 method caption2 s f offset =
3982 m_l <- (s, `string f, offset, Noaction) :: m_l
3984 method getitemcount = Array.length m_a
3986 method getitem n =
3987 let tostr = function
3988 | `int f -> string_of_int (f ())
3989 | `intws f -> string_with_suffix_of_int (f ())
3990 | `string f -> f ()
3991 | `color f -> color_to_string (f ())
3992 | `bool (btos, f) -> btos (f ())
3993 | `empty -> ""
3995 let name, t, offset, _ = m_a.(n) in
3996 ((let s = tostr t in
3997 if String.length s > 0
3998 then Printf.sprintf "%s\t%s" name s
3999 else name),
4000 offset)
4002 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4003 let uiohopt =
4004 if not cancel
4005 then (
4006 m_qsearch <- qsearch;
4007 let uioh =
4008 match m_a.(active) with
4009 | _, _, _, Action f -> f uioh
4010 | _ -> uioh
4012 Some uioh
4014 else None
4016 m_active <- active;
4017 m_first <- first;
4018 m_pan <- pan;
4019 uiohopt
4021 method hasaction n =
4022 match m_a.(n) with
4023 | _, _, _, Action _ -> true
4024 | _ -> false
4025 end)
4027 let rec fillsrc prevmode prevuioh =
4028 let sep () = src#caption "" 0 in
4029 let colorp name get set =
4030 src#string name
4031 (fun () -> color_to_string (get ()))
4032 (fun v ->
4034 let c = color_of_string v in
4035 set c
4036 with exn ->
4037 state.text <- Printf.sprintf "bad color `%s': %s"
4038 v (Printexc.to_string exn);
4041 let oldmode = state.mode in
4042 let birdseye = isbirdseye state.mode in
4044 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
4046 src#bool "presentation mode"
4047 (fun () -> conf.presentation)
4048 (fun v ->
4049 conf.presentation <- v;
4050 state.anchor <- getanchor ();
4051 represent ());
4053 src#bool "ignore case in searches"
4054 (fun () -> conf.icase)
4055 (fun v -> conf.icase <- v);
4057 src#bool "preload"
4058 (fun () -> conf.preload)
4059 (fun v -> conf.preload <- v);
4061 src#bool "highlight links"
4062 (fun () -> conf.hlinks)
4063 (fun v -> conf.hlinks <- v);
4065 src#bool "under info"
4066 (fun () -> conf.underinfo)
4067 (fun v -> conf.underinfo <- v);
4069 src#bool "persistent bookmarks"
4070 (fun () -> conf.savebmarks)
4071 (fun v -> conf.savebmarks <- v);
4073 src#bool "proportional display"
4074 (fun () -> conf.proportional)
4075 (fun v -> reqlayout conf.angle v);
4077 src#bool "trim margins"
4078 (fun () -> conf.trimmargins)
4079 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
4081 src#bool "persistent location"
4082 (fun () -> conf.jumpback)
4083 (fun v -> conf.jumpback <- v);
4085 sep ();
4086 src#int "inter-page space"
4087 (fun () -> conf.interpagespace)
4088 (fun n ->
4089 conf.interpagespace <- n;
4090 docolumns conf.columns;
4091 let pageno, py =
4092 match state.layout with
4093 | [] -> 0, 0
4094 | l :: _ ->
4095 l.pageno, l.pagey
4097 state.maxy <- calcheight ();
4098 let y = getpagey pageno in
4099 gotoy (y + py)
4102 src#int "page bias"
4103 (fun () -> conf.pagebias)
4104 (fun v -> conf.pagebias <- v);
4106 src#int "scroll step"
4107 (fun () -> conf.scrollstep)
4108 (fun n -> conf.scrollstep <- n);
4110 src#int "horizontal scroll step"
4111 (fun () -> conf.hscrollstep)
4112 (fun v -> conf.hscrollstep <- v);
4114 src#int "auto scroll step"
4115 (fun () ->
4116 match state.autoscroll with
4117 | Some step -> step
4118 | _ -> conf.autoscrollstep)
4119 (fun n ->
4120 if state.autoscroll <> None
4121 then state.autoscroll <- Some n;
4122 conf.autoscrollstep <- n);
4124 src#int "zoom"
4125 (fun () -> truncate (conf.zoom *. 100.))
4126 (fun v -> setzoom ((float v) /. 100.));
4128 src#int "rotation"
4129 (fun () -> conf.angle)
4130 (fun v -> reqlayout v conf.proportional);
4132 src#int "scroll bar width"
4133 (fun () -> state.scrollw)
4134 (fun v ->
4135 state.scrollw <- v;
4136 conf.scrollbw <- v;
4137 reshape conf.winw conf.winh;
4140 src#int "scroll handle height"
4141 (fun () -> conf.scrollh)
4142 (fun v -> conf.scrollh <- v;);
4144 src#int "thumbnail width"
4145 (fun () -> conf.thumbw)
4146 (fun v ->
4147 conf.thumbw <- min 4096 v;
4148 match oldmode with
4149 | Birdseye beye ->
4150 leavebirdseye beye false;
4151 enterbirdseye ()
4152 | _ -> ()
4155 let mode = state.mode in
4156 src#string "columns"
4157 (fun () ->
4158 match conf.columns with
4159 | Csingle _ -> "1"
4160 | Cmulti (multi, _) -> multicolumns_to_string multi
4161 | Csplit (count, _) -> "-" ^ string_of_int count
4163 (fun v ->
4164 let n, a, b = multicolumns_of_string v in
4165 setcolumns mode n a b);
4167 sep ();
4168 src#caption "Presentation mode" 0;
4169 src#bool "scrollbar visible"
4170 (fun () -> conf.scrollbarinpm)
4171 (fun v ->
4172 if v != conf.scrollbarinpm
4173 then (
4174 conf.scrollbarinpm <- v;
4175 if conf.presentation
4176 then (
4177 state.scrollw <- if v then conf.scrollbw else 0;
4178 reshape conf.winw conf.winh;
4183 sep ();
4184 src#caption "Pixmap cache" 0;
4185 src#int_with_suffix "size (advisory)"
4186 (fun () -> conf.memlimit)
4187 (fun v -> conf.memlimit <- v);
4189 src#caption2 "used"
4190 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4191 (string_with_suffix_of_int state.memused)
4192 (Hashtbl.length state.tilemap)) 1;
4194 sep ();
4195 src#caption "Layout" 0;
4196 src#caption2 "Dimension"
4197 (fun () ->
4198 Printf.sprintf "%dx%d (virtual %dx%d)"
4199 conf.winw conf.winh
4200 state.w state.maxy)
4202 if conf.debug
4203 then
4204 src#caption2 "Position" (fun () ->
4205 Printf.sprintf "%dx%d" state.x state.y
4207 else
4208 src#caption2 "Visible" (fun () -> describe_location ()) 1
4211 sep ();
4212 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4213 "Save these parameters as global defaults at exit"
4214 (fun () -> conf.bedefault)
4215 (fun v -> conf.bedefault <- v)
4218 sep ();
4219 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4220 src#bool ~offset:0 ~btos "Extended parameters"
4221 (fun () -> !showextended)
4222 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4223 if !showextended
4224 then (
4225 src#bool "checkers"
4226 (fun () -> conf.checkers)
4227 (fun v -> conf.checkers <- v; setcheckers v);
4228 src#bool "update cursor"
4229 (fun () -> conf.updatecurs)
4230 (fun v -> conf.updatecurs <- v);
4231 src#bool "verbose"
4232 (fun () -> conf.verbose)
4233 (fun v -> conf.verbose <- v);
4234 src#bool "invert colors"
4235 (fun () -> conf.invert)
4236 (fun v -> conf.invert <- v);
4237 src#bool "max fit"
4238 (fun () -> conf.maxhfit)
4239 (fun v -> conf.maxhfit <- v);
4240 src#bool "redirect stderr"
4241 (fun () -> conf.redirectstderr)
4242 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4243 src#string "uri launcher"
4244 (fun () -> conf.urilauncher)
4245 (fun v -> conf.urilauncher <- v);
4246 src#string "path launcher"
4247 (fun () -> conf.pathlauncher)
4248 (fun v -> conf.pathlauncher <- v);
4249 src#string "tile size"
4250 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4251 (fun v ->
4253 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4254 conf.tilew <- max 64 w;
4255 conf.tileh <- max 64 h;
4256 flushtiles ();
4257 with exn ->
4258 state.text <- Printf.sprintf "bad tile size `%s': %s"
4259 v (Printexc.to_string exn));
4260 src#int "texture count"
4261 (fun () -> conf.texcount)
4262 (fun v ->
4263 if realloctexts v
4264 then conf.texcount <- v
4265 else showtext '!' " Failed to set texture count please retry later"
4267 src#int "slice height"
4268 (fun () -> conf.sliceheight)
4269 (fun v ->
4270 conf.sliceheight <- v;
4271 wcmd "sliceh %d" conf.sliceheight;
4273 src#int "anti-aliasing level"
4274 (fun () -> conf.aalevel)
4275 (fun v ->
4276 conf.aalevel <- bound v 0 8;
4277 state.anchor <- getanchor ();
4278 opendoc state.path state.password;
4280 src#string "page scroll scaling factor"
4281 (fun () -> string_of_float conf.pgscale)
4282 (fun v ->
4284 let s = float_of_string v in
4285 conf.pgscale <- s
4286 with exn ->
4287 state.text <- Printf.sprintf
4288 "bad page scroll scaling factor `%s': %s"
4289 v (Printexc.to_string exn)
4292 src#int "ui font size"
4293 (fun () -> fstate.fontsize)
4294 (fun v -> setfontsize (bound v 5 100));
4295 src#int "hint font size"
4296 (fun () -> conf.hfsize)
4297 (fun v -> conf.hfsize <- bound v 5 100);
4298 colorp "background color"
4299 (fun () -> conf.bgcolor)
4300 (fun v -> conf.bgcolor <- v);
4301 src#bool "crop hack"
4302 (fun () -> conf.crophack)
4303 (fun v -> conf.crophack <- v);
4304 src#string "trim fuzz"
4305 (fun () -> irect_to_string conf.trimfuzz)
4306 (fun v ->
4308 conf.trimfuzz <- irect_of_string v;
4309 if conf.trimmargins
4310 then settrim true conf.trimfuzz;
4311 with exn ->
4312 state.text <- Printf.sprintf "bad irect `%s': %s"
4313 v (Printexc.to_string exn)
4315 src#string "throttle"
4316 (fun () ->
4317 match conf.maxwait with
4318 | None -> "show place holder if page is not ready"
4319 | Some time ->
4320 if time = infinity
4321 then "wait for page to fully render"
4322 else
4323 "wait " ^ string_of_float time
4324 ^ " seconds before showing placeholder"
4326 (fun v ->
4328 let f = float_of_string v in
4329 if f <= 0.0
4330 then conf.maxwait <- None
4331 else conf.maxwait <- Some f
4332 with exn ->
4333 state.text <- Printf.sprintf "bad time `%s': %s"
4334 v (Printexc.to_string exn)
4336 src#string "ghyll scroll"
4337 (fun () ->
4338 match conf.ghyllscroll with
4339 | None -> ""
4340 | Some nab -> ghyllscroll_to_string nab
4342 (fun v ->
4344 let gs =
4345 if String.length v = 0
4346 then None
4347 else Some (ghyllscroll_of_string v)
4349 conf.ghyllscroll <- gs
4350 with exn ->
4351 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4352 v (Printexc.to_string exn)
4354 src#string "selection command"
4355 (fun () -> conf.selcmd)
4356 (fun v -> conf.selcmd <- v);
4357 src#colorspace "color space"
4358 (fun () -> colorspace_to_string conf.colorspace)
4359 (fun v ->
4360 conf.colorspace <- colorspace_of_int v;
4361 wcmd "cs %d" v;
4362 load state.layout;
4366 sep ();
4367 src#caption "Document" 0;
4368 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4369 src#caption2 "Pages"
4370 (fun () -> string_of_int state.pagecount) 1;
4371 src#caption2 "Dimensions"
4372 (fun () -> string_of_int (List.length state.pdims)) 1;
4373 if conf.trimmargins
4374 then (
4375 sep ();
4376 src#caption "Trimmed margins" 0;
4377 src#caption2 "Dimensions"
4378 (fun () -> string_of_int (List.length state.pdims)) 1;
4381 sep ();
4382 src#caption "OpenGL" 0;
4383 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
4384 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
4385 src#reset prevmode prevuioh;
4387 fun () ->
4388 state.text <- "";
4389 let prevmode = state.mode
4390 and prevuioh = state.uioh in
4391 fillsrc prevmode prevuioh;
4392 let source = (src :> lvsource) in
4393 let modehash = findkeyhash conf "info" in
4394 state.uioh <- coe (object (self)
4395 inherit listview ~source ~trusted:true ~modehash as super
4396 val mutable m_prevmemused = 0
4397 method infochanged = function
4398 | Memused ->
4399 if m_prevmemused != state.memused
4400 then (
4401 m_prevmemused <- state.memused;
4402 G.postRedisplay "memusedchanged";
4404 | Pdim -> G.postRedisplay "pdimchanged"
4405 | Docinfo -> fillsrc prevmode prevuioh
4407 method key key mask =
4408 if not (Wsi.withctrl mask)
4409 then
4410 match key with
4411 | 0xff51 -> coe (self#updownlevel ~-1)
4412 | 0xff53 -> coe (self#updownlevel 1)
4413 | _ -> super#key key mask
4414 else super#key key mask
4415 end);
4416 G.postRedisplay "info";
4419 let enterhelpmode =
4420 let source =
4421 (object
4422 inherit lvsourcebase
4423 method getitemcount = Array.length state.help
4424 method getitem n =
4425 let s, n, _ = state.help.(n) in
4426 (s, n)
4428 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4429 let optuioh =
4430 if not cancel
4431 then (
4432 m_qsearch <- qsearch;
4433 match state.help.(active) with
4434 | _, _, Action f -> Some (f uioh)
4435 | _ -> Some (uioh)
4437 else None
4439 m_active <- active;
4440 m_first <- first;
4441 m_pan <- pan;
4442 optuioh
4444 method hasaction n =
4445 match state.help.(n) with
4446 | _, _, Action _ -> true
4447 | _ -> false
4449 initializer
4450 m_active <- -1
4451 end)
4452 in fun () ->
4453 let modehash = findkeyhash conf "help" in
4454 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4455 G.postRedisplay "help";
4458 let entermsgsmode =
4459 let msgsource =
4460 let re = Str.regexp "[\r\n]" in
4461 (object
4462 inherit lvsourcebase
4463 val mutable m_items = [||]
4465 method getitemcount = 1 + Array.length m_items
4467 method getitem n =
4468 if n = 0
4469 then "[Clear]", 0
4470 else m_items.(n-1), 0
4472 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4473 ignore uioh;
4474 if not cancel
4475 then (
4476 if active = 0
4477 then Buffer.clear state.errmsgs;
4478 m_qsearch <- qsearch;
4480 m_active <- active;
4481 m_first <- first;
4482 m_pan <- pan;
4483 None
4485 method hasaction n =
4486 n = 0
4488 method reset =
4489 state.newerrmsgs <- false;
4490 let l = Str.split re (Buffer.contents state.errmsgs) in
4491 m_items <- Array.of_list l
4493 initializer
4494 m_active <- 0
4495 end)
4496 in fun () ->
4497 state.text <- "";
4498 msgsource#reset;
4499 let source = (msgsource :> lvsource) in
4500 let modehash = findkeyhash conf "listview" in
4501 state.uioh <- coe (object
4502 inherit listview ~source ~trusted:false ~modehash as super
4503 method display =
4504 if state.newerrmsgs
4505 then msgsource#reset;
4506 super#display
4507 end);
4508 G.postRedisplay "msgs";
4511 let quickbookmark ?title () =
4512 match state.layout with
4513 | [] -> ()
4514 | l :: _ ->
4515 let title =
4516 match title with
4517 | None ->
4518 let sec = Unix.gettimeofday () in
4519 let tm = Unix.localtime sec in
4520 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4521 (l.pageno+1)
4522 tm.Unix.tm_mday
4523 tm.Unix.tm_mon
4524 (tm.Unix.tm_year + 1900)
4525 tm.Unix.tm_hour
4526 tm.Unix.tm_min
4527 | Some title -> title
4529 state.bookmarks <- (title, 0, getanchor1 l) :: state.bookmarks
4532 let doreshape w h =
4533 state.fullscreen <- None;
4534 Wsi.reshape w h;
4537 let setautoscrollspeed step goingdown =
4538 let incr = max 1 ((abs step) / 2) in
4539 let incr = if goingdown then incr else -incr in
4540 let astep = step + incr in
4541 state.autoscroll <- Some astep;
4544 let gotounder = function
4545 | Ulinkgoto (pageno, top) ->
4546 if pageno >= 0
4547 then (
4548 addnav ();
4549 gotopage1 pageno top;
4552 | Ulinkuri s ->
4553 gotouri s
4555 | Uremote (filename, pageno) ->
4556 let path =
4557 if Sys.file_exists filename
4558 then filename
4559 else
4560 let dir = Filename.dirname state.path in
4561 let path = Filename.concat dir filename in
4562 if Sys.file_exists path
4563 then path
4564 else ""
4566 if String.length path > 0
4567 then (
4568 let anchor = getanchor () in
4569 let ranchor = state.path, state.password, anchor in
4570 state.anchor <- (pageno, 0.0, 0.0);
4571 state.ranchors <- ranchor :: state.ranchors;
4572 opendoc path "";
4574 else showtext '!' ("Could not find " ^ filename)
4576 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4579 let canpan () =
4580 match conf.columns with
4581 | Csplit _ -> true
4582 | _ -> conf.zoom > 1.0
4585 let viewkeyboard key mask =
4586 let enttext te =
4587 let mode = state.mode in
4588 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4589 state.text <- "";
4590 enttext ();
4591 G.postRedisplay "view:enttext"
4593 let ctrl = Wsi.withctrl mask in
4594 match key with
4595 | 81 -> (* Q *)
4596 exit 0
4598 | 0xff63 -> (* insert *)
4599 if conf.angle mod 360 = 0
4600 then (
4601 state.mode <- LinkNav (Ltgendir 0);
4602 gotoy state.y;
4604 else showtext '!' "Keyboard link naviagtion does not work under rotation"
4606 | 0xff1b | 113 -> (* escape / q *)
4607 begin match state.mstate with
4608 | Mzoomrect _ ->
4609 state.mstate <- Mnone;
4610 Wsi.setcursor Wsi.CURSOR_INHERIT;
4611 G.postRedisplay "kill zoom rect";
4612 | _ ->
4613 match state.ranchors with
4614 | [] -> raise Quit
4615 | (path, password, anchor) :: rest ->
4616 state.ranchors <- rest;
4617 state.anchor <- anchor;
4618 opendoc path password
4619 end;
4621 | 0xff08 -> (* backspace *)
4622 let y = getnav ~-1 in
4623 gotoy_and_clear_text y
4625 | 111 -> (* o *)
4626 enteroutlinemode ()
4628 | 117 -> (* u *)
4629 state.rects <- [];
4630 state.text <- "";
4631 G.postRedisplay "dehighlight";
4633 | 47 | 63 -> (* / ? *)
4634 let ondone isforw s =
4635 cbput state.hists.pat s;
4636 state.searchpattern <- s;
4637 search s isforw
4639 let s = String.create 1 in
4640 s.[0] <- Char.chr key;
4641 enttext (s, "", Some (onhist state.hists.pat),
4642 textentry, ondone (key = 47), true)
4644 | 43 | 0xffab | 61 when ctrl -> (* ctrl-+ or ctrl-= *)
4645 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4646 setzoom (conf.zoom +. incr)
4648 | 43 | 0xffab -> (* + *)
4649 let ondone s =
4650 let n =
4651 try int_of_string s with exc ->
4652 state.text <- Printf.sprintf "bad integer `%s': %s"
4653 s (Printexc.to_string exc);
4654 max_int
4656 if n != max_int
4657 then (
4658 conf.pagebias <- n;
4659 state.text <- "page bias is now " ^ string_of_int n;
4662 enttext ("page bias: ", "", None, intentry, ondone, true)
4664 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4665 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4666 setzoom (max 0.01 (conf.zoom -. decr))
4668 | 45 | 0xffad -> (* - *)
4669 let ondone msg = state.text <- msg in
4670 enttext (
4671 "option [acfhilpstvxACFPRSZTIS]: ", "", None,
4672 optentry state.mode, ondone, true
4675 | 48 when ctrl -> (* ctrl-0 *)
4676 setzoom 1.0
4678 | 49 when ctrl -> (* ctrl-1 *)
4679 let cols =
4680 match conf.columns with
4681 | Csingle _ | Cmulti _ -> 1
4682 | Csplit (n, _) -> n
4684 let zoom = zoomforh conf.winw conf.winh state.scrollw cols in
4685 if zoom < 1.0
4686 then setzoom zoom
4688 | 0xffc6 -> (* f9 *)
4689 togglebirdseye ()
4691 | 57 when ctrl -> (* ctrl-9 *)
4692 togglebirdseye ()
4694 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4695 when not ctrl -> (* 0..9 *)
4696 let ondone s =
4697 let n =
4698 try int_of_string s with exc ->
4699 state.text <- Printf.sprintf "bad integer `%s': %s"
4700 s (Printexc.to_string exc);
4703 if n >= 0
4704 then (
4705 addnav ();
4706 cbput state.hists.pag (string_of_int n);
4707 gotopage1 (n + conf.pagebias - 1) 0;
4710 let pageentry text key =
4711 match Char.unsafe_chr key with
4712 | 'g' -> TEdone text
4713 | _ -> intentry text key
4715 let text = "x" in text.[0] <- Char.chr key;
4716 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone, true)
4718 | 98 -> (* b *)
4719 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4720 reshape conf.winw conf.winh;
4722 | 108 -> (* l *)
4723 conf.hlinks <- not conf.hlinks;
4724 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4725 G.postRedisplay "toggle highlightlinks";
4727 | 70 -> (* F *)
4728 state.glinks <- true;
4729 let mode = state.mode in
4730 state.mode <- Textentry (
4731 (":", "", None, linknentry, linkndone (fun under ->
4732 addnav ();
4733 gotounder under
4734 ), false
4735 ), fun _ ->
4736 state.glinks <- false;
4737 state.mode <- mode
4739 state.text <- "";
4740 G.postRedisplay "view:linkent(F)"
4742 | 121 -> (* y *)
4743 state.glinks <- true;
4744 let mode = state.mode in
4745 state.mode <- Textentry (
4746 (":", "", None, linknentry, linkndone (fun under ->
4747 match Ne.pipe () with
4748 | Ne.Exn exn ->
4749 showtext '!' (Printf.sprintf "pipe failed: %s"
4750 (Printexc.to_string exn));
4751 | Ne.Res (r, w) ->
4752 let popened =
4753 try popen conf.selcmd [r, 0; w, -1]; true
4754 with exn ->
4755 showtext '!'
4756 (Printf.sprintf "failed to execute %s: %s"
4757 conf.selcmd (Printexc.to_string exn));
4758 false
4760 let clo cap fd =
4761 Ne.clo fd (fun msg ->
4762 showtext '!' (Printf.sprintf "failed to close %s: %s" cap msg)
4765 let s = undertext under in
4766 if popened
4767 then
4768 (try
4769 let l = String.length s in
4770 let n = Unix.write w s 0 l in
4771 if n != l
4772 then
4773 showtext '!'
4774 (Printf.sprintf
4775 "failed to write %d characters to sel pipe, wrote %d"
4778 with exn ->
4779 showtext '!'
4780 (Printf.sprintf "failed to write to sel pipe: %s"
4781 (Printexc.to_string exn)
4784 else dolog "%s" s;
4785 clo "pipe/r" r;
4786 clo "pipe/w" w;
4787 ), false
4789 fun _ ->
4790 state.glinks <- false;
4791 state.mode <- mode
4793 state.text <- "";
4794 G.postRedisplay "view:linkent"
4796 | 97 -> (* a *)
4797 begin match state.autoscroll with
4798 | Some step ->
4799 conf.autoscrollstep <- step;
4800 state.autoscroll <- None
4801 | None ->
4802 if conf.autoscrollstep = 0
4803 then state.autoscroll <- Some 1
4804 else state.autoscroll <- Some conf.autoscrollstep
4807 | 112 when ctrl -> (* ctrl-p *)
4808 launchpath ()
4810 | 80 -> (* P *)
4811 conf.presentation <- not conf.presentation;
4812 if conf.presentation
4813 then (
4814 if not conf.scrollbarinpm
4815 then state.scrollw <- 0;
4817 else
4818 state.scrollw <- conf.scrollbw;
4820 showtext ' ' ("presentation mode " ^
4821 if conf.presentation then "on" else "off");
4822 state.anchor <- getanchor ();
4823 represent ()
4825 | 102 -> (* f *)
4826 begin match state.fullscreen with
4827 | None ->
4828 state.fullscreen <- Some (conf.winw, conf.winh);
4829 Wsi.fullscreen ()
4830 | Some (w, h) ->
4831 state.fullscreen <- None;
4832 doreshape w h
4835 | 103 -> (* g *)
4836 gotoy_and_clear_text 0
4838 | 71 -> (* G *)
4839 gotopage1 (state.pagecount - 1) 0
4841 | 112 | 78 -> (* p|N *)
4842 search state.searchpattern false
4844 | 110 | 0xffc0 -> (* n|F3 *)
4845 search state.searchpattern true
4847 | 116 -> (* t *)
4848 begin match state.layout with
4849 | [] -> ()
4850 | l :: _ ->
4851 gotoy_and_clear_text (getpagey l.pageno)
4854 | 32 -> (* space *)
4855 begin match state.layout with
4856 | [] -> ()
4857 | l :: rest ->
4858 match conf.columns with
4859 | Csingle _ | Cmulti _ ->
4860 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4861 then
4862 let y = clamp (pgscale conf.winh) in
4863 gotoy_and_clear_text y
4864 else
4865 let pageno = min (l.pageno+1) (state.pagecount-1) in
4866 gotoy_and_clear_text (getpagey pageno)
4867 | Csplit (n, _) ->
4868 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
4869 then
4870 let pagey, pageh = getpageyh l.pageno in
4871 let pagey = pagey + pageh * l.pagecol in
4872 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
4873 gotoy_and_clear_text (pagey + pageh + ips)
4876 | 0xff9f | 0xffff -> (* delete *)
4877 begin match state.layout with
4878 | [] -> ()
4879 | l :: _ ->
4880 match conf.columns with
4881 | Csingle _ | Cmulti _ ->
4882 if conf.presentation && l.pagey != 0
4883 then
4884 gotoy_and_clear_text (clamp (pgscale ~-(conf.winh)))
4885 else
4886 let pageno = max 0 (l.pageno-1) in
4887 gotoy_and_clear_text (getpagey pageno)
4888 | Csplit (n, _) ->
4889 let y =
4890 if l.pagecol = 0
4891 then
4892 if l.pageno = 0
4893 then l.pagey
4894 else
4895 let pageno = max 0 (l.pageno-1) in
4896 let pagey, pageh = getpageyh pageno in
4897 pagey + (n-1)*pageh
4898 else
4899 let pagey, pageh = getpageyh l.pageno in
4900 pagey + pageh * (l.pagecol-1) - conf.interpagespace
4902 gotoy_and_clear_text y
4905 | 61 -> (* = *)
4906 showtext ' ' (describe_location ());
4908 | 119 -> (* w *)
4909 begin match state.layout with
4910 | [] -> ()
4911 | l :: _ ->
4912 doreshape (l.pagew + state.scrollw) l.pageh;
4913 G.postRedisplay "w"
4916 | 39 -> (* ' *)
4917 enterbookmarkmode ()
4919 | 104 | 0xffbe -> (* h|F1 *)
4920 enterhelpmode ()
4922 | 105 -> (* i *)
4923 enterinfomode ()
4925 | 101 when conf.redirectstderr -> (* e *)
4926 entermsgsmode ()
4928 | 109 -> (* m *)
4929 let ondone s =
4930 match state.layout with
4931 | l :: _ -> state.bookmarks <- (s, 0, getanchor1 l) :: state.bookmarks
4932 | _ -> ()
4934 enttext ("bookmark: ", "", None, textentry, ondone, true)
4936 | 126 -> (* ~ *)
4937 quickbookmark ();
4938 showtext ' ' "Quick bookmark added";
4940 | 122 -> (* z *)
4941 begin match state.layout with
4942 | l :: _ ->
4943 let rect = getpdimrect l.pagedimno in
4944 let w, h =
4945 if conf.crophack
4946 then
4947 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4948 truncate (1.2 *. (rect.(3) -. rect.(0))))
4949 else
4950 (truncate (rect.(1) -. rect.(0)),
4951 truncate (rect.(3) -. rect.(0)))
4953 let w = truncate ((float w)*.conf.zoom)
4954 and h = truncate ((float h)*.conf.zoom) in
4955 if w != 0 && h != 0
4956 then (
4957 state.anchor <- getanchor ();
4958 doreshape (w + state.scrollw) (h + conf.interpagespace)
4960 G.postRedisplay "z";
4962 | [] -> ()
4965 | 50 when ctrl -> (* ctrl-2 *)
4966 let maxw = getmaxw () in
4967 if maxw > 0.0
4968 then setzoom (maxw /. float conf.winw)
4970 | 60 | 62 -> (* < > *)
4971 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
4973 | 91 | 93 -> (* [ ] *)
4974 conf.colorscale <-
4975 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
4977 G.postRedisplay "brightness";
4979 | 99 when state.mode = View -> (* c *)
4980 let (c, a, b), z =
4981 match state.prevcolumns with
4982 | None -> (1, 0, 0), 1.0
4983 | Some (columns, z) ->
4984 let cab =
4985 match columns with
4986 | Csplit (c, _) -> -c, 0, 0
4987 | Cmulti ((c, a, b), _) -> c, a, b
4988 | Csingle _ -> 1, 0, 0
4990 cab, z
4992 setcolumns View c a b;
4993 setzoom z;
4995 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
4996 setzoom state.prevzoom
4998 | 107 | 0xff52 -> (* k up *)
4999 begin match state.autoscroll with
5000 | None ->
5001 begin match state.mode with
5002 | Birdseye beye -> upbirdseye 1 beye
5003 | _ ->
5004 if ctrl
5005 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
5006 else gotoy_and_clear_text (clamp (-conf.scrollstep))
5008 | Some n ->
5009 setautoscrollspeed n false
5012 | 106 | 0xff54 -> (* j down *)
5013 begin match state.autoscroll with
5014 | None ->
5015 begin match state.mode with
5016 | Birdseye beye -> downbirdseye 1 beye
5017 | _ ->
5018 if ctrl
5019 then gotoy_and_clear_text (clamp (conf.winh/2))
5020 else gotoy_and_clear_text (clamp conf.scrollstep)
5022 | Some n ->
5023 setautoscrollspeed n true
5026 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
5027 if canpan ()
5028 then
5029 let dx =
5030 if ctrl
5031 then conf.winw / 2
5032 else 10
5034 let dx = if key = 0xff51 then dx else -dx in
5035 state.x <- state.x + dx;
5036 gotoy_and_clear_text state.y
5037 else (
5038 state.text <- "";
5039 G.postRedisplay "lef/right"
5042 | 0xff55 -> (* prior *)
5043 let y =
5044 if ctrl
5045 then
5046 match state.layout with
5047 | [] -> state.y
5048 | l :: _ -> state.y - l.pagey
5049 else
5050 clamp (pgscale (-conf.winh))
5052 gotoghyll y
5054 | 0xff56 -> (* next *)
5055 let y =
5056 if ctrl
5057 then
5058 match List.rev state.layout with
5059 | [] -> state.y
5060 | l :: _ -> getpagey l.pageno
5061 else
5062 clamp (pgscale conf.winh)
5064 gotoghyll y
5066 | 0xff50 -> (* home *)
5067 gotoghyll 0
5068 | 0xff57 -> (* end *)
5069 gotoghyll (clamp state.maxy)
5070 | 0xff53 when Wsi.withalt mask -> (* right *)
5071 gotoghyll (getnav ~-1)
5072 | 0xff51 when Wsi.withalt mask -> (* left *)
5073 gotoghyll (getnav 1)
5075 | 114 -> (* r *)
5076 state.anchor <- getanchor ();
5077 opendoc state.path state.password
5079 | 118 when conf.debug -> (* v *)
5080 state.rects <- [];
5081 List.iter (fun l ->
5082 match getopaque l.pageno with
5083 | None -> ()
5084 | Some opaque ->
5085 let x0, y0, x1, y1 = pagebbox opaque in
5086 let a,b = float x0, float y0 in
5087 let c,d = float x1, float y0 in
5088 let e,f = float x1, float y1 in
5089 let h,j = float x0, float y1 in
5090 let rect = (a,b,c,d,e,f,h,j) in
5091 debugrect rect;
5092 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
5093 ) state.layout;
5094 G.postRedisplay "v";
5096 | _ ->
5097 vlog "huh? %s" (Wsi.keyname key)
5100 let linknavkeyboard key mask linknav =
5101 let getpage pageno =
5102 let rec loop = function
5103 | [] -> None
5104 | l :: _ when l.pageno = pageno -> Some l
5105 | _ :: rest -> loop rest
5106 in loop state.layout
5108 let doexact (pageno, n) =
5109 match getopaque pageno, getpage pageno with
5110 | Some opaque, Some l ->
5111 if key = 0xff0d
5112 then
5113 let under = getlink opaque n in
5114 G.postRedisplay "link gotounder";
5115 gotounder under;
5116 state.mode <- View;
5117 else
5118 let opt, dir =
5119 match key with
5120 | 0xff50 -> (* home *)
5121 Some (findlink opaque LDfirst), -1
5123 | 0xff57 -> (* end *)
5124 Some (findlink opaque LDlast), 1
5126 | 0xff51 -> (* left *)
5127 Some (findlink opaque (LDleft n)), -1
5129 | 0xff53 -> (* right *)
5130 Some (findlink opaque (LDright n)), 1
5132 | 0xff52 -> (* up *)
5133 Some (findlink opaque (LDup n)), -1
5135 | 0xff54 -> (* down *)
5136 Some (findlink opaque (LDdown n)), 1
5138 | _ -> None, 0
5140 let pwl l dir =
5141 begin match findpwl l.pageno dir with
5142 | Pwlnotfound -> ()
5143 | Pwl pageno ->
5144 let notfound dir =
5145 state.mode <- LinkNav (Ltgendir dir);
5146 let y, h = getpageyh pageno in
5147 let y =
5148 if dir < 0
5149 then y + h - conf.winh
5150 else y
5152 gotoy y
5154 begin match getopaque pageno, getpage pageno with
5155 | Some opaque, Some _ ->
5156 let link =
5157 let ld = if dir > 0 then LDfirst else LDlast in
5158 findlink opaque ld
5160 begin match link with
5161 | Lfound m ->
5162 showlinktype (getlink opaque m);
5163 state.mode <- LinkNav (Ltexact (pageno, m));
5164 G.postRedisplay "linknav jpage";
5165 | _ -> notfound dir
5166 end;
5167 | _ -> notfound dir
5168 end;
5169 end;
5171 begin match opt with
5172 | Some Lnotfound -> pwl l dir;
5173 | Some (Lfound m) ->
5174 if m = n
5175 then pwl l dir
5176 else (
5177 let _, y0, _, y1 = getlinkrect opaque m in
5178 if y0 < l.pagey
5179 then gotopage1 l.pageno y0
5180 else (
5181 let d = fstate.fontsize + 1 in
5182 if y1 - l.pagey > l.pagevh - d
5183 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh + d)
5184 else G.postRedisplay "linknav";
5186 showlinktype (getlink opaque m);
5187 state.mode <- LinkNav (Ltexact (l.pageno, m));
5190 | None -> viewkeyboard key mask
5191 end;
5192 | _ -> viewkeyboard key mask
5194 if key = 0xff63
5195 then (
5196 state.mode <- View;
5197 G.postRedisplay "leave linknav"
5199 else
5200 match linknav with
5201 | Ltgendir _ -> viewkeyboard key mask
5202 | Ltexact exact -> doexact exact
5205 let keyboard key mask =
5206 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5207 then wcmd "interrupt"
5208 else state.uioh <- state.uioh#key key mask
5211 let birdseyekeyboard key mask
5212 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5213 let incr =
5214 match conf.columns with
5215 | Csingle _ -> 1
5216 | Cmulti ((c, _, _), _) -> c
5217 | Csplit _ -> failwith "bird's eye split mode"
5219 let pgh layout = List.fold_left (fun m l -> max l.pageh m) conf.winh layout in
5220 match key with
5221 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5222 let y, h = getpageyh pageno in
5223 let top = (conf.winh - h) / 2 in
5224 gotoy (max 0 (y - top))
5225 | 0xff0d -> leavebirdseye beye false
5226 | 0xff1b -> leavebirdseye beye true (* escape *)
5227 | 0xff52 -> upbirdseye incr beye (* up *)
5228 | 0xff54 -> downbirdseye incr beye (* down *)
5229 | 0xff51 -> upbirdseye 1 beye (* left *)
5230 | 0xff53 -> downbirdseye 1 beye (* right *)
5232 | 0xff55 -> (* prior *)
5233 begin match state.layout with
5234 | l :: _ ->
5235 if l.pagey != 0
5236 then (
5237 state.mode <- Birdseye (
5238 oconf, leftx, l.pageno, hooverpageno, anchor
5240 gotopage1 l.pageno 0;
5242 else (
5243 let layout = layout (state.y-conf.winh) (pgh state.layout) in
5244 match layout with
5245 | [] -> gotoy (clamp (-conf.winh))
5246 | l :: _ ->
5247 state.mode <- Birdseye (
5248 oconf, leftx, l.pageno, hooverpageno, anchor
5250 gotopage1 l.pageno 0
5253 | [] -> gotoy (clamp (-conf.winh))
5254 end;
5256 | 0xff56 -> (* next *)
5257 begin match List.rev state.layout with
5258 | l :: _ ->
5259 let layout = layout (state.y + (pgh state.layout)) conf.winh in
5260 begin match layout with
5261 | [] ->
5262 let incr = l.pageh - l.pagevh in
5263 if incr = 0
5264 then (
5265 state.mode <-
5266 Birdseye (
5267 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5269 G.postRedisplay "birdseye pagedown";
5271 else gotoy (clamp (incr + conf.interpagespace*2));
5273 | l :: _ ->
5274 state.mode <-
5275 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5276 gotopage1 l.pageno 0;
5279 | [] -> gotoy (clamp conf.winh)
5280 end;
5282 | 0xff50 -> (* home *)
5283 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5284 gotopage1 0 0
5286 | 0xff57 -> (* end *)
5287 let pageno = state.pagecount - 1 in
5288 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5289 if not (pagevisible state.layout pageno)
5290 then
5291 let h =
5292 match List.rev state.pdims with
5293 | [] -> conf.winh
5294 | (_, _, h, _) :: _ -> h
5296 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
5297 else G.postRedisplay "birdseye end";
5298 | _ -> viewkeyboard key mask
5301 let drawpage l linkindexbase =
5302 let color =
5303 match state.mode with
5304 | Textentry _ -> scalecolor 0.4
5305 | LinkNav _
5306 | View -> scalecolor 1.0
5307 | Birdseye (_, _, pageno, hooverpageno, _) ->
5308 if l.pageno = hooverpageno
5309 then scalecolor 0.9
5310 else (
5311 if l.pageno = pageno
5312 then scalecolor 1.0
5313 else scalecolor 0.8
5316 drawtiles l color;
5317 begin match getopaque l.pageno with
5318 | Some opaque ->
5319 if tileready l l.pagex l.pagey
5320 then
5321 let x = l.pagedispx - l.pagex
5322 and y = l.pagedispy - l.pagey in
5323 let hlmask =
5324 match conf.columns with
5325 | Csingle _ | Cmulti _ ->
5326 (if conf.hlinks then 1 else 0)
5327 + (if state.glinks
5328 && not (isbirdseye state.mode) then 2 else 0)
5329 | _ -> 0
5331 let s =
5332 match state.mode with
5333 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5334 | _ -> ""
5336 postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize);
5337 else 0
5339 | _ -> 0
5340 end;
5343 let scrollindicator () =
5344 let sbw, ph, sh = state.uioh#scrollph in
5345 let sbh, pw, sw = state.uioh#scrollpw in
5347 GlDraw.color (0.64, 0.64, 0.64);
5348 GlDraw.rect
5349 (float (conf.winw - sbw), 0.)
5350 (float conf.winw, float conf.winh)
5352 GlDraw.rect
5353 (0., float (conf.winh - sbh))
5354 (float (conf.winw - state.scrollw - 1), float conf.winh)
5356 GlDraw.color (0.0, 0.0, 0.0);
5358 GlDraw.rect
5359 (float (conf.winw - sbw), ph)
5360 (float conf.winw, ph +. sh)
5362 GlDraw.rect
5363 (pw, float (conf.winh - sbh))
5364 (pw +. sw, float conf.winh)
5368 let showsel () =
5369 match state.mstate with
5370 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5373 | Msel ((x0, y0), (x1, y1)) ->
5374 let rec loop = function
5375 | l :: ls ->
5376 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5377 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5378 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5379 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5380 then
5381 match getopaque l.pageno with
5382 | Some opaque ->
5383 let x0, y0 = pagetranslatepoint l x0 y0 in
5384 let x1, y1 = pagetranslatepoint l x1 y1 in
5385 seltext opaque (x0, y0, x1, y1);
5386 | _ -> ()
5387 else loop ls
5388 | [] -> ()
5390 loop state.layout
5393 let showrects rects =
5394 Gl.enable `blend;
5395 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5396 GlDraw.polygon_mode `both `fill;
5397 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5398 List.iter
5399 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5400 List.iter (fun l ->
5401 if l.pageno = pageno
5402 then (
5403 let dx = float (l.pagedispx - l.pagex) in
5404 let dy = float (l.pagedispy - l.pagey) in
5405 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5406 GlDraw.begins `quads;
5408 GlDraw.vertex2 (x0+.dx, y0+.dy);
5409 GlDraw.vertex2 (x1+.dx, y1+.dy);
5410 GlDraw.vertex2 (x2+.dx, y2+.dy);
5411 GlDraw.vertex2 (x3+.dx, y3+.dy);
5413 GlDraw.ends ();
5415 ) state.layout
5416 ) rects
5418 Gl.disable `blend;
5421 let display () =
5422 GlClear.color (scalecolor2 conf.bgcolor);
5423 GlClear.clear [`color];
5424 let rec loop linkindexbase = function
5425 | l :: rest ->
5426 let linkindexbase = linkindexbase + drawpage l linkindexbase in
5427 loop linkindexbase rest
5428 | [] -> ()
5430 loop 0 state.layout;
5431 let rects =
5432 match state.mode with
5433 | LinkNav (Ltexact (pageno, linkno)) ->
5434 begin match getopaque pageno with
5435 | Some opaque ->
5436 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5437 (pageno, 5, (
5438 float x0, float y0,
5439 float x1, float y0,
5440 float x1, float y1,
5441 float x0, float y1)
5442 ) :: state.rects
5443 | None -> state.rects
5445 | _ -> state.rects
5447 showrects rects;
5448 showsel ();
5449 state.uioh#display;
5450 begin match state.mstate with
5451 | Mzoomrect ((x0, y0), (x1, y1)) ->
5452 Gl.enable `blend;
5453 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5454 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5455 GlDraw.rect (float x0, float y0)
5456 (float x1, float y1);
5457 Gl.disable `blend;
5458 | _ -> ()
5459 end;
5460 enttext ();
5461 scrollindicator ();
5462 Wsi.swapb ();
5465 let zoomrect x y x1 y1 =
5466 let x0 = min x x1
5467 and x1 = max x x1
5468 and y0 = min y y1 in
5469 gotoy (state.y + y0);
5470 state.anchor <- getanchor ();
5471 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5472 let margin =
5473 if state.w < conf.winw - state.scrollw
5474 then (conf.winw - state.scrollw - state.w) / 2
5475 else 0
5477 state.x <- (state.x + margin) - x0;
5478 setzoom zoom;
5479 Wsi.setcursor Wsi.CURSOR_INHERIT;
5480 state.mstate <- Mnone;
5483 let scrollx x =
5484 let winw = conf.winw - state.scrollw - 1 in
5485 let s = float x /. float winw in
5486 let destx = truncate (float (state.w + winw) *. s) in
5487 state.x <- winw - destx;
5488 gotoy_and_clear_text state.y;
5489 state.mstate <- Mscrollx;
5492 let scrolly y =
5493 let s = float y /. float conf.winh in
5494 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5495 gotoy_and_clear_text desty;
5496 state.mstate <- Mscrolly;
5499 let viewmouse button down x y mask =
5500 match button with
5501 | n when (n == 4 || n == 5) && not down ->
5502 if Wsi.withctrl mask
5503 then (
5504 match state.mstate with
5505 | Mzoom (oldn, i) ->
5506 if oldn = n
5507 then (
5508 if i = 2
5509 then
5510 let incr =
5511 match n with
5512 | 5 ->
5513 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5514 | _ ->
5515 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5517 let zoom = conf.zoom -. incr in
5518 setzoom zoom;
5519 state.mstate <- Mzoom (n, 0);
5520 else
5521 state.mstate <- Mzoom (n, i+1);
5523 else state.mstate <- Mzoom (n, 0)
5525 | _ -> state.mstate <- Mzoom (n, 0)
5527 else (
5528 match state.autoscroll with
5529 | Some step -> setautoscrollspeed step (n=4)
5530 | None ->
5531 let incr =
5532 if n = 4
5533 then -conf.scrollstep
5534 else conf.scrollstep
5536 let incr = incr * 2 in
5537 let y = clamp incr in
5538 gotoy_and_clear_text y
5541 | n when (n = 6 || n = 7) && not down && canpan () ->
5542 state.x <- state.x + (if n = 7 then -2 else 2) * conf.hscrollstep;
5543 gotoy_and_clear_text state.y
5545 | 1 when Wsi.withctrl mask ->
5546 if down
5547 then (
5548 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5549 state.mstate <- Mpan (x, y)
5551 else
5552 state.mstate <- Mnone
5554 | 3 ->
5555 if down
5556 then (
5557 Wsi.setcursor Wsi.CURSOR_CYCLE;
5558 let p = (x, y) in
5559 state.mstate <- Mzoomrect (p, p)
5561 else (
5562 match state.mstate with
5563 | Mzoomrect ((x0, y0), _) ->
5564 if abs (x-x0) > 10 && abs (y - y0) > 10
5565 then zoomrect x0 y0 x y
5566 else (
5567 state.mstate <- Mnone;
5568 Wsi.setcursor Wsi.CURSOR_INHERIT;
5569 G.postRedisplay "kill accidental zoom rect";
5571 | _ ->
5572 Wsi.setcursor Wsi.CURSOR_INHERIT;
5573 state.mstate <- Mnone
5576 | 1 when x > conf.winw - state.scrollw ->
5577 if down
5578 then
5579 let _, position, sh = state.uioh#scrollph in
5580 if y > truncate position && y < truncate (position +. sh)
5581 then state.mstate <- Mscrolly
5582 else scrolly y
5583 else
5584 state.mstate <- Mnone
5586 | 1 when y > conf.winh - state.hscrollh ->
5587 if down
5588 then
5589 let _, position, sw = state.uioh#scrollpw in
5590 if x > truncate position && x < truncate (position +. sw)
5591 then state.mstate <- Mscrollx
5592 else scrollx x
5593 else
5594 state.mstate <- Mnone
5596 | 1 ->
5597 let dest = if down then getunder x y else Unone in
5598 begin match dest with
5599 | Ulinkgoto _
5600 | Ulinkuri _
5601 | Uremote _
5602 | Uunexpected _ | Ulaunch _ | Unamed _ ->
5603 gotounder dest
5605 | Unone when down ->
5606 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5607 state.mstate <- Mpan (x, y);
5609 | Unone | Utext _ ->
5610 if down
5611 then (
5612 if conf.angle mod 360 = 0
5613 then (
5614 state.mstate <- Msel ((x, y), (x, y));
5615 G.postRedisplay "mouse select";
5618 else (
5619 match state.mstate with
5620 | Mnone -> ()
5622 | Mzoom _ | Mscrollx | Mscrolly ->
5623 state.mstate <- Mnone
5625 | Mzoomrect ((x0, y0), _) ->
5626 zoomrect x0 y0 x y
5628 | Mpan _ ->
5629 Wsi.setcursor Wsi.CURSOR_INHERIT;
5630 state.mstate <- Mnone
5632 | Msel ((_, y0), (_, y1)) ->
5633 let rec loop = function
5634 | [] -> ()
5635 | l :: rest ->
5636 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5637 || ((y1 >= l.pagedispy
5638 && y1 <= (l.pagedispy + l.pagevh)))
5639 then
5640 match getopaque l.pageno with
5641 | Some opaque ->
5642 begin
5643 match Ne.pipe () with
5644 | Ne.Exn exn ->
5645 showtext '!'
5646 (Printf.sprintf
5647 "can not create sel pipe: %s"
5648 (Printexc.to_string exn));
5649 | Ne.Res (r, w) ->
5650 let doclose what fd =
5651 Ne.clo fd (fun msg ->
5652 dolog "%s close failed: %s" what msg)
5655 popen conf.selcmd [r, 0; w, -1];
5656 copysel w opaque;
5657 doclose "pipe/r" r;
5658 G.postRedisplay "copysel";
5659 with exn ->
5660 dolog "can not execute %S: %s"
5661 conf.selcmd (Printexc.to_string exn);
5662 doclose "pipe/r" r;
5663 doclose "pipe/w" w;
5665 | None -> ()
5666 else loop rest
5668 loop state.layout;
5669 Wsi.setcursor Wsi.CURSOR_INHERIT;
5670 state.mstate <- Mnone;
5674 | _ -> ()
5677 let birdseyemouse button down x y mask
5678 (conf, leftx, _, hooverpageno, anchor) =
5679 match button with
5680 | 1 when down ->
5681 let rec loop = function
5682 | [] -> ()
5683 | l :: rest ->
5684 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5685 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5686 then (
5687 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5689 else loop rest
5691 loop state.layout
5692 | 3 -> ()
5693 | _ -> viewmouse button down x y mask
5696 let mouse button down x y mask =
5697 state.uioh <- state.uioh#button button down x y mask;
5700 let motion ~x ~y =
5701 state.uioh <- state.uioh#motion x y
5704 let pmotion ~x ~y =
5705 state.uioh <- state.uioh#pmotion x y;
5708 let uioh = object
5709 method display = ()
5711 method key key mask =
5712 begin match state.mode with
5713 | Textentry textentry -> textentrykeyboard key mask textentry
5714 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5715 | View -> viewkeyboard key mask
5716 | LinkNav linknav -> linknavkeyboard key mask linknav
5717 end;
5718 state.uioh
5720 method button button bstate x y mask =
5721 begin match state.mode with
5722 | LinkNav _
5723 | View -> viewmouse button bstate x y mask
5724 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5725 | Textentry _ -> ()
5726 end;
5727 state.uioh
5729 method motion x y =
5730 begin match state.mode with
5731 | Textentry _ -> ()
5732 | View | Birdseye _ | LinkNav _ ->
5733 match state.mstate with
5734 | Mzoom _ | Mnone -> ()
5736 | Mpan (x0, y0) ->
5737 let dx = x - x0
5738 and dy = y0 - y in
5739 state.mstate <- Mpan (x, y);
5740 if canpan ()
5741 then state.x <- state.x + dx;
5742 let y = clamp dy in
5743 gotoy_and_clear_text y
5745 | Msel (a, _) ->
5746 state.mstate <- Msel (a, (x, y));
5747 G.postRedisplay "motion select";
5749 | Mscrolly ->
5750 let y = min conf.winh (max 0 y) in
5751 scrolly y
5753 | Mscrollx ->
5754 let x = min conf.winw (max 0 x) in
5755 scrollx x
5757 | Mzoomrect (p0, _) ->
5758 state.mstate <- Mzoomrect (p0, (x, y));
5759 G.postRedisplay "motion zoomrect";
5760 end;
5761 state.uioh
5763 method pmotion x y =
5764 begin match state.mode with
5765 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5766 let rec loop = function
5767 | [] ->
5768 if hooverpageno != -1
5769 then (
5770 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5771 G.postRedisplay "pmotion birdseye no hoover";
5773 | l :: rest ->
5774 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5775 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5776 then (
5777 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5778 G.postRedisplay "pmotion birdseye hoover";
5780 else loop rest
5782 loop state.layout
5784 | Textentry _ -> ()
5786 | LinkNav _
5787 | View ->
5788 match state.mstate with
5789 | Mnone -> updateunder x y
5790 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5792 end;
5793 state.uioh
5795 method infochanged _ = ()
5797 method scrollph =
5798 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5799 let p, h = scrollph state.y maxy in
5800 state.scrollw, p, h
5802 method scrollpw =
5803 let winw = conf.winw - state.scrollw - 1 in
5804 let fwinw = float winw in
5805 let sw =
5806 let sw = fwinw /. float state.w in
5807 let sw = fwinw *. sw in
5808 max sw (float conf.scrollh)
5810 let position, sw =
5811 let f = state.w+winw in
5812 let r = float (winw-state.x) /. float f in
5813 let p = fwinw *. r in
5814 p-.sw/.2., sw
5816 let sw =
5817 if position +. sw > fwinw
5818 then fwinw -. position
5819 else sw
5821 state.hscrollh, position, sw
5823 method modehash =
5824 let modename =
5825 match state.mode with
5826 | LinkNav _ -> "links"
5827 | Textentry _ -> "textentry"
5828 | Birdseye _ -> "birdseye"
5829 | View -> "view"
5831 findkeyhash conf modename
5832 end;;
5834 module Config =
5835 struct
5836 open Parser
5838 let fontpath = ref "";;
5840 module KeyMap =
5841 Map.Make (struct type t = (int * int) let compare = compare end);;
5843 let unent s =
5844 let l = String.length s in
5845 let b = Buffer.create l in
5846 unent b s 0 l;
5847 Buffer.contents b;
5850 let home =
5851 try Sys.getenv "HOME"
5852 with exn ->
5853 prerr_endline
5854 ("Can not determine home directory location: " ^
5855 Printexc.to_string exn);
5859 let modifier_of_string = function
5860 | "alt" -> Wsi.altmask
5861 | "shift" -> Wsi.shiftmask
5862 | "ctrl" | "control" -> Wsi.ctrlmask
5863 | "meta" -> Wsi.metamask
5864 | _ -> 0
5867 let key_of_string =
5868 let r = Str.regexp "-" in
5869 fun s ->
5870 let elems = Str.full_split r s in
5871 let f n k m =
5872 let g s =
5873 let m1 = modifier_of_string s in
5874 if m1 = 0
5875 then (Wsi.namekey s, m)
5876 else (k, m lor m1)
5877 in function
5878 | Str.Delim s when n land 1 = 0 -> g s
5879 | Str.Text s -> g s
5880 | Str.Delim _ -> (k, m)
5882 let rec loop n k m = function
5883 | [] -> (k, m)
5884 | x :: xs ->
5885 let k, m = f n k m x in
5886 loop (n+1) k m xs
5888 loop 0 0 0 elems
5891 let keys_of_string =
5892 let r = Str.regexp "[ \t]" in
5893 fun s ->
5894 let elems = Str.split r s in
5895 List.map key_of_string elems
5898 let copykeyhashes c =
5899 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
5902 let config_of c attrs =
5903 let apply c k v =
5905 match k with
5906 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5907 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5908 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5909 | "preload" -> { c with preload = bool_of_string v }
5910 | "page-bias" -> { c with pagebias = int_of_string v }
5911 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5912 | "horizontal-scroll-step" ->
5913 { c with hscrollstep = max (int_of_string v) 1 }
5914 | "auto-scroll-step" ->
5915 { c with autoscrollstep = max 0 (int_of_string v) }
5916 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5917 | "crop-hack" -> { c with crophack = bool_of_string v }
5918 | "throttle" ->
5919 let mw =
5920 match String.lowercase v with
5921 | "true" -> Some infinity
5922 | "false" -> None
5923 | f -> Some (float_of_string f)
5925 { c with maxwait = mw}
5926 | "highlight-links" -> { c with hlinks = bool_of_string v }
5927 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5928 | "vertical-margin" ->
5929 { c with interpagespace = max 0 (int_of_string v) }
5930 | "zoom" ->
5931 let zoom = float_of_string v /. 100. in
5932 let zoom = max zoom 0.0 in
5933 { c with zoom = zoom }
5934 | "presentation" -> { c with presentation = bool_of_string v }
5935 | "rotation-angle" -> { c with angle = int_of_string v }
5936 | "width" -> { c with winw = max 20 (int_of_string v) }
5937 | "height" -> { c with winh = max 20 (int_of_string v) }
5938 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5939 | "proportional-display" -> { c with proportional = bool_of_string v }
5940 | "pixmap-cache-size" ->
5941 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5942 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5943 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5944 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5945 | "persistent-location" -> { c with jumpback = bool_of_string v }
5946 | "background-color" -> { c with bgcolor = color_of_string v }
5947 | "scrollbar-in-presentation" ->
5948 { c with scrollbarinpm = bool_of_string v }
5949 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5950 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5951 | "mupdf-store-size" ->
5952 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5953 | "checkers" -> { c with checkers = bool_of_string v }
5954 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5955 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5956 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5957 | "uri-launcher" -> { c with urilauncher = unent v }
5958 | "path-launcher" -> { c with pathlauncher = unent v }
5959 | "color-space" -> { c with colorspace = colorspace_of_string v }
5960 | "invert-colors" -> { c with invert = bool_of_string v }
5961 | "brightness" -> { c with colorscale = float_of_string v }
5962 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5963 | "ghyllscroll" ->
5964 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5965 | "columns" ->
5966 let (n, _, _) as nab = multicolumns_of_string v in
5967 if n < 0
5968 then { c with columns = Csplit (-n, [||]) }
5969 else { c with columns = Cmulti (nab, [||]) }
5970 | "birds-eye-columns" ->
5971 { c with beyecolumns = Some (max (int_of_string v) 2) }
5972 | "selection-command" -> { c with selcmd = unent v }
5973 | "update-cursor" -> { c with updatecurs = bool_of_string v }
5974 | "hint-font-size" -> { c with hfsize = bound (int_of_string v) 5 100 }
5975 | "page-scroll-scale" -> { c with pgscale = float_of_string v }
5976 | _ -> c
5977 with exn ->
5978 prerr_endline ("Error processing attribute (`" ^
5979 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5982 let rec fold c = function
5983 | [] -> c
5984 | (k, v) :: rest ->
5985 let c = apply c k v in
5986 fold c rest
5988 fold { c with keyhashes = copykeyhashes c } attrs;
5991 let fromstring f pos n v d =
5992 try f v
5993 with exn ->
5994 dolog "Error processing attribute (%S=%S) at %d\n%s"
5995 n v pos (Printexc.to_string exn)
6000 let bookmark_of attrs =
6001 let rec fold title page rely visy = function
6002 | ("title", v) :: rest -> fold v page rely visy rest
6003 | ("page", v) :: rest -> fold title v rely visy rest
6004 | ("rely", v) :: rest -> fold title page v visy rest
6005 | ("visy", v) :: rest -> fold title page rely v rest
6006 | _ :: rest -> fold title page rely visy rest
6007 | [] -> title, page, rely, visy
6009 fold "invalid" "0" "0" "0" attrs
6012 let doc_of attrs =
6013 let rec fold path page rely pan visy = function
6014 | ("path", v) :: rest -> fold v page rely pan visy rest
6015 | ("page", v) :: rest -> fold path v rely pan visy rest
6016 | ("rely", v) :: rest -> fold path page v pan visy rest
6017 | ("pan", v) :: rest -> fold path page rely v visy rest
6018 | ("visy", v) :: rest -> fold path page rely pan v rest
6019 | _ :: rest -> fold path page rely pan visy rest
6020 | [] -> path, page, rely, pan, visy
6022 fold "" "0" "0" "0" "0" attrs
6025 let map_of attrs =
6026 let rec fold rs ls = function
6027 | ("out", v) :: rest -> fold v ls rest
6028 | ("in", v) :: rest -> fold rs v rest
6029 | _ :: rest -> fold ls rs rest
6030 | [] -> ls, rs
6032 fold "" "" attrs
6035 let setconf dst src =
6036 dst.scrollbw <- src.scrollbw;
6037 dst.scrollh <- src.scrollh;
6038 dst.icase <- src.icase;
6039 dst.preload <- src.preload;
6040 dst.pagebias <- src.pagebias;
6041 dst.verbose <- src.verbose;
6042 dst.scrollstep <- src.scrollstep;
6043 dst.maxhfit <- src.maxhfit;
6044 dst.crophack <- src.crophack;
6045 dst.autoscrollstep <- src.autoscrollstep;
6046 dst.maxwait <- src.maxwait;
6047 dst.hlinks <- src.hlinks;
6048 dst.underinfo <- src.underinfo;
6049 dst.interpagespace <- src.interpagespace;
6050 dst.zoom <- src.zoom;
6051 dst.presentation <- src.presentation;
6052 dst.angle <- src.angle;
6053 dst.winw <- src.winw;
6054 dst.winh <- src.winh;
6055 dst.savebmarks <- src.savebmarks;
6056 dst.memlimit <- src.memlimit;
6057 dst.proportional <- src.proportional;
6058 dst.texcount <- src.texcount;
6059 dst.sliceheight <- src.sliceheight;
6060 dst.thumbw <- src.thumbw;
6061 dst.jumpback <- src.jumpback;
6062 dst.bgcolor <- src.bgcolor;
6063 dst.scrollbarinpm <- src.scrollbarinpm;
6064 dst.tilew <- src.tilew;
6065 dst.tileh <- src.tileh;
6066 dst.mustoresize <- src.mustoresize;
6067 dst.checkers <- src.checkers;
6068 dst.aalevel <- src.aalevel;
6069 dst.trimmargins <- src.trimmargins;
6070 dst.trimfuzz <- src.trimfuzz;
6071 dst.urilauncher <- src.urilauncher;
6072 dst.colorspace <- src.colorspace;
6073 dst.invert <- src.invert;
6074 dst.colorscale <- src.colorscale;
6075 dst.redirectstderr <- src.redirectstderr;
6076 dst.ghyllscroll <- src.ghyllscroll;
6077 dst.columns <- src.columns;
6078 dst.beyecolumns <- src.beyecolumns;
6079 dst.selcmd <- src.selcmd;
6080 dst.updatecurs <- src.updatecurs;
6081 dst.pathlauncher <- src.pathlauncher;
6082 dst.keyhashes <- copykeyhashes src;
6083 dst.hfsize <- src.hfsize;
6084 dst.hscrollstep <- src.hscrollstep;
6085 dst.pgscale <- src.pgscale;
6088 let get s =
6089 let h = Hashtbl.create 10 in
6090 let dc = { defconf with angle = defconf.angle } in
6091 let rec toplevel v t spos _ =
6092 match t with
6093 | Vdata | Vcdata | Vend -> v
6094 | Vopen ("llppconfig", _, closed) ->
6095 if closed
6096 then v
6097 else { v with f = llppconfig }
6098 | Vopen _ ->
6099 error "unexpected subelement at top level" s spos
6100 | Vclose _ -> error "unexpected close at top level" s spos
6102 and llppconfig v t spos _ =
6103 match t with
6104 | Vdata | Vcdata -> v
6105 | Vend -> error "unexpected end of input in llppconfig" s spos
6106 | Vopen ("defaults", attrs, closed) ->
6107 let c = config_of dc attrs in
6108 setconf dc c;
6109 if closed
6110 then v
6111 else { v with f = defaults }
6113 | Vopen ("ui-font", attrs, closed) ->
6114 let rec getsize size = function
6115 | [] -> size
6116 | ("size", v) :: rest ->
6117 let size =
6118 fromstring int_of_string spos "size" v fstate.fontsize in
6119 getsize size rest
6120 | l -> getsize size l
6122 fstate.fontsize <- getsize fstate.fontsize attrs;
6123 if closed
6124 then v
6125 else { v with f = uifont (Buffer.create 10) }
6127 | Vopen ("doc", attrs, closed) ->
6128 let pathent, spage, srely, span, svisy = doc_of attrs in
6129 let path = unent pathent
6130 and pageno = fromstring int_of_string spos "page" spage 0
6131 and rely = fromstring float_of_string spos "rely" srely 0.0
6132 and pan = fromstring int_of_string spos "pan" span 0
6133 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6134 let c = config_of dc attrs in
6135 let anchor = (pageno, rely, visy) in
6136 if closed
6137 then (Hashtbl.add h path (c, [], pan, anchor); v)
6138 else { v with f = doc path pan anchor c [] }
6140 | Vopen _ ->
6141 error "unexpected subelement in llppconfig" s spos
6143 | Vclose "llppconfig" -> { v with f = toplevel }
6144 | Vclose _ -> error "unexpected close in llppconfig" s spos
6146 and defaults v t spos _ =
6147 match t with
6148 | Vdata | Vcdata -> v
6149 | Vend -> error "unexpected end of input in defaults" s spos
6150 | Vopen ("keymap", attrs, closed) ->
6151 let modename =
6152 try List.assoc "mode" attrs
6153 with Not_found -> "global" in
6154 if closed
6155 then v
6156 else
6157 let ret keymap =
6158 let h = findkeyhash dc modename in
6159 KeyMap.iter (Hashtbl.replace h) keymap;
6160 defaults
6162 { v with f = pkeymap ret KeyMap.empty }
6164 | Vopen (_, _, _) ->
6165 error "unexpected subelement in defaults" s spos
6167 | Vclose "defaults" ->
6168 { v with f = llppconfig }
6170 | Vclose _ -> error "unexpected close in defaults" s spos
6172 and uifont b v t spos epos =
6173 match t with
6174 | Vdata | Vcdata ->
6175 Buffer.add_substring b s spos (epos - spos);
6177 | Vopen (_, _, _) ->
6178 error "unexpected subelement in ui-font" s spos
6179 | Vclose "ui-font" ->
6180 if String.length !fontpath = 0
6181 then fontpath := Buffer.contents b;
6182 { v with f = llppconfig }
6183 | Vclose _ -> error "unexpected close in ui-font" s spos
6184 | Vend -> error "unexpected end of input in ui-font" s spos
6186 and doc path pan anchor c bookmarks v t spos _ =
6187 match t with
6188 | Vdata | Vcdata -> v
6189 | Vend -> error "unexpected end of input in doc" s spos
6190 | Vopen ("bookmarks", _, closed) ->
6191 if closed
6192 then v
6193 else { v with f = pbookmarks path pan anchor c bookmarks }
6195 | Vopen ("keymap", attrs, closed) ->
6196 let modename =
6197 try List.assoc "mode" attrs
6198 with Not_found -> "global"
6200 if closed
6201 then v
6202 else
6203 let ret keymap =
6204 let h = findkeyhash c modename in
6205 KeyMap.iter (Hashtbl.replace h) keymap;
6206 doc path pan anchor c bookmarks
6208 { v with f = pkeymap ret KeyMap.empty }
6210 | Vopen (_, _, _) ->
6211 error "unexpected subelement in doc" s spos
6213 | Vclose "doc" ->
6214 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
6215 { v with f = llppconfig }
6217 | Vclose _ -> error "unexpected close in doc" s spos
6219 and pkeymap ret keymap v t spos _ =
6220 match t with
6221 | Vdata | Vcdata -> v
6222 | Vend -> error "unexpected end of input in keymap" s spos
6223 | Vopen ("map", attrs, closed) ->
6224 let r, l = map_of attrs in
6225 let kss = fromstring keys_of_string spos "in" r [] in
6226 let lss = fromstring keys_of_string spos "out" l [] in
6227 let keymap =
6228 match kss with
6229 | [] -> keymap
6230 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
6231 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
6233 if closed
6234 then { v with f = pkeymap ret keymap }
6235 else
6236 let f () = v in
6237 { v with f = skip "map" f }
6239 | Vopen _ ->
6240 error "unexpected subelement in keymap" s spos
6242 | Vclose "keymap" ->
6243 { v with f = ret keymap }
6245 | Vclose _ -> error "unexpected close in keymap" s spos
6247 and pbookmarks path pan anchor c bookmarks v t spos _ =
6248 match t with
6249 | Vdata | Vcdata -> v
6250 | Vend -> error "unexpected end of input in bookmarks" s spos
6251 | Vopen ("item", attrs, closed) ->
6252 let titleent, spage, srely, svisy = bookmark_of attrs in
6253 let page = fromstring int_of_string spos "page" spage 0
6254 and rely = fromstring float_of_string spos "rely" srely 0.0
6255 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6256 let bookmarks =
6257 (unent titleent, 0, (page, rely, visy)) :: bookmarks
6259 if closed
6260 then { v with f = pbookmarks path pan anchor c bookmarks }
6261 else
6262 let f () = v in
6263 { v with f = skip "item" f }
6265 | Vopen _ ->
6266 error "unexpected subelement in bookmarks" s spos
6268 | Vclose "bookmarks" ->
6269 { v with f = doc path pan anchor c bookmarks }
6271 | Vclose _ -> error "unexpected close in bookmarks" s spos
6273 and skip tag f v t spos _ =
6274 match t with
6275 | Vdata | Vcdata -> v
6276 | Vend ->
6277 error ("unexpected end of input in skipped " ^ tag) s spos
6278 | Vopen (tag', _, closed) ->
6279 if closed
6280 then v
6281 else
6282 let f' () = { v with f = skip tag f } in
6283 { v with f = skip tag' f' }
6284 | Vclose ctag ->
6285 if tag = ctag
6286 then f ()
6287 else error ("unexpected close in skipped " ^ tag) s spos
6290 parse { f = toplevel; accu = () } s;
6291 h, dc;
6294 let do_load f ic =
6296 let len = in_channel_length ic in
6297 let s = String.create len in
6298 really_input ic s 0 len;
6299 f s;
6300 with
6301 | Parse_error (msg, s, pos) ->
6302 let subs = subs s pos in
6303 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6304 failwith ("parse error: " ^ s)
6306 | exn ->
6307 failwith ("config load error: " ^ Printexc.to_string exn)
6310 let defconfpath =
6311 let dir =
6313 let dir = Filename.concat home ".config" in
6314 if Sys.is_directory dir then dir else home
6315 with _ -> home
6317 Filename.concat dir "llpp.conf"
6320 let confpath = ref defconfpath;;
6322 let load1 f =
6323 if Sys.file_exists !confpath
6324 then
6325 match
6326 (try Some (open_in_bin !confpath)
6327 with exn ->
6328 prerr_endline
6329 ("Error opening configuation file `" ^ !confpath ^ "': " ^
6330 Printexc.to_string exn);
6331 None
6333 with
6334 | Some ic ->
6335 begin try
6336 f (do_load get ic)
6337 with exn ->
6338 prerr_endline
6339 ("Error loading configuation from `" ^ !confpath ^ "': " ^
6340 Printexc.to_string exn);
6341 end;
6342 close_in ic;
6344 | None -> ()
6345 else
6346 f (Hashtbl.create 0, defconf)
6349 let load () =
6350 let f (h, dc) =
6351 let pc, pb, px, pa =
6353 Hashtbl.find h (Filename.basename state.path)
6354 with Not_found -> dc, [], 0, emptyanchor
6356 setconf defconf dc;
6357 setconf conf pc;
6358 state.bookmarks <- pb;
6359 state.x <- px;
6360 state.scrollw <- conf.scrollbw;
6361 if conf.jumpback
6362 then state.anchor <- pa;
6363 cbput state.hists.nav pa;
6365 load1 f
6368 let add_attrs bb always dc c =
6369 let ob s a b =
6370 if always || a != b
6371 then Printf.bprintf bb "\n %s='%b'" s a
6372 and oi s a b =
6373 if always || a != b
6374 then Printf.bprintf bb "\n %s='%d'" s a
6375 and oI s a b =
6376 if always || a != b
6377 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6378 and oz s a b =
6379 if always || a <> b
6380 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
6381 and oF s a b =
6382 if always || a <> b
6383 then Printf.bprintf bb "\n %s='%f'" s a
6384 and oc s a b =
6385 if always || a <> b
6386 then
6387 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6388 and oC s a b =
6389 if always || a <> b
6390 then
6391 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6392 and oR s a b =
6393 if always || a <> b
6394 then
6395 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6396 and os s a b =
6397 if always || a <> b
6398 then
6399 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6400 and og s a b =
6401 if always || a <> b
6402 then
6403 match a with
6404 | None -> ()
6405 | Some (_N, _A, _B) ->
6406 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6407 and oW s a b =
6408 if always || a <> b
6409 then
6410 let v =
6411 match a with
6412 | None -> "false"
6413 | Some f ->
6414 if f = infinity
6415 then "true"
6416 else string_of_float f
6418 Printf.bprintf bb "\n %s='%s'" s v
6419 and oco s a b =
6420 if always || a <> b
6421 then
6422 match a with
6423 | Cmulti ((n, a, b), _) when n > 1 ->
6424 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6425 | Csplit (n, _) when n > 1 ->
6426 Printf.bprintf bb "\n %s='%d'" s ~-n
6427 | _ -> ()
6428 and obeco s a b =
6429 if always || a <> b
6430 then
6431 match a with
6432 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6433 | _ -> ()
6435 let w, h =
6436 if always
6437 then dc.winw, dc.winh
6438 else
6439 match state.fullscreen with
6440 | Some wh -> wh
6441 | None -> c.winw, c.winh
6443 oi "width" w dc.winw;
6444 oi "height" h dc.winh;
6445 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6446 oi "scroll-handle-height" c.scrollh dc.scrollh;
6447 ob "case-insensitive-search" c.icase dc.icase;
6448 ob "preload" c.preload dc.preload;
6449 oi "page-bias" c.pagebias dc.pagebias;
6450 oi "scroll-step" c.scrollstep dc.scrollstep;
6451 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6452 ob "max-height-fit" c.maxhfit dc.maxhfit;
6453 ob "crop-hack" c.crophack dc.crophack;
6454 oW "throttle" c.maxwait dc.maxwait;
6455 ob "highlight-links" c.hlinks dc.hlinks;
6456 ob "under-cursor-info" c.underinfo dc.underinfo;
6457 oi "vertical-margin" c.interpagespace dc.interpagespace;
6458 oz "zoom" c.zoom dc.zoom;
6459 ob "presentation" c.presentation dc.presentation;
6460 oi "rotation-angle" c.angle dc.angle;
6461 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6462 ob "proportional-display" c.proportional dc.proportional;
6463 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6464 oi "tex-count" c.texcount dc.texcount;
6465 oi "slice-height" c.sliceheight dc.sliceheight;
6466 oi "thumbnail-width" c.thumbw dc.thumbw;
6467 ob "persistent-location" c.jumpback dc.jumpback;
6468 oc "background-color" c.bgcolor dc.bgcolor;
6469 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6470 oi "tile-width" c.tilew dc.tilew;
6471 oi "tile-height" c.tileh dc.tileh;
6472 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6473 ob "checkers" c.checkers dc.checkers;
6474 oi "aalevel" c.aalevel dc.aalevel;
6475 ob "trim-margins" c.trimmargins dc.trimmargins;
6476 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6477 os "uri-launcher" c.urilauncher dc.urilauncher;
6478 os "path-launcher" c.pathlauncher dc.pathlauncher;
6479 oC "color-space" c.colorspace dc.colorspace;
6480 ob "invert-colors" c.invert dc.invert;
6481 oF "brightness" c.colorscale dc.colorscale;
6482 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6483 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6484 oco "columns" c.columns dc.columns;
6485 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6486 os "selection-command" c.selcmd dc.selcmd;
6487 ob "update-cursor" c.updatecurs dc.updatecurs;
6488 oi "hint-font-size" c.hfsize dc.hfsize;
6489 oi "horizontal-scroll-step" c.hscrollstep dc.hscrollstep;
6490 oF "page-scroll-scale" c.pgscale dc.pgscale;
6493 let keymapsbuf always dc c =
6494 let bb = Buffer.create 16 in
6495 let rec loop = function
6496 | [] -> ()
6497 | (modename, h) :: rest ->
6498 let dh = findkeyhash dc modename in
6499 if always || h <> dh
6500 then (
6501 if Hashtbl.length h > 0
6502 then (
6503 if Buffer.length bb > 0
6504 then Buffer.add_char bb '\n';
6505 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6506 Hashtbl.iter (fun i o ->
6507 let isdifferent = always ||
6509 let dO = Hashtbl.find dh i in
6510 dO <> o
6511 with Not_found -> true
6513 if isdifferent
6514 then
6515 let addkm (k, m) =
6516 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6517 if Wsi.withalt m then Buffer.add_string bb "alt-";
6518 if Wsi.withshift m then Buffer.add_string bb "shift-";
6519 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6520 Buffer.add_string bb (Wsi.keyname k);
6522 let addkms l =
6523 let rec loop = function
6524 | [] -> ()
6525 | km :: [] -> addkm km
6526 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6528 loop l
6530 Buffer.add_string bb "<map in='";
6531 addkm i;
6532 match o with
6533 | KMinsrt km ->
6534 Buffer.add_string bb "' out='";
6535 addkm km;
6536 Buffer.add_string bb "'/>\n"
6538 | KMinsrl kms ->
6539 Buffer.add_string bb "' out='";
6540 addkms kms;
6541 Buffer.add_string bb "'/>\n"
6543 | KMmulti (ins, kms) ->
6544 Buffer.add_char bb ' ';
6545 addkms ins;
6546 Buffer.add_string bb "' out='";
6547 addkms kms;
6548 Buffer.add_string bb "'/>\n"
6549 ) h;
6550 Buffer.add_string bb "</keymap>";
6553 loop rest
6555 loop c.keyhashes;
6559 let save () =
6560 let uifontsize = fstate.fontsize in
6561 let bb = Buffer.create 32768 in
6562 let f (h, dc) =
6563 let dc = if conf.bedefault then conf else dc in
6564 Buffer.add_string bb "<llppconfig>\n";
6566 if String.length !fontpath > 0
6567 then
6568 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6569 uifontsize
6570 !fontpath
6571 else (
6572 if uifontsize <> 14
6573 then
6574 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6577 Buffer.add_string bb "<defaults ";
6578 add_attrs bb true dc dc;
6579 let kb = keymapsbuf true dc dc in
6580 if Buffer.length kb > 0
6581 then (
6582 Buffer.add_string bb ">\n";
6583 Buffer.add_buffer bb kb;
6584 Buffer.add_string bb "\n</defaults>\n";
6586 else Buffer.add_string bb "/>\n";
6588 let adddoc path pan anchor c bookmarks =
6589 if bookmarks == [] && c = dc && anchor = emptyanchor
6590 then ()
6591 else (
6592 Printf.bprintf bb "<doc path='%s'"
6593 (enent path 0 (String.length path));
6595 if anchor <> emptyanchor
6596 then (
6597 let n, rely, visy = anchor in
6598 Printf.bprintf bb " page='%d'" n;
6599 if rely > 1e-6
6600 then
6601 Printf.bprintf bb " rely='%f'" rely
6603 if visy > 1e-6
6604 then
6605 Printf.bprintf bb " visy='%f'" visy
6609 if pan != 0
6610 then Printf.bprintf bb " pan='%d'" pan;
6612 add_attrs bb false dc c;
6613 let kb = keymapsbuf false dc c in
6615 begin match bookmarks with
6616 | [] ->
6617 if Buffer.length kb > 0
6618 then (
6619 Buffer.add_string bb ">\n";
6620 Buffer.add_buffer bb kb;
6621 Buffer.add_string bb "\n</doc>\n";
6623 else Buffer.add_string bb "/>\n"
6624 | _ ->
6625 Buffer.add_string bb ">\n<bookmarks>\n";
6626 List.iter (fun (title, _level, (page, rely, visy)) ->
6627 Printf.bprintf bb
6628 "<item title='%s' page='%d'"
6629 (enent title 0 (String.length title))
6630 page
6632 if rely > 1e-6
6633 then
6634 Printf.bprintf bb " rely='%f'" rely
6636 if visy > 1e-6
6637 then
6638 Printf.bprintf bb " visy='%f'" visy
6640 Buffer.add_string bb "/>\n";
6641 ) bookmarks;
6642 Buffer.add_string bb "</bookmarks>";
6643 if Buffer.length kb > 0
6644 then (
6645 Buffer.add_string bb "\n";
6646 Buffer.add_buffer bb kb;
6648 Buffer.add_string bb "\n</doc>\n";
6649 end;
6653 let pan, conf =
6654 match state.mode with
6655 | Birdseye (c, pan, _, _, _) ->
6656 let beyecolumns =
6657 match conf.columns with
6658 | Cmulti ((c, _, _), _) -> Some c
6659 | Csingle _ -> None
6660 | Csplit _ -> None
6661 and columns =
6662 match c.columns with
6663 | Cmulti (c, _) -> Cmulti (c, [||])
6664 | Csingle _ -> Csingle [||]
6665 | Csplit _ -> failwith "quit from bird's eye while split"
6667 pan, { c with beyecolumns = beyecolumns; columns = columns }
6668 | _ -> state.x, conf
6670 let basename = Filename.basename state.path in
6671 adddoc basename pan (getanchor ())
6672 (let conf =
6673 let autoscrollstep =
6674 match state.autoscroll with
6675 | Some step -> step
6676 | None -> conf.autoscrollstep
6678 match state.mode with
6679 | Birdseye (bc, _, _, _, _) ->
6680 { conf with
6681 zoom = bc.zoom;
6682 presentation = bc.presentation;
6683 interpagespace = bc.interpagespace;
6684 maxwait = bc.maxwait;
6685 autoscrollstep = autoscrollstep }
6686 | _ -> { conf with autoscrollstep = autoscrollstep }
6687 in conf)
6688 (if conf.savebmarks then state.bookmarks else []);
6690 Hashtbl.iter (fun path (c, bookmarks, x, anchor) ->
6691 if basename <> path
6692 then adddoc path x anchor c bookmarks
6693 ) h;
6694 Buffer.add_string bb "</llppconfig>";
6696 load1 f;
6697 if Buffer.length bb > 0
6698 then
6700 let tmp = !confpath ^ ".tmp" in
6701 let oc = open_out_bin tmp in
6702 Buffer.output_buffer oc bb;
6703 close_out oc;
6704 Unix.rename tmp !confpath;
6705 with exn ->
6706 prerr_endline
6707 ("error while saving configuration: " ^ Printexc.to_string exn)
6709 end;;
6711 let () =
6712 let trimcachepath = ref "" in
6713 Arg.parse
6714 (Arg.align
6715 [("-p", Arg.String (fun s -> state.password <- s) ,
6716 "<password> Set password");
6718 ("-f", Arg.String (fun s -> Config.fontpath := s),
6719 "<path> Set path to the user interface font");
6721 ("-c", Arg.String (fun s -> Config.confpath := s),
6722 "<path> Set path to the configuration file");
6724 ("-tcf", Arg.String (fun s -> trimcachepath := s),
6725 "<path> Set path to the trim cache file");
6727 ("-v", Arg.Unit (fun () ->
6728 Printf.printf
6729 "%s\nconfiguration path: %s\n"
6730 (version ())
6731 Config.defconfpath
6733 exit 0), " Print version and exit");
6736 (fun s -> state.path <- s)
6737 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6739 if String.length state.path = 0
6740 then (prerr_endline "file name missing"; exit 1);
6742 Config.load ();
6744 let globalkeyhash = findkeyhash conf "global" in
6745 let wsfd, winw, winh = Wsi.init (object
6746 method expose =
6747 if nogeomcmds state.geomcmds || platform == Posx
6748 then display ()
6749 else (
6750 GlClear.color (scalecolor2 conf.bgcolor);
6751 GlClear.clear [`color];
6753 method display = display ()
6754 method reshape w h = reshape w h
6755 method mouse b d x y m = mouse b d x y m
6756 method motion x y = state.mpos <- (x, y); motion x y
6757 method pmotion x y = state.mpos <- (x, y); pmotion x y
6758 method key k m =
6759 let mascm = m land (
6760 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
6761 ) in
6762 match state.keystate with
6763 | KSnone ->
6764 let km = k, mascm in
6765 begin
6766 match
6767 let modehash = state.uioh#modehash in
6768 try Hashtbl.find modehash km
6769 with Not_found ->
6770 try Hashtbl.find globalkeyhash km
6771 with Not_found -> KMinsrt (k, m)
6772 with
6773 | KMinsrt (k, m) -> keyboard k m
6774 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6775 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6777 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
6778 List.iter (fun (k, m) -> keyboard k m) insrt;
6779 state.keystate <- KSnone
6780 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
6781 state.keystate <- KSinto (keys, insrt)
6782 | _ ->
6783 state.keystate <- KSnone
6785 method enter x y = state.mpos <- (x, y); pmotion x y
6786 method leave = state.mpos <- (-1, -1)
6787 method quit = raise Quit
6788 end) conf.winw conf.winh (platform = Posx) in
6790 state.wsfd <- wsfd;
6792 if not (
6793 List.exists GlMisc.check_extension
6794 [ "GL_ARB_texture_rectangle"
6795 ; "GL_EXT_texture_recangle"
6796 ; "GL_NV_texture_rectangle" ]
6798 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
6800 let cr, sw =
6801 match Ne.pipe () with
6802 | Ne.Exn exn ->
6803 Printf.eprintf "pipe/crsw failed: %s" (Printexc.to_string exn);
6804 exit 1
6805 | Ne.Res rw -> rw
6806 and sr, cw =
6807 match Ne.pipe () with
6808 | Ne.Exn exn ->
6809 Printf.eprintf "pipe/srcw failed: %s" (Printexc.to_string exn);
6810 exit 1
6811 | Ne.Res rw -> rw
6814 cloexec cr;
6815 cloexec sw;
6816 cloexec sr;
6817 cloexec cw;
6819 setcheckers conf.checkers;
6820 redirectstderr ();
6822 init (cr, cw) (
6823 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
6824 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6825 !Config.fontpath, !trimcachepath
6827 state.sr <- sr;
6828 state.sw <- sw;
6829 state.text <- "Opening " ^ state.path;
6830 reshape winw winh;
6831 opendoc state.path state.password;
6832 state.uioh <- uioh;
6834 let rec loop deadline =
6835 let r =
6836 match state.errfd with
6837 | None -> [state.sr; state.wsfd]
6838 | Some fd -> [state.sr; state.wsfd; fd]
6840 if state.redisplay
6841 then (
6842 state.redisplay <- false;
6843 display ();
6845 let timeout =
6846 let now = now () in
6847 if deadline > now
6848 then (
6849 if deadline = infinity
6850 then ~-.1.0
6851 else max 0.0 (deadline -. now)
6853 else 0.0
6855 let r, _, _ =
6856 try Unix.select r [] [] timeout
6857 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
6859 begin match r with
6860 | [] ->
6861 state.ghyll None;
6862 let newdeadline =
6863 if state.ghyll == noghyll
6864 then
6865 match state.autoscroll with
6866 | Some step when step != 0 ->
6867 let y = state.y + step in
6868 let y =
6869 if y < 0
6870 then state.maxy
6871 else if y >= state.maxy then 0 else y
6873 gotoy y;
6874 if state.mode = View
6875 then state.text <- "";
6876 deadline +. 0.01
6877 | _ -> infinity
6878 else deadline +. 0.01
6880 loop newdeadline
6882 | l ->
6883 let rec checkfds = function
6884 | [] -> ()
6885 | fd :: rest when fd = state.sr ->
6886 let cmd = readcmd state.sr in
6887 act cmd;
6888 checkfds rest
6890 | fd :: rest when fd = state.wsfd ->
6891 Wsi.readresp fd;
6892 checkfds rest
6894 | fd :: rest ->
6895 let s = String.create 80 in
6896 let n = Unix.read fd s 0 80 in
6897 if conf.redirectstderr
6898 then (
6899 Buffer.add_substring state.errmsgs s 0 n;
6900 state.newerrmsgs <- true;
6901 state.redisplay <- true;
6903 else (
6904 prerr_string (String.sub s 0 n);
6905 flush stderr;
6907 checkfds rest
6909 checkfds l;
6910 let newdeadline =
6911 let deadline1 =
6912 if deadline = infinity
6913 then now () +. 0.01
6914 else deadline
6916 match state.autoscroll with
6917 | Some step when step != 0 -> deadline1
6918 | _ -> if state.ghyll == noghyll then infinity else deadline1
6920 loop newdeadline
6921 end;
6924 loop infinity;
6925 with Quit ->
6926 Config.save ();