Add opendocument(s)
[llpp.git] / main.ml
blob195e7ceb7b861650e9d0e6f03cc7244b47b87ab4
1 open Utils;;
3 exception Quit;;
5 type under =
6 | Unone
7 | Ulinkuri of string
8 | Ulinkgoto of (int * int)
9 | Utext of facename
10 | Uunexpected of string
11 | Ulaunch of string
12 | Unamed of string
13 | Uremote of (string * int)
14 and facename = string;;
16 type params = (angle * fitmodel * trimparams
17 * texcount * sliceheight * memsize
18 * colorspace * fontpath * trimcachepath
19 * haspbo)
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 trimmargins = bool
29 and interpagespace = int
30 and texcount = int
31 and sliceheight = int
32 and gen = int
33 and top = float
34 and dtop = float
35 and fontpath = string
36 and trimcachepath = string
37 and memsize = int
38 and aalevel = int
39 and irect = (int * int * int * int)
40 and trimparams = (trimmargins * irect)
41 and colorspace = | Rgb | Bgr | Gray
42 and fitmodel = | FitWidth | FitProportional | FitPage
43 and haspbo = bool
46 type x = int
47 and y = int
48 and tilex = int
49 and tiley = int
50 and tileparams = (x * y * width * height * tilex * tiley)
53 type link =
54 | Lnotfound
55 | Lfound of int
56 and linkdir =
57 | LDfirst
58 | LDlast
59 | LDfirstvisible of (int * int * int)
60 | LDleft of int
61 | LDright of int
62 | LDdown of int
63 | LDup of int
66 type pagewithlinks =
67 | Pwlnotfound
68 | Pwl of int
71 type keymap =
72 | KMinsrt of key
73 | KMinsrl of key list
74 | KMmulti of key list * key list
75 and key = int * int
76 and keyhash = (key, keymap) Hashtbl.t
77 and keystate =
78 | KSnone
79 | KSinto of (key list * key list)
82 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
83 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
85 type pipe = (Unix.file_descr * Unix.file_descr);;
87 external init : pipe -> params -> unit = "ml_init";;
88 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
89 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
90 external getpdimrect : int -> float array = "ml_getpdimrect";;
91 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
92 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
93 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
94 external measurestr : int -> string -> float = "ml_measure_string";;
95 external postprocess :
96 opaque -> int -> int -> int -> (int * string * int) -> int
97 = "ml_postprocess";;
98 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
99 external platform : unit -> platform = "ml_platform";;
100 external setaalevel : int -> unit = "ml_setaalevel";;
101 external realloctexts : int -> bool = "ml_realloctexts";;
102 external findlink : opaque -> linkdir -> link = "ml_findlink";;
103 external getlink : opaque -> int -> under = "ml_getlink";;
104 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
105 external getlinkcount : opaque -> int = "ml_getlinkcount";;
106 external findpwl : int -> int -> pagewithlinks = "ml_find_page_with_links"
107 external popen : string -> (Unix.file_descr * int) list -> unit = "ml_popen";;
108 external getpbo : width -> height -> colorspace -> string = "ml_getpbo";;
109 external freepbo : string -> unit = "ml_freepbo";;
110 external unmappbo : string -> unit = "ml_unmappbo";;
111 external pbousable : unit -> bool = "ml_pbo_usable";;
112 external unproject : opaque -> int -> int -> (int * int) option
113 = "ml_unproject";;
114 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
116 let platform_to_string = function
117 | Punknown -> "unknown"
118 | Plinux -> "Linux"
119 | Posx -> "OSX"
120 | Psun -> "Sun"
121 | Pfreebsd -> "FreeBSD"
122 | Pdragonflybsd -> "DragonflyBSD"
123 | Popenbsd -> "OpenBSD"
124 | Pnetbsd -> "NetBSD"
125 | Pcygwin -> "Cygwin"
128 let platform = platform ();;
130 let now = Unix.gettimeofday;;
132 let popen cmd fda =
133 if platform = Pcygwin
134 then (
135 let sh = "/bin/sh" in
136 let args = [|sh; "-c"; cmd|] in
137 let rec std si so se = function
138 | [] -> si, so, se
139 | (fd, 0) :: rest -> std fd so se rest
140 | (fd, -1) :: rest ->
141 Unix.set_close_on_exec fd;
142 std si so se rest
143 | (_, n) :: _ ->
144 failwith ("unexpected fdn in cygwin popen " ^ string_of_int n)
146 let si, so, se = std Unix.stdin Unix.stdout Unix.stderr fda in
147 ignore (Unix.create_process sh args si so se)
149 else popen cmd fda;
152 type mpos = int * int
153 and mstate =
154 | Msel of (mpos * mpos)
155 | Mpan of mpos
156 | Mscrolly | Mscrollx
157 | Mzoom of (int * int)
158 | Mzoomrect of (mpos * mpos)
159 | Mnone
162 type textentry = string * string * onhist option * onkey * ondone * cancelonempty
163 and onkey = string -> int -> te
164 and ondone = string -> unit
165 and histcancel = unit -> unit
166 and onhist = ((histcmd -> string) * histcancel)
167 and histcmd = HCnext | HCprev | HCfirst | HClast
168 and cancelonempty = bool
169 and te =
170 | TEstop
171 | TEdone of string
172 | TEcont of string
173 | TEswitch of textentry
176 type 'a circbuf =
177 { store : 'a array
178 ; mutable rc : int
179 ; mutable wc : int
180 ; mutable len : int
184 let bound v minv maxv =
185 max minv (min maxv v);
188 let cbnew n v =
189 { store = Array.create n v
190 ; rc = 0
191 ; wc = 0
192 ; len = 0
196 let cbcap b = Array.length b.store;;
198 let cbput b v =
199 let cap = cbcap b in
200 b.store.(b.wc) <- v;
201 b.wc <- (b.wc + 1) mod cap;
202 b.rc <- b.wc;
203 b.len <- min (b.len + 1) cap;
206 let cbempty b = b.len = 0;;
208 let cbgetg b circular dir =
209 if cbempty b
210 then b.store.(0)
211 else
212 let rc = b.rc + dir in
213 let rc =
214 if circular
215 then (
216 if rc = -1
217 then b.len-1
218 else (
219 if rc >= b.len
220 then 0
221 else rc
224 else bound rc 0 (b.len-1)
226 b.rc <- rc;
227 b.store.(rc);
230 let cbget b = cbgetg b false;;
231 let cbgetc b = cbgetg b true;;
233 let drawstring size x y s =
234 Gl.enable `blend;
235 Gl.enable `texture_2d;
236 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
237 ignore (drawstr size x y s);
238 Gl.disable `blend;
239 Gl.disable `texture_2d;
242 let drawstring1 size x y s =
243 drawstr size x y s;
246 let drawstring2 size x y fmt =
247 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
250 type page =
251 { pageno : int
252 ; pagedimno : int
253 ; pagew : int
254 ; pageh : int
255 ; pagex : int
256 ; pagey : int
257 ; pagevw : int
258 ; pagevh : int
259 ; pagedispx : int
260 ; pagedispy : int
261 ; pagecol : int
265 let debugl l =
266 dolog "l %d dim=%d {" l.pageno l.pagedimno;
267 dolog " WxH %dx%d" l.pagew l.pageh;
268 dolog " vWxH %dx%d" l.pagevw l.pagevh;
269 dolog " pagex,y %d,%d" l.pagex l.pagey;
270 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
271 dolog " column %d" l.pagecol;
272 dolog "}";
275 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
276 dolog "rect {";
277 dolog " x0,y0=(% f, % f)" x0 y0;
278 dolog " x1,y1=(% f, % f)" x1 y1;
279 dolog " x2,y2=(% f, % f)" x2 y2;
280 dolog " x3,y3=(% f, % f)" x3 y3;
281 dolog "}";
284 type multicolumns = multicol * pagegeom
285 and singlecolumn = pagegeom
286 and splitcolumns = columncount * pagegeom
287 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
288 and multicol = columncount * covercount * covercount
289 and pdimno = int
290 and columncount = int
291 and covercount = int;;
293 type conf =
294 { mutable scrollbw : int
295 ; mutable scrollh : int
296 ; mutable icase : bool
297 ; mutable preload : bool
298 ; mutable pagebias : int
299 ; mutable verbose : bool
300 ; mutable debug : bool
301 ; mutable scrollstep : int
302 ; mutable hscrollstep : int
303 ; mutable maxhfit : bool
304 ; mutable crophack : bool
305 ; mutable autoscrollstep : int
306 ; mutable maxwait : float option
307 ; mutable hlinks : bool
308 ; mutable underinfo : bool
309 ; mutable interpagespace : interpagespace
310 ; mutable zoom : float
311 ; mutable presentation : bool
312 ; mutable angle : angle
313 ; mutable cwinw : int
314 ; mutable cwinh : int
315 ; mutable savebmarks : bool
316 ; mutable fitmodel : fitmodel
317 ; mutable trimmargins : trimmargins
318 ; mutable trimfuzz : irect
319 ; mutable memlimit : memsize
320 ; mutable texcount : texcount
321 ; mutable sliceheight : sliceheight
322 ; mutable thumbw : width
323 ; mutable jumpback : bool
324 ; mutable bgcolor : (float * float * float)
325 ; mutable bedefault : bool
326 ; mutable scrollbarinpm : bool
327 ; mutable tilew : int
328 ; mutable tileh : int
329 ; mutable mustoresize : memsize
330 ; mutable checkers : bool
331 ; mutable aalevel : int
332 ; mutable urilauncher : string
333 ; mutable pathlauncher : string
334 ; mutable colorspace : colorspace
335 ; mutable invert : bool
336 ; mutable colorscale : float
337 ; mutable redirectstderr : bool
338 ; mutable ghyllscroll : (int * int * int) option
339 ; mutable columns : columns
340 ; mutable beyecolumns : columncount option
341 ; mutable selcmd : string
342 ; mutable updatecurs : bool
343 ; mutable keyhashes : (string * keyhash) list
344 ; mutable hfsize : int
345 ; mutable pgscale : float
346 ; mutable usepbo : bool
347 ; mutable wheelbypage : bool
348 ; mutable stcmd : string
350 and columns =
351 | Csingle of singlecolumn
352 | Cmulti of multicolumns
353 | Csplit of splitcolumns
356 type anchor = pageno * top * dtop;;
358 type outline = string * int * anchor;;
360 type rect = float * float * float * float * float * float * float * float;;
362 type tile = opaque * pixmapsize * elapsed
363 and elapsed = float;;
364 type pagemapkey = pageno * gen;;
365 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
366 and row = int
367 and col = int;;
369 let emptyanchor = (0, 0.0, 0.0);;
371 type infochange = | Memused | Docinfo | Pdim;;
373 class type uioh = object
374 method display : unit
375 method key : int -> int -> uioh
376 method button : int -> bool -> int -> int -> int -> uioh
377 method motion : int -> int -> uioh
378 method pmotion : int -> int -> uioh
379 method infochanged : infochange -> unit
380 method scrollpw : (int * float * float)
381 method scrollph : (int * float * float)
382 method modehash : keyhash
383 method eformsgs : bool
384 end;;
386 type mode =
387 | Birdseye of (conf * leftx * pageno * pageno * anchor)
388 | Textentry of (textentry * onleave)
389 | View
390 | LinkNav of linktarget
391 and onleave = leavetextentrystatus -> unit
392 and leavetextentrystatus = | Cancel | Confirm
393 and helpitem = string * int * action
394 and action =
395 | Noaction
396 | Action of (uioh -> uioh)
397 and linktarget =
398 | Ltexact of (pageno * int)
399 | Ltgendir of int
402 let isbirdseye = function Birdseye _ -> true | _ -> false;;
403 let istextentry = function Textentry _ -> true | _ -> false;;
405 type currently =
406 | Idle
407 | Loading of (page * gen)
408 | Tiling of (
409 page * opaque * colorspace * angle * gen * col * row * width * height
411 | Outlining of outline list
414 let emptykeyhash = Hashtbl.create 0;;
415 let nouioh : uioh = object (self)
416 method display = ()
417 method key _ _ = self
418 method button _ _ _ _ _ = self
419 method motion _ _ = self
420 method pmotion _ _ = self
421 method infochanged _ = ()
422 method scrollpw = (0, nan, nan)
423 method scrollph = (0, nan, nan)
424 method modehash = emptykeyhash
425 method eformsgs = false
426 end;;
428 type state =
429 { mutable sr : Unix.file_descr
430 ; mutable sw : Unix.file_descr
431 ; mutable wsfd : Unix.file_descr
432 ; mutable errfd : Unix.file_descr option
433 ; mutable stderr : Unix.file_descr
434 ; mutable errmsgs : Buffer.t
435 ; mutable newerrmsgs : bool
436 ; mutable w : int
437 ; mutable x : int
438 ; mutable y : int
439 ; mutable scrollw : int
440 ; mutable hscrollh : int
441 ; mutable anchor : anchor
442 ; mutable ranchors : (string * string * anchor * string) list
443 ; mutable maxy : int
444 ; mutable layout : page list
445 ; pagemap : (pagemapkey, opaque) Hashtbl.t
446 ; tilemap : (tilemapkey, tile) Hashtbl.t
447 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
448 ; mutable pdims : (pageno * width * height * leftx) list
449 ; mutable pagecount : int
450 ; mutable currently : currently
451 ; mutable mstate : mstate
452 ; mutable searchpattern : string
453 ; mutable rects : (pageno * recttype * rect) list
454 ; mutable rects1 : (pageno * recttype * rect) list
455 ; mutable text : string
456 ; mutable winstate : Wsi.winstate list
457 ; mutable mode : mode
458 ; mutable uioh : uioh
459 ; mutable outlines : outline array
460 ; mutable bookmarks : outline list
461 ; mutable path : string
462 ; mutable password : string
463 ; mutable nameddest : string
464 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
465 ; mutable memused : memsize
466 ; mutable gen : gen
467 ; mutable throttle : (page list * int * float) option
468 ; mutable autoscroll : int option
469 ; mutable ghyll : (int option -> unit)
470 ; mutable help : helpitem array
471 ; mutable docinfo : (int * string) list
472 ; mutable texid : GlTex.texture_id option
473 ; hists : hists
474 ; mutable prevzoom : float
475 ; mutable progress : float
476 ; mutable redisplay : bool
477 ; mutable mpos : mpos
478 ; mutable keystate : keystate
479 ; mutable glinks : bool
480 ; mutable prevcolumns : (columns * float) option
481 ; mutable winw : int
482 ; mutable winh : int
483 ; mutable reprf : (unit -> unit)
484 ; mutable origin : string
486 and hists =
487 { pat : string circbuf
488 ; pag : string circbuf
489 ; nav : anchor circbuf
490 ; sel : string circbuf
494 let defconf =
495 { scrollbw = 7
496 ; scrollh = 12
497 ; icase = true
498 ; preload = true
499 ; pagebias = 0
500 ; verbose = false
501 ; debug = false
502 ; scrollstep = 24
503 ; hscrollstep = 24
504 ; maxhfit = true
505 ; crophack = false
506 ; autoscrollstep = 2
507 ; maxwait = None
508 ; hlinks = false
509 ; underinfo = false
510 ; interpagespace = 2
511 ; zoom = 1.0
512 ; presentation = false
513 ; angle = 0
514 ; cwinw = 900
515 ; cwinh = 900
516 ; savebmarks = true
517 ; fitmodel = FitProportional
518 ; trimmargins = false
519 ; trimfuzz = (0,0,0,0)
520 ; memlimit = 32 lsl 20
521 ; texcount = 256
522 ; sliceheight = 24
523 ; thumbw = 76
524 ; jumpback = true
525 ; bgcolor = (0.5, 0.5, 0.5)
526 ; bedefault = false
527 ; scrollbarinpm = true
528 ; tilew = 2048
529 ; tileh = 2048
530 ; mustoresize = 256 lsl 20
531 ; checkers = true
532 ; aalevel = 8
533 ; urilauncher =
534 (match platform with
535 | Plinux | Pfreebsd | Pdragonflybsd
536 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
537 | Posx -> "open \"%s\""
538 | Pcygwin -> "cygstart \"%s\""
539 | Punknown -> "echo %s")
540 ; pathlauncher = "lp \"%s\""
541 ; selcmd =
542 (match platform with
543 | Plinux | Pfreebsd | Pdragonflybsd
544 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
545 | Posx -> "pbcopy"
546 | Pcygwin -> "wsel"
547 | Punknown -> "cat")
548 ; colorspace = Rgb
549 ; invert = false
550 ; colorscale = 1.0
551 ; redirectstderr = false
552 ; ghyllscroll = None
553 ; columns = Csingle [||]
554 ; beyecolumns = None
555 ; updatecurs = false
556 ; hfsize = 12
557 ; pgscale = 1.0
558 ; usepbo = false
559 ; wheelbypage = false
560 ; stcmd = "echo SyncTex"
561 ; keyhashes =
562 let mk n = (n, Hashtbl.create 1) in
563 [ mk "global"
564 ; mk "info"
565 ; mk "help"
566 ; mk "outline"
567 ; mk "listview"
568 ; mk "birdseye"
569 ; mk "textentry"
570 ; mk "links"
571 ; mk "view"
576 let wtmode = ref false;;
578 let findkeyhash c name =
579 try List.assoc name c.keyhashes
580 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
583 let conf = { defconf with angle = defconf.angle };;
585 let pgscale h = truncate (float h *. conf.pgscale);;
587 type fontstate =
588 { mutable fontsize : int
589 ; mutable wwidth : float
590 ; mutable maxrows : int
594 let fstate =
595 { fontsize = 14
596 ; wwidth = nan
597 ; maxrows = -1
601 let geturl s =
602 let colonpos = try String.index s ':' with Not_found -> -1 in
603 let len = String.length s in
604 if colonpos >= 0 && colonpos + 3 < len
605 then (
606 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
607 then
608 let schemestartpos =
609 try String.rindex_from s colonpos ' '
610 with Not_found -> -1
612 let scheme =
613 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
615 match scheme with
616 | "http" | "ftp" | "mailto" ->
617 let epos =
618 try String.index_from s colonpos ' '
619 with Not_found -> len
621 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
622 | _ -> ""
623 else ""
625 else ""
628 let gotouri uri =
629 if String.length conf.urilauncher = 0
630 then print_endline uri
631 else (
632 let url = geturl uri in
633 if String.length url = 0
634 then print_endline uri
635 else
636 let re = Str.regexp "%s" in
637 let command = Str.global_replace re url conf.urilauncher in
638 try popen command []
639 with exn ->
640 Printf.eprintf
641 "failed to execute `%s': %s\n" command (exntos exn);
642 flush stderr;
646 let version () =
647 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
648 (platform_to_string platform) Sys.word_size Sys.ocaml_version
651 let makehelp () =
652 let strings = version () :: "" :: Help.keys in
653 Array.of_list (
654 List.map (fun s ->
655 let url = geturl s in
656 if String.length url > 0
657 then (s, 0, Action (fun u -> gotouri url; u))
658 else (s, 0, Noaction)
659 ) strings);
662 let noghyll _ = ();;
663 let firstgeomcmds = "", [];;
664 let noreprf () = ();;
666 let state =
667 { sr = Unix.stdin
668 ; sw = Unix.stdin
669 ; wsfd = Unix.stdin
670 ; errfd = None
671 ; stderr = Unix.stderr
672 ; errmsgs = Buffer.create 0
673 ; newerrmsgs = false
674 ; x = 0
675 ; y = 0
676 ; w = 0
677 ; scrollw = 0
678 ; hscrollh = 0
679 ; anchor = emptyanchor
680 ; ranchors = []
681 ; layout = []
682 ; maxy = max_int
683 ; tilelru = Queue.create ()
684 ; pagemap = Hashtbl.create 10
685 ; tilemap = Hashtbl.create 10
686 ; pdims = []
687 ; pagecount = 0
688 ; currently = Idle
689 ; mstate = Mnone
690 ; rects = []
691 ; rects1 = []
692 ; text = ""
693 ; mode = View
694 ; winstate = []
695 ; searchpattern = ""
696 ; outlines = [||]
697 ; bookmarks = []
698 ; path = ""
699 ; password = ""
700 ; nameddest = ""
701 ; geomcmds = firstgeomcmds
702 ; hists =
703 { nav = cbnew 10 emptyanchor
704 ; pat = cbnew 10 ""
705 ; pag = cbnew 10 ""
706 ; sel = cbnew 10 ""
708 ; memused = 0
709 ; gen = 0
710 ; throttle = None
711 ; autoscroll = None
712 ; ghyll = noghyll
713 ; help = makehelp ()
714 ; docinfo = []
715 ; texid = None
716 ; prevzoom = 1.0
717 ; progress = -1.0
718 ; uioh = nouioh
719 ; redisplay = true
720 ; mpos = (-1, -1)
721 ; keystate = KSnone
722 ; glinks = false
723 ; prevcolumns = None
724 ; winw = -1
725 ; winh = -1
726 ; reprf = noreprf
727 ; origin = ""
731 let setfontsize n =
732 fstate.fontsize <- n;
733 fstate.wwidth <- measurestr fstate.fontsize "w";
734 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
737 let vlog fmt =
738 if conf.verbose
739 then
740 Printf.kprintf prerr_endline fmt
741 else
742 Printf.kprintf ignore fmt
745 let launchpath () =
746 if String.length conf.pathlauncher = 0
747 then print_endline state.path
748 else (
749 let re = Str.regexp "%s" in
750 let command = Str.global_replace re state.path conf.pathlauncher in
751 try popen command []
752 with exn ->
753 Printf.eprintf "failed to execute `%s': %s\n" command (exntos exn);
754 flush stderr;
758 module Ne = struct
759 type 'a t = | Res of 'a | Exn of exn;;
761 let pipe () =
762 try Res (Unix.pipe ())
763 with exn -> Exn exn
766 let clo fd f =
767 try tempfailureretry Unix.close fd
768 with exn -> f (exntos exn)
771 let dup fd =
772 try Res (tempfailureretry Unix.dup fd)
773 with exn -> Exn exn
776 let dup2 fd1 fd2 =
777 try Res (tempfailureretry (Unix.dup2 fd1) fd2)
778 with exn -> Exn exn
780 end;;
782 let redirectstderr () =
783 let clofail what errmsg = dolog "failed to close %s: %s" what errmsg in
784 if conf.redirectstderr
785 then
786 match Ne.pipe () with
787 | Ne.Exn exn ->
788 dolog "failed to create stderr redirection pipes: %s" (exntos exn)
790 | Ne.Res (r, w) ->
791 begin match Ne.dup Unix.stderr with
792 | Ne.Exn exn ->
793 dolog "failed to dup stderr: %s" (exntos exn);
794 Ne.clo r (clofail "pipe/r");
795 Ne.clo w (clofail "pipe/w");
797 | Ne.Res dupstderr ->
798 begin match Ne.dup2 w Unix.stderr with
799 | Ne.Exn exn ->
800 dolog "failed to dup2 to stderr: %s" (exntos exn);
801 Ne.clo dupstderr (clofail "stderr duplicate");
802 Ne.clo r (clofail "redir pipe/r");
803 Ne.clo w (clofail "redir pipe/w");
805 | Ne.Res () ->
806 state.stderr <- dupstderr;
807 state.errfd <- Some r;
808 end;
810 else (
811 state.newerrmsgs <- false;
812 begin match state.errfd with
813 | Some fd ->
814 begin match Ne.dup2 state.stderr Unix.stderr with
815 | Ne.Exn exn ->
816 dolog "failed to dup2 original stderr: %s" (exntos exn)
817 | Ne.Res () ->
818 Ne.clo fd (clofail "dup of stderr");
819 state.errfd <- None;
820 end;
821 | None -> ()
822 end;
823 prerr_string (Buffer.contents state.errmsgs);
824 flush stderr;
825 Buffer.clear state.errmsgs;
829 module G =
830 struct
831 let postRedisplay who =
832 if conf.verbose
833 then prerr_endline ("redisplay for " ^ who);
834 state.redisplay <- true;
836 end;;
838 let getopaque pageno =
839 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
840 with Not_found -> None
843 let putopaque pageno opaque =
844 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
847 let pagetranslatepoint l x y =
848 let dy = y - l.pagedispy in
849 let y = dy + l.pagey in
850 let dx = x - l.pagedispx in
851 let x = dx + l.pagex in
852 (x, y);
855 let onppundermouse g x y d =
856 let rec f = function
857 | l :: rest ->
858 begin match getopaque l.pageno with
859 | Some opaque ->
860 let x0 = l.pagedispx in
861 let x1 = x0 + l.pagevw in
862 let y0 = l.pagedispy in
863 let y1 = y0 + l.pagevh in
864 if y >= y0 && y <= y1 && x >= x0 && x <= x1
865 then
866 let px, py = pagetranslatepoint l x y in
867 match g opaque l px py with
868 | Some res -> res
869 | None -> f rest
870 else f rest
871 | _ ->
872 f rest
874 | [] -> d
876 f state.layout
879 let getunder x y =
880 let g opaque _ px py =
881 match whatsunder opaque px py with
882 | Unone -> None
883 | under -> Some under
885 onppundermouse g x y Unone
888 let unproject x y =
889 let g opaque l x y =
890 match unproject opaque x y with
891 | Some (x, y) -> Some (Some (l.pageno, x, y))
892 | None -> None
894 onppundermouse g x y None;
897 let showtext c s =
898 state.text <- Printf.sprintf "%c%s" c s;
899 G.postRedisplay "showtext";
902 let selstring s =
903 match Ne.pipe () with
904 | Ne.Exn exn ->
905 showtext '!' (Printf.sprintf "pipe failed: %s" (exntos exn))
906 | Ne.Res (r, w) ->
907 let popened =
908 try popen conf.selcmd [r, 0; w, -1]; true
909 with exn ->
910 showtext '!'
911 (Printf.sprintf "failed to execute %s: %s"
912 conf.selcmd (exntos exn));
913 false
915 let clo cap fd =
916 Ne.clo fd (fun msg ->
917 showtext '!' (Printf.sprintf "failed to close %s: %s" cap msg)
920 if popened
921 then
922 (try
923 let l = String.length s in
924 let n = tempfailureretry (Unix.write w s 0) l in
925 if n != l
926 then
927 showtext '!'
928 (Printf.sprintf
929 "failed to write %d characters to sel pipe, wrote %d"
932 with exn ->
933 showtext '!'
934 (Printf.sprintf "failed to write to sel pipe: %s"
935 (exntos exn)
938 else dolog "%s" s;
939 clo "pipe/r" r;
940 clo "pipe/w" w;
943 let undertext = function
944 | Unone -> "none"
945 | Ulinkuri s -> s
946 | Ulinkgoto (pageno, _) -> Printf.sprintf "%s: page %d" state.path (pageno+1)
947 | Utext s -> "font: " ^ s
948 | Uunexpected s -> "unexpected: " ^ s
949 | Ulaunch s -> "launch: " ^ s
950 | Unamed s -> "named: " ^ s
951 | Uremote (filename, pageno) ->
952 Printf.sprintf "%s: page %d" filename (pageno+1)
955 let updateunder x y =
956 match getunder x y with
957 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
958 | Ulinkuri uri ->
959 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
960 Wsi.setcursor Wsi.CURSOR_INFO
961 | Ulinkgoto (pageno, _) ->
962 if conf.underinfo
963 then showtext 'p' ("age: " ^ string_of_int (pageno+1));
964 Wsi.setcursor Wsi.CURSOR_INFO
965 | Utext s ->
966 if conf.underinfo then showtext 'f' ("ont: " ^ s);
967 Wsi.setcursor Wsi.CURSOR_TEXT
968 | Uunexpected s ->
969 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
970 Wsi.setcursor Wsi.CURSOR_INHERIT
971 | Ulaunch s ->
972 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
973 Wsi.setcursor Wsi.CURSOR_INHERIT
974 | Unamed s ->
975 if conf.underinfo then showtext 'n' ("amed: " ^ s);
976 Wsi.setcursor Wsi.CURSOR_INHERIT
977 | Uremote (filename, pageno) ->
978 if conf.underinfo then showtext 'r'
979 (Printf.sprintf "emote: %s (%d)" filename (pageno+1));
980 Wsi.setcursor Wsi.CURSOR_INFO
983 let showlinktype under =
984 if conf.underinfo
985 then
986 match under with
987 | Unone -> ()
988 | under ->
989 let s = undertext under in
990 showtext ' ' s
993 let addchar s c =
994 let b = Buffer.create (String.length s + 1) in
995 Buffer.add_string b s;
996 Buffer.add_char b c;
997 Buffer.contents b;
1000 let colorspace_of_string s =
1001 match String.lowercase s with
1002 | "rgb" -> Rgb
1003 | "bgr" -> Bgr
1004 | "gray" -> Gray
1005 | _ -> failwith "invalid colorspace"
1008 let int_of_colorspace = function
1009 | Rgb -> 0
1010 | Bgr -> 1
1011 | Gray -> 2
1014 let colorspace_of_int = function
1015 | 0 -> Rgb
1016 | 1 -> Bgr
1017 | 2 -> Gray
1018 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
1021 let colorspace_to_string = function
1022 | Rgb -> "rgb"
1023 | Bgr -> "bgr"
1024 | Gray -> "gray"
1027 let fitmodel_of_string s =
1028 match String.lowercase s with
1029 | "width" -> FitWidth
1030 | "proportional" -> FitProportional
1031 | "page" -> FitPage
1032 | _ -> failwith "invalid fit model"
1035 let int_of_fitmodel = function
1036 | FitWidth -> 0
1037 | FitProportional -> 1
1038 | FitPage -> 2
1041 let int_of_fitmodel_and_zoom fitmodel zoom =
1042 let fitmodel =
1043 match fitmodel with
1044 | FitPage when zoom > 1.0 -> FitWidth
1045 | _ -> fitmodel
1047 int_of_fitmodel fitmodel;
1050 let fitmodel_of_int = function
1051 | 0 -> FitWidth
1052 | 1 -> FitProportional
1053 | 2 -> FitPage
1054 | n -> failwith ("invalid fit model index " ^ string_of_int n)
1057 let fitmodel_to_string = function
1058 | FitWidth -> "width"
1059 | FitProportional -> "proportional"
1060 | FitPage -> "page"
1063 let intentry_with_suffix text key =
1064 let c =
1065 if key >= 32 && key < 127
1066 then Char.chr key
1067 else '\000'
1069 match Char.lowercase c with
1070 | '0' .. '9' ->
1071 let text = addchar text c in
1072 TEcont text
1074 | 'k' | 'm' | 'g' ->
1075 let text = addchar text c in
1076 TEcont text
1078 | _ ->
1079 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
1080 TEcont text
1083 let multicolumns_to_string (n, a, b) =
1084 if a = 0 && b = 0
1085 then Printf.sprintf "%d" n
1086 else Printf.sprintf "%d,%d,%d" n a b;
1089 let multicolumns_of_string s =
1091 (int_of_string s, 0, 0)
1092 with _ ->
1093 Scanf.sscanf s "%u,%u,%u" (fun n a b ->
1094 if a > 1 || b > 1
1095 then failwith "subtly broken"; (n, a, b)
1099 let readcmd fd =
1100 let s = "xxxx" in
1101 let n = tempfailureretry (Unix.read fd s 0) 4 in
1102 if n != 4 then failwith "incomplete read(len)";
1103 let len = 0
1104 lor (Char.code s.[0] lsl 24)
1105 lor (Char.code s.[1] lsl 16)
1106 lor (Char.code s.[2] lsl 8)
1107 lor (Char.code s.[3] lsl 0)
1109 let s = String.create len in
1110 let n = tempfailureretry (Unix.read fd s 0) len in
1111 if n != len then failwith "incomplete read(data)";
1115 let btod b = if b then 1 else 0;;
1117 let wcmd fmt =
1118 let b = Buffer.create 16 in
1119 Buffer.add_string b "llll";
1120 Printf.kbprintf
1121 (fun b ->
1122 let s = Buffer.contents b in
1123 let n = String.length s in
1124 let len = n - 4 in
1125 (* dolog "wcmd %S" (String.sub s 4 len); *)
1126 s.[0] <- Char.chr ((len lsr 24) land 0xff);
1127 s.[1] <- Char.chr ((len lsr 16) land 0xff);
1128 s.[2] <- Char.chr ((len lsr 8) land 0xff);
1129 s.[3] <- Char.chr (len land 0xff);
1130 let n' = tempfailureretry (Unix.write state.sw s 0) n in
1131 if n' != n then failwith "write failed";
1132 ) b fmt;
1135 let calcips h =
1136 let d = state.winh - h in
1137 max conf.interpagespace ((d + 1) / 2)
1140 let rowyh (c, coverA, coverB) b n =
1141 if c = 1 || (n < coverA || n >= state.pagecount - coverB)
1142 then
1143 let _, _, vy, (_, _, h, _) = b.(n) in
1144 (vy, h)
1145 else
1146 let n' = n - coverA in
1147 let d = n' mod c in
1148 let s = n - d in
1149 let e = min state.pagecount (s + c) in
1150 let rec find m miny maxh = if m = e then miny, maxh else
1151 let _, _, y, (_, _, h, _) = b.(m) in
1152 let miny = min miny y in
1153 let maxh = max maxh h in
1154 find (m+1) miny maxh
1155 in find s max_int 0
1158 let calcheight () =
1159 match conf.columns with
1160 | Cmulti ((_, _, _) as cl, b) ->
1161 if Array.length b > 0
1162 then
1163 let y, h = rowyh cl b (Array.length b - 1) in
1164 y + h + (if conf.presentation then calcips h else 0)
1165 else 0
1166 | Csingle b ->
1167 if Array.length b > 0
1168 then
1169 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1170 y + h + (if conf.presentation then calcips h else 0)
1171 else 0
1172 | Csplit (_, b) ->
1173 if Array.length b > 0
1174 then
1175 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1176 y + h
1177 else 0
1180 let getpageyh pageno =
1181 let pageno = bound pageno 0 (state.pagecount-1) in
1182 match conf.columns with
1183 | Csingle b ->
1184 if Array.length b = 0
1185 then 0, 0
1186 else
1187 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1188 let y =
1189 if conf.presentation
1190 then y - calcips h
1191 else y
1193 y, h
1194 | Cmulti (cl, b) ->
1195 if Array.length b = 0
1196 then 0, 0
1197 else
1198 let y, h = rowyh cl b pageno in
1199 let y =
1200 if conf.presentation
1201 then y - calcips h
1202 else y
1204 y, h
1205 | Csplit (c, b) ->
1206 if Array.length b = 0
1207 then 0, 0
1208 else
1209 let n = pageno*c in
1210 let (_, _, y, (_, _, h, _)) = b.(n) in
1211 y, h
1214 let getpagedim pageno =
1215 let rec f ppdim l =
1216 match l with
1217 | (n, _, _, _) as pdim :: rest ->
1218 if n >= pageno
1219 then (if n = pageno then pdim else ppdim)
1220 else f pdim rest
1222 | [] -> ppdim
1224 f (-1, -1, -1, -1) state.pdims
1227 let getpagey pageno = fst (getpageyh pageno);;
1229 let nogeomcmds cmds =
1230 match cmds with
1231 | s, [] -> String.length s = 0
1232 | _ -> false
1235 let page_of_y y =
1236 let ((c, coverA, coverB) as cl), b =
1237 match conf.columns with
1238 | Csingle b -> (1, 0, 0), b
1239 | Cmulti (c, b) -> c, b
1240 | Csplit (_, b) -> (1, 0, 0), b
1242 if Array.length b = 0
1243 then -1
1244 else
1245 let rec bsearch nmin nmax =
1246 if nmin > nmax
1247 then bound nmin 0 (state.pagecount-1)
1248 else
1249 let n = (nmax + nmin) / 2 in
1250 let vy, h = rowyh cl b n in
1251 let y0, y1 =
1252 if conf.presentation
1253 then
1254 let ips = calcips h in
1255 let y0 = vy - ips in
1256 let y1 = vy + h + ips in
1257 y0, y1
1258 else (
1259 if n = 0
1260 then 0, vy + h + conf.interpagespace
1261 else
1262 let y0 = vy - conf.interpagespace in
1263 y0, y0 + h + conf.interpagespace
1266 if y >= y0 && y < y1
1267 then (
1268 if c = 1
1269 then n
1270 else (
1271 if n > coverA
1272 then
1273 if n < state.pagecount - coverB
1274 then ((n-coverA)/c)*c + coverA
1275 else n
1276 else n
1279 else (
1280 if y > y0
1281 then bsearch (n+1) nmax
1282 else bsearch nmin (n-1)
1285 let r = bsearch 0 (state.pagecount-1) in
1289 let layoutN ((columns, coverA, coverB), b) y sh =
1290 let sh = sh - state.hscrollh in
1291 let rec fold accu n =
1292 if n = Array.length b
1293 then accu
1294 else
1295 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1296 if (vy - y) > sh &&
1297 (n = coverA - 1
1298 || n = state.pagecount - coverB
1299 || (n - coverA) mod columns = columns - 1)
1300 then accu
1301 else
1302 let accu =
1303 if vy + h > y
1304 then
1305 let pagey = max 0 (y - vy) in
1306 let pagedispy = if pagey > 0 then 0 else vy - y in
1307 let pagedispx, pagex =
1308 let pdx =
1309 if n = coverA - 1 || n = state.pagecount - coverB
1310 then state.x + (state.winw - state.scrollw - w) / 2
1311 else dx + xoff + state.x
1313 if pdx < 0
1314 then 0, -pdx
1315 else pdx, 0
1317 let pagevw =
1318 let vw = state.winw - state.scrollw - pagedispx in
1319 let pw = w - pagex in
1320 min vw pw
1322 let pagevh = min (h - pagey) (sh - pagedispy) in
1323 if pagevw > 0 && pagevh > 0
1324 then
1325 let e =
1326 { pageno = n
1327 ; pagedimno = pdimno
1328 ; pagew = w
1329 ; pageh = h
1330 ; pagex = pagex
1331 ; pagey = pagey
1332 ; pagevw = pagevw
1333 ; pagevh = pagevh
1334 ; pagedispx = pagedispx
1335 ; pagedispy = pagedispy
1336 ; pagecol = 0
1339 e :: accu
1340 else
1341 accu
1342 else
1343 accu
1345 fold accu (n+1)
1347 List.rev (fold [] (page_of_y y));
1350 let layoutS (columns, b) y sh =
1351 let sh = sh - state.hscrollh in
1352 let rec fold accu n =
1353 if n = Array.length b
1354 then accu
1355 else
1356 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1357 if (vy - y) > sh
1358 then accu
1359 else
1360 let accu =
1361 if vy + pageh > y
1362 then
1363 let x = xoff + state.x in
1364 let pagey = max 0 (y - vy) in
1365 let pagedispy = if pagey > 0 then 0 else vy - y in
1366 let pagedispx, pagex =
1367 if px = 0
1368 then (
1369 if x < 0
1370 then 0, -x
1371 else x, 0
1373 else (
1374 let px = px - x in
1375 if px < 0
1376 then -px, 0
1377 else 0, px
1380 let pagecolw = pagew/columns in
1381 let pagedispx =
1382 if pagecolw < state.winw
1383 then pagedispx + ((state.winw - state.scrollw - pagecolw) / 2)
1384 else pagedispx
1386 let pagevw =
1387 let vw = state.winw - pagedispx - state.scrollw in
1388 let pw = pagew - pagex in
1389 min vw pw
1391 let pagevw = min pagevw pagecolw in
1392 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1393 if pagevw > 0 && pagevh > 0
1394 then
1395 let e =
1396 { pageno = n/columns
1397 ; pagedimno = pdimno
1398 ; pagew = pagew
1399 ; pageh = pageh
1400 ; pagex = pagex
1401 ; pagey = pagey
1402 ; pagevw = pagevw
1403 ; pagevh = pagevh
1404 ; pagedispx = pagedispx
1405 ; pagedispy = pagedispy
1406 ; pagecol = n mod columns
1409 e :: accu
1410 else
1411 accu
1412 else
1413 accu
1415 fold accu (n+1)
1417 List.rev (fold [] 0)
1420 let layout y sh =
1421 if nogeomcmds state.geomcmds
1422 then
1423 match conf.columns with
1424 | Csingle b -> layoutN ((1, 0, 0), b) y sh
1425 | Cmulti c -> layoutN c y sh
1426 | Csplit s -> layoutS s y sh
1427 else []
1430 let clamp incr =
1431 let y = state.y + incr in
1432 let y = max 0 y in
1433 let y = min y (state.maxy - (if conf.maxhfit then state.winh else 0)) in
1437 let itertiles l f =
1438 let tilex = l.pagex mod conf.tilew in
1439 let tiley = l.pagey mod conf.tileh in
1441 let col = l.pagex / conf.tilew in
1442 let row = l.pagey / conf.tileh in
1444 let rec rowloop row y0 dispy h =
1445 if h = 0
1446 then ()
1447 else (
1448 let dh = conf.tileh - y0 in
1449 let dh = min h dh in
1450 let rec colloop col x0 dispx w =
1451 if w = 0
1452 then ()
1453 else (
1454 let dw = conf.tilew - x0 in
1455 let dw = min w dw in
1457 f col row dispx dispy x0 y0 dw dh;
1458 colloop (col+1) 0 (dispx+dw) (w-dw)
1461 colloop col tilex l.pagedispx l.pagevw;
1462 rowloop (row+1) 0 (dispy+dh) (h-dh)
1465 if l.pagevw > 0 && l.pagevh > 0
1466 then rowloop row tiley l.pagedispy l.pagevh;
1469 let gettileopaque l col row =
1470 let key =
1471 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1473 try Some (Hashtbl.find state.tilemap key)
1474 with Not_found -> None
1477 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1478 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1479 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1482 let drawtiles l color =
1483 GlDraw.color color;
1484 let f col row x y tilex tiley w h =
1485 match gettileopaque l col row with
1486 | Some (opaque, _, t) ->
1487 let params = x, y, w, h, tilex, tiley in
1488 if conf.invert
1489 then (
1490 Gl.enable `blend;
1491 GlFunc.blend_func `zero `one_minus_src_color;
1493 drawtile params opaque;
1494 if conf.invert
1495 then Gl.disable `blend;
1496 if conf.debug
1497 then (
1498 let s = Printf.sprintf
1499 "%d[%d,%d] %f sec"
1500 l.pageno col row t
1502 let w = measurestr fstate.fontsize s in
1503 GlMisc.push_attrib [`current];
1504 GlDraw.color (0.0, 0.0, 0.0);
1505 GlDraw.rect
1506 (float (x-2), float (y-2))
1507 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1508 GlDraw.color (1.0, 1.0, 1.0);
1509 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1510 GlMisc.pop_attrib ();
1513 | _ ->
1514 let w =
1515 let lw = state.winw - state.scrollw - x in
1516 min lw w
1517 and h =
1518 let lh = state.winh - y in
1519 min lh h
1521 begin match state.texid with
1522 | Some id ->
1523 Gl.enable `texture_2d;
1524 GlTex.bind_texture `texture_2d id;
1525 let x0 = float x
1526 and y0 = float y
1527 and x1 = float (x+w)
1528 and y1 = float (y+h) in
1530 let tw = float w /. 16.0
1531 and th = float h /. 16.0 in
1532 let tx0 = float tilex /. 16.0
1533 and ty0 = float tiley /. 16.0 in
1534 let tx1 = tx0 +. tw
1535 and ty1 = ty0 +. th in
1536 GlDraw.begins `quads;
1537 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1538 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1539 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1540 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1541 GlDraw.ends ();
1543 Gl.disable `texture_2d;
1544 | None ->
1545 GlDraw.color (1.0, 1.0, 1.0);
1546 GlDraw.rect
1547 (float x, float y)
1548 (float (x+w), float (y+h));
1549 end;
1550 if w > 128 && h > fstate.fontsize + 10
1551 then (
1552 GlDraw.color (0.0, 0.0, 0.0);
1553 let c, r =
1554 if conf.verbose
1555 then (col*conf.tilew, row*conf.tileh)
1556 else col, row
1558 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1560 GlDraw.color color;
1562 itertiles l f
1565 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1567 let tilevisible1 l x y =
1568 let ax0 = l.pagex
1569 and ax1 = l.pagex + l.pagevw
1570 and ay0 = l.pagey
1571 and ay1 = l.pagey + l.pagevh in
1573 let bx0 = x
1574 and by0 = y in
1575 let bx1 = min (bx0 + conf.tilew) l.pagew
1576 and by1 = min (by0 + conf.tileh) l.pageh in
1578 let rx0 = max ax0 bx0
1579 and ry0 = max ay0 by0
1580 and rx1 = min ax1 bx1
1581 and ry1 = min ay1 by1 in
1583 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1584 nonemptyintersection
1587 let tilevisible layout n x y =
1588 let rec findpageinlayout m = function
1589 | l :: rest when l.pageno = n ->
1590 tilevisible1 l x y || (
1591 match conf.columns with
1592 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
1593 | _ -> false
1595 | _ :: rest -> findpageinlayout 0 rest
1596 | [] -> false
1598 findpageinlayout 0 layout;
1601 let tileready l x y =
1602 tilevisible1 l x y &&
1603 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1606 let tilepage n p layout =
1607 let rec loop = function
1608 | l :: rest ->
1609 if l.pageno = n
1610 then
1611 let f col row _ _ _ _ _ _ =
1612 if state.currently = Idle
1613 then
1614 match gettileopaque l col row with
1615 | Some _ -> ()
1616 | None ->
1617 let x = col*conf.tilew
1618 and y = row*conf.tileh in
1619 let w =
1620 let w = l.pagew - x in
1621 min w conf.tilew
1623 let h =
1624 let h = l.pageh - y in
1625 min h conf.tileh
1627 let pbo =
1628 if conf.usepbo
1629 then getpbo w h conf.colorspace
1630 else "0"
1632 wcmd "tile %s %d %d %d %d %s" p x y w h pbo;
1633 state.currently <-
1634 Tiling (
1635 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1636 conf.tilew, conf.tileh
1639 itertiles l f;
1640 else
1641 loop rest
1643 | [] -> ()
1645 if nogeomcmds state.geomcmds
1646 then loop layout;
1649 let preloadlayout y =
1650 let y = if y < state.winh then 0 else y - state.winh in
1651 let h = state.winh*3 in
1652 layout y h;
1655 let load pages =
1656 let rec loop pages =
1657 if state.currently != Idle
1658 then ()
1659 else
1660 match pages with
1661 | l :: rest ->
1662 begin match getopaque l.pageno with
1663 | None ->
1664 wcmd "page %d %d" l.pageno l.pagedimno;
1665 state.currently <- Loading (l, state.gen);
1666 | Some opaque ->
1667 tilepage l.pageno opaque pages;
1668 loop rest
1669 end;
1670 | _ -> ()
1672 if nogeomcmds state.geomcmds
1673 then loop pages
1676 let preload pages =
1677 load pages;
1678 if conf.preload && state.currently = Idle
1679 then load (preloadlayout state.y);
1682 let layoutready layout =
1683 let rec fold all ls =
1684 all && match ls with
1685 | l :: rest ->
1686 let seen = ref false in
1687 let allvisible = ref true in
1688 let foo col row _ _ _ _ _ _ =
1689 seen := true;
1690 allvisible := !allvisible &&
1691 begin match gettileopaque l col row with
1692 | Some _ -> true
1693 | None -> false
1696 itertiles l foo;
1697 fold (!seen && !allvisible) rest
1698 | [] -> true
1700 let alltilesvisible = fold true layout in
1701 alltilesvisible;
1704 let gotoy y =
1705 let y = bound y 0 state.maxy in
1706 let y, layout, proceed =
1707 match conf.maxwait with
1708 | Some time when state.ghyll == noghyll ->
1709 begin match state.throttle with
1710 | None ->
1711 let layout = layout y state.winh in
1712 let ready = layoutready layout in
1713 if not ready
1714 then (
1715 load layout;
1716 state.throttle <- Some (layout, y, now ());
1718 else G.postRedisplay "gotoy showall (None)";
1719 y, layout, ready
1720 | Some (_, _, started) ->
1721 let dt = now () -. started in
1722 if dt > time
1723 then (
1724 state.throttle <- None;
1725 let layout = layout y state.winh in
1726 load layout;
1727 G.postRedisplay "maxwait";
1728 y, layout, true
1730 else -1, [], false
1733 | _ ->
1734 let layout = layout y state.winh in
1735 if not !wtmode || layoutready layout
1736 then G.postRedisplay "gotoy ready";
1737 y, layout, true
1739 if proceed
1740 then (
1741 state.y <- y;
1742 state.layout <- layout;
1743 begin match state.mode with
1744 | LinkNav (Ltexact (pageno, linkno)) ->
1745 let rec loop = function
1746 | [] ->
1747 state.mode <- LinkNav (Ltgendir 0)
1748 | l :: _ when l.pageno = pageno ->
1749 begin match getopaque pageno with
1750 | None ->
1751 state.mode <- LinkNav (Ltgendir 0)
1752 | Some opaque ->
1753 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1754 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1755 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1756 then state.mode <- LinkNav (Ltgendir 0)
1758 | _ :: rest -> loop rest
1760 loop layout
1761 | _ -> ()
1762 end;
1763 begin match state.mode with
1764 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1765 if not (pagevisible layout pageno)
1766 then (
1767 match state.layout with
1768 | [] -> ()
1769 | l :: _ ->
1770 state.mode <- Birdseye (
1771 conf, leftx, l.pageno, hooverpageno, anchor
1774 | LinkNav (Ltgendir dir as lt) ->
1775 let linknav =
1776 let rec loop = function
1777 | [] -> lt
1778 | l :: rest ->
1779 match getopaque l.pageno with
1780 | None -> loop rest
1781 | Some opaque ->
1782 let link =
1783 let ld =
1784 if dir = 0
1785 then LDfirstvisible (l.pagex, l.pagey, dir)
1786 else (
1787 if dir > 0 then LDfirst else LDlast
1790 findlink opaque ld
1792 match link with
1793 | Lnotfound -> loop rest
1794 | Lfound n ->
1795 showlinktype (getlink opaque n);
1796 Ltexact (l.pageno, n)
1798 loop state.layout
1800 state.mode <- LinkNav linknav
1801 | _ -> ()
1802 end;
1803 preload layout;
1805 state.ghyll <- noghyll;
1806 if conf.updatecurs
1807 then (
1808 let mx, my = state.mpos in
1809 updateunder mx my;
1813 let conttiling pageno opaque =
1814 tilepage pageno opaque
1815 (if conf.preload then preloadlayout state.y else state.layout)
1818 let gotoy_and_clear_text y =
1819 if not conf.verbose then state.text <- "";
1820 gotoy y;
1823 let getanchor1 l =
1824 let top =
1825 let coloff = l.pagecol * l.pageh in
1826 float (l.pagey + coloff) /. float l.pageh
1828 let dtop =
1829 if l.pagedispy = 0
1830 then
1832 else
1833 if conf.presentation
1834 then float l.pagedispy /. float (calcips l.pageh)
1835 else float l.pagedispy /. float conf.interpagespace
1837 (l.pageno, top, dtop)
1840 let getanchor () =
1841 match state.layout with
1842 | l :: _ -> getanchor1 l
1843 | [] ->
1844 let n = page_of_y state.y in
1845 if n = -1
1846 then state.anchor
1847 else
1848 let y, h = getpageyh n in
1849 let dy = y - state.y in
1850 let dtop =
1851 if conf.presentation
1852 then
1853 let ips = calcips h in
1854 float (dy + ips) /. float ips
1855 else
1856 float dy /. float conf.interpagespace
1858 (n, 0.0, dtop)
1861 let getanchory (n, top, dtop) =
1862 let y, h = getpageyh n in
1863 if conf.presentation
1864 then
1865 let ips = calcips h in
1866 y + truncate (top*.float h -. dtop*.float ips) + ips;
1867 else
1868 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
1871 let gotoanchor anchor =
1872 gotoy (getanchory anchor);
1875 let addnav () =
1876 cbput state.hists.nav (getanchor ());
1879 let getnav dir =
1880 let anchor = cbgetc state.hists.nav dir in
1881 getanchory anchor;
1884 let gotoghyll y =
1885 let scroll f n a b =
1886 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1887 let snake f a b =
1888 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1889 if f < a
1890 then s (float f /. float a)
1891 else (
1892 if f > b
1893 then 1.0 -. s ((float (f-b) /. float (n-b)))
1894 else 1.0
1897 snake f a b
1898 and summa f n a b =
1899 (* courtesy:
1900 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1901 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1902 let iv1 = iv f in
1903 let ins = float a *. iv1
1904 and outs = float (n-b) *. iv1 in
1905 let ones = b - a in
1906 ins +. outs +. float ones
1908 let rec set (_N, _A, _B) y sy =
1909 let sum = summa 1.0 _N _A _B in
1910 let dy = float (y - sy) in
1911 state.ghyll <- (
1912 let rec gf n y1 o =
1913 if n >= _N
1914 then state.ghyll <- noghyll
1915 else
1916 let go n =
1917 let s = scroll n _N _A _B in
1918 let y1 = y1 +. ((s *. dy) /. sum) in
1919 gotoy_and_clear_text (truncate y1);
1920 state.ghyll <- gf (n+1) y1;
1922 match o with
1923 | None -> go n
1924 | Some y' -> set (_N/2, 1, 1) y' state.y
1926 gf 0 (float state.y)
1929 match conf.ghyllscroll with
1930 | None ->
1931 gotoy_and_clear_text y
1932 | Some nab ->
1933 if state.ghyll == noghyll
1934 then set nab y state.y
1935 else state.ghyll (Some y)
1938 let gotopage n top =
1939 let y, h = getpageyh n in
1940 let y = y + (truncate (top *. float h)) in
1941 gotoghyll y
1944 let gotopage1 n top =
1945 let y = getpagey n in
1946 let y = y + top in
1947 gotoghyll y
1950 let invalidate s f =
1951 state.layout <- [];
1952 state.pdims <- [];
1953 state.rects <- [];
1954 state.rects1 <- [];
1955 match state.geomcmds with
1956 | ps, [] when String.length ps = 0 ->
1957 f ();
1958 state.geomcmds <- s, [];
1960 | ps, [] ->
1961 state.geomcmds <- ps, [s, f];
1963 | ps, (s', _) :: rest when s' = s ->
1964 state.geomcmds <- ps, ((s, f) :: rest);
1966 | ps, cmds ->
1967 state.geomcmds <- ps, ((s, f) :: cmds);
1970 let flushpages () =
1971 Hashtbl.iter (fun _ opaque ->
1972 wcmd "freepage %s" opaque;
1973 ) state.pagemap;
1974 Hashtbl.clear state.pagemap;
1977 let flushtiles () =
1978 if not (Queue.is_empty state.tilelru)
1979 then (
1980 Queue.iter (fun (k, p, s) ->
1981 wcmd "freetile %s" p;
1982 state.memused <- state.memused - s;
1983 Hashtbl.remove state.tilemap k;
1984 ) state.tilelru;
1985 state.uioh#infochanged Memused;
1986 Queue.clear state.tilelru;
1988 load state.layout;
1991 let opendoc path password =
1992 state.path <- path;
1993 state.password <- password;
1994 state.gen <- state.gen + 1;
1995 state.docinfo <- [];
1997 flushpages ();
1998 setaalevel conf.aalevel;
1999 let titlepath =
2000 if String.length state.origin = 0
2001 then path
2002 else state.origin
2004 Wsi.settitle ("llpp " ^ (mbtoutf8 (Filename.basename titlepath)));
2005 wcmd "open %d %s\000%s\000" (btod !wtmode) path password;
2006 invalidate "reqlayout"
2007 (fun () ->
2008 wcmd "reqlayout %d %d %s\000"
2009 conf.angle (int_of_fitmodel_and_zoom conf.fitmodel conf.zoom)
2010 state.nameddest;
2014 let reload () =
2015 state.anchor <- getanchor ();
2016 opendoc state.path state.password;
2019 let scalecolor c =
2020 let c = c *. conf.colorscale in
2021 (c, c, c);
2024 let scalecolor2 (r, g, b) =
2025 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
2028 let docolumns = function
2029 | Csingle _ ->
2030 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
2031 let rec loop pageno pdimno pdim y ph pdims =
2032 if pageno = state.pagecount
2033 then ()
2034 else
2035 let pdimno, ((_, w, h, xoff) as pdim), pdims =
2036 match pdims with
2037 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2038 pdimno+1, pdim, rest
2039 | _ ->
2040 pdimno, pdim, pdims
2042 let x = max 0 (((state.winw - state.scrollw - w) / 2) - xoff) in
2043 let y = y +
2044 (if conf.presentation
2045 then (if pageno = 0 then calcips h else calcips ph + calcips h)
2046 else (if pageno = 0 then 0 else conf.interpagespace)
2049 a.(pageno) <- (pdimno, x, y, pdim);
2050 loop (pageno+1) pdimno pdim (y + h) h pdims
2052 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
2053 conf.columns <- Csingle a;
2055 | Cmulti ((columns, coverA, coverB), _) ->
2056 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
2057 let rec loop pageno pdimno pdim x y rowh pdims =
2058 let rec fixrow m = if m = pageno then () else
2059 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
2060 if h < rowh
2061 then (
2062 let y = y + (rowh - h) / 2 in
2063 a.(m) <- (pdimno, x, y, pdim);
2065 fixrow (m+1)
2067 if pageno = state.pagecount
2068 then fixrow (((pageno - 1) / columns) * columns)
2069 else
2070 let pdimno, ((_, w, h, xoff) as pdim), pdims =
2071 match pdims with
2072 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2073 pdimno+1, pdim, rest
2074 | _ ->
2075 pdimno, pdim, pdims
2077 let x, y, rowh' =
2078 if pageno = coverA - 1 || pageno = state.pagecount - coverB
2079 then (
2080 let x = (state.winw - state.scrollw - w) / 2 in
2081 let ips =
2082 if conf.presentation then calcips h else conf.interpagespace in
2083 x, y + ips + rowh, h
2085 else (
2086 if (pageno - coverA) mod columns = 0
2087 then (
2088 let x = max 0 (state.winw - state.scrollw - state.w) / 2 in
2089 let y =
2090 if conf.presentation
2091 then
2092 let ips = calcips h in
2093 y + (if pageno = 0 then 0 else calcips rowh + ips)
2094 else
2095 y + (if pageno = 0 then 0 else conf.interpagespace)
2097 x, y + rowh, h
2099 else x, y, max rowh h
2102 let y =
2103 if pageno > 1 && (pageno - coverA) mod columns = 0
2104 then (
2105 let y =
2106 if pageno = columns && conf.presentation
2107 then (
2108 let ips = calcips rowh in
2109 for i = 0 to pred columns
2111 let (pdimno, x, y, pdim) = a.(i) in
2112 a.(i) <- (pdimno, x, y+ips, pdim)
2113 done;
2114 y+ips;
2116 else y
2118 fixrow (pageno - columns);
2121 else y
2123 a.(pageno) <- (pdimno, x, y, pdim);
2124 let x = x + w + xoff*2 + conf.interpagespace in
2125 loop (pageno+1) pdimno pdim x y rowh' pdims
2127 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
2128 conf.columns <- Cmulti ((columns, coverA, coverB), a);
2130 | Csplit (c, _) ->
2131 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
2132 let rec loop pageno pdimno pdim y pdims =
2133 if pageno = state.pagecount
2134 then ()
2135 else
2136 let pdimno, ((_, w, h, _) as pdim), pdims =
2137 match pdims with
2138 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
2139 pdimno+1, pdim, rest
2140 | _ ->
2141 pdimno, pdim, pdims
2143 let cw = w / c in
2144 let rec loop1 n x y =
2145 if n = c then y else (
2146 a.(pageno*c + n) <- (pdimno, x, y, pdim);
2147 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
2150 let y = loop1 0 0 y in
2151 loop (pageno+1) pdimno pdim y pdims
2153 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
2154 conf.columns <- Csplit (c, a);
2157 let represent () =
2158 docolumns conf.columns;
2159 state.maxy <- calcheight ();
2160 state.hscrollh <-
2161 if state.x = 0 && state.w <= state.winw - state.scrollw
2162 then 0
2163 else state.scrollw
2165 if state.reprf == noreprf
2166 then (
2167 match state.mode with
2168 | Birdseye (_, _, pageno, _, _) ->
2169 let y, h = getpageyh pageno in
2170 let top = (state.winh - h) / 2 in
2171 gotoy (max 0 (y - top))
2172 | _ -> gotoanchor state.anchor
2174 else (
2175 state.reprf ();
2176 state.reprf <- noreprf;
2180 let reshape w h =
2181 GlDraw.viewport 0 0 w h;
2182 let firsttime = state.geomcmds == firstgeomcmds in
2183 if not firsttime && nogeomcmds state.geomcmds
2184 then state.anchor <- getanchor ();
2186 state.winw <- w;
2187 let w = truncate (float w *. conf.zoom) - state.scrollw in
2188 let w = max w 2 in
2189 state.winh <- h;
2190 setfontsize fstate.fontsize;
2191 GlMat.mode `modelview;
2192 GlMat.load_identity ();
2194 GlMat.mode `projection;
2195 GlMat.load_identity ();
2196 GlMat.rotate ~x:1.0 ~angle:180.0 ();
2197 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
2198 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
2200 let relx =
2201 if conf.zoom <= 1.0
2202 then 0.0
2203 else float state.x /. float state.w
2205 invalidate "geometry"
2206 (fun () ->
2207 state.w <- w;
2208 if not firsttime
2209 then state.x <- truncate (relx *. float w);
2210 let w =
2211 match conf.columns with
2212 | Csingle _ -> w
2213 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
2214 | Csplit (c, _) -> w * c
2216 wcmd "geometry %d %d %d"
2217 w (h - 2*conf.interpagespace)
2218 (int_of_fitmodel_and_zoom conf.fitmodel conf.zoom)
2222 let enttext () =
2223 let len = String.length state.text in
2224 let drawstring s =
2225 let hscrollh =
2226 match state.mode with
2227 | Textentry _
2228 | View ->
2229 let h, _, _ = state.uioh#scrollpw in
2231 | _ -> 0
2233 let rect x w =
2234 GlDraw.rect
2235 (x, float (state.winh - (fstate.fontsize + 4) - hscrollh))
2236 (x+.w, float (state.winh - hscrollh))
2239 let w = float (state.winw - state.scrollw - 1) in
2240 if state.progress >= 0.0 && state.progress < 1.0
2241 then (
2242 GlDraw.color (0.3, 0.3, 0.3);
2243 let w1 = w *. state.progress in
2244 rect 0.0 w1;
2245 GlDraw.color (0.0, 0.0, 0.0);
2246 rect w1 (w-.w1)
2248 else (
2249 GlDraw.color (0.0, 0.0, 0.0);
2250 rect 0.0 w;
2253 GlDraw.color (1.0, 1.0, 1.0);
2254 drawstring fstate.fontsize
2255 (if len > 0 then 8 else 2) (state.winh - hscrollh - 5) s;
2257 let s =
2258 match state.mode with
2259 | Textentry ((prefix, text, _, _, _, _), _) ->
2260 let s =
2261 if len > 0
2262 then
2263 Printf.sprintf "%s%s_ [%s]" prefix text state.text
2264 else
2265 Printf.sprintf "%s%s_" prefix text
2269 | _ -> state.text
2271 let s =
2272 if state.newerrmsgs
2273 then (
2274 if not (istextentry state.mode) && state.uioh#eformsgs
2275 then
2276 let s1 = "(press 'e' to review error messasges)" in
2277 if String.length s > 0 then s ^ " " ^ s1 else s1
2278 else s
2280 else s
2282 if String.length s > 0
2283 then drawstring s
2286 let gctiles () =
2287 let len = Queue.length state.tilelru in
2288 let layout = lazy (
2289 match state.throttle with
2290 | None ->
2291 if conf.preload
2292 then preloadlayout state.y
2293 else state.layout
2294 | Some (layout, _, _) ->
2295 layout
2296 ) in
2297 let rec loop qpos =
2298 if state.memused <= conf.memlimit
2299 then ()
2300 else (
2301 if qpos < len
2302 then
2303 let (k, p, s) as lruitem = Queue.pop state.tilelru in
2304 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
2305 let (_, pw, ph, _) = getpagedim n in
2307 gen = state.gen
2308 && colorspace = conf.colorspace
2309 && angle = conf.angle
2310 && pagew = pw
2311 && pageh = ph
2312 && (
2313 let x = col*conf.tilew
2314 and y = row*conf.tileh in
2315 tilevisible (Lazy.force_val layout) n x y
2317 then Queue.push lruitem state.tilelru
2318 else (
2319 freepbo p;
2320 wcmd "freetile %s" p;
2321 state.memused <- state.memused - s;
2322 state.uioh#infochanged Memused;
2323 Hashtbl.remove state.tilemap k;
2325 loop (qpos+1)
2328 loop 0
2331 let logcurrently = function
2332 | Idle -> dolog "Idle"
2333 | Loading (l, gen) ->
2334 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2335 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2336 dolog
2337 "Tiling %d[%d,%d] page=%s cs=%s angle"
2338 l.pageno col row pageopaque
2339 (colorspace_to_string colorspace)
2341 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2342 angle gen conf.angle state.gen
2343 tilew tileh
2344 conf.tilew conf.tileh
2346 | Outlining _ ->
2347 dolog "outlining"
2350 let splitatspace =
2351 let r = Str.regexp " " in
2352 fun s -> Str.bounded_split r s 2;
2355 let onpagerect pageno f =
2356 let b =
2357 match conf.columns with
2358 | Cmulti (_, b) -> b
2359 | Csingle b -> b
2360 | Csplit (_, b) -> b
2362 if pageno >= 0 && pageno < Array.length b
2363 then
2364 let (pdimno, _, _, (_, _, _, _)) = b.(pageno) in
2365 let r = getpdimrect pdimno in
2366 f (r.(1)-.r.(0)) (r.(3)-.r.(2))
2369 let gotopagexy1 pageno x y =
2370 onpagerect pageno (fun w h ->
2371 let top = y /. h in
2372 let _,w1,_,leftx = getpagedim pageno in
2373 let wh = state.winh - state.hscrollh in
2374 let sw = float w1 /. w in
2375 let x = sw *. x in
2376 let x = leftx + state.x + truncate x in
2377 let sx =
2378 if x < 0 || x >= state.winw - state.scrollw
2379 then state.x - x
2380 else state.x
2382 let py, h = getpageyh pageno in
2383 let pdy = truncate (top *. float h) in
2384 let y' = py + pdy in
2385 let dy = y' - state.y in
2386 let sy =
2387 if x != state.x || not (dy > 0 && dy < wh)
2388 then (
2389 if conf.presentation
2390 then
2391 if abs (py - y') > wh
2392 then y'
2393 else py
2394 else y';
2396 else state.y
2398 if state.x != sx || state.y != sy
2399 then (
2400 let x, y =
2401 if !wtmode
2402 then (
2403 let ww = state.winw - state.scrollw in
2404 let qx = sx / ww
2405 and qy = pdy / wh in
2406 let x = qx * ww
2407 and y = py + qy * wh in
2408 let x = if -x + ww > w1 then -(w1-ww) else x
2409 and y' = if y + wh > state.maxy then state.maxy - wh else y in
2410 let y =
2411 if conf.presentation
2412 then
2413 if abs (py - y') > wh
2414 then y'
2415 else py
2416 else y';
2418 (x, y)
2420 else (sx, sy)
2422 state.x <- x;
2423 state.hscrollh <-
2424 if x = 0 && state.w <= state.winw - state.scrollw
2425 then 0
2426 else state.scrollw
2428 gotoy_and_clear_text y;
2430 else gotoy_and_clear_text state.y;
2434 let gotopagexy pageno x y =
2435 match state.mode with
2436 | Birdseye _ -> gotopage pageno 0.0
2437 | _ -> gotopagexy1 pageno x y
2440 let act cmds =
2441 (* dolog "%S" cmds; *)
2442 let cl = splitatspace cmds in
2443 let scan s fmt f =
2444 try Scanf.sscanf s fmt f
2445 with exn ->
2446 dolog "error processing '%S': %s" cmds (exntos exn);
2447 exit 1
2449 match cl with
2450 | "clear" :: [] ->
2451 state.uioh#infochanged Pdim;
2452 state.pdims <- [];
2454 | "clearrects" :: [] ->
2455 state.rects <- state.rects1;
2456 G.postRedisplay "clearrects";
2458 | "continue" :: args :: [] ->
2459 let n = scan args "%u" (fun n -> n) in
2460 state.pagecount <- n;
2461 begin match state.currently with
2462 | Outlining l ->
2463 state.currently <- Idle;
2464 state.outlines <- Array.of_list (List.rev l)
2465 | _ -> ()
2466 end;
2468 let cur, cmds = state.geomcmds in
2469 if String.length cur = 0
2470 then failwith "umpossible";
2472 begin match List.rev cmds with
2473 | [] ->
2474 state.geomcmds <- "", [];
2475 represent ();
2476 | (s, f) :: rest ->
2477 f ();
2478 state.geomcmds <- s, List.rev rest;
2479 end;
2480 if conf.maxwait = None && not !wtmode
2481 then G.postRedisplay "continue";
2483 | "title" :: args :: [] ->
2484 Wsi.settitle args
2486 | "msg" :: args :: [] ->
2487 showtext ' ' args
2489 | "vmsg" :: args :: [] ->
2490 if conf.verbose
2491 then showtext ' ' args
2493 | "emsg" :: args :: [] ->
2494 Buffer.add_string state.errmsgs args;
2495 state.newerrmsgs <- true;
2496 G.postRedisplay "error message"
2498 | "progress" :: args :: [] ->
2499 let progress, text =
2500 scan args "%f %n"
2501 (fun f pos ->
2502 f, String.sub args pos (String.length args - pos))
2504 state.text <- text;
2505 state.progress <- progress;
2506 G.postRedisplay "progress"
2508 | "firstmatch" :: args :: [] ->
2509 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2510 scan args "%u %d %f %f %f %f %f %f %f %f"
2511 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2512 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2514 let y = (getpagey pageno) + truncate y0 in
2515 addnav ();
2516 gotoy y;
2517 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2519 | "match" :: args :: [] ->
2520 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2521 scan args "%u %d %f %f %f %f %f %f %f %f"
2522 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2523 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2525 state.rects1 <-
2526 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2528 | "page" :: args :: [] ->
2529 let pageopaque, t = scan args "%s %f" (fun p t -> p, t) in
2530 begin match state.currently with
2531 | Loading (l, gen) ->
2532 vlog "page %d took %f sec" l.pageno t;
2533 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2534 begin match state.throttle with
2535 | None ->
2536 let preloadedpages =
2537 if conf.preload
2538 then preloadlayout state.y
2539 else state.layout
2541 let evict () =
2542 let set =
2543 List.fold_left (fun s l -> IntSet.add l.pageno s)
2544 IntSet.empty preloadedpages
2546 let evictedpages =
2547 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2548 if not (IntSet.mem pageno set)
2549 then (
2550 wcmd "freepage %s" opaque;
2551 key :: accu
2553 else accu
2554 ) state.pagemap []
2556 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2558 evict ();
2559 state.currently <- Idle;
2560 if gen = state.gen
2561 then (
2562 tilepage l.pageno pageopaque state.layout;
2563 load state.layout;
2564 load preloadedpages;
2565 if pagevisible state.layout l.pageno
2566 && layoutready state.layout
2567 then G.postRedisplay "page";
2570 | Some (layout, _, _) ->
2571 state.currently <- Idle;
2572 tilepage l.pageno pageopaque layout;
2573 load state.layout
2574 end;
2576 | _ ->
2577 dolog "Inconsistent loading state";
2578 logcurrently state.currently;
2579 exit 1
2582 | "tile" :: args :: [] ->
2583 let (x, y, opaque, size, t) =
2584 scan args "%u %u %s %u %f"
2585 (fun x y p size t -> (x, y, p, size, t))
2587 begin match state.currently with
2588 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2589 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2591 unmappbo opaque;
2592 if tilew != conf.tilew || tileh != conf.tileh
2593 then (
2594 wcmd "freetile %s" opaque;
2595 state.currently <- Idle;
2596 load state.layout;
2598 else (
2599 puttileopaque l col row gen cs angle opaque size t;
2600 state.memused <- state.memused + size;
2601 state.uioh#infochanged Memused;
2602 gctiles ();
2603 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2604 opaque, size) state.tilelru;
2606 let layout =
2607 match state.throttle with
2608 | None -> state.layout
2609 | Some (layout, _, _) -> layout
2612 state.currently <- Idle;
2613 if gen = state.gen
2614 && conf.colorspace = cs
2615 && conf.angle = angle
2616 && tilevisible layout l.pageno x y
2617 then conttiling l.pageno pageopaque;
2619 begin match state.throttle with
2620 | None ->
2621 preload state.layout;
2622 if gen = state.gen
2623 && conf.colorspace = cs
2624 && conf.angle = angle
2625 && tilevisible state.layout l.pageno x y
2626 && (not !wtmode || layoutready state.layout)
2627 then G.postRedisplay "tile nothrottle";
2629 | Some (layout, y, _) ->
2630 let ready = layoutready layout in
2631 if ready
2632 then (
2633 state.y <- y;
2634 state.layout <- layout;
2635 state.throttle <- None;
2636 G.postRedisplay "throttle";
2638 else load layout;
2639 end;
2642 | _ ->
2643 dolog "Inconsistent tiling state";
2644 logcurrently state.currently;
2645 exit 1
2648 | "pdim" :: args :: [] ->
2649 let pdim =
2650 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2652 state.uioh#infochanged Pdim;
2653 state.pdims <- pdim :: state.pdims
2655 | "o" :: args :: [] ->
2656 let (l, n, t, h, pos) =
2657 scan args "%u %u %d %u %n"
2658 (fun l n t h pos -> l, n, t, h, pos)
2660 let s = String.sub args pos (String.length args - pos) in
2661 let outline = (s, l, (n, float t /. float h, 0.0)) in
2662 begin match state.currently with
2663 | Outlining outlines ->
2664 state.currently <- Outlining (outline :: outlines)
2665 | Idle ->
2666 state.currently <- Outlining [outline]
2667 | currently ->
2668 dolog "invalid outlining state";
2669 logcurrently currently
2672 | "a" :: args :: [] ->
2673 let (n, l, t) =
2674 scan args "%u %d %d" (fun n l t -> n, l, t)
2676 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
2678 | "info" :: args :: [] ->
2679 state.docinfo <- (1, args) :: state.docinfo
2681 | "infoend" :: [] ->
2682 state.uioh#infochanged Docinfo;
2683 state.docinfo <- List.rev state.docinfo
2685 | _ ->
2686 failwith (Printf.sprintf "unknown cmd `%S'" cmds)
2689 let onhist cb =
2690 let rc = cb.rc in
2691 let action = function
2692 | HCprev -> cbget cb ~-1
2693 | HCnext -> cbget cb 1
2694 | HCfirst -> cbget cb ~-(cb.rc)
2695 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2696 and cancel () = cb.rc <- rc
2697 in (action, cancel)
2700 let search pattern forward =
2701 if String.length pattern > 0
2702 then
2703 let pn, py =
2704 match state.layout with
2705 | [] -> 0, 0
2706 | l :: _ ->
2707 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2709 wcmd "search %d %d %d %d,%s\000"
2710 (btod conf.icase) pn py (btod forward) pattern;
2713 let intentry text key =
2714 let c =
2715 if key >= 32 && key < 127
2716 then Char.chr key
2717 else '\000'
2719 match c with
2720 | '0' .. '9' ->
2721 let text = addchar text c in
2722 TEcont text
2724 | _ ->
2725 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2726 TEcont text
2729 let linknentry text key =
2730 let c =
2731 if key >= 32 && key < 127
2732 then Char.chr key
2733 else '\000'
2735 match c with
2736 | 'a' .. 'z' ->
2737 let text = addchar text c in
2738 TEcont text
2740 | _ ->
2741 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2742 TEcont text
2745 let linkndone f s =
2746 if String.length s > 0
2747 then (
2748 let n =
2749 let l = String.length s in
2750 let rec loop pos n = if pos = l then n else
2751 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
2752 loop (pos+1) (n*26 + m)
2753 in loop 0 0
2755 let rec loop n = function
2756 | [] -> ()
2757 | l :: rest ->
2758 match getopaque l.pageno with
2759 | None -> loop n rest
2760 | Some opaque ->
2761 let m = getlinkcount opaque in
2762 if n < m
2763 then (
2764 let under = getlink opaque n in
2765 f under
2767 else loop (n-m) rest
2769 loop n state.layout;
2773 let textentry text key =
2774 if key land 0xff00 = 0xff00
2775 then TEcont text
2776 else TEcont (text ^ toutf8 key)
2779 let reqlayout angle fitmodel =
2780 match state.throttle with
2781 | None ->
2782 if nogeomcmds state.geomcmds
2783 then state.anchor <- getanchor ();
2784 conf.angle <- angle mod 360;
2785 if conf.angle != 0
2786 then (
2787 match state.mode with
2788 | LinkNav _ -> state.mode <- View
2789 | _ -> ()
2791 conf.fitmodel <- fitmodel;
2792 invalidate "reqlayout"
2793 (fun () ->
2794 wcmd "reqlayout %d %d" conf.angle
2795 (int_of_fitmodel_and_zoom conf.fitmodel conf.zoom)
2797 | _ -> ()
2800 let settrim trimmargins trimfuzz =
2801 if nogeomcmds state.geomcmds
2802 then state.anchor <- getanchor ();
2803 conf.trimmargins <- trimmargins;
2804 conf.trimfuzz <- trimfuzz;
2805 let x0, y0, x1, y1 = trimfuzz in
2806 invalidate "settrim"
2807 (fun () ->
2808 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2809 flushpages ();
2812 let setzoom zoom =
2813 match state.throttle with
2814 | None ->
2815 let zoom = max 0.0001 zoom in
2816 if zoom <> conf.zoom
2817 then (
2818 state.prevzoom <- conf.zoom;
2819 conf.zoom <- zoom;
2820 reshape state.winw state.winh;
2821 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
2824 | Some (layout, y, started) ->
2825 let time =
2826 match conf.maxwait with
2827 | None -> 0.0
2828 | Some t -> t
2830 let dt = now () -. started in
2831 if dt > time
2832 then (
2833 state.y <- y;
2834 load layout;
2838 let setcolumns mode columns coverA coverB =
2839 state.prevcolumns <- Some (conf.columns, conf.zoom);
2840 if columns < 0
2841 then (
2842 if isbirdseye mode
2843 then showtext '!' "split mode doesn't work in bird's eye"
2844 else (
2845 conf.columns <- Csplit (-columns, [||]);
2846 state.x <- 0;
2847 conf.zoom <- 1.0;
2850 else (
2851 if columns < 2
2852 then (
2853 conf.columns <- Csingle [||];
2854 state.x <- 0;
2855 setzoom 1.0;
2857 else (
2858 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2859 conf.zoom <- 1.0;
2862 reshape state.winw state.winh;
2865 let enterbirdseye () =
2866 let zoom = float conf.thumbw /. float state.winw in
2867 let birdseyepageno =
2868 let cy = state.winh / 2 in
2869 let fold = function
2870 | [] -> 0
2871 | l :: rest ->
2872 let rec fold best = function
2873 | [] -> best.pageno
2874 | l :: rest ->
2875 let d = cy - (l.pagedispy + l.pagevh/2)
2876 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2877 if abs d < abs dbest
2878 then fold l rest
2879 else best.pageno
2880 in fold l rest
2882 fold state.layout
2884 state.mode <- Birdseye (
2885 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2887 conf.zoom <- zoom;
2888 conf.presentation <- false;
2889 conf.interpagespace <- 10;
2890 conf.hlinks <- false;
2891 conf.fitmodel <- FitProportional;
2892 state.x <- 0;
2893 state.mstate <- Mnone;
2894 conf.maxwait <- None;
2895 conf.columns <- (
2896 match conf.beyecolumns with
2897 | Some c ->
2898 conf.zoom <- 1.0;
2899 Cmulti ((c, 0, 0), [||])
2900 | None -> Csingle [||]
2902 Wsi.setcursor Wsi.CURSOR_INHERIT;
2903 if conf.verbose
2904 then
2905 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2906 (100.0*.zoom)
2907 else
2908 state.text <- ""
2910 reshape state.winw state.winh;
2913 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2914 state.mode <- View;
2915 conf.zoom <- c.zoom;
2916 conf.presentation <- c.presentation;
2917 conf.interpagespace <- c.interpagespace;
2918 conf.maxwait <- c.maxwait;
2919 conf.hlinks <- c.hlinks;
2920 conf.fitmodel <- c.fitmodel;
2921 conf.beyecolumns <- (
2922 match conf.columns with
2923 | Cmulti ((c, _, _), _) -> Some c
2924 | Csingle _ -> None
2925 | Csplit _ -> failwith "leaving bird's eye split mode"
2927 conf.columns <- (
2928 match c.columns with
2929 | Cmulti (c, _) -> Cmulti (c, [||])
2930 | Csingle _ -> Csingle [||]
2931 | Csplit (c, _) -> Csplit (c, [||])
2933 state.x <- leftx;
2934 if conf.verbose
2935 then
2936 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2937 (100.0*.conf.zoom)
2939 reshape state.winw state.winh;
2940 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
2943 let togglebirdseye () =
2944 match state.mode with
2945 | Birdseye vals -> leavebirdseye vals true
2946 | View -> enterbirdseye ()
2947 | _ -> ()
2950 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2951 let pageno = max 0 (pageno - incr) in
2952 let rec loop = function
2953 | [] -> gotopage1 pageno 0
2954 | l :: _ when l.pageno = pageno ->
2955 if l.pagedispy >= 0 && l.pagey = 0
2956 then G.postRedisplay "upbirdseye"
2957 else gotopage1 pageno 0
2958 | _ :: rest -> loop rest
2960 loop state.layout;
2961 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2964 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2965 let pageno = min (state.pagecount - 1) (pageno + incr) in
2966 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2967 let rec loop = function
2968 | [] ->
2969 let y, h = getpageyh pageno in
2970 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
2971 gotoy (clamp dy)
2972 | l :: _ when l.pageno = pageno ->
2973 if l.pagevh != l.pageh
2974 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2975 else G.postRedisplay "downbirdseye"
2976 | _ :: rest -> loop rest
2978 loop state.layout
2981 let optentry mode _ key =
2982 let btos b = if b then "on" else "off" in
2983 if key >= 32 && key < 127
2984 then
2985 let c = Char.chr key in
2986 match c with
2987 | 's' ->
2988 let ondone s =
2989 try conf.scrollstep <- int_of_string s with exc ->
2990 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
2992 TEswitch ("scroll step: ", "", None, intentry, ondone, true)
2994 | 'A' ->
2995 let ondone s =
2997 conf.autoscrollstep <- int_of_string s;
2998 if state.autoscroll <> None
2999 then state.autoscroll <- Some conf.autoscrollstep
3000 with exc ->
3001 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3003 TEswitch ("auto scroll step: ", "", None, intentry, ondone, true)
3005 | 'C' ->
3006 let ondone s =
3008 let n, a, b = multicolumns_of_string s in
3009 setcolumns mode n a b;
3010 with exc ->
3011 state.text <- Printf.sprintf "bad columns `%s': %s" s (exntos exc)
3013 TEswitch ("columns: ", "", None, textentry, ondone, true)
3015 | 'Z' ->
3016 let ondone s =
3018 let zoom = float (int_of_string s) /. 100.0 in
3019 setzoom zoom
3020 with exc ->
3021 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3023 TEswitch ("zoom: ", "", None, intentry, ondone, true)
3025 | 't' ->
3026 let ondone s =
3028 conf.thumbw <- bound (int_of_string s) 2 4096;
3029 state.text <-
3030 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
3031 begin match mode with
3032 | Birdseye beye ->
3033 leavebirdseye beye false;
3034 enterbirdseye ();
3035 | _ -> ();
3037 with exc ->
3038 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3040 TEswitch ("thumbnail width: ", "", None, intentry, ondone, true)
3042 | 'R' ->
3043 let ondone s =
3044 match try
3045 Some (int_of_string s)
3046 with exc ->
3047 state.text <- Printf.sprintf "bad integer `%s': %s"
3048 s (exntos exc);
3049 None
3050 with
3051 | Some angle -> reqlayout angle conf.fitmodel
3052 | None -> ()
3054 TEswitch ("rotation: ", "", None, intentry, ondone, true)
3056 | 'i' ->
3057 conf.icase <- not conf.icase;
3058 TEdone ("case insensitive search " ^ (btos conf.icase))
3060 | 'p' ->
3061 conf.preload <- not conf.preload;
3062 gotoy state.y;
3063 TEdone ("preload " ^ (btos conf.preload))
3065 | 'v' ->
3066 conf.verbose <- not conf.verbose;
3067 TEdone ("verbose " ^ (btos conf.verbose))
3069 | 'd' ->
3070 conf.debug <- not conf.debug;
3071 TEdone ("debug " ^ (btos conf.debug))
3073 | 'h' ->
3074 conf.maxhfit <- not conf.maxhfit;
3075 state.maxy <- calcheight ();
3076 TEdone ("maxhfit " ^ (btos conf.maxhfit))
3078 | 'c' ->
3079 conf.crophack <- not conf.crophack;
3080 TEdone ("crophack " ^ btos conf.crophack)
3082 | 'a' ->
3083 let s =
3084 match conf.maxwait with
3085 | None ->
3086 conf.maxwait <- Some infinity;
3087 "always wait for page to complete"
3088 | Some _ ->
3089 conf.maxwait <- None;
3090 "show placeholder if page is not ready"
3092 TEdone s
3094 | 'f' ->
3095 conf.underinfo <- not conf.underinfo;
3096 TEdone ("underinfo " ^ btos conf.underinfo)
3098 | 'P' ->
3099 conf.savebmarks <- not conf.savebmarks;
3100 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
3102 | 'S' ->
3103 let ondone s =
3105 let pageno, py =
3106 match state.layout with
3107 | [] -> 0, 0
3108 | l :: _ ->
3109 l.pageno, l.pagey
3111 conf.interpagespace <- int_of_string s;
3112 docolumns conf.columns;
3113 state.maxy <- calcheight ();
3114 let y = getpagey pageno in
3115 gotoy (y + py)
3116 with exc ->
3117 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc)
3119 TEswitch ("vertical margin: ", "", None, intentry, ondone, true)
3121 | 'l' ->
3122 let fm =
3123 match conf.fitmodel with
3124 | FitProportional -> FitWidth
3125 | _ -> FitProportional
3127 reqlayout conf.angle fm;
3128 TEdone ("proportional display " ^ btos (fm == FitProportional))
3130 | 'T' ->
3131 settrim (not conf.trimmargins) conf.trimfuzz;
3132 TEdone ("trim margins " ^ btos conf.trimmargins)
3134 | 'I' ->
3135 conf.invert <- not conf.invert;
3136 TEdone ("invert colors " ^ btos conf.invert)
3138 | 'x' ->
3139 let ondone s =
3140 cbput state.hists.sel s;
3141 conf.selcmd <- s;
3143 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
3144 textentry, ondone, true)
3146 | _ ->
3147 state.text <- Printf.sprintf "bad option %d `%c'" key c;
3148 TEstop
3149 else
3150 TEcont state.text
3153 class type lvsource = object
3154 method getitemcount : int
3155 method getitem : int -> (string * int)
3156 method hasaction : int -> bool
3157 method exit :
3158 uioh:uioh ->
3159 cancel:bool ->
3160 active:int ->
3161 first:int ->
3162 pan:int ->
3163 qsearch:string ->
3164 uioh option
3165 method getactive : int
3166 method getfirst : int
3167 method getqsearch : string
3168 method setqsearch : string -> unit
3169 method getpan : int
3170 end;;
3172 class virtual lvsourcebase = object
3173 val mutable m_active = 0
3174 val mutable m_first = 0
3175 val mutable m_qsearch = ""
3176 val mutable m_pan = 0
3177 method getactive = m_active
3178 method getfirst = m_first
3179 method getqsearch = m_qsearch
3180 method getpan = m_pan
3181 method setqsearch s = m_qsearch <- s
3182 end;;
3184 let withoutlastutf8 s =
3185 let len = String.length s in
3186 if len = 0
3187 then s
3188 else
3189 let rec find pos =
3190 if pos = 0
3191 then pos
3192 else
3193 let b = Char.code s.[pos] in
3194 if b land 0b11000000 = 0b11000000
3195 then pos
3196 else find (pos-1)
3198 let first =
3199 if Char.code s.[len-1] land 0x80 = 0
3200 then len-1
3201 else find (len-1)
3203 String.sub s 0 first;
3206 let textentrykeyboard
3207 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
3208 let key =
3209 if key >= 0xffb0 && key <= 0xffb9
3210 then key - 0xffb0 + 48 else key
3212 let enttext te =
3213 state.mode <- Textentry (te, onleave);
3214 state.text <- "";
3215 enttext ();
3216 G.postRedisplay "textentrykeyboard enttext";
3218 let histaction cmd =
3219 match opthist with
3220 | None -> ()
3221 | Some (action, _) ->
3222 state.mode <- Textentry (
3223 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
3225 G.postRedisplay "textentry histaction"
3227 match key with
3228 | 0xff08 -> (* backspace *)
3229 let s = withoutlastutf8 text in
3230 let len = String.length s in
3231 if cancelonempty && len = 0
3232 then (
3233 onleave Cancel;
3234 G.postRedisplay "textentrykeyboard after cancel";
3236 else (
3237 enttext (c, s, opthist, onkey, ondone, cancelonempty)
3240 | 0xff0d | 0xff8d -> (* (kp) enter *)
3241 ondone text;
3242 onleave Confirm;
3243 G.postRedisplay "textentrykeyboard after confirm"
3245 | 0xff52 | 0xff97 -> histaction HCprev (* (kp) up *)
3246 | 0xff54 | 0xff99 -> histaction HCnext (* (kp) down *)
3247 | 0xff50 | 0xff95 -> histaction HCfirst (* (kp) home) *)
3248 | 0xff57 | 0xff9c -> histaction HClast (* (kp) end *)
3250 | 0xff1b -> (* escape*)
3251 if String.length text = 0
3252 then (
3253 begin match opthist with
3254 | None -> ()
3255 | Some (_, onhistcancel) -> onhistcancel ()
3256 end;
3257 onleave Cancel;
3258 state.text <- "";
3259 G.postRedisplay "textentrykeyboard after cancel2"
3261 else (
3262 enttext (c, "", opthist, onkey, ondone, cancelonempty)
3265 | 0xff9f | 0xffff -> () (* delete *)
3267 | _ when key != 0
3268 && key land 0xff00 != 0xff00 (* keyboard *)
3269 && key land 0xfe00 != 0xfe00 (* xkb *)
3270 && key land 0xfd00 != 0xfd00 (* 3270 *)
3272 begin match onkey text key with
3273 | TEdone text ->
3274 ondone text;
3275 onleave Confirm;
3276 G.postRedisplay "textentrykeyboard after confirm2";
3278 | TEcont text ->
3279 enttext (c, text, opthist, onkey, ondone, cancelonempty);
3281 | TEstop ->
3282 onleave Cancel;
3283 G.postRedisplay "textentrykeyboard after cancel3"
3285 | TEswitch te ->
3286 state.mode <- Textentry (te, onleave);
3287 G.postRedisplay "textentrykeyboard switch";
3288 end;
3290 | _ ->
3291 vlog "unhandled key %s" (Wsi.keyname key)
3294 let firstof first active =
3295 if first > active || abs (first - active) > fstate.maxrows - 1
3296 then max 0 (active - (fstate.maxrows/2))
3297 else first
3300 let calcfirst first active =
3301 if active > first
3302 then
3303 let rows = active - first in
3304 if rows > fstate.maxrows then active - fstate.maxrows else first
3305 else active
3308 let scrollph y maxy =
3309 let sh = float (maxy + state.winh) /. float state.winh in
3310 let sh = float state.winh /. sh in
3311 let sh = max sh (float conf.scrollh) in
3313 let percent = float y /. float maxy in
3314 let position = (float state.winh -. sh) *. percent in
3316 let position =
3317 if position +. sh > float state.winh
3318 then float state.winh -. sh
3319 else position
3321 position, sh;
3324 let coe s = (s :> uioh);;
3326 class listview ~(source:lvsource) ~trusted ~modehash =
3327 object (self)
3328 val m_pan = source#getpan
3329 val m_first = source#getfirst
3330 val m_active = source#getactive
3331 val m_qsearch = source#getqsearch
3332 val m_prev_uioh = state.uioh
3334 method private elemunder y =
3335 let n = y / (fstate.fontsize+1) in
3336 if m_first + n < source#getitemcount
3337 then (
3338 if source#hasaction (m_first + n)
3339 then Some (m_first + n)
3340 else None
3342 else None
3344 method display =
3345 Gl.enable `blend;
3346 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3347 GlDraw.color (0., 0., 0.) ~alpha:0.85;
3348 GlDraw.rect (0., 0.) (float state.winw, float state.winh);
3349 GlDraw.color (1., 1., 1.);
3350 Gl.enable `texture_2d;
3351 let fs = fstate.fontsize in
3352 let nfs = fs + 1 in
3353 let ww = fstate.wwidth in
3354 let tabw = 30.0*.ww in
3355 let itemcount = source#getitemcount in
3356 let rec loop row =
3357 if (row - m_first) > fstate.maxrows
3358 then ()
3359 else (
3360 if row >= 0 && row < itemcount
3361 then (
3362 let (s, level) = source#getitem row in
3363 let y = (row - m_first) * nfs in
3364 let x = 5.0 +. float (level + m_pan) *. ww in
3365 if row = m_active
3366 then (
3367 Gl.disable `texture_2d;
3368 GlDraw.polygon_mode `both `line;
3369 let alpha = if source#hasaction row then 0.9 else 0.3 in
3370 GlDraw.color (1., 1., 1.) ~alpha;
3371 GlDraw.rect (1., float (y + 1))
3372 (float (state.winw - conf.scrollbw - 1), float (y + fs + 3));
3373 GlDraw.polygon_mode `both `fill;
3374 GlDraw.color (1., 1., 1.);
3375 Gl.enable `texture_2d;
3378 let drawtabularstring s =
3379 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
3380 if trusted
3381 then
3382 let tabpos = try String.index s '\t' with Not_found -> -1 in
3383 if tabpos > 0
3384 then
3385 let len = String.length s - tabpos - 1 in
3386 let s1 = String.sub s 0 tabpos
3387 and s2 = String.sub s (tabpos + 1) len in
3388 let nx = drawstr x s1 in
3389 let sw = nx -. x in
3390 let x = x +. (max tabw sw) in
3391 drawstr x s2
3392 else
3393 drawstr x s
3394 else
3395 drawstr x s
3397 let _ = drawtabularstring s in
3398 loop (row+1)
3402 loop m_first;
3403 Gl.disable `blend;
3404 Gl.disable `texture_2d;
3406 method updownlevel incr =
3407 let len = source#getitemcount in
3408 let curlevel =
3409 if m_active >= 0 && m_active < len
3410 then snd (source#getitem m_active)
3411 else -1
3413 let rec flow i =
3414 if i = len then i-1 else if i = -1 then 0 else
3415 let _, l = source#getitem i in
3416 if l != curlevel then i else flow (i+incr)
3418 let active = flow m_active in
3419 let first = calcfirst m_first active in
3420 G.postRedisplay "outline updownlevel";
3421 {< m_active = active; m_first = first >}
3423 method private key1 key mask =
3424 let set1 active first qsearch =
3425 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3427 let search active pattern incr =
3428 let active = if active = -1 then m_first else active in
3429 let dosearch re =
3430 let rec loop n =
3431 if n >= 0 && n < source#getitemcount
3432 then (
3433 let s, _ = source#getitem n in
3435 (try ignore (Str.search_forward re s 0); true
3436 with Not_found -> false)
3437 then Some n
3438 else loop (n + incr)
3440 else None
3442 loop active
3445 let re = Str.regexp_case_fold pattern in
3446 dosearch re
3447 with Failure s ->
3448 state.text <- s;
3449 None
3451 let itemcount = source#getitemcount in
3452 let find start incr =
3453 let rec find i =
3454 if i = -1 || i = itemcount
3455 then -1
3456 else (
3457 if source#hasaction i
3458 then i
3459 else find (i + incr)
3462 find start
3464 let set active first =
3465 let first = bound first 0 (itemcount - fstate.maxrows) in
3466 state.text <- "";
3467 coe {< m_active = active; m_first = first; m_qsearch = "" >}
3469 let navigate incr =
3470 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3471 let active, first =
3472 let incr1 = if incr > 0 then 1 else -1 in
3473 if isvisible m_first m_active
3474 then
3475 let next =
3476 let next = m_active + incr in
3477 let next =
3478 if next < 0 || next >= itemcount
3479 then -1
3480 else find next incr1
3482 if next = -1 || abs (m_active - next) > fstate.maxrows
3483 then -1
3484 else next
3486 if next = -1
3487 then
3488 let first = m_first + incr in
3489 let first = bound first 0 (itemcount - 1) in
3490 let next =
3491 let next = m_active + incr in
3492 let next = bound next 0 (itemcount - 1) in
3493 find next ~-incr1
3495 let active = if next = -1 then m_active else next in
3496 active, first
3497 else
3498 let first = min next m_first in
3499 let first =
3500 if abs (next - first) > fstate.maxrows
3501 then first + incr
3502 else first
3504 next, first
3505 else
3506 let first = m_first + incr in
3507 let first = bound first 0 (itemcount - 1) in
3508 let active =
3509 let next = m_active + incr in
3510 let next = bound next 0 (itemcount - 1) in
3511 let next = find next incr1 in
3512 let active =
3513 if next = -1 || abs (m_active - first) > fstate.maxrows
3514 then (
3515 let active = if m_active = -1 then next else m_active in
3516 active
3518 else next
3520 if isvisible first active
3521 then active
3522 else -1
3524 active, first
3526 G.postRedisplay "listview navigate";
3527 set active first;
3529 match key with
3530 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3531 let incr = if key = 0x72 then -1 else 1 in
3532 let active, first =
3533 match search (m_active + incr) m_qsearch incr with
3534 | None ->
3535 state.text <- m_qsearch ^ " [not found]";
3536 m_active, m_first
3537 | Some active ->
3538 state.text <- m_qsearch;
3539 active, firstof m_first active
3541 G.postRedisplay "listview ctrl-r/s";
3542 set1 active first m_qsearch;
3544 | 0xff63 when Wsi.withctrl mask -> (* ctrl-insert *)
3545 if m_active >= 0 && m_active < source#getitemcount
3546 then (
3547 let s, _ = source#getitem m_active in
3548 selstring s;
3550 coe self
3552 | 0xff08 -> (* backspace *)
3553 if String.length m_qsearch = 0
3554 then coe self
3555 else (
3556 let qsearch = withoutlastutf8 m_qsearch in
3557 let len = String.length qsearch in
3558 if len = 0
3559 then (
3560 state.text <- "";
3561 G.postRedisplay "listview empty qsearch";
3562 set1 m_active m_first "";
3564 else
3565 let active, first =
3566 match search m_active qsearch ~-1 with
3567 | None ->
3568 state.text <- qsearch ^ " [not found]";
3569 m_active, m_first
3570 | Some active ->
3571 state.text <- qsearch;
3572 active, firstof m_first active
3574 G.postRedisplay "listview backspace qsearch";
3575 set1 active first qsearch
3578 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3579 let pattern = m_qsearch ^ toutf8 key in
3580 let active, first =
3581 match search m_active pattern 1 with
3582 | None ->
3583 state.text <- pattern ^ " [not found]";
3584 m_active, m_first
3585 | Some active ->
3586 state.text <- pattern;
3587 active, firstof m_first active
3589 G.postRedisplay "listview qsearch add";
3590 set1 active first pattern;
3592 | 0xff1b -> (* escape *)
3593 state.text <- "";
3594 if String.length m_qsearch = 0
3595 then (
3596 G.postRedisplay "list view escape";
3597 begin
3598 match
3599 source#exit (coe self) true m_active m_first m_pan m_qsearch
3600 with
3601 | None -> m_prev_uioh
3602 | Some uioh -> uioh
3605 else (
3606 G.postRedisplay "list view kill qsearch";
3607 source#setqsearch "";
3608 coe {< m_qsearch = "" >}
3611 | 0xff0d | 0xff8d -> (* (kp) enter *)
3612 state.text <- "";
3613 let self = {< m_qsearch = "" >} in
3614 source#setqsearch "";
3615 let opt =
3616 G.postRedisplay "listview enter";
3617 if m_active >= 0 && m_active < source#getitemcount
3618 then (
3619 source#exit (coe self) false m_active m_first m_pan "";
3621 else (
3622 source#exit (coe self) true m_active m_first m_pan "";
3625 begin match opt with
3626 | None -> m_prev_uioh
3627 | Some uioh -> uioh
3630 | 0xff9f | 0xffff -> (* (kp) delete *)
3631 coe self
3633 | 0xff52 | 0xff97 -> navigate ~-1 (* (kp) up *)
3634 | 0xff54 | 0xff99 -> navigate 1 (* (kp) down *)
3635 | 0xff55 | 0xff9a -> navigate ~-(fstate.maxrows) (* (kp) prior *)
3636 | 0xff56 | 0xff9b -> navigate fstate.maxrows (* (kp) next *)
3638 | 0xff53 | 0xff98 -> (* (kp) right *)
3639 state.text <- "";
3640 G.postRedisplay "listview right";
3641 coe {< m_pan = m_pan - 1 >}
3643 | 0xff51 | 0xff96 -> (* (kp) left *)
3644 state.text <- "";
3645 G.postRedisplay "listview left";
3646 coe {< m_pan = m_pan + 1 >}
3648 | 0xff50 | 0xff95 -> (* (kp) home *)
3649 let active = find 0 1 in
3650 G.postRedisplay "listview home";
3651 set active 0;
3653 | 0xff57 | 0xff9c -> (* (kp) end *)
3654 let first = max 0 (itemcount - fstate.maxrows) in
3655 let active = find (itemcount - 1) ~-1 in
3656 G.postRedisplay "listview end";
3657 set active first;
3659 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3660 coe self
3662 | _ ->
3663 dolog "listview unknown key %#x" key; coe self
3665 method key key mask =
3666 match state.mode with
3667 | Textentry te -> textentrykeyboard key mask te; coe self
3668 | _ -> self#key1 key mask
3670 method button button down x y _ =
3671 let opt =
3672 match button with
3673 | 1 when x > state.winw - conf.scrollbw ->
3674 G.postRedisplay "listview scroll";
3675 if down
3676 then
3677 let _, position, sh = self#scrollph in
3678 if y > truncate position && y < truncate (position +. sh)
3679 then (
3680 state.mstate <- Mscrolly;
3681 Some (coe self)
3683 else
3684 let s = float (max 0 (y - conf.scrollh)) /. float state.winh in
3685 let first = truncate (s *. float source#getitemcount) in
3686 let first = min source#getitemcount first in
3687 Some (coe {< m_first = first; m_active = first >})
3688 else (
3689 state.mstate <- Mnone;
3690 Some (coe self);
3692 | 1 when not down ->
3693 begin match self#elemunder y with
3694 | Some n ->
3695 G.postRedisplay "listview click";
3696 source#exit
3697 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3698 | _ ->
3699 Some (coe self)
3701 | n when (n == 4 || n == 5) && not down ->
3702 let len = source#getitemcount in
3703 let first =
3704 if n = 5 && m_first + fstate.maxrows >= len
3705 then
3706 m_first
3707 else
3708 let first = m_first + (if n == 4 then -1 else 1) in
3709 bound first 0 (len - 1)
3711 G.postRedisplay "listview wheel";
3712 Some (coe {< m_first = first >})
3713 | n when (n = 6 || n = 7) && not down ->
3714 let inc = m_first + (if n = 7 then -1 else 1) in
3715 G.postRedisplay "listview hwheel";
3716 Some (coe {< m_pan = m_pan + inc >})
3717 | _ ->
3718 Some (coe self)
3720 match opt with
3721 | None -> m_prev_uioh
3722 | Some uioh -> uioh
3724 method motion _ y =
3725 match state.mstate with
3726 | Mscrolly ->
3727 let s = float (max 0 (y - conf.scrollh)) /. float state.winh in
3728 let first = truncate (s *. float source#getitemcount) in
3729 let first = min source#getitemcount first in
3730 G.postRedisplay "listview motion";
3731 coe {< m_first = first; m_active = first >}
3732 | _ -> coe self
3734 method pmotion x y =
3735 if x < state.winw - conf.scrollbw
3736 then
3737 let n =
3738 match self#elemunder y with
3739 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3740 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3742 let o =
3743 if n != m_active
3744 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3745 else self
3747 coe o
3748 else (
3749 Wsi.setcursor Wsi.CURSOR_INHERIT;
3750 coe self
3753 method infochanged _ = ()
3755 method scrollpw = (0, 0.0, 0.0)
3756 method scrollph =
3757 let nfs = fstate.fontsize + 1 in
3758 let y = m_first * nfs in
3759 let itemcount = source#getitemcount in
3760 let maxi = max 0 (itemcount - fstate.maxrows) in
3761 let maxy = maxi * nfs in
3762 let p, h = scrollph y maxy in
3763 conf.scrollbw, p, h
3765 method modehash = modehash
3766 method eformsgs = false
3767 end;;
3769 class outlinelistview ~source =
3770 object (self)
3771 inherit listview
3772 ~source:(source :> lvsource)
3773 ~trusted:false
3774 ~modehash:(findkeyhash conf "outline")
3775 as super
3777 method key key mask =
3778 let calcfirst first active =
3779 if active > first
3780 then
3781 let rows = active - first in
3782 let maxrows =
3783 if String.length state.text = 0
3784 then fstate.maxrows
3785 else fstate.maxrows - 2
3787 if rows > maxrows then active - maxrows else first
3788 else active
3790 let navigate incr =
3791 let active = m_active + incr in
3792 let active = bound active 0 (source#getitemcount - 1) in
3793 let first = calcfirst m_first active in
3794 G.postRedisplay "outline navigate";
3795 coe {< m_active = active; m_first = first >}
3797 let ctrl = Wsi.withctrl mask in
3798 match key with
3799 | 110 when ctrl -> (* ctrl-n *)
3800 source#narrow m_qsearch;
3801 G.postRedisplay "outline ctrl-n";
3802 coe {< m_first = 0; m_active = 0 >}
3804 | 117 when ctrl -> (* ctrl-u *)
3805 source#denarrow;
3806 G.postRedisplay "outline ctrl-u";
3807 state.text <- "";
3808 coe {< m_first = 0; m_active = 0 >}
3810 | 108 when ctrl -> (* ctrl-l *)
3811 let first = max 0 (m_active - (fstate.maxrows / 2)) in
3812 G.postRedisplay "outline ctrl-l";
3813 coe {< m_first = first >}
3815 | 0xff9f | 0xffff -> (* (kp) delete *)
3816 source#remove m_active;
3817 G.postRedisplay "outline delete";
3818 let active = max 0 (m_active-1) in
3819 coe {< m_first = firstof m_first active;
3820 m_active = active >}
3822 | 0xff52 | 0xff97 -> navigate ~-1 (* (kp) up *)
3823 | 0xff54 | 0xff99 -> navigate 1 (* (kp) down *)
3824 | 0xff55 | 0xff9a -> (* (kp) prior *)
3825 navigate ~-(fstate.maxrows)
3826 | 0xff56 | 0xff9b -> (* (kp) next *)
3827 navigate fstate.maxrows
3829 | 0xff53 | 0xff98 -> (* [ctrl-] (kp) right *)
3830 let o =
3831 if ctrl
3832 then (
3833 G.postRedisplay "outline ctrl right";
3834 {< m_pan = m_pan + 1 >}
3836 else self#updownlevel 1
3838 coe o
3840 | 0xff51 | 0xff96 -> (* [ctrl-] (kp) left *)
3841 let o =
3842 if ctrl
3843 then (
3844 G.postRedisplay "outline ctrl left";
3845 {< m_pan = m_pan - 1 >}
3847 else self#updownlevel ~-1
3849 coe o
3851 | 0xff50 | 0xff95 -> (* (kp) home *)
3852 G.postRedisplay "outline home";
3853 coe {< m_first = 0; m_active = 0 >}
3855 | 0xff57 | 0xff9c -> (* (kp) end *)
3856 let active = source#getitemcount - 1 in
3857 let first = max 0 (active - fstate.maxrows) in
3858 G.postRedisplay "outline end";
3859 coe {< m_active = active; m_first = first >}
3861 | _ -> super#key key mask
3864 let outlinesource usebookmarks =
3865 let empty = [||] in
3866 (object
3867 inherit lvsourcebase
3868 val mutable m_items = empty
3869 val mutable m_orig_items = empty
3870 val mutable m_prev_items = empty
3871 val mutable m_narrow_pattern = ""
3872 val mutable m_hadremovals = false
3874 method getitemcount =
3875 Array.length m_items + (if m_hadremovals then 1 else 0)
3877 method getitem n =
3878 if n == Array.length m_items && m_hadremovals
3879 then
3880 ("[Confirm removal]", 0)
3881 else
3882 let s, n, _ = m_items.(n) in
3883 (s, n)
3885 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3886 ignore (uioh, first, qsearch);
3887 let confrimremoval = m_hadremovals && active = Array.length m_items in
3888 let items =
3889 if String.length m_narrow_pattern = 0
3890 then m_orig_items
3891 else m_items
3893 if not cancel
3894 then (
3895 if not confrimremoval
3896 then(
3897 let _, _, anchor = m_items.(active) in
3898 gotoghyll (getanchory anchor);
3899 m_items <- items;
3901 else (
3902 state.bookmarks <- Array.to_list m_items;
3903 m_orig_items <- m_items;
3906 else m_items <- items;
3907 m_pan <- pan;
3908 None
3910 method hasaction _ = true
3912 method greetmsg =
3913 if Array.length m_items != Array.length m_orig_items
3914 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3915 else ""
3917 method narrow pattern =
3918 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3919 match reopt with
3920 | None -> ()
3921 | Some re ->
3922 let rec loop accu n =
3923 if n = -1
3924 then (
3925 m_narrow_pattern <- pattern;
3926 m_items <- Array.of_list accu
3928 else
3929 let (s, _, _) as o = m_items.(n) in
3930 let accu =
3931 if (try ignore (Str.search_forward re s 0); true
3932 with Not_found -> false)
3933 then o :: accu
3934 else accu
3936 loop accu (n-1)
3938 loop [] (Array.length m_items - 1)
3940 method denarrow =
3941 m_orig_items <- (
3942 if usebookmarks
3943 then Array.of_list state.bookmarks
3944 else state.outlines
3946 m_items <- m_orig_items
3948 method remove m =
3949 if usebookmarks
3950 then
3951 if m >= 0 && m < Array.length m_items
3952 then (
3953 m_hadremovals <- true;
3954 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3955 let n = if n >= m then n+1 else n in
3956 m_items.(n)
3960 method reset anchor items =
3961 m_hadremovals <- false;
3962 if m_orig_items == empty || m_prev_items != items
3963 then (
3964 m_orig_items <- items;
3965 if String.length m_narrow_pattern = 0
3966 then m_items <- items;
3968 m_prev_items <- items;
3969 let rely = getanchory anchor in
3970 let active =
3971 let rec loop n best bestd =
3972 if n = Array.length m_items
3973 then best
3974 else
3975 let (_, _, anchor) = m_items.(n) in
3976 let orely = getanchory anchor in
3977 let d = abs (orely - rely) in
3978 if d < bestd
3979 then loop (n+1) n d
3980 else loop (n+1) best bestd
3982 loop 0 ~-1 max_int
3984 m_active <- active;
3985 m_first <- firstof m_first active
3986 end)
3989 let enterselector usebookmarks =
3990 let source = outlinesource usebookmarks in
3991 fun errmsg ->
3992 let outlines =
3993 if usebookmarks
3994 then Array.of_list state.bookmarks
3995 else state.outlines
3997 if Array.length outlines = 0
3998 then (
3999 showtext ' ' errmsg;
4001 else (
4002 state.text <- source#greetmsg;
4003 Wsi.setcursor Wsi.CURSOR_INHERIT;
4004 let anchor = getanchor () in
4005 source#reset anchor outlines;
4006 state.uioh <- coe (new outlinelistview ~source);
4007 G.postRedisplay "enter selector";
4011 let enteroutlinemode =
4012 let f = enterselector false in
4013 fun ()-> f "Document has no outline";
4016 let enterbookmarkmode =
4017 let f = enterselector true in
4018 fun () -> f "Document has no bookmarks (yet)";
4021 let color_of_string s =
4022 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
4023 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
4027 let color_to_string (r, g, b) =
4028 let r = truncate (r *. 256.0)
4029 and g = truncate (g *. 256.0)
4030 and b = truncate (b *. 256.0) in
4031 Printf.sprintf "%d/%d/%d" r g b
4034 let irect_of_string s =
4035 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
4038 let irect_to_string (x0,y0,x1,y1) =
4039 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
4042 let makecheckers () =
4043 (* Based on lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
4044 following to say:
4045 converted by Issac Trotts. July 25, 2002 *)
4046 let image = GlPix.create `ubyte ~format:`luminance ~width:2 ~height:2 in
4047 Raw.sets_string (GlPix.to_raw image) ~pos:0 "\255\200\200\255";
4048 let id = GlTex.gen_texture () in
4049 GlTex.bind_texture `texture_2d id;
4050 GlPix.store (`unpack_alignment 1);
4051 GlTex.image2d image;
4052 List.iter (GlTex.parameter ~target:`texture_2d)
4053 [ `mag_filter `nearest; `min_filter `nearest ];
4057 let setcheckers enabled =
4058 match state.texid with
4059 | None ->
4060 if enabled then state.texid <- Some (makecheckers ())
4062 | Some texid ->
4063 if not enabled
4064 then (
4065 GlTex.delete_texture texid;
4066 state.texid <- None;
4070 let int_of_string_with_suffix s =
4071 let l = String.length s in
4072 let s1, shift =
4073 if l > 1
4074 then
4075 let suffix = Char.lowercase s.[l-1] in
4076 match suffix with
4077 | 'k' -> String.sub s 0 (l-1), 10
4078 | 'm' -> String.sub s 0 (l-1), 20
4079 | 'g' -> String.sub s 0 (l-1), 30
4080 | _ -> s, 0
4081 else s, 0
4083 let n = int_of_string s1 in
4084 let m = n lsl shift in
4085 if m < 0 || m < n
4086 then raise (Failure "value too large")
4087 else m
4090 let string_with_suffix_of_int n =
4091 if n = 0
4092 then "0"
4093 else
4094 let n, s =
4095 if n land ((1 lsl 30) - 1) = 0
4096 then n lsr 30, "G"
4097 else (
4098 if n land ((1 lsl 20) - 1) = 0
4099 then n lsr 20, "M"
4100 else (
4101 if n land ((1 lsl 10) - 1) = 0
4102 then n lsr 10, "K"
4103 else n, ""
4107 let rec loop s n =
4108 let h = n mod 1000 in
4109 let n = n / 1000 in
4110 if n = 0
4111 then string_of_int h ^ s
4112 else (
4113 let s = Printf.sprintf "_%03d%s" h s in
4114 loop s n
4117 loop "" n ^ s;
4120 let defghyllscroll = (40, 8, 32);;
4121 let ghyllscroll_of_string s =
4122 let (n, a, b) as nab =
4123 if s = "default"
4124 then defghyllscroll
4125 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
4127 if n <= a || n <= b || a >= b
4128 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
4129 nab;
4132 let ghyllscroll_to_string ((n, a, b) as nab) =
4133 if nab = defghyllscroll
4134 then "default"
4135 else Printf.sprintf "%d,%d,%d" n a b;
4138 let describe_location () =
4139 let fn = page_of_y state.y in
4140 let ln = page_of_y (state.y + state.winh - state.hscrollh - 1) in
4141 let maxy = state.maxy - (if conf.maxhfit then state.winh else 0) in
4142 let percent =
4143 if maxy <= 0
4144 then 100.
4145 else (100. *. (float state.y /. float maxy))
4147 if fn = ln
4148 then
4149 Printf.sprintf "page %d of %d [%.2f%%]"
4150 (fn+1) state.pagecount percent
4151 else
4152 Printf.sprintf
4153 "pages %d-%d of %d [%.2f%%]"
4154 (fn+1) (ln+1) state.pagecount percent
4157 let setpresentationmode v =
4158 let n = page_of_y state.y in
4159 state.anchor <- (n, 0.0, 1.0);
4160 conf.presentation <- v;
4161 if conf.presentation
4162 then (
4163 if not conf.scrollbarinpm
4164 then state.scrollw <- 0;
4166 else state.scrollw <- conf.scrollbw;
4167 represent ();
4170 let enterinfomode =
4171 let btos b = if b then "\xe2\x88\x9a" else "" in
4172 let showextended = ref false in
4173 let leave mode = function
4174 | Confirm -> state.mode <- mode
4175 | Cancel -> state.mode <- mode in
4176 let src =
4177 (object
4178 val mutable m_first_time = true
4179 val mutable m_l = []
4180 val mutable m_a = [||]
4181 val mutable m_prev_uioh = nouioh
4182 val mutable m_prev_mode = View
4184 inherit lvsourcebase
4186 method reset prev_mode prev_uioh =
4187 m_a <- Array.of_list (List.rev m_l);
4188 m_l <- [];
4189 m_prev_mode <- prev_mode;
4190 m_prev_uioh <- prev_uioh;
4191 if m_first_time
4192 then (
4193 let rec loop n =
4194 if n >= Array.length m_a
4195 then ()
4196 else
4197 match m_a.(n) with
4198 | _, _, _, Action _ -> m_active <- n
4199 | _ -> loop (n+1)
4201 loop 0;
4202 m_first_time <- false;
4205 method int name get set =
4206 m_l <-
4207 (name, `int get, 1, Action (
4208 fun u ->
4209 let ondone s =
4210 try set (int_of_string s)
4211 with exn ->
4212 state.text <- Printf.sprintf "bad integer `%s': %s"
4213 s (exntos exn)
4215 state.text <- "";
4216 let te = name ^ ": ", "", None, intentry, ondone, true in
4217 state.mode <- Textentry (te, leave m_prev_mode);
4219 )) :: m_l
4221 method int_with_suffix name get set =
4222 m_l <-
4223 (name, `intws get, 1, Action (
4224 fun u ->
4225 let ondone s =
4226 try set (int_of_string_with_suffix s)
4227 with exn ->
4228 state.text <- Printf.sprintf "bad integer `%s': %s"
4229 s (exntos exn)
4231 state.text <- "";
4232 let te =
4233 name ^ ": ", "", None, intentry_with_suffix, ondone, true
4235 state.mode <- Textentry (te, leave m_prev_mode);
4237 )) :: m_l
4239 method bool ?(offset=1) ?(btos=btos) name get set =
4240 m_l <-
4241 (name, `bool (btos, get), offset, Action (
4242 fun u ->
4243 let v = get () in
4244 set (not v);
4246 )) :: m_l
4248 method color name get set =
4249 m_l <-
4250 (name, `color get, 1, Action (
4251 fun u ->
4252 let invalid = (nan, nan, nan) in
4253 let ondone s =
4254 let c =
4255 try color_of_string s
4256 with exn ->
4257 state.text <- Printf.sprintf "bad color `%s': %s"
4258 s (exntos exn);
4259 invalid
4261 if c <> invalid
4262 then set c;
4264 let te = name ^ ": ", "", None, textentry, ondone, true in
4265 state.text <- color_to_string (get ());
4266 state.mode <- Textentry (te, leave m_prev_mode);
4268 )) :: m_l
4270 method string name get set =
4271 m_l <-
4272 (name, `string get, 1, Action (
4273 fun u ->
4274 let ondone s = set s in
4275 let te = name ^ ": ", "", None, textentry, ondone, true in
4276 state.mode <- Textentry (te, leave m_prev_mode);
4278 )) :: m_l
4280 method colorspace name get set =
4281 m_l <-
4282 (name, `string get, 1, Action (
4283 fun _ ->
4284 let source =
4285 let vals = [| "rgb"; "bgr"; "gray" |] in
4286 (object
4287 inherit lvsourcebase
4289 initializer
4290 m_active <- int_of_colorspace conf.colorspace;
4291 m_first <- 0;
4293 method getitemcount = Array.length vals
4294 method getitem n = (vals.(n), 0)
4295 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4296 ignore (uioh, first, pan, qsearch);
4297 if not cancel then set active;
4298 None
4299 method hasaction _ = true
4300 end)
4302 state.text <- "";
4303 let modehash = findkeyhash conf "info" in
4304 coe (new listview ~source ~trusted:true ~modehash)
4305 )) :: m_l
4307 method fitmodel name get set =
4308 m_l <-
4309 (name, `string get, 1, Action (
4310 fun _ ->
4311 let source =
4312 let vals = [| "fit width"; "proportional"; "fit page" |] in
4313 (object
4314 inherit lvsourcebase
4316 initializer
4317 m_active <- int_of_fitmodel conf.fitmodel;
4318 m_first <- 0;
4320 method getitemcount = Array.length vals
4321 method getitem n = (vals.(n), 0)
4322 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4323 ignore (uioh, first, pan, qsearch);
4324 if not cancel then set active;
4325 None
4326 method hasaction _ = true
4327 end)
4329 state.text <- "";
4330 let modehash = findkeyhash conf "info" in
4331 coe (new listview ~source ~trusted:true ~modehash)
4332 )) :: m_l
4334 method caption s offset =
4335 m_l <- (s, `empty, offset, Noaction) :: m_l
4337 method caption2 s f offset =
4338 m_l <- (s, `string f, offset, Noaction) :: m_l
4340 method getitemcount = Array.length m_a
4342 method getitem n =
4343 let tostr = function
4344 | `int f -> string_of_int (f ())
4345 | `intws f -> string_with_suffix_of_int (f ())
4346 | `string f -> f ()
4347 | `color f -> color_to_string (f ())
4348 | `bool (btos, f) -> btos (f ())
4349 | `empty -> ""
4351 let name, t, offset, _ = m_a.(n) in
4352 ((let s = tostr t in
4353 if String.length s > 0
4354 then Printf.sprintf "%s\t%s" name s
4355 else name),
4356 offset)
4358 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4359 let uiohopt =
4360 if not cancel
4361 then (
4362 m_qsearch <- qsearch;
4363 let uioh =
4364 match m_a.(active) with
4365 | _, _, _, Action f -> f uioh
4366 | _ -> uioh
4368 Some uioh
4370 else None
4372 m_active <- active;
4373 m_first <- first;
4374 m_pan <- pan;
4375 uiohopt
4377 method hasaction n =
4378 match m_a.(n) with
4379 | _, _, _, Action _ -> true
4380 | _ -> false
4381 end)
4383 let rec fillsrc prevmode prevuioh =
4384 let sep () = src#caption "" 0 in
4385 let colorp name get set =
4386 src#string name
4387 (fun () -> color_to_string (get ()))
4388 (fun v ->
4390 let c = color_of_string v in
4391 set c
4392 with exn ->
4393 state.text <- Printf.sprintf "bad color `%s': %s" v (exntos exn)
4396 let oldmode = state.mode in
4397 let birdseye = isbirdseye state.mode in
4399 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
4401 src#bool "presentation mode"
4402 (fun () -> conf.presentation)
4403 (fun v -> setpresentationmode v);
4405 src#bool "ignore case in searches"
4406 (fun () -> conf.icase)
4407 (fun v -> conf.icase <- v);
4409 src#bool "preload"
4410 (fun () -> conf.preload)
4411 (fun v -> conf.preload <- v);
4413 src#bool "highlight links"
4414 (fun () -> conf.hlinks)
4415 (fun v -> conf.hlinks <- v);
4417 src#bool "under info"
4418 (fun () -> conf.underinfo)
4419 (fun v -> conf.underinfo <- v);
4421 src#bool "persistent bookmarks"
4422 (fun () -> conf.savebmarks)
4423 (fun v -> conf.savebmarks <- v);
4425 src#fitmodel "fit model"
4426 (fun () -> fitmodel_to_string conf.fitmodel)
4427 (fun v -> reqlayout conf.angle (fitmodel_of_int v));
4429 src#bool "trim margins"
4430 (fun () -> conf.trimmargins)
4431 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
4433 src#bool "persistent location"
4434 (fun () -> conf.jumpback)
4435 (fun v -> conf.jumpback <- v);
4437 sep ();
4438 src#int "inter-page space"
4439 (fun () -> conf.interpagespace)
4440 (fun n ->
4441 conf.interpagespace <- n;
4442 docolumns conf.columns;
4443 let pageno, py =
4444 match state.layout with
4445 | [] -> 0, 0
4446 | l :: _ ->
4447 l.pageno, l.pagey
4449 state.maxy <- calcheight ();
4450 let y = getpagey pageno in
4451 gotoy (y + py)
4454 src#int "page bias"
4455 (fun () -> conf.pagebias)
4456 (fun v -> conf.pagebias <- v);
4458 src#int "scroll step"
4459 (fun () -> conf.scrollstep)
4460 (fun n -> conf.scrollstep <- n);
4462 src#int "horizontal scroll step"
4463 (fun () -> conf.hscrollstep)
4464 (fun v -> conf.hscrollstep <- v);
4466 src#int "auto scroll step"
4467 (fun () ->
4468 match state.autoscroll with
4469 | Some step -> step
4470 | _ -> conf.autoscrollstep)
4471 (fun n ->
4472 if state.autoscroll <> None
4473 then state.autoscroll <- Some n;
4474 conf.autoscrollstep <- n);
4476 src#int "zoom"
4477 (fun () -> truncate (conf.zoom *. 100.))
4478 (fun v -> setzoom ((float v) /. 100.));
4480 src#int "rotation"
4481 (fun () -> conf.angle)
4482 (fun v -> reqlayout v conf.fitmodel);
4484 src#int "scroll bar width"
4485 (fun () -> state.scrollw)
4486 (fun v ->
4487 state.scrollw <- v;
4488 conf.scrollbw <- v;
4489 reshape state.winw state.winh;
4492 src#int "scroll handle height"
4493 (fun () -> conf.scrollh)
4494 (fun v -> conf.scrollh <- v;);
4496 src#int "thumbnail width"
4497 (fun () -> conf.thumbw)
4498 (fun v ->
4499 conf.thumbw <- min 4096 v;
4500 match oldmode with
4501 | Birdseye beye ->
4502 leavebirdseye beye false;
4503 enterbirdseye ()
4504 | _ -> ()
4507 let mode = state.mode in
4508 src#string "columns"
4509 (fun () ->
4510 match conf.columns with
4511 | Csingle _ -> "1"
4512 | Cmulti (multi, _) -> multicolumns_to_string multi
4513 | Csplit (count, _) -> "-" ^ string_of_int count
4515 (fun v ->
4516 let n, a, b = multicolumns_of_string v in
4517 setcolumns mode n a b);
4519 sep ();
4520 src#caption "Presentation mode" 0;
4521 src#bool "scrollbar visible"
4522 (fun () -> conf.scrollbarinpm)
4523 (fun v ->
4524 if v != conf.scrollbarinpm
4525 then (
4526 conf.scrollbarinpm <- v;
4527 if conf.presentation
4528 then (
4529 state.scrollw <- if v then conf.scrollbw else 0;
4530 reshape state.winw state.winh;
4535 sep ();
4536 src#caption "Pixmap cache" 0;
4537 src#int_with_suffix "size (advisory)"
4538 (fun () -> conf.memlimit)
4539 (fun v -> conf.memlimit <- v);
4541 src#caption2 "used"
4542 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4543 (string_with_suffix_of_int state.memused)
4544 (Hashtbl.length state.tilemap)) 1;
4546 sep ();
4547 src#caption "Layout" 0;
4548 src#caption2 "Dimension"
4549 (fun () ->
4550 Printf.sprintf "%dx%d (virtual %dx%d)"
4551 state.winw state.winh
4552 state.w state.maxy)
4554 if conf.debug
4555 then
4556 src#caption2 "Position" (fun () ->
4557 Printf.sprintf "%dx%d" state.x state.y
4559 else
4560 src#caption2 "Position" (fun () -> describe_location ()) 1
4563 sep ();
4564 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4565 "Save these parameters as global defaults at exit"
4566 (fun () -> conf.bedefault)
4567 (fun v -> conf.bedefault <- v)
4570 sep ();
4571 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4572 src#bool ~offset:0 ~btos "Extended parameters"
4573 (fun () -> !showextended)
4574 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4575 if !showextended
4576 then (
4577 src#bool "checkers"
4578 (fun () -> conf.checkers)
4579 (fun v -> conf.checkers <- v; setcheckers v);
4580 src#bool "update cursor"
4581 (fun () -> conf.updatecurs)
4582 (fun v -> conf.updatecurs <- v);
4583 src#bool "verbose"
4584 (fun () -> conf.verbose)
4585 (fun v -> conf.verbose <- v);
4586 src#bool "invert colors"
4587 (fun () -> conf.invert)
4588 (fun v -> conf.invert <- v);
4589 src#bool "max fit"
4590 (fun () -> conf.maxhfit)
4591 (fun v -> conf.maxhfit <- v);
4592 src#bool "redirect stderr"
4593 (fun () -> conf.redirectstderr)
4594 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4595 src#string "uri launcher"
4596 (fun () -> conf.urilauncher)
4597 (fun v -> conf.urilauncher <- v);
4598 src#string "path launcher"
4599 (fun () -> conf.pathlauncher)
4600 (fun v -> conf.pathlauncher <- v);
4601 src#string "tile size"
4602 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4603 (fun v ->
4605 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4606 conf.tilew <- max 64 w;
4607 conf.tileh <- max 64 h;
4608 flushtiles ();
4609 with exn ->
4610 state.text <- Printf.sprintf "bad tile size `%s': %s"
4611 v (exntos exn)
4613 src#int "texture count"
4614 (fun () -> conf.texcount)
4615 (fun v ->
4616 if realloctexts v
4617 then conf.texcount <- v
4618 else showtext '!' " Failed to set texture count please retry later"
4620 src#int "slice height"
4621 (fun () -> conf.sliceheight)
4622 (fun v ->
4623 conf.sliceheight <- v;
4624 wcmd "sliceh %d" conf.sliceheight;
4626 src#int "anti-aliasing level"
4627 (fun () -> conf.aalevel)
4628 (fun v ->
4629 conf.aalevel <- bound v 0 8;
4630 state.anchor <- getanchor ();
4631 opendoc state.path state.password;
4633 src#string "page scroll scaling factor"
4634 (fun () -> string_of_float conf.pgscale)
4635 (fun v ->
4637 let s = float_of_string v in
4638 conf.pgscale <- s
4639 with exn ->
4640 state.text <- Printf.sprintf
4641 "bad page scroll scaling factor `%s': %s" v (exntos exn)
4644 src#int "ui font size"
4645 (fun () -> fstate.fontsize)
4646 (fun v -> setfontsize (bound v 5 100));
4647 src#int "hint font size"
4648 (fun () -> conf.hfsize)
4649 (fun v -> conf.hfsize <- bound v 5 100);
4650 colorp "background color"
4651 (fun () -> conf.bgcolor)
4652 (fun v -> conf.bgcolor <- v);
4653 src#bool "crop hack"
4654 (fun () -> conf.crophack)
4655 (fun v -> conf.crophack <- v);
4656 src#string "trim fuzz"
4657 (fun () -> irect_to_string conf.trimfuzz)
4658 (fun v ->
4660 conf.trimfuzz <- irect_of_string v;
4661 if conf.trimmargins
4662 then settrim true conf.trimfuzz;
4663 with exn ->
4664 state.text <- Printf.sprintf "bad irect `%s': %s" v (exntos exn)
4666 src#string "throttle"
4667 (fun () ->
4668 match conf.maxwait with
4669 | None -> "show place holder if page is not ready"
4670 | Some time ->
4671 if time = infinity
4672 then "wait for page to fully render"
4673 else
4674 "wait " ^ string_of_float time
4675 ^ " seconds before showing placeholder"
4677 (fun v ->
4679 let f = float_of_string v in
4680 if f <= 0.0
4681 then conf.maxwait <- None
4682 else conf.maxwait <- Some f
4683 with exn ->
4684 state.text <- Printf.sprintf "bad time `%s': %s" v (exntos exn)
4686 src#string "ghyll scroll"
4687 (fun () ->
4688 match conf.ghyllscroll with
4689 | None -> ""
4690 | Some nab -> ghyllscroll_to_string nab
4692 (fun v ->
4694 let gs =
4695 if String.length v = 0
4696 then None
4697 else Some (ghyllscroll_of_string v)
4699 conf.ghyllscroll <- gs
4700 with exn ->
4701 state.text <- Printf.sprintf "bad ghyll `%s': %s" v (exntos exn)
4703 src#string "selection command"
4704 (fun () -> conf.selcmd)
4705 (fun v -> conf.selcmd <- v);
4706 src#string "synctex command"
4707 (fun () -> conf.stcmd)
4708 (fun v -> conf.stcmd <- v);
4709 src#colorspace "color space"
4710 (fun () -> colorspace_to_string conf.colorspace)
4711 (fun v ->
4712 conf.colorspace <- colorspace_of_int v;
4713 wcmd "cs %d" v;
4714 load state.layout;
4716 if pbousable ()
4717 then
4718 src#bool "use PBO"
4719 (fun () -> conf.usepbo)
4720 (fun v -> conf.usepbo <- v);
4721 src#bool "mouse wheel scrolls pages"
4722 (fun () -> conf.wheelbypage)
4723 (fun v -> conf.wheelbypage <- v);
4726 sep ();
4727 src#caption "Document" 0;
4728 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4729 src#caption2 "Pages"
4730 (fun () -> string_of_int state.pagecount) 1;
4731 src#caption2 "Dimensions"
4732 (fun () -> string_of_int (List.length state.pdims)) 1;
4733 if conf.trimmargins
4734 then (
4735 sep ();
4736 src#caption "Trimmed margins" 0;
4737 src#caption2 "Dimensions"
4738 (fun () -> string_of_int (List.length state.pdims)) 1;
4741 sep ();
4742 src#caption "OpenGL" 0;
4743 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
4744 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
4746 sep ();
4747 src#caption "Location" 0;
4748 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
4749 if String.length state.origin > 0
4750 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
4752 src#reset prevmode prevuioh;
4754 fun () ->
4755 state.text <- "";
4756 let prevmode = state.mode
4757 and prevuioh = state.uioh in
4758 fillsrc prevmode prevuioh;
4759 let source = (src :> lvsource) in
4760 let modehash = findkeyhash conf "info" in
4761 state.uioh <- coe (object (self)
4762 inherit listview ~source ~trusted:true ~modehash as super
4763 val mutable m_prevmemused = 0
4764 method infochanged = function
4765 | Memused ->
4766 if m_prevmemused != state.memused
4767 then (
4768 m_prevmemused <- state.memused;
4769 G.postRedisplay "memusedchanged";
4771 | Pdim -> G.postRedisplay "pdimchanged"
4772 | Docinfo -> fillsrc prevmode prevuioh
4774 method key key mask =
4775 if not (Wsi.withctrl mask)
4776 then
4777 match key with
4778 | 0xff51 | 0xff96 -> coe (self#updownlevel ~-1) (* (kp) left *)
4779 | 0xff53 | 0xff98 -> coe (self#updownlevel 1) (* (kp) right *)
4780 | _ -> super#key key mask
4781 else super#key key mask
4782 end);
4783 G.postRedisplay "info";
4786 let enterhelpmode =
4787 let source =
4788 (object
4789 inherit lvsourcebase
4790 method getitemcount = Array.length state.help
4791 method getitem n =
4792 let s, l, _ = state.help.(n) in
4793 (s, l)
4795 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4796 let optuioh =
4797 if not cancel
4798 then (
4799 m_qsearch <- qsearch;
4800 match state.help.(active) with
4801 | _, _, Action f -> Some (f uioh)
4802 | _ -> Some (uioh)
4804 else None
4806 m_active <- active;
4807 m_first <- first;
4808 m_pan <- pan;
4809 optuioh
4811 method hasaction n =
4812 match state.help.(n) with
4813 | _, _, Action _ -> true
4814 | _ -> false
4816 initializer
4817 m_active <- -1
4818 end)
4819 in fun () ->
4820 let modehash = findkeyhash conf "help" in
4821 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4822 G.postRedisplay "help";
4825 let entermsgsmode =
4826 let msgsource =
4827 let re = Str.regexp "[\r\n]" in
4828 (object
4829 inherit lvsourcebase
4830 val mutable m_items = [||]
4832 method getitemcount = 1 + Array.length m_items
4834 method getitem n =
4835 if n = 0
4836 then "[Clear]", 0
4837 else m_items.(n-1), 0
4839 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4840 ignore uioh;
4841 if not cancel
4842 then (
4843 if active = 0
4844 then Buffer.clear state.errmsgs;
4845 m_qsearch <- qsearch;
4847 m_active <- active;
4848 m_first <- first;
4849 m_pan <- pan;
4850 None
4852 method hasaction n =
4853 n = 0
4855 method reset =
4856 state.newerrmsgs <- false;
4857 let l = Str.split re (Buffer.contents state.errmsgs) in
4858 m_items <- Array.of_list l
4860 initializer
4861 m_active <- 0
4862 end)
4863 in fun () ->
4864 state.text <- "";
4865 msgsource#reset;
4866 let source = (msgsource :> lvsource) in
4867 let modehash = findkeyhash conf "listview" in
4868 state.uioh <- coe (object
4869 inherit listview ~source ~trusted:false ~modehash as super
4870 method display =
4871 if state.newerrmsgs
4872 then msgsource#reset;
4873 super#display
4874 end);
4875 G.postRedisplay "msgs";
4878 let quickbookmark ?title () =
4879 match state.layout with
4880 | [] -> ()
4881 | l :: _ ->
4882 let title =
4883 match title with
4884 | None ->
4885 let sec = Unix.gettimeofday () in
4886 let tm = Unix.localtime sec in
4887 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4888 (l.pageno+1)
4889 tm.Unix.tm_mday
4890 tm.Unix.tm_mon
4891 (tm.Unix.tm_year + 1900)
4892 tm.Unix.tm_hour
4893 tm.Unix.tm_min
4894 | Some title -> title
4896 state.bookmarks <- (title, 0, getanchor1 l) :: state.bookmarks
4899 let setautoscrollspeed step goingdown =
4900 let incr = max 1 ((abs step) / 2) in
4901 let incr = if goingdown then incr else -incr in
4902 let astep = step + incr in
4903 state.autoscroll <- Some astep;
4906 let gotounder = function
4907 | Ulinkgoto (pageno, top) ->
4908 if pageno >= 0
4909 then (
4910 addnav ();
4911 gotopage1 pageno top;
4914 | Ulinkuri s ->
4915 gotouri s
4917 | Uremote (filename, pageno) ->
4918 let path =
4919 if Sys.file_exists filename
4920 then filename
4921 else
4922 let dir = Filename.dirname state.path in
4923 let path = Filename.concat dir filename in
4924 if Sys.file_exists path
4925 then path
4926 else ""
4928 if String.length path > 0
4929 then (
4930 let anchor = getanchor () in
4931 let ranchor = state.path, state.password, anchor, state.origin in
4932 state.origin <- "";
4933 state.anchor <- (pageno, 0.0, 0.0);
4934 state.ranchors <- ranchor :: state.ranchors;
4935 opendoc path "";
4937 else showtext '!' ("Could not find " ^ filename)
4939 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4942 let canpan () =
4943 match conf.columns with
4944 | Csplit _ -> true
4945 | _ -> state.x != 0 || conf.zoom > 1.0
4948 let panbound x = bound x (-state.w) (state.winw - state.scrollw);;
4950 let existsinrow pageno (columns, coverA, coverB) p =
4951 let last = ((pageno - coverA) mod columns) + columns in
4952 let rec any = function
4953 | [] -> false
4954 | l :: rest ->
4955 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
4956 then p l
4957 else (
4958 if not (p l)
4959 then (if l.pageno = last then false else any rest)
4960 else true
4963 any state.layout
4966 let nextpage () =
4967 match state.layout with
4968 | [] ->
4969 let pageno = page_of_y state.y in
4970 gotoghyll (getpagey (pageno+1))
4971 | l :: rest ->
4972 match conf.columns with
4973 | Csingle _ ->
4974 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4975 then
4976 let y = clamp (pgscale state.winh) in
4977 gotoghyll y
4978 else
4979 let pageno = min (l.pageno+1) (state.pagecount-1) in
4980 gotoghyll (getpagey pageno)
4981 | Cmulti ((c, _, _) as cl, _) ->
4982 if conf.presentation
4983 && (existsinrow l.pageno cl
4984 (fun l -> l.pageh > l.pagey + l.pagevh))
4985 then
4986 let y = clamp (pgscale state.winh) in
4987 gotoghyll y
4988 else
4989 let pageno = min (l.pageno+c) (state.pagecount-1) in
4990 gotoghyll (getpagey pageno)
4991 | Csplit (n, _) ->
4992 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
4993 then
4994 let pagey, pageh = getpageyh l.pageno in
4995 let pagey = pagey + pageh * l.pagecol in
4996 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
4997 gotoghyll (pagey + pageh + ips)
5000 let prevpage () =
5001 match state.layout with
5002 | [] ->
5003 let pageno = page_of_y state.y in
5004 gotoghyll (getpagey (pageno-1))
5005 | l :: _ ->
5006 match conf.columns with
5007 | Csingle _ ->
5008 if conf.presentation && l.pagey != 0
5009 then
5010 gotoghyll (clamp (pgscale ~-(state.winh)))
5011 else
5012 let pageno = max 0 (l.pageno-1) in
5013 gotoghyll (getpagey pageno)
5014 | Cmulti ((c, _, coverB) as cl, _) ->
5015 if conf.presentation &&
5016 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
5017 then
5018 gotoghyll (clamp (pgscale ~-(state.winh)))
5019 else
5020 let decr =
5021 if l.pageno = state.pagecount - coverB
5022 then 1
5023 else c
5025 let pageno = max 0 (l.pageno-decr) in
5026 gotoghyll (getpagey pageno)
5027 | Csplit (n, _) ->
5028 let y =
5029 if l.pagecol = 0
5030 then
5031 if l.pageno = 0
5032 then l.pagey
5033 else
5034 let pageno = max 0 (l.pageno-1) in
5035 let pagey, pageh = getpageyh pageno in
5036 pagey + (n-1)*pageh
5037 else
5038 let pagey, pageh = getpageyh l.pageno in
5039 pagey + pageh * (l.pagecol-1) - conf.interpagespace
5041 gotoghyll y
5044 let viewkeyboard key mask =
5045 let enttext te =
5046 let mode = state.mode in
5047 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
5048 state.text <- "";
5049 enttext ();
5050 G.postRedisplay "view:enttext"
5052 let ctrl = Wsi.withctrl mask in
5053 let key =
5054 if key >= 0xffb0 && key < 0xffb9 then key - 0xffb0 + 48 else key
5056 match key with
5057 | 81 -> (* Q *)
5058 exit 0
5060 | 0xff63 -> (* insert *)
5061 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
5062 then (
5063 state.mode <- LinkNav (Ltgendir 0);
5064 gotoy state.y;
5066 else showtext '!' "Keyboard link navigation does not work under rotation"
5068 | 0xff1b | 113 -> (* escape / q *)
5069 begin match state.mstate with
5070 | Mzoomrect _ ->
5071 state.mstate <- Mnone;
5072 Wsi.setcursor Wsi.CURSOR_INHERIT;
5073 G.postRedisplay "kill zoom rect";
5074 | _ ->
5075 begin match state.mode with
5076 | LinkNav _ ->
5077 state.mode <- View;
5078 G.postRedisplay "esc leave linknav"
5079 | _ ->
5080 match state.ranchors with
5081 | [] -> raise Quit
5082 | (path, password, anchor, origin) :: rest ->
5083 state.ranchors <- rest;
5084 state.anchor <- anchor;
5085 state.origin <- origin;
5086 opendoc path password
5087 end;
5088 end;
5090 | 0xff08 -> (* backspace *)
5091 gotoghyll (getnav ~-1)
5093 | 111 -> (* o *)
5094 enteroutlinemode ()
5096 | 117 -> (* u *)
5097 state.rects <- [];
5098 state.text <- "";
5099 G.postRedisplay "dehighlight";
5101 | 47 | 63 -> (* / ? *)
5102 let ondone isforw s =
5103 cbput state.hists.pat s;
5104 state.searchpattern <- s;
5105 search s isforw
5107 let s = String.create 1 in
5108 s.[0] <- Char.chr key;
5109 enttext (s, "", Some (onhist state.hists.pat),
5110 textentry, ondone (key = 47), true)
5112 | 43 | 0xffab | 61 when ctrl -> (* ctrl-+ or ctrl-= *)
5113 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
5114 setzoom (conf.zoom +. incr)
5116 | 43 | 0xffab -> (* + *)
5117 let ondone s =
5118 let n =
5119 try int_of_string s with exc ->
5120 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc);
5121 max_int
5123 if n != max_int
5124 then (
5125 conf.pagebias <- n;
5126 state.text <- "page bias is now " ^ string_of_int n;
5129 enttext ("page bias: ", "", None, intentry, ondone, true)
5131 | 45 | 0xffad when ctrl -> (* ctrl-- *)
5132 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
5133 setzoom (max 0.01 (conf.zoom -. decr))
5135 | 45 | 0xffad -> (* - *)
5136 let ondone msg = state.text <- msg in
5137 enttext (
5138 "option [acfhilpstvxACFPRSZTIS]: ", "", None,
5139 optentry state.mode, ondone, true
5142 | 48 when ctrl -> (* ctrl-0 *)
5143 if conf.zoom = 1.0
5144 then (
5145 state.x <- 0;
5146 state.hscrollh <-
5147 if state.w <= state.winw - state.scrollw
5148 then 0
5149 else state.scrollw
5151 gotoy state.y
5153 else setzoom 1.0
5155 | (49 | 50) when ctrl && conf.fitmodel != FitPage -> (* ctrl-1/2 *)
5156 let cols =
5157 match conf.columns with
5158 | Csingle _ | Cmulti _ -> 1
5159 | Csplit (n, _) -> n
5161 let h = state.winh -
5162 conf.interpagespace lsl (if conf.presentation then 1 else 0)
5164 let zoom = zoomforh state.winw h state.scrollw cols in
5165 if zoom > 0.0 && (key = 50 || zoom < 1.0)
5166 then setzoom zoom
5168 | 51 when ctrl -> (* ctrl-3 *)
5169 let fm =
5170 match conf.fitmodel with
5171 | FitWidth -> FitProportional
5172 | FitProportional -> FitPage
5173 | FitPage -> FitWidth
5175 state.text <- "fit model: " ^ fitmodel_to_string fm;
5176 reqlayout conf.angle fm
5178 | 0xffc6 -> (* f9 *)
5179 togglebirdseye ()
5181 | 57 when ctrl -> (* ctrl-9 *)
5182 togglebirdseye ()
5184 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
5185 when not ctrl -> (* 0..9 *)
5186 let ondone s =
5187 let n =
5188 try int_of_string s with exc ->
5189 state.text <- Printf.sprintf "bad integer `%s': %s" s (exntos exc);
5192 if n >= 0
5193 then (
5194 addnav ();
5195 cbput state.hists.pag (string_of_int n);
5196 gotopage1 (n + conf.pagebias - 1) 0;
5199 let pageentry text key =
5200 match Char.unsafe_chr key with
5201 | 'g' -> TEdone text
5202 | _ -> intentry text key
5204 let text = "x" in text.[0] <- Char.chr key;
5205 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone, true)
5207 | 98 -> (* b *)
5208 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
5209 reshape state.winw state.winh;
5211 | 108 -> (* l *)
5212 conf.hlinks <- not conf.hlinks;
5213 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
5214 G.postRedisplay "toggle highlightlinks";
5216 | 70 -> (* F *)
5217 state.glinks <- true;
5218 let mode = state.mode in
5219 state.mode <- Textentry (
5220 (":", "", None, linknentry, linkndone gotounder, false),
5221 (fun _ ->
5222 state.glinks <- false;
5223 state.mode <- mode)
5225 state.text <- "";
5226 G.postRedisplay "view:linkent(F)"
5228 | 121 -> (* y *)
5229 state.glinks <- true;
5230 let mode = state.mode in
5231 state.mode <- Textentry (
5233 ":", "", None, linknentry, linkndone (fun under ->
5234 selstring (undertext under);
5235 ), false
5237 fun _ ->
5238 state.glinks <- false;
5239 state.mode <- mode
5241 state.text <- "";
5242 G.postRedisplay "view:linkent"
5244 | 97 -> (* a *)
5245 begin match state.autoscroll with
5246 | Some step ->
5247 conf.autoscrollstep <- step;
5248 state.autoscroll <- None
5249 | None ->
5250 if conf.autoscrollstep = 0
5251 then state.autoscroll <- Some 1
5252 else state.autoscroll <- Some conf.autoscrollstep
5255 | 112 when ctrl -> (* ctrl-p *)
5256 launchpath ()
5258 | 80 -> (* P *)
5259 setpresentationmode (not conf.presentation);
5260 showtext ' ' ("presentation mode " ^
5261 if conf.presentation then "on" else "off");
5263 | 102 -> (* f *)
5264 if List.mem Wsi.Fullscreen state.winstate
5265 then Wsi.reshape conf.cwinw conf.cwinh
5266 else Wsi.fullscreen ()
5268 | 112 | 78 -> (* p|N *)
5269 search state.searchpattern false
5271 | 110 | 0xffc0 -> (* n|F3 *)
5272 search state.searchpattern true
5274 | 116 -> (* t *)
5275 begin match state.layout with
5276 | [] -> ()
5277 | l :: _ ->
5278 gotoghyll (getpagey l.pageno)
5281 | 32 -> (* space *)
5282 nextpage ()
5284 | 0xff9f | 0xffff -> (* delete *)
5285 prevpage ()
5287 | 61 -> (* = *)
5288 showtext ' ' (describe_location ());
5290 | 119 -> (* w *)
5291 begin match state.layout with
5292 | [] -> ()
5293 | l :: _ ->
5294 Wsi.reshape (l.pagew + state.scrollw) l.pageh;
5295 G.postRedisplay "w"
5298 | 39 -> (* ' *)
5299 enterbookmarkmode ()
5301 | 104 | 0xffbe -> (* h|F1 *)
5302 enterhelpmode ()
5304 | 105 -> (* i *)
5305 enterinfomode ()
5307 | 101 when Buffer.length state.errmsgs > 0 -> (* e *)
5308 entermsgsmode ()
5310 | 109 -> (* m *)
5311 let ondone s =
5312 match state.layout with
5313 | l :: _ ->
5314 if String.length s > 0
5315 then
5316 state.bookmarks <- (s, 0, getanchor1 l) :: state.bookmarks
5317 | _ -> ()
5319 enttext ("bookmark: ", "", None, textentry, ondone, true)
5321 | 126 -> (* ~ *)
5322 quickbookmark ();
5323 showtext ' ' "Quick bookmark added";
5325 | 122 -> (* z *)
5326 begin match state.layout with
5327 | l :: _ ->
5328 let rect = getpdimrect l.pagedimno in
5329 let w, h =
5330 if conf.crophack
5331 then
5332 (truncate (1.8 *. (rect.(1) -. rect.(0))),
5333 truncate (1.2 *. (rect.(3) -. rect.(0))))
5334 else
5335 (truncate (rect.(1) -. rect.(0)),
5336 truncate (rect.(3) -. rect.(0)))
5338 let w = truncate ((float w)*.conf.zoom)
5339 and h = truncate ((float h)*.conf.zoom) in
5340 if w != 0 && h != 0
5341 then (
5342 state.anchor <- getanchor ();
5343 Wsi.reshape (w + state.scrollw) (h + conf.interpagespace)
5345 G.postRedisplay "z";
5347 | [] -> ()
5350 | 60 | 62 -> (* < > *)
5351 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.fitmodel
5353 | 91 | 93 -> (* [ ] *)
5354 conf.colorscale <-
5355 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
5357 G.postRedisplay "brightness";
5359 | 99 when state.mode = View -> (* c *)
5360 let (c, a, b), z =
5361 match state.prevcolumns with
5362 | None -> (1, 0, 0), 1.0
5363 | Some (columns, z) ->
5364 let cab =
5365 match columns with
5366 | Csplit (c, _) -> -c, 0, 0
5367 | Cmulti ((c, a, b), _) -> c, a, b
5368 | Csingle _ -> 1, 0, 0
5370 cab, z
5372 setcolumns View c a b;
5373 setzoom z;
5375 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
5376 setzoom state.prevzoom
5378 | 107 | 0xff52 | 0xff97 -> (* k (kp) up *)
5379 begin match state.autoscroll with
5380 | None ->
5381 begin match state.mode with
5382 | Birdseye beye -> upbirdseye 1 beye
5383 | _ ->
5384 if ctrl
5385 then gotoy_and_clear_text (clamp ~-(state.winh/2))
5386 else (
5387 if not (Wsi.withshift mask) && conf.presentation
5388 then prevpage ()
5389 else gotoy_and_clear_text (clamp (-conf.scrollstep))
5392 | Some n ->
5393 setautoscrollspeed n false
5396 | 106 | 0xff54 | 0xff99 -> (* j (kp) down *)
5397 begin match state.autoscroll with
5398 | None ->
5399 begin match state.mode with
5400 | Birdseye beye -> downbirdseye 1 beye
5401 | _ ->
5402 if ctrl
5403 then gotoy_and_clear_text (clamp (state.winh/2))
5404 else (
5405 if not (Wsi.withshift mask) && conf.presentation
5406 then nextpage ()
5407 else gotoy_and_clear_text (clamp conf.scrollstep)
5410 | Some n ->
5411 setautoscrollspeed n true
5414 | 0xff51 | 0xff53 | 0xff96 | 0xff98
5415 when not (Wsi.withalt mask) -> (* (kp) left / right *)
5416 if canpan ()
5417 then
5418 let dx =
5419 if ctrl
5420 then state.winw / 2
5421 else conf.hscrollstep
5423 let dx = if key = 0xff51 or key = 0xff96 then dx else -dx in
5424 state.x <- panbound (state.x + dx);
5425 gotoy_and_clear_text state.y
5426 else (
5427 state.text <- "";
5428 G.postRedisplay "left/right"
5431 | 0xff55 | 0xff9a -> (* (kp) prior *)
5432 let y =
5433 if ctrl
5434 then
5435 match state.layout with
5436 | [] -> state.y
5437 | l :: _ -> state.y - l.pagey
5438 else
5439 clamp (pgscale (-state.winh))
5441 gotoghyll y
5443 | 0xff56 | 0xff9b -> (* (kp) next *)
5444 let y =
5445 if ctrl
5446 then
5447 match List.rev state.layout with
5448 | [] -> state.y
5449 | l :: _ -> getpagey l.pageno
5450 else
5451 clamp (pgscale state.winh)
5453 gotoghyll y
5455 | 103 | 0xff50 | 0xff95 -> (* g (kp) home *)
5456 gotoghyll 0
5457 | 71 | 0xff57 | 0xff9c -> (* G (kp) end *)
5458 gotoghyll (clamp state.maxy)
5460 | 0xff53 | 0xff98
5461 when Wsi.withalt mask -> (* alt-(kp) right *)
5462 gotoghyll (getnav 1)
5463 | 0xff51 | 0xff96
5464 when Wsi.withalt mask -> (* alt-(kp) left *)
5465 gotoghyll (getnav ~-1)
5467 | 114 -> (* r *)
5468 reload ()
5470 | 118 when conf.debug -> (* v *)
5471 state.rects <- [];
5472 List.iter (fun l ->
5473 match getopaque l.pageno with
5474 | None -> ()
5475 | Some opaque ->
5476 let x0, y0, x1, y1 = pagebbox opaque in
5477 let a,b = float x0, float y0 in
5478 let c,d = float x1, float y0 in
5479 let e,f = float x1, float y1 in
5480 let h,j = float x0, float y1 in
5481 let rect = (a,b,c,d,e,f,h,j) in
5482 debugrect rect;
5483 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
5484 ) state.layout;
5485 G.postRedisplay "v";
5487 | _ ->
5488 vlog "huh? %s" (Wsi.keyname key)
5491 let linknavkeyboard key mask linknav =
5492 let getpage pageno =
5493 let rec loop = function
5494 | [] -> None
5495 | l :: _ when l.pageno = pageno -> Some l
5496 | _ :: rest -> loop rest
5497 in loop state.layout
5499 let doexact (pageno, n) =
5500 match getopaque pageno, getpage pageno with
5501 | Some opaque, Some l ->
5502 if key = 0xff0d || key = 0xff8d (* (kp)enter *)
5503 then
5504 let under = getlink opaque n in
5505 G.postRedisplay "link gotounder";
5506 gotounder under;
5507 state.mode <- View;
5508 else
5509 let opt, dir =
5510 match key with
5511 | 0xff50 -> (* home *)
5512 Some (findlink opaque LDfirst), -1
5514 | 0xff57 -> (* end *)
5515 Some (findlink opaque LDlast), 1
5517 | 0xff51 -> (* left *)
5518 Some (findlink opaque (LDleft n)), -1
5520 | 0xff53 -> (* right *)
5521 Some (findlink opaque (LDright n)), 1
5523 | 0xff52 -> (* up *)
5524 Some (findlink opaque (LDup n)), -1
5526 | 0xff54 -> (* down *)
5527 Some (findlink opaque (LDdown n)), 1
5529 | _ -> None, 0
5531 let pwl l dir =
5532 begin match findpwl l.pageno dir with
5533 | Pwlnotfound -> ()
5534 | Pwl pageno ->
5535 let notfound dir =
5536 state.mode <- LinkNav (Ltgendir dir);
5537 let y, h = getpageyh pageno in
5538 let y =
5539 if dir < 0
5540 then y + h - state.winh
5541 else y
5543 gotoy y
5545 begin match getopaque pageno, getpage pageno with
5546 | Some opaque, Some _ ->
5547 let link =
5548 let ld = if dir > 0 then LDfirst else LDlast in
5549 findlink opaque ld
5551 begin match link with
5552 | Lfound m ->
5553 showlinktype (getlink opaque m);
5554 state.mode <- LinkNav (Ltexact (pageno, m));
5555 G.postRedisplay "linknav jpage";
5556 | _ -> notfound dir
5557 end;
5558 | _ -> notfound dir
5559 end;
5560 end;
5562 begin match opt with
5563 | Some Lnotfound -> pwl l dir;
5564 | Some (Lfound m) ->
5565 if m = n
5566 then pwl l dir
5567 else (
5568 let _, y0, _, y1 = getlinkrect opaque m in
5569 if y0 < l.pagey
5570 then gotopage1 l.pageno y0
5571 else (
5572 let d = fstate.fontsize + 1 in
5573 if y1 - l.pagey > l.pagevh - d
5574 then gotopage1 l.pageno (y1 - state.winh - state.hscrollh + d)
5575 else G.postRedisplay "linknav";
5577 showlinktype (getlink opaque m);
5578 state.mode <- LinkNav (Ltexact (l.pageno, m));
5581 | None -> viewkeyboard key mask
5582 end;
5583 | _ -> viewkeyboard key mask
5585 if key = 0xff63
5586 then (
5587 state.mode <- View;
5588 G.postRedisplay "leave linknav"
5590 else
5591 match linknav with
5592 | Ltgendir _ -> viewkeyboard key mask
5593 | Ltexact exact -> doexact exact
5596 let keyboard key mask =
5597 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5598 then wcmd "interrupt"
5599 else state.uioh <- state.uioh#key key mask
5602 let birdseyekeyboard key mask
5603 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5604 let incr =
5605 match conf.columns with
5606 | Csingle _ -> 1
5607 | Cmulti ((c, _, _), _) -> c
5608 | Csplit _ -> failwith "bird's eye split mode"
5610 let pgh layout = List.fold_left (fun m l -> max l.pageh m) state.winh layout in
5611 match key with
5612 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5613 let y, h = getpageyh pageno in
5614 let top = (state.winh - h) / 2 in
5615 gotoy (max 0 (y - top))
5616 | 0xff0d (* enter *)
5617 | 0xff8d -> leavebirdseye beye false (* kp enter *)
5618 | 0xff1b -> leavebirdseye beye true (* escape *)
5619 | 0xff52 -> upbirdseye incr beye (* up *)
5620 | 0xff54 -> downbirdseye incr beye (* down *)
5621 | 0xff51 -> upbirdseye 1 beye (* left *)
5622 | 0xff53 -> downbirdseye 1 beye (* right *)
5624 | 0xff55 -> (* prior *)
5625 begin match state.layout with
5626 | l :: _ ->
5627 if l.pagey != 0
5628 then (
5629 state.mode <- Birdseye (
5630 oconf, leftx, l.pageno, hooverpageno, anchor
5632 gotopage1 l.pageno 0;
5634 else (
5635 let layout = layout (state.y-state.winh) (pgh state.layout) in
5636 match layout with
5637 | [] -> gotoy (clamp (-state.winh))
5638 | l :: _ ->
5639 state.mode <- Birdseye (
5640 oconf, leftx, l.pageno, hooverpageno, anchor
5642 gotopage1 l.pageno 0
5645 | [] -> gotoy (clamp (-state.winh))
5646 end;
5648 | 0xff56 -> (* next *)
5649 begin match List.rev state.layout with
5650 | l :: _ ->
5651 let layout = layout (state.y + (pgh state.layout)) state.winh in
5652 begin match layout with
5653 | [] ->
5654 let incr = l.pageh - l.pagevh in
5655 if incr = 0
5656 then (
5657 state.mode <-
5658 Birdseye (
5659 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5661 G.postRedisplay "birdseye pagedown";
5663 else gotoy (clamp (incr + conf.interpagespace*2));
5665 | l :: _ ->
5666 state.mode <-
5667 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5668 gotopage1 l.pageno 0;
5671 | [] -> gotoy (clamp state.winh)
5672 end;
5674 | 0xff50 -> (* home *)
5675 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5676 gotopage1 0 0
5678 | 0xff57 -> (* end *)
5679 let pageno = state.pagecount - 1 in
5680 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5681 if not (pagevisible state.layout pageno)
5682 then
5683 let h =
5684 match List.rev state.pdims with
5685 | [] -> state.winh
5686 | (_, _, h, _) :: _ -> h
5688 gotoy (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
5689 else G.postRedisplay "birdseye end";
5690 | _ -> viewkeyboard key mask
5693 let drawpage l =
5694 let color =
5695 match state.mode with
5696 | Textentry _ -> scalecolor 0.4
5697 | LinkNav _
5698 | View -> scalecolor 1.0
5699 | Birdseye (_, _, pageno, hooverpageno, _) ->
5700 if l.pageno = hooverpageno
5701 then scalecolor 0.9
5702 else (
5703 if l.pageno = pageno
5704 then scalecolor 1.0
5705 else scalecolor 0.8
5708 drawtiles l color;
5711 let postdrawpage l linkindexbase =
5712 match getopaque l.pageno with
5713 | Some opaque ->
5714 if tileready l l.pagex l.pagey
5715 then
5716 let x = l.pagedispx - l.pagex
5717 and y = l.pagedispy - l.pagey in
5718 let hlmask =
5719 match conf.columns with
5720 | Csingle _ | Cmulti _ ->
5721 (if conf.hlinks then 1 else 0)
5722 + (if state.glinks
5723 && not (isbirdseye state.mode) then 2 else 0)
5724 | _ -> 0
5726 let s =
5727 match state.mode with
5728 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5729 | _ -> ""
5731 postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize);
5732 else 0
5733 | _ -> 0
5736 let scrollindicator () =
5737 let sbw, ph, sh = state.uioh#scrollph in
5738 let sbh, pw, sw = state.uioh#scrollpw in
5740 GlDraw.color (0.64, 0.64, 0.64);
5741 GlDraw.rect
5742 (float (state.winw - sbw), 0.)
5743 (float state.winw, float state.winh)
5745 GlDraw.rect
5746 (0., float (state.winh - sbh))
5747 (float (state.winw - state.scrollw - 1), float state.winh)
5749 GlDraw.color (0.0, 0.0, 0.0);
5751 GlDraw.rect
5752 (float (state.winw - sbw), ph)
5753 (float state.winw, ph +. sh)
5755 GlDraw.rect
5756 (pw, float (state.winh - sbh))
5757 (pw +. sw, float state.winh)
5761 let showsel () =
5762 match state.mstate with
5763 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5766 | Msel ((x0, y0), (x1, y1)) ->
5767 let rec loop = function
5768 | l :: ls ->
5769 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5770 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5771 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5772 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5773 then
5774 match getopaque l.pageno with
5775 | Some opaque ->
5776 let x0, y0 = pagetranslatepoint l x0 y0 in
5777 let x1, y1 = pagetranslatepoint l x1 y1 in
5778 seltext opaque (x0, y0, x1, y1);
5779 | _ -> ()
5780 else loop ls
5781 | [] -> ()
5783 loop state.layout
5786 let showrects rects =
5787 Gl.enable `blend;
5788 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5789 GlDraw.polygon_mode `both `fill;
5790 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5791 List.iter
5792 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5793 List.iter (fun l ->
5794 if l.pageno = pageno
5795 then (
5796 let dx = float (l.pagedispx - l.pagex) in
5797 let dy = float (l.pagedispy - l.pagey) in
5798 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5799 GlDraw.begins `quads;
5801 GlDraw.vertex2 (x0+.dx, y0+.dy);
5802 GlDraw.vertex2 (x1+.dx, y1+.dy);
5803 GlDraw.vertex2 (x2+.dx, y2+.dy);
5804 GlDraw.vertex2 (x3+.dx, y3+.dy);
5806 GlDraw.ends ();
5808 ) state.layout
5809 ) rects
5811 Gl.disable `blend;
5814 let display () =
5815 GlClear.color (scalecolor2 conf.bgcolor);
5816 GlClear.clear [`color];
5817 List.iter drawpage state.layout;
5818 let rects =
5819 match state.mode with
5820 | LinkNav (Ltexact (pageno, linkno)) ->
5821 begin match getopaque pageno with
5822 | Some opaque ->
5823 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5824 (pageno, 5, (
5825 float x0, float y0,
5826 float x1, float y0,
5827 float x1, float y1,
5828 float x0, float y1)
5829 ) :: state.rects
5830 | None -> state.rects
5832 | _ -> state.rects
5834 showrects rects;
5835 let rec postloop linkindexbase = function
5836 | l :: rest ->
5837 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
5838 postloop linkindexbase rest
5839 | [] -> ()
5841 showsel ();
5842 postloop 0 state.layout;
5843 state.uioh#display;
5844 begin match state.mstate with
5845 | Mzoomrect ((x0, y0), (x1, y1)) ->
5846 Gl.enable `blend;
5847 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5848 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5849 GlDraw.rect (float x0, float y0)
5850 (float x1, float y1);
5851 Gl.disable `blend;
5852 | _ -> ()
5853 end;
5854 enttext ();
5855 scrollindicator ();
5856 Wsi.swapb ();
5859 let zoomrect x y x1 y1 =
5860 let x0 = min x x1
5861 and x1 = max x x1
5862 and y0 = min y y1 in
5863 gotoy (state.y + y0);
5864 state.anchor <- getanchor ();
5865 let zoom = (float state.w) /. float (x1 - x0) in
5866 let margin =
5867 if state.w < state.winw - state.scrollw
5868 then (state.winw - state.scrollw - state.w) / 2
5869 else 0
5871 state.x <- (state.x + margin) - x0;
5872 setzoom zoom;
5873 Wsi.setcursor Wsi.CURSOR_INHERIT;
5874 state.mstate <- Mnone;
5877 let scrollx x =
5878 let winw = state.winw - state.scrollw - 1 in
5879 let s = float x /. float winw in
5880 let destx = truncate (float (state.w + winw) *. s) in
5881 state.x <- winw - destx;
5882 gotoy_and_clear_text state.y;
5883 state.mstate <- Mscrollx;
5886 let scrolly y =
5887 let s = float y /. float state.winh in
5888 let desty = truncate (float (state.maxy - state.winh) *. s) in
5889 gotoy_and_clear_text desty;
5890 state.mstate <- Mscrolly;
5893 let viewmouse button down x y mask =
5894 match button with
5895 | n when (n == 4 || n == 5) && not down ->
5896 if Wsi.withctrl mask
5897 then (
5898 match state.mstate with
5899 | Mzoom (oldn, i) ->
5900 if oldn = n
5901 then (
5902 if i = 2
5903 then
5904 let incr =
5905 match n with
5906 | 5 ->
5907 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5908 | _ ->
5909 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5911 let zoom = conf.zoom -. incr in
5912 setzoom zoom;
5913 state.mstate <- Mzoom (n, 0);
5914 else
5915 state.mstate <- Mzoom (n, i+1);
5917 else state.mstate <- Mzoom (n, 0)
5919 | _ -> state.mstate <- Mzoom (n, 0)
5921 else (
5922 match state.autoscroll with
5923 | Some step -> setautoscrollspeed step (n=4)
5924 | None ->
5925 if conf.wheelbypage || conf.presentation
5926 then (
5927 if n = 4
5928 then prevpage ()
5929 else nextpage ()
5931 else
5932 let incr =
5933 if n = 4
5934 then -conf.scrollstep
5935 else conf.scrollstep
5937 let incr = incr * 2 in
5938 let y = clamp incr in
5939 gotoy_and_clear_text y
5942 | n when (n = 6 || n = 7) && not down && canpan () ->
5943 state.x <-
5944 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep);
5945 gotoy_and_clear_text state.y
5947 | 1 when Wsi.withshift mask ->
5948 state.mstate <- Mnone;
5949 if not down
5950 then (
5951 match unproject x y with
5952 | Some (pageno, ux, uy) ->
5953 let cmd = Printf.sprintf
5954 "%s %s %d %d %d"
5955 conf.stcmd state.path pageno ux uy
5957 popen cmd []
5958 | None -> ()
5961 | 1 when Wsi.withctrl mask ->
5962 if down
5963 then (
5964 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5965 state.mstate <- Mpan (x, y)
5967 else
5968 state.mstate <- Mnone
5970 | 3 ->
5971 if down
5972 then (
5973 Wsi.setcursor Wsi.CURSOR_CYCLE;
5974 let p = (x, y) in
5975 state.mstate <- Mzoomrect (p, p)
5977 else (
5978 match state.mstate with
5979 | Mzoomrect ((x0, y0), _) ->
5980 if abs (x-x0) > 10 && abs (y - y0) > 10
5981 then zoomrect x0 y0 x y
5982 else (
5983 state.mstate <- Mnone;
5984 Wsi.setcursor Wsi.CURSOR_INHERIT;
5985 G.postRedisplay "kill accidental zoom rect";
5987 | _ ->
5988 Wsi.setcursor Wsi.CURSOR_INHERIT;
5989 state.mstate <- Mnone
5992 | 1 when x > state.winw - state.scrollw ->
5993 if down
5994 then
5995 let _, position, sh = state.uioh#scrollph in
5996 if y > truncate position && y < truncate (position +. sh)
5997 then state.mstate <- Mscrolly
5998 else scrolly y
5999 else
6000 state.mstate <- Mnone
6002 | 1 when y > state.winh - state.hscrollh ->
6003 if down
6004 then
6005 let _, position, sw = state.uioh#scrollpw in
6006 if x > truncate position && x < truncate (position +. sw)
6007 then state.mstate <- Mscrollx
6008 else scrollx x
6009 else
6010 state.mstate <- Mnone
6012 | 1 ->
6013 let dest = if down then getunder x y else Unone in
6014 begin match dest with
6015 | Ulinkgoto _
6016 | Ulinkuri _
6017 | Uremote _
6018 | Uunexpected _ | Ulaunch _ | Unamed _ ->
6019 gotounder dest
6021 | Unone when down ->
6022 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
6023 state.mstate <- Mpan (x, y);
6025 | Unone | Utext _ ->
6026 if down
6027 then (
6028 if conf.angle mod 360 = 0
6029 then (
6030 state.mstate <- Msel ((x, y), (x, y));
6031 G.postRedisplay "mouse select";
6034 else (
6035 match state.mstate with
6036 | Mnone -> ()
6038 | Mzoom _ | Mscrollx | Mscrolly ->
6039 state.mstate <- Mnone
6041 | Mzoomrect ((x0, y0), _) ->
6042 zoomrect x0 y0 x y
6044 | Mpan _ ->
6045 Wsi.setcursor Wsi.CURSOR_INHERIT;
6046 state.mstate <- Mnone
6048 | Msel ((x0, y0), (x1, y1)) ->
6049 let rec loop = function
6050 | [] -> ()
6051 | l :: rest ->
6052 let inside =
6053 let a0 = l.pagedispy in
6054 let a1 = a0 + l.pagevh in
6055 let b0 = l.pagedispx in
6056 let b1 = b0 + l.pagevw in
6057 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
6058 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
6060 if inside
6061 then
6062 match getopaque l.pageno with
6063 | Some opaque ->
6064 begin
6065 match Ne.pipe () with
6066 | Ne.Exn exn ->
6067 showtext '!'
6068 (Printf.sprintf
6069 "can not create sel pipe: %s"
6070 (exntos exn));
6071 | Ne.Res (r, w) ->
6072 let doclose what fd =
6073 Ne.clo fd (fun msg ->
6074 dolog "%s close failed: %s" what msg)
6077 popen conf.selcmd [r, 0; w, -1];
6078 copysel w opaque;
6079 doclose "pipe/r" r;
6080 G.postRedisplay "copysel";
6081 with exn ->
6082 dolog "can not execute %S: %s"
6083 conf.selcmd (exntos exn);
6084 doclose "pipe/r" r;
6085 doclose "pipe/w" w;
6087 | None -> ()
6088 else loop rest
6090 loop state.layout;
6091 Wsi.setcursor Wsi.CURSOR_INHERIT;
6092 state.mstate <- Mnone;
6096 | _ -> ()
6099 let birdseyemouse button down x y mask
6100 (conf, leftx, _, hooverpageno, anchor) =
6101 match button with
6102 | 1 when down ->
6103 let rec loop = function
6104 | [] -> ()
6105 | l :: rest ->
6106 if y > l.pagedispy && y < l.pagedispy + l.pagevh
6107 && x > l.pagedispx && x < l.pagedispx + l.pagevw
6108 then (
6109 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
6111 else loop rest
6113 loop state.layout
6114 | 3 -> ()
6115 | _ -> viewmouse button down x y mask
6118 let mouse button down x y mask =
6119 state.uioh <- state.uioh#button button down x y mask;
6122 let motion ~x ~y =
6123 state.uioh <- state.uioh#motion x y
6126 let pmotion ~x ~y =
6127 state.uioh <- state.uioh#pmotion x y;
6130 let uioh = object
6131 method display = ()
6133 method key key mask =
6134 begin match state.mode with
6135 | Textentry textentry -> textentrykeyboard key mask textentry
6136 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
6137 | View -> viewkeyboard key mask
6138 | LinkNav linknav -> linknavkeyboard key mask linknav
6139 end;
6140 state.uioh
6142 method button button bstate x y mask =
6143 begin match state.mode with
6144 | LinkNav _
6145 | View -> viewmouse button bstate x y mask
6146 | Birdseye beye -> birdseyemouse button bstate x y mask beye
6147 | Textentry _ -> ()
6148 end;
6149 state.uioh
6151 method motion x y =
6152 begin match state.mode with
6153 | Textentry _ -> ()
6154 | View | Birdseye _ | LinkNav _ ->
6155 match state.mstate with
6156 | Mzoom _ | Mnone -> ()
6158 | Mpan (x0, y0) ->
6159 let dx = x - x0
6160 and dy = y0 - y in
6161 state.mstate <- Mpan (x, y);
6162 if canpan ()
6163 then state.x <- panbound (state.x + dx);
6164 let y = clamp dy in
6165 gotoy_and_clear_text y
6167 | Msel (a, _) ->
6168 state.mstate <- Msel (a, (x, y));
6169 G.postRedisplay "motion select";
6171 | Mscrolly ->
6172 let y = min state.winh (max 0 y) in
6173 scrolly y
6175 | Mscrollx ->
6176 let x = min state.winw (max 0 x) in
6177 scrollx x
6179 | Mzoomrect (p0, _) ->
6180 state.mstate <- Mzoomrect (p0, (x, y));
6181 G.postRedisplay "motion zoomrect";
6182 end;
6183 state.uioh
6185 method pmotion x y =
6186 begin match state.mode with
6187 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
6188 let rec loop = function
6189 | [] ->
6190 if hooverpageno != -1
6191 then (
6192 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
6193 G.postRedisplay "pmotion birdseye no hoover";
6195 | l :: rest ->
6196 if y > l.pagedispy && y < l.pagedispy + l.pagevh
6197 && x > l.pagedispx && x < l.pagedispx + l.pagevw
6198 then (
6199 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
6200 G.postRedisplay "pmotion birdseye hoover";
6202 else loop rest
6204 loop state.layout
6206 | Textentry _ -> ()
6208 | LinkNav _
6209 | View ->
6210 match state.mstate with
6211 | Mnone -> updateunder x y
6212 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
6214 end;
6215 state.uioh
6217 method infochanged _ = ()
6219 method scrollph =
6220 let maxy = state.maxy - (if conf.maxhfit then state.winh else 0) in
6221 let p, h =
6222 if maxy = 0
6223 then 0.0, float state.winh
6224 else scrollph state.y maxy
6226 state.scrollw, p, h
6228 method scrollpw =
6229 let winw = state.winw - state.scrollw in
6230 let fwinw = float winw in
6231 let sw =
6232 let sw = fwinw /. float state.w in
6233 let sw = fwinw *. sw in
6234 max sw (float conf.scrollh)
6236 let position =
6237 let maxx = state.w + winw in
6238 let x = winw - state.x in
6239 let percent = float x /. float maxx in
6240 (fwinw -. sw) *. percent
6242 state.hscrollh, position, sw
6244 method modehash =
6245 let modename =
6246 match state.mode with
6247 | LinkNav _ -> "links"
6248 | Textentry _ -> "textentry"
6249 | Birdseye _ -> "birdseye"
6250 | View -> "view"
6252 findkeyhash conf modename
6254 method eformsgs = true
6255 end;;
6257 module Config =
6258 struct
6259 open Parser
6261 let fontpath = ref "";;
6263 module KeyMap =
6264 Map.Make (struct type t = (int * int) let compare = compare end);;
6266 let unent s =
6267 let l = String.length s in
6268 let b = Buffer.create l in
6269 unent b s 0 l;
6270 Buffer.contents b;
6273 let home =
6274 try Sys.getenv "HOME"
6275 with exn ->
6276 prerr_endline
6277 ("Can not determine home directory location: " ^ exntos exn);
6281 let modifier_of_string = function
6282 | "alt" -> Wsi.altmask
6283 | "shift" -> Wsi.shiftmask
6284 | "ctrl" | "control" -> Wsi.ctrlmask
6285 | "meta" -> Wsi.metamask
6286 | _ -> 0
6289 let key_of_string =
6290 let r = Str.regexp "-" in
6291 fun s ->
6292 let elems = Str.full_split r s in
6293 let f n k m =
6294 let g s =
6295 let m1 = modifier_of_string s in
6296 if m1 = 0
6297 then (Wsi.namekey s, m)
6298 else (k, m lor m1)
6299 in function
6300 | Str.Delim s when n land 1 = 0 -> g s
6301 | Str.Text s -> g s
6302 | Str.Delim _ -> (k, m)
6304 let rec loop n k m = function
6305 | [] -> (k, m)
6306 | x :: xs ->
6307 let k, m = f n k m x in
6308 loop (n+1) k m xs
6310 loop 0 0 0 elems
6313 let keys_of_string =
6314 let r = Str.regexp "[ \t]" in
6315 fun s ->
6316 let elems = Str.split r s in
6317 List.map key_of_string elems
6320 let copykeyhashes c =
6321 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
6324 let config_of c attrs =
6325 let apply c k v =
6327 match k with
6328 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
6329 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
6330 | "case-insensitive-search" -> { c with icase = bool_of_string v }
6331 | "preload" -> { c with preload = bool_of_string v }
6332 | "page-bias" -> { c with pagebias = int_of_string v }
6333 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
6334 | "horizontal-scroll-step" ->
6335 { c with hscrollstep = max (int_of_string v) 1 }
6336 | "auto-scroll-step" ->
6337 { c with autoscrollstep = max 0 (int_of_string v) }
6338 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
6339 | "crop-hack" -> { c with crophack = bool_of_string v }
6340 | "throttle" ->
6341 let mw =
6342 match String.lowercase v with
6343 | "true" -> Some infinity
6344 | "false" -> None
6345 | f -> Some (float_of_string f)
6347 { c with maxwait = mw}
6348 | "highlight-links" -> { c with hlinks = bool_of_string v }
6349 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
6350 | "vertical-margin" ->
6351 { c with interpagespace = max 0 (int_of_string v) }
6352 | "zoom" ->
6353 let zoom = float_of_string v /. 100. in
6354 let zoom = max zoom 0.0 in
6355 { c with zoom = zoom }
6356 | "presentation" -> { c with presentation = bool_of_string v }
6357 | "rotation-angle" -> { c with angle = int_of_string v }
6358 | "width" -> { c with cwinw = max 20 (int_of_string v) }
6359 | "height" -> { c with cwinh = max 20 (int_of_string v) }
6360 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
6361 | "proportional-display" ->
6362 let fm =
6363 if bool_of_string v
6364 then FitProportional
6365 else FitWidth
6367 { c with fitmodel = fm }
6368 | "fit-model" -> { c with fitmodel = fitmodel_of_string v }
6369 | "pixmap-cache-size" ->
6370 { c with memlimit = max 2 (int_of_string_with_suffix v) }
6371 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
6372 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
6373 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
6374 | "persistent-location" -> { c with jumpback = bool_of_string v }
6375 | "background-color" -> { c with bgcolor = color_of_string v }
6376 | "scrollbar-in-presentation" ->
6377 { c with scrollbarinpm = bool_of_string v }
6378 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
6379 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
6380 | "mupdf-store-size" ->
6381 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
6382 | "checkers" -> { c with checkers = bool_of_string v }
6383 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
6384 | "trim-margins" -> { c with trimmargins = bool_of_string v }
6385 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
6386 | "uri-launcher" -> { c with urilauncher = unent v }
6387 | "path-launcher" -> { c with pathlauncher = unent v }
6388 | "color-space" -> { c with colorspace = colorspace_of_string v }
6389 | "invert-colors" -> { c with invert = bool_of_string v }
6390 | "brightness" -> { c with colorscale = float_of_string v }
6391 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
6392 | "ghyllscroll" ->
6393 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
6394 | "columns" ->
6395 let (n, _, _) as nab = multicolumns_of_string v in
6396 if n < 0
6397 then { c with columns = Csplit (-n, [||]) }
6398 else { c with columns = Cmulti (nab, [||]) }
6399 | "birds-eye-columns" ->
6400 { c with beyecolumns = Some (max (int_of_string v) 2) }
6401 | "selection-command" -> { c with selcmd = unent v }
6402 | "synctex-command" -> { c with stcmd = unent v }
6403 | "update-cursor" -> { c with updatecurs = bool_of_string v }
6404 | "hint-font-size" -> { c with hfsize = bound (int_of_string v) 5 100 }
6405 | "page-scroll-scale" -> { c with pgscale = float_of_string v }
6406 | "use-pbo" -> { c with usepbo = bool_of_string v }
6407 | "wheel-scrolls-pages" -> { c with wheelbypage = bool_of_string v }
6408 | _ -> c
6409 with exn ->
6410 prerr_endline ("Error processing attribute (`" ^
6411 k ^ "'=`" ^ v ^ "'): " ^ exntos exn);
6414 let rec fold c = function
6415 | [] -> c
6416 | (k, v) :: rest ->
6417 let c = apply c k v in
6418 fold c rest
6420 fold { c with keyhashes = copykeyhashes c } attrs;
6423 let fromstring f pos n v d =
6424 try f v
6425 with exn ->
6426 dolog "Error processing attribute (%S=%S) at %d\n%s"
6427 n v pos (exntos exn)
6432 let bookmark_of attrs =
6433 let rec fold title page rely visy = function
6434 | ("title", v) :: rest -> fold v page rely visy rest
6435 | ("page", v) :: rest -> fold title v rely visy rest
6436 | ("rely", v) :: rest -> fold title page v visy rest
6437 | ("visy", v) :: rest -> fold title page rely v rest
6438 | _ :: rest -> fold title page rely visy rest
6439 | [] -> title, page, rely, visy
6441 fold "invalid" "0" "0" "0" attrs
6444 let doc_of attrs =
6445 let rec fold path page rely pan visy = function
6446 | ("path", v) :: rest -> fold v page rely pan visy rest
6447 | ("page", v) :: rest -> fold path v rely pan visy rest
6448 | ("rely", v) :: rest -> fold path page v pan visy rest
6449 | ("pan", v) :: rest -> fold path page rely v visy rest
6450 | ("visy", v) :: rest -> fold path page rely pan v rest
6451 | _ :: rest -> fold path page rely pan visy rest
6452 | [] -> path, page, rely, pan, visy
6454 fold "" "0" "0" "0" "0" attrs
6457 let map_of attrs =
6458 let rec fold rs ls = function
6459 | ("out", v) :: rest -> fold v ls rest
6460 | ("in", v) :: rest -> fold rs v rest
6461 | _ :: rest -> fold ls rs rest
6462 | [] -> ls, rs
6464 fold "" "" attrs
6467 let setconf dst src =
6468 dst.scrollbw <- src.scrollbw;
6469 dst.scrollh <- src.scrollh;
6470 dst.icase <- src.icase;
6471 dst.preload <- src.preload;
6472 dst.pagebias <- src.pagebias;
6473 dst.verbose <- src.verbose;
6474 dst.scrollstep <- src.scrollstep;
6475 dst.maxhfit <- src.maxhfit;
6476 dst.crophack <- src.crophack;
6477 dst.autoscrollstep <- src.autoscrollstep;
6478 dst.maxwait <- src.maxwait;
6479 dst.hlinks <- src.hlinks;
6480 dst.underinfo <- src.underinfo;
6481 dst.interpagespace <- src.interpagespace;
6482 dst.zoom <- src.zoom;
6483 dst.presentation <- src.presentation;
6484 dst.angle <- src.angle;
6485 dst.cwinw <- src.cwinw;
6486 dst.cwinh <- src.cwinh;
6487 dst.savebmarks <- src.savebmarks;
6488 dst.memlimit <- src.memlimit;
6489 dst.fitmodel <- src.fitmodel;
6490 dst.texcount <- src.texcount;
6491 dst.sliceheight <- src.sliceheight;
6492 dst.thumbw <- src.thumbw;
6493 dst.jumpback <- src.jumpback;
6494 dst.bgcolor <- src.bgcolor;
6495 dst.scrollbarinpm <- src.scrollbarinpm;
6496 dst.tilew <- src.tilew;
6497 dst.tileh <- src.tileh;
6498 dst.mustoresize <- src.mustoresize;
6499 dst.checkers <- src.checkers;
6500 dst.aalevel <- src.aalevel;
6501 dst.trimmargins <- src.trimmargins;
6502 dst.trimfuzz <- src.trimfuzz;
6503 dst.urilauncher <- src.urilauncher;
6504 dst.colorspace <- src.colorspace;
6505 dst.invert <- src.invert;
6506 dst.colorscale <- src.colorscale;
6507 dst.redirectstderr <- src.redirectstderr;
6508 dst.ghyllscroll <- src.ghyllscroll;
6509 dst.columns <- src.columns;
6510 dst.beyecolumns <- src.beyecolumns;
6511 dst.selcmd <- src.selcmd;
6512 dst.updatecurs <- src.updatecurs;
6513 dst.pathlauncher <- src.pathlauncher;
6514 dst.keyhashes <- copykeyhashes src;
6515 dst.hfsize <- src.hfsize;
6516 dst.hscrollstep <- src.hscrollstep;
6517 dst.pgscale <- src.pgscale;
6518 dst.usepbo <- src.usepbo;
6519 dst.wheelbypage <- src.wheelbypage;
6520 dst.stcmd <- src.stcmd;
6523 let get s =
6524 let h = Hashtbl.create 10 in
6525 let dc = { defconf with angle = defconf.angle } in
6526 let rec toplevel v t spos _ =
6527 match t with
6528 | Vdata | Vcdata | Vend -> v
6529 | Vopen ("llppconfig", _, closed) ->
6530 if closed
6531 then v
6532 else { v with f = llppconfig }
6533 | Vopen _ ->
6534 error "unexpected subelement at top level" s spos
6535 | Vclose _ -> error "unexpected close at top level" s spos
6537 and llppconfig v t spos _ =
6538 match t with
6539 | Vdata | Vcdata -> v
6540 | Vend -> error "unexpected end of input in llppconfig" s spos
6541 | Vopen ("defaults", attrs, closed) ->
6542 let c = config_of dc attrs in
6543 setconf dc c;
6544 if closed
6545 then v
6546 else { v with f = defaults }
6548 | Vopen ("ui-font", attrs, closed) ->
6549 let rec getsize size = function
6550 | [] -> size
6551 | ("size", v) :: rest ->
6552 let size =
6553 fromstring int_of_string spos "size" v fstate.fontsize in
6554 getsize size rest
6555 | l -> getsize size l
6557 fstate.fontsize <- getsize fstate.fontsize attrs;
6558 if closed
6559 then v
6560 else { v with f = uifont (Buffer.create 10) }
6562 | Vopen ("doc", attrs, closed) ->
6563 let pathent, spage, srely, span, svisy = doc_of attrs in
6564 let path = unent pathent
6565 and pageno = fromstring int_of_string spos "page" spage 0
6566 and rely = fromstring float_of_string spos "rely" srely 0.0
6567 and pan = fromstring int_of_string spos "pan" span 0
6568 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6569 let c = config_of dc attrs in
6570 let anchor = (pageno, rely, visy) in
6571 if closed
6572 then (Hashtbl.add h path (c, [], pan, anchor); v)
6573 else { v with f = doc path pan anchor c [] }
6575 | Vopen _ ->
6576 error "unexpected subelement in llppconfig" s spos
6578 | Vclose "llppconfig" -> { v with f = toplevel }
6579 | Vclose _ -> error "unexpected close in llppconfig" s spos
6581 and defaults v t spos _ =
6582 match t with
6583 | Vdata | Vcdata -> v
6584 | Vend -> error "unexpected end of input in defaults" s spos
6585 | Vopen ("keymap", attrs, closed) ->
6586 let modename =
6587 try List.assoc "mode" attrs
6588 with Not_found -> "global" in
6589 if closed
6590 then v
6591 else
6592 let ret keymap =
6593 let h = findkeyhash dc modename in
6594 KeyMap.iter (Hashtbl.replace h) keymap;
6595 defaults
6597 { v with f = pkeymap ret KeyMap.empty }
6599 | Vopen (_, _, _) ->
6600 error "unexpected subelement in defaults" s spos
6602 | Vclose "defaults" ->
6603 { v with f = llppconfig }
6605 | Vclose _ -> error "unexpected close in defaults" s spos
6607 and uifont b v t spos epos =
6608 match t with
6609 | Vdata | Vcdata ->
6610 Buffer.add_substring b s spos (epos - spos);
6612 | Vopen (_, _, _) ->
6613 error "unexpected subelement in ui-font" s spos
6614 | Vclose "ui-font" ->
6615 if String.length !fontpath = 0
6616 then fontpath := Buffer.contents b;
6617 { v with f = llppconfig }
6618 | Vclose _ -> error "unexpected close in ui-font" s spos
6619 | Vend -> error "unexpected end of input in ui-font" s spos
6621 and doc path pan anchor c bookmarks v t spos _ =
6622 match t with
6623 | Vdata | Vcdata -> v
6624 | Vend -> error "unexpected end of input in doc" s spos
6625 | Vopen ("bookmarks", _, closed) ->
6626 if closed
6627 then v
6628 else { v with f = pbookmarks path pan anchor c bookmarks }
6630 | Vopen ("keymap", attrs, closed) ->
6631 let modename =
6632 try List.assoc "mode" attrs
6633 with Not_found -> "global"
6635 if closed
6636 then v
6637 else
6638 let ret keymap =
6639 let h = findkeyhash c modename in
6640 KeyMap.iter (Hashtbl.replace h) keymap;
6641 doc path pan anchor c bookmarks
6643 { v with f = pkeymap ret KeyMap.empty }
6645 | Vopen (_, _, _) ->
6646 error "unexpected subelement in doc" s spos
6648 | Vclose "doc" ->
6649 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
6650 { v with f = llppconfig }
6652 | Vclose _ -> error "unexpected close in doc" s spos
6654 and pkeymap ret keymap v t spos _ =
6655 match t with
6656 | Vdata | Vcdata -> v
6657 | Vend -> error "unexpected end of input in keymap" s spos
6658 | Vopen ("map", attrs, closed) ->
6659 let r, l = map_of attrs in
6660 let kss = fromstring keys_of_string spos "in" r [] in
6661 let lss = fromstring keys_of_string spos "out" l [] in
6662 let keymap =
6663 match kss with
6664 | [] -> keymap
6665 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
6666 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
6668 if closed
6669 then { v with f = pkeymap ret keymap }
6670 else
6671 let f () = v in
6672 { v with f = skip "map" f }
6674 | Vopen _ ->
6675 error "unexpected subelement in keymap" s spos
6677 | Vclose "keymap" ->
6678 { v with f = ret keymap }
6680 | Vclose _ -> error "unexpected close in keymap" s spos
6682 and pbookmarks path pan anchor c bookmarks v t spos _ =
6683 match t with
6684 | Vdata | Vcdata -> v
6685 | Vend -> error "unexpected end of input in bookmarks" s spos
6686 | Vopen ("item", attrs, closed) ->
6687 let titleent, spage, srely, svisy = bookmark_of attrs in
6688 let page = fromstring int_of_string spos "page" spage 0
6689 and rely = fromstring float_of_string spos "rely" srely 0.0
6690 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6691 let bookmarks =
6692 (unent titleent, 0, (page, rely, visy)) :: bookmarks
6694 if closed
6695 then { v with f = pbookmarks path pan anchor c bookmarks }
6696 else
6697 let f () = v in
6698 { v with f = skip "item" f }
6700 | Vopen _ ->
6701 error "unexpected subelement in bookmarks" s spos
6703 | Vclose "bookmarks" ->
6704 { v with f = doc path pan anchor c bookmarks }
6706 | Vclose _ -> error "unexpected close in bookmarks" s spos
6708 and skip tag f v t spos _ =
6709 match t with
6710 | Vdata | Vcdata -> v
6711 | Vend ->
6712 error ("unexpected end of input in skipped " ^ tag) s spos
6713 | Vopen (tag', _, closed) ->
6714 if closed
6715 then v
6716 else
6717 let f' () = { v with f = skip tag f } in
6718 { v with f = skip tag' f' }
6719 | Vclose ctag ->
6720 if tag = ctag
6721 then f ()
6722 else error ("unexpected close in skipped " ^ tag) s spos
6725 parse { f = toplevel; accu = () } s;
6726 h, dc;
6729 let do_load f ic =
6731 let len = in_channel_length ic in
6732 let s = String.create len in
6733 really_input ic s 0 len;
6734 f s;
6735 with
6736 | Parse_error (msg, s, pos) ->
6737 let subs = subs s pos in
6738 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6739 failwith ("parse error: " ^ s)
6741 | exn ->
6742 failwith ("config load error: " ^ exntos exn)
6745 let defconfpath =
6746 let dir =
6748 let dir = Filename.concat home ".config" in
6749 if Sys.is_directory dir then dir else home
6750 with _ -> home
6752 Filename.concat dir "llpp.conf"
6755 let confpath = ref defconfpath;;
6757 let load1 f =
6758 if Sys.file_exists !confpath
6759 then
6760 match
6761 (try Some (open_in_bin !confpath)
6762 with exn ->
6763 prerr_endline
6764 ("Error opening configuration file `" ^ !confpath ^ "': " ^
6765 exntos exn);
6766 None
6768 with
6769 | Some ic ->
6770 let success =
6772 f (do_load get ic)
6773 with exn ->
6774 prerr_endline
6775 ("Error loading configuration from `" ^ !confpath ^ "': " ^
6776 exntos exn);
6777 false
6779 close_in ic;
6780 success
6782 | None -> false
6783 else
6784 f (Hashtbl.create 0, defconf)
6787 let load () =
6788 let f (h, dc) =
6789 let pc, pb, px, pa =
6791 let key =
6792 if String.length state.origin = 0
6793 then state.path
6794 else state.origin
6796 Hashtbl.find h (Filename.basename key)
6797 with Not_found -> dc, [], 0, emptyanchor
6799 setconf defconf dc;
6800 setconf conf pc;
6801 state.bookmarks <- pb;
6802 state.x <- px;
6803 state.scrollw <- conf.scrollbw;
6804 if conf.jumpback
6805 then state.anchor <- pa;
6806 cbput state.hists.nav pa;
6807 true
6809 load1 f
6812 let add_attrs bb always dc c =
6813 let ob s a b =
6814 if always || a != b
6815 then Printf.bprintf bb "\n %s='%b'" s a
6816 and oi s a b =
6817 if always || a != b
6818 then Printf.bprintf bb "\n %s='%d'" s a
6819 and oI s a b =
6820 if always || a != b
6821 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6822 and oz s a b =
6823 if always || a <> b
6824 then Printf.bprintf bb "\n %s='%g'" s (a*.100.)
6825 and oF s a b =
6826 if always || a <> b
6827 then Printf.bprintf bb "\n %s='%f'" s a
6828 and oc s a b =
6829 if always || a <> b
6830 then
6831 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6832 and oC s a b =
6833 if always || a <> b
6834 then
6835 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6836 and oR s a b =
6837 if always || a <> b
6838 then
6839 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6840 and os s a b =
6841 if always || a <> b
6842 then
6843 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6844 and og s a b =
6845 if always || a <> b
6846 then
6847 match a with
6848 | None -> ()
6849 | Some (_N, _A, _B) ->
6850 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6851 and oW s a b =
6852 if always || a <> b
6853 then
6854 let v =
6855 match a with
6856 | None -> "false"
6857 | Some f ->
6858 if f = infinity
6859 then "true"
6860 else string_of_float f
6862 Printf.bprintf bb "\n %s='%s'" s v
6863 and oco s a b =
6864 if always || a <> b
6865 then
6866 match a with
6867 | Cmulti ((n, a, b), _) when n > 1 ->
6868 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6869 | Csplit (n, _) when n > 1 ->
6870 Printf.bprintf bb "\n %s='%d'" s ~-n
6871 | _ -> ()
6872 and obeco s a b =
6873 if always || a <> b
6874 then
6875 match a with
6876 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6877 | _ -> ()
6878 and oFm s a b =
6879 if always || a <> b
6880 then
6881 Printf.bprintf bb "\n %s='%s'" s (fitmodel_to_string a)
6883 oi "width" c.cwinw dc.cwinw;
6884 oi "height" c.cwinh dc.cwinh;
6885 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6886 oi "scroll-handle-height" c.scrollh dc.scrollh;
6887 ob "case-insensitive-search" c.icase dc.icase;
6888 ob "preload" c.preload dc.preload;
6889 oi "page-bias" c.pagebias dc.pagebias;
6890 oi "scroll-step" c.scrollstep dc.scrollstep;
6891 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6892 ob "max-height-fit" c.maxhfit dc.maxhfit;
6893 ob "crop-hack" c.crophack dc.crophack;
6894 oW "throttle" c.maxwait dc.maxwait;
6895 ob "highlight-links" c.hlinks dc.hlinks;
6896 ob "under-cursor-info" c.underinfo dc.underinfo;
6897 oi "vertical-margin" c.interpagespace dc.interpagespace;
6898 oz "zoom" c.zoom dc.zoom;
6899 ob "presentation" c.presentation dc.presentation;
6900 oi "rotation-angle" c.angle dc.angle;
6901 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6902 oFm "fit-model" c.fitmodel dc.fitmodel;
6903 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6904 oi "tex-count" c.texcount dc.texcount;
6905 oi "slice-height" c.sliceheight dc.sliceheight;
6906 oi "thumbnail-width" c.thumbw dc.thumbw;
6907 ob "persistent-location" c.jumpback dc.jumpback;
6908 oc "background-color" c.bgcolor dc.bgcolor;
6909 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6910 oi "tile-width" c.tilew dc.tilew;
6911 oi "tile-height" c.tileh dc.tileh;
6912 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6913 ob "checkers" c.checkers dc.checkers;
6914 oi "aalevel" c.aalevel dc.aalevel;
6915 ob "trim-margins" c.trimmargins dc.trimmargins;
6916 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6917 os "uri-launcher" c.urilauncher dc.urilauncher;
6918 os "path-launcher" c.pathlauncher dc.pathlauncher;
6919 oC "color-space" c.colorspace dc.colorspace;
6920 ob "invert-colors" c.invert dc.invert;
6921 oF "brightness" c.colorscale dc.colorscale;
6922 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6923 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6924 oco "columns" c.columns dc.columns;
6925 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6926 os "selection-command" c.selcmd dc.selcmd;
6927 os "synctex-command" c.stcmd dc.stcmd;
6928 ob "update-cursor" c.updatecurs dc.updatecurs;
6929 oi "hint-font-size" c.hfsize dc.hfsize;
6930 oi "horizontal-scroll-step" c.hscrollstep dc.hscrollstep;
6931 oF "page-scroll-scale" c.pgscale dc.pgscale;
6932 ob "use-pbo" c.usepbo dc.usepbo;
6933 ob "wheel-scrolls-pages" c.wheelbypage dc.wheelbypage;
6936 let keymapsbuf always dc c =
6937 let bb = Buffer.create 16 in
6938 let rec loop = function
6939 | [] -> ()
6940 | (modename, h) :: rest ->
6941 let dh = findkeyhash dc modename in
6942 if always || h <> dh
6943 then (
6944 if Hashtbl.length h > 0
6945 then (
6946 if Buffer.length bb > 0
6947 then Buffer.add_char bb '\n';
6948 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6949 Hashtbl.iter (fun i o ->
6950 let isdifferent = always ||
6952 let dO = Hashtbl.find dh i in
6953 dO <> o
6954 with Not_found -> true
6956 if isdifferent
6957 then
6958 let addkm (k, m) =
6959 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6960 if Wsi.withalt m then Buffer.add_string bb "alt-";
6961 if Wsi.withshift m then Buffer.add_string bb "shift-";
6962 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6963 Buffer.add_string bb (Wsi.keyname k);
6965 let addkms l =
6966 let rec loop = function
6967 | [] -> ()
6968 | km :: [] -> addkm km
6969 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6971 loop l
6973 Buffer.add_string bb "<map in='";
6974 addkm i;
6975 match o with
6976 | KMinsrt km ->
6977 Buffer.add_string bb "' out='";
6978 addkm km;
6979 Buffer.add_string bb "'/>\n"
6981 | KMinsrl kms ->
6982 Buffer.add_string bb "' out='";
6983 addkms kms;
6984 Buffer.add_string bb "'/>\n"
6986 | KMmulti (ins, kms) ->
6987 Buffer.add_char bb ' ';
6988 addkms ins;
6989 Buffer.add_string bb "' out='";
6990 addkms kms;
6991 Buffer.add_string bb "'/>\n"
6992 ) h;
6993 Buffer.add_string bb "</keymap>";
6996 loop rest
6998 loop c.keyhashes;
7002 let save () =
7003 let uifontsize = fstate.fontsize in
7004 let bb = Buffer.create 32768 in
7005 let w, h =
7006 List.fold_left
7007 (fun (w, h) ws ->
7008 match ws with
7009 | Wsi.Fullscreen -> (conf.cwinw, conf.cwinh)
7010 | Wsi.MaxVert -> (w, conf.cwinh)
7011 | Wsi.MaxHorz -> (conf.cwinw, h)
7013 (state.winw, state.winh) state.winstate
7015 conf.cwinw <- w;
7016 conf.cwinh <- h;
7017 let f (h, dc) =
7018 let dc = if conf.bedefault then conf else dc in
7019 Buffer.add_string bb "<llppconfig>\n";
7021 if String.length !fontpath > 0
7022 then
7023 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
7024 uifontsize
7025 !fontpath
7026 else (
7027 if uifontsize <> 14
7028 then
7029 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
7032 Buffer.add_string bb "<defaults ";
7033 add_attrs bb true dc dc;
7034 let kb = keymapsbuf true dc dc in
7035 if Buffer.length kb > 0
7036 then (
7037 Buffer.add_string bb ">\n";
7038 Buffer.add_buffer bb kb;
7039 Buffer.add_string bb "\n</defaults>\n";
7041 else Buffer.add_string bb "/>\n";
7043 let adddoc path pan anchor c bookmarks =
7044 if bookmarks == [] && c = dc && anchor = emptyanchor
7045 then ()
7046 else (
7047 Printf.bprintf bb "<doc path='%s'"
7048 (enent path 0 (String.length path));
7050 if anchor <> emptyanchor
7051 then (
7052 let n, rely, visy = anchor in
7053 Printf.bprintf bb " page='%d'" n;
7054 if rely > 1e-6
7055 then
7056 Printf.bprintf bb " rely='%f'" rely
7058 if abs_float visy > 1e-6
7059 then
7060 Printf.bprintf bb " visy='%f'" visy
7064 if pan != 0
7065 then Printf.bprintf bb " pan='%d'" pan;
7067 add_attrs bb false dc c;
7068 let kb = keymapsbuf false dc c in
7070 begin match bookmarks with
7071 | [] ->
7072 if Buffer.length kb > 0
7073 then (
7074 Buffer.add_string bb ">\n";
7075 Buffer.add_buffer bb kb;
7076 Buffer.add_string bb "\n</doc>\n";
7078 else Buffer.add_string bb "/>\n"
7079 | _ ->
7080 Buffer.add_string bb ">\n<bookmarks>\n";
7081 List.iter (fun (title, _level, (page, rely, visy)) ->
7082 Printf.bprintf bb
7083 "<item title='%s' page='%d'"
7084 (enent title 0 (String.length title))
7085 page
7087 if rely > 1e-6
7088 then
7089 Printf.bprintf bb " rely='%f'" rely
7091 if abs_float visy > 1e-6
7092 then
7093 Printf.bprintf bb " visy='%f'" visy
7095 Buffer.add_string bb "/>\n";
7096 ) bookmarks;
7097 Buffer.add_string bb "</bookmarks>";
7098 if Buffer.length kb > 0
7099 then (
7100 Buffer.add_string bb "\n";
7101 Buffer.add_buffer bb kb;
7103 Buffer.add_string bb "\n</doc>\n";
7104 end;
7108 let pan, conf =
7109 match state.mode with
7110 | Birdseye (c, pan, _, _, _) ->
7111 let beyecolumns =
7112 match conf.columns with
7113 | Cmulti ((c, _, _), _) -> Some c
7114 | Csingle _ -> None
7115 | Csplit _ -> None
7116 and columns =
7117 match c.columns with
7118 | Cmulti (c, _) -> Cmulti (c, [||])
7119 | Csingle _ -> Csingle [||]
7120 | Csplit _ -> failwith "quit from bird's eye while split"
7122 pan, { c with beyecolumns = beyecolumns; columns = columns }
7123 | _ -> state.x, conf
7125 let basename = Filename.basename
7126 (if String.length state.origin = 0 then state.path else state.origin)
7128 adddoc basename pan (getanchor ())
7129 (let conf =
7130 let autoscrollstep =
7131 match state.autoscroll with
7132 | Some step -> step
7133 | None -> conf.autoscrollstep
7135 match state.mode with
7136 | Birdseye (bc, _, _, _, _) ->
7137 { conf with
7138 zoom = bc.zoom;
7139 presentation = bc.presentation;
7140 interpagespace = bc.interpagespace;
7141 maxwait = bc.maxwait;
7142 autoscrollstep = autoscrollstep }
7143 | _ -> { conf with autoscrollstep = autoscrollstep }
7144 in conf)
7145 (if conf.savebmarks then state.bookmarks else []);
7147 Hashtbl.iter (fun path (c, bookmarks, x, anchor) ->
7148 if basename <> path
7149 then adddoc path x anchor c bookmarks
7150 ) h;
7151 Buffer.add_string bb "</llppconfig>\n";
7152 true;
7154 if load1 f && Buffer.length bb > 0
7155 then
7157 let tmp = !confpath ^ ".tmp" in
7158 let oc = open_out_bin tmp in
7159 Buffer.output_buffer oc bb;
7160 close_out oc;
7161 Unix.rename tmp !confpath;
7162 with exn ->
7163 prerr_endline
7164 ("error while saving configuration: " ^ exntos exn)
7166 end;;
7168 let adderrmsg src msg =
7169 Buffer.add_string state.errmsgs msg;
7170 state.newerrmsgs <- true;
7171 G.postRedisplay src
7174 let adderrfmt src fmt =
7175 Format.kprintf (fun s -> adderrmsg src s) fmt;
7178 let ract cmds =
7179 let cl = splitatspace cmds in
7180 let scan s fmt f =
7181 try Scanf.sscanf s fmt f
7182 with exn ->
7183 adderrfmt "remote exec"
7184 "error processing '%S': %s\n" cmds (exntos exn)
7186 match cl with
7187 | "reload" :: [] -> reload ()
7188 | "goto" :: args :: [] ->
7189 scan args "%u %f %f"
7190 (fun pageno x y ->
7191 let cmd, _ = state.geomcmds in
7192 if String.length cmd = 0
7193 then gotopagexy pageno x y
7194 else
7195 let f prevf () =
7196 gotopagexy pageno x y;
7197 prevf ()
7199 state.reprf <- f state.reprf
7201 | "goto1" :: args :: [] -> scan args "%u %f" gotopage
7202 | "rect" :: args :: [] ->
7203 scan args "%u %u %f %f %f %f"
7204 (fun pageno color x0 y0 x1 y1 ->
7205 onpagerect pageno (fun w h ->
7206 let _,w1,h1,_ = getpagedim pageno in
7207 let sw = float w1 /. w
7208 and sh = float h1 /. h in
7209 let x0s = x0 *. sw
7210 and x1s = x1 *. sw
7211 and y0s = y0 *. sh
7212 and y1s = y1 *. sh in
7213 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
7214 debugrect rect;
7215 state.rects <- (pageno, color, rect) :: state.rects;
7216 G.postRedisplay "rect";
7219 | "activatewin" :: [] -> Wsi.activatewin ()
7220 | "quit" :: [] -> raise Quit
7221 | _ ->
7222 adderrfmt "remote command"
7223 "error processing remote command: %S\n" cmds;
7226 let remote =
7227 let scratch = String.create 80 in
7228 let buf = Buffer.create 80 in
7229 fun fd ->
7230 let rec tempfr () =
7231 try Some (Unix.read fd scratch 0 80)
7232 with
7233 | Unix.Unix_error (Unix.EAGAIN, _, _) -> None
7234 | Unix.Unix_error (Unix.EINTR, _, _) -> tempfr ()
7235 | exn -> raise exn
7237 match tempfr () with
7238 | None -> Some fd
7239 | Some n ->
7240 if n = 0
7241 then (
7242 Unix.close fd;
7243 if Buffer.length buf > 0
7244 then (
7245 let s = Buffer.contents buf in
7246 Buffer.clear buf;
7247 ract s;
7249 None
7251 else
7252 let rec eat ppos =
7253 let nlpos =
7255 let pos = String.index_from scratch ppos '\n' in
7256 if pos >= n then -1 else pos
7257 with Not_found -> -1
7259 if nlpos >= 0
7260 then (
7261 Buffer.add_substring buf scratch ppos (nlpos-ppos);
7262 let s = Buffer.contents buf in
7263 Buffer.clear buf;
7264 ract s;
7265 eat (nlpos+1);
7267 else (
7268 Buffer.add_substring buf scratch ppos (n-ppos);
7269 Some fd
7271 in eat 0
7274 let remoteopen path =
7275 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
7276 with exn ->
7277 adderrfmt "remoteopen" "error opening %S: %s" path (exntos exn);
7278 None
7281 let () =
7282 let trimcachepath = ref "" in
7283 let rcmdpath = ref "" in
7284 Arg.parse
7285 (Arg.align
7286 [("-p", Arg.String (fun s -> state.password <- s),
7287 "<password> Set password");
7289 ("-f", Arg.String (fun s -> Config.fontpath := s),
7290 "<path> Set path to the user interface font");
7292 ("-c", Arg.String (fun s -> Config.confpath := s),
7293 "<path> Set path to the configuration file");
7295 ("-tcf", Arg.String (fun s -> trimcachepath := s),
7296 "<path> Set path to the trim cache file");
7298 ("-dest", Arg.String (fun s -> state.nameddest <- s),
7299 "<named-destination> Set named destination");
7301 ("-wtmode", Arg.Set wtmode, " Operate in wt mode");
7303 ("-remote", Arg.String (fun s -> rcmdpath := s),
7304 "<path> Set path to the remote commands source");
7306 ("-origin", Arg.String (fun s -> state.origin <- s),
7307 "<original path> Set original path");
7309 ("-v", Arg.Unit (fun () ->
7310 Printf.printf
7311 "%s\nconfiguration path: %s\n"
7312 (version ())
7313 Config.defconfpath
7315 exit 0), " Print version and exit");
7318 (fun s -> state.path <- s)
7319 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
7321 if String.length state.path = 0
7322 then (prerr_endline "file name missing"; exit 1);
7324 if not (Config.load ())
7325 then prerr_endline "failed to load configuration";
7327 let globalkeyhash = findkeyhash conf "global" in
7328 let wsfd, winw, winh = Wsi.init (object
7329 method expose =
7330 if nogeomcmds state.geomcmds || platform == Posx
7331 then display ()
7332 else (
7333 GlClear.color (scalecolor2 conf.bgcolor);
7334 GlClear.clear [`color];
7336 method display = display ()
7337 method reshape w h = reshape w h
7338 method mouse b d x y m = mouse b d x y m
7339 method motion x y = state.mpos <- (x, y); motion x y
7340 method pmotion x y = state.mpos <- (x, y); pmotion x y
7341 method key k m =
7342 let mascm = m land (
7343 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
7344 ) in
7345 match state.keystate with
7346 | KSnone ->
7347 let km = k, mascm in
7348 begin
7349 match
7350 let modehash = state.uioh#modehash in
7351 try Hashtbl.find modehash km
7352 with Not_found ->
7353 try Hashtbl.find globalkeyhash km
7354 with Not_found -> KMinsrt (k, m)
7355 with
7356 | KMinsrt (k, m) -> keyboard k m
7357 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
7358 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
7360 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
7361 List.iter (fun (k, m) -> keyboard k m) insrt;
7362 state.keystate <- KSnone
7363 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
7364 state.keystate <- KSinto (keys, insrt)
7365 | _ ->
7366 state.keystate <- KSnone
7368 method enter x y = state.mpos <- (x, y); pmotion x y
7369 method leave = state.mpos <- (-1, -1)
7370 method winstate wsl = state.winstate <- wsl
7371 method quit = raise Quit
7372 end) conf.cwinw conf.cwinh (platform = Posx) in
7374 state.wsfd <- wsfd;
7376 if not (
7377 List.exists GlMisc.check_extension
7378 [ "GL_ARB_texture_rectangle"
7379 ; "GL_EXT_texture_recangle"
7380 ; "GL_NV_texture_rectangle" ]
7382 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
7384 let cr, sw =
7385 match Ne.pipe () with
7386 | Ne.Exn exn ->
7387 Printf.eprintf "pipe/crsw failed: %s" (exntos exn);
7388 exit 1
7389 | Ne.Res rw -> rw
7390 and sr, cw =
7391 match Ne.pipe () with
7392 | Ne.Exn exn ->
7393 Printf.eprintf "pipe/srcw failed: %s" (exntos exn);
7394 exit 1
7395 | Ne.Res rw -> rw
7398 cloexec cr;
7399 cloexec sw;
7400 cloexec sr;
7401 cloexec cw;
7403 setcheckers conf.checkers;
7404 redirectstderr ();
7406 init (cr, cw) (
7407 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
7408 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
7409 !Config.fontpath, !trimcachepath,
7410 GlMisc.check_extension "GL_ARB_pixel_buffer_object"
7412 state.sr <- sr;
7413 state.sw <- sw;
7414 state.text <- "Opening " ^ (mbtoutf8 state.path);
7415 reshape winw winh;
7416 opendoc state.path state.password;
7417 state.uioh <- uioh;
7419 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
7420 let optrfd =
7421 ref (
7422 if String.length !rcmdpath > 0
7423 then remoteopen !rcmdpath
7424 else None
7428 let rec loop deadline =
7429 let r =
7430 match state.errfd with
7431 | None -> [state.sr; state.wsfd]
7432 | Some fd -> [state.sr; state.wsfd; fd]
7434 let r =
7435 match !optrfd with
7436 | None -> r
7437 | Some fd -> fd :: r
7439 if state.redisplay
7440 then (
7441 state.redisplay <- false;
7442 display ();
7444 let timeout =
7445 let now = now () in
7446 if deadline > now
7447 then (
7448 if deadline = infinity
7449 then ~-.1.0
7450 else max 0.0 (deadline -. now)
7452 else 0.0
7454 let r, _, _ =
7455 try Unix.select r [] [] timeout
7456 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
7458 begin match r with
7459 | [] ->
7460 state.ghyll None;
7461 let newdeadline =
7462 if state.ghyll == noghyll
7463 then
7464 match state.autoscroll with
7465 | Some step when step != 0 ->
7466 let y = state.y + step in
7467 let y =
7468 if y < 0
7469 then state.maxy
7470 else if y >= state.maxy then 0 else y
7472 gotoy y;
7473 if state.mode = View
7474 then state.text <- "";
7475 deadline +. 0.01
7476 | _ -> infinity
7477 else deadline +. 0.01
7479 loop newdeadline
7481 | l ->
7482 let rec checkfds = function
7483 | [] -> ()
7484 | fd :: rest when fd = state.sr ->
7485 let cmd = readcmd state.sr in
7486 act cmd;
7487 checkfds rest
7489 | fd :: rest when fd = state.wsfd ->
7490 Wsi.readresp fd;
7491 checkfds rest
7493 | fd :: rest when Some fd = !optrfd ->
7494 begin match remote fd with
7495 | None -> optrfd := remoteopen !rcmdpath;
7496 | opt -> optrfd := opt
7497 end;
7498 checkfds rest
7500 | fd :: rest ->
7501 let s = String.create 80 in
7502 let n = tempfailureretry (Unix.read fd s 0) 80 in
7503 if conf.redirectstderr
7504 then (
7505 Buffer.add_substring state.errmsgs s 0 n;
7506 state.newerrmsgs <- true;
7507 state.redisplay <- true;
7509 else (
7510 prerr_string (String.sub s 0 n);
7511 flush stderr;
7513 checkfds rest
7515 checkfds l;
7516 let newdeadline =
7517 let deadline1 =
7518 if deadline = infinity
7519 then now () +. 0.01
7520 else deadline
7522 match state.autoscroll with
7523 | Some step when step != 0 -> deadline1
7524 | _ -> if state.ghyll == noghyll then infinity else deadline1
7526 loop newdeadline
7527 end;
7530 loop infinity;
7531 with Quit ->
7532 Config.save ();