Shrug again
[llpp.git] / main.ml
blobda20dfddd743747062b30c4cc4c952bc6c5b467b
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 | Posx | Psun
45 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
46 | Pmingw | 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";;
65 let platform_to_string = function
66 | Punknown -> "unknown"
67 | Plinux -> "Linux"
68 | Pwindows -> "Windows"
69 | Posx -> "OSX"
70 | Psun -> "Sun"
71 | Pfreebsd -> "FreeBSD"
72 | Pdragonflybsd -> "DragonflyBSD"
73 | Popenbsd -> "OpenBSD"
74 | Pnetbsd -> "NetBSD"
75 | Pcygwin -> "Cygwin"
76 | Pmingw -> "MingW"
79 let platform = platform ();;
81 let is_windows =
82 match platform with
83 | Pwindows | Pmingw -> true
84 | _ -> false
87 type x = int
88 and y = int
89 and tilex = int
90 and tiley = int
91 and tileparams = (x * y * width * height * tilex * tiley)
94 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
96 type mpos = int * int
97 and mstate =
98 | Msel of (mpos * mpos)
99 | Mpan of mpos
100 | Mscrolly | Mscrollx
101 | Mzoom of (int * int)
102 | Mzoomrect of (mpos * mpos)
103 | Mnone
106 type textentry = string * string * onhist option * onkey * ondone
107 and onkey = string -> int -> te
108 and ondone = string -> unit
109 and histcancel = unit -> unit
110 and onhist = ((histcmd -> string) * histcancel)
111 and histcmd = HCnext | HCprev | HCfirst | HClast
112 and te =
113 | TEstop
114 | TEdone of string
115 | TEcont of string
116 | TEswitch of textentry
119 type 'a circbuf =
120 { store : 'a array
121 ; mutable rc : int
122 ; mutable wc : int
123 ; mutable len : int
127 let bound v minv maxv =
128 max minv (min maxv v);
131 let cbnew n v =
132 { store = Array.create n v
133 ; rc = 0
134 ; wc = 0
135 ; len = 0
139 let drawstring size x y s =
140 Gl.enable `blend;
141 Gl.enable `texture_2d;
142 ignore (drawstr size x y s);
143 Gl.disable `blend;
144 Gl.disable `texture_2d;
147 let drawstring1 size x y s =
148 drawstr size x y s;
151 let drawstring2 size x y fmt =
152 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
155 let cbcap b = Array.length b.store;;
157 let cbput b v =
158 let cap = cbcap b in
159 b.store.(b.wc) <- v;
160 b.wc <- (b.wc + 1) mod cap;
161 b.rc <- b.wc;
162 b.len <- min (b.len + 1) cap;
165 let cbempty b = b.len = 0;;
167 let cbgetg b circular dir =
168 if cbempty b
169 then b.store.(0)
170 else
171 let rc = b.rc + dir in
172 let rc =
173 if circular
174 then (
175 if rc = -1
176 then b.len-1
177 else (
178 if rc = b.len
179 then 0
180 else rc
183 else max 0 (min rc (b.len-1))
185 b.rc <- rc;
186 b.store.(rc);
189 let cbget b = cbgetg b false;;
190 let cbgetc b = cbgetg b true;;
192 type page =
193 { pageno : int
194 ; pagedimno : int
195 ; pagew : int
196 ; pageh : int
197 ; pagex : int
198 ; pagey : int
199 ; pagevw : int
200 ; pagevh : int
201 ; pagedispx : int
202 ; pagedispy : int
206 let debugl l =
207 dolog "l %d dim=%d {" l.pageno l.pagedimno;
208 dolog " WxH %dx%d" l.pagew l.pageh;
209 dolog " vWxH %dx%d" l.pagevw l.pagevh;
210 dolog " pagex,y %d,%d" l.pagex l.pagey;
211 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
212 dolog "}";
215 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
216 dolog "rect {";
217 dolog " x0,y0=(% f, % f)" x0 y0;
218 dolog " x1,y1=(% f, % f)" x1 y1;
219 dolog " x2,y2=(% f, % f)" x2 y2;
220 dolog " x3,y3=(% f, % f)" x3 y3;
221 dolog "}";
224 type columns =
225 multicol * ((pdimno * x * y * (pageno * width * height * leftx)) array)
226 and multicol = columncount * covercount * covercount
227 and pdimno = int
228 and columncount = int
229 and covercount = int;;
231 type conf =
232 { mutable scrollbw : int
233 ; mutable scrollh : int
234 ; mutable icase : bool
235 ; mutable preload : bool
236 ; mutable pagebias : int
237 ; mutable verbose : bool
238 ; mutable debug : bool
239 ; mutable scrollstep : int
240 ; mutable maxhfit : bool
241 ; mutable crophack : bool
242 ; mutable autoscrollstep : int
243 ; mutable maxwait : float option
244 ; mutable hlinks : bool
245 ; mutable underinfo : bool
246 ; mutable interpagespace : interpagespace
247 ; mutable zoom : float
248 ; mutable presentation : bool
249 ; mutable angle : angle
250 ; mutable winw : int
251 ; mutable winh : int
252 ; mutable savebmarks : bool
253 ; mutable proportional : proportional
254 ; mutable trimmargins : trimmargins
255 ; mutable trimfuzz : irect
256 ; mutable memlimit : memsize
257 ; mutable texcount : texcount
258 ; mutable sliceheight : sliceheight
259 ; mutable thumbw : width
260 ; mutable jumpback : bool
261 ; mutable bgcolor : float * float * float
262 ; mutable bedefault : bool
263 ; mutable scrollbarinpm : bool
264 ; mutable tilew : int
265 ; mutable tileh : int
266 ; mutable mustoresize : memsize
267 ; mutable checkers : bool
268 ; mutable aalevel : int
269 ; mutable urilauncher : string
270 ; mutable colorspace : colorspace
271 ; mutable invert : bool
272 ; mutable colorscale : float
273 ; mutable redirectstderr : bool
274 ; mutable ghyllscroll : (int * int * int) option
275 ; mutable columns : columns option
276 ; mutable beyecolumns : columncount option
277 ; mutable selcmd : string
281 type anchor = pageno * top;;
283 type outline = string * int * anchor;;
285 type rect = float * float * float * float * float * float * float * float;;
287 type tile = opaque * pixmapsize * elapsed
288 and elapsed = float;;
289 type pagemapkey = pageno * gen;;
290 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
291 and row = int
292 and col = int;;
294 let emptyanchor = (0, 0.0);;
296 type infochange = | Memused | Docinfo | Pdim;;
298 class type uioh = object
299 method display : unit
300 method key : int -> uioh
301 method special : Glut.special_key_t -> uioh
302 method button :
303 Glut.button_t -> Glut.mouse_button_state_t -> int -> int -> uioh
304 method motion : int -> int -> uioh
305 method pmotion : int -> int -> uioh
306 method infochanged : infochange -> unit
307 method scrollpw : (int * float * float)
308 method scrollph : (int * float * float)
309 end;;
311 type mode =
312 | Birdseye of (conf * leftx * pageno * pageno * anchor)
313 | Textentry of (textentry * onleave)
314 | View
315 and onleave = leavetextentrystatus -> unit
316 and leavetextentrystatus = | Cancel | Confirm
317 and helpitem = string * int * action
318 and action =
319 | Noaction
320 | Action of (uioh -> uioh)
323 let isbirdseye = function Birdseye _ -> true | _ -> false;;
324 let istextentry = function Textentry _ -> true | _ -> false;;
326 type currently =
327 | Idle
328 | Loading of (page * gen)
329 | Tiling of (
330 page * opaque * colorspace * angle * gen * col * row * width * height
332 | Outlining of outline list
335 let nouioh : uioh = object (self)
336 method display = ()
337 method key _ = self
338 method special _ = self
339 method button _ _ _ _ = self
340 method motion _ _ = self
341 method pmotion _ _ = self
342 method infochanged _ = ()
343 method scrollpw = (0, nan, nan)
344 method scrollph = (0, nan, nan)
345 end;;
347 type state =
348 { mutable sr : Unix.file_descr
349 ; mutable sw : Unix.file_descr
350 ; mutable errfd : Unix.file_descr option
351 ; mutable stderr : Unix.file_descr
352 ; mutable errmsgs : Buffer.t
353 ; mutable newerrmsgs : bool
354 ; mutable w : int
355 ; mutable x : int
356 ; mutable y : int
357 ; mutable scrollw : int
358 ; mutable hscrollh : int
359 ; mutable anchor : anchor
360 ; mutable ranchors : (string * string * anchor) list
361 ; mutable maxy : int
362 ; mutable layout : page list
363 ; pagemap : (pagemapkey, opaque) Hashtbl.t
364 ; tilemap : (tilemapkey, tile) Hashtbl.t
365 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
366 ; mutable pdims : (pageno * width * height * leftx) list
367 ; mutable pagecount : int
368 ; mutable currently : currently
369 ; mutable mstate : mstate
370 ; mutable searchpattern : string
371 ; mutable rects : (pageno * recttype * rect) list
372 ; mutable rects1 : (pageno * recttype * rect) list
373 ; mutable text : string
374 ; mutable fullscreen : (width * height) option
375 ; mutable mode : mode
376 ; mutable uioh : uioh
377 ; mutable outlines : outline array
378 ; mutable bookmarks : outline list
379 ; mutable path : string
380 ; mutable password : string
381 ; mutable invalidated : int
382 ; mutable memused : memsize
383 ; mutable gen : gen
384 ; mutable throttle : (page list * int * float) option
385 ; mutable autoscroll : int option
386 ; mutable ghyll : int option -> unit
387 ; mutable help : helpitem array
388 ; mutable docinfo : (int * string) list
389 ; mutable deadline : float
390 ; mutable texid : GlTex.texture_id option
391 ; hists : hists
392 ; mutable prevzoom : float
393 ; mutable progress : float
395 and hists =
396 { pat : string circbuf
397 ; pag : string circbuf
398 ; nav : anchor circbuf
399 ; sel : string circbuf
403 let defconf =
404 { scrollbw = 7
405 ; scrollh = 12
406 ; icase = true
407 ; preload = true
408 ; pagebias = 0
409 ; verbose = false
410 ; debug = false
411 ; scrollstep = 24
412 ; maxhfit = true
413 ; crophack = false
414 ; autoscrollstep = 2
415 ; maxwait = None
416 ; hlinks = false
417 ; underinfo = false
418 ; interpagespace = 2
419 ; zoom = 1.0
420 ; presentation = false
421 ; angle = 0
422 ; winw = 900
423 ; winh = 900
424 ; savebmarks = true
425 ; proportional = true
426 ; trimmargins = false
427 ; trimfuzz = (0,0,0,0)
428 ; memlimit = 32 lsl 20
429 ; texcount = 256
430 ; sliceheight = 24
431 ; thumbw = 76
432 ; jumpback = true
433 ; bgcolor = (0.5, 0.5, 0.5)
434 ; bedefault = false
435 ; scrollbarinpm = true
436 ; tilew = 2048
437 ; tileh = 2048
438 ; mustoresize = 128 lsl 20
439 ; checkers = true
440 ; aalevel = 8
441 ; urilauncher =
442 (match platform with
443 | Plinux
444 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
445 | Psun -> "xdg-open \"%s\""
446 | Posx -> "open \"%s\""
447 | Pwindows | Pcygwin | Pmingw -> "start %s"
448 | Punknown -> "echo %s")
449 ; selcmd =
450 (match platform with
451 | Plinux
452 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
453 | Psun -> "xsel -i"
454 | Posx -> "pbcopy"
455 | Pwindows | Pcygwin | Pmingw -> "wsel"
456 | Punknown -> "cat")
457 ; colorspace = Rgb
458 ; invert = false
459 ; colorscale = 1.0
460 ; redirectstderr = false
461 ; ghyllscroll = None
462 ; columns = None
463 ; beyecolumns = None
467 let conf = { defconf with angle = defconf.angle };;
469 type fontstate =
470 { mutable fontsize : int
471 ; mutable wwidth : float
472 ; mutable maxrows : int
476 let fstate =
477 { fontsize = 14
478 ; wwidth = nan
479 ; maxrows = -1
483 let setfontsize n =
484 fstate.fontsize <- n;
485 fstate.wwidth <- measurestr fstate.fontsize "w";
486 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
489 let geturl s =
490 let colonpos = try String.index s ':' with Not_found -> -1 in
491 let len = String.length s in
492 if colonpos >= 0 && colonpos + 3 < len
493 then (
494 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
495 then
496 let schemestartpos =
497 try String.rindex_from s colonpos ' '
498 with Not_found -> -1
500 let scheme =
501 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
503 match scheme with
504 | "http" | "ftp" | "mailto" ->
505 let epos =
506 try String.index_from s colonpos ' '
507 with Not_found -> len
509 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
510 | _ -> ""
511 else ""
513 else ""
516 let popen =
517 let shell, farg =
518 if is_windows
519 then (try Sys.getenv "COMSPEC" with Not_found -> "cmd"), "/c"
520 else "/bin/sh", "-c"
522 fun s ->
523 let args = [|shell; farg; s|] in
524 ignore (Unix.create_process shell args Unix.stdin Unix.stdout Unix.stderr)
527 let gotouri uri =
528 if String.length conf.urilauncher = 0
529 then print_endline uri
530 else (
531 let url = geturl uri in
532 if String.length url = 0
533 then print_endline uri
534 else
535 let re = Str.regexp "%s" in
536 let command = Str.global_replace re url conf.urilauncher in
537 try popen command
538 with exn ->
539 Printf.eprintf
540 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
541 flush stderr;
545 let version () =
546 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
547 (platform_to_string platform) Sys.word_size Sys.ocaml_version
550 let makehelp () =
551 let strings = version () :: "" :: Help.keys in
552 Array.of_list (
553 List.map (fun s ->
554 let url = geturl s in
555 if String.length url > 0
556 then (s, 0, Action (fun u -> gotouri url; u))
557 else (s, 0, Noaction)
558 ) strings);
561 let noghyll _ = ();;
563 let state =
564 { sr = Unix.stdin
565 ; sw = Unix.stdin
566 ; errfd = None
567 ; stderr = Unix.stderr
568 ; errmsgs = Buffer.create 0
569 ; newerrmsgs = false
570 ; x = 0
571 ; y = 0
572 ; w = 0
573 ; scrollw = 0
574 ; hscrollh = 0
575 ; anchor = emptyanchor
576 ; ranchors = []
577 ; layout = []
578 ; maxy = max_int
579 ; tilelru = Queue.create ()
580 ; pagemap = Hashtbl.create 10
581 ; tilemap = Hashtbl.create 10
582 ; pdims = []
583 ; pagecount = 0
584 ; currently = Idle
585 ; mstate = Mnone
586 ; rects = []
587 ; rects1 = []
588 ; text = ""
589 ; mode = View
590 ; fullscreen = None
591 ; searchpattern = ""
592 ; outlines = [||]
593 ; bookmarks = []
594 ; path = ""
595 ; password = ""
596 ; invalidated = 0
597 ; hists =
598 { nav = cbnew 10 (0, 0.0)
599 ; pat = cbnew 10 ""
600 ; pag = cbnew 10 ""
601 ; sel = cbnew 10 ""
603 ; memused = 0
604 ; gen = 0
605 ; throttle = None
606 ; autoscroll = None
607 ; ghyll = noghyll
608 ; help = makehelp ()
609 ; docinfo = []
610 ; deadline = nan
611 ; texid = None
612 ; prevzoom = 1.0
613 ; progress = -1.0
614 ; uioh = nouioh
618 let vlog fmt =
619 if conf.verbose
620 then
621 Printf.kprintf prerr_endline fmt
622 else
623 Printf.kprintf ignore fmt
626 let redirectstderr () =
627 if conf.redirectstderr
628 then
629 let rfd, wfd = Unix.pipe () in
630 state.stderr <- Unix.dup Unix.stderr;
631 state.errfd <- Some rfd;
632 Unix.dup2 wfd Unix.stderr;
633 else (
634 state.newerrmsgs <- false;
635 begin match state.errfd with
636 | Some fd ->
637 Unix.close fd;
638 Unix.dup2 state.stderr Unix.stderr;
639 state.errfd <- None;
640 | None -> ()
641 end;
642 prerr_string (Buffer.contents state.errmsgs);
643 flush stderr;
644 Buffer.clear state.errmsgs;
648 module G =
649 struct
650 let postRedisplay who =
651 if conf.verbose
652 then prerr_endline ("redisplay for " ^ who);
653 Glut.postRedisplay ();
655 end;;
657 let addchar s c =
658 let b = Buffer.create (String.length s + 1) in
659 Buffer.add_string b s;
660 Buffer.add_char b c;
661 Buffer.contents b;
664 let colorspace_of_string s =
665 match String.lowercase s with
666 | "rgb" -> Rgb
667 | "bgr" -> Bgr
668 | "gray" -> Gray
669 | _ -> failwith "invalid colorspace"
672 let int_of_colorspace = function
673 | Rgb -> 0
674 | Bgr -> 1
675 | Gray -> 2
678 let colorspace_of_int = function
679 | 0 -> Rgb
680 | 1 -> Bgr
681 | 2 -> Gray
682 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
685 let colorspace_to_string = function
686 | Rgb -> "rgb"
687 | Bgr -> "bgr"
688 | Gray -> "gray"
691 let intentry_with_suffix text key =
692 let c = Char.unsafe_chr key in
693 match Char.lowercase c with
694 | '0' .. '9' ->
695 let text = addchar text c in
696 TEcont text
698 | 'k' | 'm' | 'g' ->
699 let text = addchar text c in
700 TEcont text
702 | _ ->
703 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
704 TEcont text
707 let columns_to_string (n, a, b) =
708 if a = 0 && b = 0
709 then Printf.sprintf "%d" n
710 else Printf.sprintf "%d,%d,%d" n a b;
713 let columns_of_string s =
715 (int_of_string s, 0, 0)
716 with _ ->
717 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
720 let writecmd fd s =
721 let len = String.length s in
722 let n = 4 + len in
723 let b = Buffer.create n in
724 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
725 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
726 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
727 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
728 Buffer.add_string b s;
729 let s' = Buffer.contents b in
730 let n' = Unix.write fd s' 0 n in
731 if n' != n then failwith "write failed";
734 let readcmd fd =
735 let s = "xxxx" in
736 let n = Unix.read fd s 0 4 in
737 if n != 4 then failwith "incomplete read(len)";
738 let len = 0
739 lor (Char.code s.[0] lsl 24)
740 lor (Char.code s.[1] lsl 16)
741 lor (Char.code s.[2] lsl 8)
742 lor (Char.code s.[3] lsl 0)
744 let s = String.create len in
745 let n = Unix.read fd s 0 len in
746 if n != len then failwith "incomplete read(data)";
750 let makecmd s l =
751 let b = Buffer.create 10 in
752 Buffer.add_string b s;
753 let rec combine = function
754 | [] -> b
755 | x :: xs ->
756 Buffer.add_char b ' ';
757 let s =
758 match x with
759 | `b b -> if b then "1" else "0"
760 | `s s -> s
761 | `i i -> string_of_int i
762 | `f f -> string_of_float f
763 | `I f -> string_of_int (truncate f)
765 Buffer.add_string b s;
766 combine xs;
768 combine l;
771 let wcmd s l =
772 let cmd = Buffer.contents (makecmd s l) in
773 writecmd state.sw cmd;
776 let calcips h =
777 if conf.presentation
778 then
779 let d = conf.winh - h in
780 max 0 ((d + 1) / 2)
781 else
782 conf.interpagespace
785 let calcheight () =
786 let rec f pn ph pi fh l =
787 match l with
788 | (n, _, h, _) :: rest ->
789 let ips = calcips h in
790 let fh =
791 if conf.presentation
792 then fh+ips
793 else (
794 if isbirdseye state.mode && pn = 0
795 then fh + ips
796 else fh
799 let fh = fh + ((n - pn) * (ph + pi)) in
800 f n h ips fh rest;
802 | [] ->
803 let inc =
804 if conf.presentation || (isbirdseye state.mode && pn = 0)
805 then 0
806 else -pi
808 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
809 max 0 fh
811 let fh = f 0 0 0 0 state.pdims in
815 let calcheight () =
816 match conf.columns with
817 | None -> calcheight ()
818 | Some (_, b) ->
819 if Array.length b > 0
820 then
821 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
822 y + h
823 else 0
826 let getpageyh pageno =
827 let rec f pn ph pi y l =
828 match l with
829 | (n, _, h, _) :: rest ->
830 let ips = calcips h in
831 if n >= pageno
832 then
833 let h = if n = pageno then h else ph in
834 if conf.presentation && n = pageno
835 then
836 y + (pageno - pn) * (ph + pi) + pi, h
837 else
838 y + (pageno - pn) * (ph + pi), h
839 else
840 let y = y + (if conf.presentation then pi else 0) in
841 let y = y + (n - pn) * (ph + pi) in
842 f n h ips y rest
844 | [] ->
845 y + (pageno - pn) * (ph + pi), ph
847 f 0 0 0 0 state.pdims
850 let getpageyh pageno =
851 match conf.columns with
852 | None -> getpageyh pageno
853 | Some (_, b) ->
854 let (_, _, y, (_, _, h, _)) = b.(pageno) in
855 y, h
858 let getpagedim pageno =
859 let rec f ppdim l =
860 match l with
861 | (n, _, _, _) as pdim :: rest ->
862 if n >= pageno
863 then (if n = pageno then pdim else ppdim)
864 else f pdim rest
866 | [] -> ppdim
868 f (-1, -1, -1, -1) state.pdims
871 let getpagey pageno = fst (getpageyh pageno);;
873 let layout1 y sh =
874 let sh = sh - state.hscrollh in
875 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
876 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
877 match pdims with
878 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
879 let ips = calcips h in
880 let yinc =
881 if conf.presentation || (isbirdseye state.mode && pageno = 0)
882 then ips
883 else 0
885 (w, h, ips, xoff), rest, pdimno + 1, yinc
886 | _ ->
887 prev, pdims, pdimno, 0
889 let dy = dy + yinc in
890 let py = py + yinc in
891 if pageno = state.pagecount || dy >= sh
892 then
893 accu
894 else
895 let vy = y + dy in
896 if py + h <= vy - yinc
897 then
898 let py = py + h + ips in
899 let dy = max 0 (py - y) in
900 f ~pageno:(pageno+1)
901 ~pdimno
902 ~prev:curr
905 ~pdims:rest
906 ~accu
907 else
908 let pagey = vy - py in
909 let pagevh = h - pagey in
910 let pagevh = min (sh - dy) pagevh in
911 let off = if yinc > 0 then py - vy else 0 in
912 let py = py + h + ips in
913 let pagex, dx =
914 let xoff = xoff +
915 if state.w < conf.winw - state.scrollw
916 then (conf.winw - state.scrollw - state.w) / 2
917 else 0
919 let dispx = xoff + state.x in
920 if dispx < 0
921 then (-dispx, 0)
922 else (0, dispx)
924 let pagevw =
925 let lw = w - pagex in
926 min lw (conf.winw - state.scrollw)
928 let e =
929 { pageno = pageno
930 ; pagedimno = pdimno
931 ; pagew = w
932 ; pageh = h
933 ; pagex = pagex
934 ; pagey = pagey + off
935 ; pagevw = pagevw
936 ; pagevh = pagevh - off
937 ; pagedispx = dx
938 ; pagedispy = dy + off
941 let accu = e :: accu in
942 f ~pageno:(pageno+1)
943 ~pdimno
944 ~prev:curr
946 ~dy:(dy+pagevh+ips)
947 ~pdims:rest
948 ~accu
950 if state.invalidated = 0
951 then (
952 let accu =
954 ~pageno:0
955 ~pdimno:~-1
956 ~prev:(0,0,0,0)
957 ~py:0
958 ~dy:0
959 ~pdims:state.pdims
960 ~accu:[]
962 List.rev accu
964 else
968 let layoutN ((columns, coverA, coverB), b) y sh =
969 let sh = sh - state.hscrollh in
970 let rec fold accu n =
971 if n = Array.length b
972 then accu
973 else
974 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
975 if (vy - y) > sh &&
976 (n = coverA - 1
977 || n = state.pagecount - coverB
978 || (n - coverA) mod columns = columns - 1)
979 then accu
980 else
981 let accu =
982 if vy + h > y
983 then
984 let pagey = max 0 (y - vy) in
985 let pagedispy = if pagey > 0 then 0 else vy - y in
986 let pagedispx, pagex, pagevw =
987 let pdx =
988 if n = coverA - 1 || n = state.pagecount - coverB
989 then state.x + (conf.winw - state.scrollw - w) / 2
990 else dx + xoff + state.x
992 if pdx < 0
993 then 0, -pdx, w + pdx
994 else pdx, 0, min (conf.winw - state.scrollw) w
996 let pagevh = min (h - pagey) (sh - pagedispy) in
997 if pagedispx < conf.winw - state.scrollw && pagevw > 0 && pagevh > 0
998 then
999 let e =
1000 { pageno = n
1001 ; pagedimno = pdimno
1002 ; pagew = w
1003 ; pageh = h
1004 ; pagex = pagex
1005 ; pagey = pagey
1006 ; pagevw = pagevw
1007 ; pagevh = pagevh
1008 ; pagedispx = pagedispx
1009 ; pagedispy = pagedispy
1012 e :: accu
1013 else
1014 accu
1015 else
1016 accu
1018 fold accu (n+1)
1020 if state.invalidated = 0
1021 then List.rev (fold [] 0)
1022 else []
1025 let layout y sh =
1026 match conf.columns with
1027 | None -> layout1 y sh
1028 | Some c -> layoutN c y sh
1031 let clamp incr =
1032 let y = state.y + incr in
1033 let y = max 0 y in
1034 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1038 let getopaque pageno =
1039 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
1040 with Not_found -> None
1043 let putopaque pageno opaque =
1044 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
1047 let itertiles l f =
1048 let tilex = l.pagex mod conf.tilew in
1049 let tiley = l.pagey mod conf.tileh in
1051 let col = l.pagex / conf.tilew in
1052 let row = l.pagey / conf.tileh in
1054 let vw =
1055 let a = l.pagew - l.pagex in
1056 let b = conf.winw - state.scrollw in
1057 min a b
1058 and vh = l.pagevh in
1060 let rec rowloop row y0 dispy h =
1061 if h = 0
1062 then ()
1063 else (
1064 let dh = conf.tileh - y0 in
1065 let dh = min h dh in
1066 let rec colloop col x0 dispx w =
1067 if w = 0
1068 then ()
1069 else (
1070 let dw = conf.tilew - x0 in
1071 let dw = min w dw in
1073 f col row dispx dispy x0 y0 dw dh;
1074 colloop (col+1) 0 (dispx+dw) (w-dw)
1077 colloop col tilex l.pagedispx vw;
1078 rowloop (row+1) 0 (dispy+dh) (h-dh)
1081 if vw > 0 && vh > 0
1082 then rowloop row tiley l.pagedispy vh;
1085 let gettileopaque l col row =
1086 let key =
1087 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1089 try Some (Hashtbl.find state.tilemap key)
1090 with Not_found -> None
1093 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1094 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1095 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1098 let drawtiles l color =
1099 GlDraw.color color;
1100 let f col row x y tilex tiley w h =
1101 match gettileopaque l col row with
1102 | Some (opaque, _, t) ->
1103 let params = x, y, w, h, tilex, tiley in
1104 if conf.invert
1105 then (
1106 Gl.enable `blend;
1107 GlFunc.blend_func `zero `one_minus_src_color;
1109 drawtile params opaque;
1110 if conf.invert
1111 then Gl.disable `blend;
1112 if conf.debug
1113 then (
1114 let s = Printf.sprintf
1115 "%d[%d,%d] %f sec"
1116 l.pageno col row t
1118 let w = measurestr fstate.fontsize s in
1119 GlMisc.push_attrib [`current];
1120 GlDraw.color (0.0, 0.0, 0.0);
1121 GlDraw.rect
1122 (float (x-2), float (y-2))
1123 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1124 GlDraw.color (1.0, 1.0, 1.0);
1125 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1126 GlMisc.pop_attrib ();
1129 | _ ->
1130 let w =
1131 let lw = conf.winw - state.scrollw - x in
1132 min lw w
1133 and h =
1134 let lh = conf.winh - y in
1135 min lh h
1137 Gl.enable `texture_2d;
1138 begin match state.texid with
1139 | Some id ->
1140 GlTex.bind_texture `texture_2d id;
1141 let x0 = float x
1142 and y0 = float y
1143 and x1 = float (x+w)
1144 and y1 = float (y+h) in
1146 let tw = float w /. 64.0
1147 and th = float h /. 64.0 in
1148 let tx0 = float tilex /. 64.0
1149 and ty0 = float tiley /. 64.0 in
1150 let tx1 = tx0 +. tw
1151 and ty1 = ty0 +. th in
1152 GlDraw.begins `quads;
1153 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1154 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1155 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1156 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1157 GlDraw.ends ();
1159 Gl.disable `texture_2d;
1160 | None ->
1161 GlDraw.color (1.0, 1.0, 1.0);
1162 GlDraw.rect
1163 (float x, float y)
1164 (float (x+w), float (y+h));
1165 end;
1166 if w > 128 && h > fstate.fontsize + 10
1167 then (
1168 GlDraw.color (0.0, 0.0, 0.0);
1169 let c, r =
1170 if conf.verbose
1171 then (col*conf.tilew, row*conf.tileh)
1172 else col, row
1174 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1176 GlDraw.color color;
1178 itertiles l f
1181 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1183 let tilevisible1 l x y =
1184 let ax0 = l.pagex
1185 and ax1 = l.pagex + l.pagevw
1186 and ay0 = l.pagey
1187 and ay1 = l.pagey + l.pagevh in
1189 let bx0 = x
1190 and by0 = y in
1191 let bx1 = min (bx0 + conf.tilew) l.pagew
1192 and by1 = min (by0 + conf.tileh) l.pageh in
1194 let rx0 = max ax0 bx0
1195 and ry0 = max ay0 by0
1196 and rx1 = min ax1 bx1
1197 and ry1 = min ay1 by1 in
1199 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1200 nonemptyintersection
1203 let tilevisible layout n x y =
1204 let rec findpageinlayout = function
1205 | l :: _ when l.pageno = n -> tilevisible1 l x y
1206 | _ :: rest -> findpageinlayout rest
1207 | [] -> false
1209 findpageinlayout layout
1212 let tileready l x y =
1213 tilevisible1 l x y &&
1214 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1217 let tilepage n p layout =
1218 let rec loop = function
1219 | l :: rest ->
1220 if l.pageno = n
1221 then
1222 let f col row _ _ _ _ _ _ =
1223 if state.currently = Idle
1224 then
1225 match gettileopaque l col row with
1226 | Some _ -> ()
1227 | None ->
1228 let x = col*conf.tilew
1229 and y = row*conf.tileh in
1230 let w =
1231 let w = l.pagew - x in
1232 min w conf.tilew
1234 let h =
1235 let h = l.pageh - y in
1236 min h conf.tileh
1238 wcmd "tile"
1239 [`s p
1240 ;`i x
1241 ;`i y
1242 ;`i w
1243 ;`i h
1245 state.currently <-
1246 Tiling (
1247 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1248 conf.tilew, conf.tileh
1251 itertiles l f;
1252 else
1253 loop rest
1255 | [] -> ()
1257 if state.invalidated = 0 then loop layout;
1260 let preloadlayout visiblepages =
1261 let presentation = conf.presentation in
1262 let interpagespace = conf.interpagespace in
1263 let maxy = state.maxy in
1264 conf.presentation <- false;
1265 conf.interpagespace <- 0;
1266 state.maxy <- calcheight ();
1267 let y =
1268 match visiblepages with
1269 | [] -> 0
1270 | l :: _ -> getpagey l.pageno + l.pagey
1272 let y = if y < conf.winh then 0 else y - conf.winh in
1273 let h = state.y - y + conf.winh*3 in
1274 let pages = layout y h in
1275 conf.presentation <- presentation;
1276 conf.interpagespace <- interpagespace;
1277 state.maxy <- maxy;
1278 pages;
1281 let load pages =
1282 let rec loop pages =
1283 if state.currently != Idle
1284 then ()
1285 else
1286 match pages with
1287 | l :: rest ->
1288 begin match getopaque l.pageno with
1289 | None ->
1290 wcmd "page" [`i l.pageno; `i l.pagedimno];
1291 state.currently <- Loading (l, state.gen);
1292 | Some opaque ->
1293 tilepage l.pageno opaque pages;
1294 loop rest
1295 end;
1296 | _ -> ()
1298 if state.invalidated = 0 then loop pages
1301 let preload pages =
1302 load pages;
1303 if conf.preload && state.currently = Idle
1304 then load (preloadlayout pages);
1307 let layoutready layout =
1308 let rec fold all ls =
1309 all && match ls with
1310 | l :: rest ->
1311 let seen = ref false in
1312 let allvisible = ref true in
1313 let foo col row _ _ _ _ _ _ =
1314 seen := true;
1315 allvisible := !allvisible &&
1316 begin match gettileopaque l col row with
1317 | Some _ -> true
1318 | None -> false
1321 itertiles l foo;
1322 fold (!seen && !allvisible) rest
1323 | [] -> true
1325 let alltilesvisible = fold true layout in
1326 alltilesvisible;
1329 let gotoy y =
1330 let y = bound y 0 state.maxy in
1331 let y, layout, proceed =
1332 match conf.maxwait with
1333 | Some time when state.ghyll == noghyll ->
1334 begin match state.throttle with
1335 | None ->
1336 let layout = layout y conf.winh in
1337 let ready = layoutready layout in
1338 if not ready
1339 then (
1340 load layout;
1341 state.throttle <- Some (layout, y, now ());
1343 else G.postRedisplay "gotoy showall (None)";
1344 y, layout, ready
1345 | Some (_, _, started) ->
1346 let dt = now () -. started in
1347 if dt > time
1348 then (
1349 state.throttle <- None;
1350 let layout = layout y conf.winh in
1351 load layout;
1352 G.postRedisplay "maxwait";
1353 y, layout, true
1355 else -1, [], false
1358 | _ ->
1359 let layout = layout y conf.winh in
1360 if true || layoutready layout
1361 then G.postRedisplay "gotoy ready";
1362 y, layout, true
1364 if proceed
1365 then (
1366 state.y <- y;
1367 state.layout <- layout;
1368 begin match state.mode with
1369 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1370 if not (pagevisible layout pageno)
1371 then (
1372 match state.layout with
1373 | [] -> ()
1374 | l :: _ ->
1375 state.mode <- Birdseye (
1376 conf, leftx, l.pageno, hooverpageno, anchor
1379 | _ -> ()
1380 end;
1381 preload layout;
1383 state.ghyll <- noghyll;
1386 let conttiling pageno opaque =
1387 tilepage pageno opaque
1388 (if conf.preload then preloadlayout state.layout else state.layout)
1391 let gotoy_and_clear_text y =
1392 gotoy y;
1393 if not conf.verbose then state.text <- "";
1396 let getanchor () =
1397 match state.layout with
1398 | [] -> emptyanchor
1399 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1402 let getanchory (n, top) =
1403 let y, h = getpageyh n in
1404 y + (truncate (top *. float h));
1407 let gotoanchor anchor =
1408 gotoy (getanchory anchor);
1411 let addnav () =
1412 cbput state.hists.nav (getanchor ());
1415 let getnav dir =
1416 let anchor = cbgetc state.hists.nav dir in
1417 getanchory anchor;
1420 let gotoghyll y =
1421 let rec scroll f n a b =
1422 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1423 let snake f a b =
1424 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1425 if f < a
1426 then s (float f /. float a)
1427 else (
1428 if f > b
1429 then 1.0 -. s ((float (f-b) /. float (n-b)))
1430 else 1.0
1433 snake f a b
1434 and summa f n a b =
1435 (* courtesy:
1436 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1437 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1438 let iv1 = iv f in
1439 let ins = float a *. iv1
1440 and outs = float (n-b) *. iv1 in
1441 let ones = b - a in
1442 ins +. outs +. float ones
1444 let rec set (_N, _A, _B) y sy =
1445 let sum = summa 1.0 _N _A _B in
1446 let dy = float (y - sy) in
1447 state.ghyll <- (
1448 let rec gf n y1 o =
1449 if n >= _N
1450 then state.ghyll <- noghyll
1451 else
1452 let go n =
1453 let s = scroll n _N _A _B in
1454 let y1 = y1 +. ((s *. dy) /. sum) in
1455 gotoy_and_clear_text (truncate y1);
1456 state.ghyll <- gf (n+1) y1;
1458 match o with
1459 | None -> go n
1460 | Some y' -> set (_N/2, 0, 0) y' state.y
1462 gf 0 (float state.y)
1465 match conf.ghyllscroll with
1466 | None ->
1467 gotoy_and_clear_text y
1468 | Some nab ->
1469 if state.ghyll == noghyll
1470 then set nab y state.y
1471 else state.ghyll (Some y)
1474 let gotopage n top =
1475 let y, h = getpageyh n in
1476 let y = y + (truncate (top *. float h)) in
1477 gotoghyll y
1480 let gotopage1 n top =
1481 let y = getpagey n in
1482 let y = y + top in
1483 gotoghyll y
1486 let invalidate () =
1487 state.layout <- [];
1488 state.pdims <- [];
1489 state.rects <- [];
1490 state.rects1 <- [];
1491 state.invalidated <- state.invalidated + 1;
1494 let writeopen path password =
1495 writecmd state.sw ("open " ^ path ^ "\000" ^ password ^ "\000");
1498 let opendoc path password =
1499 invalidate ();
1500 state.path <- path;
1501 state.password <- password;
1502 state.gen <- state.gen + 1;
1503 state.docinfo <- [];
1505 setaalevel conf.aalevel;
1506 writeopen path password;
1507 Glut.setWindowTitle ("llpp " ^ Filename.basename path);
1508 wcmd "geometry" [`i state.w; `i conf.winh];
1511 let scalecolor c =
1512 let c = c *. conf.colorscale in
1513 (c, c, c);
1516 let scalecolor2 (r, g, b) =
1517 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1520 let represent () =
1521 let docolumns = function
1522 | None -> ()
1523 | Some ((columns, coverA, coverB), _) ->
1524 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1525 let rec loop pageno pdimno pdim x y rowh pdims =
1526 if pageno = state.pagecount
1527 then ()
1528 else
1529 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1530 match pdims with
1531 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1532 pdimno+1, pdim, rest
1533 | _ ->
1534 pdimno, pdim, pdims
1536 let x, y, rowh' =
1537 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1538 then (
1539 (conf.winw - state.scrollw - w) / 2,
1540 y + rowh + conf.interpagespace, h
1542 else (
1543 if (pageno - coverA) mod columns = 0
1544 then 0, y + rowh + conf.interpagespace, h
1545 else x, y, max rowh h
1548 let rec fixrow m = if m = pageno then () else
1549 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1550 if h < rowh
1551 then (
1552 let y = y + (rowh - h) / 2 in
1553 a.(m) <- (pdimno, x, y, pdim);
1555 fixrow (m+1)
1557 if pageno > 1 && (pageno - coverA) mod columns = 0
1558 then fixrow (pageno - columns);
1559 a.(pageno) <- (pdimno, x, y, pdim);
1560 let x = x + w + xoff*2 + conf.interpagespace in
1561 loop (pageno+1) pdimno pdim x y rowh' pdims
1563 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1564 conf.columns <- Some ((columns, coverA, coverB), a);
1566 docolumns conf.columns;
1567 state.maxy <- calcheight ();
1568 state.hscrollh <-
1569 if state.w <= conf.winw - state.scrollw
1570 then 0
1571 else state.scrollw
1573 match state.mode with
1574 | Birdseye (_, _, pageno, _, _) ->
1575 let y, h = getpageyh pageno in
1576 let top = (conf.winh - h) / 2 in
1577 gotoy (max 0 (y - top))
1578 | _ -> gotoanchor state.anchor
1581 let reshape =
1582 let firsttime = ref true in
1583 fun ~w ~h ->
1584 GlDraw.viewport 0 0 w h;
1585 if state.invalidated = 0 && not !firsttime
1586 then state.anchor <- getanchor ();
1588 firsttime := false;
1589 conf.winw <- w;
1590 let w = truncate (float w *. conf.zoom) - state.scrollw in
1591 let w = max w 2 in
1592 state.w <- w;
1593 conf.winh <- h;
1594 setfontsize fstate.fontsize;
1595 GlMat.mode `modelview;
1596 GlMat.load_identity ();
1598 GlMat.mode `projection;
1599 GlMat.load_identity ();
1600 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1601 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1602 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1604 let w =
1605 match conf.columns with
1606 | None -> w
1607 | Some ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1609 invalidate ();
1610 wcmd "geometry" [`i w; `i h];
1613 let enttext () =
1614 let len = String.length state.text in
1615 let drawstring s =
1616 let hscrollh =
1617 match state.mode with
1618 | View -> state.hscrollh
1619 | _ -> 0
1621 let rect x w =
1622 GlDraw.rect
1623 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1624 (x+.w, float (conf.winh - hscrollh))
1627 let w = float (conf.winw - state.scrollw - 1) in
1628 if state.progress >= 0.0 && state.progress < 1.0
1629 then (
1630 GlDraw.color (0.3, 0.3, 0.3);
1631 let w1 = w *. state.progress in
1632 rect 0.0 w1;
1633 GlDraw.color (0.0, 0.0, 0.0);
1634 rect w1 (w-.w1)
1636 else (
1637 GlDraw.color (0.0, 0.0, 0.0);
1638 rect 0.0 w;
1641 GlDraw.color (1.0, 1.0, 1.0);
1642 drawstring fstate.fontsize
1643 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1645 let s =
1646 match state.mode with
1647 | Textentry ((prefix, text, _, _, _), _) ->
1648 let s =
1649 if len > 0
1650 then
1651 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1652 else
1653 Printf.sprintf "%s%s_" prefix text
1657 | _ -> state.text
1659 let s =
1660 if state.newerrmsgs
1661 then (
1662 if not (istextentry state.mode)
1663 then
1664 let s1 = "(press 'e' to review error messasges)" in
1665 if String.length s > 0 then s ^ " " ^ s1 else s1
1666 else s
1668 else s
1670 if String.length s > 0
1671 then drawstring s
1674 let showtext c s =
1675 state.text <- Printf.sprintf "%c%s" c s;
1676 G.postRedisplay "showtext";
1679 let gctiles () =
1680 let len = Queue.length state.tilelru in
1681 let rec loop qpos =
1682 if state.memused <= conf.memlimit
1683 then ()
1684 else (
1685 if qpos < len
1686 then
1687 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1688 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1689 let (_, pw, ph, _) = getpagedim n in
1691 gen = state.gen
1692 && colorspace = conf.colorspace
1693 && angle = conf.angle
1694 && pagew = pw
1695 && pageh = ph
1696 && (
1697 let layout =
1698 match state.throttle with
1699 | None ->
1700 if conf.preload
1701 then preloadlayout state.layout
1702 else state.layout
1703 | Some (layout, _, _) ->
1704 layout
1706 let x = col*conf.tilew
1707 and y = row*conf.tileh in
1708 tilevisible layout n x y
1710 then Queue.push lruitem state.tilelru
1711 else (
1712 wcmd "freetile" [`s p];
1713 state.memused <- state.memused - s;
1714 state.uioh#infochanged Memused;
1715 Hashtbl.remove state.tilemap k;
1717 loop (qpos+1)
1720 loop 0
1723 let flushtiles () =
1724 Queue.iter (fun (k, p, s) ->
1725 wcmd "freetile" [`s p];
1726 state.memused <- state.memused - s;
1727 state.uioh#infochanged Memused;
1728 Hashtbl.remove state.tilemap k;
1729 ) state.tilelru;
1730 Queue.clear state.tilelru;
1731 load state.layout;
1734 let logcurrently = function
1735 | Idle -> dolog "Idle"
1736 | Loading (l, gen) ->
1737 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1738 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1739 dolog
1740 "Tiling %d[%d,%d] page=%s cs=%s angle"
1741 l.pageno col row pageopaque
1742 (colorspace_to_string colorspace)
1744 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1745 angle gen conf.angle state.gen
1746 tilew tileh
1747 conf.tilew conf.tileh
1749 | Outlining _ ->
1750 dolog "outlining"
1753 let act cmds =
1754 (* dolog "%S" cmds; *)
1755 let op, args =
1756 let spacepos =
1757 try String.index cmds ' '
1758 with Not_found -> -1
1760 if spacepos = -1
1761 then cmds, ""
1762 else
1763 let l = String.length cmds in
1764 let op = String.sub cmds 0 spacepos in
1765 op, begin
1766 if l - spacepos < 2 then ""
1767 else String.sub cmds (spacepos+1) (l-spacepos-1)
1770 match op with
1771 | "clear" ->
1772 state.uioh#infochanged Pdim;
1773 state.pdims <- [];
1775 | "clearrects" ->
1776 state.rects <- state.rects1;
1777 G.postRedisplay "clearrects";
1779 | "continue" ->
1780 let n =
1781 try Scanf.sscanf args "%u" (fun n -> n)
1782 with exn ->
1783 dolog "error processing 'continue' %S: %s"
1784 cmds (Printexc.to_string exn);
1785 exit 1;
1787 state.pagecount <- n;
1788 state.invalidated <- state.invalidated - 1;
1789 begin match state.currently with
1790 | Outlining l ->
1791 state.currently <- Idle;
1792 state.outlines <- Array.of_list (List.rev l)
1793 | _ -> ()
1794 end;
1795 if state.invalidated = 0
1796 then represent ();
1797 if conf.maxwait = None
1798 then G.postRedisplay "continue";
1800 | "title" ->
1801 Glut.setWindowTitle args
1803 | "msg" ->
1804 showtext ' ' args
1806 | "vmsg" ->
1807 if conf.verbose
1808 then showtext ' ' args
1810 | "progress" ->
1811 let progress, text =
1813 Scanf.sscanf args "%f %n"
1814 (fun f pos ->
1815 f, String.sub args pos (String.length args - pos))
1816 with exn ->
1817 dolog "error processing 'progress' %S: %s"
1818 cmds (Printexc.to_string exn);
1819 exit 1;
1821 state.text <- text;
1822 state.progress <- progress;
1823 G.postRedisplay "progress"
1825 | "firstmatch" ->
1826 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1828 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1829 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1830 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1831 with exn ->
1832 dolog "error processing 'firstmatch' %S: %s"
1833 cmds (Printexc.to_string exn);
1834 exit 1;
1836 let y = (getpagey pageno) + truncate y0 in
1837 addnav ();
1838 gotoy y;
1839 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1841 | "match" ->
1842 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1844 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1845 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1846 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1847 with exn ->
1848 dolog "error processing 'match' %S: %s"
1849 cmds (Printexc.to_string exn);
1850 exit 1;
1852 state.rects1 <-
1853 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1855 | "page" ->
1856 let pageopaque, t =
1858 Scanf.sscanf args "%s %f" (fun p t -> p, t)
1859 with exn ->
1860 dolog "error processing 'page' %S: %s"
1861 cmds (Printexc.to_string exn);
1862 exit 1;
1864 begin match state.currently with
1865 | Loading (l, gen) ->
1866 vlog "page %d took %f sec" l.pageno t;
1867 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1868 begin match state.throttle with
1869 | None ->
1870 let preloadedpages =
1871 if conf.preload
1872 then preloadlayout state.layout
1873 else state.layout
1875 let evict () =
1876 let module IntSet =
1877 Set.Make (struct type t = int let compare = (-) end) in
1878 let set =
1879 List.fold_left (fun s l -> IntSet.add l.pageno s)
1880 IntSet.empty preloadedpages
1882 let evictedpages =
1883 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1884 if not (IntSet.mem pageno set)
1885 then (
1886 wcmd "freepage" [`s opaque];
1887 key :: accu
1889 else accu
1890 ) state.pagemap []
1892 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1894 evict ();
1895 state.currently <- Idle;
1896 if gen = state.gen
1897 then (
1898 tilepage l.pageno pageopaque state.layout;
1899 load state.layout;
1900 load preloadedpages;
1901 if pagevisible state.layout l.pageno
1902 && layoutready state.layout
1903 then G.postRedisplay "page";
1906 | Some (layout, _, _) ->
1907 state.currently <- Idle;
1908 tilepage l.pageno pageopaque layout;
1909 load state.layout
1910 end;
1912 | _ ->
1913 dolog "Inconsistent loading state";
1914 logcurrently state.currently;
1915 raise Quit;
1918 | "tile" ->
1919 let (x, y, opaque, size, t) =
1921 Scanf.sscanf args "%u %u %s %u %f"
1922 (fun x y p size t -> (x, y, p, size, t))
1923 with exn ->
1924 dolog "error processing 'tile' %S: %s"
1925 cmds (Printexc.to_string exn);
1926 exit 1;
1928 begin match state.currently with
1929 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1930 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1932 if tilew != conf.tilew || tileh != conf.tileh
1933 then (
1934 wcmd "freetile" [`s opaque];
1935 state.currently <- Idle;
1936 load state.layout;
1938 else (
1939 puttileopaque l col row gen cs angle opaque size t;
1940 state.memused <- state.memused + size;
1941 state.uioh#infochanged Memused;
1942 gctiles ();
1943 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1944 opaque, size) state.tilelru;
1946 let layout =
1947 match state.throttle with
1948 | None -> state.layout
1949 | Some (layout, _, _) -> layout
1952 state.currently <- Idle;
1953 if gen = state.gen
1954 && conf.colorspace = cs
1955 && conf.angle = angle
1956 && tilevisible layout l.pageno x y
1957 then conttiling l.pageno pageopaque;
1959 begin match state.throttle with
1960 | None ->
1961 preload state.layout;
1962 if gen = state.gen
1963 && conf.colorspace = cs
1964 && conf.angle = angle
1965 && tilevisible state.layout l.pageno x y
1966 then G.postRedisplay "tile nothrottle";
1968 | Some (layout, y, _) ->
1969 let ready = layoutready layout in
1970 if ready
1971 then (
1972 state.y <- y;
1973 state.layout <- layout;
1974 state.throttle <- None;
1975 G.postRedisplay "throttle";
1977 else load layout;
1978 end;
1981 | _ ->
1982 dolog "Inconsistent tiling state";
1983 logcurrently state.currently;
1984 raise Quit;
1987 | "pdim" ->
1988 let pdim =
1990 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1991 with exn ->
1992 dolog "error processing 'pdim' %S: %s"
1993 cmds (Printexc.to_string exn);
1994 exit 1;
1996 state.uioh#infochanged Pdim;
1997 state.pdims <- pdim :: state.pdims
1999 | "o" ->
2000 let (l, n, t, h, pos) =
2002 Scanf.sscanf args "%u %u %d %u %n"
2003 (fun l n t h pos -> l, n, t, h, pos)
2004 with exn ->
2005 dolog "error processing 'o' %S: %s"
2006 cmds (Printexc.to_string exn);
2007 exit 1;
2009 let s = String.sub args pos (String.length args - pos) in
2010 let outline = (s, l, (n, float t /. float h)) in
2011 begin match state.currently with
2012 | Outlining outlines ->
2013 state.currently <- Outlining (outline :: outlines)
2014 | Idle ->
2015 state.currently <- Outlining [outline]
2016 | currently ->
2017 dolog "invalid outlining state";
2018 logcurrently currently
2021 | "info" ->
2022 state.docinfo <- (1, args) :: state.docinfo
2024 | "infoend" ->
2025 state.uioh#infochanged Docinfo;
2026 state.docinfo <- List.rev state.docinfo
2028 | _ ->
2029 dolog "unknown cmd `%S'" cmds
2032 let idle () =
2033 if state.deadline == nan then state.deadline <- now ();
2034 let r =
2035 match state.errfd with
2036 | None -> [state.sr]
2037 | Some fd -> [state.sr; fd]
2039 let rec loop delay =
2040 let deadline =
2041 if state.ghyll == noghyll
2042 then state.deadline
2043 else now () +. 0.02
2045 let timeout =
2046 if delay > 0.0
2047 then max 0.0 (deadline -. now ())
2048 else 0.0
2050 let r, _, _ =
2051 try Unix.select r [] [] timeout
2052 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [] ,[]
2054 begin match r with
2055 | [] ->
2056 state.ghyll None;
2057 begin match state.autoscroll with
2058 | Some step when step != 0 ->
2059 let y = state.y + step in
2060 let y =
2061 if y < 0
2062 then state.maxy
2063 else if y >= state.maxy then 0 else y
2065 gotoy y;
2066 if state.mode = View
2067 then state.text <- "";
2068 state.deadline <- state.deadline +. 0.005;
2070 | _ ->
2071 state.deadline <- state.deadline +. delay;
2072 end;
2074 | l ->
2075 let rec checkfds c = function
2076 | [] -> c
2077 | fd :: rest when fd = state.sr ->
2078 let cmd = readcmd state.sr in
2079 act cmd;
2080 checkfds true rest
2081 | fd :: rest ->
2082 let s = String.create 80 in
2083 let n = Unix.read fd s 0 80 in
2084 if conf.redirectstderr
2085 then (
2086 Buffer.add_substring state.errmsgs s 0 n;
2087 state.newerrmsgs <- true;
2088 Glut.postRedisplay ();
2090 else (
2091 prerr_string (String.sub s 0 n);
2092 flush stderr;
2094 checkfds c rest
2096 if checkfds false l
2097 then loop 0.0
2098 end;
2099 in loop 0.007
2102 let onhist cb =
2103 let rc = cb.rc in
2104 let action = function
2105 | HCprev -> cbget cb ~-1
2106 | HCnext -> cbget cb 1
2107 | HCfirst -> cbget cb ~-(cb.rc)
2108 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2109 and cancel () = cb.rc <- rc
2110 in (action, cancel)
2113 let search pattern forward =
2114 if String.length pattern > 0
2115 then
2116 let pn, py =
2117 match state.layout with
2118 | [] -> 0, 0
2119 | l :: _ ->
2120 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2122 let cmd =
2123 let b = makecmd "search"
2124 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
2126 Buffer.add_char b ',';
2127 Buffer.add_string b pattern;
2128 Buffer.add_char b '\000';
2129 Buffer.contents b;
2131 writecmd state.sw cmd;
2134 let intentry text key =
2135 let c = Char.unsafe_chr key in
2136 match c with
2137 | '0' .. '9' ->
2138 let text = addchar text c in
2139 TEcont text
2141 | _ ->
2142 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2143 TEcont text
2146 let textentry text key =
2147 let c = Char.unsafe_chr key in
2148 match c with
2149 | _ when key >= 32 && key < 127 ->
2150 let text = addchar text c in
2151 TEcont text
2153 | _ ->
2154 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
2155 TEcont text
2158 let reqlayout angle proportional =
2159 match state.throttle with
2160 | None ->
2161 if state.invalidated = 0 then state.anchor <- getanchor ();
2162 conf.angle <- angle mod 360;
2163 conf.proportional <- proportional;
2164 invalidate ();
2165 wcmd "reqlayout" [`i conf.angle; `b proportional];
2166 | _ -> ()
2169 let settrim trimmargins trimfuzz =
2170 if state.invalidated = 0 then state.anchor <- getanchor ();
2171 conf.trimmargins <- trimmargins;
2172 conf.trimfuzz <- trimfuzz;
2173 let x0, y0, x1, y1 = trimfuzz in
2174 invalidate ();
2175 wcmd "settrim" [
2176 `b conf.trimmargins;
2177 `i x0;
2178 `i y0;
2179 `i x1;
2180 `i y1;
2182 Hashtbl.iter (fun _ opaque ->
2183 wcmd "freepage" [`s opaque];
2184 ) state.pagemap;
2185 Hashtbl.clear state.pagemap;
2188 let setzoom zoom =
2189 match state.throttle with
2190 | None ->
2191 let zoom = max 0.01 zoom in
2192 if zoom <> conf.zoom
2193 then (
2194 state.prevzoom <- conf.zoom;
2195 let relx =
2196 if zoom <= 1.0
2197 then (state.x <- 0; 0.0)
2198 else float state.x /. float state.w
2200 conf.zoom <- zoom;
2201 reshape conf.winw conf.winh;
2202 if zoom > 1.0
2203 then (
2204 let x = relx *. float state.w in
2205 state.x <- truncate x;
2207 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2210 | Some (layout, y, started) ->
2211 let time =
2212 match conf.maxwait with
2213 | None -> 0.0
2214 | Some t -> t
2216 let dt = now () -. started in
2217 if dt > time
2218 then (
2219 state.y <- y;
2220 load layout;
2224 let setcolumns columns coverA coverB =
2225 if columns < 2
2226 then (
2227 conf.columns <- None;
2228 state.x <- 0;
2229 setzoom 1.0;
2231 else (
2232 conf.columns <- Some ((columns, coverA, coverB), [||]);
2233 conf.zoom <- 1.0;
2235 reshape conf.winw conf.winh;
2238 let enterbirdseye () =
2239 let zoom = float conf.thumbw /. float conf.winw in
2240 let birdseyepageno =
2241 let cy = conf.winh / 2 in
2242 let fold = function
2243 | [] -> 0
2244 | l :: rest ->
2245 let rec fold best = function
2246 | [] -> best.pageno
2247 | l :: rest ->
2248 let d = cy - (l.pagedispy + l.pagevh/2)
2249 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2250 if abs d < abs dbest
2251 then fold l rest
2252 else best.pageno
2253 in fold l rest
2255 fold state.layout
2257 state.mode <- Birdseye (
2258 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2260 conf.zoom <- zoom;
2261 conf.presentation <- false;
2262 conf.interpagespace <- 10;
2263 conf.hlinks <- false;
2264 state.x <- 0;
2265 state.mstate <- Mnone;
2266 conf.maxwait <- None;
2267 conf.columns <- (
2268 match conf.beyecolumns with
2269 | Some c ->
2270 conf.zoom <- 1.0;
2271 Some ((c, 0, 0), [||])
2272 | None -> None
2274 Glut.setCursor Glut.CURSOR_INHERIT;
2275 if conf.verbose
2276 then
2277 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2278 (100.0*.zoom)
2279 else
2280 state.text <- ""
2282 reshape conf.winw conf.winh;
2285 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2286 state.mode <- View;
2287 conf.zoom <- c.zoom;
2288 conf.presentation <- c.presentation;
2289 conf.interpagespace <- c.interpagespace;
2290 conf.maxwait <- c.maxwait;
2291 conf.hlinks <- c.hlinks;
2292 conf.beyecolumns <- (
2293 match conf.columns with
2294 | Some ((c, _, _), _) -> Some c
2295 | None -> None
2297 conf.columns <- (
2298 match c.columns with
2299 | Some (c, _) -> Some (c, [||])
2300 | None -> None
2302 state.x <- leftx;
2303 if conf.verbose
2304 then
2305 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2306 (100.0*.conf.zoom)
2308 reshape conf.winw conf.winh;
2309 state.anchor <- if goback then anchor else (pageno, 0.0);
2312 let togglebirdseye () =
2313 match state.mode with
2314 | Birdseye vals -> leavebirdseye vals true
2315 | View -> enterbirdseye ()
2316 | _ -> ()
2319 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2320 let pageno = max 0 (pageno - incr) in
2321 let rec loop = function
2322 | [] -> gotopage1 pageno 0
2323 | l :: _ when l.pageno = pageno ->
2324 if l.pagedispy >= 0 && l.pagey = 0
2325 then G.postRedisplay "upbirdseye"
2326 else gotopage1 pageno 0
2327 | _ :: rest -> loop rest
2329 loop state.layout;
2330 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2333 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2334 let pageno = min (state.pagecount - 1) (pageno + incr) in
2335 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2336 let rec loop = function
2337 | [] ->
2338 let y, h = getpageyh pageno in
2339 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2340 gotoy (clamp dy)
2341 | l :: _ when l.pageno = pageno ->
2342 if l.pagevh != l.pageh
2343 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2344 else G.postRedisplay "downbirdseye"
2345 | _ :: rest -> loop rest
2347 loop state.layout
2350 let optentry mode _ key =
2351 let btos b = if b then "on" else "off" in
2352 let c = Char.unsafe_chr key in
2353 match c with
2354 | 's' ->
2355 let ondone s =
2356 try conf.scrollstep <- int_of_string s with exc ->
2357 state.text <- Printf.sprintf "bad integer `%s': %s"
2358 s (Printexc.to_string exc)
2360 TEswitch ("scroll step: ", "", None, intentry, ondone)
2362 | 'A' ->
2363 let ondone s =
2365 conf.autoscrollstep <- int_of_string s;
2366 if state.autoscroll <> None
2367 then state.autoscroll <- Some conf.autoscrollstep
2368 with exc ->
2369 state.text <- Printf.sprintf "bad integer `%s': %s"
2370 s (Printexc.to_string exc)
2372 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2374 | 'C' ->
2375 let ondone s =
2377 let n, a, b = columns_of_string s in
2378 setcolumns n a b;
2379 with exc ->
2380 state.text <- Printf.sprintf "bad columns `%s': %s"
2381 s (Printexc.to_string exc)
2383 TEswitch ("columns: ", "", None, textentry, ondone)
2385 | 'Z' ->
2386 let ondone s =
2388 let zoom = float (int_of_string s) /. 100.0 in
2389 setzoom zoom
2390 with exc ->
2391 state.text <- Printf.sprintf "bad integer `%s': %s"
2392 s (Printexc.to_string exc)
2394 TEswitch ("zoom: ", "", None, intentry, ondone)
2396 | 't' ->
2397 let ondone s =
2399 conf.thumbw <- bound (int_of_string s) 2 4096;
2400 state.text <-
2401 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2402 begin match mode with
2403 | Birdseye beye ->
2404 leavebirdseye beye false;
2405 enterbirdseye ();
2406 | _ -> ();
2408 with exc ->
2409 state.text <- Printf.sprintf "bad integer `%s': %s"
2410 s (Printexc.to_string exc)
2412 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2414 | 'R' ->
2415 let ondone s =
2416 match try
2417 Some (int_of_string s)
2418 with exc ->
2419 state.text <- Printf.sprintf "bad integer `%s': %s"
2420 s (Printexc.to_string exc);
2421 None
2422 with
2423 | Some angle -> reqlayout angle conf.proportional
2424 | None -> ()
2426 TEswitch ("rotation: ", "", None, intentry, ondone)
2428 | 'i' ->
2429 conf.icase <- not conf.icase;
2430 TEdone ("case insensitive search " ^ (btos conf.icase))
2432 | 'p' ->
2433 conf.preload <- not conf.preload;
2434 gotoy state.y;
2435 TEdone ("preload " ^ (btos conf.preload))
2437 | 'v' ->
2438 conf.verbose <- not conf.verbose;
2439 TEdone ("verbose " ^ (btos conf.verbose))
2441 | 'd' ->
2442 conf.debug <- not conf.debug;
2443 TEdone ("debug " ^ (btos conf.debug))
2445 | 'h' ->
2446 conf.maxhfit <- not conf.maxhfit;
2447 state.maxy <-
2448 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2449 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2451 | 'c' ->
2452 conf.crophack <- not conf.crophack;
2453 TEdone ("crophack " ^ btos conf.crophack)
2455 | 'a' ->
2456 let s =
2457 match conf.maxwait with
2458 | None ->
2459 conf.maxwait <- Some infinity;
2460 "always wait for page to complete"
2461 | Some _ ->
2462 conf.maxwait <- None;
2463 "show placeholder if page is not ready"
2465 TEdone s
2467 | 'f' ->
2468 conf.underinfo <- not conf.underinfo;
2469 TEdone ("underinfo " ^ btos conf.underinfo)
2471 | 'P' ->
2472 conf.savebmarks <- not conf.savebmarks;
2473 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2475 | 'S' ->
2476 let ondone s =
2478 let pageno, py =
2479 match state.layout with
2480 | [] -> 0, 0
2481 | l :: _ ->
2482 l.pageno, l.pagey
2484 conf.interpagespace <- int_of_string s;
2485 state.maxy <- calcheight ();
2486 let y = getpagey pageno in
2487 gotoy (y + py)
2488 with exc ->
2489 state.text <- Printf.sprintf "bad integer `%s': %s"
2490 s (Printexc.to_string exc)
2492 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2494 | 'l' ->
2495 reqlayout conf.angle (not conf.proportional);
2496 TEdone ("proportional display " ^ btos conf.proportional)
2498 | 'T' ->
2499 settrim (not conf.trimmargins) conf.trimfuzz;
2500 TEdone ("trim margins " ^ btos conf.trimmargins)
2502 | 'I' ->
2503 conf.invert <- not conf.invert;
2504 TEdone ("invert colors " ^ btos conf.invert)
2506 | 'x' ->
2507 let ondone s =
2508 cbput state.hists.sel s;
2509 conf.selcmd <- s;
2511 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2512 textentry, ondone)
2514 | _ ->
2515 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2516 TEstop
2519 class type lvsource = object
2520 method getitemcount : int
2521 method getitem : int -> (string * int)
2522 method hasaction : int -> bool
2523 method exit :
2524 uioh:uioh ->
2525 cancel:bool ->
2526 active:int ->
2527 first:int ->
2528 pan:int ->
2529 qsearch:string ->
2530 uioh option
2531 method getactive : int
2532 method getfirst : int
2533 method getqsearch : string
2534 method setqsearch : string -> unit
2535 method getpan : int
2536 end;;
2538 class virtual lvsourcebase = object
2539 val mutable m_active = 0
2540 val mutable m_first = 0
2541 val mutable m_qsearch = ""
2542 val mutable m_pan = 0
2543 method getactive = m_active
2544 method getfirst = m_first
2545 method getqsearch = m_qsearch
2546 method getpan = m_pan
2547 method setqsearch s = m_qsearch <- s
2548 end;;
2550 let textentryspecial key = function
2551 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2552 let s =
2553 match key with
2554 | Glut.KEY_UP -> action HCprev
2555 | Glut.KEY_DOWN -> action HCnext
2556 | Glut.KEY_HOME -> action HCfirst
2557 | Glut.KEY_END -> action HClast
2558 | _ -> state.text
2560 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2561 G.postRedisplay "special textentry";
2562 | _ -> ()
2565 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2566 let enttext te =
2567 state.mode <- Textentry (te, onleave);
2568 state.text <- "";
2569 enttext ();
2570 G.postRedisplay "textentrykeyboard enttext";
2572 match Char.unsafe_chr key with
2573 | '\008' -> (* backspace *)
2574 let len = String.length text in
2575 if len = 0
2576 then (
2577 onleave Cancel;
2578 G.postRedisplay "textentrykeyboard after cancel";
2580 else (
2581 let s = String.sub text 0 (len - 1) in
2582 enttext (c, s, opthist, onkey, ondone)
2585 | '\r' | '\n' ->
2586 ondone text;
2587 onleave Confirm;
2588 G.postRedisplay "textentrykeyboard after confirm"
2590 | '\007' (* ctrl-g *)
2591 | '\027' -> (* escape *)
2592 if String.length text = 0
2593 then (
2594 begin match opthist with
2595 | None -> ()
2596 | Some (_, onhistcancel) -> onhistcancel ()
2597 end;
2598 onleave Cancel;
2599 state.text <- "";
2600 G.postRedisplay "textentrykeyboard after cancel2"
2602 else (
2603 enttext (c, "", opthist, onkey, ondone)
2606 | '\127' -> () (* delete *)
2608 | _ ->
2609 begin match onkey text key with
2610 | TEdone text ->
2611 ondone text;
2612 onleave Confirm;
2613 G.postRedisplay "textentrykeyboard after confirm2";
2615 | TEcont text ->
2616 enttext (c, text, opthist, onkey, ondone);
2618 | TEstop ->
2619 onleave Cancel;
2620 G.postRedisplay "textentrykeyboard after cancel3"
2622 | TEswitch te ->
2623 state.mode <- Textentry (te, onleave);
2624 G.postRedisplay "textentrykeyboard switch";
2625 end;
2628 let firstof first active =
2629 if first > active || abs (first - active) > fstate.maxrows - 1
2630 then max 0 (active - (fstate.maxrows/2))
2631 else first
2634 let calcfirst first active =
2635 if active > first
2636 then
2637 let rows = active - first in
2638 if rows > fstate.maxrows then active - fstate.maxrows else first
2639 else active
2642 let scrollph y maxy =
2643 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2644 let sh = float conf.winh /. sh in
2645 let sh = max sh (float conf.scrollh) in
2647 let percent =
2648 if y = state.maxy
2649 then 1.0
2650 else float y /. float maxy
2652 let position = (float conf.winh -. sh) *. percent in
2654 let position =
2655 if position +. sh > float conf.winh
2656 then float conf.winh -. sh
2657 else position
2659 position, sh;
2662 let coe s = (s :> uioh);;
2664 class listview ~(source:lvsource) ~trusted =
2665 object (self)
2666 val m_pan = source#getpan
2667 val m_first = source#getfirst
2668 val m_active = source#getactive
2669 val m_qsearch = source#getqsearch
2670 val m_prev_uioh = state.uioh
2672 method private elemunder y =
2673 let n = y / (fstate.fontsize+1) in
2674 if m_first + n < source#getitemcount
2675 then (
2676 if source#hasaction (m_first + n)
2677 then Some (m_first + n)
2678 else None
2680 else None
2682 method display =
2683 Gl.enable `blend;
2684 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2685 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2686 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2687 GlDraw.color (1., 1., 1.);
2688 Gl.enable `texture_2d;
2689 let fs = fstate.fontsize in
2690 let nfs = fs + 1 in
2691 let ww = fstate.wwidth in
2692 let tabw = 30.0*.ww in
2693 let itemcount = source#getitemcount in
2694 let rec loop row =
2695 if (row - m_first) * nfs > conf.winh
2696 then ()
2697 else (
2698 if row >= 0 && row < itemcount
2699 then (
2700 let (s, level) = source#getitem row in
2701 let y = (row - m_first) * nfs in
2702 let x = 5.0 +. float (level + m_pan) *. ww in
2703 if row = m_active
2704 then (
2705 Gl.disable `texture_2d;
2706 GlDraw.polygon_mode `both `line;
2707 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2708 GlDraw.rect (1., float (y + 1))
2709 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2710 GlDraw.polygon_mode `both `fill;
2711 GlDraw.color (1., 1., 1.);
2712 Gl.enable `texture_2d;
2715 let drawtabularstring s =
2716 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2717 if trusted
2718 then
2719 let tabpos = try String.index s '\t' with Not_found -> -1 in
2720 if tabpos > 0
2721 then
2722 let len = String.length s - tabpos - 1 in
2723 let s1 = String.sub s 0 tabpos
2724 and s2 = String.sub s (tabpos + 1) len in
2725 let nx = drawstr x s1 in
2726 let sw = nx -. x in
2727 let x = x +. (max tabw sw) in
2728 drawstr x s2
2729 else
2730 drawstr x s
2731 else
2732 drawstr x s
2734 let _ = drawtabularstring s in
2735 loop (row+1)
2739 loop m_first;
2740 Gl.disable `blend;
2741 Gl.disable `texture_2d;
2743 method updownlevel incr =
2744 let len = source#getitemcount in
2745 let curlevel =
2746 if m_active >= 0 && m_active < len
2747 then snd (source#getitem m_active)
2748 else -1
2750 let rec flow i =
2751 if i = len then i-1 else if i = -1 then 0 else
2752 let _, l = source#getitem i in
2753 if l != curlevel then i else flow (i+incr)
2755 let active = flow m_active in
2756 let first = calcfirst m_first active in
2757 G.postRedisplay "special outline updownlevel";
2758 {< m_active = active; m_first = first >}
2760 method private key1 key =
2761 let set active first qsearch =
2762 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2764 let search active pattern incr =
2765 let dosearch re =
2766 let rec loop n =
2767 if n >= 0 && n < source#getitemcount
2768 then (
2769 let s, _ = source#getitem n in
2771 (try ignore (Str.search_forward re s 0); true
2772 with Not_found -> false)
2773 then Some n
2774 else loop (n + incr)
2776 else None
2778 loop active
2781 let re = Str.regexp_case_fold pattern in
2782 dosearch re
2783 with Failure s ->
2784 state.text <- s;
2785 None
2787 match key with
2788 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2789 let incr = if key = 18 then -1 else 1 in
2790 let active, first =
2791 match search (m_active + incr) m_qsearch incr with
2792 | None ->
2793 state.text <- m_qsearch ^ " [not found]";
2794 m_active, m_first
2795 | Some active ->
2796 state.text <- m_qsearch;
2797 active, firstof m_first active
2799 G.postRedisplay "listview ctrl-r/s";
2800 set active first m_qsearch;
2802 | 8 -> (* backspace *)
2803 let len = String.length m_qsearch in
2804 if len = 0
2805 then coe self
2806 else (
2807 if len = 1
2808 then (
2809 state.text <- "";
2810 G.postRedisplay "listview empty qsearch";
2811 set m_active m_first "";
2813 else
2814 let qsearch = String.sub m_qsearch 0 (len - 1) in
2815 let active, first =
2816 match search m_active qsearch ~-1 with
2817 | None ->
2818 state.text <- qsearch ^ " [not found]";
2819 m_active, m_first
2820 | Some active ->
2821 state.text <- qsearch;
2822 active, firstof m_first active
2824 G.postRedisplay "listview backspace qsearch";
2825 set active first qsearch
2828 | _ when key >= 32 && key < 127 ->
2829 let pattern = addchar m_qsearch (Char.chr key) in
2830 let active, first =
2831 match search m_active pattern 1 with
2832 | None ->
2833 state.text <- pattern ^ " [not found]";
2834 m_active, m_first
2835 | Some active ->
2836 state.text <- pattern;
2837 active, firstof m_first active
2839 G.postRedisplay "listview qsearch add";
2840 set active first pattern;
2842 | 27 -> (* escape *)
2843 state.text <- "";
2844 if String.length m_qsearch = 0
2845 then (
2846 G.postRedisplay "list view escape";
2847 begin
2848 match
2849 source#exit (coe self) true m_active m_first m_pan m_qsearch
2850 with
2851 | None -> m_prev_uioh
2852 | Some uioh -> uioh
2855 else (
2856 G.postRedisplay "list view kill qsearch";
2857 source#setqsearch "";
2858 coe {< m_qsearch = "" >}
2861 | 13 -> (* enter *)
2862 state.text <- "";
2863 let self = {< m_qsearch = "" >} in
2864 source#setqsearch "";
2865 let opt =
2866 G.postRedisplay "listview enter";
2867 if m_active >= 0 && m_active < source#getitemcount
2868 then (
2869 source#exit (coe self) false m_active m_first m_pan "";
2871 else (
2872 source#exit (coe self) true m_active m_first m_pan "";
2875 begin match opt with
2876 | None -> m_prev_uioh
2877 | Some uioh -> uioh
2880 | 127 -> (* delete *)
2881 coe self
2883 | _ -> dolog "unknown key %d" key; coe self
2885 method private special1 key =
2886 let itemcount = source#getitemcount in
2887 let find start incr =
2888 let rec find i =
2889 if i = -1 || i = itemcount
2890 then -1
2891 else (
2892 if source#hasaction i
2893 then i
2894 else find (i + incr)
2897 find start
2899 let set active first =
2900 let first = bound first 0 (itemcount - fstate.maxrows) in
2901 state.text <- "";
2902 coe {< m_active = active; m_first = first >}
2904 let navigate incr =
2905 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2906 let active, first =
2907 let incr1 = if incr > 0 then 1 else -1 in
2908 if isvisible m_first m_active
2909 then
2910 let next =
2911 let next = m_active + incr in
2912 let next =
2913 if next < 0 || next >= itemcount
2914 then -1
2915 else find next incr1
2917 if next = -1 || abs (m_active - next) > fstate.maxrows
2918 then -1
2919 else next
2921 if next = -1
2922 then
2923 let first = m_first + incr in
2924 let first = bound first 0 (itemcount - 1) in
2925 let next =
2926 let next = m_active + incr in
2927 let next = bound next 0 (itemcount - 1) in
2928 find next ~-incr1
2930 let active = if next = -1 then m_active else next in
2931 active, first
2932 else
2933 let first = min next m_first in
2934 let first =
2935 if abs (next - first) > fstate.maxrows
2936 then first + incr
2937 else first
2939 next, first
2940 else
2941 let first = m_first + incr in
2942 let first = bound first 0 (itemcount - 1) in
2943 let active =
2944 let next = m_active + incr in
2945 let next = bound next 0 (itemcount - 1) in
2946 let next = find next incr1 in
2947 let active =
2948 if next = -1 || abs (m_active - first) > fstate.maxrows
2949 then (
2950 let active = if m_active = -1 then next else m_active in
2951 active
2953 else next
2955 if isvisible first active
2956 then active
2957 else -1
2959 active, first
2961 G.postRedisplay "listview navigate";
2962 set active first;
2964 begin match key with
2965 | Glut.KEY_UP -> navigate ~-1
2966 | Glut.KEY_DOWN -> navigate 1
2967 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2968 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2970 | Glut.KEY_RIGHT ->
2971 state.text <- "";
2972 G.postRedisplay "listview right";
2973 coe {< m_pan = m_pan - 1 >}
2975 | Glut.KEY_LEFT ->
2976 state.text <- "";
2977 G.postRedisplay "listview left";
2978 coe {< m_pan = m_pan + 1 >}
2980 | Glut.KEY_HOME ->
2981 let active = find 0 1 in
2982 G.postRedisplay "listview home";
2983 set active 0;
2985 | Glut.KEY_END ->
2986 let first = max 0 (itemcount - fstate.maxrows) in
2987 let active = find (itemcount - 1) ~-1 in
2988 G.postRedisplay "listview end";
2989 set active first;
2991 | _ -> coe self
2992 end;
2994 method key key =
2995 match state.mode with
2996 | Textentry te -> textentrykeyboard key te; coe self
2997 | _ -> self#key1 key
2999 method special key =
3000 match state.mode with
3001 | Textentry te -> textentryspecial key te; coe self
3002 | _ -> self#special1 key
3004 method button button bstate x y =
3005 let opt =
3006 match button with
3007 | Glut.LEFT_BUTTON when x > conf.winw - conf.scrollbw ->
3008 G.postRedisplay "listview scroll";
3009 if bstate = Glut.DOWN
3010 then
3011 let _, position, sh = self#scrollph in
3012 if y > truncate position && y < truncate (position +. sh)
3013 then (
3014 state.mstate <- Mscrolly;
3015 Some (coe self)
3017 else
3018 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3019 let first = truncate (s *. float source#getitemcount) in
3020 let first = min source#getitemcount first in
3021 Some (coe {< m_first = first; m_active = first >})
3022 else (
3023 state.mstate <- Mnone;
3024 Some (coe self);
3026 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
3027 begin match self#elemunder y with
3028 | Some n ->
3029 G.postRedisplay "listview click";
3030 source#exit
3031 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3032 | _ ->
3033 Some (coe self)
3035 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
3036 let len = source#getitemcount in
3037 let first =
3038 if n = 4 && m_first + fstate.maxrows >= len
3039 then
3040 m_first
3041 else
3042 let first = m_first + (if n == 3 then -1 else 1) in
3043 bound first 0 (len - 1)
3045 G.postRedisplay "listview wheel";
3046 Some (coe {< m_first = first >})
3047 | _ ->
3048 Some (coe self)
3050 match opt with
3051 | None -> m_prev_uioh
3052 | Some uioh -> uioh
3054 method motion _ y =
3055 match state.mstate with
3056 | Mscrolly ->
3057 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3058 let first = truncate (s *. float source#getitemcount) in
3059 let first = min source#getitemcount first in
3060 G.postRedisplay "listview motion";
3061 coe {< m_first = first; m_active = first >}
3062 | _ -> coe self
3064 method pmotion x y =
3065 if x < conf.winw - conf.scrollbw
3066 then
3067 let n =
3068 match self#elemunder y with
3069 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
3070 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
3072 let o =
3073 if n != m_active
3074 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3075 else self
3077 coe o
3078 else (
3079 Glut.setCursor Glut.CURSOR_INHERIT;
3080 coe self
3083 method infochanged _ = ()
3085 method scrollpw = (0, 0.0, 0.0)
3086 method scrollph =
3087 let nfs = fstate.fontsize + 1 in
3088 let y = m_first * nfs in
3089 let itemcount = source#getitemcount in
3090 let maxi = max 0 (itemcount - fstate.maxrows) in
3091 let maxy = maxi * nfs in
3092 let p, h = scrollph y maxy in
3093 conf.scrollbw, p, h
3094 end;;
3096 class outlinelistview ~source =
3097 object (self)
3098 inherit listview ~source:(source :> lvsource) ~trusted:false as super
3100 method key key =
3101 match key with
3102 | 14 -> (* ctrl-n *)
3103 source#narrow m_qsearch;
3104 G.postRedisplay "outline ctrl-n";
3105 coe {< m_first = 0; m_active = 0 >}
3107 | 21 -> (* ctrl-u *)
3108 source#denarrow;
3109 G.postRedisplay "outline ctrl-u";
3110 state.text <- "";
3111 coe {< m_first = 0; m_active = 0 >}
3113 | 12 -> (* ctrl-l *)
3114 let first = m_active - (fstate.maxrows / 2) in
3115 G.postRedisplay "outline ctrl-l";
3116 coe {< m_first = first >}
3118 | 127 -> (* delete *)
3119 source#remove m_active;
3120 G.postRedisplay "outline delete";
3121 let active = max 0 (m_active-1) in
3122 coe {< m_first = firstof m_first active;
3123 m_active = active >}
3125 | key -> super#key key
3127 method special key =
3128 let calcfirst first active =
3129 if active > first
3130 then
3131 let rows = active - first in
3132 if rows > fstate.maxrows then active - fstate.maxrows else first
3133 else active
3135 let navigate incr =
3136 let active = m_active + incr in
3137 let active = bound active 0 (source#getitemcount - 1) in
3138 let first = calcfirst m_first active in
3139 G.postRedisplay "special outline navigate";
3140 coe {< m_active = active; m_first = first >}
3142 match key with
3143 | Glut.KEY_UP -> navigate ~-1
3144 | Glut.KEY_DOWN -> navigate 1
3145 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
3146 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
3148 | Glut.KEY_RIGHT ->
3149 let o =
3150 if Glut.getModifiers () land Glut.active_ctrl != 0
3151 then (
3152 G.postRedisplay "special outline right";
3153 {< m_pan = m_pan + 1 >}
3155 else self#updownlevel 1
3157 coe o
3159 | Glut.KEY_LEFT ->
3160 let o =
3161 if Glut.getModifiers () land Glut.active_ctrl != 0
3162 then (
3163 G.postRedisplay "special outline left";
3164 {< m_pan = m_pan - 1 >}
3166 else self#updownlevel ~-1
3168 coe o
3170 | Glut.KEY_HOME ->
3171 G.postRedisplay "special outline home";
3172 coe {< m_first = 0; m_active = 0 >}
3174 | Glut.KEY_END ->
3175 let active = source#getitemcount - 1 in
3176 let first = max 0 (active - fstate.maxrows) in
3177 G.postRedisplay "special outline end";
3178 coe {< m_active = active; m_first = first >}
3180 | _ -> super#special key
3183 let outlinesource usebookmarks =
3184 let empty = [||] in
3185 (object
3186 inherit lvsourcebase
3187 val mutable m_items = empty
3188 val mutable m_orig_items = empty
3189 val mutable m_prev_items = empty
3190 val mutable m_narrow_pattern = ""
3191 val mutable m_hadremovals = false
3193 method getitemcount =
3194 Array.length m_items + (if m_hadremovals then 1 else 0)
3196 method getitem n =
3197 if n == Array.length m_items && m_hadremovals
3198 then
3199 ("[Confirm removal]", 0)
3200 else
3201 let s, n, _ = m_items.(n) in
3202 (s, n)
3204 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3205 ignore (uioh, first, qsearch);
3206 let confrimremoval = m_hadremovals && active = Array.length m_items in
3207 let items =
3208 if String.length m_narrow_pattern = 0
3209 then m_orig_items
3210 else m_items
3212 if not cancel
3213 then (
3214 if not confrimremoval
3215 then(
3216 let _, _, anchor = m_items.(active) in
3217 gotoanchor anchor;
3218 m_items <- items;
3220 else (
3221 state.bookmarks <- Array.to_list m_items;
3222 m_orig_items <- m_items;
3225 else m_items <- items;
3226 m_pan <- pan;
3227 None
3229 method hasaction _ = true
3231 method greetmsg =
3232 if Array.length m_items != Array.length m_orig_items
3233 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3234 else ""
3236 method narrow pattern =
3237 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3238 match reopt with
3239 | None -> ()
3240 | Some re ->
3241 let rec loop accu n =
3242 if n = -1
3243 then (
3244 m_narrow_pattern <- pattern;
3245 m_items <- Array.of_list accu
3247 else
3248 let (s, _, _) as o = m_items.(n) in
3249 let accu =
3250 if (try ignore (Str.search_forward re s 0); true
3251 with Not_found -> false)
3252 then o :: accu
3253 else accu
3255 loop accu (n-1)
3257 loop [] (Array.length m_items - 1)
3259 method denarrow =
3260 m_orig_items <- (
3261 if usebookmarks
3262 then Array.of_list state.bookmarks
3263 else state.outlines
3265 m_items <- m_orig_items
3267 method remove m =
3268 if usebookmarks
3269 then
3270 if m >= 0 && m < Array.length m_items
3271 then (
3272 m_hadremovals <- true;
3273 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3274 let n = if n >= m then n+1 else n in
3275 m_items.(n)
3279 method reset anchor items =
3280 m_hadremovals <- false;
3281 if m_orig_items == empty || m_prev_items != items
3282 then (
3283 m_orig_items <- items;
3284 if String.length m_narrow_pattern = 0
3285 then m_items <- items;
3287 m_prev_items <- items;
3288 let rely = getanchory anchor in
3289 let active =
3290 let rec loop n best bestd =
3291 if n = Array.length m_items
3292 then best
3293 else
3294 let (_, _, anchor) = m_items.(n) in
3295 let orely = getanchory anchor in
3296 let d = abs (orely - rely) in
3297 if d < bestd
3298 then loop (n+1) n d
3299 else loop (n+1) best bestd
3301 loop 0 ~-1 max_int
3303 m_active <- active;
3304 m_first <- firstof m_first active
3305 end)
3308 let enterselector usebookmarks =
3309 let source = outlinesource usebookmarks in
3310 fun errmsg ->
3311 let outlines =
3312 if usebookmarks
3313 then Array.of_list state.bookmarks
3314 else state.outlines
3316 if Array.length outlines = 0
3317 then (
3318 showtext ' ' errmsg;
3320 else (
3321 state.text <- source#greetmsg;
3322 Glut.setCursor Glut.CURSOR_INHERIT;
3323 let anchor = getanchor () in
3324 source#reset anchor outlines;
3325 state.uioh <- coe (new outlinelistview ~source);
3326 G.postRedisplay "enter selector";
3330 let enteroutlinemode =
3331 let f = enterselector false in
3332 fun ()-> f "Document has no outline";
3335 let enterbookmarkmode =
3336 let f = enterselector true in
3337 fun () -> f "Document has no bookmarks (yet)";
3340 let color_of_string s =
3341 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3342 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3346 let color_to_string (r, g, b) =
3347 let r = truncate (r *. 256.0)
3348 and g = truncate (g *. 256.0)
3349 and b = truncate (b *. 256.0) in
3350 Printf.sprintf "%d/%d/%d" r g b
3353 let irect_of_string s =
3354 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3357 let irect_to_string (x0,y0,x1,y1) =
3358 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3361 let makecheckers () =
3362 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3363 following to say:
3364 converted by Issac Trotts. July 25, 2002 *)
3365 let image_height = 64
3366 and image_width = 64 in
3368 let make_image () =
3369 let image =
3370 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3372 for i = 0 to image_width - 1 do
3373 for j = 0 to image_height - 1 do
3374 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3375 (if (i land 8 ) lxor (j land 8) = 0
3376 then [|255;255;255|] else [|200;200;200|])
3377 done
3378 done;
3379 image
3381 let image = make_image () in
3382 let id = GlTex.gen_texture () in
3383 GlTex.bind_texture `texture_2d id;
3384 GlPix.store (`unpack_alignment 1);
3385 GlTex.image2d image;
3386 List.iter (GlTex.parameter ~target:`texture_2d)
3387 [ `wrap_s `repeat;
3388 `wrap_t `repeat;
3389 `mag_filter `nearest;
3390 `min_filter `nearest ];
3394 let setcheckers enabled =
3395 match state.texid with
3396 | None ->
3397 if enabled then state.texid <- Some (makecheckers ())
3399 | Some texid ->
3400 if not enabled
3401 then (
3402 GlTex.delete_texture texid;
3403 state.texid <- None;
3407 let int_of_string_with_suffix s =
3408 let l = String.length s in
3409 let s1, shift =
3410 if l > 1
3411 then
3412 let suffix = Char.lowercase s.[l-1] in
3413 match suffix with
3414 | 'k' -> String.sub s 0 (l-1), 10
3415 | 'm' -> String.sub s 0 (l-1), 20
3416 | 'g' -> String.sub s 0 (l-1), 30
3417 | _ -> s, 0
3418 else s, 0
3420 let n = int_of_string s1 in
3421 let m = n lsl shift in
3422 if m < 0 || m < n
3423 then raise (Failure "value too large")
3424 else m
3427 let string_with_suffix_of_int n =
3428 if n = 0
3429 then "0"
3430 else
3431 let n, s =
3432 if n = 0
3433 then 0, ""
3434 else (
3435 if n land ((1 lsl 20) - 1) = 0
3436 then n lsr 20, "M"
3437 else (
3438 if n land ((1 lsl 10) - 1) = 0
3439 then n lsr 10, "K"
3440 else n, ""
3444 let rec loop s n =
3445 let h = n mod 1000 in
3446 let n = n / 1000 in
3447 if n = 0
3448 then string_of_int h ^ s
3449 else (
3450 let s = Printf.sprintf "_%03d%s" h s in
3451 loop s n
3454 loop "" n ^ s;
3457 let defghyllscroll = (40, 8, 32);;
3458 let ghyllscroll_of_string s =
3459 let (n, a, b) as nab =
3460 if s = "default"
3461 then defghyllscroll
3462 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3464 if n <= a || n <= b || a >= b
3465 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3466 nab;
3469 let ghyllscroll_to_string ((n, a, b) as nab) =
3470 if nab = defghyllscroll
3471 then "default"
3472 else Printf.sprintf "%d,%d,%d" n a b;
3475 let describe_location () =
3476 let f (fn, _) l =
3477 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3479 let fn, ln = List.fold_left f (-1, -1) state.layout in
3480 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3481 let percent =
3482 if maxy <= 0
3483 then 100.
3484 else (100. *. (float state.y /. float maxy))
3486 if fn = ln
3487 then
3488 Printf.sprintf "page %d of %d [%.2f%%]"
3489 (fn+1) state.pagecount percent
3490 else
3491 Printf.sprintf
3492 "pages %d-%d of %d [%.2f%%]"
3493 (fn+1) (ln+1) state.pagecount percent
3496 let enterinfomode =
3497 let btos b = if b then "\xe2\x88\x9a" else "" in
3498 let showextended = ref false in
3499 let leave mode = function
3500 | Confirm -> state.mode <- mode
3501 | Cancel -> state.mode <- mode in
3502 let src =
3503 (object
3504 val mutable m_first_time = true
3505 val mutable m_l = []
3506 val mutable m_a = [||]
3507 val mutable m_prev_uioh = nouioh
3508 val mutable m_prev_mode = View
3510 inherit lvsourcebase
3512 method reset prev_mode prev_uioh =
3513 m_a <- Array.of_list (List.rev m_l);
3514 m_l <- [];
3515 m_prev_mode <- prev_mode;
3516 m_prev_uioh <- prev_uioh;
3517 if m_first_time
3518 then (
3519 let rec loop n =
3520 if n >= Array.length m_a
3521 then ()
3522 else
3523 match m_a.(n) with
3524 | _, _, _, Action _ -> m_active <- n
3525 | _ -> loop (n+1)
3527 loop 0;
3528 m_first_time <- false;
3531 method int name get set =
3532 m_l <-
3533 (name, `int get, 1, Action (
3534 fun u ->
3535 let ondone s =
3536 try set (int_of_string s)
3537 with exn ->
3538 state.text <- Printf.sprintf "bad integer `%s': %s"
3539 s (Printexc.to_string exn)
3541 state.text <- "";
3542 let te = name ^ ": ", "", None, intentry, ondone in
3543 state.mode <- Textentry (te, leave m_prev_mode);
3545 )) :: m_l
3547 method int_with_suffix name get set =
3548 m_l <-
3549 (name, `intws get, 1, Action (
3550 fun u ->
3551 let ondone s =
3552 try set (int_of_string_with_suffix s)
3553 with exn ->
3554 state.text <- Printf.sprintf "bad integer `%s': %s"
3555 s (Printexc.to_string exn)
3557 state.text <- "";
3558 let te =
3559 name ^ ": ", "", None, intentry_with_suffix, ondone
3561 state.mode <- Textentry (te, leave m_prev_mode);
3563 )) :: m_l
3565 method bool ?(offset=1) ?(btos=btos) name get set =
3566 m_l <-
3567 (name, `bool (btos, get), offset, Action (
3568 fun u ->
3569 let v = get () in
3570 set (not v);
3572 )) :: m_l
3574 method color name get set =
3575 m_l <-
3576 (name, `color get, 1, Action (
3577 fun u ->
3578 let invalid = (nan, nan, nan) in
3579 let ondone s =
3580 let c =
3581 try color_of_string s
3582 with exn ->
3583 state.text <- Printf.sprintf "bad color `%s': %s"
3584 s (Printexc.to_string exn);
3585 invalid
3587 if c <> invalid
3588 then set c;
3590 let te = name ^ ": ", "", None, textentry, ondone in
3591 state.text <- color_to_string (get ());
3592 state.mode <- Textentry (te, leave m_prev_mode);
3594 )) :: m_l
3596 method string name get set =
3597 m_l <-
3598 (name, `string get, 1, Action (
3599 fun u ->
3600 let ondone s = set s in
3601 let te = name ^ ": ", "", None, textentry, ondone in
3602 state.mode <- Textentry (te, leave m_prev_mode);
3604 )) :: m_l
3606 method colorspace name get set =
3607 m_l <-
3608 (name, `string get, 1, Action (
3609 fun _ ->
3610 let source =
3611 let vals = [| "rgb"; "bgr"; "gray" |] in
3612 (object
3613 inherit lvsourcebase
3615 initializer
3616 m_active <- int_of_colorspace conf.colorspace;
3617 m_first <- 0;
3619 method getitemcount = Array.length vals
3620 method getitem n = (vals.(n), 0)
3621 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3622 ignore (uioh, first, pan, qsearch);
3623 if not cancel then set active;
3624 None
3625 method hasaction _ = true
3626 end)
3628 state.text <- "";
3629 coe (new listview ~source ~trusted:true)
3630 )) :: m_l
3632 method caption s offset =
3633 m_l <- (s, `empty, offset, Noaction) :: m_l
3635 method caption2 s f offset =
3636 m_l <- (s, `string f, offset, Noaction) :: m_l
3638 method getitemcount = Array.length m_a
3640 method getitem n =
3641 let tostr = function
3642 | `int f -> string_of_int (f ())
3643 | `intws f -> string_with_suffix_of_int (f ())
3644 | `string f -> f ()
3645 | `color f -> color_to_string (f ())
3646 | `bool (btos, f) -> btos (f ())
3647 | `empty -> ""
3649 let name, t, offset, _ = m_a.(n) in
3650 ((let s = tostr t in
3651 if String.length s > 0
3652 then Printf.sprintf "%s\t%s" name s
3653 else name),
3654 offset)
3656 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3657 let uiohopt =
3658 if not cancel
3659 then (
3660 m_qsearch <- qsearch;
3661 let uioh =
3662 match m_a.(active) with
3663 | _, _, _, Action f -> f uioh
3664 | _ -> uioh
3666 Some uioh
3668 else None
3670 m_active <- active;
3671 m_first <- first;
3672 m_pan <- pan;
3673 uiohopt
3675 method hasaction n =
3676 match m_a.(n) with
3677 | _, _, _, Action _ -> true
3678 | _ -> false
3679 end)
3681 let rec fillsrc prevmode prevuioh =
3682 let sep () = src#caption "" 0 in
3683 let colorp name get set =
3684 src#string name
3685 (fun () -> color_to_string (get ()))
3686 (fun v ->
3688 let c = color_of_string v in
3689 set c
3690 with exn ->
3691 state.text <- Printf.sprintf "bad color `%s': %s"
3692 v (Printexc.to_string exn);
3695 let oldmode = state.mode in
3696 let birdseye = isbirdseye state.mode in
3698 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3700 src#bool "presentation mode"
3701 (fun () -> conf.presentation)
3702 (fun v ->
3703 conf.presentation <- v;
3704 state.anchor <- getanchor ();
3705 represent ());
3707 src#bool "ignore case in searches"
3708 (fun () -> conf.icase)
3709 (fun v -> conf.icase <- v);
3711 src#bool "preload"
3712 (fun () -> conf.preload)
3713 (fun v -> conf.preload <- v);
3715 src#bool "highlight links"
3716 (fun () -> conf.hlinks)
3717 (fun v -> conf.hlinks <- v);
3719 src#bool "under info"
3720 (fun () -> conf.underinfo)
3721 (fun v -> conf.underinfo <- v);
3723 src#bool "persistent bookmarks"
3724 (fun () -> conf.savebmarks)
3725 (fun v -> conf.savebmarks <- v);
3727 src#bool "proportional display"
3728 (fun () -> conf.proportional)
3729 (fun v -> reqlayout conf.angle v);
3731 src#bool "trim margins"
3732 (fun () -> conf.trimmargins)
3733 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3735 src#bool "persistent location"
3736 (fun () -> conf.jumpback)
3737 (fun v -> conf.jumpback <- v);
3739 sep ();
3740 src#int "inter-page space"
3741 (fun () -> conf.interpagespace)
3742 (fun n ->
3743 conf.interpagespace <- n;
3744 let pageno, py =
3745 match state.layout with
3746 | [] -> 0, 0
3747 | l :: _ ->
3748 l.pageno, l.pagey
3750 state.maxy <- calcheight ();
3751 let y = getpagey pageno in
3752 gotoy (y + py)
3755 src#int "page bias"
3756 (fun () -> conf.pagebias)
3757 (fun v -> conf.pagebias <- v);
3759 src#int "scroll step"
3760 (fun () -> conf.scrollstep)
3761 (fun n -> conf.scrollstep <- n);
3763 src#int "auto scroll step"
3764 (fun () ->
3765 match state.autoscroll with
3766 | Some step -> step
3767 | _ -> conf.autoscrollstep)
3768 (fun n ->
3769 if state.autoscroll <> None
3770 then state.autoscroll <- Some n;
3771 conf.autoscrollstep <- n);
3773 src#int "zoom"
3774 (fun () -> truncate (conf.zoom *. 100.))
3775 (fun v -> setzoom ((float v) /. 100.));
3777 src#int "rotation"
3778 (fun () -> conf.angle)
3779 (fun v -> reqlayout v conf.proportional);
3781 src#int "scroll bar width"
3782 (fun () -> state.scrollw)
3783 (fun v ->
3784 state.scrollw <- v;
3785 conf.scrollbw <- v;
3786 reshape conf.winw conf.winh;
3789 src#int "scroll handle height"
3790 (fun () -> conf.scrollh)
3791 (fun v -> conf.scrollh <- v;);
3793 src#int "thumbnail width"
3794 (fun () -> conf.thumbw)
3795 (fun v ->
3796 conf.thumbw <- min 4096 v;
3797 match oldmode with
3798 | Birdseye beye ->
3799 leavebirdseye beye false;
3800 enterbirdseye ()
3801 | _ -> ()
3804 src#string "columns"
3805 (fun () ->
3806 match conf.columns with
3807 | None -> "1"
3808 | Some (multicol, _) -> columns_to_string multicol)
3809 (fun v ->
3810 let n, a, b = columns_of_string v in
3811 setcolumns n a b);
3813 sep ();
3814 src#caption "Presentation mode" 0;
3815 src#bool "scrollbar visible"
3816 (fun () -> conf.scrollbarinpm)
3817 (fun v ->
3818 if v != conf.scrollbarinpm
3819 then (
3820 conf.scrollbarinpm <- v;
3821 if conf.presentation
3822 then (
3823 state.scrollw <- if v then conf.scrollbw else 0;
3824 reshape conf.winw conf.winh;
3829 sep ();
3830 src#caption "Pixmap cache" 0;
3831 src#int_with_suffix "size (advisory)"
3832 (fun () -> conf.memlimit)
3833 (fun v -> conf.memlimit <- v);
3835 src#caption2 "used"
3836 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3837 (string_with_suffix_of_int state.memused)
3838 (Hashtbl.length state.tilemap)) 1;
3840 sep ();
3841 src#caption "Layout" 0;
3842 src#caption2 "Dimension"
3843 (fun () ->
3844 Printf.sprintf "%dx%d (virtual %dx%d)"
3845 conf.winw conf.winh
3846 state.w state.maxy)
3848 if conf.debug
3849 then
3850 src#caption2 "Position" (fun () ->
3851 Printf.sprintf "%dx%d" state.x state.y
3853 else
3854 src#caption2 "Visible" (fun () -> describe_location ()) 1
3857 sep ();
3858 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3859 "Save these parameters as global defaults at exit"
3860 (fun () -> conf.bedefault)
3861 (fun v -> conf.bedefault <- v)
3864 sep ();
3865 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3866 src#bool ~offset:0 ~btos "Extended parameters"
3867 (fun () -> !showextended)
3868 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3869 if !showextended
3870 then (
3871 src#bool "checkers"
3872 (fun () -> conf.checkers)
3873 (fun v -> conf.checkers <- v; setcheckers v);
3874 src#bool "verbose"
3875 (fun () -> conf.verbose)
3876 (fun v -> conf.verbose <- v);
3877 src#bool "invert colors"
3878 (fun () -> conf.invert)
3879 (fun v -> conf.invert <- v);
3880 src#bool "max fit"
3881 (fun () -> conf.maxhfit)
3882 (fun v -> conf.maxhfit <- v);
3883 src#bool "redirect stderr"
3884 (fun () -> conf.redirectstderr)
3885 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3886 src#string "uri launcher"
3887 (fun () -> conf.urilauncher)
3888 (fun v -> conf.urilauncher <- v);
3889 src#string "tile size"
3890 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3891 (fun v ->
3893 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3894 conf.tileh <- max 64 w;
3895 conf.tilew <- max 64 h;
3896 flushtiles ();
3897 with exn ->
3898 state.text <- Printf.sprintf "bad tile size `%s': %s"
3899 v (Printexc.to_string exn));
3900 src#int "texture count"
3901 (fun () -> conf.texcount)
3902 (fun v ->
3903 if realloctexts v
3904 then conf.texcount <- v
3905 else showtext '!' " Failed to set texture count please retry later"
3907 src#int "slice height"
3908 (fun () -> conf.sliceheight)
3909 (fun v ->
3910 conf.sliceheight <- v;
3911 wcmd "sliceh" [`i conf.sliceheight];
3913 src#int "anti-aliasing level"
3914 (fun () -> conf.aalevel)
3915 (fun v ->
3916 conf.aalevel <- bound v 0 8;
3917 state.anchor <- getanchor ();
3918 opendoc state.path state.password;
3920 src#int "ui font size"
3921 (fun () -> fstate.fontsize)
3922 (fun v -> setfontsize (bound v 5 100));
3923 colorp "background color"
3924 (fun () -> conf.bgcolor)
3925 (fun v -> conf.bgcolor <- v);
3926 src#bool "crop hack"
3927 (fun () -> conf.crophack)
3928 (fun v -> conf.crophack <- v);
3929 src#string "trim fuzz"
3930 (fun () -> irect_to_string conf.trimfuzz)
3931 (fun v ->
3933 conf.trimfuzz <- irect_of_string v;
3934 if conf.trimmargins
3935 then settrim true conf.trimfuzz;
3936 with exn ->
3937 state.text <- Printf.sprintf "bad irect `%s': %s"
3938 v (Printexc.to_string exn)
3940 src#string "throttle"
3941 (fun () ->
3942 match conf.maxwait with
3943 | None -> "show place holder if page is not ready"
3944 | Some time ->
3945 if time = infinity
3946 then "wait for page to fully render"
3947 else
3948 "wait " ^ string_of_float time
3949 ^ " seconds before showing placeholder"
3951 (fun v ->
3953 let f = float_of_string v in
3954 if f <= 0.0
3955 then conf.maxwait <- None
3956 else conf.maxwait <- Some f
3957 with exn ->
3958 state.text <- Printf.sprintf "bad time `%s': %s"
3959 v (Printexc.to_string exn)
3961 src#string "ghyll scroll"
3962 (fun () ->
3963 match conf.ghyllscroll with
3964 | None -> ""
3965 | Some nab -> ghyllscroll_to_string nab
3967 (fun v ->
3969 let gs =
3970 if String.length v = 0
3971 then None
3972 else Some (ghyllscroll_of_string v)
3974 conf.ghyllscroll <- gs
3975 with exn ->
3976 state.text <- Printf.sprintf "bad ghyll `%s': %s"
3977 v (Printexc.to_string exn)
3979 src#string "selection command"
3980 (fun () -> conf.selcmd)
3981 (fun v -> conf.selcmd <- v);
3982 src#colorspace "color space"
3983 (fun () -> colorspace_to_string conf.colorspace)
3984 (fun v ->
3985 conf.colorspace <- colorspace_of_int v;
3986 wcmd "cs" [`i v];
3987 load state.layout;
3991 sep ();
3992 src#caption "Document" 0;
3993 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3994 src#caption2 "Pages"
3995 (fun () -> string_of_int state.pagecount) 1;
3996 src#caption2 "Dimensions"
3997 (fun () -> string_of_int (List.length state.pdims)) 1;
3998 if conf.trimmargins
3999 then (
4000 sep ();
4001 src#caption "Trimmed margins" 0;
4002 src#caption2 "Dimensions"
4003 (fun () -> string_of_int (List.length state.pdims)) 1;
4006 src#reset prevmode prevuioh;
4008 fun () ->
4009 state.text <- "";
4010 let prevmode = state.mode
4011 and prevuioh = state.uioh in
4012 fillsrc prevmode prevuioh;
4013 let source = (src :> lvsource) in
4014 state.uioh <- coe (object (self)
4015 inherit listview ~source ~trusted:true as super
4016 val mutable m_prevmemused = 0
4017 method infochanged = function
4018 | Memused ->
4019 if m_prevmemused != state.memused
4020 then (
4021 m_prevmemused <- state.memused;
4022 G.postRedisplay "memusedchanged";
4024 | Pdim -> G.postRedisplay "pdimchanged"
4025 | Docinfo -> fillsrc prevmode prevuioh
4027 method special key =
4028 if Glut.getModifiers () land Glut.active_ctrl = 0
4029 then
4030 match key with
4031 | Glut.KEY_LEFT -> coe (self#updownlevel ~-1)
4032 | Glut.KEY_RIGHT -> coe (self#updownlevel 1)
4033 | _ -> super#special key
4034 else super#special key
4035 end);
4036 G.postRedisplay "info";
4039 let enterhelpmode =
4040 let source =
4041 (object
4042 inherit lvsourcebase
4043 method getitemcount = Array.length state.help
4044 method getitem n =
4045 let s, n, _ = state.help.(n) in
4046 (s, n)
4048 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4049 let optuioh =
4050 if not cancel
4051 then (
4052 m_qsearch <- qsearch;
4053 match state.help.(active) with
4054 | _, _, Action f -> Some (f uioh)
4055 | _ -> Some (uioh)
4057 else None
4059 m_active <- active;
4060 m_first <- first;
4061 m_pan <- pan;
4062 optuioh
4064 method hasaction n =
4065 match state.help.(n) with
4066 | _, _, Action _ -> true
4067 | _ -> false
4069 initializer
4070 m_active <- -1
4071 end)
4072 in fun () ->
4073 state.uioh <- coe (new listview ~source ~trusted:true);
4074 G.postRedisplay "help";
4077 let entermsgsmode =
4078 let msgsource =
4079 let re = Str.regexp "[\r\n]" in
4080 (object
4081 inherit lvsourcebase
4082 val mutable m_items = [||]
4084 method getitemcount = 1 + Array.length m_items
4086 method getitem n =
4087 if n = 0
4088 then "[Clear]", 0
4089 else m_items.(n-1), 0
4091 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4092 ignore uioh;
4093 if not cancel
4094 then (
4095 if active = 0
4096 then Buffer.clear state.errmsgs;
4097 m_qsearch <- qsearch;
4099 m_active <- active;
4100 m_first <- first;
4101 m_pan <- pan;
4102 None
4104 method hasaction n =
4105 n = 0
4107 method reset =
4108 state.newerrmsgs <- false;
4109 let l = Str.split re (Buffer.contents state.errmsgs) in
4110 m_items <- Array.of_list l
4112 initializer
4113 m_active <- 0
4114 end)
4115 in fun () ->
4116 state.text <- "";
4117 msgsource#reset;
4118 let source = (msgsource :> lvsource) in
4119 state.uioh <- coe (object
4120 inherit listview ~source ~trusted:false as super
4121 method display =
4122 if state.newerrmsgs
4123 then msgsource#reset;
4124 super#display
4125 end);
4126 G.postRedisplay "msgs";
4129 let quickbookmark ?title () =
4130 match state.layout with
4131 | [] -> ()
4132 | l :: _ ->
4133 let title =
4134 match title with
4135 | None ->
4136 let sec = Unix.gettimeofday () in
4137 let tm = Unix.localtime sec in
4138 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4139 (l.pageno+1)
4140 tm.Unix.tm_mday
4141 tm.Unix.tm_mon
4142 (tm.Unix.tm_year + 1900)
4143 tm.Unix.tm_hour
4144 tm.Unix.tm_min
4145 | Some title -> title
4147 state.bookmarks <-
4148 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4149 :: state.bookmarks
4152 let doreshape w h =
4153 state.fullscreen <- None;
4154 Glut.reshapeWindow w h;
4157 let viewkeyboard key =
4158 let enttext te =
4159 let mode = state.mode in
4160 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4161 state.text <- "";
4162 enttext ();
4163 G.postRedisplay "view:enttext"
4165 let c = Char.chr key in
4166 match c with
4167 | '\027' | 'q' -> (* escape *)
4168 begin match state.mstate with
4169 | Mzoomrect _ ->
4170 state.mstate <- Mnone;
4171 Glut.setCursor Glut.CURSOR_INHERIT;
4172 G.postRedisplay "kill zoom rect";
4173 | _ ->
4174 match state.ranchors with
4175 | [] -> raise Quit
4176 | (path, password, anchor) :: rest ->
4177 state.ranchors <- rest;
4178 state.anchor <- anchor;
4179 opendoc path password
4180 end;
4182 | '\008' -> (* backspace *)
4183 let y = getnav ~-1 in
4184 gotoy_and_clear_text y
4186 | 'o' ->
4187 enteroutlinemode ()
4189 | 'u' ->
4190 state.rects <- [];
4191 state.text <- "";
4192 G.postRedisplay "dehighlight";
4194 | '/' | '?' ->
4195 let ondone isforw s =
4196 cbput state.hists.pat s;
4197 state.searchpattern <- s;
4198 search s isforw
4200 let s = String.create 1 in
4201 s.[0] <- c;
4202 enttext (s, "", Some (onhist state.hists.pat),
4203 textentry, ondone (c ='/'))
4205 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4206 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4207 setzoom (conf.zoom +. incr)
4209 | '+' ->
4210 let ondone s =
4211 let n =
4212 try int_of_string s with exc ->
4213 state.text <- Printf.sprintf "bad integer `%s': %s"
4214 s (Printexc.to_string exc);
4215 max_int
4217 if n != max_int
4218 then (
4219 conf.pagebias <- n;
4220 state.text <- "page bias is now " ^ string_of_int n;
4223 enttext ("page bias: ", "", None, intentry, ondone)
4225 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4226 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4227 setzoom (max 0.01 (conf.zoom -. decr))
4229 | '-' ->
4230 let ondone msg = state.text <- msg in
4231 enttext (
4232 "option [acfhilpstvxACPRSZTIS]: ", "", None,
4233 optentry state.mode, ondone
4236 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4237 setzoom 1.0
4239 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4240 let zoom = zoomforh conf.winw conf.winh state.scrollw in
4241 if zoom < 1.0
4242 then setzoom zoom
4244 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4245 togglebirdseye ()
4247 | '0' .. '9' ->
4248 let ondone s =
4249 let n =
4250 try int_of_string s with exc ->
4251 state.text <- Printf.sprintf "bad integer `%s': %s"
4252 s (Printexc.to_string exc);
4255 if n >= 0
4256 then (
4257 addnav ();
4258 cbput state.hists.pag (string_of_int n);
4259 gotopage1 (n + conf.pagebias - 1) 0;
4262 let pageentry text key =
4263 match Char.unsafe_chr key with
4264 | 'g' -> TEdone text
4265 | _ -> intentry text key
4267 let text = "x" in text.[0] <- c;
4268 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
4270 | 'b' ->
4271 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4272 reshape conf.winw conf.winh;
4274 | 'l' ->
4275 conf.hlinks <- not conf.hlinks;
4276 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4277 G.postRedisplay "toggle highlightlinks";
4279 | 'a' ->
4280 begin match state.autoscroll with
4281 | Some step ->
4282 conf.autoscrollstep <- step;
4283 state.autoscroll <- None
4284 | None ->
4285 if conf.autoscrollstep = 0
4286 then state.autoscroll <- Some 1
4287 else state.autoscroll <- Some conf.autoscrollstep
4290 | 'P' ->
4291 conf.presentation <- not conf.presentation;
4292 if conf.presentation
4293 then (
4294 if not conf.scrollbarinpm
4295 then state.scrollw <- 0;
4297 else
4298 state.scrollw <- conf.scrollbw;
4300 showtext ' ' ("presentation mode " ^
4301 if conf.presentation then "on" else "off");
4302 state.anchor <- getanchor ();
4303 represent ()
4305 | 'f' ->
4306 begin match state.fullscreen with
4307 | None ->
4308 state.fullscreen <- Some (conf.winw, conf.winh);
4309 Glut.fullScreen ()
4310 | Some (w, h) ->
4311 state.fullscreen <- None;
4312 doreshape w h
4315 | 'g' ->
4316 gotoy_and_clear_text 0
4318 | 'G' ->
4319 gotopage1 (state.pagecount - 1) 0
4321 | 'n' ->
4322 search state.searchpattern true
4324 | 'p' | 'N' ->
4325 search state.searchpattern false
4327 | 't' ->
4328 begin match state.layout with
4329 | [] -> ()
4330 | l :: _ ->
4331 gotoy_and_clear_text (getpagey l.pageno)
4334 | ' ' ->
4335 begin match List.rev state.layout with
4336 | [] -> ()
4337 | l :: _ ->
4338 let pageno = min (l.pageno+1) (state.pagecount-1) in
4339 gotoy_and_clear_text (getpagey pageno)
4342 | '\127' -> (* del *)
4343 begin match state.layout with
4344 | [] -> ()
4345 | l :: _ ->
4346 let pageno = max 0 (l.pageno-1) in
4347 gotoy_and_clear_text (getpagey pageno)
4350 | '=' ->
4351 showtext ' ' (describe_location ());
4353 | 'w' ->
4354 begin match state.layout with
4355 | [] -> ()
4356 | l :: _ ->
4357 doreshape (l.pagew + state.scrollw) l.pageh;
4358 G.postRedisplay "w"
4361 | '\'' ->
4362 enterbookmarkmode ()
4364 | 'h' ->
4365 enterhelpmode ()
4367 | 'i' ->
4368 enterinfomode ()
4370 | 'e' when conf.redirectstderr ->
4371 entermsgsmode ()
4373 | 'm' ->
4374 let ondone s =
4375 match state.layout with
4376 | l :: _ ->
4377 state.bookmarks <-
4378 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4379 :: state.bookmarks
4380 | _ -> ()
4382 enttext ("bookmark: ", "", None, textentry, ondone)
4384 | '~' ->
4385 quickbookmark ();
4386 showtext ' ' "Quick bookmark added";
4388 | 'z' ->
4389 begin match state.layout with
4390 | l :: _ ->
4391 let rect = getpdimrect l.pagedimno in
4392 let w, h =
4393 if conf.crophack
4394 then
4395 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4396 truncate (1.2 *. (rect.(3) -. rect.(0))))
4397 else
4398 (truncate (rect.(1) -. rect.(0)),
4399 truncate (rect.(3) -. rect.(0)))
4401 let w = truncate ((float w)*.conf.zoom)
4402 and h = truncate ((float h)*.conf.zoom) in
4403 if w != 0 && h != 0
4404 then (
4405 state.anchor <- getanchor ();
4406 doreshape (w + state.scrollw) (h + conf.interpagespace)
4408 G.postRedisplay "z";
4410 | [] -> ()
4413 | '\000' -> (* ctrl-2 *)
4414 let maxw = getmaxw () in
4415 if maxw > 0.0
4416 then setzoom (maxw /. float conf.winw)
4418 | '<' | '>' ->
4419 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
4421 | '[' | ']' ->
4422 conf.colorscale <-
4423 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
4425 G.postRedisplay "brightness";
4427 | 'k' ->
4428 begin match state.mode with
4429 | Birdseye beye -> upbirdseye 1 beye
4430 | _ -> gotoy (clamp (-conf.scrollstep))
4433 | 'j' ->
4434 begin match state.mode with
4435 | Birdseye beye -> downbirdseye 1 beye
4436 | _ -> gotoy (clamp conf.scrollstep)
4439 | 'r' ->
4440 state.anchor <- getanchor ();
4441 opendoc state.path state.password
4443 | 'v' when not conf.debug ->
4444 List.iter debugl state.layout;
4446 | 'v' when conf.debug ->
4447 state.rects <- [];
4448 List.iter (fun l ->
4449 match getopaque l.pageno with
4450 | None -> ()
4451 | Some opaque ->
4452 let x0, y0, x1, y1 = pagebbox opaque in
4453 let a,b = float x0, float y0 in
4454 let c,d = float x1, float y0 in
4455 let e,f = float x1, float y1 in
4456 let h,j = float x0, float y1 in
4457 let rect = (a,b,c,d,e,f,h,j) in
4458 debugrect rect;
4459 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4460 ) state.layout;
4461 G.postRedisplay "v";
4463 | _ ->
4464 vlog "huh? %d %c" key (Char.chr key);
4467 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
4468 match key with
4469 | 27 -> (* escape *)
4470 leavebirdseye beye true
4472 | 12 -> (* ctrl-l *)
4473 let y, h = getpageyh pageno in
4474 let top = (conf.winh - h) / 2 in
4475 gotoy (max 0 (y - top))
4477 | 13 -> (* enter *)
4478 leavebirdseye beye false
4480 | _ ->
4481 viewkeyboard key
4484 let keyboard ~key ~x ~y =
4485 ignore x;
4486 ignore y;
4487 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
4488 then wcmd "interrupt" []
4489 else state.uioh <- state.uioh#key key
4492 let birdseyespecial key ((oconf, leftx, _, hooverpageno, anchor) as beye) =
4493 let incr =
4494 match conf.columns with
4495 | None -> 1
4496 | Some ((c, _, _), _) -> c
4498 match key with
4499 | Glut.KEY_UP -> upbirdseye incr beye
4500 | Glut.KEY_DOWN -> downbirdseye incr beye
4501 | Glut.KEY_LEFT -> upbirdseye 1 beye
4502 | Glut.KEY_RIGHT -> downbirdseye 1 beye
4504 | Glut.KEY_PAGE_UP ->
4505 begin match state.layout with
4506 | l :: _ ->
4507 if l.pagey != 0
4508 then (
4509 state.mode <- Birdseye (
4510 oconf, leftx, l.pageno, hooverpageno, anchor
4512 gotopage1 l.pageno 0;
4514 else (
4515 let layout = layout (state.y-conf.winh) conf.winh in
4516 match layout with
4517 | [] -> gotoy (clamp (-conf.winh))
4518 | l :: _ ->
4519 state.mode <- Birdseye (
4520 oconf, leftx, l.pageno, hooverpageno, anchor
4522 gotopage1 l.pageno 0
4525 | [] -> gotoy (clamp (-conf.winh))
4526 end;
4528 | Glut.KEY_PAGE_DOWN ->
4529 begin match List.rev state.layout with
4530 | l :: _ ->
4531 let layout = layout (state.y + conf.winh) conf.winh in
4532 begin match layout with
4533 | [] ->
4534 let incr = l.pageh - l.pagevh in
4535 if incr = 0
4536 then (
4537 state.mode <-
4538 Birdseye (
4539 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4541 G.postRedisplay "birdseye pagedown";
4543 else gotoy (clamp (incr + conf.interpagespace*2));
4545 | l :: _ ->
4546 state.mode <-
4547 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4548 gotopage1 l.pageno 0;
4551 | [] -> gotoy (clamp conf.winh)
4552 end;
4554 | Glut.KEY_HOME ->
4555 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4556 gotopage1 0 0
4558 | Glut.KEY_END ->
4559 let pageno = state.pagecount - 1 in
4560 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4561 if not (pagevisible state.layout pageno)
4562 then
4563 let h =
4564 match List.rev state.pdims with
4565 | [] -> conf.winh
4566 | (_, _, h, _) :: _ -> h
4568 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4569 else G.postRedisplay "birdseye end";
4570 | _ -> ()
4573 let setautoscrollspeed step goingdown =
4574 let incr = max 1 ((abs step) / 2) in
4575 let incr = if goingdown then incr else -incr in
4576 let astep = step + incr in
4577 state.autoscroll <- Some astep;
4580 let special ~key ~x ~y =
4581 ignore x;
4582 ignore y;
4583 state.uioh <- state.uioh#special key
4586 let drawpage l =
4587 let color =
4588 match state.mode with
4589 | Textentry _ -> scalecolor 0.4
4590 | View -> scalecolor 1.0
4591 | Birdseye (_, _, pageno, hooverpageno, _) ->
4592 if l.pageno = hooverpageno
4593 then scalecolor 0.9
4594 else (
4595 if l.pageno = pageno
4596 then scalecolor 1.0
4597 else scalecolor 0.8
4600 drawtiles l color;
4601 begin match getopaque l.pageno with
4602 | Some opaque ->
4603 if tileready l l.pagex l.pagey
4604 then
4605 let x = l.pagedispx - l.pagex
4606 and y = l.pagedispy - l.pagey in
4607 postprocess opaque conf.hlinks x y;
4609 | _ -> ()
4610 end;
4613 let scrollindicator () =
4614 let sbw, ph, sh = state.uioh#scrollph in
4615 let sbh, pw, sw = state.uioh#scrollpw in
4617 GlDraw.color (0.64, 0.64, 0.64);
4618 GlDraw.rect
4619 (float (conf.winw - sbw), 0.)
4620 (float conf.winw, float conf.winh)
4622 GlDraw.rect
4623 (0., float (conf.winh - sbh))
4624 (float (conf.winw - state.scrollw - 1), float conf.winh)
4626 GlDraw.color (0.0, 0.0, 0.0);
4628 GlDraw.rect
4629 (float (conf.winw - sbw), ph)
4630 (float conf.winw, ph +. sh)
4632 GlDraw.rect
4633 (pw, float (conf.winh - sbh))
4634 (pw +. sw, float conf.winh)
4638 let pagetranslatepoint l x y =
4639 let dy = y - l.pagedispy in
4640 let y = dy + l.pagey in
4641 let dx = x - l.pagedispx in
4642 let x = dx + l.pagex in
4643 (x, y);
4646 let showsel () =
4647 match state.mstate with
4648 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4651 | Msel ((x0, y0), (x1, y1)) ->
4652 let rec loop = function
4653 | l :: ls ->
4654 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4655 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
4656 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
4657 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
4658 then
4659 match getopaque l.pageno with
4660 | Some opaque ->
4661 let dx, dy = pagetranslatepoint l 0 0 in
4662 let x0 = x0 + dx
4663 and y0 = y0 + dy
4664 and x1 = x1 + dx
4665 and y1 = y1 + dy in
4666 GlMat.mode `modelview;
4667 GlMat.push ();
4668 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
4669 seltext opaque (x0, y0, x1, y1);
4670 GlMat.pop ();
4671 | _ -> ()
4672 else loop ls
4673 | [] -> ()
4675 loop state.layout
4678 let showrects () =
4679 Gl.enable `blend;
4680 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4681 GlDraw.polygon_mode `both `fill;
4682 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4683 List.iter
4684 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4685 List.iter (fun l ->
4686 if l.pageno = pageno
4687 then (
4688 let dx = float (l.pagedispx - l.pagex) in
4689 let dy = float (l.pagedispy - l.pagey) in
4690 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4691 GlDraw.begins `quads;
4693 GlDraw.vertex2 (x0+.dx, y0+.dy);
4694 GlDraw.vertex2 (x1+.dx, y1+.dy);
4695 GlDraw.vertex2 (x2+.dx, y2+.dy);
4696 GlDraw.vertex2 (x3+.dx, y3+.dy);
4698 GlDraw.ends ();
4700 ) state.layout
4701 ) state.rects
4703 Gl.disable `blend;
4706 let display () =
4707 GlClear.color (scalecolor2 conf.bgcolor);
4708 GlClear.clear [`color];
4709 List.iter drawpage state.layout;
4710 showrects ();
4711 showsel ();
4712 state.uioh#display;
4713 scrollindicator ();
4714 begin match state.mstate with
4715 | Mzoomrect ((x0, y0), (x1, y1)) ->
4716 Gl.enable `blend;
4717 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4718 GlDraw.polygon_mode `both `fill;
4719 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4720 GlDraw.rect (float x0, float y0)
4721 (float x1, float y1);
4722 Gl.disable `blend;
4723 | _ -> ()
4724 end;
4725 enttext ();
4726 Glut.swapBuffers ();
4729 let getunder x y =
4730 let rec f = function
4731 | l :: rest ->
4732 begin match getopaque l.pageno with
4733 | Some opaque ->
4734 let x0 = l.pagedispx in
4735 let x1 = x0 + l.pagevw in
4736 let y0 = l.pagedispy in
4737 let y1 = y0 + l.pagevh in
4738 if y >= y0 && y <= y1 && x >= x0 && x <= x1
4739 then
4740 let px, py = pagetranslatepoint l x y in
4741 match whatsunder opaque px py with
4742 | Unone -> f rest
4743 | under -> under
4744 else f rest
4745 | _ ->
4746 f rest
4748 | [] -> Unone
4750 f state.layout
4753 let zoomrect x y x1 y1 =
4754 let x0 = min x x1
4755 and x1 = max x x1
4756 and y0 = min y y1 in
4757 gotoy (state.y + y0);
4758 state.anchor <- getanchor ();
4759 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4760 let margin =
4761 if state.w < conf.winw - state.scrollw
4762 then (conf.winw - state.scrollw - state.w) / 2
4763 else 0
4765 state.x <- (state.x + margin) - x0;
4766 setzoom zoom;
4767 Glut.setCursor Glut.CURSOR_INHERIT;
4768 state.mstate <- Mnone;
4771 let scrollx x =
4772 let winw = conf.winw - state.scrollw - 1 in
4773 let s = float x /. float winw in
4774 let destx = truncate (float (state.w + winw) *. s) in
4775 state.x <- winw - destx;
4776 gotoy_and_clear_text state.y;
4777 state.mstate <- Mscrollx;
4780 let scrolly y =
4781 let s = float y /. float conf.winh in
4782 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4783 gotoy_and_clear_text desty;
4784 state.mstate <- Mscrolly;
4787 let viewmouse button bstate x y =
4788 match button with
4789 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4790 if Glut.getModifiers () land Glut.active_ctrl != 0
4791 then (
4792 match state.mstate with
4793 | Mzoom (oldn, i) ->
4794 if oldn = n
4795 then (
4796 if i = 2
4797 then
4798 let incr =
4799 match n with
4800 | 4 ->
4801 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4802 | _ ->
4803 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4805 let zoom = conf.zoom -. incr in
4806 setzoom zoom;
4807 state.mstate <- Mzoom (n, 0);
4808 else
4809 state.mstate <- Mzoom (n, i+1);
4811 else state.mstate <- Mzoom (n, 0)
4813 | _ -> state.mstate <- Mzoom (n, 0)
4815 else (
4816 match state.autoscroll with
4817 | Some step -> setautoscrollspeed step (n=4)
4818 | None ->
4819 let incr =
4820 if n = 3
4821 then -conf.scrollstep
4822 else conf.scrollstep
4824 let incr = incr * 2 in
4825 let y = clamp incr in
4826 gotoy_and_clear_text y
4829 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4830 if bstate = Glut.DOWN
4831 then (
4832 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4833 state.mstate <- Mpan (x, y)
4835 else
4836 state.mstate <- Mnone
4838 | Glut.RIGHT_BUTTON ->
4839 if bstate = Glut.DOWN
4840 then (
4841 Glut.setCursor Glut.CURSOR_CYCLE;
4842 let p = (x, y) in
4843 state.mstate <- Mzoomrect (p, p)
4845 else (
4846 match state.mstate with
4847 | Mzoomrect ((x0, y0), _) ->
4848 if abs (x-x0) > 10 && abs (y - y0) > 10
4849 then zoomrect x0 y0 x y
4850 else (
4851 state.mstate <- Mnone;
4852 Glut.setCursor Glut.CURSOR_INHERIT;
4853 G.postRedisplay "kill accidental zoom rect";
4855 | _ ->
4856 Glut.setCursor Glut.CURSOR_INHERIT;
4857 state.mstate <- Mnone
4860 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4861 if bstate = Glut.DOWN
4862 then
4863 let _, position, sh = state.uioh#scrollph in
4864 if y > truncate position && y < truncate (position +. sh)
4865 then state.mstate <- Mscrolly
4866 else scrolly y
4867 else
4868 state.mstate <- Mnone
4870 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4871 if bstate = Glut.DOWN
4872 then
4873 let _, position, sw = state.uioh#scrollpw in
4874 if x > truncate position && x < truncate (position +. sw)
4875 then state.mstate <- Mscrollx
4876 else scrollx x
4877 else
4878 state.mstate <- Mnone
4880 | Glut.LEFT_BUTTON ->
4881 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4882 begin match dest with
4883 | Ulinkgoto (pageno, top) ->
4884 if pageno >= 0
4885 then (
4886 addnav ();
4887 gotopage1 pageno top;
4890 | Ulinkuri s ->
4891 gotouri s
4893 | Uremote (filename, pageno) ->
4894 let path =
4895 if Sys.file_exists filename
4896 then filename
4897 else
4898 let dir = Filename.dirname state.path in
4899 let path = Filename.concat dir filename in
4900 if Sys.file_exists path
4901 then path
4902 else ""
4904 if String.length path > 0
4905 then (
4906 let anchor = getanchor () in
4907 let ranchor = state.path, state.password, anchor in
4908 state.anchor <- (pageno, 0.0);
4909 state.ranchors <- ranchor :: state.ranchors;
4910 opendoc path "";
4912 else showtext '!' ("Could not find " ^ filename)
4914 | Uunexpected _ | Ulaunch _ | Unamed _ -> ()
4916 | Unone when bstate = Glut.DOWN ->
4917 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4918 state.mstate <- Mpan (x, y);
4920 | Unone | Utext _ ->
4921 if bstate = Glut.DOWN
4922 then (
4923 if conf.angle mod 360 = 0
4924 then (
4925 state.mstate <- Msel ((x, y), (x, y));
4926 G.postRedisplay "mouse select";
4929 else (
4930 match state.mstate with
4931 | Mnone -> ()
4933 | Mzoom _ | Mscrollx | Mscrolly ->
4934 state.mstate <- Mnone
4936 | Mzoomrect ((x0, y0), _) ->
4937 zoomrect x0 y0 x y
4939 | Mpan _ ->
4940 Glut.setCursor Glut.CURSOR_INHERIT;
4941 state.mstate <- Mnone
4943 | Msel ((_, y0), (_, y1)) ->
4944 let rec loop = function
4945 | [] -> ()
4946 | l :: rest ->
4947 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4948 || ((y1 >= l.pagedispy
4949 && y1 <= (l.pagedispy + l.pagevh)))
4950 then
4951 match getopaque l.pageno with
4952 | Some opaque ->
4953 copysel conf.selcmd opaque;
4954 G.postRedisplay "copysel"
4955 | _ -> ()
4956 else loop rest
4958 loop state.layout;
4959 Glut.setCursor Glut.CURSOR_INHERIT;
4960 state.mstate <- Mnone;
4964 | _ -> ()
4967 let birdseyemouse button bstate x y
4968 (conf, leftx, _, hooverpageno, anchor) =
4969 match button with
4970 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4971 let rec loop = function
4972 | [] -> ()
4973 | l :: rest ->
4974 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4975 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4976 then (
4977 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4979 else loop rest
4981 loop state.layout
4982 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
4983 | _ -> ()
4986 let mouse bstate button x y =
4987 state.uioh <- state.uioh#button button bstate x y;
4990 let mouse ~button ~state ~x ~y = mouse state button x y;;
4992 let motion ~x ~y =
4993 state.uioh <- state.uioh#motion x y
4996 let pmotion ~x ~y =
4997 state.uioh <- state.uioh#pmotion x y;
5000 let uioh = object
5001 method display = ()
5003 method key key =
5004 begin match state.mode with
5005 | Textentry textentry -> textentrykeyboard key textentry
5006 | Birdseye birdseye -> birdseyekeyboard key birdseye
5007 | View -> viewkeyboard key
5008 end;
5009 state.uioh
5011 method special key =
5012 begin match state.mode with
5013 | View | (Birdseye _) when key = Glut.KEY_F9 ->
5014 togglebirdseye ()
5016 | Birdseye vals ->
5017 birdseyespecial key vals
5019 | View when key = Glut.KEY_F1 ->
5020 enterhelpmode ()
5022 | View ->
5023 begin match state.autoscroll with
5024 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
5025 setautoscrollspeed step (key = Glut.KEY_DOWN)
5027 | _ ->
5028 let y =
5029 match key with
5030 | Glut.KEY_F3 -> search state.searchpattern true; state.y
5031 | Glut.KEY_UP ->
5032 if Glut.getModifiers () land Glut.active_ctrl != 0
5033 then
5034 if Glut.getModifiers () land Glut.active_shift != 0
5035 then (setzoom state.prevzoom; state.y)
5036 else clamp (-conf.winh/2)
5037 else clamp (-conf.scrollstep)
5038 | Glut.KEY_DOWN ->
5039 if Glut.getModifiers () land Glut.active_ctrl != 0
5040 then
5041 if Glut.getModifiers () land Glut.active_shift != 0
5042 then (setzoom state.prevzoom; state.y)
5043 else clamp (conf.winh/2)
5044 else clamp (conf.scrollstep)
5045 | Glut.KEY_PAGE_UP ->
5046 if Glut.getModifiers () land Glut.active_ctrl != 0
5047 then
5048 match state.layout with
5049 | [] -> state.y
5050 | l :: _ -> state.y - l.pagey
5051 else
5052 clamp (-conf.winh)
5053 | Glut.KEY_PAGE_DOWN ->
5054 if Glut.getModifiers () land Glut.active_ctrl != 0
5055 then
5056 match List.rev state.layout with
5057 | [] -> state.y
5058 | l :: _ -> getpagey l.pageno
5059 else
5060 clamp conf.winh
5061 | Glut.KEY_HOME ->
5062 addnav ();
5064 | Glut.KEY_END ->
5065 addnav ();
5066 state.maxy - (if conf.maxhfit then conf.winh else 0)
5068 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
5069 Glut.getModifiers () land Glut.active_alt != 0 ->
5070 getnav (if key = Glut.KEY_LEFT then 1 else -1)
5072 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
5073 let dx =
5074 if Glut.getModifiers () land Glut.active_ctrl != 0
5075 then (conf.winw / 2)
5076 else 10
5078 state.x <- state.x - dx;
5079 state.y
5080 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
5081 let dx =
5082 if Glut.getModifiers () land Glut.active_ctrl != 0
5083 then (conf.winw / 2)
5084 else 10
5086 state.x <- state.x + dx;
5087 state.y
5089 | _ -> state.y
5091 if abs (state.y - y) > conf.scrollstep*2
5092 then gotoghyll y
5093 else gotoy_and_clear_text y
5096 | Textentry te -> textentryspecial key te
5097 end;
5098 state.uioh
5100 method button button bstate x y =
5101 begin match state.mode with
5102 | View -> viewmouse button bstate x y
5103 | Birdseye beye -> birdseyemouse button bstate x y beye
5104 | Textentry _ -> ()
5105 end;
5106 state.uioh
5108 method motion x y =
5109 begin match state.mode with
5110 | Textentry _ -> ()
5111 | View | Birdseye _ ->
5112 match state.mstate with
5113 | Mzoom _ | Mnone -> ()
5115 | Mpan (x0, y0) ->
5116 let dx = x - x0
5117 and dy = y0 - y in
5118 state.mstate <- Mpan (x, y);
5119 if conf.zoom > 1.0 then state.x <- state.x + dx;
5120 let y = clamp dy in
5121 gotoy_and_clear_text y
5123 | Msel (a, _) ->
5124 state.mstate <- Msel (a, (x, y));
5125 G.postRedisplay "motion select";
5127 | Mscrolly ->
5128 let y = min conf.winh (max 0 y) in
5129 scrolly y
5131 | Mscrollx ->
5132 let x = min conf.winw (max 0 x) in
5133 scrollx x
5135 | Mzoomrect (p0, _) ->
5136 state.mstate <- Mzoomrect (p0, (x, y));
5137 G.postRedisplay "motion zoomrect";
5138 end;
5139 state.uioh
5141 method pmotion x y =
5142 begin match state.mode with
5143 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5144 let rec loop = function
5145 | [] ->
5146 if hooverpageno != -1
5147 then (
5148 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5149 G.postRedisplay "pmotion birdseye no hoover";
5151 | l :: rest ->
5152 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5153 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5154 then (
5155 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5156 G.postRedisplay "pmotion birdseye hoover";
5158 else loop rest
5160 loop state.layout
5162 | Textentry _ -> ()
5164 | View ->
5165 match state.mstate with
5166 | Mnone ->
5167 begin match getunder x y with
5168 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
5169 | Ulinkuri uri ->
5170 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
5171 Glut.setCursor Glut.CURSOR_INFO
5172 | Ulinkgoto (page, _) ->
5173 if conf.underinfo
5174 then showtext 'p' ("age: " ^ string_of_int (page+1));
5175 Glut.setCursor Glut.CURSOR_INFO
5176 | Utext s ->
5177 if conf.underinfo then showtext 'f' ("ont: " ^ s);
5178 Glut.setCursor Glut.CURSOR_TEXT
5179 | Uunexpected s ->
5180 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
5181 Glut.setCursor Glut.CURSOR_INHERIT
5182 | Ulaunch s ->
5183 if conf.underinfo then showtext 'l' ("launch: " ^ s);
5184 Glut.setCursor Glut.CURSOR_INHERIT
5185 | Unamed s ->
5186 if conf.underinfo then showtext 'n' ("named: " ^ s);
5187 Glut.setCursor Glut.CURSOR_INHERIT
5188 | Uremote (filename, pageno) ->
5189 if conf.underinfo then showtext 'r'
5190 (Printf.sprintf "emote: %s (%d)" filename pageno);
5191 Glut.setCursor Glut.CURSOR_INFO
5194 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5196 end;
5197 state.uioh
5199 method infochanged _ = ()
5201 method scrollph =
5202 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5203 let p, h = scrollph state.y maxy in
5204 state.scrollw, p, h
5206 method scrollpw =
5207 let winw = conf.winw - state.scrollw - 1 in
5208 let fwinw = float winw in
5209 let sw =
5210 let sw = fwinw /. float state.w in
5211 let sw = fwinw *. sw in
5212 max sw (float conf.scrollh)
5214 let position, sw =
5215 let f = state.w+winw in
5216 let r = float (winw-state.x) /. float f in
5217 let p = fwinw *. r in
5218 p-.sw/.2., sw
5220 let sw =
5221 if position +. sw > fwinw
5222 then fwinw -. position
5223 else sw
5225 state.hscrollh, position, sw
5226 end;;
5228 module Config =
5229 struct
5230 open Parser
5232 let fontpath = ref "";;
5233 let wmclasshack = ref false;;
5235 let unent s =
5236 let l = String.length s in
5237 let b = Buffer.create l in
5238 unent b s 0 l;
5239 Buffer.contents b;
5242 let home =
5244 match platform with
5245 | Pwindows | Pmingw -> Sys.getenv "HOMEPATH"
5246 | _ -> Sys.getenv "HOME"
5247 with exn ->
5248 prerr_endline
5249 ("Can not determine home directory location: " ^
5250 Printexc.to_string exn);
5254 let config_of c attrs =
5255 let apply c k v =
5257 match k with
5258 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5259 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5260 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5261 | "preload" -> { c with preload = bool_of_string v }
5262 | "page-bias" -> { c with pagebias = int_of_string v }
5263 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5264 | "auto-scroll-step" ->
5265 { c with autoscrollstep = max 0 (int_of_string v) }
5266 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5267 | "crop-hack" -> { c with crophack = bool_of_string v }
5268 | "throttle" ->
5269 let mw =
5270 match String.lowercase v with
5271 | "true" -> Some infinity
5272 | "false" -> None
5273 | f -> Some (float_of_string f)
5275 { c with maxwait = mw}
5276 | "highlight-links" -> { c with hlinks = bool_of_string v }
5277 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5278 | "vertical-margin" ->
5279 { c with interpagespace = max 0 (int_of_string v) }
5280 | "zoom" ->
5281 let zoom = float_of_string v /. 100. in
5282 let zoom = max zoom 0.0 in
5283 { c with zoom = zoom }
5284 | "presentation" -> { c with presentation = bool_of_string v }
5285 | "rotation-angle" -> { c with angle = int_of_string v }
5286 | "width" -> { c with winw = max 20 (int_of_string v) }
5287 | "height" -> { c with winh = max 20 (int_of_string v) }
5288 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5289 | "proportional-display" -> { c with proportional = bool_of_string v }
5290 | "pixmap-cache-size" ->
5291 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5292 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5293 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5294 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5295 | "persistent-location" -> { c with jumpback = bool_of_string v }
5296 | "background-color" -> { c with bgcolor = color_of_string v }
5297 | "scrollbar-in-presentation" ->
5298 { c with scrollbarinpm = bool_of_string v }
5299 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5300 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5301 | "mupdf-store-size" ->
5302 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5303 | "checkers" -> { c with checkers = bool_of_string v }
5304 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5305 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5306 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5307 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
5308 | "uri-launcher" -> { c with urilauncher = unent v }
5309 | "color-space" -> { c with colorspace = colorspace_of_string v }
5310 | "invert-colors" -> { c with invert = bool_of_string v }
5311 | "brightness" -> { c with colorscale = float_of_string v }
5312 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5313 | "ghyllscroll" ->
5314 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5315 | "columns" ->
5316 let nab = columns_of_string v in
5317 { c with columns = Some (nab, [||]) }
5318 | "birds-eye-columns" ->
5319 { c with beyecolumns = Some (max (int_of_string v) 2) }
5320 | "selection-command" -> { c with selcmd = unent v }
5321 | _ -> c
5322 with exn ->
5323 prerr_endline ("Error processing attribute (`" ^
5324 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5327 let rec fold c = function
5328 | [] -> c
5329 | (k, v) :: rest ->
5330 let c = apply c k v in
5331 fold c rest
5333 fold c attrs;
5336 let fromstring f pos n v d =
5337 try f v
5338 with exn ->
5339 dolog "Error processing attribute (%S=%S) at %d\n%s"
5340 n v pos (Printexc.to_string exn)
5345 let bookmark_of attrs =
5346 let rec fold title page rely = function
5347 | ("title", v) :: rest -> fold v page rely rest
5348 | ("page", v) :: rest -> fold title v rely rest
5349 | ("rely", v) :: rest -> fold title page v rest
5350 | _ :: rest -> fold title page rely rest
5351 | [] -> title, page, rely
5353 fold "invalid" "0" "0" attrs
5356 let doc_of attrs =
5357 let rec fold path page rely pan = function
5358 | ("path", v) :: rest -> fold v page rely pan rest
5359 | ("page", v) :: rest -> fold path v rely pan rest
5360 | ("rely", v) :: rest -> fold path page v pan rest
5361 | ("pan", v) :: rest -> fold path page rely v rest
5362 | _ :: rest -> fold path page rely pan rest
5363 | [] -> path, page, rely, pan
5365 fold "" "0" "0" "0" attrs
5368 let setconf dst src =
5369 dst.scrollbw <- src.scrollbw;
5370 dst.scrollh <- src.scrollh;
5371 dst.icase <- src.icase;
5372 dst.preload <- src.preload;
5373 dst.pagebias <- src.pagebias;
5374 dst.verbose <- src.verbose;
5375 dst.scrollstep <- src.scrollstep;
5376 dst.maxhfit <- src.maxhfit;
5377 dst.crophack <- src.crophack;
5378 dst.autoscrollstep <- src.autoscrollstep;
5379 dst.maxwait <- src.maxwait;
5380 dst.hlinks <- src.hlinks;
5381 dst.underinfo <- src.underinfo;
5382 dst.interpagespace <- src.interpagespace;
5383 dst.zoom <- src.zoom;
5384 dst.presentation <- src.presentation;
5385 dst.angle <- src.angle;
5386 dst.winw <- src.winw;
5387 dst.winh <- src.winh;
5388 dst.savebmarks <- src.savebmarks;
5389 dst.memlimit <- src.memlimit;
5390 dst.proportional <- src.proportional;
5391 dst.texcount <- src.texcount;
5392 dst.sliceheight <- src.sliceheight;
5393 dst.thumbw <- src.thumbw;
5394 dst.jumpback <- src.jumpback;
5395 dst.bgcolor <- src.bgcolor;
5396 dst.scrollbarinpm <- src.scrollbarinpm;
5397 dst.tilew <- src.tilew;
5398 dst.tileh <- src.tileh;
5399 dst.mustoresize <- src.mustoresize;
5400 dst.checkers <- src.checkers;
5401 dst.aalevel <- src.aalevel;
5402 dst.trimmargins <- src.trimmargins;
5403 dst.trimfuzz <- src.trimfuzz;
5404 dst.urilauncher <- src.urilauncher;
5405 dst.colorspace <- src.colorspace;
5406 dst.invert <- src.invert;
5407 dst.colorscale <- src.colorscale;
5408 dst.redirectstderr <- src.redirectstderr;
5409 dst.ghyllscroll <- src.ghyllscroll;
5410 dst.columns <- src.columns;
5411 dst.beyecolumns <- src.beyecolumns;
5412 dst.selcmd <- src.selcmd;
5415 let get s =
5416 let h = Hashtbl.create 10 in
5417 let dc = { defconf with angle = defconf.angle } in
5418 let rec toplevel v t spos _ =
5419 match t with
5420 | Vdata | Vcdata | Vend -> v
5421 | Vopen ("llppconfig", _, closed) ->
5422 if closed
5423 then v
5424 else { v with f = llppconfig }
5425 | Vopen _ ->
5426 error "unexpected subelement at top level" s spos
5427 | Vclose _ -> error "unexpected close at top level" s spos
5429 and llppconfig v t spos _ =
5430 match t with
5431 | Vdata | Vcdata -> v
5432 | Vend -> error "unexpected end of input in llppconfig" s spos
5433 | Vopen ("defaults", attrs, closed) ->
5434 let c = config_of dc attrs in
5435 setconf dc c;
5436 if closed
5437 then v
5438 else { v with f = skip "defaults" (fun () -> v) }
5440 | Vopen ("ui-font", attrs, closed) ->
5441 let rec getsize size = function
5442 | [] -> size
5443 | ("size", v) :: rest ->
5444 let size =
5445 fromstring int_of_string spos "size" v fstate.fontsize in
5446 getsize size rest
5447 | l -> getsize size l
5449 fstate.fontsize <- getsize fstate.fontsize attrs;
5450 if closed
5451 then v
5452 else { v with f = uifont (Buffer.create 10) }
5454 | Vopen ("doc", attrs, closed) ->
5455 let pathent, spage, srely, span = doc_of attrs in
5456 let path = unent pathent
5457 and pageno = fromstring int_of_string spos "page" spage 0
5458 and rely = fromstring float_of_string spos "rely" srely 0.0
5459 and pan = fromstring int_of_string spos "pan" span 0 in
5460 let c = config_of dc attrs in
5461 let anchor = (pageno, rely) in
5462 if closed
5463 then (Hashtbl.add h path (c, [], pan, anchor); v)
5464 else { v with f = doc path pan anchor c [] }
5466 | Vopen _ ->
5467 error "unexpected subelement in llppconfig" s spos
5469 | Vclose "llppconfig" -> { v with f = toplevel }
5470 | Vclose _ -> error "unexpected close in llppconfig" s spos
5472 and uifont b v t spos epos =
5473 match t with
5474 | Vdata | Vcdata ->
5475 Buffer.add_substring b s spos (epos - spos);
5477 | Vopen (_, _, _) ->
5478 error "unexpected subelement in ui-font" s spos
5479 | Vclose "ui-font" ->
5480 if String.length !fontpath = 0
5481 then fontpath := Buffer.contents b;
5482 { v with f = llppconfig }
5483 | Vclose _ -> error "unexpected close in ui-font" s spos
5484 | Vend -> error "unexpected end of input in ui-font" s spos
5486 and doc path pan anchor c bookmarks v t spos _ =
5487 match t with
5488 | Vdata | Vcdata -> v
5489 | Vend -> error "unexpected end of input in doc" s spos
5490 | Vopen ("bookmarks", _, closed) ->
5491 if closed
5492 then v
5493 else { v with f = pbookmarks path pan anchor c bookmarks }
5495 | Vopen (_, _, _) ->
5496 error "unexpected subelement in doc" s spos
5498 | Vclose "doc" ->
5499 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5500 { v with f = llppconfig }
5502 | Vclose _ -> error "unexpected close in doc" s spos
5504 and pbookmarks path pan anchor c bookmarks v t spos _ =
5505 match t with
5506 | Vdata | Vcdata -> v
5507 | Vend -> error "unexpected end of input in bookmarks" s spos
5508 | Vopen ("item", attrs, closed) ->
5509 let titleent, spage, srely = bookmark_of attrs in
5510 let page = fromstring int_of_string spos "page" spage 0
5511 and rely = fromstring float_of_string spos "rely" srely 0.0 in
5512 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
5513 if closed
5514 then { v with f = pbookmarks path pan anchor c bookmarks }
5515 else
5516 let f () = v in
5517 { v with f = skip "item" f }
5519 | Vopen _ ->
5520 error "unexpected subelement in bookmarks" s spos
5522 | Vclose "bookmarks" ->
5523 { v with f = doc path pan anchor c bookmarks }
5525 | Vclose _ -> error "unexpected close in bookmarks" s spos
5527 and skip tag f v t spos _ =
5528 match t with
5529 | Vdata | Vcdata -> v
5530 | Vend ->
5531 error ("unexpected end of input in skipped " ^ tag) s spos
5532 | Vopen (tag', _, closed) ->
5533 if closed
5534 then v
5535 else
5536 let f' () = { v with f = skip tag f } in
5537 { v with f = skip tag' f' }
5538 | Vclose ctag ->
5539 if tag = ctag
5540 then f ()
5541 else error ("unexpected close in skipped " ^ tag) s spos
5544 parse { f = toplevel; accu = () } s;
5545 h, dc;
5548 let do_load f ic =
5550 let len = in_channel_length ic in
5551 let s = String.create len in
5552 really_input ic s 0 len;
5553 f s;
5554 with
5555 | Parse_error (msg, s, pos) ->
5556 let subs = subs s pos in
5557 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
5558 failwith ("parse error: " ^ s)
5560 | exn ->
5561 failwith ("config load error: " ^ Printexc.to_string exn)
5564 let defconfpath =
5565 let dir =
5567 let dir = Filename.concat home ".config" in
5568 if Sys.is_directory dir then dir else home
5569 with _ -> home
5571 Filename.concat dir "llpp.conf"
5574 let confpath = ref defconfpath;;
5576 let load1 f =
5577 if Sys.file_exists !confpath
5578 then
5579 match
5580 (try Some (open_in_bin !confpath)
5581 with exn ->
5582 prerr_endline
5583 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5584 Printexc.to_string exn);
5585 None
5587 with
5588 | Some ic ->
5589 begin try
5590 f (do_load get ic)
5591 with exn ->
5592 prerr_endline
5593 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5594 Printexc.to_string exn);
5595 end;
5596 close_in ic;
5598 | None -> ()
5599 else
5600 f (Hashtbl.create 0, defconf)
5603 let load () =
5604 let f (h, dc) =
5605 let pc, pb, px, pa =
5607 Hashtbl.find h (Filename.basename state.path)
5608 with Not_found -> dc, [], 0, (0, 0.0)
5610 setconf defconf dc;
5611 setconf conf pc;
5612 state.bookmarks <- pb;
5613 state.x <- px;
5614 state.scrollw <- conf.scrollbw;
5615 if conf.jumpback
5616 then state.anchor <- pa;
5617 cbput state.hists.nav pa;
5619 load1 f
5622 let add_attrs bb always dc c =
5623 let ob s a b =
5624 if always || a != b
5625 then Printf.bprintf bb "\n %s='%b'" s a
5626 and oi s a b =
5627 if always || a != b
5628 then Printf.bprintf bb "\n %s='%d'" s a
5629 and oI s a b =
5630 if always || a != b
5631 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5632 and oz s a b =
5633 if always || a <> b
5634 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5635 and oF s a b =
5636 if always || a <> b
5637 then Printf.bprintf bb "\n %s='%f'" s a
5638 and oc s a b =
5639 if always || a <> b
5640 then
5641 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5642 and oC s a b =
5643 if always || a <> b
5644 then
5645 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5646 and oR s a b =
5647 if always || a <> b
5648 then
5649 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5650 and os s a b =
5651 if always || a <> b
5652 then
5653 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5654 and og s a b =
5655 if always || a <> b
5656 then
5657 match a with
5658 | None -> ()
5659 | Some (_N, _A, _B) ->
5660 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
5661 and oW s a b =
5662 if always || a <> b
5663 then
5664 let v =
5665 match a with
5666 | None -> "false"
5667 | Some f ->
5668 if f = infinity
5669 then "true"
5670 else string_of_float f
5672 Printf.bprintf bb "\n %s='%s'" s v
5673 and oco s a b =
5674 if always || a <> b
5675 then
5676 match a with
5677 | Some ((n, a, b), _) when n > 1 ->
5678 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
5679 | _ -> ()
5680 and obeco s a b =
5681 if always || a <> b
5682 then
5683 match a with
5684 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
5685 | _ -> ()
5687 let w, h =
5688 if always
5689 then dc.winw, dc.winh
5690 else
5691 match state.fullscreen with
5692 | Some wh -> wh
5693 | None -> c.winw, c.winh
5695 let zoom, presentation, interpagespace, maxwait =
5696 if always
5697 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
5698 else
5699 match state.mode with
5700 | Birdseye (bc, _, _, _, _) ->
5701 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
5702 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
5704 oi "width" w dc.winw;
5705 oi "height" h dc.winh;
5706 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
5707 oi "scroll-handle-height" c.scrollh dc.scrollh;
5708 ob "case-insensitive-search" c.icase dc.icase;
5709 ob "preload" c.preload dc.preload;
5710 oi "page-bias" c.pagebias dc.pagebias;
5711 oi "scroll-step" c.scrollstep dc.scrollstep;
5712 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
5713 ob "max-height-fit" c.maxhfit dc.maxhfit;
5714 ob "crop-hack" c.crophack dc.crophack;
5715 oW "throttle" maxwait dc.maxwait;
5716 ob "highlight-links" c.hlinks dc.hlinks;
5717 ob "under-cursor-info" c.underinfo dc.underinfo;
5718 oi "vertical-margin" interpagespace dc.interpagespace;
5719 oz "zoom" zoom dc.zoom;
5720 ob "presentation" presentation dc.presentation;
5721 oi "rotation-angle" c.angle dc.angle;
5722 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
5723 ob "proportional-display" c.proportional dc.proportional;
5724 oI "pixmap-cache-size" c.memlimit dc.memlimit;
5725 oi "tex-count" c.texcount dc.texcount;
5726 oi "slice-height" c.sliceheight dc.sliceheight;
5727 oi "thumbnail-width" c.thumbw dc.thumbw;
5728 ob "persistent-location" c.jumpback dc.jumpback;
5729 oc "background-color" c.bgcolor dc.bgcolor;
5730 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
5731 oi "tile-width" c.tilew dc.tilew;
5732 oi "tile-height" c.tileh dc.tileh;
5733 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
5734 ob "checkers" c.checkers dc.checkers;
5735 oi "aalevel" c.aalevel dc.aalevel;
5736 ob "trim-margins" c.trimmargins dc.trimmargins;
5737 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
5738 os "uri-launcher" c.urilauncher dc.urilauncher;
5739 oC "color-space" c.colorspace dc.colorspace;
5740 ob "invert-colors" c.invert dc.invert;
5741 oF "brightness" c.colorscale dc.colorscale;
5742 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
5743 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
5744 oco "columns" c.columns dc.columns;
5745 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
5746 if always
5747 then ob "wmclass-hack" !wmclasshack false;
5748 os "selection-command" c.selcmd dc.selcmd;
5751 let save () =
5752 let uifontsize = fstate.fontsize in
5753 let bb = Buffer.create 32768 in
5754 let f (h, dc) =
5755 let dc = if conf.bedefault then conf else dc in
5756 Buffer.add_string bb "<llppconfig>\n";
5758 if String.length !fontpath > 0
5759 then
5760 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
5761 uifontsize
5762 !fontpath
5763 else (
5764 if uifontsize <> 14
5765 then
5766 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
5769 Buffer.add_string bb "<defaults ";
5770 add_attrs bb true dc dc;
5771 Buffer.add_string bb "/>\n";
5773 let adddoc path pan anchor c bookmarks =
5774 if bookmarks == [] && c = dc && anchor = emptyanchor
5775 then ()
5776 else (
5777 Printf.bprintf bb "<doc path='%s'"
5778 (enent path 0 (String.length path));
5780 if anchor <> emptyanchor
5781 then (
5782 let n, y = anchor in
5783 Printf.bprintf bb " page='%d'" n;
5784 if y > 1e-6
5785 then
5786 Printf.bprintf bb " rely='%f'" y
5790 if pan != 0
5791 then Printf.bprintf bb " pan='%d'" pan;
5793 add_attrs bb false dc c;
5795 begin match bookmarks with
5796 | [] -> Buffer.add_string bb "/>\n"
5797 | _ ->
5798 Buffer.add_string bb ">\n<bookmarks>\n";
5799 List.iter (fun (title, _level, (page, rely)) ->
5800 Printf.bprintf bb
5801 "<item title='%s' page='%d'"
5802 (enent title 0 (String.length title))
5803 page
5805 if rely > 1e-6
5806 then
5807 Printf.bprintf bb " rely='%f'" rely
5809 Buffer.add_string bb "/>\n";
5810 ) bookmarks;
5811 Buffer.add_string bb "</bookmarks>\n</doc>\n";
5812 end;
5816 let pan, conf =
5817 match state.mode with
5818 | Birdseye (c, pan, _, _, _) ->
5819 let beyecolumns =
5820 match conf.columns with
5821 | Some ((c, _, _), _) -> Some c
5822 | None -> None
5823 and columns =
5824 match c.columns with
5825 | Some (c, _) -> Some (c, [||])
5826 | None -> None
5828 pan, { c with beyecolumns = beyecolumns; columns = columns }
5829 | _ -> state.x, conf
5831 let basename = Filename.basename state.path in
5832 adddoc basename pan (getanchor ())
5833 { conf with
5834 autoscrollstep =
5835 match state.autoscroll with
5836 | Some step -> step
5837 | None -> conf.autoscrollstep }
5838 (if conf.savebmarks then state.bookmarks else []);
5840 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
5841 if basename <> path
5842 then adddoc path x y c bookmarks
5843 ) h;
5844 Buffer.add_string bb "</llppconfig>";
5846 load1 f;
5847 if Buffer.length bb > 0
5848 then
5850 let tmp = !confpath ^ ".tmp" in
5851 let oc = open_out_bin tmp in
5852 Buffer.output_buffer oc bb;
5853 close_out oc;
5854 Unix.rename tmp !confpath;
5855 with exn ->
5856 prerr_endline
5857 ("error while saving configuration: " ^ Printexc.to_string exn)
5859 end;;
5861 let () =
5862 Arg.parse
5863 (Arg.align
5864 [("-p", Arg.String (fun s -> state.password <- s) ,
5865 "<password> Set password");
5867 ("-f", Arg.String (fun s -> Config.fontpath := s),
5868 "<path> Set path to the user interface font");
5870 ("-c", Arg.String (fun s -> Config.confpath := s),
5871 "<path> Set path to the configuration file");
5873 ("-v", Arg.Unit (fun () ->
5874 Printf.printf
5875 "%s\nconfiguration path: %s\n"
5876 (version ())
5877 Config.defconfpath
5879 exit 0), " Print version and exit");
5882 (fun s -> state.path <- s)
5883 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
5885 if String.length state.path = 0
5886 then (prerr_endline "file name missing"; exit 1);
5888 Config.load ();
5890 let _ = Glut.init Sys.argv in
5891 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
5892 let () = Glut.initWindowSize conf.winw conf.winh in
5893 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
5895 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
5896 || Glut.extensionSupported "GL_EXT_texture_rectangle")
5897 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
5899 let () = Glut.displayFunc display in
5900 let () = Glut.reshapeFunc reshape in
5901 let () = Glut.keyboardFunc keyboard in
5902 let () = Glut.specialFunc special in
5903 let () = Glut.idleFunc (Some idle) in
5904 let () = Glut.mouseFunc mouse in
5905 let () = Glut.motionFunc motion in
5906 let () = Glut.passiveMotionFunc pmotion in
5908 let cr, sw = Unix.pipe ()
5909 and sr, cw = Unix.pipe () in
5911 setcheckers conf.checkers;
5912 init (cr, cw) (
5913 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5914 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5915 !Config.wmclasshack, !Config.fontpath
5917 state.sr <- sr;
5918 state.sw <- sw;
5919 state.text <- "Opening " ^ state.path;
5920 setaalevel conf.aalevel;
5921 writeopen state.path state.password;
5922 state.uioh <- uioh;
5923 setfontsize fstate.fontsize;
5925 redirectstderr ();
5927 while true do
5929 Glut.mainLoop ();
5930 with
5931 | Glut.BadEnum "key in special_of_int" ->
5932 showtext '!' " LablGlut bug: special key not recognized";
5934 | Quit ->
5935 wcmd "quit" [];
5936 Config.save ();
5937 exit 0
5939 | exn when conf.redirectstderr ->
5940 let s =
5941 Printf.sprintf "exception %s\n%s"
5942 (Printexc.to_string exn)
5943 (Printexc.get_backtrace ())
5945 ignore (try
5946 Unix.single_write state.stderr s 0 (String.length s);
5947 with _ -> 0);
5948 exit 1
5949 done;