Kill stray empty line
[llpp.git] / main.ml
blob5f985c5040678d5f1cb1b7e9e66f78db94ebe1c7
1 type under =
2 | Unone
3 | Ulinkuri of string
4 | Ulinkgoto of (int * int)
5 | Utext of facename
6 | Uunexpected of string
7 | Ulaunch of string
8 | Unamed of string
9 | Uremote of (string * int)
10 and facename = string;;
12 let dolog fmt = Printf.kprintf prerr_endline fmt;;
13 let now = Unix.gettimeofday;;
15 exception Quit;;
17 type params = (angle * proportional * trimparams
18 * texcount * sliceheight * memsize
19 * colorspace * wmclasshack * fontpath)
20 and pageno = int
21 and width = int
22 and height = int
23 and leftx = int
24 and opaque = string
25 and recttype = int
26 and pixmapsize = int
27 and angle = int
28 and proportional = bool
29 and trimmargins = bool
30 and interpagespace = int
31 and texcount = int
32 and sliceheight = int
33 and gen = int
34 and top = float
35 and fontpath = string
36 and memsize = int
37 and aalevel = int
38 and wmclasshack = bool
39 and irect = (int * int * int * int)
40 and trimparams = (trimmargins * irect)
41 and colorspace = | Rgb | Bgr | Gray
44 type platform = | Punknown | Plinux | Pwindows | Pwindowsgui | Posx | Psun
45 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
46 | Pmingw | Pmingwgui| Pcygwin;;
48 type pipe = (Unix.file_descr * Unix.file_descr);;
50 external init : pipe -> params -> unit = "ml_init";;
51 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
52 external copysel : string -> opaque -> unit = "ml_copysel";;
53 external getpdimrect : int -> float array = "ml_getpdimrect";;
54 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
55 external zoomforh : int -> int -> int -> float = "ml_zoom_for_height";;
56 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
57 external measurestr : int -> string -> float = "ml_measure_string";;
58 external getmaxw : unit -> float = "ml_getmaxw";;
59 external postprocess : opaque -> bool -> int -> int -> unit = "ml_postprocess";;
60 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
61 external platform : unit -> platform = "ml_platform";;
62 external setaalevel : int -> unit = "ml_setaalevel";;
63 external realloctexts : int -> bool = "ml_realloctexts";;
64 external seterrhandle : bool -> Unix.file_descr -> unit = "ml_seterrhandle";;
66 let platform_to_string = function
67 | Punknown -> "unknown"
68 | Plinux -> "Linux"
69 | Pwindows -> "Windows"
70 | Pwindowsgui -> "Windows/GUI"
71 | Posx -> "OSX"
72 | Psun -> "Sun"
73 | Pfreebsd -> "FreeBSD"
74 | Pdragonflybsd -> "DragonflyBSD"
75 | Popenbsd -> "OpenBSD"
76 | Pnetbsd -> "NetBSD"
77 | Pcygwin -> "Cygwin"
78 | Pmingw -> "MingW"
79 | Pmingwgui -> "MingW/GUI"
82 let platform = platform ();;
84 let is_windows =
85 match platform with
86 | Pwindows | Pwindowsgui | Pmingw | Pmingwgui -> true
87 | _ -> false
90 let is_gui =
91 match platform with
92 | Pwindowsgui | Pmingwgui -> true
93 | _ -> false
96 type x = int
97 and y = int
98 and tilex = int
99 and tiley = int
100 and tileparams = (x * y * width * height * tilex * tiley)
103 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
105 type mpos = int * int
106 and mstate =
107 | Msel of (mpos * mpos)
108 | Mpan of mpos
109 | Mscrolly | Mscrollx
110 | Mzoom of (int * int)
111 | Mzoomrect of (mpos * mpos)
112 | Mnone
115 type textentry = string * string * onhist option * onkey * ondone
116 and onkey = string -> int -> te
117 and ondone = string -> unit
118 and histcancel = unit -> unit
119 and onhist = ((histcmd -> string) * histcancel)
120 and histcmd = HCnext | HCprev | HCfirst | HClast
121 and te =
122 | TEstop
123 | TEdone of string
124 | TEcont of string
125 | TEswitch of textentry
128 type 'a circbuf =
129 { store : 'a array
130 ; mutable rc : int
131 ; mutable wc : int
132 ; mutable len : int
136 let bound v minv maxv =
137 max minv (min maxv v);
140 let cbnew n v =
141 { store = Array.create n v
142 ; rc = 0
143 ; wc = 0
144 ; len = 0
148 let drawstring size x y s =
149 Gl.enable `blend;
150 Gl.enable `texture_2d;
151 ignore (drawstr size x y s);
152 Gl.disable `blend;
153 Gl.disable `texture_2d;
156 let drawstring1 size x y s =
157 drawstr size x y s;
160 let drawstring2 size x y fmt =
161 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
164 let cbcap b = Array.length b.store;;
166 let cbput b v =
167 let cap = cbcap b in
168 b.store.(b.wc) <- v;
169 b.wc <- (b.wc + 1) mod cap;
170 b.rc <- b.wc;
171 b.len <- min (b.len + 1) cap;
174 let cbempty b = b.len = 0;;
176 let cbgetg b circular dir =
177 if cbempty b
178 then b.store.(0)
179 else
180 let rc = b.rc + dir in
181 let rc =
182 if circular
183 then (
184 if rc = -1
185 then b.len-1
186 else (
187 if rc = b.len
188 then 0
189 else rc
192 else max 0 (min rc (b.len-1))
194 b.rc <- rc;
195 b.store.(rc);
198 let cbget b = cbgetg b false;;
199 let cbgetc b = cbgetg b true;;
201 type page =
202 { pageno : int
203 ; pagedimno : int
204 ; pagew : int
205 ; pageh : int
206 ; pagex : int
207 ; pagey : int
208 ; pagevw : int
209 ; pagevh : int
210 ; pagedispx : int
211 ; pagedispy : int
215 let debugl l =
216 dolog "l %d dim=%d {" l.pageno l.pagedimno;
217 dolog " WxH %dx%d" l.pagew l.pageh;
218 dolog " vWxH %dx%d" l.pagevw l.pagevh;
219 dolog " pagex,y %d,%d" l.pagex l.pagey;
220 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
221 dolog "}";
224 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
225 dolog "rect {";
226 dolog " x0,y0=(% f, % f)" x0 y0;
227 dolog " x1,y1=(% f, % f)" x1 y1;
228 dolog " x2,y2=(% f, % f)" x2 y2;
229 dolog " x3,y3=(% f, % f)" x3 y3;
230 dolog "}";
233 type columns =
234 multicol * ((pdimno * x * y * (pageno * width * height * leftx)) array)
235 and multicol = columncount * covercount * covercount
236 and pdimno = int
237 and columncount = int
238 and covercount = int;;
240 type conf =
241 { mutable scrollbw : int
242 ; mutable scrollh : int
243 ; mutable icase : bool
244 ; mutable preload : bool
245 ; mutable pagebias : int
246 ; mutable verbose : bool
247 ; mutable debug : bool
248 ; mutable scrollstep : int
249 ; mutable maxhfit : bool
250 ; mutable crophack : bool
251 ; mutable autoscrollstep : int
252 ; mutable maxwait : float option
253 ; mutable hlinks : bool
254 ; mutable underinfo : bool
255 ; mutable interpagespace : interpagespace
256 ; mutable zoom : float
257 ; mutable presentation : bool
258 ; mutable angle : angle
259 ; mutable winw : int
260 ; mutable winh : int
261 ; mutable savebmarks : bool
262 ; mutable proportional : proportional
263 ; mutable trimmargins : trimmargins
264 ; mutable trimfuzz : irect
265 ; mutable memlimit : memsize
266 ; mutable texcount : texcount
267 ; mutable sliceheight : sliceheight
268 ; mutable thumbw : width
269 ; mutable jumpback : bool
270 ; mutable bgcolor : float * float * float
271 ; mutable bedefault : bool
272 ; mutable scrollbarinpm : bool
273 ; mutable tilew : int
274 ; mutable tileh : int
275 ; mutable mustoresize : memsize
276 ; mutable checkers : bool
277 ; mutable aalevel : int
278 ; mutable urilauncher : string
279 ; mutable colorspace : colorspace
280 ; mutable invert : bool
281 ; mutable colorscale : float
282 ; mutable redirectstderr : bool
283 ; mutable ghyllscroll : (int * int * int) option
284 ; mutable columns : columns option
285 ; mutable beyecolumns : columncount option
286 ; mutable selcmd : string
290 type anchor = pageno * top;;
292 type outline = string * int * anchor;;
294 type rect = float * float * float * float * float * float * float * float;;
296 type tile = opaque * pixmapsize * elapsed
297 and elapsed = float;;
298 type pagemapkey = pageno * gen;;
299 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
300 and row = int
301 and col = int;;
303 let emptyanchor = (0, 0.0);;
305 type infochange = | Memused | Docinfo | Pdim;;
307 class type uioh = object
308 method display : unit
309 method key : int -> uioh
310 method special : Glut.special_key_t -> uioh
311 method button :
312 Glut.button_t -> Glut.mouse_button_state_t -> int -> int -> uioh
313 method motion : int -> int -> uioh
314 method pmotion : int -> int -> uioh
315 method infochanged : infochange -> unit
316 method scrollpw : (int * float * float)
317 method scrollph : (int * float * float)
318 end;;
320 type mode =
321 | Birdseye of (conf * leftx * pageno * pageno * anchor)
322 | Textentry of (textentry * onleave)
323 | View
324 and onleave = leavetextentrystatus -> unit
325 and leavetextentrystatus = | Cancel | Confirm
326 and helpitem = string * int * action
327 and action =
328 | Noaction
329 | Action of (uioh -> uioh)
332 let isbirdseye = function Birdseye _ -> true | _ -> false;;
333 let istextentry = function Textentry _ -> true | _ -> false;;
335 type currently =
336 | Idle
337 | Loading of (page * gen)
338 | Tiling of (
339 page * opaque * colorspace * angle * gen * col * row * width * height
341 | Outlining of outline list
344 let nouioh : uioh = object (self)
345 method display = ()
346 method key _ = self
347 method special _ = self
348 method button _ _ _ _ = self
349 method motion _ _ = self
350 method pmotion _ _ = self
351 method infochanged _ = ()
352 method scrollpw = (0, nan, nan)
353 method scrollph = (0, nan, nan)
354 end;;
356 type state =
357 { mutable sr : Unix.file_descr
358 ; mutable sw : Unix.file_descr
359 ; mutable errfd : Unix.file_descr option
360 ; mutable stderr : Unix.file_descr
361 ; mutable errmsgs : Buffer.t
362 ; mutable newerrmsgs : bool
363 ; mutable w : int
364 ; mutable x : int
365 ; mutable y : int
366 ; mutable scrollw : int
367 ; mutable hscrollh : int
368 ; mutable anchor : anchor
369 ; mutable ranchors : (string * string * anchor) list
370 ; mutable maxy : int
371 ; mutable layout : page list
372 ; pagemap : (pagemapkey, opaque) Hashtbl.t
373 ; tilemap : (tilemapkey, tile) Hashtbl.t
374 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
375 ; mutable pdims : (pageno * width * height * leftx) list
376 ; mutable pagecount : int
377 ; mutable currently : currently
378 ; mutable mstate : mstate
379 ; mutable searchpattern : string
380 ; mutable rects : (pageno * recttype * rect) list
381 ; mutable rects1 : (pageno * recttype * rect) list
382 ; mutable text : string
383 ; mutable fullscreen : (width * height) option
384 ; mutable mode : mode
385 ; mutable uioh : uioh
386 ; mutable outlines : outline array
387 ; mutable bookmarks : outline list
388 ; mutable path : string
389 ; mutable password : string
390 ; mutable invalidated : int
391 ; mutable memused : memsize
392 ; mutable gen : gen
393 ; mutable throttle : (page list * int * float) option
394 ; mutable autoscroll : int option
395 ; mutable ghyll : int option -> unit
396 ; mutable help : helpitem array
397 ; mutable docinfo : (int * string) list
398 ; mutable deadline : float
399 ; mutable texid : GlTex.texture_id option
400 ; hists : hists
401 ; mutable prevzoom : float
402 ; mutable progress : float
404 and hists =
405 { pat : string circbuf
406 ; pag : string circbuf
407 ; nav : anchor circbuf
408 ; sel : string circbuf
412 let defconf =
413 { scrollbw = 7
414 ; scrollh = 12
415 ; icase = true
416 ; preload = true
417 ; pagebias = 0
418 ; verbose = false
419 ; debug = false
420 ; scrollstep = 24
421 ; maxhfit = true
422 ; crophack = false
423 ; autoscrollstep = 2
424 ; maxwait = None
425 ; hlinks = false
426 ; underinfo = false
427 ; interpagespace = 2
428 ; zoom = 1.0
429 ; presentation = false
430 ; angle = 0
431 ; winw = 900
432 ; winh = 900
433 ; savebmarks = true
434 ; proportional = true
435 ; trimmargins = false
436 ; trimfuzz = (0,0,0,0)
437 ; memlimit = 32 lsl 20
438 ; texcount = 256
439 ; sliceheight = 24
440 ; thumbw = 76
441 ; jumpback = true
442 ; bgcolor = (0.5, 0.5, 0.5)
443 ; bedefault = false
444 ; scrollbarinpm = true
445 ; tilew = 2048
446 ; tileh = 2048
447 ; mustoresize = 128 lsl 20
448 ; checkers = true
449 ; aalevel = 8
450 ; urilauncher =
451 (match platform with
452 | Plinux
453 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
454 | Psun -> "xdg-open \"%s\""
455 | Posx -> "open \"%s\""
456 | Pwindows | Pwindowsgui | Pcygwin | Pmingw | Pmingwgui -> "start %s"
457 | Punknown -> "echo %s")
458 ; selcmd =
459 (match platform with
460 | Plinux
461 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
462 | Psun -> "xsel -i"
463 | Posx -> "pbcopy"
464 | Pwindows | Pwindowsgui | Pcygwin | Pmingw | Pmingwgui -> "wsel"
465 | Punknown -> "cat")
466 ; colorspace = Rgb
467 ; invert = false
468 ; colorscale = 1.0
469 ; redirectstderr = false
470 ; ghyllscroll = None
471 ; columns = None
472 ; beyecolumns = None
476 let conf = { defconf with angle = defconf.angle };;
478 type fontstate =
479 { mutable fontsize : int
480 ; mutable wwidth : float
481 ; mutable maxrows : int
485 let fstate =
486 { fontsize = 14
487 ; wwidth = nan
488 ; maxrows = -1
492 let setfontsize n =
493 fstate.fontsize <- n;
494 fstate.wwidth <- measurestr fstate.fontsize "w";
495 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
498 let geturl s =
499 let colonpos = try String.index s ':' with Not_found -> -1 in
500 let len = String.length s in
501 if colonpos >= 0 && colonpos + 3 < len
502 then (
503 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
504 then
505 let schemestartpos =
506 try String.rindex_from s colonpos ' '
507 with Not_found -> -1
509 let scheme =
510 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
512 match scheme with
513 | "http" | "ftp" | "mailto" ->
514 let epos =
515 try String.index_from s colonpos ' '
516 with Not_found -> len
518 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
519 | _ -> ""
520 else ""
522 else ""
525 let popen =
526 let shell, farg =
527 if is_windows
528 then (try Sys.getenv "COMSPEC" with Not_found -> "cmd"), "/c"
529 else "/bin/sh", "-c"
531 fun s ->
532 let args = [|shell; farg; s|] in
533 ignore (Unix.create_process shell args Unix.stdin Unix.stdout Unix.stderr)
536 let gotouri uri =
537 if String.length conf.urilauncher = 0
538 then print_endline uri
539 else (
540 let url = geturl uri in
541 if String.length url = 0
542 then print_endline uri
543 else
544 let re = Str.regexp "%s" in
545 let command = Str.global_replace re url conf.urilauncher in
546 try popen command
547 with exn ->
548 Printf.eprintf
549 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
550 flush stderr;
554 let version () =
555 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
556 (platform_to_string platform) Sys.word_size Sys.ocaml_version
559 let makehelp () =
560 let strings = version () :: "" :: Help.keys in
561 Array.of_list (
562 List.map (fun s ->
563 let url = geturl s in
564 if String.length url > 0
565 then (s, 0, Action (fun u -> gotouri url; u))
566 else (s, 0, Noaction)
567 ) strings);
570 let noghyll _ = ();;
572 let state =
573 { sr = Unix.stdin
574 ; sw = Unix.stdin
575 ; errfd = None
576 ; stderr = Unix.stderr
577 ; errmsgs = Buffer.create 0
578 ; newerrmsgs = false
579 ; x = 0
580 ; y = 0
581 ; w = 0
582 ; scrollw = 0
583 ; hscrollh = 0
584 ; anchor = emptyanchor
585 ; ranchors = []
586 ; layout = []
587 ; maxy = max_int
588 ; tilelru = Queue.create ()
589 ; pagemap = Hashtbl.create 10
590 ; tilemap = Hashtbl.create 10
591 ; pdims = []
592 ; pagecount = 0
593 ; currently = Idle
594 ; mstate = Mnone
595 ; rects = []
596 ; rects1 = []
597 ; text = ""
598 ; mode = View
599 ; fullscreen = None
600 ; searchpattern = ""
601 ; outlines = [||]
602 ; bookmarks = []
603 ; path = ""
604 ; password = ""
605 ; invalidated = 0
606 ; hists =
607 { nav = cbnew 10 (0, 0.0)
608 ; pat = cbnew 10 ""
609 ; pag = cbnew 10 ""
610 ; sel = cbnew 10 ""
612 ; memused = 0
613 ; gen = 0
614 ; throttle = None
615 ; autoscroll = None
616 ; ghyll = noghyll
617 ; help = makehelp ()
618 ; docinfo = []
619 ; deadline = nan
620 ; texid = None
621 ; prevzoom = 1.0
622 ; progress = -1.0
623 ; uioh = nouioh
627 let vlog fmt =
628 if conf.verbose
629 then
630 Printf.kprintf prerr_endline fmt
631 else
632 Printf.kprintf ignore fmt
635 let () =
636 if is_gui then (
637 let rfd, wfd = Unix.pipe () in
638 state.errfd <- Some rfd;
639 seterrhandle true wfd;
640 Unix.dup2 wfd Unix.stderr;
644 let redirectstderr () =
645 if not is_gui
646 then (
647 if conf.redirectstderr
648 then
649 let rfd, wfd = Unix.pipe () in
650 state.stderr <- Unix.dup Unix.stderr;
651 state.errfd <- Some rfd;
652 seterrhandle false wfd;
653 Unix.dup2 wfd Unix.stderr;
654 else (
655 state.newerrmsgs <- false;
656 begin match state.errfd with
657 | Some fd ->
658 Unix.close fd;
659 seterrhandle false state.stderr;
660 Unix.dup2 state.stderr Unix.stderr;
661 state.errfd <- None;
662 | None -> ()
663 end;
664 prerr_string (Buffer.contents state.errmsgs);
665 flush stderr;
666 Buffer.clear state.errmsgs;
671 module G =
672 struct
673 let postRedisplay who =
674 if conf.verbose
675 then prerr_endline ("redisplay for " ^ who);
676 Glut.postRedisplay ();
678 end;;
680 let addchar s c =
681 let b = Buffer.create (String.length s + 1) in
682 Buffer.add_string b s;
683 Buffer.add_char b c;
684 Buffer.contents b;
687 let colorspace_of_string s =
688 match String.lowercase s with
689 | "rgb" -> Rgb
690 | "bgr" -> Bgr
691 | "gray" -> Gray
692 | _ -> failwith "invalid colorspace"
695 let int_of_colorspace = function
696 | Rgb -> 0
697 | Bgr -> 1
698 | Gray -> 2
701 let colorspace_of_int = function
702 | 0 -> Rgb
703 | 1 -> Bgr
704 | 2 -> Gray
705 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
708 let colorspace_to_string = function
709 | Rgb -> "rgb"
710 | Bgr -> "bgr"
711 | Gray -> "gray"
714 let intentry_with_suffix text key =
715 let c = Char.unsafe_chr key in
716 match Char.lowercase c with
717 | '0' .. '9' ->
718 let text = addchar text c in
719 TEcont text
721 | 'k' | 'm' | 'g' ->
722 let text = addchar text c in
723 TEcont text
725 | _ ->
726 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
727 TEcont text
730 let columns_to_string (n, a, b) =
731 if a = 0 && b = 0
732 then Printf.sprintf "%d" n
733 else Printf.sprintf "%d,%d,%d" n a b;
736 let columns_of_string s =
738 (int_of_string s, 0, 0)
739 with _ ->
740 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
743 let writecmd fd s =
744 let len = String.length s in
745 let n = 4 + len in
746 let b = Buffer.create n in
747 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
748 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
749 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
750 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
751 Buffer.add_string b s;
752 let s' = Buffer.contents b in
753 let n' = Unix.write fd s' 0 n in
754 if n' != n then failwith "write failed";
757 let readcmd fd =
758 let s = "xxxx" in
759 let n = Unix.read fd s 0 4 in
760 if n != 4 then failwith "incomplete read(len)";
761 let len = 0
762 lor (Char.code s.[0] lsl 24)
763 lor (Char.code s.[1] lsl 16)
764 lor (Char.code s.[2] lsl 8)
765 lor (Char.code s.[3] lsl 0)
767 let s = String.create len in
768 let n = Unix.read fd s 0 len in
769 if n != len then failwith "incomplete read(data)";
773 let makecmd s l =
774 let b = Buffer.create 10 in
775 Buffer.add_string b s;
776 let rec combine = function
777 | [] -> b
778 | x :: xs ->
779 Buffer.add_char b ' ';
780 let s =
781 match x with
782 | `b b -> if b then "1" else "0"
783 | `s s -> s
784 | `i i -> string_of_int i
785 | `f f -> string_of_float f
786 | `I f -> string_of_int (truncate f)
788 Buffer.add_string b s;
789 combine xs;
791 combine l;
794 let wcmd s l =
795 let cmd = Buffer.contents (makecmd s l) in
796 writecmd state.sw cmd;
799 let calcips h =
800 if conf.presentation
801 then
802 let d = conf.winh - h in
803 max 0 ((d + 1) / 2)
804 else
805 conf.interpagespace
808 let calcheight () =
809 let rec f pn ph pi fh l =
810 match l with
811 | (n, _, h, _) :: rest ->
812 let ips = calcips h in
813 let fh =
814 if conf.presentation
815 then fh+ips
816 else (
817 if isbirdseye state.mode && pn = 0
818 then fh + ips
819 else fh
822 let fh = fh + ((n - pn) * (ph + pi)) in
823 f n h ips fh rest;
825 | [] ->
826 let inc =
827 if conf.presentation || (isbirdseye state.mode && pn = 0)
828 then 0
829 else -pi
831 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
832 max 0 fh
834 let fh = f 0 0 0 0 state.pdims in
838 let calcheight () =
839 match conf.columns with
840 | None -> calcheight ()
841 | Some (_, b) ->
842 if Array.length b > 0
843 then
844 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
845 y + h
846 else 0
849 let getpageyh pageno =
850 let rec f pn ph pi y l =
851 match l with
852 | (n, _, h, _) :: rest ->
853 let ips = calcips h in
854 if n >= pageno
855 then
856 let h = if n = pageno then h else ph in
857 if conf.presentation && n = pageno
858 then
859 y + (pageno - pn) * (ph + pi) + pi, h
860 else
861 y + (pageno - pn) * (ph + pi), h
862 else
863 let y = y + (if conf.presentation then pi else 0) in
864 let y = y + (n - pn) * (ph + pi) in
865 f n h ips y rest
867 | [] ->
868 y + (pageno - pn) * (ph + pi), ph
870 f 0 0 0 0 state.pdims
873 let getpageyh pageno =
874 match conf.columns with
875 | None -> getpageyh pageno
876 | Some (_, b) ->
877 let (_, _, y, (_, _, h, _)) = b.(pageno) in
878 y, h
881 let getpagedim pageno =
882 let rec f ppdim l =
883 match l with
884 | (n, _, _, _) as pdim :: rest ->
885 if n >= pageno
886 then (if n = pageno then pdim else ppdim)
887 else f pdim rest
889 | [] -> ppdim
891 f (-1, -1, -1, -1) state.pdims
894 let getpagey pageno = fst (getpageyh pageno);;
896 let layout1 y sh =
897 let sh = sh - state.hscrollh in
898 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
899 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
900 match pdims with
901 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
902 let ips = calcips h in
903 let yinc =
904 if conf.presentation || (isbirdseye state.mode && pageno = 0)
905 then ips
906 else 0
908 (w, h, ips, xoff), rest, pdimno + 1, yinc
909 | _ ->
910 prev, pdims, pdimno, 0
912 let dy = dy + yinc in
913 let py = py + yinc in
914 if pageno = state.pagecount || dy >= sh
915 then
916 accu
917 else
918 let vy = y + dy in
919 if py + h <= vy - yinc
920 then
921 let py = py + h + ips in
922 let dy = max 0 (py - y) in
923 f ~pageno:(pageno+1)
924 ~pdimno
925 ~prev:curr
928 ~pdims:rest
929 ~accu
930 else
931 let pagey = vy - py in
932 let pagevh = h - pagey in
933 let pagevh = min (sh - dy) pagevh in
934 let off = if yinc > 0 then py - vy else 0 in
935 let py = py + h + ips in
936 let pagex, dx =
937 let xoff = xoff +
938 if state.w < conf.winw - state.scrollw
939 then (conf.winw - state.scrollw - state.w) / 2
940 else 0
942 let dispx = xoff + state.x in
943 if dispx < 0
944 then (-dispx, 0)
945 else (0, dispx)
947 let pagevw =
948 let lw = w - pagex in
949 min lw (conf.winw - state.scrollw)
951 let e =
952 { pageno = pageno
953 ; pagedimno = pdimno
954 ; pagew = w
955 ; pageh = h
956 ; pagex = pagex
957 ; pagey = pagey + off
958 ; pagevw = pagevw
959 ; pagevh = pagevh - off
960 ; pagedispx = dx
961 ; pagedispy = dy + off
964 let accu = e :: accu in
965 f ~pageno:(pageno+1)
966 ~pdimno
967 ~prev:curr
969 ~dy:(dy+pagevh+ips)
970 ~pdims:rest
971 ~accu
973 if state.invalidated = 0
974 then (
975 let accu =
977 ~pageno:0
978 ~pdimno:~-1
979 ~prev:(0,0,0,0)
980 ~py:0
981 ~dy:0
982 ~pdims:state.pdims
983 ~accu:[]
985 List.rev accu
987 else
991 let layoutN ((columns, coverA, coverB), b) y sh =
992 let sh = sh - state.hscrollh in
993 let rec fold accu n =
994 if n = Array.length b
995 then accu
996 else
997 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
998 if (vy - y) > sh &&
999 (n = coverA - 1
1000 || n = state.pagecount - coverB
1001 || (n - coverA) mod columns = columns - 1)
1002 then accu
1003 else
1004 let accu =
1005 if vy + h > y
1006 then
1007 let pagey = max 0 (y - vy) in
1008 let pagedispy = if pagey > 0 then 0 else vy - y in
1009 let pagedispx, pagex, pagevw =
1010 let pdx =
1011 if n = coverA - 1 || n = state.pagecount - coverB
1012 then state.x + (conf.winw - state.scrollw - w) / 2
1013 else dx + xoff + state.x
1015 if pdx < 0
1016 then 0, -pdx, w + pdx
1017 else pdx, 0, min (conf.winw - state.scrollw) w
1019 let pagevh = min (h - pagey) (sh - pagedispy) in
1020 if pagedispx < conf.winw - state.scrollw && pagevw > 0 && pagevh > 0
1021 then
1022 let e =
1023 { pageno = n
1024 ; pagedimno = pdimno
1025 ; pagew = w
1026 ; pageh = h
1027 ; pagex = pagex
1028 ; pagey = pagey
1029 ; pagevw = pagevw
1030 ; pagevh = pagevh
1031 ; pagedispx = pagedispx
1032 ; pagedispy = pagedispy
1035 e :: accu
1036 else
1037 accu
1038 else
1039 accu
1041 fold accu (n+1)
1043 if state.invalidated = 0
1044 then List.rev (fold [] 0)
1045 else []
1048 let layout y sh =
1049 match conf.columns with
1050 | None -> layout1 y sh
1051 | Some c -> layoutN c y sh
1054 let clamp incr =
1055 let y = state.y + incr in
1056 let y = max 0 y in
1057 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1061 let getopaque pageno =
1062 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
1063 with Not_found -> None
1066 let putopaque pageno opaque =
1067 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
1070 let itertiles l f =
1071 let tilex = l.pagex mod conf.tilew in
1072 let tiley = l.pagey mod conf.tileh in
1074 let col = l.pagex / conf.tilew in
1075 let row = l.pagey / conf.tileh in
1077 let vw =
1078 let a = l.pagew - l.pagex in
1079 let b = conf.winw - state.scrollw in
1080 min a b
1081 and vh = l.pagevh in
1083 let rec rowloop row y0 dispy h =
1084 if h = 0
1085 then ()
1086 else (
1087 let dh = conf.tileh - y0 in
1088 let dh = min h dh in
1089 let rec colloop col x0 dispx w =
1090 if w = 0
1091 then ()
1092 else (
1093 let dw = conf.tilew - x0 in
1094 let dw = min w dw in
1096 f col row dispx dispy x0 y0 dw dh;
1097 colloop (col+1) 0 (dispx+dw) (w-dw)
1100 colloop col tilex l.pagedispx vw;
1101 rowloop (row+1) 0 (dispy+dh) (h-dh)
1104 if vw > 0 && vh > 0
1105 then rowloop row tiley l.pagedispy vh;
1108 let gettileopaque l col row =
1109 let key =
1110 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1112 try Some (Hashtbl.find state.tilemap key)
1113 with Not_found -> None
1116 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1117 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1118 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1121 let drawtiles l color =
1122 GlDraw.color color;
1123 let f col row x y tilex tiley w h =
1124 match gettileopaque l col row with
1125 | Some (opaque, _, t) ->
1126 let params = x, y, w, h, tilex, tiley in
1127 if conf.invert
1128 then (
1129 Gl.enable `blend;
1130 GlFunc.blend_func `zero `one_minus_src_color;
1132 drawtile params opaque;
1133 if conf.invert
1134 then Gl.disable `blend;
1135 if conf.debug
1136 then (
1137 let s = Printf.sprintf
1138 "%d[%d,%d] %f sec"
1139 l.pageno col row t
1141 let w = measurestr fstate.fontsize s in
1142 GlMisc.push_attrib [`current];
1143 GlDraw.color (0.0, 0.0, 0.0);
1144 GlDraw.rect
1145 (float (x-2), float (y-2))
1146 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1147 GlDraw.color (1.0, 1.0, 1.0);
1148 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1149 GlMisc.pop_attrib ();
1152 | _ ->
1153 let w =
1154 let lw = conf.winw - state.scrollw - x in
1155 min lw w
1156 and h =
1157 let lh = conf.winh - y in
1158 min lh h
1160 Gl.enable `texture_2d;
1161 begin match state.texid with
1162 | Some id ->
1163 GlTex.bind_texture `texture_2d id;
1164 let x0 = float x
1165 and y0 = float y
1166 and x1 = float (x+w)
1167 and y1 = float (y+h) in
1169 let tw = float w /. 64.0
1170 and th = float h /. 64.0 in
1171 let tx0 = float tilex /. 64.0
1172 and ty0 = float tiley /. 64.0 in
1173 let tx1 = tx0 +. tw
1174 and ty1 = ty0 +. th in
1175 GlDraw.begins `quads;
1176 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1177 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1178 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1179 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1180 GlDraw.ends ();
1182 Gl.disable `texture_2d;
1183 | None ->
1184 GlDraw.color (1.0, 1.0, 1.0);
1185 GlDraw.rect
1186 (float x, float y)
1187 (float (x+w), float (y+h));
1188 end;
1189 if w > 128 && h > fstate.fontsize + 10
1190 then (
1191 GlDraw.color (0.0, 0.0, 0.0);
1192 let c, r =
1193 if conf.verbose
1194 then (col*conf.tilew, row*conf.tileh)
1195 else col, row
1197 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1199 GlDraw.color color;
1201 itertiles l f
1204 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1206 let tilevisible1 l x y =
1207 let ax0 = l.pagex
1208 and ax1 = l.pagex + l.pagevw
1209 and ay0 = l.pagey
1210 and ay1 = l.pagey + l.pagevh in
1212 let bx0 = x
1213 and by0 = y in
1214 let bx1 = min (bx0 + conf.tilew) l.pagew
1215 and by1 = min (by0 + conf.tileh) l.pageh in
1217 let rx0 = max ax0 bx0
1218 and ry0 = max ay0 by0
1219 and rx1 = min ax1 bx1
1220 and ry1 = min ay1 by1 in
1222 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1223 nonemptyintersection
1226 let tilevisible layout n x y =
1227 let rec findpageinlayout = function
1228 | l :: _ when l.pageno = n -> tilevisible1 l x y
1229 | _ :: rest -> findpageinlayout rest
1230 | [] -> false
1232 findpageinlayout layout
1235 let tileready l x y =
1236 tilevisible1 l x y &&
1237 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1240 let tilepage n p layout =
1241 let rec loop = function
1242 | l :: rest ->
1243 if l.pageno = n
1244 then
1245 let f col row _ _ _ _ _ _ =
1246 if state.currently = Idle
1247 then
1248 match gettileopaque l col row with
1249 | Some _ -> ()
1250 | None ->
1251 let x = col*conf.tilew
1252 and y = row*conf.tileh in
1253 let w =
1254 let w = l.pagew - x in
1255 min w conf.tilew
1257 let h =
1258 let h = l.pageh - y in
1259 min h conf.tileh
1261 wcmd "tile"
1262 [`s p
1263 ;`i x
1264 ;`i y
1265 ;`i w
1266 ;`i h
1268 state.currently <-
1269 Tiling (
1270 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1271 conf.tilew, conf.tileh
1274 itertiles l f;
1275 else
1276 loop rest
1278 | [] -> ()
1280 if state.invalidated = 0 then loop layout;
1283 let preloadlayout visiblepages =
1284 let presentation = conf.presentation in
1285 let interpagespace = conf.interpagespace in
1286 let maxy = state.maxy in
1287 conf.presentation <- false;
1288 conf.interpagespace <- 0;
1289 state.maxy <- calcheight ();
1290 let y =
1291 match visiblepages with
1292 | [] -> 0
1293 | l :: _ -> getpagey l.pageno + l.pagey
1295 let y = if y < conf.winh then 0 else y - conf.winh in
1296 let h = state.y - y + conf.winh*3 in
1297 let pages = layout y h in
1298 conf.presentation <- presentation;
1299 conf.interpagespace <- interpagespace;
1300 state.maxy <- maxy;
1301 pages;
1304 let load pages =
1305 let rec loop pages =
1306 if state.currently != Idle
1307 then ()
1308 else
1309 match pages with
1310 | l :: rest ->
1311 begin match getopaque l.pageno with
1312 | None ->
1313 wcmd "page" [`i l.pageno; `i l.pagedimno];
1314 state.currently <- Loading (l, state.gen);
1315 | Some opaque ->
1316 tilepage l.pageno opaque pages;
1317 loop rest
1318 end;
1319 | _ -> ()
1321 if state.invalidated = 0 then loop pages
1324 let preload pages =
1325 load pages;
1326 if conf.preload && state.currently = Idle
1327 then load (preloadlayout pages);
1330 let layoutready layout =
1331 let rec fold all ls =
1332 all && match ls with
1333 | l :: rest ->
1334 let seen = ref false in
1335 let allvisible = ref true in
1336 let foo col row _ _ _ _ _ _ =
1337 seen := true;
1338 allvisible := !allvisible &&
1339 begin match gettileopaque l col row with
1340 | Some _ -> true
1341 | None -> false
1344 itertiles l foo;
1345 fold (!seen && !allvisible) rest
1346 | [] -> true
1348 let alltilesvisible = fold true layout in
1349 alltilesvisible;
1352 let gotoy y =
1353 let y = bound y 0 state.maxy in
1354 let y, layout, proceed =
1355 match conf.maxwait with
1356 | Some time when state.ghyll == noghyll ->
1357 begin match state.throttle with
1358 | None ->
1359 let layout = layout y conf.winh in
1360 let ready = layoutready layout in
1361 if not ready
1362 then (
1363 load layout;
1364 state.throttle <- Some (layout, y, now ());
1366 else G.postRedisplay "gotoy showall (None)";
1367 y, layout, ready
1368 | Some (_, _, started) ->
1369 let dt = now () -. started in
1370 if dt > time
1371 then (
1372 state.throttle <- None;
1373 let layout = layout y conf.winh in
1374 load layout;
1375 G.postRedisplay "maxwait";
1376 y, layout, true
1378 else -1, [], false
1381 | _ ->
1382 let layout = layout y conf.winh in
1383 if true || layoutready layout
1384 then G.postRedisplay "gotoy ready";
1385 y, layout, true
1387 if proceed
1388 then (
1389 state.y <- y;
1390 state.layout <- layout;
1391 begin match state.mode with
1392 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1393 if not (pagevisible layout pageno)
1394 then (
1395 match state.layout with
1396 | [] -> ()
1397 | l :: _ ->
1398 state.mode <- Birdseye (
1399 conf, leftx, l.pageno, hooverpageno, anchor
1402 | _ -> ()
1403 end;
1404 preload layout;
1406 state.ghyll <- noghyll;
1409 let conttiling pageno opaque =
1410 tilepage pageno opaque
1411 (if conf.preload then preloadlayout state.layout else state.layout)
1414 let gotoy_and_clear_text y =
1415 gotoy y;
1416 if not conf.verbose then state.text <- "";
1419 let getanchor () =
1420 match state.layout with
1421 | [] -> emptyanchor
1422 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1425 let getanchory (n, top) =
1426 let y, h = getpageyh n in
1427 y + (truncate (top *. float h));
1430 let gotoanchor anchor =
1431 gotoy (getanchory anchor);
1434 let addnav () =
1435 cbput state.hists.nav (getanchor ());
1438 let getnav dir =
1439 let anchor = cbgetc state.hists.nav dir in
1440 getanchory anchor;
1443 let gotoghyll y =
1444 let rec scroll f n a b =
1445 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1446 let snake f a b =
1447 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1448 if f < a
1449 then s (float f /. float a)
1450 else (
1451 if f > b
1452 then 1.0 -. s ((float (f-b) /. float (n-b)))
1453 else 1.0
1456 snake f a b
1457 and summa f n a b =
1458 (* courtesy:
1459 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1460 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1461 let iv1 = iv f in
1462 let ins = float a *. iv1
1463 and outs = float (n-b) *. iv1 in
1464 let ones = b - a in
1465 ins +. outs +. float ones
1467 let rec set (_N, _A, _B) y sy =
1468 let sum = summa 1.0 _N _A _B in
1469 let dy = float (y - sy) in
1470 state.ghyll <- (
1471 let rec gf n y1 o =
1472 if n >= _N
1473 then state.ghyll <- noghyll
1474 else
1475 let go n =
1476 let s = scroll n _N _A _B in
1477 let y1 = y1 +. ((s *. dy) /. sum) in
1478 gotoy_and_clear_text (truncate y1);
1479 state.ghyll <- gf (n+1) y1;
1481 match o with
1482 | None -> go n
1483 | Some y' -> set (_N/2, 0, 0) y' state.y
1485 gf 0 (float state.y)
1488 match conf.ghyllscroll with
1489 | None ->
1490 gotoy_and_clear_text y
1491 | Some nab ->
1492 if state.ghyll == noghyll
1493 then set nab y state.y
1494 else state.ghyll (Some y)
1497 let gotopage n top =
1498 let y, h = getpageyh n in
1499 let y = y + (truncate (top *. float h)) in
1500 gotoghyll y
1503 let gotopage1 n top =
1504 let y = getpagey n in
1505 let y = y + top in
1506 gotoghyll y
1509 let invalidate () =
1510 state.layout <- [];
1511 state.pdims <- [];
1512 state.rects <- [];
1513 state.rects1 <- [];
1514 state.invalidated <- state.invalidated + 1;
1517 let writeopen path password =
1518 writecmd state.sw ("open " ^ path ^ "\000" ^ password ^ "\000");
1521 let opendoc path password =
1522 invalidate ();
1523 state.path <- path;
1524 state.password <- password;
1525 state.gen <- state.gen + 1;
1526 state.docinfo <- [];
1528 setaalevel conf.aalevel;
1529 writeopen path password;
1530 Glut.setWindowTitle ("llpp " ^ Filename.basename path);
1531 wcmd "geometry" [`i state.w; `i conf.winh];
1534 let scalecolor c =
1535 let c = c *. conf.colorscale in
1536 (c, c, c);
1539 let scalecolor2 (r, g, b) =
1540 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1543 let represent () =
1544 let docolumns = function
1545 | None -> ()
1546 | Some ((columns, coverA, coverB), _) ->
1547 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1548 let rec loop pageno pdimno pdim x y rowh pdims =
1549 if pageno = state.pagecount
1550 then ()
1551 else
1552 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1553 match pdims with
1554 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1555 pdimno+1, pdim, rest
1556 | _ ->
1557 pdimno, pdim, pdims
1559 let x, y, rowh' =
1560 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1561 then (
1562 (conf.winw - state.scrollw - w) / 2,
1563 y + rowh + conf.interpagespace, h
1565 else (
1566 if (pageno - coverA) mod columns = 0
1567 then 0, y + rowh + conf.interpagespace, h
1568 else x, y, max rowh h
1571 let rec fixrow m = if m = pageno then () else
1572 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1573 if h < rowh
1574 then (
1575 let y = y + (rowh - h) / 2 in
1576 a.(m) <- (pdimno, x, y, pdim);
1578 fixrow (m+1)
1580 if pageno > 1 && (pageno - coverA) mod columns = 0
1581 then fixrow (pageno - columns);
1582 a.(pageno) <- (pdimno, x, y, pdim);
1583 let x = x + w + xoff*2 + conf.interpagespace in
1584 loop (pageno+1) pdimno pdim x y rowh' pdims
1586 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1587 conf.columns <- Some ((columns, coverA, coverB), a);
1589 docolumns conf.columns;
1590 state.maxy <- calcheight ();
1591 state.hscrollh <-
1592 if state.w <= conf.winw - state.scrollw
1593 then 0
1594 else state.scrollw
1596 match state.mode with
1597 | Birdseye (_, _, pageno, _, _) ->
1598 let y, h = getpageyh pageno in
1599 let top = (conf.winh - h) / 2 in
1600 gotoy (max 0 (y - top))
1601 | _ -> gotoanchor state.anchor
1604 let reshape =
1605 let firsttime = ref true in
1606 fun ~w ~h ->
1607 GlDraw.viewport 0 0 w h;
1608 if state.invalidated = 0 && not !firsttime
1609 then state.anchor <- getanchor ();
1611 firsttime := false;
1612 conf.winw <- w;
1613 let w = truncate (float w *. conf.zoom) - state.scrollw in
1614 let w = max w 2 in
1615 state.w <- w;
1616 conf.winh <- h;
1617 setfontsize fstate.fontsize;
1618 GlMat.mode `modelview;
1619 GlMat.load_identity ();
1621 GlMat.mode `projection;
1622 GlMat.load_identity ();
1623 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1624 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1625 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1627 let w =
1628 match conf.columns with
1629 | None -> w
1630 | Some ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1632 invalidate ();
1633 wcmd "geometry" [`i w; `i h];
1636 let enttext () =
1637 let len = String.length state.text in
1638 let drawstring s =
1639 let hscrollh =
1640 match state.mode with
1641 | View -> state.hscrollh
1642 | _ -> 0
1644 let rect x w =
1645 GlDraw.rect
1646 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1647 (x+.w, float (conf.winh - hscrollh))
1650 let w = float (conf.winw - state.scrollw - 1) in
1651 if state.progress >= 0.0 && state.progress < 1.0
1652 then (
1653 GlDraw.color (0.3, 0.3, 0.3);
1654 let w1 = w *. state.progress in
1655 rect 0.0 w1;
1656 GlDraw.color (0.0, 0.0, 0.0);
1657 rect w1 (w-.w1)
1659 else (
1660 GlDraw.color (0.0, 0.0, 0.0);
1661 rect 0.0 w;
1664 GlDraw.color (1.0, 1.0, 1.0);
1665 drawstring fstate.fontsize
1666 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1668 let s =
1669 match state.mode with
1670 | Textentry ((prefix, text, _, _, _), _) ->
1671 let s =
1672 if len > 0
1673 then
1674 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1675 else
1676 Printf.sprintf "%s%s_" prefix text
1680 | _ -> state.text
1682 let s =
1683 if state.newerrmsgs
1684 then (
1685 if not (istextentry state.mode)
1686 then
1687 let s1 = "(press 'e' to review error messasges)" in
1688 if String.length s > 0 then s ^ " " ^ s1 else s1
1689 else s
1691 else s
1693 if String.length s > 0
1694 then drawstring s
1697 let showtext c s =
1698 state.text <- Printf.sprintf "%c%s" c s;
1699 G.postRedisplay "showtext";
1702 let gctiles () =
1703 let len = Queue.length state.tilelru in
1704 let rec loop qpos =
1705 if state.memused <= conf.memlimit
1706 then ()
1707 else (
1708 if qpos < len
1709 then
1710 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1711 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1712 let (_, pw, ph, _) = getpagedim n in
1714 gen = state.gen
1715 && colorspace = conf.colorspace
1716 && angle = conf.angle
1717 && pagew = pw
1718 && pageh = ph
1719 && (
1720 let layout =
1721 match state.throttle with
1722 | None ->
1723 if conf.preload
1724 then preloadlayout state.layout
1725 else state.layout
1726 | Some (layout, _, _) ->
1727 layout
1729 let x = col*conf.tilew
1730 and y = row*conf.tileh in
1731 tilevisible layout n x y
1733 then Queue.push lruitem state.tilelru
1734 else (
1735 wcmd "freetile" [`s p];
1736 state.memused <- state.memused - s;
1737 state.uioh#infochanged Memused;
1738 Hashtbl.remove state.tilemap k;
1740 loop (qpos+1)
1743 loop 0
1746 let flushtiles () =
1747 Queue.iter (fun (k, p, s) ->
1748 wcmd "freetile" [`s p];
1749 state.memused <- state.memused - s;
1750 state.uioh#infochanged Memused;
1751 Hashtbl.remove state.tilemap k;
1752 ) state.tilelru;
1753 Queue.clear state.tilelru;
1754 load state.layout;
1757 let logcurrently = function
1758 | Idle -> dolog "Idle"
1759 | Loading (l, gen) ->
1760 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1761 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1762 dolog
1763 "Tiling %d[%d,%d] page=%s cs=%s angle"
1764 l.pageno col row pageopaque
1765 (colorspace_to_string colorspace)
1767 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1768 angle gen conf.angle state.gen
1769 tilew tileh
1770 conf.tilew conf.tileh
1772 | Outlining _ ->
1773 dolog "outlining"
1776 let act cmds =
1777 (* dolog "%S" cmds; *)
1778 let op, args =
1779 let spacepos =
1780 try String.index cmds ' '
1781 with Not_found -> -1
1783 if spacepos = -1
1784 then cmds, ""
1785 else
1786 let l = String.length cmds in
1787 let op = String.sub cmds 0 spacepos in
1788 op, begin
1789 if l - spacepos < 2 then ""
1790 else String.sub cmds (spacepos+1) (l-spacepos-1)
1793 match op with
1794 | "clear" ->
1795 state.uioh#infochanged Pdim;
1796 state.pdims <- [];
1798 | "clearrects" ->
1799 state.rects <- state.rects1;
1800 G.postRedisplay "clearrects";
1802 | "continue" ->
1803 let n =
1804 try Scanf.sscanf args "%u" (fun n -> n)
1805 with exn ->
1806 dolog "error processing 'continue' %S: %s"
1807 cmds (Printexc.to_string exn);
1808 exit 1;
1810 state.pagecount <- n;
1811 state.invalidated <- state.invalidated - 1;
1812 begin match state.currently with
1813 | Outlining l ->
1814 state.currently <- Idle;
1815 state.outlines <- Array.of_list (List.rev l)
1816 | _ -> ()
1817 end;
1818 if state.invalidated = 0
1819 then represent ();
1820 if conf.maxwait = None
1821 then G.postRedisplay "continue";
1823 | "title" ->
1824 Glut.setWindowTitle args
1826 | "msg" ->
1827 showtext ' ' args
1829 | "vmsg" ->
1830 if conf.verbose
1831 then showtext ' ' args
1833 | "progress" ->
1834 let progress, text =
1836 Scanf.sscanf args "%f %n"
1837 (fun f pos ->
1838 f, String.sub args pos (String.length args - pos))
1839 with exn ->
1840 dolog "error processing 'progress' %S: %s"
1841 cmds (Printexc.to_string exn);
1842 exit 1;
1844 state.text <- text;
1845 state.progress <- progress;
1846 G.postRedisplay "progress"
1848 | "firstmatch" ->
1849 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1851 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1852 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1853 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1854 with exn ->
1855 dolog "error processing 'firstmatch' %S: %s"
1856 cmds (Printexc.to_string exn);
1857 exit 1;
1859 let y = (getpagey pageno) + truncate y0 in
1860 addnav ();
1861 gotoy y;
1862 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1864 | "match" ->
1865 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1867 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1868 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1869 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1870 with exn ->
1871 dolog "error processing 'match' %S: %s"
1872 cmds (Printexc.to_string exn);
1873 exit 1;
1875 state.rects1 <-
1876 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1878 | "page" ->
1879 let pageopaque, t =
1881 Scanf.sscanf args "%s %f" (fun p t -> p, t)
1882 with exn ->
1883 dolog "error processing 'page' %S: %s"
1884 cmds (Printexc.to_string exn);
1885 exit 1;
1887 begin match state.currently with
1888 | Loading (l, gen) ->
1889 vlog "page %d took %f sec" l.pageno t;
1890 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1891 begin match state.throttle with
1892 | None ->
1893 let preloadedpages =
1894 if conf.preload
1895 then preloadlayout state.layout
1896 else state.layout
1898 let evict () =
1899 let module IntSet =
1900 Set.Make (struct type t = int let compare = (-) end) in
1901 let set =
1902 List.fold_left (fun s l -> IntSet.add l.pageno s)
1903 IntSet.empty preloadedpages
1905 let evictedpages =
1906 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1907 if not (IntSet.mem pageno set)
1908 then (
1909 wcmd "freepage" [`s opaque];
1910 key :: accu
1912 else accu
1913 ) state.pagemap []
1915 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1917 evict ();
1918 state.currently <- Idle;
1919 if gen = state.gen
1920 then (
1921 tilepage l.pageno pageopaque state.layout;
1922 load state.layout;
1923 load preloadedpages;
1924 if pagevisible state.layout l.pageno
1925 && layoutready state.layout
1926 then G.postRedisplay "page";
1929 | Some (layout, _, _) ->
1930 state.currently <- Idle;
1931 tilepage l.pageno pageopaque layout;
1932 load state.layout
1933 end;
1935 | _ ->
1936 dolog "Inconsistent loading state";
1937 logcurrently state.currently;
1938 raise Quit;
1941 | "tile" ->
1942 let (x, y, opaque, size, t) =
1944 Scanf.sscanf args "%u %u %s %u %f"
1945 (fun x y p size t -> (x, y, p, size, t))
1946 with exn ->
1947 dolog "error processing 'tile' %S: %s"
1948 cmds (Printexc.to_string exn);
1949 exit 1;
1951 begin match state.currently with
1952 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1953 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1955 if tilew != conf.tilew || tileh != conf.tileh
1956 then (
1957 wcmd "freetile" [`s opaque];
1958 state.currently <- Idle;
1959 load state.layout;
1961 else (
1962 puttileopaque l col row gen cs angle opaque size t;
1963 state.memused <- state.memused + size;
1964 state.uioh#infochanged Memused;
1965 gctiles ();
1966 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1967 opaque, size) state.tilelru;
1969 let layout =
1970 match state.throttle with
1971 | None -> state.layout
1972 | Some (layout, _, _) -> layout
1975 state.currently <- Idle;
1976 if gen = state.gen
1977 && conf.colorspace = cs
1978 && conf.angle = angle
1979 && tilevisible layout l.pageno x y
1980 then conttiling l.pageno pageopaque;
1982 begin match state.throttle with
1983 | None ->
1984 preload state.layout;
1985 if gen = state.gen
1986 && conf.colorspace = cs
1987 && conf.angle = angle
1988 && tilevisible state.layout l.pageno x y
1989 then G.postRedisplay "tile nothrottle";
1991 | Some (layout, y, _) ->
1992 let ready = layoutready layout in
1993 if ready
1994 then (
1995 state.y <- y;
1996 state.layout <- layout;
1997 state.throttle <- None;
1998 G.postRedisplay "throttle";
2000 else load layout;
2001 end;
2004 | _ ->
2005 dolog "Inconsistent tiling state";
2006 logcurrently state.currently;
2007 raise Quit;
2010 | "pdim" ->
2011 let pdim =
2013 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2014 with exn ->
2015 dolog "error processing 'pdim' %S: %s"
2016 cmds (Printexc.to_string exn);
2017 exit 1;
2019 state.uioh#infochanged Pdim;
2020 state.pdims <- pdim :: state.pdims
2022 | "o" ->
2023 let (l, n, t, h, pos) =
2025 Scanf.sscanf args "%u %u %d %u %n"
2026 (fun l n t h pos -> l, n, t, h, pos)
2027 with exn ->
2028 dolog "error processing 'o' %S: %s"
2029 cmds (Printexc.to_string exn);
2030 exit 1;
2032 let s = String.sub args pos (String.length args - pos) in
2033 let outline = (s, l, (n, float t /. float h)) in
2034 begin match state.currently with
2035 | Outlining outlines ->
2036 state.currently <- Outlining (outline :: outlines)
2037 | Idle ->
2038 state.currently <- Outlining [outline]
2039 | currently ->
2040 dolog "invalid outlining state";
2041 logcurrently currently
2044 | "info" ->
2045 state.docinfo <- (1, args) :: state.docinfo
2047 | "infoend" ->
2048 state.uioh#infochanged Docinfo;
2049 state.docinfo <- List.rev state.docinfo
2051 | _ ->
2052 dolog "unknown cmd `%S'" cmds
2055 let idle () =
2056 if state.deadline == nan then state.deadline <- now ();
2057 let r =
2058 match state.errfd with
2059 | None -> [state.sr]
2060 | Some fd -> [state.sr; fd]
2062 let rec loop delay =
2063 let deadline =
2064 if state.ghyll == noghyll
2065 then state.deadline
2066 else now () +. 0.02
2068 let timeout =
2069 if delay > 0.0
2070 then max 0.0 (deadline -. now ())
2071 else 0.0
2073 let r, _, _ =
2074 try Unix.select r [] [] timeout
2075 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [] ,[]
2077 begin match r with
2078 | [] ->
2079 state.ghyll None;
2080 begin match state.autoscroll with
2081 | Some step when step != 0 ->
2082 let y = state.y + step in
2083 let y =
2084 if y < 0
2085 then state.maxy
2086 else if y >= state.maxy then 0 else y
2088 gotoy y;
2089 if state.mode = View
2090 then state.text <- "";
2091 state.deadline <- state.deadline +. 0.005;
2093 | _ ->
2094 state.deadline <- state.deadline +. delay;
2095 end;
2097 | l ->
2098 let rec checkfds c = function
2099 | [] -> c
2100 | fd :: rest when fd = state.sr ->
2101 let cmd = readcmd state.sr in
2102 act cmd;
2103 checkfds true rest
2104 | fd :: rest ->
2105 let s = String.create 80 in
2106 let n = Unix.read fd s 0 80 in
2107 if conf.redirectstderr || is_gui
2108 then (
2109 Buffer.add_substring state.errmsgs s 0 n;
2110 state.newerrmsgs <- true;
2111 Glut.postRedisplay ();
2113 else (
2114 prerr_string (String.sub s 0 n);
2115 flush stderr;
2117 checkfds c rest
2119 if checkfds false l
2120 then loop 0.0
2121 end;
2122 in loop 0.007
2125 let onhist cb =
2126 let rc = cb.rc in
2127 let action = function
2128 | HCprev -> cbget cb ~-1
2129 | HCnext -> cbget cb 1
2130 | HCfirst -> cbget cb ~-(cb.rc)
2131 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2132 and cancel () = cb.rc <- rc
2133 in (action, cancel)
2136 let search pattern forward =
2137 if String.length pattern > 0
2138 then
2139 let pn, py =
2140 match state.layout with
2141 | [] -> 0, 0
2142 | l :: _ ->
2143 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2145 let cmd =
2146 let b = makecmd "search"
2147 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
2149 Buffer.add_char b ',';
2150 Buffer.add_string b pattern;
2151 Buffer.add_char b '\000';
2152 Buffer.contents b;
2154 writecmd state.sw cmd;
2157 let intentry text key =
2158 let c = Char.unsafe_chr key in
2159 match c with
2160 | '0' .. '9' ->
2161 let text = addchar text c in
2162 TEcont text
2164 | _ ->
2165 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2166 TEcont text
2169 let textentry text key =
2170 let c = Char.unsafe_chr key in
2171 match c with
2172 | _ when key >= 32 && key < 127 ->
2173 let text = addchar text c in
2174 TEcont text
2176 | _ ->
2177 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
2178 TEcont text
2181 let reqlayout angle proportional =
2182 match state.throttle with
2183 | None ->
2184 if state.invalidated = 0 then state.anchor <- getanchor ();
2185 conf.angle <- angle mod 360;
2186 conf.proportional <- proportional;
2187 invalidate ();
2188 wcmd "reqlayout" [`i conf.angle; `b proportional];
2189 | _ -> ()
2192 let settrim trimmargins trimfuzz =
2193 if state.invalidated = 0 then state.anchor <- getanchor ();
2194 conf.trimmargins <- trimmargins;
2195 conf.trimfuzz <- trimfuzz;
2196 let x0, y0, x1, y1 = trimfuzz in
2197 invalidate ();
2198 wcmd "settrim" [
2199 `b conf.trimmargins;
2200 `i x0;
2201 `i y0;
2202 `i x1;
2203 `i y1;
2205 Hashtbl.iter (fun _ opaque ->
2206 wcmd "freepage" [`s opaque];
2207 ) state.pagemap;
2208 Hashtbl.clear state.pagemap;
2211 let setzoom zoom =
2212 match state.throttle with
2213 | None ->
2214 let zoom = max 0.01 zoom in
2215 if zoom <> conf.zoom
2216 then (
2217 state.prevzoom <- conf.zoom;
2218 let relx =
2219 if zoom <= 1.0
2220 then (state.x <- 0; 0.0)
2221 else float state.x /. float state.w
2223 conf.zoom <- zoom;
2224 reshape conf.winw conf.winh;
2225 if zoom > 1.0
2226 then (
2227 let x = relx *. float state.w in
2228 state.x <- truncate x;
2230 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2233 | Some (layout, y, started) ->
2234 let time =
2235 match conf.maxwait with
2236 | None -> 0.0
2237 | Some t -> t
2239 let dt = now () -. started in
2240 if dt > time
2241 then (
2242 state.y <- y;
2243 load layout;
2247 let setcolumns columns coverA coverB =
2248 if columns < 2
2249 then (
2250 conf.columns <- None;
2251 state.x <- 0;
2252 setzoom 1.0;
2254 else (
2255 conf.columns <- Some ((columns, coverA, coverB), [||]);
2256 conf.zoom <- 1.0;
2258 reshape conf.winw conf.winh;
2261 let enterbirdseye () =
2262 let zoom = float conf.thumbw /. float conf.winw in
2263 let birdseyepageno =
2264 let cy = conf.winh / 2 in
2265 let fold = function
2266 | [] -> 0
2267 | l :: rest ->
2268 let rec fold best = function
2269 | [] -> best.pageno
2270 | l :: rest ->
2271 let d = cy - (l.pagedispy + l.pagevh/2)
2272 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2273 if abs d < abs dbest
2274 then fold l rest
2275 else best.pageno
2276 in fold l rest
2278 fold state.layout
2280 state.mode <- Birdseye (
2281 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2283 conf.zoom <- zoom;
2284 conf.presentation <- false;
2285 conf.interpagespace <- 10;
2286 conf.hlinks <- false;
2287 state.x <- 0;
2288 state.mstate <- Mnone;
2289 conf.maxwait <- None;
2290 conf.columns <- (
2291 match conf.beyecolumns with
2292 | Some c ->
2293 conf.zoom <- 1.0;
2294 Some ((c, 0, 0), [||])
2295 | None -> None
2297 Glut.setCursor Glut.CURSOR_INHERIT;
2298 if conf.verbose
2299 then
2300 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2301 (100.0*.zoom)
2302 else
2303 state.text <- ""
2305 reshape conf.winw conf.winh;
2308 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2309 state.mode <- View;
2310 conf.zoom <- c.zoom;
2311 conf.presentation <- c.presentation;
2312 conf.interpagespace <- c.interpagespace;
2313 conf.maxwait <- c.maxwait;
2314 conf.hlinks <- c.hlinks;
2315 conf.beyecolumns <- (
2316 match conf.columns with
2317 | Some ((c, _, _), _) -> Some c
2318 | None -> None
2320 conf.columns <- (
2321 match c.columns with
2322 | Some (c, _) -> Some (c, [||])
2323 | None -> None
2325 state.x <- leftx;
2326 if conf.verbose
2327 then
2328 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2329 (100.0*.conf.zoom)
2331 reshape conf.winw conf.winh;
2332 state.anchor <- if goback then anchor else (pageno, 0.0);
2335 let togglebirdseye () =
2336 match state.mode with
2337 | Birdseye vals -> leavebirdseye vals true
2338 | View -> enterbirdseye ()
2339 | _ -> ()
2342 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2343 let pageno = max 0 (pageno - incr) in
2344 let rec loop = function
2345 | [] -> gotopage1 pageno 0
2346 | l :: _ when l.pageno = pageno ->
2347 if l.pagedispy >= 0 && l.pagey = 0
2348 then G.postRedisplay "upbirdseye"
2349 else gotopage1 pageno 0
2350 | _ :: rest -> loop rest
2352 loop state.layout;
2353 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2356 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2357 let pageno = min (state.pagecount - 1) (pageno + incr) in
2358 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2359 let rec loop = function
2360 | [] ->
2361 let y, h = getpageyh pageno in
2362 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2363 gotoy (clamp dy)
2364 | l :: _ when l.pageno = pageno ->
2365 if l.pagevh != l.pageh
2366 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2367 else G.postRedisplay "downbirdseye"
2368 | _ :: rest -> loop rest
2370 loop state.layout
2373 let optentry mode _ key =
2374 let btos b = if b then "on" else "off" in
2375 let c = Char.unsafe_chr key in
2376 match c with
2377 | 's' ->
2378 let ondone s =
2379 try conf.scrollstep <- int_of_string s with exc ->
2380 state.text <- Printf.sprintf "bad integer `%s': %s"
2381 s (Printexc.to_string exc)
2383 TEswitch ("scroll step: ", "", None, intentry, ondone)
2385 | 'A' ->
2386 let ondone s =
2388 conf.autoscrollstep <- int_of_string s;
2389 if state.autoscroll <> None
2390 then state.autoscroll <- Some conf.autoscrollstep
2391 with exc ->
2392 state.text <- Printf.sprintf "bad integer `%s': %s"
2393 s (Printexc.to_string exc)
2395 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2397 | 'C' ->
2398 let ondone s =
2400 let n, a, b = columns_of_string s in
2401 setcolumns n a b;
2402 with exc ->
2403 state.text <- Printf.sprintf "bad columns `%s': %s"
2404 s (Printexc.to_string exc)
2406 TEswitch ("columns: ", "", None, textentry, ondone)
2408 | 'Z' ->
2409 let ondone s =
2411 let zoom = float (int_of_string s) /. 100.0 in
2412 setzoom zoom
2413 with exc ->
2414 state.text <- Printf.sprintf "bad integer `%s': %s"
2415 s (Printexc.to_string exc)
2417 TEswitch ("zoom: ", "", None, intentry, ondone)
2419 | 't' ->
2420 let ondone s =
2422 conf.thumbw <- bound (int_of_string s) 2 4096;
2423 state.text <-
2424 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2425 begin match mode with
2426 | Birdseye beye ->
2427 leavebirdseye beye false;
2428 enterbirdseye ();
2429 | _ -> ();
2431 with exc ->
2432 state.text <- Printf.sprintf "bad integer `%s': %s"
2433 s (Printexc.to_string exc)
2435 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2437 | 'R' ->
2438 let ondone s =
2439 match try
2440 Some (int_of_string s)
2441 with exc ->
2442 state.text <- Printf.sprintf "bad integer `%s': %s"
2443 s (Printexc.to_string exc);
2444 None
2445 with
2446 | Some angle -> reqlayout angle conf.proportional
2447 | None -> ()
2449 TEswitch ("rotation: ", "", None, intentry, ondone)
2451 | 'i' ->
2452 conf.icase <- not conf.icase;
2453 TEdone ("case insensitive search " ^ (btos conf.icase))
2455 | 'p' ->
2456 conf.preload <- not conf.preload;
2457 gotoy state.y;
2458 TEdone ("preload " ^ (btos conf.preload))
2460 | 'v' ->
2461 conf.verbose <- not conf.verbose;
2462 TEdone ("verbose " ^ (btos conf.verbose))
2464 | 'd' ->
2465 conf.debug <- not conf.debug;
2466 TEdone ("debug " ^ (btos conf.debug))
2468 | 'h' ->
2469 conf.maxhfit <- not conf.maxhfit;
2470 state.maxy <-
2471 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2472 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2474 | 'c' ->
2475 conf.crophack <- not conf.crophack;
2476 TEdone ("crophack " ^ btos conf.crophack)
2478 | 'a' ->
2479 let s =
2480 match conf.maxwait with
2481 | None ->
2482 conf.maxwait <- Some infinity;
2483 "always wait for page to complete"
2484 | Some _ ->
2485 conf.maxwait <- None;
2486 "show placeholder if page is not ready"
2488 TEdone s
2490 | 'f' ->
2491 conf.underinfo <- not conf.underinfo;
2492 TEdone ("underinfo " ^ btos conf.underinfo)
2494 | 'P' ->
2495 conf.savebmarks <- not conf.savebmarks;
2496 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2498 | 'S' ->
2499 let ondone s =
2501 let pageno, py =
2502 match state.layout with
2503 | [] -> 0, 0
2504 | l :: _ ->
2505 l.pageno, l.pagey
2507 conf.interpagespace <- int_of_string s;
2508 state.maxy <- calcheight ();
2509 let y = getpagey pageno in
2510 gotoy (y + py)
2511 with exc ->
2512 state.text <- Printf.sprintf "bad integer `%s': %s"
2513 s (Printexc.to_string exc)
2515 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2517 | 'l' ->
2518 reqlayout conf.angle (not conf.proportional);
2519 TEdone ("proportional display " ^ btos conf.proportional)
2521 | 'T' ->
2522 settrim (not conf.trimmargins) conf.trimfuzz;
2523 TEdone ("trim margins " ^ btos conf.trimmargins)
2525 | 'I' ->
2526 conf.invert <- not conf.invert;
2527 TEdone ("invert colors " ^ btos conf.invert)
2529 | 'x' ->
2530 let ondone s =
2531 cbput state.hists.sel s;
2532 conf.selcmd <- s;
2534 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2535 textentry, ondone)
2537 | _ ->
2538 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2539 TEstop
2542 class type lvsource = object
2543 method getitemcount : int
2544 method getitem : int -> (string * int)
2545 method hasaction : int -> bool
2546 method exit :
2547 uioh:uioh ->
2548 cancel:bool ->
2549 active:int ->
2550 first:int ->
2551 pan:int ->
2552 qsearch:string ->
2553 uioh option
2554 method getactive : int
2555 method getfirst : int
2556 method getqsearch : string
2557 method setqsearch : string -> unit
2558 method getpan : int
2559 end;;
2561 class virtual lvsourcebase = object
2562 val mutable m_active = 0
2563 val mutable m_first = 0
2564 val mutable m_qsearch = ""
2565 val mutable m_pan = 0
2566 method getactive = m_active
2567 method getfirst = m_first
2568 method getqsearch = m_qsearch
2569 method getpan = m_pan
2570 method setqsearch s = m_qsearch <- s
2571 end;;
2573 let textentryspecial key = function
2574 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2575 let s =
2576 match key with
2577 | Glut.KEY_UP -> action HCprev
2578 | Glut.KEY_DOWN -> action HCnext
2579 | Glut.KEY_HOME -> action HCfirst
2580 | Glut.KEY_END -> action HClast
2581 | _ -> state.text
2583 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2584 G.postRedisplay "special textentry";
2585 | _ -> ()
2588 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2589 let enttext te =
2590 state.mode <- Textentry (te, onleave);
2591 state.text <- "";
2592 enttext ();
2593 G.postRedisplay "textentrykeyboard enttext";
2595 match Char.unsafe_chr key with
2596 | '\008' -> (* backspace *)
2597 let len = String.length text in
2598 if len = 0
2599 then (
2600 onleave Cancel;
2601 G.postRedisplay "textentrykeyboard after cancel";
2603 else (
2604 let s = String.sub text 0 (len - 1) in
2605 enttext (c, s, opthist, onkey, ondone)
2608 | '\r' | '\n' ->
2609 ondone text;
2610 onleave Confirm;
2611 G.postRedisplay "textentrykeyboard after confirm"
2613 | '\007' (* ctrl-g *)
2614 | '\027' -> (* escape *)
2615 if String.length text = 0
2616 then (
2617 begin match opthist with
2618 | None -> ()
2619 | Some (_, onhistcancel) -> onhistcancel ()
2620 end;
2621 onleave Cancel;
2622 state.text <- "";
2623 G.postRedisplay "textentrykeyboard after cancel2"
2625 else (
2626 enttext (c, "", opthist, onkey, ondone)
2629 | '\127' -> () (* delete *)
2631 | _ ->
2632 begin match onkey text key with
2633 | TEdone text ->
2634 ondone text;
2635 onleave Confirm;
2636 G.postRedisplay "textentrykeyboard after confirm2";
2638 | TEcont text ->
2639 enttext (c, text, opthist, onkey, ondone);
2641 | TEstop ->
2642 onleave Cancel;
2643 G.postRedisplay "textentrykeyboard after cancel3"
2645 | TEswitch te ->
2646 state.mode <- Textentry (te, onleave);
2647 G.postRedisplay "textentrykeyboard switch";
2648 end;
2651 let firstof first active =
2652 if first > active || abs (first - active) > fstate.maxrows - 1
2653 then max 0 (active - (fstate.maxrows/2))
2654 else first
2657 let calcfirst first active =
2658 if active > first
2659 then
2660 let rows = active - first in
2661 if rows > fstate.maxrows then active - fstate.maxrows else first
2662 else active
2665 let scrollph y maxy =
2666 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2667 let sh = float conf.winh /. sh in
2668 let sh = max sh (float conf.scrollh) in
2670 let percent =
2671 if y = state.maxy
2672 then 1.0
2673 else float y /. float maxy
2675 let position = (float conf.winh -. sh) *. percent in
2677 let position =
2678 if position +. sh > float conf.winh
2679 then float conf.winh -. sh
2680 else position
2682 position, sh;
2685 let coe s = (s :> uioh);;
2687 class listview ~(source:lvsource) ~trusted =
2688 object (self)
2689 val m_pan = source#getpan
2690 val m_first = source#getfirst
2691 val m_active = source#getactive
2692 val m_qsearch = source#getqsearch
2693 val m_prev_uioh = state.uioh
2695 method private elemunder y =
2696 let n = y / (fstate.fontsize+1) in
2697 if m_first + n < source#getitemcount
2698 then (
2699 if source#hasaction (m_first + n)
2700 then Some (m_first + n)
2701 else None
2703 else None
2705 method display =
2706 Gl.enable `blend;
2707 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2708 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2709 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2710 GlDraw.color (1., 1., 1.);
2711 Gl.enable `texture_2d;
2712 let fs = fstate.fontsize in
2713 let nfs = fs + 1 in
2714 let ww = fstate.wwidth in
2715 let tabw = 30.0*.ww in
2716 let itemcount = source#getitemcount in
2717 let rec loop row =
2718 if (row - m_first) * nfs > conf.winh
2719 then ()
2720 else (
2721 if row >= 0 && row < itemcount
2722 then (
2723 let (s, level) = source#getitem row in
2724 let y = (row - m_first) * nfs in
2725 let x = 5.0 +. float (level + m_pan) *. ww in
2726 if row = m_active
2727 then (
2728 Gl.disable `texture_2d;
2729 GlDraw.polygon_mode `both `line;
2730 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2731 GlDraw.rect (1., float (y + 1))
2732 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2733 GlDraw.polygon_mode `both `fill;
2734 GlDraw.color (1., 1., 1.);
2735 Gl.enable `texture_2d;
2738 let drawtabularstring s =
2739 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2740 if trusted
2741 then
2742 let tabpos = try String.index s '\t' with Not_found -> -1 in
2743 if tabpos > 0
2744 then
2745 let len = String.length s - tabpos - 1 in
2746 let s1 = String.sub s 0 tabpos
2747 and s2 = String.sub s (tabpos + 1) len in
2748 let nx = drawstr x s1 in
2749 let sw = nx -. x in
2750 let x = x +. (max tabw sw) in
2751 drawstr x s2
2752 else
2753 drawstr x s
2754 else
2755 drawstr x s
2757 let _ = drawtabularstring s in
2758 loop (row+1)
2762 loop m_first;
2763 Gl.disable `blend;
2764 Gl.disable `texture_2d;
2766 method updownlevel incr =
2767 let len = source#getitemcount in
2768 let curlevel =
2769 if m_active >= 0 && m_active < len
2770 then snd (source#getitem m_active)
2771 else -1
2773 let rec flow i =
2774 if i = len then i-1 else if i = -1 then 0 else
2775 let _, l = source#getitem i in
2776 if l != curlevel then i else flow (i+incr)
2778 let active = flow m_active in
2779 let first = calcfirst m_first active in
2780 G.postRedisplay "special outline updownlevel";
2781 {< m_active = active; m_first = first >}
2783 method private key1 key =
2784 let set active first qsearch =
2785 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2787 let search active pattern incr =
2788 let dosearch re =
2789 let rec loop n =
2790 if n >= 0 && n < source#getitemcount
2791 then (
2792 let s, _ = source#getitem n in
2794 (try ignore (Str.search_forward re s 0); true
2795 with Not_found -> false)
2796 then Some n
2797 else loop (n + incr)
2799 else None
2801 loop active
2804 let re = Str.regexp_case_fold pattern in
2805 dosearch re
2806 with Failure s ->
2807 state.text <- s;
2808 None
2810 match key with
2811 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2812 let incr = if key = 18 then -1 else 1 in
2813 let active, first =
2814 match search (m_active + incr) m_qsearch incr with
2815 | None ->
2816 state.text <- m_qsearch ^ " [not found]";
2817 m_active, m_first
2818 | Some active ->
2819 state.text <- m_qsearch;
2820 active, firstof m_first active
2822 G.postRedisplay "listview ctrl-r/s";
2823 set active first m_qsearch;
2825 | 8 -> (* backspace *)
2826 let len = String.length m_qsearch in
2827 if len = 0
2828 then coe self
2829 else (
2830 if len = 1
2831 then (
2832 state.text <- "";
2833 G.postRedisplay "listview empty qsearch";
2834 set m_active m_first "";
2836 else
2837 let qsearch = String.sub m_qsearch 0 (len - 1) in
2838 let active, first =
2839 match search m_active qsearch ~-1 with
2840 | None ->
2841 state.text <- qsearch ^ " [not found]";
2842 m_active, m_first
2843 | Some active ->
2844 state.text <- qsearch;
2845 active, firstof m_first active
2847 G.postRedisplay "listview backspace qsearch";
2848 set active first qsearch
2851 | _ when key >= 32 && key < 127 ->
2852 let pattern = addchar m_qsearch (Char.chr key) in
2853 let active, first =
2854 match search m_active pattern 1 with
2855 | None ->
2856 state.text <- pattern ^ " [not found]";
2857 m_active, m_first
2858 | Some active ->
2859 state.text <- pattern;
2860 active, firstof m_first active
2862 G.postRedisplay "listview qsearch add";
2863 set active first pattern;
2865 | 27 -> (* escape *)
2866 state.text <- "";
2867 if String.length m_qsearch = 0
2868 then (
2869 G.postRedisplay "list view escape";
2870 begin
2871 match
2872 source#exit (coe self) true m_active m_first m_pan m_qsearch
2873 with
2874 | None -> m_prev_uioh
2875 | Some uioh -> uioh
2878 else (
2879 G.postRedisplay "list view kill qsearch";
2880 source#setqsearch "";
2881 coe {< m_qsearch = "" >}
2884 | 13 -> (* enter *)
2885 state.text <- "";
2886 let self = {< m_qsearch = "" >} in
2887 source#setqsearch "";
2888 let opt =
2889 G.postRedisplay "listview enter";
2890 if m_active >= 0 && m_active < source#getitemcount
2891 then (
2892 source#exit (coe self) false m_active m_first m_pan "";
2894 else (
2895 source#exit (coe self) true m_active m_first m_pan "";
2898 begin match opt with
2899 | None -> m_prev_uioh
2900 | Some uioh -> uioh
2903 | 127 -> (* delete *)
2904 coe self
2906 | _ -> dolog "unknown key %d" key; coe self
2908 method private special1 key =
2909 let itemcount = source#getitemcount in
2910 let find start incr =
2911 let rec find i =
2912 if i = -1 || i = itemcount
2913 then -1
2914 else (
2915 if source#hasaction i
2916 then i
2917 else find (i + incr)
2920 find start
2922 let set active first =
2923 let first = bound first 0 (itemcount - fstate.maxrows) in
2924 state.text <- "";
2925 coe {< m_active = active; m_first = first >}
2927 let navigate incr =
2928 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2929 let active, first =
2930 let incr1 = if incr > 0 then 1 else -1 in
2931 if isvisible m_first m_active
2932 then
2933 let next =
2934 let next = m_active + incr in
2935 let next =
2936 if next < 0 || next >= itemcount
2937 then -1
2938 else find next incr1
2940 if next = -1 || abs (m_active - next) > fstate.maxrows
2941 then -1
2942 else next
2944 if next = -1
2945 then
2946 let first = m_first + incr in
2947 let first = bound first 0 (itemcount - 1) in
2948 let next =
2949 let next = m_active + incr in
2950 let next = bound next 0 (itemcount - 1) in
2951 find next ~-incr1
2953 let active = if next = -1 then m_active else next in
2954 active, first
2955 else
2956 let first = min next m_first in
2957 let first =
2958 if abs (next - first) > fstate.maxrows
2959 then first + incr
2960 else first
2962 next, first
2963 else
2964 let first = m_first + incr in
2965 let first = bound first 0 (itemcount - 1) in
2966 let active =
2967 let next = m_active + incr in
2968 let next = bound next 0 (itemcount - 1) in
2969 let next = find next incr1 in
2970 let active =
2971 if next = -1 || abs (m_active - first) > fstate.maxrows
2972 then (
2973 let active = if m_active = -1 then next else m_active in
2974 active
2976 else next
2978 if isvisible first active
2979 then active
2980 else -1
2982 active, first
2984 G.postRedisplay "listview navigate";
2985 set active first;
2987 begin match key with
2988 | Glut.KEY_UP -> navigate ~-1
2989 | Glut.KEY_DOWN -> navigate 1
2990 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2991 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2993 | Glut.KEY_RIGHT ->
2994 state.text <- "";
2995 G.postRedisplay "listview right";
2996 coe {< m_pan = m_pan - 1 >}
2998 | Glut.KEY_LEFT ->
2999 state.text <- "";
3000 G.postRedisplay "listview left";
3001 coe {< m_pan = m_pan + 1 >}
3003 | Glut.KEY_HOME ->
3004 let active = find 0 1 in
3005 G.postRedisplay "listview home";
3006 set active 0;
3008 | Glut.KEY_END ->
3009 let first = max 0 (itemcount - fstate.maxrows) in
3010 let active = find (itemcount - 1) ~-1 in
3011 G.postRedisplay "listview end";
3012 set active first;
3014 | _ -> coe self
3015 end;
3017 method key key =
3018 match state.mode with
3019 | Textentry te -> textentrykeyboard key te; coe self
3020 | _ -> self#key1 key
3022 method special key =
3023 match state.mode with
3024 | Textentry te -> textentryspecial key te; coe self
3025 | _ -> self#special1 key
3027 method button button bstate x y =
3028 let opt =
3029 match button with
3030 | Glut.LEFT_BUTTON when x > conf.winw - conf.scrollbw ->
3031 G.postRedisplay "listview scroll";
3032 if bstate = Glut.DOWN
3033 then
3034 let _, position, sh = self#scrollph in
3035 if y > truncate position && y < truncate (position +. sh)
3036 then (
3037 state.mstate <- Mscrolly;
3038 Some (coe self)
3040 else
3041 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3042 let first = truncate (s *. float source#getitemcount) in
3043 let first = min source#getitemcount first in
3044 Some (coe {< m_first = first; m_active = first >})
3045 else (
3046 state.mstate <- Mnone;
3047 Some (coe self);
3049 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
3050 begin match self#elemunder y with
3051 | Some n ->
3052 G.postRedisplay "listview click";
3053 source#exit
3054 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3055 | _ ->
3056 Some (coe self)
3058 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
3059 let len = source#getitemcount in
3060 let first =
3061 if n = 4 && m_first + fstate.maxrows >= len
3062 then
3063 m_first
3064 else
3065 let first = m_first + (if n == 3 then -1 else 1) in
3066 bound first 0 (len - 1)
3068 G.postRedisplay "listview wheel";
3069 Some (coe {< m_first = first >})
3070 | _ ->
3071 Some (coe self)
3073 match opt with
3074 | None -> m_prev_uioh
3075 | Some uioh -> uioh
3077 method motion _ y =
3078 match state.mstate with
3079 | Mscrolly ->
3080 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3081 let first = truncate (s *. float source#getitemcount) in
3082 let first = min source#getitemcount first in
3083 G.postRedisplay "listview motion";
3084 coe {< m_first = first; m_active = first >}
3085 | _ -> coe self
3087 method pmotion x y =
3088 if x < conf.winw - conf.scrollbw
3089 then
3090 let n =
3091 match self#elemunder y with
3092 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
3093 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
3095 let o =
3096 if n != m_active
3097 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3098 else self
3100 coe o
3101 else (
3102 Glut.setCursor Glut.CURSOR_INHERIT;
3103 coe self
3106 method infochanged _ = ()
3108 method scrollpw = (0, 0.0, 0.0)
3109 method scrollph =
3110 let nfs = fstate.fontsize + 1 in
3111 let y = m_first * nfs in
3112 let itemcount = source#getitemcount in
3113 let maxi = max 0 (itemcount - fstate.maxrows) in
3114 let maxy = maxi * nfs in
3115 let p, h = scrollph y maxy in
3116 conf.scrollbw, p, h
3117 end;;
3119 class outlinelistview ~source =
3120 object (self)
3121 inherit listview ~source:(source :> lvsource) ~trusted:false as super
3123 method key key =
3124 match key with
3125 | 14 -> (* ctrl-n *)
3126 source#narrow m_qsearch;
3127 G.postRedisplay "outline ctrl-n";
3128 coe {< m_first = 0; m_active = 0 >}
3130 | 21 -> (* ctrl-u *)
3131 source#denarrow;
3132 G.postRedisplay "outline ctrl-u";
3133 state.text <- "";
3134 coe {< m_first = 0; m_active = 0 >}
3136 | 12 -> (* ctrl-l *)
3137 let first = m_active - (fstate.maxrows / 2) in
3138 G.postRedisplay "outline ctrl-l";
3139 coe {< m_first = first >}
3141 | 127 -> (* delete *)
3142 source#remove m_active;
3143 G.postRedisplay "outline delete";
3144 let active = max 0 (m_active-1) in
3145 coe {< m_first = firstof m_first active;
3146 m_active = active >}
3148 | key -> super#key key
3150 method special key =
3151 let calcfirst first active =
3152 if active > first
3153 then
3154 let rows = active - first in
3155 if rows > fstate.maxrows then active - fstate.maxrows else first
3156 else active
3158 let navigate incr =
3159 let active = m_active + incr in
3160 let active = bound active 0 (source#getitemcount - 1) in
3161 let first = calcfirst m_first active in
3162 G.postRedisplay "special outline navigate";
3163 coe {< m_active = active; m_first = first >}
3165 match key with
3166 | Glut.KEY_UP -> navigate ~-1
3167 | Glut.KEY_DOWN -> navigate 1
3168 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
3169 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
3171 | Glut.KEY_RIGHT ->
3172 let o =
3173 if Glut.getModifiers () land Glut.active_ctrl != 0
3174 then (
3175 G.postRedisplay "special outline right";
3176 {< m_pan = m_pan + 1 >}
3178 else self#updownlevel 1
3180 coe o
3182 | Glut.KEY_LEFT ->
3183 let o =
3184 if Glut.getModifiers () land Glut.active_ctrl != 0
3185 then (
3186 G.postRedisplay "special outline left";
3187 {< m_pan = m_pan - 1 >}
3189 else self#updownlevel ~-1
3191 coe o
3193 | Glut.KEY_HOME ->
3194 G.postRedisplay "special outline home";
3195 coe {< m_first = 0; m_active = 0 >}
3197 | Glut.KEY_END ->
3198 let active = source#getitemcount - 1 in
3199 let first = max 0 (active - fstate.maxrows) in
3200 G.postRedisplay "special outline end";
3201 coe {< m_active = active; m_first = first >}
3203 | _ -> super#special key
3206 let outlinesource usebookmarks =
3207 let empty = [||] in
3208 (object
3209 inherit lvsourcebase
3210 val mutable m_items = empty
3211 val mutable m_orig_items = empty
3212 val mutable m_prev_items = empty
3213 val mutable m_narrow_pattern = ""
3214 val mutable m_hadremovals = false
3216 method getitemcount =
3217 Array.length m_items + (if m_hadremovals then 1 else 0)
3219 method getitem n =
3220 if n == Array.length m_items && m_hadremovals
3221 then
3222 ("[Confirm removal]", 0)
3223 else
3224 let s, n, _ = m_items.(n) in
3225 (s, n)
3227 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3228 ignore (uioh, first, qsearch);
3229 let confrimremoval = m_hadremovals && active = Array.length m_items in
3230 let items =
3231 if String.length m_narrow_pattern = 0
3232 then m_orig_items
3233 else m_items
3235 if not cancel
3236 then (
3237 if not confrimremoval
3238 then(
3239 let _, _, anchor = m_items.(active) in
3240 gotoanchor anchor;
3241 m_items <- items;
3243 else (
3244 state.bookmarks <- Array.to_list m_items;
3245 m_orig_items <- m_items;
3248 else m_items <- items;
3249 m_pan <- pan;
3250 None
3252 method hasaction _ = true
3254 method greetmsg =
3255 if Array.length m_items != Array.length m_orig_items
3256 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3257 else ""
3259 method narrow pattern =
3260 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3261 match reopt with
3262 | None -> ()
3263 | Some re ->
3264 let rec loop accu n =
3265 if n = -1
3266 then (
3267 m_narrow_pattern <- pattern;
3268 m_items <- Array.of_list accu
3270 else
3271 let (s, _, _) as o = m_items.(n) in
3272 let accu =
3273 if (try ignore (Str.search_forward re s 0); true
3274 with Not_found -> false)
3275 then o :: accu
3276 else accu
3278 loop accu (n-1)
3280 loop [] (Array.length m_items - 1)
3282 method denarrow =
3283 m_orig_items <- (
3284 if usebookmarks
3285 then Array.of_list state.bookmarks
3286 else state.outlines
3288 m_items <- m_orig_items
3290 method remove m =
3291 if usebookmarks
3292 then
3293 if m >= 0 && m < Array.length m_items
3294 then (
3295 m_hadremovals <- true;
3296 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3297 let n = if n >= m then n+1 else n in
3298 m_items.(n)
3302 method reset anchor items =
3303 m_hadremovals <- false;
3304 if m_orig_items == empty || m_prev_items != items
3305 then (
3306 m_orig_items <- items;
3307 if String.length m_narrow_pattern = 0
3308 then m_items <- items;
3310 m_prev_items <- items;
3311 let rely = getanchory anchor in
3312 let active =
3313 let rec loop n best bestd =
3314 if n = Array.length m_items
3315 then best
3316 else
3317 let (_, _, anchor) = m_items.(n) in
3318 let orely = getanchory anchor in
3319 let d = abs (orely - rely) in
3320 if d < bestd
3321 then loop (n+1) n d
3322 else loop (n+1) best bestd
3324 loop 0 ~-1 max_int
3326 m_active <- active;
3327 m_first <- firstof m_first active
3328 end)
3331 let enterselector usebookmarks =
3332 let source = outlinesource usebookmarks in
3333 fun errmsg ->
3334 let outlines =
3335 if usebookmarks
3336 then Array.of_list state.bookmarks
3337 else state.outlines
3339 if Array.length outlines = 0
3340 then (
3341 showtext ' ' errmsg;
3343 else (
3344 state.text <- source#greetmsg;
3345 Glut.setCursor Glut.CURSOR_INHERIT;
3346 let anchor = getanchor () in
3347 source#reset anchor outlines;
3348 state.uioh <- coe (new outlinelistview ~source);
3349 G.postRedisplay "enter selector";
3353 let enteroutlinemode =
3354 let f = enterselector false in
3355 fun ()-> f "Document has no outline";
3358 let enterbookmarkmode =
3359 let f = enterselector true in
3360 fun () -> f "Document has no bookmarks (yet)";
3363 let color_of_string s =
3364 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3365 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3369 let color_to_string (r, g, b) =
3370 let r = truncate (r *. 256.0)
3371 and g = truncate (g *. 256.0)
3372 and b = truncate (b *. 256.0) in
3373 Printf.sprintf "%d/%d/%d" r g b
3376 let irect_of_string s =
3377 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3380 let irect_to_string (x0,y0,x1,y1) =
3381 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3384 let makecheckers () =
3385 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3386 following to say:
3387 converted by Issac Trotts. July 25, 2002 *)
3388 let image_height = 64
3389 and image_width = 64 in
3391 let make_image () =
3392 let image =
3393 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3395 for i = 0 to image_width - 1 do
3396 for j = 0 to image_height - 1 do
3397 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3398 (if (i land 8 ) lxor (j land 8) = 0
3399 then [|255;255;255|] else [|200;200;200|])
3400 done
3401 done;
3402 image
3404 let image = make_image () in
3405 let id = GlTex.gen_texture () in
3406 GlTex.bind_texture `texture_2d id;
3407 GlPix.store (`unpack_alignment 1);
3408 GlTex.image2d image;
3409 List.iter (GlTex.parameter ~target:`texture_2d)
3410 [ `wrap_s `repeat;
3411 `wrap_t `repeat;
3412 `mag_filter `nearest;
3413 `min_filter `nearest ];
3417 let setcheckers enabled =
3418 match state.texid with
3419 | None ->
3420 if enabled then state.texid <- Some (makecheckers ())
3422 | Some texid ->
3423 if not enabled
3424 then (
3425 GlTex.delete_texture texid;
3426 state.texid <- None;
3430 let int_of_string_with_suffix s =
3431 let l = String.length s in
3432 let s1, shift =
3433 if l > 1
3434 then
3435 let suffix = Char.lowercase s.[l-1] in
3436 match suffix with
3437 | 'k' -> String.sub s 0 (l-1), 10
3438 | 'm' -> String.sub s 0 (l-1), 20
3439 | 'g' -> String.sub s 0 (l-1), 30
3440 | _ -> s, 0
3441 else s, 0
3443 let n = int_of_string s1 in
3444 let m = n lsl shift in
3445 if m < 0 || m < n
3446 then raise (Failure "value too large")
3447 else m
3450 let string_with_suffix_of_int n =
3451 if n = 0
3452 then "0"
3453 else
3454 let n, s =
3455 if n = 0
3456 then 0, ""
3457 else (
3458 if n land ((1 lsl 20) - 1) = 0
3459 then n lsr 20, "M"
3460 else (
3461 if n land ((1 lsl 10) - 1) = 0
3462 then n lsr 10, "K"
3463 else n, ""
3467 let rec loop s n =
3468 let h = n mod 1000 in
3469 let n = n / 1000 in
3470 if n = 0
3471 then string_of_int h ^ s
3472 else (
3473 let s = Printf.sprintf "_%03d%s" h s in
3474 loop s n
3477 loop "" n ^ s;
3480 let defghyllscroll = (40, 8, 32);;
3481 let ghyllscroll_of_string s =
3482 let (n, a, b) as nab =
3483 if s = "default"
3484 then defghyllscroll
3485 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3487 if n <= a || n <= b || a >= b
3488 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3489 nab;
3492 let ghyllscroll_to_string ((n, a, b) as nab) =
3493 if nab = defghyllscroll
3494 then "default"
3495 else Printf.sprintf "%d,%d,%d" n a b;
3498 let describe_location () =
3499 let f (fn, _) l =
3500 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3502 let fn, ln = List.fold_left f (-1, -1) state.layout in
3503 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3504 let percent =
3505 if maxy <= 0
3506 then 100.
3507 else (100. *. (float state.y /. float maxy))
3509 if fn = ln
3510 then
3511 Printf.sprintf "page %d of %d [%.2f%%]"
3512 (fn+1) state.pagecount percent
3513 else
3514 Printf.sprintf
3515 "pages %d-%d of %d [%.2f%%]"
3516 (fn+1) (ln+1) state.pagecount percent
3519 let enterinfomode =
3520 let btos b = if b then "\xe2\x88\x9a" else "" in
3521 let showextended = ref false in
3522 let leave mode = function
3523 | Confirm -> state.mode <- mode
3524 | Cancel -> state.mode <- mode in
3525 let src =
3526 (object
3527 val mutable m_first_time = true
3528 val mutable m_l = []
3529 val mutable m_a = [||]
3530 val mutable m_prev_uioh = nouioh
3531 val mutable m_prev_mode = View
3533 inherit lvsourcebase
3535 method reset prev_mode prev_uioh =
3536 m_a <- Array.of_list (List.rev m_l);
3537 m_l <- [];
3538 m_prev_mode <- prev_mode;
3539 m_prev_uioh <- prev_uioh;
3540 if m_first_time
3541 then (
3542 let rec loop n =
3543 if n >= Array.length m_a
3544 then ()
3545 else
3546 match m_a.(n) with
3547 | _, _, _, Action _ -> m_active <- n
3548 | _ -> loop (n+1)
3550 loop 0;
3551 m_first_time <- false;
3554 method int name get set =
3555 m_l <-
3556 (name, `int get, 1, Action (
3557 fun u ->
3558 let ondone s =
3559 try set (int_of_string s)
3560 with exn ->
3561 state.text <- Printf.sprintf "bad integer `%s': %s"
3562 s (Printexc.to_string exn)
3564 state.text <- "";
3565 let te = name ^ ": ", "", None, intentry, ondone in
3566 state.mode <- Textentry (te, leave m_prev_mode);
3568 )) :: m_l
3570 method int_with_suffix name get set =
3571 m_l <-
3572 (name, `intws get, 1, Action (
3573 fun u ->
3574 let ondone s =
3575 try set (int_of_string_with_suffix s)
3576 with exn ->
3577 state.text <- Printf.sprintf "bad integer `%s': %s"
3578 s (Printexc.to_string exn)
3580 state.text <- "";
3581 let te =
3582 name ^ ": ", "", None, intentry_with_suffix, ondone
3584 state.mode <- Textentry (te, leave m_prev_mode);
3586 )) :: m_l
3588 method bool ?(offset=1) ?(btos=btos) name get set =
3589 m_l <-
3590 (name, `bool (btos, get), offset, Action (
3591 fun u ->
3592 let v = get () in
3593 set (not v);
3595 )) :: m_l
3597 method color name get set =
3598 m_l <-
3599 (name, `color get, 1, Action (
3600 fun u ->
3601 let invalid = (nan, nan, nan) in
3602 let ondone s =
3603 let c =
3604 try color_of_string s
3605 with exn ->
3606 state.text <- Printf.sprintf "bad color `%s': %s"
3607 s (Printexc.to_string exn);
3608 invalid
3610 if c <> invalid
3611 then set c;
3613 let te = name ^ ": ", "", None, textentry, ondone in
3614 state.text <- color_to_string (get ());
3615 state.mode <- Textentry (te, leave m_prev_mode);
3617 )) :: m_l
3619 method string name get set =
3620 m_l <-
3621 (name, `string get, 1, Action (
3622 fun u ->
3623 let ondone s = set s in
3624 let te = name ^ ": ", "", None, textentry, ondone in
3625 state.mode <- Textentry (te, leave m_prev_mode);
3627 )) :: m_l
3629 method colorspace name get set =
3630 m_l <-
3631 (name, `string get, 1, Action (
3632 fun _ ->
3633 let source =
3634 let vals = [| "rgb"; "bgr"; "gray" |] in
3635 (object
3636 inherit lvsourcebase
3638 initializer
3639 m_active <- int_of_colorspace conf.colorspace;
3640 m_first <- 0;
3642 method getitemcount = Array.length vals
3643 method getitem n = (vals.(n), 0)
3644 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3645 ignore (uioh, first, pan, qsearch);
3646 if not cancel then set active;
3647 None
3648 method hasaction _ = true
3649 end)
3651 state.text <- "";
3652 coe (new listview ~source ~trusted:true)
3653 )) :: m_l
3655 method caption s offset =
3656 m_l <- (s, `empty, offset, Noaction) :: m_l
3658 method caption2 s f offset =
3659 m_l <- (s, `string f, offset, Noaction) :: m_l
3661 method getitemcount = Array.length m_a
3663 method getitem n =
3664 let tostr = function
3665 | `int f -> string_of_int (f ())
3666 | `intws f -> string_with_suffix_of_int (f ())
3667 | `string f -> f ()
3668 | `color f -> color_to_string (f ())
3669 | `bool (btos, f) -> btos (f ())
3670 | `empty -> ""
3672 let name, t, offset, _ = m_a.(n) in
3673 ((let s = tostr t in
3674 if String.length s > 0
3675 then Printf.sprintf "%s\t%s" name s
3676 else name),
3677 offset)
3679 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3680 let uiohopt =
3681 if not cancel
3682 then (
3683 m_qsearch <- qsearch;
3684 let uioh =
3685 match m_a.(active) with
3686 | _, _, _, Action f -> f uioh
3687 | _ -> uioh
3689 Some uioh
3691 else None
3693 m_active <- active;
3694 m_first <- first;
3695 m_pan <- pan;
3696 uiohopt
3698 method hasaction n =
3699 match m_a.(n) with
3700 | _, _, _, Action _ -> true
3701 | _ -> false
3702 end)
3704 let rec fillsrc prevmode prevuioh =
3705 let sep () = src#caption "" 0 in
3706 let colorp name get set =
3707 src#string name
3708 (fun () -> color_to_string (get ()))
3709 (fun v ->
3711 let c = color_of_string v in
3712 set c
3713 with exn ->
3714 state.text <- Printf.sprintf "bad color `%s': %s"
3715 v (Printexc.to_string exn);
3718 let oldmode = state.mode in
3719 let birdseye = isbirdseye state.mode in
3721 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3723 src#bool "presentation mode"
3724 (fun () -> conf.presentation)
3725 (fun v ->
3726 conf.presentation <- v;
3727 state.anchor <- getanchor ();
3728 represent ());
3730 src#bool "ignore case in searches"
3731 (fun () -> conf.icase)
3732 (fun v -> conf.icase <- v);
3734 src#bool "preload"
3735 (fun () -> conf.preload)
3736 (fun v -> conf.preload <- v);
3738 src#bool "highlight links"
3739 (fun () -> conf.hlinks)
3740 (fun v -> conf.hlinks <- v);
3742 src#bool "under info"
3743 (fun () -> conf.underinfo)
3744 (fun v -> conf.underinfo <- v);
3746 src#bool "persistent bookmarks"
3747 (fun () -> conf.savebmarks)
3748 (fun v -> conf.savebmarks <- v);
3750 src#bool "proportional display"
3751 (fun () -> conf.proportional)
3752 (fun v -> reqlayout conf.angle v);
3754 src#bool "trim margins"
3755 (fun () -> conf.trimmargins)
3756 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3758 src#bool "persistent location"
3759 (fun () -> conf.jumpback)
3760 (fun v -> conf.jumpback <- v);
3762 sep ();
3763 src#int "inter-page space"
3764 (fun () -> conf.interpagespace)
3765 (fun n ->
3766 conf.interpagespace <- n;
3767 let pageno, py =
3768 match state.layout with
3769 | [] -> 0, 0
3770 | l :: _ ->
3771 l.pageno, l.pagey
3773 state.maxy <- calcheight ();
3774 let y = getpagey pageno in
3775 gotoy (y + py)
3778 src#int "page bias"
3779 (fun () -> conf.pagebias)
3780 (fun v -> conf.pagebias <- v);
3782 src#int "scroll step"
3783 (fun () -> conf.scrollstep)
3784 (fun n -> conf.scrollstep <- n);
3786 src#int "auto scroll step"
3787 (fun () ->
3788 match state.autoscroll with
3789 | Some step -> step
3790 | _ -> conf.autoscrollstep)
3791 (fun n ->
3792 if state.autoscroll <> None
3793 then state.autoscroll <- Some n;
3794 conf.autoscrollstep <- n);
3796 src#int "zoom"
3797 (fun () -> truncate (conf.zoom *. 100.))
3798 (fun v -> setzoom ((float v) /. 100.));
3800 src#int "rotation"
3801 (fun () -> conf.angle)
3802 (fun v -> reqlayout v conf.proportional);
3804 src#int "scroll bar width"
3805 (fun () -> state.scrollw)
3806 (fun v ->
3807 state.scrollw <- v;
3808 conf.scrollbw <- v;
3809 reshape conf.winw conf.winh;
3812 src#int "scroll handle height"
3813 (fun () -> conf.scrollh)
3814 (fun v -> conf.scrollh <- v;);
3816 src#int "thumbnail width"
3817 (fun () -> conf.thumbw)
3818 (fun v ->
3819 conf.thumbw <- min 4096 v;
3820 match oldmode with
3821 | Birdseye beye ->
3822 leavebirdseye beye false;
3823 enterbirdseye ()
3824 | _ -> ()
3827 src#string "columns"
3828 (fun () ->
3829 match conf.columns with
3830 | None -> "1"
3831 | Some (multicol, _) -> columns_to_string multicol)
3832 (fun v ->
3833 let n, a, b = columns_of_string v in
3834 setcolumns n a b);
3836 sep ();
3837 src#caption "Presentation mode" 0;
3838 src#bool "scrollbar visible"
3839 (fun () -> conf.scrollbarinpm)
3840 (fun v ->
3841 if v != conf.scrollbarinpm
3842 then (
3843 conf.scrollbarinpm <- v;
3844 if conf.presentation
3845 then (
3846 state.scrollw <- if v then conf.scrollbw else 0;
3847 reshape conf.winw conf.winh;
3852 sep ();
3853 src#caption "Pixmap cache" 0;
3854 src#int_with_suffix "size (advisory)"
3855 (fun () -> conf.memlimit)
3856 (fun v -> conf.memlimit <- v);
3858 src#caption2 "used"
3859 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3860 (string_with_suffix_of_int state.memused)
3861 (Hashtbl.length state.tilemap)) 1;
3863 sep ();
3864 src#caption "Layout" 0;
3865 src#caption2 "Dimension"
3866 (fun () ->
3867 Printf.sprintf "%dx%d (virtual %dx%d)"
3868 conf.winw conf.winh
3869 state.w state.maxy)
3871 if conf.debug
3872 then
3873 src#caption2 "Position" (fun () ->
3874 Printf.sprintf "%dx%d" state.x state.y
3876 else
3877 src#caption2 "Visible" (fun () -> describe_location ()) 1
3880 sep ();
3881 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3882 "Save these parameters as global defaults at exit"
3883 (fun () -> conf.bedefault)
3884 (fun v -> conf.bedefault <- v)
3887 sep ();
3888 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3889 src#bool ~offset:0 ~btos "Extended parameters"
3890 (fun () -> !showextended)
3891 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3892 if !showextended
3893 then (
3894 src#bool "checkers"
3895 (fun () -> conf.checkers)
3896 (fun v -> conf.checkers <- v; setcheckers v);
3897 src#bool "verbose"
3898 (fun () -> conf.verbose)
3899 (fun v -> conf.verbose <- v);
3900 src#bool "invert colors"
3901 (fun () -> conf.invert)
3902 (fun v -> conf.invert <- v);
3903 src#bool "max fit"
3904 (fun () -> conf.maxhfit)
3905 (fun v -> conf.maxhfit <- v);
3906 if not is_gui
3907 then
3908 src#bool "redirect stderr"
3909 (fun () -> conf.redirectstderr)
3910 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3911 src#string "uri launcher"
3912 (fun () -> conf.urilauncher)
3913 (fun v -> conf.urilauncher <- v);
3914 src#string "tile size"
3915 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3916 (fun v ->
3918 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3919 conf.tileh <- max 64 w;
3920 conf.tilew <- max 64 h;
3921 flushtiles ();
3922 with exn ->
3923 state.text <- Printf.sprintf "bad tile size `%s': %s"
3924 v (Printexc.to_string exn));
3925 src#int "texture count"
3926 (fun () -> conf.texcount)
3927 (fun v ->
3928 if realloctexts v
3929 then conf.texcount <- v
3930 else showtext '!' " Failed to set texture count please retry later"
3932 src#int "slice height"
3933 (fun () -> conf.sliceheight)
3934 (fun v ->
3935 conf.sliceheight <- v;
3936 wcmd "sliceh" [`i conf.sliceheight];
3938 src#int "anti-aliasing level"
3939 (fun () -> conf.aalevel)
3940 (fun v ->
3941 conf.aalevel <- bound v 0 8;
3942 state.anchor <- getanchor ();
3943 opendoc state.path state.password;
3945 src#int "ui font size"
3946 (fun () -> fstate.fontsize)
3947 (fun v -> setfontsize (bound v 5 100));
3948 colorp "background color"
3949 (fun () -> conf.bgcolor)
3950 (fun v -> conf.bgcolor <- v);
3951 src#bool "crop hack"
3952 (fun () -> conf.crophack)
3953 (fun v -> conf.crophack <- v);
3954 src#string "trim fuzz"
3955 (fun () -> irect_to_string conf.trimfuzz)
3956 (fun v ->
3958 conf.trimfuzz <- irect_of_string v;
3959 if conf.trimmargins
3960 then settrim true conf.trimfuzz;
3961 with exn ->
3962 state.text <- Printf.sprintf "bad irect `%s': %s"
3963 v (Printexc.to_string exn)
3965 src#string "throttle"
3966 (fun () ->
3967 match conf.maxwait with
3968 | None -> "show place holder if page is not ready"
3969 | Some time ->
3970 if time = infinity
3971 then "wait for page to fully render"
3972 else
3973 "wait " ^ string_of_float time
3974 ^ " seconds before showing placeholder"
3976 (fun v ->
3978 let f = float_of_string v in
3979 if f <= 0.0
3980 then conf.maxwait <- None
3981 else conf.maxwait <- Some f
3982 with exn ->
3983 state.text <- Printf.sprintf "bad time `%s': %s"
3984 v (Printexc.to_string exn)
3986 src#string "ghyll scroll"
3987 (fun () ->
3988 match conf.ghyllscroll with
3989 | None -> ""
3990 | Some nab -> ghyllscroll_to_string nab
3992 (fun v ->
3994 let gs =
3995 if String.length v = 0
3996 then None
3997 else Some (ghyllscroll_of_string v)
3999 conf.ghyllscroll <- gs
4000 with exn ->
4001 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4002 v (Printexc.to_string exn)
4004 src#string "selection command"
4005 (fun () -> conf.selcmd)
4006 (fun v -> conf.selcmd <- v);
4007 src#colorspace "color space"
4008 (fun () -> colorspace_to_string conf.colorspace)
4009 (fun v ->
4010 conf.colorspace <- colorspace_of_int v;
4011 wcmd "cs" [`i v];
4012 load state.layout;
4016 sep ();
4017 src#caption "Document" 0;
4018 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4019 src#caption2 "Pages"
4020 (fun () -> string_of_int state.pagecount) 1;
4021 src#caption2 "Dimensions"
4022 (fun () -> string_of_int (List.length state.pdims)) 1;
4023 if conf.trimmargins
4024 then (
4025 sep ();
4026 src#caption "Trimmed margins" 0;
4027 src#caption2 "Dimensions"
4028 (fun () -> string_of_int (List.length state.pdims)) 1;
4031 src#reset prevmode prevuioh;
4033 fun () ->
4034 state.text <- "";
4035 let prevmode = state.mode
4036 and prevuioh = state.uioh in
4037 fillsrc prevmode prevuioh;
4038 let source = (src :> lvsource) in
4039 state.uioh <- coe (object (self)
4040 inherit listview ~source ~trusted:true as super
4041 val mutable m_prevmemused = 0
4042 method infochanged = function
4043 | Memused ->
4044 if m_prevmemused != state.memused
4045 then (
4046 m_prevmemused <- state.memused;
4047 G.postRedisplay "memusedchanged";
4049 | Pdim -> G.postRedisplay "pdimchanged"
4050 | Docinfo -> fillsrc prevmode prevuioh
4052 method special key =
4053 if Glut.getModifiers () land Glut.active_ctrl = 0
4054 then
4055 match key with
4056 | Glut.KEY_LEFT -> coe (self#updownlevel ~-1)
4057 | Glut.KEY_RIGHT -> coe (self#updownlevel 1)
4058 | _ -> super#special key
4059 else super#special key
4060 end);
4061 G.postRedisplay "info";
4064 let enterhelpmode =
4065 let source =
4066 (object
4067 inherit lvsourcebase
4068 method getitemcount = Array.length state.help
4069 method getitem n =
4070 let s, n, _ = state.help.(n) in
4071 (s, n)
4073 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4074 let optuioh =
4075 if not cancel
4076 then (
4077 m_qsearch <- qsearch;
4078 match state.help.(active) with
4079 | _, _, Action f -> Some (f uioh)
4080 | _ -> Some (uioh)
4082 else None
4084 m_active <- active;
4085 m_first <- first;
4086 m_pan <- pan;
4087 optuioh
4089 method hasaction n =
4090 match state.help.(n) with
4091 | _, _, Action _ -> true
4092 | _ -> false
4094 initializer
4095 m_active <- -1
4096 end)
4097 in fun () ->
4098 state.uioh <- coe (new listview ~source ~trusted:true);
4099 G.postRedisplay "help";
4102 let entermsgsmode =
4103 let msgsource =
4104 let re = Str.regexp "[\r\n]" in
4105 (object
4106 inherit lvsourcebase
4107 val mutable m_items = [||]
4109 method getitemcount = 1 + Array.length m_items
4111 method getitem n =
4112 if n = 0
4113 then "[Clear]", 0
4114 else m_items.(n-1), 0
4116 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4117 ignore uioh;
4118 if not cancel
4119 then (
4120 if active = 0
4121 then Buffer.clear state.errmsgs;
4122 m_qsearch <- qsearch;
4124 m_active <- active;
4125 m_first <- first;
4126 m_pan <- pan;
4127 None
4129 method hasaction n =
4130 n = 0
4132 method reset =
4133 state.newerrmsgs <- false;
4134 let l = Str.split re (Buffer.contents state.errmsgs) in
4135 m_items <- Array.of_list l
4137 initializer
4138 m_active <- 0
4139 end)
4140 in fun () ->
4141 state.text <- "";
4142 msgsource#reset;
4143 let source = (msgsource :> lvsource) in
4144 state.uioh <- coe (object
4145 inherit listview ~source ~trusted:false as super
4146 method display =
4147 if state.newerrmsgs
4148 then msgsource#reset;
4149 super#display
4150 end);
4151 G.postRedisplay "msgs";
4154 let quickbookmark ?title () =
4155 match state.layout with
4156 | [] -> ()
4157 | l :: _ ->
4158 let title =
4159 match title with
4160 | None ->
4161 let sec = Unix.gettimeofday () in
4162 let tm = Unix.localtime sec in
4163 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4164 (l.pageno+1)
4165 tm.Unix.tm_mday
4166 tm.Unix.tm_mon
4167 (tm.Unix.tm_year + 1900)
4168 tm.Unix.tm_hour
4169 tm.Unix.tm_min
4170 | Some title -> title
4172 state.bookmarks <-
4173 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4174 :: state.bookmarks
4177 let doreshape w h =
4178 state.fullscreen <- None;
4179 Glut.reshapeWindow w h;
4182 let viewkeyboard key =
4183 let enttext te =
4184 let mode = state.mode in
4185 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4186 state.text <- "";
4187 enttext ();
4188 G.postRedisplay "view:enttext"
4190 let c = Char.chr key in
4191 match c with
4192 | '\027' | 'q' -> (* escape *)
4193 begin match state.mstate with
4194 | Mzoomrect _ ->
4195 state.mstate <- Mnone;
4196 Glut.setCursor Glut.CURSOR_INHERIT;
4197 G.postRedisplay "kill zoom rect";
4198 | _ ->
4199 match state.ranchors with
4200 | [] -> raise Quit
4201 | (path, password, anchor) :: rest ->
4202 state.ranchors <- rest;
4203 state.anchor <- anchor;
4204 opendoc path password
4205 end;
4207 | '\008' -> (* backspace *)
4208 let y = getnav ~-1 in
4209 gotoy_and_clear_text y
4211 | 'o' ->
4212 enteroutlinemode ()
4214 | 'u' ->
4215 state.rects <- [];
4216 state.text <- "";
4217 G.postRedisplay "dehighlight";
4219 | '/' | '?' ->
4220 let ondone isforw s =
4221 cbput state.hists.pat s;
4222 state.searchpattern <- s;
4223 search s isforw
4225 let s = String.create 1 in
4226 s.[0] <- c;
4227 enttext (s, "", Some (onhist state.hists.pat),
4228 textentry, ondone (c ='/'))
4230 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4231 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4232 setzoom (conf.zoom +. incr)
4234 | '+' ->
4235 let ondone s =
4236 let n =
4237 try int_of_string s with exc ->
4238 state.text <- Printf.sprintf "bad integer `%s': %s"
4239 s (Printexc.to_string exc);
4240 max_int
4242 if n != max_int
4243 then (
4244 conf.pagebias <- n;
4245 state.text <- "page bias is now " ^ string_of_int n;
4248 enttext ("page bias: ", "", None, intentry, ondone)
4250 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4251 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4252 setzoom (max 0.01 (conf.zoom -. decr))
4254 | '-' ->
4255 let ondone msg = state.text <- msg in
4256 enttext (
4257 "option [acfhilpstvxACPRSZTIS]: ", "", None,
4258 optentry state.mode, ondone
4261 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4262 setzoom 1.0
4264 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4265 let zoom = zoomforh conf.winw conf.winh state.scrollw in
4266 if zoom < 1.0
4267 then setzoom zoom
4269 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4270 togglebirdseye ()
4272 | '0' .. '9' ->
4273 let ondone s =
4274 let n =
4275 try int_of_string s with exc ->
4276 state.text <- Printf.sprintf "bad integer `%s': %s"
4277 s (Printexc.to_string exc);
4280 if n >= 0
4281 then (
4282 addnav ();
4283 cbput state.hists.pag (string_of_int n);
4284 gotopage1 (n + conf.pagebias - 1) 0;
4287 let pageentry text key =
4288 match Char.unsafe_chr key with
4289 | 'g' -> TEdone text
4290 | _ -> intentry text key
4292 let text = "x" in text.[0] <- c;
4293 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
4295 | 'b' ->
4296 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4297 reshape conf.winw conf.winh;
4299 | 'l' ->
4300 conf.hlinks <- not conf.hlinks;
4301 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4302 G.postRedisplay "toggle highlightlinks";
4304 | 'a' ->
4305 begin match state.autoscroll with
4306 | Some step ->
4307 conf.autoscrollstep <- step;
4308 state.autoscroll <- None
4309 | None ->
4310 if conf.autoscrollstep = 0
4311 then state.autoscroll <- Some 1
4312 else state.autoscroll <- Some conf.autoscrollstep
4315 | 'P' ->
4316 conf.presentation <- not conf.presentation;
4317 if conf.presentation
4318 then (
4319 if not conf.scrollbarinpm
4320 then state.scrollw <- 0;
4322 else
4323 state.scrollw <- conf.scrollbw;
4325 showtext ' ' ("presentation mode " ^
4326 if conf.presentation then "on" else "off");
4327 state.anchor <- getanchor ();
4328 represent ()
4330 | 'f' ->
4331 begin match state.fullscreen with
4332 | None ->
4333 state.fullscreen <- Some (conf.winw, conf.winh);
4334 Glut.fullScreen ()
4335 | Some (w, h) ->
4336 state.fullscreen <- None;
4337 doreshape w h
4340 | 'g' ->
4341 gotoy_and_clear_text 0
4343 | 'G' ->
4344 gotopage1 (state.pagecount - 1) 0
4346 | 'n' ->
4347 search state.searchpattern true
4349 | 'p' | 'N' ->
4350 search state.searchpattern false
4352 | 't' ->
4353 begin match state.layout with
4354 | [] -> ()
4355 | l :: _ ->
4356 gotoy_and_clear_text (getpagey l.pageno)
4359 | ' ' ->
4360 begin match List.rev state.layout with
4361 | [] -> ()
4362 | l :: _ ->
4363 let pageno = min (l.pageno+1) (state.pagecount-1) in
4364 gotoy_and_clear_text (getpagey pageno)
4367 | '\127' -> (* del *)
4368 begin match state.layout with
4369 | [] -> ()
4370 | l :: _ ->
4371 let pageno = max 0 (l.pageno-1) in
4372 gotoy_and_clear_text (getpagey pageno)
4375 | '=' ->
4376 showtext ' ' (describe_location ());
4378 | 'w' ->
4379 begin match state.layout with
4380 | [] -> ()
4381 | l :: _ ->
4382 doreshape (l.pagew + state.scrollw) l.pageh;
4383 G.postRedisplay "w"
4386 | '\'' ->
4387 enterbookmarkmode ()
4389 | 'h' ->
4390 enterhelpmode ()
4392 | 'i' ->
4393 enterinfomode ()
4395 | 'e' when conf.redirectstderr || is_gui ->
4396 entermsgsmode ()
4398 | 'm' ->
4399 let ondone s =
4400 match state.layout with
4401 | l :: _ ->
4402 state.bookmarks <-
4403 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4404 :: state.bookmarks
4405 | _ -> ()
4407 enttext ("bookmark: ", "", None, textentry, ondone)
4409 | '~' ->
4410 quickbookmark ();
4411 showtext ' ' "Quick bookmark added";
4413 | 'z' ->
4414 begin match state.layout with
4415 | l :: _ ->
4416 let rect = getpdimrect l.pagedimno in
4417 let w, h =
4418 if conf.crophack
4419 then
4420 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4421 truncate (1.2 *. (rect.(3) -. rect.(0))))
4422 else
4423 (truncate (rect.(1) -. rect.(0)),
4424 truncate (rect.(3) -. rect.(0)))
4426 let w = truncate ((float w)*.conf.zoom)
4427 and h = truncate ((float h)*.conf.zoom) in
4428 if w != 0 && h != 0
4429 then (
4430 state.anchor <- getanchor ();
4431 doreshape (w + state.scrollw) (h + conf.interpagespace)
4433 G.postRedisplay "z";
4435 | [] -> ()
4438 | '\000' -> (* ctrl-2 *)
4439 let maxw = getmaxw () in
4440 if maxw > 0.0
4441 then setzoom (maxw /. float conf.winw)
4443 | '<' | '>' ->
4444 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
4446 | '[' | ']' ->
4447 conf.colorscale <-
4448 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
4450 G.postRedisplay "brightness";
4452 | 'k' ->
4453 begin match state.mode with
4454 | Birdseye beye -> upbirdseye 1 beye
4455 | _ -> gotoy (clamp (-conf.scrollstep))
4458 | 'j' ->
4459 begin match state.mode with
4460 | Birdseye beye -> downbirdseye 1 beye
4461 | _ -> gotoy (clamp conf.scrollstep)
4464 | 'r' ->
4465 state.anchor <- getanchor ();
4466 opendoc state.path state.password
4468 | 'v' when not conf.debug ->
4469 List.iter debugl state.layout;
4471 | 'v' when conf.debug ->
4472 state.rects <- [];
4473 List.iter (fun l ->
4474 match getopaque l.pageno with
4475 | None -> ()
4476 | Some opaque ->
4477 let x0, y0, x1, y1 = pagebbox opaque in
4478 let a,b = float x0, float y0 in
4479 let c,d = float x1, float y0 in
4480 let e,f = float x1, float y1 in
4481 let h,j = float x0, float y1 in
4482 let rect = (a,b,c,d,e,f,h,j) in
4483 debugrect rect;
4484 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4485 ) state.layout;
4486 G.postRedisplay "v";
4488 | _ ->
4489 vlog "huh? %d %c" key (Char.chr key);
4492 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
4493 match key with
4494 | 27 -> (* escape *)
4495 leavebirdseye beye true
4497 | 12 -> (* ctrl-l *)
4498 let y, h = getpageyh pageno in
4499 let top = (conf.winh - h) / 2 in
4500 gotoy (max 0 (y - top))
4502 | 13 -> (* enter *)
4503 leavebirdseye beye false
4505 | _ ->
4506 viewkeyboard key
4509 let keyboard ~key ~x ~y =
4510 ignore x;
4511 ignore y;
4512 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
4513 then wcmd "interrupt" []
4514 else state.uioh <- state.uioh#key key
4517 let birdseyespecial key ((oconf, leftx, _, hooverpageno, anchor) as beye) =
4518 let incr =
4519 match conf.columns with
4520 | None -> 1
4521 | Some ((c, _, _), _) -> c
4523 match key with
4524 | Glut.KEY_UP -> upbirdseye incr beye
4525 | Glut.KEY_DOWN -> downbirdseye incr beye
4526 | Glut.KEY_LEFT -> upbirdseye 1 beye
4527 | Glut.KEY_RIGHT -> downbirdseye 1 beye
4529 | Glut.KEY_PAGE_UP ->
4530 begin match state.layout with
4531 | l :: _ ->
4532 if l.pagey != 0
4533 then (
4534 state.mode <- Birdseye (
4535 oconf, leftx, l.pageno, hooverpageno, anchor
4537 gotopage1 l.pageno 0;
4539 else (
4540 let layout = layout (state.y-conf.winh) conf.winh in
4541 match layout with
4542 | [] -> gotoy (clamp (-conf.winh))
4543 | l :: _ ->
4544 state.mode <- Birdseye (
4545 oconf, leftx, l.pageno, hooverpageno, anchor
4547 gotopage1 l.pageno 0
4550 | [] -> gotoy (clamp (-conf.winh))
4551 end;
4553 | Glut.KEY_PAGE_DOWN ->
4554 begin match List.rev state.layout with
4555 | l :: _ ->
4556 let layout = layout (state.y + conf.winh) conf.winh in
4557 begin match layout with
4558 | [] ->
4559 let incr = l.pageh - l.pagevh in
4560 if incr = 0
4561 then (
4562 state.mode <-
4563 Birdseye (
4564 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4566 G.postRedisplay "birdseye pagedown";
4568 else gotoy (clamp (incr + conf.interpagespace*2));
4570 | l :: _ ->
4571 state.mode <-
4572 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4573 gotopage1 l.pageno 0;
4576 | [] -> gotoy (clamp conf.winh)
4577 end;
4579 | Glut.KEY_HOME ->
4580 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4581 gotopage1 0 0
4583 | Glut.KEY_END ->
4584 let pageno = state.pagecount - 1 in
4585 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4586 if not (pagevisible state.layout pageno)
4587 then
4588 let h =
4589 match List.rev state.pdims with
4590 | [] -> conf.winh
4591 | (_, _, h, _) :: _ -> h
4593 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4594 else G.postRedisplay "birdseye end";
4595 | _ -> ()
4598 let setautoscrollspeed step goingdown =
4599 let incr = max 1 ((abs step) / 2) in
4600 let incr = if goingdown then incr else -incr in
4601 let astep = step + incr in
4602 state.autoscroll <- Some astep;
4605 let special ~key ~x ~y =
4606 ignore x;
4607 ignore y;
4608 state.uioh <- state.uioh#special key
4611 let drawpage l =
4612 let color =
4613 match state.mode with
4614 | Textentry _ -> scalecolor 0.4
4615 | View -> scalecolor 1.0
4616 | Birdseye (_, _, pageno, hooverpageno, _) ->
4617 if l.pageno = hooverpageno
4618 then scalecolor 0.9
4619 else (
4620 if l.pageno = pageno
4621 then scalecolor 1.0
4622 else scalecolor 0.8
4625 drawtiles l color;
4626 begin match getopaque l.pageno with
4627 | Some opaque ->
4628 if tileready l l.pagex l.pagey
4629 then
4630 let x = l.pagedispx - l.pagex
4631 and y = l.pagedispy - l.pagey in
4632 postprocess opaque conf.hlinks x y;
4634 | _ -> ()
4635 end;
4638 let scrollindicator () =
4639 let sbw, ph, sh = state.uioh#scrollph in
4640 let sbh, pw, sw = state.uioh#scrollpw in
4642 GlDraw.color (0.64, 0.64, 0.64);
4643 GlDraw.rect
4644 (float (conf.winw - sbw), 0.)
4645 (float conf.winw, float conf.winh)
4647 GlDraw.rect
4648 (0., float (conf.winh - sbh))
4649 (float (conf.winw - state.scrollw - 1), float conf.winh)
4651 GlDraw.color (0.0, 0.0, 0.0);
4653 GlDraw.rect
4654 (float (conf.winw - sbw), ph)
4655 (float conf.winw, ph +. sh)
4657 GlDraw.rect
4658 (pw, float (conf.winh - sbh))
4659 (pw +. sw, float conf.winh)
4663 let pagetranslatepoint l x y =
4664 let dy = y - l.pagedispy in
4665 let y = dy + l.pagey in
4666 let dx = x - l.pagedispx in
4667 let x = dx + l.pagex in
4668 (x, y);
4671 let showsel () =
4672 match state.mstate with
4673 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4676 | Msel ((x0, y0), (x1, y1)) ->
4677 let rec loop = function
4678 | l :: ls ->
4679 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4680 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
4681 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
4682 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
4683 then
4684 match getopaque l.pageno with
4685 | Some opaque ->
4686 let dx, dy = pagetranslatepoint l 0 0 in
4687 let x0 = x0 + dx
4688 and y0 = y0 + dy
4689 and x1 = x1 + dx
4690 and y1 = y1 + dy in
4691 GlMat.mode `modelview;
4692 GlMat.push ();
4693 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
4694 seltext opaque (x0, y0, x1, y1);
4695 GlMat.pop ();
4696 | _ -> ()
4697 else loop ls
4698 | [] -> ()
4700 loop state.layout
4703 let showrects () =
4704 Gl.enable `blend;
4705 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4706 GlDraw.polygon_mode `both `fill;
4707 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4708 List.iter
4709 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4710 List.iter (fun l ->
4711 if l.pageno = pageno
4712 then (
4713 let dx = float (l.pagedispx - l.pagex) in
4714 let dy = float (l.pagedispy - l.pagey) in
4715 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4716 GlDraw.begins `quads;
4718 GlDraw.vertex2 (x0+.dx, y0+.dy);
4719 GlDraw.vertex2 (x1+.dx, y1+.dy);
4720 GlDraw.vertex2 (x2+.dx, y2+.dy);
4721 GlDraw.vertex2 (x3+.dx, y3+.dy);
4723 GlDraw.ends ();
4725 ) state.layout
4726 ) state.rects
4728 Gl.disable `blend;
4731 let display () =
4732 GlClear.color (scalecolor2 conf.bgcolor);
4733 GlClear.clear [`color];
4734 List.iter drawpage state.layout;
4735 showrects ();
4736 showsel ();
4737 state.uioh#display;
4738 scrollindicator ();
4739 begin match state.mstate with
4740 | Mzoomrect ((x0, y0), (x1, y1)) ->
4741 Gl.enable `blend;
4742 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4743 GlDraw.polygon_mode `both `fill;
4744 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4745 GlDraw.rect (float x0, float y0)
4746 (float x1, float y1);
4747 Gl.disable `blend;
4748 | _ -> ()
4749 end;
4750 enttext ();
4751 Glut.swapBuffers ();
4754 let getunder x y =
4755 let rec f = function
4756 | l :: rest ->
4757 begin match getopaque l.pageno with
4758 | Some opaque ->
4759 let x0 = l.pagedispx in
4760 let x1 = x0 + l.pagevw in
4761 let y0 = l.pagedispy in
4762 let y1 = y0 + l.pagevh in
4763 if y >= y0 && y <= y1 && x >= x0 && x <= x1
4764 then
4765 let px, py = pagetranslatepoint l x y in
4766 match whatsunder opaque px py with
4767 | Unone -> f rest
4768 | under -> under
4769 else f rest
4770 | _ ->
4771 f rest
4773 | [] -> Unone
4775 f state.layout
4778 let zoomrect x y x1 y1 =
4779 let x0 = min x x1
4780 and x1 = max x x1
4781 and y0 = min y y1 in
4782 gotoy (state.y + y0);
4783 state.anchor <- getanchor ();
4784 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4785 let margin =
4786 if state.w < conf.winw - state.scrollw
4787 then (conf.winw - state.scrollw - state.w) / 2
4788 else 0
4790 state.x <- (state.x + margin) - x0;
4791 setzoom zoom;
4792 Glut.setCursor Glut.CURSOR_INHERIT;
4793 state.mstate <- Mnone;
4796 let scrollx x =
4797 let winw = conf.winw - state.scrollw - 1 in
4798 let s = float x /. float winw in
4799 let destx = truncate (float (state.w + winw) *. s) in
4800 state.x <- winw - destx;
4801 gotoy_and_clear_text state.y;
4802 state.mstate <- Mscrollx;
4805 let scrolly y =
4806 let s = float y /. float conf.winh in
4807 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4808 gotoy_and_clear_text desty;
4809 state.mstate <- Mscrolly;
4812 let viewmouse button bstate x y =
4813 match button with
4814 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4815 if Glut.getModifiers () land Glut.active_ctrl != 0
4816 then (
4817 match state.mstate with
4818 | Mzoom (oldn, i) ->
4819 if oldn = n
4820 then (
4821 if i = 2
4822 then
4823 let incr =
4824 match n with
4825 | 4 ->
4826 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4827 | _ ->
4828 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4830 let zoom = conf.zoom -. incr in
4831 setzoom zoom;
4832 state.mstate <- Mzoom (n, 0);
4833 else
4834 state.mstate <- Mzoom (n, i+1);
4836 else state.mstate <- Mzoom (n, 0)
4838 | _ -> state.mstate <- Mzoom (n, 0)
4840 else (
4841 match state.autoscroll with
4842 | Some step -> setautoscrollspeed step (n=4)
4843 | None ->
4844 let incr =
4845 if n = 3
4846 then -conf.scrollstep
4847 else conf.scrollstep
4849 let incr = incr * 2 in
4850 let y = clamp incr in
4851 gotoy_and_clear_text y
4854 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4855 if bstate = Glut.DOWN
4856 then (
4857 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4858 state.mstate <- Mpan (x, y)
4860 else
4861 state.mstate <- Mnone
4863 | Glut.RIGHT_BUTTON ->
4864 if bstate = Glut.DOWN
4865 then (
4866 Glut.setCursor Glut.CURSOR_CYCLE;
4867 let p = (x, y) in
4868 state.mstate <- Mzoomrect (p, p)
4870 else (
4871 match state.mstate with
4872 | Mzoomrect ((x0, y0), _) ->
4873 if abs (x-x0) > 10 && abs (y - y0) > 10
4874 then zoomrect x0 y0 x y
4875 else (
4876 state.mstate <- Mnone;
4877 Glut.setCursor Glut.CURSOR_INHERIT;
4878 G.postRedisplay "kill accidental zoom rect";
4880 | _ ->
4881 Glut.setCursor Glut.CURSOR_INHERIT;
4882 state.mstate <- Mnone
4885 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4886 if bstate = Glut.DOWN
4887 then
4888 let _, position, sh = state.uioh#scrollph in
4889 if y > truncate position && y < truncate (position +. sh)
4890 then state.mstate <- Mscrolly
4891 else scrolly y
4892 else
4893 state.mstate <- Mnone
4895 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4896 if bstate = Glut.DOWN
4897 then
4898 let _, position, sw = state.uioh#scrollpw in
4899 if x > truncate position && x < truncate (position +. sw)
4900 then state.mstate <- Mscrollx
4901 else scrollx x
4902 else
4903 state.mstate <- Mnone
4905 | Glut.LEFT_BUTTON ->
4906 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4907 begin match dest with
4908 | Ulinkgoto (pageno, top) ->
4909 if pageno >= 0
4910 then (
4911 addnav ();
4912 gotopage1 pageno top;
4915 | Ulinkuri s ->
4916 gotouri s
4918 | Uremote (filename, pageno) ->
4919 let path =
4920 if Sys.file_exists filename
4921 then filename
4922 else
4923 let dir = Filename.dirname state.path in
4924 let path = Filename.concat dir filename in
4925 if Sys.file_exists path
4926 then path
4927 else ""
4929 if String.length path > 0
4930 then (
4931 let anchor = getanchor () in
4932 let ranchor = state.path, state.password, anchor in
4933 state.anchor <- (pageno, 0.0);
4934 state.ranchors <- ranchor :: state.ranchors;
4935 opendoc path "";
4937 else showtext '!' ("Could not find " ^ filename)
4939 | Uunexpected _ | Ulaunch _ | Unamed _ -> ()
4941 | Unone when bstate = Glut.DOWN ->
4942 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4943 state.mstate <- Mpan (x, y);
4945 | Unone | Utext _ ->
4946 if bstate = Glut.DOWN
4947 then (
4948 if conf.angle mod 360 = 0
4949 then (
4950 state.mstate <- Msel ((x, y), (x, y));
4951 G.postRedisplay "mouse select";
4954 else (
4955 match state.mstate with
4956 | Mnone -> ()
4958 | Mzoom _ | Mscrollx | Mscrolly ->
4959 state.mstate <- Mnone
4961 | Mzoomrect ((x0, y0), _) ->
4962 zoomrect x0 y0 x y
4964 | Mpan _ ->
4965 Glut.setCursor Glut.CURSOR_INHERIT;
4966 state.mstate <- Mnone
4968 | Msel ((_, y0), (_, y1)) ->
4969 let rec loop = function
4970 | [] -> ()
4971 | l :: rest ->
4972 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4973 || ((y1 >= l.pagedispy
4974 && y1 <= (l.pagedispy + l.pagevh)))
4975 then
4976 match getopaque l.pageno with
4977 | Some opaque ->
4978 copysel conf.selcmd opaque;
4979 G.postRedisplay "copysel"
4980 | _ -> ()
4981 else loop rest
4983 loop state.layout;
4984 Glut.setCursor Glut.CURSOR_INHERIT;
4985 state.mstate <- Mnone;
4989 | _ -> ()
4992 let birdseyemouse button bstate x y
4993 (conf, leftx, _, hooverpageno, anchor) =
4994 match button with
4995 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4996 let rec loop = function
4997 | [] -> ()
4998 | l :: rest ->
4999 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5000 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5001 then (
5002 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5004 else loop rest
5006 loop state.layout
5007 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
5008 | _ -> ()
5011 let mouse bstate button x y =
5012 state.uioh <- state.uioh#button button bstate x y;
5015 let mouse ~button ~state ~x ~y = mouse state button x y;;
5017 let motion ~x ~y =
5018 state.uioh <- state.uioh#motion x y
5021 let pmotion ~x ~y =
5022 state.uioh <- state.uioh#pmotion x y;
5025 let uioh = object
5026 method display = ()
5028 method key key =
5029 begin match state.mode with
5030 | Textentry textentry -> textentrykeyboard key textentry
5031 | Birdseye birdseye -> birdseyekeyboard key birdseye
5032 | View -> viewkeyboard key
5033 end;
5034 state.uioh
5036 method special key =
5037 begin match state.mode with
5038 | View | (Birdseye _) when key = Glut.KEY_F9 ->
5039 togglebirdseye ()
5041 | Birdseye vals ->
5042 birdseyespecial key vals
5044 | View when key = Glut.KEY_F1 ->
5045 enterhelpmode ()
5047 | View ->
5048 begin match state.autoscroll with
5049 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
5050 setautoscrollspeed step (key = Glut.KEY_DOWN)
5052 | _ ->
5053 let y =
5054 match key with
5055 | Glut.KEY_F3 -> search state.searchpattern true; state.y
5056 | Glut.KEY_UP ->
5057 if Glut.getModifiers () land Glut.active_ctrl != 0
5058 then
5059 if Glut.getModifiers () land Glut.active_shift != 0
5060 then (setzoom state.prevzoom; state.y)
5061 else clamp (-conf.winh/2)
5062 else clamp (-conf.scrollstep)
5063 | Glut.KEY_DOWN ->
5064 if Glut.getModifiers () land Glut.active_ctrl != 0
5065 then
5066 if Glut.getModifiers () land Glut.active_shift != 0
5067 then (setzoom state.prevzoom; state.y)
5068 else clamp (conf.winh/2)
5069 else clamp (conf.scrollstep)
5070 | Glut.KEY_PAGE_UP ->
5071 if Glut.getModifiers () land Glut.active_ctrl != 0
5072 then
5073 match state.layout with
5074 | [] -> state.y
5075 | l :: _ -> state.y - l.pagey
5076 else
5077 clamp (-conf.winh)
5078 | Glut.KEY_PAGE_DOWN ->
5079 if Glut.getModifiers () land Glut.active_ctrl != 0
5080 then
5081 match List.rev state.layout with
5082 | [] -> state.y
5083 | l :: _ -> getpagey l.pageno
5084 else
5085 clamp conf.winh
5086 | Glut.KEY_HOME ->
5087 addnav ();
5089 | Glut.KEY_END ->
5090 addnav ();
5091 state.maxy - (if conf.maxhfit then conf.winh else 0)
5093 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
5094 Glut.getModifiers () land Glut.active_alt != 0 ->
5095 getnav (if key = Glut.KEY_LEFT then 1 else -1)
5097 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
5098 let dx =
5099 if Glut.getModifiers () land Glut.active_ctrl != 0
5100 then (conf.winw / 2)
5101 else 10
5103 state.x <- state.x - dx;
5104 state.y
5105 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
5106 let dx =
5107 if Glut.getModifiers () land Glut.active_ctrl != 0
5108 then (conf.winw / 2)
5109 else 10
5111 state.x <- state.x + dx;
5112 state.y
5114 | _ -> state.y
5116 if abs (state.y - y) > conf.scrollstep*2
5117 then gotoghyll y
5118 else gotoy_and_clear_text y
5121 | Textentry te -> textentryspecial key te
5122 end;
5123 state.uioh
5125 method button button bstate x y =
5126 begin match state.mode with
5127 | View -> viewmouse button bstate x y
5128 | Birdseye beye -> birdseyemouse button bstate x y beye
5129 | Textentry _ -> ()
5130 end;
5131 state.uioh
5133 method motion x y =
5134 begin match state.mode with
5135 | Textentry _ -> ()
5136 | View | Birdseye _ ->
5137 match state.mstate with
5138 | Mzoom _ | Mnone -> ()
5140 | Mpan (x0, y0) ->
5141 let dx = x - x0
5142 and dy = y0 - y in
5143 state.mstate <- Mpan (x, y);
5144 if conf.zoom > 1.0 then state.x <- state.x + dx;
5145 let y = clamp dy in
5146 gotoy_and_clear_text y
5148 | Msel (a, _) ->
5149 state.mstate <- Msel (a, (x, y));
5150 G.postRedisplay "motion select";
5152 | Mscrolly ->
5153 let y = min conf.winh (max 0 y) in
5154 scrolly y
5156 | Mscrollx ->
5157 let x = min conf.winw (max 0 x) in
5158 scrollx x
5160 | Mzoomrect (p0, _) ->
5161 state.mstate <- Mzoomrect (p0, (x, y));
5162 G.postRedisplay "motion zoomrect";
5163 end;
5164 state.uioh
5166 method pmotion x y =
5167 begin match state.mode with
5168 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5169 let rec loop = function
5170 | [] ->
5171 if hooverpageno != -1
5172 then (
5173 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5174 G.postRedisplay "pmotion birdseye no hoover";
5176 | l :: rest ->
5177 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5178 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5179 then (
5180 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5181 G.postRedisplay "pmotion birdseye hoover";
5183 else loop rest
5185 loop state.layout
5187 | Textentry _ -> ()
5189 | View ->
5190 match state.mstate with
5191 | Mnone ->
5192 begin match getunder x y with
5193 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
5194 | Ulinkuri uri ->
5195 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
5196 Glut.setCursor Glut.CURSOR_INFO
5197 | Ulinkgoto (page, _) ->
5198 if conf.underinfo
5199 then showtext 'p' ("age: " ^ string_of_int (page+1));
5200 Glut.setCursor Glut.CURSOR_INFO
5201 | Utext s ->
5202 if conf.underinfo then showtext 'f' ("ont: " ^ s);
5203 Glut.setCursor Glut.CURSOR_TEXT
5204 | Uunexpected s ->
5205 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
5206 Glut.setCursor Glut.CURSOR_INHERIT
5207 | Ulaunch s ->
5208 if conf.underinfo then showtext 'l' ("launch: " ^ s);
5209 Glut.setCursor Glut.CURSOR_INHERIT
5210 | Unamed s ->
5211 if conf.underinfo then showtext 'n' ("named: " ^ s);
5212 Glut.setCursor Glut.CURSOR_INHERIT
5213 | Uremote (filename, pageno) ->
5214 if conf.underinfo then showtext 'r'
5215 (Printf.sprintf "emote: %s (%d)" filename pageno);
5216 Glut.setCursor Glut.CURSOR_INFO
5219 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5221 end;
5222 state.uioh
5224 method infochanged _ = ()
5226 method scrollph =
5227 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5228 let p, h = scrollph state.y maxy in
5229 state.scrollw, p, h
5231 method scrollpw =
5232 let winw = conf.winw - state.scrollw - 1 in
5233 let fwinw = float winw in
5234 let sw =
5235 let sw = fwinw /. float state.w in
5236 let sw = fwinw *. sw in
5237 max sw (float conf.scrollh)
5239 let position, sw =
5240 let f = state.w+winw in
5241 let r = float (winw-state.x) /. float f in
5242 let p = fwinw *. r in
5243 p-.sw/.2., sw
5245 let sw =
5246 if position +. sw > fwinw
5247 then fwinw -. position
5248 else sw
5250 state.hscrollh, position, sw
5251 end;;
5253 module Config =
5254 struct
5255 open Parser
5257 let fontpath = ref "";;
5258 let wmclasshack = ref false;;
5260 let unent s =
5261 let l = String.length s in
5262 let b = Buffer.create l in
5263 unent b s 0 l;
5264 Buffer.contents b;
5267 let home =
5269 if is_windows
5270 then Sys.getenv "HOMEPATH"
5271 else Sys.getenv "HOME"
5272 with exn ->
5273 prerr_endline
5274 ("Can not determine home directory location: " ^
5275 Printexc.to_string exn);
5279 let config_of c attrs =
5280 let apply c k v =
5282 match k with
5283 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5284 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5285 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5286 | "preload" -> { c with preload = bool_of_string v }
5287 | "page-bias" -> { c with pagebias = int_of_string v }
5288 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5289 | "auto-scroll-step" ->
5290 { c with autoscrollstep = max 0 (int_of_string v) }
5291 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5292 | "crop-hack" -> { c with crophack = bool_of_string v }
5293 | "throttle" ->
5294 let mw =
5295 match String.lowercase v with
5296 | "true" -> Some infinity
5297 | "false" -> None
5298 | f -> Some (float_of_string f)
5300 { c with maxwait = mw}
5301 | "highlight-links" -> { c with hlinks = bool_of_string v }
5302 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5303 | "vertical-margin" ->
5304 { c with interpagespace = max 0 (int_of_string v) }
5305 | "zoom" ->
5306 let zoom = float_of_string v /. 100. in
5307 let zoom = max zoom 0.0 in
5308 { c with zoom = zoom }
5309 | "presentation" -> { c with presentation = bool_of_string v }
5310 | "rotation-angle" -> { c with angle = int_of_string v }
5311 | "width" -> { c with winw = max 20 (int_of_string v) }
5312 | "height" -> { c with winh = max 20 (int_of_string v) }
5313 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5314 | "proportional-display" -> { c with proportional = bool_of_string v }
5315 | "pixmap-cache-size" ->
5316 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5317 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5318 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5319 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5320 | "persistent-location" -> { c with jumpback = bool_of_string v }
5321 | "background-color" -> { c with bgcolor = color_of_string v }
5322 | "scrollbar-in-presentation" ->
5323 { c with scrollbarinpm = bool_of_string v }
5324 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5325 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5326 | "mupdf-store-size" ->
5327 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5328 | "checkers" -> { c with checkers = bool_of_string v }
5329 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5330 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5331 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5332 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
5333 | "uri-launcher" -> { c with urilauncher = unent v }
5334 | "color-space" -> { c with colorspace = colorspace_of_string v }
5335 | "invert-colors" -> { c with invert = bool_of_string v }
5336 | "brightness" -> { c with colorscale = float_of_string v }
5337 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5338 | "ghyllscroll" ->
5339 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5340 | "columns" ->
5341 let nab = columns_of_string v in
5342 { c with columns = Some (nab, [||]) }
5343 | "birds-eye-columns" ->
5344 { c with beyecolumns = Some (max (int_of_string v) 2) }
5345 | "selection-command" -> { c with selcmd = unent v }
5346 | _ -> c
5347 with exn ->
5348 prerr_endline ("Error processing attribute (`" ^
5349 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5352 let rec fold c = function
5353 | [] -> c
5354 | (k, v) :: rest ->
5355 let c = apply c k v in
5356 fold c rest
5358 fold c attrs;
5361 let fromstring f pos n v d =
5362 try f v
5363 with exn ->
5364 dolog "Error processing attribute (%S=%S) at %d\n%s"
5365 n v pos (Printexc.to_string exn)
5370 let bookmark_of attrs =
5371 let rec fold title page rely = function
5372 | ("title", v) :: rest -> fold v page rely rest
5373 | ("page", v) :: rest -> fold title v rely rest
5374 | ("rely", v) :: rest -> fold title page v rest
5375 | _ :: rest -> fold title page rely rest
5376 | [] -> title, page, rely
5378 fold "invalid" "0" "0" attrs
5381 let doc_of attrs =
5382 let rec fold path page rely pan = function
5383 | ("path", v) :: rest -> fold v page rely pan rest
5384 | ("page", v) :: rest -> fold path v rely pan rest
5385 | ("rely", v) :: rest -> fold path page v pan rest
5386 | ("pan", v) :: rest -> fold path page rely v rest
5387 | _ :: rest -> fold path page rely pan rest
5388 | [] -> path, page, rely, pan
5390 fold "" "0" "0" "0" attrs
5393 let setconf dst src =
5394 dst.scrollbw <- src.scrollbw;
5395 dst.scrollh <- src.scrollh;
5396 dst.icase <- src.icase;
5397 dst.preload <- src.preload;
5398 dst.pagebias <- src.pagebias;
5399 dst.verbose <- src.verbose;
5400 dst.scrollstep <- src.scrollstep;
5401 dst.maxhfit <- src.maxhfit;
5402 dst.crophack <- src.crophack;
5403 dst.autoscrollstep <- src.autoscrollstep;
5404 dst.maxwait <- src.maxwait;
5405 dst.hlinks <- src.hlinks;
5406 dst.underinfo <- src.underinfo;
5407 dst.interpagespace <- src.interpagespace;
5408 dst.zoom <- src.zoom;
5409 dst.presentation <- src.presentation;
5410 dst.angle <- src.angle;
5411 dst.winw <- src.winw;
5412 dst.winh <- src.winh;
5413 dst.savebmarks <- src.savebmarks;
5414 dst.memlimit <- src.memlimit;
5415 dst.proportional <- src.proportional;
5416 dst.texcount <- src.texcount;
5417 dst.sliceheight <- src.sliceheight;
5418 dst.thumbw <- src.thumbw;
5419 dst.jumpback <- src.jumpback;
5420 dst.bgcolor <- src.bgcolor;
5421 dst.scrollbarinpm <- src.scrollbarinpm;
5422 dst.tilew <- src.tilew;
5423 dst.tileh <- src.tileh;
5424 dst.mustoresize <- src.mustoresize;
5425 dst.checkers <- src.checkers;
5426 dst.aalevel <- src.aalevel;
5427 dst.trimmargins <- src.trimmargins;
5428 dst.trimfuzz <- src.trimfuzz;
5429 dst.urilauncher <- src.urilauncher;
5430 dst.colorspace <- src.colorspace;
5431 dst.invert <- src.invert;
5432 dst.colorscale <- src.colorscale;
5433 dst.redirectstderr <- src.redirectstderr;
5434 dst.ghyllscroll <- src.ghyllscroll;
5435 dst.columns <- src.columns;
5436 dst.beyecolumns <- src.beyecolumns;
5437 dst.selcmd <- src.selcmd;
5440 let get s =
5441 let h = Hashtbl.create 10 in
5442 let dc = { defconf with angle = defconf.angle } in
5443 let rec toplevel v t spos _ =
5444 match t with
5445 | Vdata | Vcdata | Vend -> v
5446 | Vopen ("llppconfig", _, closed) ->
5447 if closed
5448 then v
5449 else { v with f = llppconfig }
5450 | Vopen _ ->
5451 error "unexpected subelement at top level" s spos
5452 | Vclose _ -> error "unexpected close at top level" s spos
5454 and llppconfig v t spos _ =
5455 match t with
5456 | Vdata | Vcdata -> v
5457 | Vend -> error "unexpected end of input in llppconfig" s spos
5458 | Vopen ("defaults", attrs, closed) ->
5459 let c = config_of dc attrs in
5460 setconf dc c;
5461 if closed
5462 then v
5463 else { v with f = skip "defaults" (fun () -> v) }
5465 | Vopen ("ui-font", attrs, closed) ->
5466 let rec getsize size = function
5467 | [] -> size
5468 | ("size", v) :: rest ->
5469 let size =
5470 fromstring int_of_string spos "size" v fstate.fontsize in
5471 getsize size rest
5472 | l -> getsize size l
5474 fstate.fontsize <- getsize fstate.fontsize attrs;
5475 if closed
5476 then v
5477 else { v with f = uifont (Buffer.create 10) }
5479 | Vopen ("doc", attrs, closed) ->
5480 let pathent, spage, srely, span = doc_of attrs in
5481 let path = unent pathent
5482 and pageno = fromstring int_of_string spos "page" spage 0
5483 and rely = fromstring float_of_string spos "rely" srely 0.0
5484 and pan = fromstring int_of_string spos "pan" span 0 in
5485 let c = config_of dc attrs in
5486 let anchor = (pageno, rely) in
5487 if closed
5488 then (Hashtbl.add h path (c, [], pan, anchor); v)
5489 else { v with f = doc path pan anchor c [] }
5491 | Vopen _ ->
5492 error "unexpected subelement in llppconfig" s spos
5494 | Vclose "llppconfig" -> { v with f = toplevel }
5495 | Vclose _ -> error "unexpected close in llppconfig" s spos
5497 and uifont b v t spos epos =
5498 match t with
5499 | Vdata | Vcdata ->
5500 Buffer.add_substring b s spos (epos - spos);
5502 | Vopen (_, _, _) ->
5503 error "unexpected subelement in ui-font" s spos
5504 | Vclose "ui-font" ->
5505 if String.length !fontpath = 0
5506 then fontpath := Buffer.contents b;
5507 { v with f = llppconfig }
5508 | Vclose _ -> error "unexpected close in ui-font" s spos
5509 | Vend -> error "unexpected end of input in ui-font" s spos
5511 and doc path pan anchor c bookmarks v t spos _ =
5512 match t with
5513 | Vdata | Vcdata -> v
5514 | Vend -> error "unexpected end of input in doc" s spos
5515 | Vopen ("bookmarks", _, closed) ->
5516 if closed
5517 then v
5518 else { v with f = pbookmarks path pan anchor c bookmarks }
5520 | Vopen (_, _, _) ->
5521 error "unexpected subelement in doc" s spos
5523 | Vclose "doc" ->
5524 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5525 { v with f = llppconfig }
5527 | Vclose _ -> error "unexpected close in doc" s spos
5529 and pbookmarks path pan anchor c bookmarks v t spos _ =
5530 match t with
5531 | Vdata | Vcdata -> v
5532 | Vend -> error "unexpected end of input in bookmarks" s spos
5533 | Vopen ("item", attrs, closed) ->
5534 let titleent, spage, srely = bookmark_of attrs in
5535 let page = fromstring int_of_string spos "page" spage 0
5536 and rely = fromstring float_of_string spos "rely" srely 0.0 in
5537 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
5538 if closed
5539 then { v with f = pbookmarks path pan anchor c bookmarks }
5540 else
5541 let f () = v in
5542 { v with f = skip "item" f }
5544 | Vopen _ ->
5545 error "unexpected subelement in bookmarks" s spos
5547 | Vclose "bookmarks" ->
5548 { v with f = doc path pan anchor c bookmarks }
5550 | Vclose _ -> error "unexpected close in bookmarks" s spos
5552 and skip tag f v t spos _ =
5553 match t with
5554 | Vdata | Vcdata -> v
5555 | Vend ->
5556 error ("unexpected end of input in skipped " ^ tag) s spos
5557 | Vopen (tag', _, closed) ->
5558 if closed
5559 then v
5560 else
5561 let f' () = { v with f = skip tag f } in
5562 { v with f = skip tag' f' }
5563 | Vclose ctag ->
5564 if tag = ctag
5565 then f ()
5566 else error ("unexpected close in skipped " ^ tag) s spos
5569 parse { f = toplevel; accu = () } s;
5570 h, dc;
5573 let do_load f ic =
5575 let len = in_channel_length ic in
5576 let s = String.create len in
5577 really_input ic s 0 len;
5578 f s;
5579 with
5580 | Parse_error (msg, s, pos) ->
5581 let subs = subs s pos in
5582 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
5583 failwith ("parse error: " ^ s)
5585 | exn ->
5586 failwith ("config load error: " ^ Printexc.to_string exn)
5589 let defconfpath =
5590 let dir =
5592 let dir = Filename.concat home ".config" in
5593 if Sys.is_directory dir then dir else home
5594 with _ -> home
5596 Filename.concat dir "llpp.conf"
5599 let confpath = ref defconfpath;;
5601 let load1 f =
5602 if Sys.file_exists !confpath
5603 then
5604 match
5605 (try Some (open_in_bin !confpath)
5606 with exn ->
5607 prerr_endline
5608 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5609 Printexc.to_string exn);
5610 None
5612 with
5613 | Some ic ->
5614 begin try
5615 f (do_load get ic)
5616 with exn ->
5617 prerr_endline
5618 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5619 Printexc.to_string exn);
5620 end;
5621 close_in ic;
5623 | None -> ()
5624 else
5625 f (Hashtbl.create 0, defconf)
5628 let load () =
5629 let f (h, dc) =
5630 let pc, pb, px, pa =
5632 Hashtbl.find h (Filename.basename state.path)
5633 with Not_found -> dc, [], 0, (0, 0.0)
5635 setconf defconf dc;
5636 setconf conf pc;
5637 state.bookmarks <- pb;
5638 state.x <- px;
5639 state.scrollw <- conf.scrollbw;
5640 if conf.jumpback
5641 then state.anchor <- pa;
5642 cbput state.hists.nav pa;
5644 load1 f
5647 let add_attrs bb always dc c =
5648 let ob s a b =
5649 if always || a != b
5650 then Printf.bprintf bb "\n %s='%b'" s a
5651 and oi s a b =
5652 if always || a != b
5653 then Printf.bprintf bb "\n %s='%d'" s a
5654 and oI s a b =
5655 if always || a != b
5656 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5657 and oz s a b =
5658 if always || a <> b
5659 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5660 and oF s a b =
5661 if always || a <> b
5662 then Printf.bprintf bb "\n %s='%f'" s a
5663 and oc s a b =
5664 if always || a <> b
5665 then
5666 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5667 and oC s a b =
5668 if always || a <> b
5669 then
5670 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5671 and oR s a b =
5672 if always || a <> b
5673 then
5674 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5675 and os s a b =
5676 if always || a <> b
5677 then
5678 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5679 and og s a b =
5680 if always || a <> b
5681 then
5682 match a with
5683 | None -> ()
5684 | Some (_N, _A, _B) ->
5685 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
5686 and oW s a b =
5687 if always || a <> b
5688 then
5689 let v =
5690 match a with
5691 | None -> "false"
5692 | Some f ->
5693 if f = infinity
5694 then "true"
5695 else string_of_float f
5697 Printf.bprintf bb "\n %s='%s'" s v
5698 and oco s a b =
5699 if always || a <> b
5700 then
5701 match a with
5702 | Some ((n, a, b), _) when n > 1 ->
5703 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
5704 | _ -> ()
5705 and obeco s a b =
5706 if always || a <> b
5707 then
5708 match a with
5709 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
5710 | _ -> ()
5712 let w, h =
5713 if always
5714 then dc.winw, dc.winh
5715 else
5716 match state.fullscreen with
5717 | Some wh -> wh
5718 | None -> c.winw, c.winh
5720 let zoom, presentation, interpagespace, maxwait =
5721 if always
5722 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
5723 else
5724 match state.mode with
5725 | Birdseye (bc, _, _, _, _) ->
5726 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
5727 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
5729 oi "width" w dc.winw;
5730 oi "height" h dc.winh;
5731 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
5732 oi "scroll-handle-height" c.scrollh dc.scrollh;
5733 ob "case-insensitive-search" c.icase dc.icase;
5734 ob "preload" c.preload dc.preload;
5735 oi "page-bias" c.pagebias dc.pagebias;
5736 oi "scroll-step" c.scrollstep dc.scrollstep;
5737 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
5738 ob "max-height-fit" c.maxhfit dc.maxhfit;
5739 ob "crop-hack" c.crophack dc.crophack;
5740 oW "throttle" maxwait dc.maxwait;
5741 ob "highlight-links" c.hlinks dc.hlinks;
5742 ob "under-cursor-info" c.underinfo dc.underinfo;
5743 oi "vertical-margin" interpagespace dc.interpagespace;
5744 oz "zoom" zoom dc.zoom;
5745 ob "presentation" presentation dc.presentation;
5746 oi "rotation-angle" c.angle dc.angle;
5747 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
5748 ob "proportional-display" c.proportional dc.proportional;
5749 oI "pixmap-cache-size" c.memlimit dc.memlimit;
5750 oi "tex-count" c.texcount dc.texcount;
5751 oi "slice-height" c.sliceheight dc.sliceheight;
5752 oi "thumbnail-width" c.thumbw dc.thumbw;
5753 ob "persistent-location" c.jumpback dc.jumpback;
5754 oc "background-color" c.bgcolor dc.bgcolor;
5755 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
5756 oi "tile-width" c.tilew dc.tilew;
5757 oi "tile-height" c.tileh dc.tileh;
5758 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
5759 ob "checkers" c.checkers dc.checkers;
5760 oi "aalevel" c.aalevel dc.aalevel;
5761 ob "trim-margins" c.trimmargins dc.trimmargins;
5762 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
5763 os "uri-launcher" c.urilauncher dc.urilauncher;
5764 oC "color-space" c.colorspace dc.colorspace;
5765 ob "invert-colors" c.invert dc.invert;
5766 oF "brightness" c.colorscale dc.colorscale;
5767 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
5768 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
5769 oco "columns" c.columns dc.columns;
5770 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
5771 if always
5772 then ob "wmclass-hack" !wmclasshack false;
5773 os "selection-command" c.selcmd dc.selcmd;
5776 let save () =
5777 let uifontsize = fstate.fontsize in
5778 let bb = Buffer.create 32768 in
5779 let f (h, dc) =
5780 let dc = if conf.bedefault then conf else dc in
5781 Buffer.add_string bb "<llppconfig>\n";
5783 if String.length !fontpath > 0
5784 then
5785 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
5786 uifontsize
5787 !fontpath
5788 else (
5789 if uifontsize <> 14
5790 then
5791 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
5794 Buffer.add_string bb "<defaults ";
5795 add_attrs bb true dc dc;
5796 Buffer.add_string bb "/>\n";
5798 let adddoc path pan anchor c bookmarks =
5799 if bookmarks == [] && c = dc && anchor = emptyanchor
5800 then ()
5801 else (
5802 Printf.bprintf bb "<doc path='%s'"
5803 (enent path 0 (String.length path));
5805 if anchor <> emptyanchor
5806 then (
5807 let n, y = anchor in
5808 Printf.bprintf bb " page='%d'" n;
5809 if y > 1e-6
5810 then
5811 Printf.bprintf bb " rely='%f'" y
5815 if pan != 0
5816 then Printf.bprintf bb " pan='%d'" pan;
5818 add_attrs bb false dc c;
5820 begin match bookmarks with
5821 | [] -> Buffer.add_string bb "/>\n"
5822 | _ ->
5823 Buffer.add_string bb ">\n<bookmarks>\n";
5824 List.iter (fun (title, _level, (page, rely)) ->
5825 Printf.bprintf bb
5826 "<item title='%s' page='%d'"
5827 (enent title 0 (String.length title))
5828 page
5830 if rely > 1e-6
5831 then
5832 Printf.bprintf bb " rely='%f'" rely
5834 Buffer.add_string bb "/>\n";
5835 ) bookmarks;
5836 Buffer.add_string bb "</bookmarks>\n</doc>\n";
5837 end;
5841 let pan, conf =
5842 match state.mode with
5843 | Birdseye (c, pan, _, _, _) ->
5844 let beyecolumns =
5845 match conf.columns with
5846 | Some ((c, _, _), _) -> Some c
5847 | None -> None
5848 and columns =
5849 match c.columns with
5850 | Some (c, _) -> Some (c, [||])
5851 | None -> None
5853 pan, { c with beyecolumns = beyecolumns; columns = columns }
5854 | _ -> state.x, conf
5856 let basename = Filename.basename state.path in
5857 adddoc basename pan (getanchor ())
5858 { conf with
5859 autoscrollstep =
5860 match state.autoscroll with
5861 | Some step -> step
5862 | None -> conf.autoscrollstep }
5863 (if conf.savebmarks then state.bookmarks else []);
5865 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
5866 if basename <> path
5867 then adddoc path x y c bookmarks
5868 ) h;
5869 Buffer.add_string bb "</llppconfig>";
5871 load1 f;
5872 if Buffer.length bb > 0
5873 then
5875 let tmp = !confpath ^ ".tmp" in
5876 let oc = open_out_bin tmp in
5877 Buffer.output_buffer oc bb;
5878 close_out oc;
5879 Unix.rename tmp !confpath;
5880 with exn ->
5881 prerr_endline
5882 ("error while saving configuration: " ^ Printexc.to_string exn)
5884 end;;
5886 let () =
5887 Arg.parse
5888 (Arg.align
5889 [("-p", Arg.String (fun s -> state.password <- s) ,
5890 "<password> Set password");
5892 ("-f", Arg.String (fun s -> Config.fontpath := s),
5893 "<path> Set path to the user interface font");
5895 ("-c", Arg.String (fun s -> Config.confpath := s),
5896 "<path> Set path to the configuration file");
5898 ("-v", Arg.Unit (fun () ->
5899 Printf.printf
5900 "%s\nconfiguration path: %s\n"
5901 (version ())
5902 Config.defconfpath
5904 exit 0), " Print version and exit");
5907 (fun s -> state.path <- s)
5908 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
5910 if String.length state.path = 0
5911 then (prerr_endline "file name missing"; exit 1);
5913 Config.load ();
5915 let _ = Glut.init Sys.argv in
5916 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
5917 let () = Glut.initWindowSize conf.winw conf.winh in
5918 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
5920 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
5921 || Glut.extensionSupported "GL_EXT_texture_rectangle")
5922 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
5924 let () = Glut.displayFunc display in
5925 let () = Glut.reshapeFunc reshape in
5926 let () = Glut.keyboardFunc keyboard in
5927 let () = Glut.specialFunc special in
5928 let () = Glut.idleFunc (Some idle) in
5929 let () = Glut.mouseFunc mouse in
5930 let () = Glut.motionFunc motion in
5931 let () = Glut.passiveMotionFunc pmotion in
5933 let cr, sw = Unix.pipe ()
5934 and sr, cw = Unix.pipe () in
5936 setcheckers conf.checkers;
5937 redirectstderr ();
5939 init (cr, cw) (
5940 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5941 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5942 !Config.wmclasshack, !Config.fontpath
5944 state.sr <- sr;
5945 state.sw <- sw;
5946 state.text <- "Opening " ^ state.path;
5947 setaalevel conf.aalevel;
5948 writeopen state.path state.password;
5949 state.uioh <- uioh;
5950 setfontsize fstate.fontsize;
5952 while true do
5954 Glut.mainLoop ();
5955 with
5956 | Glut.BadEnum "key in special_of_int" ->
5957 showtext '!' " LablGlut bug: special key not recognized";
5959 | Quit ->
5960 wcmd "quit" [];
5961 Config.save ();
5962 exit 0
5964 | exn when conf.redirectstderr && not is_gui ->
5965 let s =
5966 Printf.sprintf "exception %s\n%s"
5967 (Printexc.to_string exn)
5968 (Printexc.get_backtrace ())
5970 ignore (try
5971 Unix.single_write state.stderr s 0 (String.length s);
5972 with _ -> 0);
5973 exit 1
5974 done;