Use ocaml 3.12 style warning specifiers
[llpp.git] / main.ml
blob818a129a704c82ed04824575ae4e3011fb06090b
1 type under =
2 | Unone
3 | Ulinkuri of string
4 | Ulinkgoto of (int * int)
5 | Utext of facename
6 and facename = string;;
8 let dolog fmt = Printf.kprintf prerr_endline fmt;;
9 let now = Unix.gettimeofday;;
11 exception Quit;;
13 type params = (angle * proportional * trimparams
14 * texcount * sliceheight * memsize
15 * colorspace * wmclasshack * fontpath)
16 and pageno = int
17 and width = int
18 and height = int
19 and leftx = int
20 and opaque = string
21 and recttype = int
22 and pixmapsize = int
23 and angle = int
24 and proportional = bool
25 and trimmargins = bool
26 and interpagespace = int
27 and texcount = int
28 and sliceheight = int
29 and gen = int
30 and top = float
31 and fontpath = string
32 and memsize = int
33 and aalevel = int
34 and wmclasshack = bool
35 and irect = (int * int * int * int)
36 and trimparams = (trimmargins * irect)
37 and colorspace = | Rgb | Bgr | Gray
40 type platform = | Punknown | Plinux | Pwindows | Posx | Psun
41 | Pfreebsd | Pdragonflybsd | Popenbsd | Pmingw | Pcygwin;;
43 external init : Unix.file_descr -> params -> unit = "ml_init";;
44 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
45 external copysel : string -> unit = "ml_copysel";;
46 external getpdimrect : int -> float array = "ml_getpdimrect";;
47 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
48 external zoomforh : int -> int -> int -> float = "ml_zoom_for_height";;
49 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
50 external measurestr : int -> string -> float = "ml_measure_string";;
51 external getmaxw : unit -> float = "ml_getmaxw";;
52 external postprocess : opaque -> bool -> int -> int -> unit = "ml_postprocess";;
53 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
54 external platform : unit -> platform = "ml_platform";;
55 external setaalevel : int -> unit = "ml_setaalevel";;
56 external realloctexts : int -> bool = "ml_realloctexts";;
58 let platform_to_string = function
59 | Punknown -> "unknown"
60 | Plinux -> "Linux"
61 | Pwindows -> "Windows"
62 | Posx -> "OSX"
63 | Psun -> "Sun"
64 | Pfreebsd -> "FreeBSD"
65 | Pdragonflybsd -> "DragonflyBSD"
66 | Popenbsd -> "OpenBSD"
67 | Pcygwin -> "Cygwin"
68 | Pmingw -> "MingW"
71 let platform = platform ();;
73 let is_windows =
74 match platform with
75 | Pwindows | Pmingw -> true
76 | _ -> false
79 type x = int
80 and y = int
81 and tilex = int
82 and tiley = int
83 and tileparams = (x * y * width * height * tilex * tiley)
86 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
88 type mpos = int * int
89 and mstate =
90 | Msel of (mpos * mpos)
91 | Mpan of mpos
92 | Mscrolly | Mscrollx
93 | Mzoom of (int * int)
94 | Mzoomrect of (mpos * mpos)
95 | Mnone
98 type textentry = string * string * onhist option * onkey * ondone
99 and onkey = string -> int -> te
100 and ondone = string -> unit
101 and histcancel = unit -> unit
102 and onhist = ((histcmd -> string) * histcancel)
103 and histcmd = HCnext | HCprev | HCfirst | HClast
104 and te =
105 | TEstop
106 | TEdone of string
107 | TEcont of string
108 | TEswitch of textentry
111 type 'a circbuf =
112 { store : 'a array
113 ; mutable rc : int
114 ; mutable wc : int
115 ; mutable len : int
119 let bound v minv maxv =
120 max minv (min maxv v);
123 let cbnew n v =
124 { store = Array.create n v
125 ; rc = 0
126 ; wc = 0
127 ; len = 0
131 let drawstring size x y s =
132 Gl.enable `blend;
133 Gl.enable `texture_2d;
134 ignore (drawstr size x y s);
135 Gl.disable `blend;
136 Gl.disable `texture_2d;
139 let drawstring1 size x y s =
140 drawstr size x y s;
143 let drawstring2 size x y fmt =
144 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
147 let cbcap b = Array.length b.store;;
149 let cbput b v =
150 let cap = cbcap b in
151 b.store.(b.wc) <- v;
152 b.wc <- (b.wc + 1) mod cap;
153 b.rc <- b.wc;
154 b.len <- min (b.len + 1) cap;
157 let cbempty b = b.len = 0;;
159 let cbgetg b circular dir =
160 if cbempty b
161 then b.store.(0)
162 else
163 let rc = b.rc + dir in
164 let rc =
165 if circular
166 then (
167 if rc = -1
168 then b.len-1
169 else (
170 if rc = b.len
171 then 0
172 else rc
175 else max 0 (min rc (b.len-1))
177 b.rc <- rc;
178 b.store.(rc);
181 let cbget b = cbgetg b false;;
182 let cbgetc b = cbgetg b true;;
184 type page =
185 { pageno : int
186 ; pagedimno : int
187 ; pagew : int
188 ; pageh : int
189 ; pagex : int
190 ; pagey : int
191 ; pagevw : int
192 ; pagevh : int
193 ; pagedispx : int
194 ; pagedispy : int
198 let debugl l =
199 dolog "l %d dim=%d {" l.pageno l.pagedimno;
200 dolog " WxH %dx%d" l.pagew l.pageh;
201 dolog " vWxH %dx%d" l.pagevw l.pagevh;
202 dolog " pagex,y %d,%d" l.pagex l.pagey;
203 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
204 dolog "}";
207 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
208 dolog "rect {";
209 dolog " x0,y0=(% f, % f)" x0 y0;
210 dolog " x1,y1=(% f, % f)" x1 y1;
211 dolog " x2,y2=(% f, % f)" x2 y2;
212 dolog " x3,y3=(% f, % f)" x3 y3;
213 dolog "}";
216 type conf =
217 { mutable scrollbw : int
218 ; mutable scrollh : int
219 ; mutable icase : bool
220 ; mutable preload : bool
221 ; mutable pagebias : int
222 ; mutable verbose : bool
223 ; mutable debug : bool
224 ; mutable scrollstep : int
225 ; mutable maxhfit : bool
226 ; mutable crophack : bool
227 ; mutable autoscrollstep : int
228 ; mutable maxwait : float option
229 ; mutable hlinks : bool
230 ; mutable underinfo : bool
231 ; mutable interpagespace : interpagespace
232 ; mutable zoom : float
233 ; mutable presentation : bool
234 ; mutable angle : angle
235 ; mutable winw : int
236 ; mutable winh : int
237 ; mutable savebmarks : bool
238 ; mutable proportional : proportional
239 ; mutable trimmargins : trimmargins
240 ; mutable trimfuzz : irect
241 ; mutable memlimit : memsize
242 ; mutable texcount : texcount
243 ; mutable sliceheight : sliceheight
244 ; mutable thumbw : width
245 ; mutable jumpback : bool
246 ; mutable bgcolor : float * float * float
247 ; mutable bedefault : bool
248 ; mutable scrollbarinpm : bool
249 ; mutable tilew : int
250 ; mutable tileh : int
251 ; mutable mumemlimit : memsize
252 ; mutable checkers : bool
253 ; mutable aalevel : int
254 ; mutable urilauncher : string
255 ; mutable colorspace : colorspace
256 ; mutable invert : bool
257 ; mutable colorscale : float
258 ; mutable redirectstderr : bool
262 type anchor = pageno * top;;
264 type outline = string * int * anchor;;
266 type rect = float * float * float * float * float * float * float * float;;
268 type tile = opaque * pixmapsize * elapsed
269 and elapsed = float;;
270 type pagemapkey = pageno * gen;;
271 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
272 and row = int
273 and col = int;;
275 let emptyanchor = (0, 0.0);;
277 type infochange = | Memused | Docinfo | Pdim;;
279 class type uioh = object
280 method display : unit
281 method key : int -> uioh
282 method special : Glut.special_key_t -> uioh
283 method button :
284 Glut.button_t -> Glut.mouse_button_state_t -> int -> int -> uioh
285 method motion : int -> int -> uioh
286 method pmotion : int -> int -> uioh
287 method infochanged : infochange -> unit
288 method scrollpw : (int * float * float)
289 method scrollph : (int * float * float)
290 end;;
292 type mode =
293 | Birdseye of (conf * leftx * pageno * pageno * anchor)
294 | Textentry of (textentry * onleave)
295 | View
296 and onleave = leavetextentrystatus -> unit
297 and leavetextentrystatus = | Cancel | Confirm
298 and helpitem = string * int * action
299 and action =
300 | Noaction
301 | Action of (uioh -> uioh)
304 let isbirdseye = function Birdseye _ -> true | _ -> false;;
305 let istextentry = function Textentry _ -> true | _ -> false;;
307 type currently =
308 | Idle
309 | Loading of (page * gen)
310 | Tiling of (
311 page * opaque * colorspace * angle * gen * col * row * width * height
313 | Outlining of outline list
316 let nouioh : uioh = object (self)
317 method display = ()
318 method key _ = self
319 method special _ = self
320 method button _ _ _ _ = self
321 method motion _ _ = self
322 method pmotion _ _ = self
323 method infochanged _ = ()
324 method scrollpw = (0, nan, nan)
325 method scrollph = (0, nan, nan)
326 end;;
328 type state =
329 { mutable csock : Unix.file_descr
330 ; mutable ssock : Unix.file_descr
331 ; mutable errfd : Unix.file_descr option
332 ; mutable stderr : Unix.file_descr
333 ; mutable errmsgs : Buffer.t
334 ; mutable newerrmsgs : bool
335 ; mutable w : int
336 ; mutable x : int
337 ; mutable y : int
338 ; mutable scrollw : int
339 ; mutable hscrollh : int
340 ; mutable anchor : anchor
341 ; mutable maxy : int
342 ; mutable layout : page list
343 ; pagemap : (pagemapkey, opaque) Hashtbl.t
344 ; tilemap : (tilemapkey, tile) Hashtbl.t
345 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
346 ; mutable pdims : (pageno * width * height * leftx) list
347 ; mutable pagecount : int
348 ; mutable currently : currently
349 ; mutable mstate : mstate
350 ; mutable searchpattern : string
351 ; mutable rects : (pageno * recttype * rect) list
352 ; mutable rects1 : (pageno * recttype * rect) list
353 ; mutable text : string
354 ; mutable fullscreen : (width * height) option
355 ; mutable mode : mode
356 ; mutable uioh : uioh
357 ; mutable outlines : outline array
358 ; mutable bookmarks : outline list
359 ; mutable path : string
360 ; mutable password : string
361 ; mutable invalidated : int
362 ; mutable memused : memsize
363 ; mutable gen : gen
364 ; mutable throttle : (page list * int * float) option
365 ; mutable autoscroll : int option
366 ; mutable help : helpitem array
367 ; mutable docinfo : (int * string) list
368 ; mutable deadline : float
369 ; mutable texid : GlTex.texture_id option
370 ; hists : hists
371 ; mutable prevzoom : float
372 ; mutable progress : float
374 and hists =
375 { pat : string circbuf
376 ; pag : string circbuf
377 ; nav : anchor circbuf
381 let defconf =
382 { scrollbw = 7
383 ; scrollh = 12
384 ; icase = true
385 ; preload = true
386 ; pagebias = 0
387 ; verbose = false
388 ; debug = false
389 ; scrollstep = 24
390 ; maxhfit = true
391 ; crophack = false
392 ; autoscrollstep = 2
393 ; maxwait = None
394 ; hlinks = false
395 ; underinfo = false
396 ; interpagespace = 2
397 ; zoom = 1.0
398 ; presentation = false
399 ; angle = 0
400 ; winw = 900
401 ; winh = 900
402 ; savebmarks = true
403 ; proportional = true
404 ; trimmargins = false
405 ; trimfuzz = (0,0,0,0)
406 ; memlimit = 32 lsl 20
407 ; texcount = 256
408 ; sliceheight = 24
409 ; thumbw = 76
410 ; jumpback = true
411 ; bgcolor = (0.5, 0.5, 0.5)
412 ; bedefault = false
413 ; scrollbarinpm = true
414 ; tilew = 2048
415 ; tileh = 2048
416 ; mumemlimit = 128 lsl 20
417 ; checkers = true
418 ; aalevel = 8
419 ; urilauncher =
420 (match platform with
421 | Plinux | Pfreebsd | Pdragonflybsd | Popenbsd | Psun -> "xdg-open \"%s\""
422 | Posx -> "open \"%s\""
423 | Pwindows | Pcygwin | Pmingw -> "iexplore \"%s\""
424 | _ -> "")
425 ; colorspace = Rgb
426 ; invert = false
427 ; colorscale = 1.0
428 ; redirectstderr = false
432 let conf = { defconf with angle = defconf.angle };;
434 type fontstate =
435 { mutable fontsize : int
436 ; mutable wwidth : float
437 ; mutable maxrows : int
441 let fstate =
442 { fontsize = 14
443 ; wwidth = nan
444 ; maxrows = -1
448 let setfontsize n =
449 fstate.fontsize <- n;
450 fstate.wwidth <- measurestr fstate.fontsize "w";
451 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
454 let gotouri uri =
455 if String.length conf.urilauncher = 0
456 then print_endline uri
457 else
458 let re = Str.regexp "%s" in
459 let command = Str.global_replace re uri conf.urilauncher in
460 let optic =
461 try Some (Unix.open_process_in command)
462 with exn ->
463 Printf.eprintf
464 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
465 flush stderr;
466 None
468 match optic with
469 | Some ic -> close_in ic
470 | None -> ()
473 let version () =
474 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
475 (platform_to_string platform) Sys.word_size Sys.ocaml_version
478 let makehelp () =
479 let strings = version () :: "" :: Help.keys in
480 Array.of_list (
481 let r = Str.regexp "\\(http://[^ ]+\\)" in
482 List.map (fun s ->
483 if (try Str.search_forward r s 0 with Not_found -> -1) >= 0
484 then
485 let uri = Str.matched_string s in
486 (s, 0, Action (fun u -> gotouri uri; u))
487 else s, 0, Noaction) strings
491 let state =
492 { csock = Unix.stdin
493 ; ssock = Unix.stdin
494 ; errfd = None
495 ; stderr = Unix.stderr
496 ; errmsgs = Buffer.create 0
497 ; newerrmsgs = false
498 ; x = 0
499 ; y = 0
500 ; w = 0
501 ; scrollw = 0
502 ; hscrollh = 0
503 ; anchor = emptyanchor
504 ; layout = []
505 ; maxy = max_int
506 ; tilelru = Queue.create ()
507 ; pagemap = Hashtbl.create 10
508 ; tilemap = Hashtbl.create 10
509 ; pdims = []
510 ; pagecount = 0
511 ; currently = Idle
512 ; mstate = Mnone
513 ; rects = []
514 ; rects1 = []
515 ; text = ""
516 ; mode = View
517 ; fullscreen = None
518 ; searchpattern = ""
519 ; outlines = [||]
520 ; bookmarks = []
521 ; path = ""
522 ; password = ""
523 ; invalidated = 0
524 ; hists =
525 { nav = cbnew 10 (0, 0.0)
526 ; pat = cbnew 1 ""
527 ; pag = cbnew 1 ""
529 ; memused = 0
530 ; gen = 0
531 ; throttle = None
532 ; autoscroll = None
533 ; help = makehelp ()
534 ; docinfo = []
535 ; deadline = nan
536 ; texid = None
537 ; prevzoom = 1.0
538 ; progress = -1.0
539 ; uioh = nouioh
543 let vlog fmt =
544 if conf.verbose
545 then
546 Printf.kprintf prerr_endline fmt
547 else
548 Printf.kprintf ignore fmt
551 let redirectstderr () =
552 if conf.redirectstderr
553 then
554 let rfd, wfd = Unix.pipe () in
555 state.stderr <- Unix.dup Unix.stderr;
556 state.errfd <- Some rfd;
557 Unix.dup2 wfd Unix.stderr;
558 else (
559 state.newerrmsgs <- false;
560 begin match state.errfd with
561 | Some fd ->
562 Unix.close fd;
563 Unix.dup2 state.stderr Unix.stderr;
564 state.errfd <- None;
565 | None -> ()
566 end;
567 prerr_string (Buffer.contents state.errmsgs);
568 flush stderr;
569 Buffer.clear state.errmsgs;
573 module G =
574 struct
575 let postRedisplay who =
576 if conf.verbose
577 then prerr_endline ("redisplay for " ^ who);
578 Glut.postRedisplay ();
580 end;;
582 let addchar s c =
583 let b = Buffer.create (String.length s + 1) in
584 Buffer.add_string b s;
585 Buffer.add_char b c;
586 Buffer.contents b;
589 let colorspace_of_string s =
590 match String.lowercase s with
591 | "rgb" -> Rgb
592 | "bgr" -> Bgr
593 | "gray" -> Gray
594 | _ -> failwith "invalid colorspace"
597 let int_of_colorspace = function
598 | Rgb -> 0
599 | Bgr -> 1
600 | Gray -> 2
603 let colorspace_of_int = function
604 | 0 -> Rgb
605 | 1 -> Bgr
606 | 2 -> Gray
607 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
610 let colorspace_to_string = function
611 | Rgb -> "rgb"
612 | Bgr -> "bgr"
613 | Gray -> "gray"
616 let intentry_with_suffix text key =
617 let c = Char.unsafe_chr key in
618 match Char.lowercase c with
619 | '0' .. '9' ->
620 let text = addchar text c in
621 TEcont text
623 | 'k' | 'm' | 'g' ->
624 let text = addchar text c in
625 TEcont text
627 | _ ->
628 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
629 TEcont text
632 let writecmd fd s =
633 let len = String.length s in
634 let n = 4 + len in
635 let b = Buffer.create n in
636 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
637 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
638 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
639 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
640 Buffer.add_string b s;
641 let s' = Buffer.contents b in
642 let n' = Unix.write fd s' 0 n in
643 if n' != n then failwith "write failed";
646 let readcmd fd =
647 let s = "xxxx" in
648 let n = Unix.read fd s 0 4 in
649 if n != 4 then failwith "incomplete read(len)";
650 let len = 0
651 lor (Char.code s.[0] lsl 24)
652 lor (Char.code s.[1] lsl 16)
653 lor (Char.code s.[2] lsl 8)
654 lor (Char.code s.[3] lsl 0)
656 let s = String.create len in
657 let n = Unix.read fd s 0 len in
658 if n != len then failwith "incomplete read(data)";
662 let makecmd s l =
663 let b = Buffer.create 10 in
664 Buffer.add_string b s;
665 let rec combine = function
666 | [] -> b
667 | x :: xs ->
668 Buffer.add_char b ' ';
669 let s =
670 match x with
671 | `b b -> if b then "1" else "0"
672 | `s s -> s
673 | `i i -> string_of_int i
674 | `f f -> string_of_float f
675 | `I f -> string_of_int (truncate f)
677 Buffer.add_string b s;
678 combine xs;
680 combine l;
683 let wcmd s l =
684 let cmd = Buffer.contents (makecmd s l) in
685 writecmd state.csock cmd;
688 let calcips h =
689 if conf.presentation
690 then
691 let d = conf.winh - h in
692 max 0 ((d + 1) / 2)
693 else
694 conf.interpagespace
697 let calcheight () =
698 let rec f pn ph pi fh l =
699 match l with
700 | (n, _, h, _) :: rest ->
701 let ips = calcips h in
702 let fh =
703 if conf.presentation
704 then fh+ips
705 else (
706 if isbirdseye state.mode && pn = 0
707 then fh + ips
708 else fh
711 let fh = fh + ((n - pn) * (ph + pi)) in
712 f n h ips fh rest;
714 | [] ->
715 let inc =
716 if conf.presentation || (isbirdseye state.mode && pn = 0)
717 then 0
718 else -pi
720 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
721 max 0 fh
723 let fh = f 0 0 0 0 state.pdims in
727 let getpageyh pageno =
728 let rec f pn ph pi y l =
729 match l with
730 | (n, _, h, _) :: rest ->
731 let ips = calcips h in
732 if n >= pageno
733 then
734 let h = if n = pageno then h else ph in
735 if conf.presentation && n = pageno
736 then
737 y + (pageno - pn) * (ph + pi) + pi, h
738 else
739 y + (pageno - pn) * (ph + pi), h
740 else
741 let y = y + (if conf.presentation then pi else 0) in
742 let y = y + (n - pn) * (ph + pi) in
743 f n h ips y rest
745 | [] ->
746 y + (pageno - pn) * (ph + pi), ph
748 f 0 0 0 0 state.pdims
751 let getpagedim pageno =
752 let rec f ppdim l =
753 match l with
754 | (n, _, _, _) as pdim :: rest ->
755 if n >= pageno
756 then (if n = pageno then pdim else ppdim)
757 else f pdim rest
759 | [] -> ppdim
761 f (-1, -1, -1, -1) state.pdims
764 let getpagey pageno = fst (getpageyh pageno);;
766 let layout y sh =
767 let sh = sh - state.hscrollh in
768 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
769 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
770 match pdims with
771 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
772 let ips = calcips h in
773 let yinc =
774 if conf.presentation || (isbirdseye state.mode && pageno = 0)
775 then ips
776 else 0
778 (w, h, ips, xoff), rest, pdimno + 1, yinc
779 | _ ->
780 prev, pdims, pdimno, 0
782 let dy = dy + yinc in
783 let py = py + yinc in
784 if pageno = state.pagecount || dy >= sh
785 then
786 accu
787 else
788 let vy = y + dy in
789 if py + h <= vy - yinc
790 then
791 let py = py + h + ips in
792 let dy = max 0 (py - y) in
793 f ~pageno:(pageno+1)
794 ~pdimno
795 ~prev:curr
798 ~pdims:rest
799 ~accu
800 else
801 let pagey = vy - py in
802 let pagevh = h - pagey in
803 let pagevh = min (sh - dy) pagevh in
804 let off = if yinc > 0 then py - vy else 0 in
805 let py = py + h + ips in
806 let pagex, dx =
807 let xoff = xoff +
808 if state.w < conf.winw - state.scrollw
809 then (conf.winw - state.scrollw - state.w) / 2
810 else 0
812 let dispx = xoff + state.x in
813 if dispx < 0
814 then (-dispx, 0)
815 else (0, dispx)
817 let pagevw =
818 let lw = w - pagex in
819 min lw (conf.winw - state.scrollw)
821 let e =
822 { pageno = pageno
823 ; pagedimno = pdimno
824 ; pagew = w
825 ; pageh = h
826 ; pagex = pagex
827 ; pagey = pagey + off
828 ; pagevw = pagevw
829 ; pagevh = pagevh - off
830 ; pagedispx = dx
831 ; pagedispy = dy + off
834 let accu = e :: accu in
835 f ~pageno:(pageno+1)
836 ~pdimno
837 ~prev:curr
839 ~dy:(dy+pagevh+ips)
840 ~pdims:rest
841 ~accu
843 if state.invalidated = 0
844 then (
845 let accu =
847 ~pageno:0
848 ~pdimno:~-1
849 ~prev:(0,0,0,0)
850 ~py:0
851 ~dy:0
852 ~pdims:state.pdims
853 ~accu:[]
855 List.rev accu
857 else
861 let clamp incr =
862 let y = state.y + incr in
863 let y = max 0 y in
864 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
868 let getopaque pageno =
869 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
870 with Not_found -> None
873 let putopaque pageno opaque =
874 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
877 let itertiles l f =
878 let tilex = l.pagex mod conf.tilew in
879 let tiley = l.pagey mod conf.tileh in
881 let col = l.pagex / conf.tilew in
882 let row = l.pagey / conf.tileh in
884 let vw =
885 let a = l.pagew - l.pagex in
886 let b = conf.winw - state.scrollw in
887 min a b
888 and vh = l.pagevh in
890 let rec rowloop row y0 dispy h =
891 if h = 0
892 then ()
893 else (
894 let dh = conf.tileh - y0 in
895 let dh = min h dh in
896 let rec colloop col x0 dispx w =
897 if w = 0
898 then ()
899 else (
900 let dw = conf.tilew - x0 in
901 let dw = min w dw in
903 f col row dispx dispy x0 y0 dw dh;
904 colloop (col+1) 0 (dispx+dw) (w-dw)
907 colloop col tilex l.pagedispx vw;
908 rowloop (row+1) 0 (dispy+dh) (h-dh)
911 if vw > 0 && vh > 0
912 then rowloop row tiley l.pagedispy vh;
915 let gettileopaque l col row =
916 let key =
917 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
919 try Some (Hashtbl.find state.tilemap key)
920 with Not_found -> None
923 let puttileopaque l col row gen colorspace angle opaque size elapsed =
924 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
925 Hashtbl.add state.tilemap key (opaque, size, elapsed)
928 let drawtiles l color =
929 GlDraw.color color;
930 let f col row x y tilex tiley w h =
931 match gettileopaque l col row with
932 | Some (opaque, _, t) ->
933 let params = x, y, w, h, tilex, tiley in
934 if conf.invert
935 then (
936 Gl.enable `blend;
937 GlFunc.blend_func `zero `one_minus_src_color;
939 drawtile params opaque;
940 if conf.invert
941 then Gl.disable `blend;
942 if conf.debug
943 then (
944 let s = Printf.sprintf
945 "%d[%d,%d] %f sec"
946 l.pageno col row t
948 let w = measurestr fstate.fontsize s in
949 GlMisc.push_attrib [`current];
950 GlDraw.color (0.0, 0.0, 0.0);
951 GlDraw.rect
952 (float (x-2), float (y-2))
953 (float (x+2) +. w, float (y + fstate.fontsize + 2));
954 GlDraw.color (1.0, 1.0, 1.0);
955 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
956 GlMisc.pop_attrib ();
959 | _ ->
960 let w =
961 let lw = conf.winw - state.scrollw - x in
962 min lw w
963 and h =
964 let lh = conf.winh - y in
965 min lh h
967 Gl.enable `texture_2d;
968 begin match state.texid with
969 | Some id ->
970 GlTex.bind_texture `texture_2d id;
971 let x0 = float x
972 and y0 = float y
973 and x1 = float (x+w)
974 and y1 = float (y+h) in
976 let tw = float w /. 64.0
977 and th = float h /. 64.0 in
978 let tx0 = float tilex /. 64.0
979 and ty0 = float tiley /. 64.0 in
980 let tx1 = tx0 +. tw
981 and ty1 = ty0 +. th in
982 GlDraw.begins `quads;
983 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
984 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
985 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
986 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
987 GlDraw.ends ();
989 Gl.disable `texture_2d;
990 | None ->
991 GlDraw.color (1.0, 1.0, 1.0);
992 GlDraw.rect
993 (float x, float y)
994 (float (x+w), float (y+h));
995 end;
996 if w > 128 && h > fstate.fontsize + 10
997 then (
998 GlDraw.color (0.0, 0.0, 0.0);
999 let c, r =
1000 if conf.verbose
1001 then (col*conf.tilew, row*conf.tileh)
1002 else col, row
1004 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1006 GlDraw.color color;
1008 itertiles l f
1011 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1013 let tilevisible1 l x y =
1014 let ax0 = l.pagex
1015 and ax1 = l.pagex + l.pagevw
1016 and ay0 = l.pagey
1017 and ay1 = l.pagey + l.pagevh in
1019 let bx0 = x
1020 and by0 = y in
1021 let bx1 = min (bx0 + conf.tilew) l.pagew
1022 and by1 = min (by0 + conf.tileh) l.pageh in
1024 let rx0 = max ax0 bx0
1025 and ry0 = max ay0 by0
1026 and rx1 = min ax1 bx1
1027 and ry1 = min ay1 by1 in
1029 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1030 nonemptyintersection
1033 let tilevisible layout n x y =
1034 let rec findpageinlayout = function
1035 | l :: _ when l.pageno = n -> tilevisible1 l x y
1036 | _ :: rest -> findpageinlayout rest
1037 | [] -> false
1039 findpageinlayout layout
1042 let tileready l x y =
1043 tilevisible1 l x y &&
1044 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1047 let tilepage n p layout =
1048 let rec loop = function
1049 | l :: rest ->
1050 if l.pageno = n
1051 then
1052 let f col row _ _ _ _ _ _ =
1053 if state.currently = Idle
1054 then
1055 match gettileopaque l col row with
1056 | Some _ -> ()
1057 | None ->
1058 let x = col*conf.tilew
1059 and y = row*conf.tileh in
1060 let w =
1061 let w = l.pagew - x in
1062 min w conf.tilew
1064 let h =
1065 let h = l.pageh - y in
1066 min h conf.tileh
1068 wcmd "tile"
1069 [`s p
1070 ;`i x
1071 ;`i y
1072 ;`i w
1073 ;`i h
1075 state.currently <-
1076 Tiling (
1077 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1078 conf.tilew, conf.tileh
1081 itertiles l f;
1082 else
1083 loop rest
1085 | [] -> ()
1087 if state.invalidated = 0 then loop layout;
1090 let preloadlayout visiblepages =
1091 let presentation = conf.presentation in
1092 let interpagespace = conf.interpagespace in
1093 let maxy = state.maxy in
1094 conf.presentation <- false;
1095 conf.interpagespace <- 0;
1096 state.maxy <- calcheight ();
1097 let y =
1098 match visiblepages with
1099 | [] -> 0
1100 | l :: _ -> getpagey l.pageno + l.pagey
1102 let y = if y < conf.winh then 0 else y - conf.winh in
1103 let h = state.y - y + conf.winh*3 in
1104 let pages = layout y h in
1105 conf.presentation <- presentation;
1106 conf.interpagespace <- interpagespace;
1107 state.maxy <- maxy;
1108 pages;
1111 let load pages =
1112 let rec loop pages =
1113 if state.currently != Idle
1114 then ()
1115 else
1116 match pages with
1117 | l :: rest ->
1118 begin match getopaque l.pageno with
1119 | None ->
1120 wcmd "page" [`i l.pageno; `i l.pagedimno];
1121 state.currently <- Loading (l, state.gen);
1122 | Some opaque ->
1123 tilepage l.pageno opaque pages;
1124 loop rest
1125 end;
1126 | _ -> ()
1128 if state.invalidated = 0 then loop pages
1131 let preload pages =
1132 load pages;
1133 if conf.preload && state.currently = Idle
1134 then load (preloadlayout pages);
1137 let layoutready layout =
1138 let rec fold all ls =
1139 all && match ls with
1140 | l :: rest ->
1141 let seen = ref false in
1142 let allvisible = ref true in
1143 let foo col row _ _ _ _ _ _ =
1144 seen := true;
1145 allvisible := !allvisible &&
1146 begin match gettileopaque l col row with
1147 | Some _ -> true
1148 | None -> false
1151 itertiles l foo;
1152 fold (!seen && !allvisible) rest
1153 | [] -> true
1155 let alltilesvisible = fold true layout in
1156 alltilesvisible;
1159 let gotoy y =
1160 let y = bound y 0 state.maxy in
1161 let y, layout, proceed =
1162 match conf.maxwait with
1163 | Some time ->
1164 begin match state.throttle with
1165 | None ->
1166 let layout = layout y conf.winh in
1167 let ready = layoutready layout in
1168 if not ready
1169 then (
1170 load layout;
1171 state.throttle <- Some (layout, y, now ());
1173 else G.postRedisplay "gotoy showall (None)";
1174 y, layout, ready
1175 | Some (_, _, started) ->
1176 let dt = now () -. started in
1177 if dt > time
1178 then (
1179 state.throttle <- None;
1180 let layout = layout y conf.winh in
1181 load layout;
1182 G.postRedisplay "maxwait";
1183 y, layout, true
1185 else -1, [], false
1188 | None ->
1189 let layout = layout y conf.winh in
1190 if true || layoutready layout
1191 then G.postRedisplay "gotoy ready";
1192 y, layout, true
1194 if proceed
1195 then (
1196 state.y <- y;
1197 state.layout <- layout;
1198 begin match state.mode with
1199 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1200 if not (pagevisible layout pageno)
1201 then (
1202 match state.layout with
1203 | [] -> ()
1204 | l :: _ ->
1205 state.mode <- Birdseye (
1206 conf, leftx, l.pageno, hooverpageno, anchor
1209 | _ -> ()
1210 end;
1211 preload layout;
1215 let conttiling pageno opaque =
1216 tilepage pageno opaque
1217 (if conf.preload then preloadlayout state.layout else state.layout)
1220 let gotoy_and_clear_text y =
1221 gotoy y;
1222 if not conf.verbose then state.text <- "";
1225 let getanchor () =
1226 match state.layout with
1227 | [] -> emptyanchor
1228 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1231 let getanchory (n, top) =
1232 let y, h = getpageyh n in
1233 y + (truncate (top *. float h));
1236 let gotoanchor anchor =
1237 gotoy (getanchory anchor);
1240 let addnav () =
1241 cbput state.hists.nav (getanchor ());
1244 let getnav dir =
1245 let anchor = cbgetc state.hists.nav dir in
1246 getanchory anchor;
1249 let gotopage n top =
1250 let y, h = getpageyh n in
1251 gotoy_and_clear_text (y + (truncate (top *. float h)));
1254 let gotopage1 n top =
1255 let y = getpagey n in
1256 gotoy_and_clear_text (y + top);
1259 let invalidate () =
1260 state.layout <- [];
1261 state.pdims <- [];
1262 state.rects <- [];
1263 state.rects1 <- [];
1264 state.invalidated <- state.invalidated + 1;
1267 let writeopen path password =
1268 writecmd state.csock ("open " ^ path ^ "\000" ^ password ^ "\000");
1271 let opendoc path password =
1272 invalidate ();
1273 state.path <- path;
1274 state.password <- password;
1275 state.gen <- state.gen + 1;
1276 state.docinfo <- [];
1278 setaalevel conf.aalevel;
1279 writeopen path password;
1280 Glut.setWindowTitle ("llpp " ^ Filename.basename path);
1281 wcmd "geometry" [`i state.w; `i conf.winh];
1284 let scalecolor c =
1285 let c = c *. conf.colorscale in
1286 (c, c, c);
1289 let scalecolor2 (r, g, b) =
1290 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1293 let represent () =
1294 state.maxy <- calcheight ();
1295 state.hscrollh <-
1296 if state.w <= conf.winw - state.scrollw
1297 then 0
1298 else state.scrollw
1300 match state.mode with
1301 | Birdseye (_, _, pageno, _, _) ->
1302 let y, h = getpageyh pageno in
1303 let top = (conf.winh - h) / 2 in
1304 gotoy (max 0 (y - top))
1305 | _ -> gotoanchor state.anchor
1308 let reshape =
1309 let firsttime = ref true in
1310 fun ~w ~h ->
1311 GlDraw.viewport 0 0 w h;
1312 if state.invalidated = 0 && not !firsttime
1313 then state.anchor <- getanchor ();
1315 firsttime := false;
1316 conf.winw <- w;
1317 let w = truncate (float w *. conf.zoom) - state.scrollw in
1318 let w = max w 2 in
1319 state.w <- w;
1320 conf.winh <- h;
1321 setfontsize fstate.fontsize;
1322 GlMat.mode `modelview;
1323 GlMat.load_identity ();
1325 GlMat.mode `projection;
1326 GlMat.load_identity ();
1327 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1328 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1329 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1331 invalidate ();
1332 wcmd "geometry" [`i w; `i h];
1335 let enttext () =
1336 let len = String.length state.text in
1337 let drawstring s =
1338 let hscrollh =
1339 match state.mode with
1340 | View -> state.hscrollh
1341 | _ -> 0
1343 let rect x w =
1344 GlDraw.rect
1345 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1346 (x+.w, float (conf.winh - hscrollh))
1349 let w = float (conf.winw - state.scrollw - 1) in
1350 if state.progress >= 0.0 && state.progress < 1.0
1351 then (
1352 GlDraw.color (0.3, 0.3, 0.3);
1353 let w1 = w *. state.progress in
1354 rect 0.0 w1;
1355 GlDraw.color (0.0, 0.0, 0.0);
1356 rect w1 (w-.w1)
1358 else (
1359 GlDraw.color (0.0, 0.0, 0.0);
1360 rect 0.0 w;
1363 GlDraw.color (1.0, 1.0, 1.0);
1364 drawstring fstate.fontsize
1365 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1367 let s =
1368 match state.mode with
1369 | Textentry ((prefix, text, _, _, _), _) ->
1370 let s =
1371 if len > 0
1372 then
1373 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1374 else
1375 Printf.sprintf "%s%s_" prefix text
1379 | _ -> state.text
1381 let s =
1382 if state.newerrmsgs
1383 then (
1384 if not (istextentry state.mode)
1385 then
1386 let s1 = "(press 'e' to review error messasges)" in
1387 if String.length s > 0 then s ^ " " ^ s1 else s1
1388 else s
1390 else s
1392 if String.length s > 0
1393 then drawstring s
1396 let showtext c s =
1397 state.text <- Printf.sprintf "%c%s" c s;
1398 G.postRedisplay "showtext";
1401 let gctiles () =
1402 let len = Queue.length state.tilelru in
1403 let rec loop qpos =
1404 if state.memused <= conf.memlimit
1405 then ()
1406 else (
1407 if qpos < len
1408 then
1409 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1410 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1411 let (_, pw, ph, _) = getpagedim n in
1413 gen = state.gen
1414 && colorspace = conf.colorspace
1415 && angle = conf.angle
1416 && pagew = pw
1417 && pageh = ph
1418 && (
1419 let layout =
1420 match state.throttle with
1421 | None ->
1422 if conf.preload
1423 then preloadlayout state.layout
1424 else state.layout
1425 | Some (layout, _, _) ->
1426 layout
1428 let x = col*conf.tilew
1429 and y = row*conf.tileh in
1430 tilevisible layout n x y
1432 then Queue.push lruitem state.tilelru
1433 else (
1434 wcmd "freetile" [`s p];
1435 state.memused <- state.memused - s;
1436 state.uioh#infochanged Memused;
1437 Hashtbl.remove state.tilemap k;
1439 loop (qpos+1)
1442 loop 0
1445 let flushtiles () =
1446 Queue.iter (fun (k, p, s) ->
1447 wcmd "freetile" [`s p];
1448 state.memused <- state.memused - s;
1449 state.uioh#infochanged Memused;
1450 Hashtbl.remove state.tilemap k;
1451 ) state.tilelru;
1452 Queue.clear state.tilelru;
1453 load state.layout;
1456 let logcurrently = function
1457 | Idle -> dolog "Idle"
1458 | Loading (l, gen) ->
1459 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1460 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1461 dolog
1462 "Tiling %d[%d,%d] page=%s cs=%s angle"
1463 l.pageno col row pageopaque
1464 (colorspace_to_string colorspace)
1466 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1467 angle gen conf.angle state.gen
1468 tilew tileh
1469 conf.tilew conf.tileh
1471 | Outlining _ ->
1472 dolog "outlining"
1475 let act cmds =
1476 (* dolog "%S" cmds; *)
1477 let op, args =
1478 let spacepos =
1479 try String.index cmds ' '
1480 with Not_found -> -1
1482 if spacepos = -1
1483 then cmds, ""
1484 else
1485 let l = String.length cmds in
1486 let op = String.sub cmds 0 spacepos in
1487 op, begin
1488 if l - spacepos < 2 then ""
1489 else String.sub cmds (spacepos+1) (l-spacepos-1)
1492 match op with
1493 | "clear" ->
1494 state.uioh#infochanged Pdim;
1495 state.pdims <- [];
1497 | "clearrects" ->
1498 state.rects <- state.rects1;
1499 G.postRedisplay "clearrects";
1501 | "continue" ->
1502 let n =
1503 try Scanf.sscanf args "%u" (fun n -> n)
1504 with exn ->
1505 dolog "error processing 'continue' %S: %s"
1506 cmds (Printexc.to_string exn);
1507 exit 1;
1509 state.pagecount <- n;
1510 state.invalidated <- state.invalidated - 1;
1511 begin match state.currently with
1512 | Outlining l ->
1513 state.currently <- Idle;
1514 state.outlines <- Array.of_list (List.rev l)
1515 | _ -> ()
1516 end;
1517 if state.invalidated = 0
1518 then represent ();
1519 if conf.maxwait = None
1520 then G.postRedisplay "continue";
1522 | "title" ->
1523 Glut.setWindowTitle args
1525 | "msg" ->
1526 showtext ' ' args
1528 | "vmsg" ->
1529 if conf.verbose
1530 then showtext ' ' args
1532 | "progress" ->
1533 let progress, text =
1535 Scanf.sscanf args "%f %n"
1536 (fun f pos ->
1537 f, String.sub args pos (String.length args - pos))
1538 with exn ->
1539 dolog "error processing 'progress' %S: %s"
1540 cmds (Printexc.to_string exn);
1541 exit 1;
1543 state.text <- text;
1544 state.progress <- progress;
1545 G.postRedisplay "progress"
1547 | "firstmatch" ->
1548 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1550 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1551 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1552 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1553 with exn ->
1554 dolog "error processing 'firstmatch' %S: %s"
1555 cmds (Printexc.to_string exn);
1556 exit 1;
1558 let y = (getpagey pageno) + truncate y0 in
1559 addnav ();
1560 gotoy y;
1561 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1563 | "match" ->
1564 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1566 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1567 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1568 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1569 with exn ->
1570 dolog "error processing 'match' %S: %s"
1571 cmds (Printexc.to_string exn);
1572 exit 1;
1574 state.rects1 <-
1575 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1577 | "page" ->
1578 let pageopaque, t =
1580 Scanf.sscanf args "%s %f" (fun p t -> p, t)
1581 with exn ->
1582 dolog "error processing 'page' %S: %s"
1583 cmds (Printexc.to_string exn);
1584 exit 1;
1586 begin match state.currently with
1587 | Loading (l, gen) ->
1588 vlog "page %d took %f sec" l.pageno t;
1589 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1590 begin match state.throttle with
1591 | None ->
1592 let preloadedpages =
1593 if conf.preload
1594 then preloadlayout state.layout
1595 else state.layout
1597 let evict () =
1598 let module IntSet =
1599 Set.Make (struct type t = int let compare = (-) end) in
1600 let set =
1601 List.fold_left (fun s l -> IntSet.add l.pageno s)
1602 IntSet.empty preloadedpages
1604 let evictedpages =
1605 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1606 if not (IntSet.mem pageno set)
1607 then (
1608 wcmd "freepage" [`s opaque];
1609 key :: accu
1611 else accu
1612 ) state.pagemap []
1614 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1616 evict ();
1617 state.currently <- Idle;
1618 if gen = state.gen
1619 then (
1620 tilepage l.pageno pageopaque state.layout;
1621 load state.layout;
1622 load preloadedpages;
1623 if pagevisible state.layout l.pageno
1624 && layoutready state.layout
1625 then G.postRedisplay "page";
1628 | Some (layout, _, _) ->
1629 state.currently <- Idle;
1630 tilepage l.pageno pageopaque layout;
1631 load state.layout
1632 end;
1634 | _ ->
1635 dolog "Inconsistent loading state";
1636 logcurrently state.currently;
1637 raise Quit;
1640 | "tile" ->
1641 let (x, y, opaque, size, t) =
1643 Scanf.sscanf args "%u %u %s %u %f"
1644 (fun x y p size t -> (x, y, p, size, t))
1645 with exn ->
1646 dolog "error processing 'tile' %S: %s"
1647 cmds (Printexc.to_string exn);
1648 exit 1;
1650 begin match state.currently with
1651 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1652 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1654 if tilew != conf.tilew || tileh != conf.tileh
1655 then (
1656 wcmd "freetile" [`s opaque];
1657 state.currently <- Idle;
1658 load state.layout;
1660 else (
1661 puttileopaque l col row gen cs angle opaque size t;
1662 state.memused <- state.memused + size;
1663 state.uioh#infochanged Memused;
1664 gctiles ();
1665 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1666 opaque, size) state.tilelru;
1668 let layout =
1669 match state.throttle with
1670 | None -> state.layout
1671 | Some (layout, _, _) -> layout
1674 state.currently <- Idle;
1675 if gen = state.gen
1676 && conf.colorspace = cs
1677 && conf.angle = angle
1678 && tilevisible layout l.pageno x y
1679 then conttiling l.pageno pageopaque;
1681 begin match state.throttle with
1682 | None ->
1683 preload state.layout;
1684 if gen = state.gen
1685 && conf.colorspace = cs
1686 && conf.angle = angle
1687 && tilevisible state.layout l.pageno x y
1688 then G.postRedisplay "tile nothrottle";
1690 | Some (layout, y, _) ->
1691 let ready = layoutready layout in
1692 if ready
1693 then (
1694 state.y <- y;
1695 state.layout <- layout;
1696 state.throttle <- None;
1697 G.postRedisplay "throttle";
1699 else load layout;
1700 end;
1703 | _ ->
1704 dolog "Inconsistent tiling state";
1705 logcurrently state.currently;
1706 raise Quit;
1709 | "pdim" ->
1710 let pdim =
1712 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1713 with exn ->
1714 dolog "error processing 'pdim' %S: %s"
1715 cmds (Printexc.to_string exn);
1716 exit 1;
1718 state.uioh#infochanged Pdim;
1719 state.pdims <- pdim :: state.pdims
1721 | "o" ->
1722 let (l, n, t, h, pos) =
1724 Scanf.sscanf args "%u %u %d %u %n"
1725 (fun l n t h pos -> l, n, t, h, pos)
1726 with exn ->
1727 dolog "error processing 'o' %S: %s"
1728 cmds (Printexc.to_string exn);
1729 exit 1;
1731 let s = String.sub args pos (String.length args - pos) in
1732 let outline = (s, l, (n, float t /. float h)) in
1733 begin match state.currently with
1734 | Outlining outlines ->
1735 state.currently <- Outlining (outline :: outlines)
1736 | Idle ->
1737 state.currently <- Outlining [outline]
1738 | currently ->
1739 dolog "invalid outlining state";
1740 logcurrently currently
1743 | "info" ->
1744 state.docinfo <- (1, args) :: state.docinfo
1746 | "infoend" ->
1747 state.uioh#infochanged Docinfo;
1748 state.docinfo <- List.rev state.docinfo
1750 | _ ->
1751 dolog "unknown cmd `%S'" cmds
1754 let idle () =
1755 if state.deadline == nan then state.deadline <- now ();
1756 let r =
1757 match state.errfd with
1758 | None -> [state.csock]
1759 | Some fd -> [state.csock; fd]
1761 let rec loop delay =
1762 let timeout =
1763 if delay > 0.0
1764 then max 0.0 (state.deadline -. now ())
1765 else 0.0
1767 let r, _, _ = Unix.select r [] [] timeout in
1768 begin match r with
1769 | [] ->
1770 begin match state.autoscroll with
1771 | Some step when step != 0 ->
1772 let y = state.y + step in
1773 let y =
1774 if y < 0
1775 then state.maxy
1776 else if y >= state.maxy then 0 else y
1778 gotoy y;
1779 if state.mode = View
1780 then state.text <- "";
1781 state.deadline <- state.deadline +. 0.005;
1783 | _ ->
1784 state.deadline <- state.deadline +. delay;
1785 end;
1787 | l ->
1788 let rec checkfds c = function
1789 | [] -> c
1790 | fd :: rest when fd = state.csock ->
1791 let cmd = readcmd state.csock in
1792 act cmd;
1793 checkfds true rest
1794 | fd :: rest ->
1795 let s = String.create 80 in
1796 let n = Unix.read fd s 0 80 in
1797 if conf.redirectstderr
1798 then (
1799 Buffer.add_substring state.errmsgs s 0 n;
1800 state.newerrmsgs <- true;
1801 Glut.postRedisplay ();
1803 else (
1804 prerr_string (String.sub s 0 n);
1805 flush stderr;
1807 checkfds c rest
1809 if checkfds false l
1810 then loop 0.0
1811 end;
1812 in loop 0.007
1815 let onhist cb =
1816 let rc = cb.rc in
1817 let action = function
1818 | HCprev -> cbget cb ~-1
1819 | HCnext -> cbget cb 1
1820 | HCfirst -> cbget cb ~-(cb.rc)
1821 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1822 and cancel () = cb.rc <- rc
1823 in (action, cancel)
1826 let search pattern forward =
1827 if String.length pattern > 0
1828 then
1829 let pn, py =
1830 match state.layout with
1831 | [] -> 0, 0
1832 | l :: _ ->
1833 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1835 let cmd =
1836 let b = makecmd "search"
1837 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
1839 Buffer.add_char b ',';
1840 Buffer.add_string b pattern;
1841 Buffer.add_char b '\000';
1842 Buffer.contents b;
1844 writecmd state.csock cmd;
1847 let intentry text key =
1848 let c = Char.unsafe_chr key in
1849 match c with
1850 | '0' .. '9' ->
1851 let text = addchar text c in
1852 TEcont text
1854 | _ ->
1855 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
1856 TEcont text
1859 let textentry text key =
1860 let c = Char.unsafe_chr key in
1861 match c with
1862 | _ when key >= 32 && key < 127 ->
1863 let text = addchar text c in
1864 TEcont text
1866 | _ ->
1867 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
1868 TEcont text
1871 let reqlayout angle proportional =
1872 match state.throttle with
1873 | None ->
1874 if state.invalidated = 0 then state.anchor <- getanchor ();
1875 conf.angle <- angle mod 360;
1876 conf.proportional <- proportional;
1877 invalidate ();
1878 wcmd "reqlayout" [`i conf.angle; `b proportional];
1879 | _ -> ()
1882 let settrim trimmargins trimfuzz =
1883 if state.invalidated = 0 then state.anchor <- getanchor ();
1884 conf.trimmargins <- trimmargins;
1885 conf.trimfuzz <- trimfuzz;
1886 let x0, y0, x1, y1 = trimfuzz in
1887 invalidate ();
1888 wcmd "settrim" [
1889 `b conf.trimmargins;
1890 `i x0;
1891 `i y0;
1892 `i x1;
1893 `i y1;
1895 Hashtbl.iter (fun _ opaque ->
1896 wcmd "freepage" [`s opaque];
1897 ) state.pagemap;
1898 Hashtbl.clear state.pagemap;
1901 let setzoom zoom =
1902 match state.throttle with
1903 | None ->
1904 let zoom = max 0.01 zoom in
1905 if zoom <> conf.zoom
1906 then (
1907 state.prevzoom <- conf.zoom;
1908 let relx =
1909 if zoom <= 1.0
1910 then (state.x <- 0; 0.0)
1911 else float state.x /. float state.w
1913 conf.zoom <- zoom;
1914 reshape conf.winw conf.winh;
1915 if zoom > 1.0
1916 then (
1917 let x = relx *. float state.w in
1918 state.x <- truncate x;
1920 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
1923 | Some (layout, y, started) ->
1924 let time =
1925 match conf.maxwait with
1926 | None -> 0.0
1927 | Some t -> t
1929 let dt = now () -. started in
1930 if dt > time
1931 then (
1932 state.y <- y;
1933 load layout;
1937 let enterbirdseye () =
1938 let zoom = float conf.thumbw /. float conf.winw in
1939 let birdseyepageno =
1940 let cy = conf.winh / 2 in
1941 let fold = function
1942 | [] -> 0
1943 | l :: rest ->
1944 let rec fold best = function
1945 | [] -> best.pageno
1946 | l :: rest ->
1947 let d = cy - (l.pagedispy + l.pagevh/2)
1948 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1949 if abs d < abs dbest
1950 then fold l rest
1951 else best.pageno
1952 in fold l rest
1954 fold state.layout
1956 state.mode <- Birdseye (
1957 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
1959 conf.zoom <- zoom;
1960 conf.presentation <- false;
1961 conf.interpagespace <- 10;
1962 conf.hlinks <- false;
1963 state.x <- 0;
1964 state.mstate <- Mnone;
1965 conf.maxwait <- None;
1966 Glut.setCursor Glut.CURSOR_INHERIT;
1967 if conf.verbose
1968 then
1969 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1970 (100.0*.zoom)
1971 else
1972 state.text <- ""
1974 reshape conf.winw conf.winh;
1977 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1978 state.mode <- View;
1979 conf.zoom <- c.zoom;
1980 conf.presentation <- c.presentation;
1981 conf.interpagespace <- c.interpagespace;
1982 conf.maxwait <- c.maxwait;
1983 conf.hlinks <- c.hlinks;
1984 state.x <- leftx;
1985 if conf.verbose
1986 then
1987 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1988 (100.0*.conf.zoom)
1990 reshape conf.winw conf.winh;
1991 state.anchor <- if goback then anchor else (pageno, 0.0);
1994 let togglebirdseye () =
1995 match state.mode with
1996 | Birdseye vals -> leavebirdseye vals true
1997 | View -> enterbirdseye ()
1998 | _ -> ()
2001 let upbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
2002 let pageno = max 0 (pageno - 1) in
2003 let rec loop = function
2004 | [] -> gotopage1 pageno 0
2005 | l :: _ when l.pageno = pageno ->
2006 if l.pagedispy >= 0 && l.pagey = 0
2007 then G.postRedisplay "upbirdseye"
2008 else gotopage1 pageno 0
2009 | _ :: rest -> loop rest
2011 loop state.layout;
2012 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2015 let downbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
2016 let pageno = min (state.pagecount - 1) (pageno + 1) in
2017 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2018 let rec loop = function
2019 | [] ->
2020 let y, h = getpageyh pageno in
2021 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2022 gotoy (clamp dy)
2023 | l :: _ when l.pageno = pageno ->
2024 if l.pagevh != l.pageh
2025 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2026 else G.postRedisplay "downbirdseye"
2027 | _ :: rest -> loop rest
2029 loop state.layout
2032 let optentry mode _ key =
2033 let btos b = if b then "on" else "off" in
2034 let c = Char.unsafe_chr key in
2035 match c with
2036 | 's' ->
2037 let ondone s =
2038 try conf.scrollstep <- int_of_string s with exc ->
2039 state.text <- Printf.sprintf "bad integer `%s': %s"
2040 s (Printexc.to_string exc)
2042 TEswitch ("scroll step: ", "", None, intentry, ondone)
2044 | 'A' ->
2045 let ondone s =
2047 conf.autoscrollstep <- int_of_string s;
2048 if state.autoscroll <> None
2049 then state.autoscroll <- Some conf.autoscrollstep
2050 with exc ->
2051 state.text <- Printf.sprintf "bad integer `%s': %s"
2052 s (Printexc.to_string exc)
2054 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2056 | 'Z' ->
2057 let ondone s =
2059 let zoom = float (int_of_string s) /. 100.0 in
2060 setzoom zoom
2061 with exc ->
2062 state.text <- Printf.sprintf "bad integer `%s': %s"
2063 s (Printexc.to_string exc)
2065 TEswitch ("zoom: ", "", None, intentry, ondone)
2067 | 't' ->
2068 let ondone s =
2070 conf.thumbw <- bound (int_of_string s) 2 4096;
2071 state.text <-
2072 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2073 begin match mode with
2074 | Birdseye beye ->
2075 leavebirdseye beye false;
2076 enterbirdseye ();
2077 | _ -> ();
2079 with exc ->
2080 state.text <- Printf.sprintf "bad integer `%s': %s"
2081 s (Printexc.to_string exc)
2083 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2085 | 'R' ->
2086 let ondone s =
2087 match try
2088 Some (int_of_string s)
2089 with exc ->
2090 state.text <- Printf.sprintf "bad integer `%s': %s"
2091 s (Printexc.to_string exc);
2092 None
2093 with
2094 | Some angle -> reqlayout angle conf.proportional
2095 | None -> ()
2097 TEswitch ("rotation: ", "", None, intentry, ondone)
2099 | 'i' ->
2100 conf.icase <- not conf.icase;
2101 TEdone ("case insensitive search " ^ (btos conf.icase))
2103 | 'p' ->
2104 conf.preload <- not conf.preload;
2105 gotoy state.y;
2106 TEdone ("preload " ^ (btos conf.preload))
2108 | 'v' ->
2109 conf.verbose <- not conf.verbose;
2110 TEdone ("verbose " ^ (btos conf.verbose))
2112 | 'd' ->
2113 conf.debug <- not conf.debug;
2114 TEdone ("debug " ^ (btos conf.debug))
2116 | 'h' ->
2117 conf.maxhfit <- not conf.maxhfit;
2118 state.maxy <-
2119 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2120 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2122 | 'c' ->
2123 conf.crophack <- not conf.crophack;
2124 TEdone ("crophack " ^ btos conf.crophack)
2126 | 'a' ->
2127 let s =
2128 match conf.maxwait with
2129 | None ->
2130 conf.maxwait <- Some infinity;
2131 "always wait for page to complete"
2132 | Some _ ->
2133 conf.maxwait <- None;
2134 "show placeholder if page is not ready"
2136 TEdone s
2138 | 'f' ->
2139 conf.underinfo <- not conf.underinfo;
2140 TEdone ("underinfo " ^ btos conf.underinfo)
2142 | 'P' ->
2143 conf.savebmarks <- not conf.savebmarks;
2144 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2146 | 'S' ->
2147 let ondone s =
2149 let pageno, py =
2150 match state.layout with
2151 | [] -> 0, 0
2152 | l :: _ ->
2153 l.pageno, l.pagey
2155 conf.interpagespace <- int_of_string s;
2156 state.maxy <- calcheight ();
2157 let y = getpagey pageno in
2158 gotoy (y + py)
2159 with exc ->
2160 state.text <- Printf.sprintf "bad integer `%s': %s"
2161 s (Printexc.to_string exc)
2163 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2165 | 'l' ->
2166 reqlayout conf.angle (not conf.proportional);
2167 TEdone ("proportional display " ^ btos conf.proportional)
2169 | 'T' ->
2170 settrim (not conf.trimmargins) conf.trimfuzz;
2171 TEdone ("trim margins " ^ btos conf.trimmargins)
2173 | 'I' ->
2174 conf.invert <- not conf.invert;
2175 TEdone ("invert colors " ^ btos conf.invert)
2177 | _ ->
2178 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2179 TEstop
2182 class type lvsource = object
2183 method getitemcount : int
2184 method getitem : int -> (string * int)
2185 method hasaction : int -> bool
2186 method exit :
2187 uioh:uioh ->
2188 cancel:bool ->
2189 active:int ->
2190 first:int ->
2191 pan:int ->
2192 qsearch:string ->
2193 uioh option
2194 method getactive : int
2195 method getfirst : int
2196 method getqsearch : string
2197 method setqsearch : string -> unit
2198 method getpan : int
2199 end;;
2201 class virtual lvsourcebase = object
2202 val mutable m_active = 0
2203 val mutable m_first = 0
2204 val mutable m_qsearch = ""
2205 val mutable m_pan = 0
2206 method getactive = m_active
2207 method getfirst = m_first
2208 method getqsearch = m_qsearch
2209 method getpan = m_pan
2210 method setqsearch s = m_qsearch <- s
2211 end;;
2213 let textentryspecial key = function
2214 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2215 let s =
2216 match key with
2217 | Glut.KEY_UP -> action HCprev
2218 | Glut.KEY_DOWN -> action HCnext
2219 | Glut.KEY_HOME -> action HCfirst
2220 | Glut.KEY_END -> action HClast
2221 | _ -> state.text
2223 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2224 G.postRedisplay "special textentry";
2225 | _ -> ()
2228 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2229 let enttext te =
2230 state.mode <- Textentry (te, onleave);
2231 state.text <- "";
2232 enttext ();
2233 G.postRedisplay "textentrykeyboard enttext";
2235 match Char.unsafe_chr key with
2236 | '\008' -> (* backspace *)
2237 let len = String.length text in
2238 if len = 0
2239 then (
2240 onleave Cancel;
2241 G.postRedisplay "textentrykeyboard after cancel";
2243 else (
2244 let s = String.sub text 0 (len - 1) in
2245 enttext (c, s, opthist, onkey, ondone)
2248 | '\r' | '\n' ->
2249 ondone text;
2250 onleave Confirm;
2251 G.postRedisplay "textentrykeyboard after confirm"
2253 | '\007' (* ctrl-g *)
2254 | '\027' -> (* escape *)
2255 if String.length text = 0
2256 then (
2257 begin match opthist with
2258 | None -> ()
2259 | Some (_, onhistcancel) -> onhistcancel ()
2260 end;
2261 onleave Cancel;
2262 state.text <- "";
2263 G.postRedisplay "textentrykeyboard after cancel2"
2265 else (
2266 enttext (c, "", opthist, onkey, ondone)
2269 | '\127' -> () (* delete *)
2271 | _ ->
2272 begin match onkey text key with
2273 | TEdone text ->
2274 ondone text;
2275 onleave Confirm;
2276 G.postRedisplay "textentrykeyboard after confirm2";
2278 | TEcont text ->
2279 enttext (c, text, opthist, onkey, ondone);
2281 | TEstop ->
2282 onleave Cancel;
2283 state.text <- "";
2284 G.postRedisplay "textentrykeyboard after cancel3"
2286 | TEswitch te ->
2287 state.mode <- Textentry (te, onleave);
2288 G.postRedisplay "textentrykeyboard switch";
2289 end;
2292 let firstof first active =
2293 if first > active || abs (first - active) > fstate.maxrows - 1
2294 then max 0 (active - (fstate.maxrows/2))
2295 else first
2298 let calcfirst first active =
2299 if active > first
2300 then
2301 let rows = active - first in
2302 if rows > fstate.maxrows then active - fstate.maxrows else first
2303 else active
2306 let scrollph y maxy =
2307 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2308 let sh = float conf.winh /. sh in
2309 let sh = max sh (float conf.scrollh) in
2311 let percent =
2312 if y = state.maxy
2313 then 1.0
2314 else float y /. float maxy
2316 let position = (float conf.winh -. sh) *. percent in
2318 let position =
2319 if position +. sh > float conf.winh
2320 then float conf.winh -. sh
2321 else position
2323 position, sh;
2326 let coe s = (s :> uioh);;
2328 class listview ~(source:lvsource) ~trusted =
2329 object (self)
2330 val m_pan = source#getpan
2331 val m_first = source#getfirst
2332 val m_active = source#getactive
2333 val m_qsearch = source#getqsearch
2334 val m_prev_uioh = state.uioh
2336 method private elemunder y =
2337 let n = y / (fstate.fontsize+1) in
2338 if m_first + n < source#getitemcount
2339 then (
2340 if source#hasaction (m_first + n)
2341 then Some (m_first + n)
2342 else None
2344 else None
2346 method display =
2347 Gl.enable `blend;
2348 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2349 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2350 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2351 GlDraw.color (1., 1., 1.);
2352 Gl.enable `texture_2d;
2353 let fs = fstate.fontsize in
2354 let nfs = fs + 1 in
2355 let ww = fstate.wwidth in
2356 let tabw = 30.0*.ww in
2357 let itemcount = source#getitemcount in
2358 let rec loop row =
2359 if (row - m_first) * nfs > conf.winh
2360 then ()
2361 else (
2362 if row >= 0 && row < itemcount
2363 then (
2364 let (s, level) = source#getitem row in
2365 let y = (row - m_first) * nfs in
2366 let x = 5.0 +. float (level + m_pan) *. ww in
2367 if row = m_active
2368 then (
2369 Gl.disable `texture_2d;
2370 GlDraw.polygon_mode `both `line;
2371 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2372 GlDraw.rect (1., float (y + 1))
2373 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2374 GlDraw.polygon_mode `both `fill;
2375 GlDraw.color (1., 1., 1.);
2376 Gl.enable `texture_2d;
2379 let drawtabularstring s =
2380 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2381 if trusted
2382 then
2383 let tabpos = try String.index s '\t' with Not_found -> -1 in
2384 if tabpos > 0
2385 then
2386 let len = String.length s - tabpos - 1 in
2387 let s1 = String.sub s 0 tabpos
2388 and s2 = String.sub s (tabpos + 1) len in
2389 let nx = drawstr x s1 in
2390 let sw = nx -. x in
2391 let x = x +. (max tabw sw) in
2392 drawstr x s2
2393 else
2394 drawstr x s
2395 else
2396 drawstr x s
2398 let _ = drawtabularstring s in
2399 loop (row+1)
2403 loop m_first;
2404 Gl.disable `blend;
2405 Gl.disable `texture_2d;
2407 method updownlevel incr =
2408 let len = source#getitemcount in
2409 let _, curlevel = source#getitem m_active in
2410 let rec flow i =
2411 if i = len then i-1 else if i = -1 then 0 else
2412 let _, l = source#getitem i in
2413 if l != curlevel then i else flow (i+incr)
2415 let active = flow m_active in
2416 let first = calcfirst m_first active in
2417 G.postRedisplay "special outline updownlevel";
2418 {< m_active = active; m_first = first >}
2420 method private key1 key =
2421 let set active first qsearch =
2422 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2424 let search active pattern incr =
2425 let dosearch re =
2426 let rec loop n =
2427 if n >= 0 && n < source#getitemcount
2428 then (
2429 let s, _ = source#getitem n in
2431 (try ignore (Str.search_forward re s 0); true
2432 with Not_found -> false)
2433 then Some n
2434 else loop (n + incr)
2436 else None
2438 loop active
2441 let re = Str.regexp_case_fold pattern in
2442 dosearch re
2443 with Failure s ->
2444 state.text <- s;
2445 None
2447 match key with
2448 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2449 let incr = if key = 18 then -1 else 1 in
2450 let active, first =
2451 match search (m_active + incr) m_qsearch incr with
2452 | None ->
2453 state.text <- m_qsearch ^ " [not found]";
2454 m_active, m_first
2455 | Some active ->
2456 state.text <- m_qsearch;
2457 active, firstof m_first active
2459 G.postRedisplay "listview ctrl-r/s";
2460 set active first m_qsearch;
2462 | 8 -> (* backspace *)
2463 let len = String.length m_qsearch in
2464 if len = 0
2465 then coe self
2466 else (
2467 if len = 1
2468 then (
2469 state.text <- "";
2470 G.postRedisplay "listview empty qsearch";
2471 set m_active m_first "";
2473 else
2474 let qsearch = String.sub m_qsearch 0 (len - 1) in
2475 let active, first =
2476 match search m_active qsearch ~-1 with
2477 | None ->
2478 state.text <- qsearch ^ " [not found]";
2479 m_active, m_first
2480 | Some active ->
2481 state.text <- qsearch;
2482 active, firstof m_first active
2484 G.postRedisplay "listview backspace qsearch";
2485 set active first qsearch
2488 | _ when key >= 32 && key < 127 ->
2489 let pattern = addchar m_qsearch (Char.chr key) in
2490 let active, first =
2491 match search m_active pattern 1 with
2492 | None ->
2493 state.text <- pattern ^ " [not found]";
2494 m_active, m_first
2495 | Some active ->
2496 state.text <- pattern;
2497 active, firstof m_first active
2499 G.postRedisplay "listview qsearch add";
2500 set active first pattern;
2502 | 27 -> (* escape *)
2503 state.text <- "";
2504 if String.length m_qsearch = 0
2505 then (
2506 G.postRedisplay "list view escape";
2507 begin
2508 match
2509 source#exit (coe self) true m_active m_first m_pan m_qsearch
2510 with
2511 | None -> m_prev_uioh
2512 | Some uioh -> uioh
2515 else (
2516 G.postRedisplay "list view kill qsearch";
2517 source#setqsearch "";
2518 coe {< m_qsearch = "" >}
2521 | 13 -> (* enter *)
2522 state.text <- "";
2523 let self = {< m_qsearch = "" >} in
2524 source#setqsearch "";
2525 let opt =
2526 G.postRedisplay "listview enter";
2527 if m_active >= 0 && m_active < source#getitemcount
2528 then (
2529 source#exit (coe self) false m_active m_first m_pan "";
2531 else (
2532 source#exit (coe self) true m_active m_first m_pan "";
2535 begin match opt with
2536 | None -> m_prev_uioh
2537 | Some uioh -> uioh
2540 | 127 -> (* delete *)
2541 coe self
2543 | _ -> dolog "unknown key %d" key; coe self
2545 method private special1 key =
2546 let itemcount = source#getitemcount in
2547 let find start incr =
2548 let rec find i =
2549 if i = -1 || i = itemcount
2550 then -1
2551 else (
2552 if source#hasaction i
2553 then i
2554 else find (i + incr)
2557 find start
2559 let set active first =
2560 let first = bound first 0 (itemcount - fstate.maxrows) in
2561 state.text <- "";
2562 coe {< m_active = active; m_first = first >}
2564 let navigate incr =
2565 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2566 let active, first =
2567 let incr1 = if incr > 0 then 1 else -1 in
2568 if isvisible m_first m_active
2569 then
2570 let next =
2571 let next = m_active + incr in
2572 let next =
2573 if next < 0 || next >= itemcount
2574 then -1
2575 else find next incr1
2577 if next = -1 || abs (m_active - next) > fstate.maxrows
2578 then -1
2579 else next
2581 if next = -1
2582 then
2583 let first = m_first + incr in
2584 let first = bound first 0 (itemcount - 1) in
2585 let next =
2586 let next = m_active + incr in
2587 let next = bound next 0 (itemcount - 1) in
2588 find next ~-incr1
2590 let active = if next = -1 then m_active else next in
2591 active, first
2592 else
2593 let first = min next m_first in
2594 next, first
2595 else
2596 let first = m_first + incr in
2597 let first = bound first 0 (itemcount - 1) in
2598 let active =
2599 let next = m_active + incr in
2600 let next = bound next 0 (itemcount - 1) in
2601 let next = find next incr1 in
2602 let active =
2603 if next = -1 || abs (m_active - first) > fstate.maxrows
2604 then (
2605 let active = if m_active = -1 then next else m_active in
2606 active
2608 else next
2610 if isvisible first active
2611 then active
2612 else -1
2614 active, first
2616 G.postRedisplay "listview navigate";
2617 set active first;
2619 begin match key with
2620 | Glut.KEY_UP -> navigate ~-1
2621 | Glut.KEY_DOWN -> navigate 1
2622 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2623 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2625 | Glut.KEY_RIGHT ->
2626 state.text <- "";
2627 G.postRedisplay "listview right";
2628 coe {< m_pan = m_pan - 1 >}
2630 | Glut.KEY_LEFT ->
2631 state.text <- "";
2632 G.postRedisplay "listview left";
2633 coe {< m_pan = m_pan + 1 >}
2635 | Glut.KEY_HOME ->
2636 let active = find 0 1 in
2637 G.postRedisplay "listview home";
2638 set active 0;
2640 | Glut.KEY_END ->
2641 let first = max 0 (itemcount - fstate.maxrows) in
2642 let active = find (itemcount - 1) ~-1 in
2643 G.postRedisplay "listview end";
2644 set active first;
2646 | _ -> coe self
2647 end;
2649 method key key =
2650 match state.mode with
2651 | Textentry te -> textentrykeyboard key te; coe self
2652 | _ -> self#key1 key
2654 method special key =
2655 match state.mode with
2656 | Textentry te -> textentryspecial key te; coe self
2657 | _ -> self#special1 key
2659 method button button bstate x y =
2660 let opt =
2661 match button with
2662 | Glut.LEFT_BUTTON when x > conf.winw - conf.scrollbw ->
2663 G.postRedisplay "listview scroll";
2664 if bstate = Glut.DOWN
2665 then
2666 let _, position, sh = self#scrollph in
2667 if y > truncate position && y < truncate (position +. sh)
2668 then (
2669 state.mstate <- Mscrolly;
2670 Some (coe self)
2672 else
2673 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
2674 let first = truncate (s *. float source#getitemcount) in
2675 let first = min source#getitemcount first in
2676 Some (coe {< m_first = first; m_active = first >})
2677 else (
2678 state.mstate <- Mnone;
2679 Some (coe self);
2681 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
2682 begin match self#elemunder y with
2683 | Some n ->
2684 G.postRedisplay "listview click";
2685 source#exit
2686 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
2687 | _ ->
2688 Some (coe self)
2690 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
2691 let len = source#getitemcount in
2692 let first =
2693 if n = 4 && m_first + fstate.maxrows >= len
2694 then
2695 m_first
2696 else
2697 let first = m_first + (if n == 3 then -1 else 1) in
2698 bound first 0 (len - 1)
2700 G.postRedisplay "listview wheel";
2701 Some (coe {< m_first = first >})
2702 | _ ->
2703 Some (coe self)
2705 match opt with
2706 | None -> m_prev_uioh
2707 | Some uioh -> uioh
2709 method motion _ y =
2710 match state.mstate with
2711 | Mscrolly ->
2712 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
2713 let first = truncate (s *. float source#getitemcount) in
2714 let first = min source#getitemcount first in
2715 G.postRedisplay "listview motion";
2716 coe {< m_first = first; m_active = first >}
2717 | _ -> coe self
2719 method pmotion x y =
2720 if x < conf.winw - conf.scrollbw
2721 then
2722 let n =
2723 match self#elemunder y with
2724 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
2725 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
2727 let o =
2728 if n != m_active
2729 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
2730 else self
2732 coe o
2733 else (
2734 Glut.setCursor Glut.CURSOR_INHERIT;
2735 coe self
2738 method infochanged _ = ()
2740 method scrollpw = (0, 0.0, 0.0)
2741 method scrollph =
2742 let nfs = fstate.fontsize + 1 in
2743 let y = m_first * nfs in
2744 let itemcount = source#getitemcount in
2745 let maxi = max 0 (itemcount - fstate.maxrows) in
2746 let maxy = maxi * nfs in
2747 let p, h = scrollph y maxy in
2748 conf.scrollbw, p, h
2749 end;;
2751 class outlinelistview ~source =
2752 object (self)
2753 inherit listview ~source:(source :> lvsource) ~trusted:false as super
2755 method key key =
2756 match key with
2757 | 14 -> (* ctrl-n *)
2758 source#narrow m_qsearch;
2759 G.postRedisplay "outline ctrl-n";
2760 coe {< m_first = 0; m_active = 0 >}
2762 | 21 -> (* ctrl-u *)
2763 source#denarrow;
2764 G.postRedisplay "outline ctrl-u";
2765 state.text <- "";
2766 coe {< m_first = 0; m_active = 0 >}
2768 | 12 -> (* ctrl-l *)
2769 let first = m_active - (fstate.maxrows / 2) in
2770 G.postRedisplay "outline ctrl-l";
2771 coe {< m_first = first >}
2773 | 127 -> (* delete *)
2774 source#remove m_active;
2775 G.postRedisplay "outline delete";
2776 let active = max 0 (m_active-1) in
2777 coe {< m_first = firstof m_first active;
2778 m_active = active >}
2780 | key -> super#key key
2782 method special key =
2783 let calcfirst first active =
2784 if active > first
2785 then
2786 let rows = active - first in
2787 if rows > fstate.maxrows then active - fstate.maxrows else first
2788 else active
2790 let navigate incr =
2791 let active = m_active + incr in
2792 let active = bound active 0 (source#getitemcount - 1) in
2793 let first = calcfirst m_first active in
2794 G.postRedisplay "special outline navigate";
2795 coe {< m_active = active; m_first = first >}
2797 match key with
2798 | Glut.KEY_UP -> navigate ~-1
2799 | Glut.KEY_DOWN -> navigate 1
2800 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2801 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2803 | Glut.KEY_RIGHT ->
2804 let o =
2805 if Glut.getModifiers () land Glut.active_ctrl != 0
2806 then (
2807 G.postRedisplay "special outline right";
2808 {< m_pan = m_pan + 1 >}
2810 else self#updownlevel 1
2812 coe o
2814 | Glut.KEY_LEFT ->
2815 let o =
2816 if Glut.getModifiers () land Glut.active_ctrl != 0
2817 then (
2818 G.postRedisplay "special outline left";
2819 {< m_pan = m_pan - 1 >}
2821 else self#updownlevel ~-1
2823 coe o
2825 | Glut.KEY_HOME ->
2826 G.postRedisplay "special outline home";
2827 coe {< m_first = 0; m_active = 0 >}
2829 | Glut.KEY_END ->
2830 let active = source#getitemcount - 1 in
2831 let first = max 0 (active - fstate.maxrows) in
2832 G.postRedisplay "special outline end";
2833 coe {< m_active = active; m_first = first >}
2835 | _ -> super#special key
2838 let outlinesource usebookmarks =
2839 let empty = [||] in
2840 (object
2841 inherit lvsourcebase
2842 val mutable m_items = empty
2843 val mutable m_orig_items = empty
2844 val mutable m_prev_items = empty
2845 val mutable m_narrow_pattern = ""
2846 val mutable m_hadremovals = false
2848 method getitemcount =
2849 Array.length m_items + (if m_hadremovals then 1 else 0)
2851 method getitem n =
2852 if n == Array.length m_items && m_hadremovals
2853 then
2854 ("[Confirm removal]", 0)
2855 else
2856 let s, n, _ = m_items.(n) in
2857 (s, n)
2859 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
2860 ignore (uioh, first, pan, qsearch);
2861 let confrimremoval = m_hadremovals && active = Array.length m_items in
2862 let items =
2863 if String.length m_narrow_pattern = 0
2864 then m_orig_items
2865 else m_items
2867 if not cancel
2868 then (
2869 if not confrimremoval
2870 then(
2871 let _, _, anchor = m_items.(active) in
2872 gotoanchor anchor;
2873 m_items <- items;
2875 else (
2876 state.bookmarks <- Array.to_list m_items;
2877 m_orig_items <- m_items;
2880 else m_items <- items;
2881 None
2883 method hasaction _ = true
2885 method greetmsg =
2886 if Array.length m_items != Array.length m_orig_items
2887 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
2888 else ""
2890 method narrow pattern =
2891 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
2892 match reopt with
2893 | None -> ()
2894 | Some re ->
2895 let rec loop accu n =
2896 if n = -1
2897 then (
2898 m_narrow_pattern <- pattern;
2899 m_items <- Array.of_list accu
2901 else
2902 let (s, _, _) as o = m_items.(n) in
2903 let accu =
2904 if (try ignore (Str.search_forward re s 0); true
2905 with Not_found -> false)
2906 then o :: accu
2907 else accu
2909 loop accu (n-1)
2911 loop [] (Array.length m_items - 1)
2913 method denarrow =
2914 m_orig_items <- (
2915 if usebookmarks
2916 then Array.of_list state.bookmarks
2917 else state.outlines
2919 m_items <- m_orig_items
2921 method remove m =
2922 if usebookmarks
2923 then
2924 if m >= 0 && m < Array.length m_items
2925 then (
2926 m_hadremovals <- true;
2927 m_items <- Array.init (Array.length m_items - 1) (fun n ->
2928 let n = if n >= m then n+1 else n in
2929 m_items.(n)
2933 method reset anchor items =
2934 m_hadremovals <- false;
2935 if m_orig_items == empty || m_prev_items != items
2936 then (
2937 m_orig_items <- items;
2938 if String.length m_narrow_pattern = 0
2939 then m_items <- items;
2941 m_prev_items <- items;
2942 let rely = getanchory anchor in
2943 let active =
2944 let rec loop n best bestd =
2945 if n = Array.length m_items
2946 then best
2947 else
2948 let (_, _, anchor) = m_items.(n) in
2949 let orely = getanchory anchor in
2950 let d = abs (orely - rely) in
2951 if d < bestd
2952 then loop (n+1) n d
2953 else loop (n+1) best bestd
2955 loop 0 ~-1 max_int
2957 m_active <- active;
2958 m_first <- firstof m_first active
2959 end)
2962 let enterselector usebookmarks =
2963 let source = outlinesource usebookmarks in
2964 fun errmsg ->
2965 let outlines =
2966 if usebookmarks
2967 then Array.of_list state.bookmarks
2968 else state.outlines
2970 if Array.length outlines = 0
2971 then (
2972 showtext ' ' errmsg;
2974 else (
2975 state.text <- source#greetmsg;
2976 Glut.setCursor Glut.CURSOR_INHERIT;
2977 let anchor = getanchor () in
2978 source#reset anchor outlines;
2979 state.uioh <- coe (new outlinelistview ~source);
2980 G.postRedisplay "enter selector";
2984 let enteroutlinemode =
2985 let f = enterselector false in
2986 fun ()-> f "Document has no outline";
2989 let enterbookmarkmode =
2990 let f = enterselector true in
2991 fun () -> f "Document has no bookmarks (yet)";
2994 let color_of_string s =
2995 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
2996 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3000 let color_to_string (r, g, b) =
3001 let r = truncate (r *. 256.0)
3002 and g = truncate (g *. 256.0)
3003 and b = truncate (b *. 256.0) in
3004 Printf.sprintf "%d/%d/%d" r g b
3007 let irect_of_string s =
3008 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3011 let irect_to_string (x0,y0,x1,y1) =
3012 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3015 let makecheckers () =
3016 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3017 following to say:
3018 converted by Issac Trotts. July 25, 2002 *)
3019 let image_height = 64
3020 and image_width = 64 in
3022 let make_image () =
3023 let image =
3024 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3026 for i = 0 to image_width - 1 do
3027 for j = 0 to image_height - 1 do
3028 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3029 (if (i land 8 ) lxor (j land 8) = 0
3030 then [|255;255;255|] else [|200;200;200|])
3031 done
3032 done;
3033 image
3035 let image = make_image () in
3036 let id = GlTex.gen_texture () in
3037 GlTex.bind_texture `texture_2d id;
3038 GlPix.store (`unpack_alignment 1);
3039 GlTex.image2d image;
3040 List.iter (GlTex.parameter ~target:`texture_2d)
3041 [ `wrap_s `repeat;
3042 `wrap_t `repeat;
3043 `mag_filter `nearest;
3044 `min_filter `nearest ];
3048 let setcheckers enabled =
3049 match state.texid with
3050 | None ->
3051 if enabled then state.texid <- Some (makecheckers ())
3053 | Some texid ->
3054 if not enabled
3055 then (
3056 GlTex.delete_texture texid;
3057 state.texid <- None;
3061 let int_of_string_with_suffix s =
3062 let l = String.length s in
3063 let s1, shift =
3064 if l > 1
3065 then
3066 let suffix = Char.lowercase s.[l-1] in
3067 match suffix with
3068 | 'k' -> String.sub s 0 (l-1), 10
3069 | 'm' -> String.sub s 0 (l-1), 20
3070 | 'g' -> String.sub s 0 (l-1), 30
3071 | _ -> s, 0
3072 else s, 0
3074 let n = int_of_string s1 in
3075 let m = n lsl shift in
3076 if m < 0 || m < n
3077 then raise (Failure "value too large")
3078 else m
3081 let string_with_suffix_of_int n =
3082 if n = 0
3083 then "0"
3084 else
3085 let n, s =
3086 if n = 0
3087 then 0, ""
3088 else (
3089 if n land ((1 lsl 20) - 1) = 0
3090 then n lsr 20, "M"
3091 else (
3092 if n land ((1 lsl 10) - 1) = 0
3093 then n lsr 10, "K"
3094 else n, ""
3098 let rec loop s n =
3099 let h = n mod 1000 in
3100 let n = n / 1000 in
3101 if n = 0
3102 then string_of_int h ^ s
3103 else (
3104 let s = Printf.sprintf "_%03d%s" h s in
3105 loop s n
3108 loop "" n ^ s;
3111 let describe_location () =
3112 let f (fn, _) l =
3113 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3115 let fn, ln = List.fold_left f (-1, -1) state.layout in
3116 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3117 let percent =
3118 if maxy <= 0
3119 then 100.
3120 else (100. *. (float state.y /. float maxy))
3122 if fn = ln
3123 then
3124 Printf.sprintf "page %d of %d [%.2f%%]"
3125 (fn+1) state.pagecount percent
3126 else
3127 Printf.sprintf
3128 "pages %d-%d of %d [%.2f%%]"
3129 (fn+1) (ln+1) state.pagecount percent
3132 let enterinfomode =
3133 let btos b = if b then "\xe2\x88\x9a" else "" in
3134 let showextended = ref false in
3135 let leave mode = function
3136 | Confirm -> state.mode <- mode
3137 | Cancel -> state.mode <- mode in
3138 let src =
3139 (object
3140 val mutable m_first_time = true
3141 val mutable m_l = []
3142 val mutable m_a = [||]
3143 val mutable m_prev_uioh = nouioh
3144 val mutable m_prev_mode = View
3146 inherit lvsourcebase
3148 method reset prev_mode prev_uioh =
3149 m_a <- Array.of_list (List.rev m_l);
3150 m_l <- [];
3151 m_prev_mode <- prev_mode;
3152 m_prev_uioh <- prev_uioh;
3153 if m_first_time
3154 then (
3155 let rec loop n =
3156 if n >= Array.length m_a
3157 then ()
3158 else
3159 match m_a.(n) with
3160 | _, _, _, Action _ -> m_active <- n
3161 | _ -> loop (n+1)
3163 loop 0;
3164 m_first_time <- false;
3167 method int name get set =
3168 m_l <-
3169 (name, `int get, 1, Action (
3170 fun u ->
3171 let ondone s =
3172 try set (int_of_string s)
3173 with exn ->
3174 state.text <- Printf.sprintf "bad integer `%s': %s"
3175 s (Printexc.to_string exn)
3177 state.text <- "";
3178 let te = name ^ ": ", "", None, intentry, ondone in
3179 state.mode <- Textentry (te, leave m_prev_mode);
3181 )) :: m_l
3183 method int_with_suffix name get set =
3184 m_l <-
3185 (name, `intws get, 1, Action (
3186 fun u ->
3187 let ondone s =
3188 try set (int_of_string_with_suffix s)
3189 with exn ->
3190 state.text <- Printf.sprintf "bad integer `%s': %s"
3191 s (Printexc.to_string exn)
3193 state.text <- "";
3194 let te =
3195 name ^ ": ", "", None, intentry_with_suffix, ondone
3197 state.mode <- Textentry (te, leave m_prev_mode);
3199 )) :: m_l
3201 method bool ?(offset=1) ?(btos=btos) name get set =
3202 m_l <-
3203 (name, `bool (btos, get), offset, Action (
3204 fun u ->
3205 let v = get () in
3206 set (not v);
3208 )) :: m_l
3210 method color name get set =
3211 m_l <-
3212 (name, `color get, 1, Action (
3213 fun u ->
3214 let invalid = (nan, nan, nan) in
3215 let ondone s =
3216 let c =
3217 try color_of_string s
3218 with exn ->
3219 state.text <- Printf.sprintf "bad color `%s': %s"
3220 s (Printexc.to_string exn);
3221 invalid
3223 if c <> invalid
3224 then set c;
3226 let te = name ^ ": ", "", None, textentry, ondone in
3227 state.text <- color_to_string (get ());
3228 state.mode <- Textentry (te, leave m_prev_mode);
3230 )) :: m_l
3232 method string name get set =
3233 m_l <-
3234 (name, `string get, 1, Action (
3235 fun u ->
3236 let ondone s = set s in
3237 let te = name ^ ": ", "", None, textentry, ondone in
3238 state.mode <- Textentry (te, leave m_prev_mode);
3240 )) :: m_l
3242 method colorspace name get set =
3243 m_l <-
3244 (name, `string get, 1, Action (
3245 fun _ ->
3246 let source =
3247 let vals = [| "rgb"; "bgr"; "gray" |] in
3248 (object
3249 inherit lvsourcebase
3251 initializer
3252 m_active <- int_of_colorspace conf.colorspace;
3253 m_first <- 0;
3255 method getitemcount = Array.length vals
3256 method getitem n = (vals.(n), 0)
3257 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3258 ignore (uioh, first, pan, qsearch);
3259 if not cancel then set active;
3260 None
3261 method hasaction _ = true
3262 end)
3264 state.text <- "";
3265 coe (new listview ~source ~trusted:true)
3266 )) :: m_l
3268 method caption s offset =
3269 m_l <- (s, `empty, offset, Noaction) :: m_l
3271 method caption2 s f offset =
3272 m_l <- (s, `string f, offset, Noaction) :: m_l
3274 method getitemcount = Array.length m_a
3276 method getitem n =
3277 let tostr = function
3278 | `int f -> string_of_int (f ())
3279 | `intws f -> string_with_suffix_of_int (f ())
3280 | `string f -> f ()
3281 | `color f -> color_to_string (f ())
3282 | `bool (btos, f) -> btos (f ())
3283 | `empty -> ""
3285 let name, t, offset, _ = m_a.(n) in
3286 ((let s = tostr t in
3287 if String.length s > 0
3288 then Printf.sprintf "%s\t%s" name s
3289 else name),
3290 offset)
3292 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3293 let uiohopt =
3294 if not cancel
3295 then (
3296 m_qsearch <- qsearch;
3297 let uioh =
3298 match m_a.(active) with
3299 | _, _, _, Action f -> f uioh
3300 | _ -> uioh
3302 Some uioh
3304 else None
3306 m_active <- active;
3307 m_first <- first;
3308 m_pan <- pan;
3309 uiohopt
3311 method hasaction n =
3312 match m_a.(n) with
3313 | _, _, _, Action _ -> true
3314 | _ -> false
3315 end)
3317 let rec fillsrc prevmode prevuioh =
3318 let sep () = src#caption "" 0 in
3319 let colorp name get set =
3320 src#string name
3321 (fun () -> color_to_string (get ()))
3322 (fun v ->
3324 let c = color_of_string v in
3325 set c
3326 with exn ->
3327 state.text <- Printf.sprintf "bad color `%s': %s"
3328 v (Printexc.to_string exn);
3331 let oldmode = state.mode in
3332 let birdseye = isbirdseye state.mode in
3334 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3336 src#bool "presentation mode"
3337 (fun () -> conf.presentation)
3338 (fun v ->
3339 conf.presentation <- v;
3340 state.anchor <- getanchor ();
3341 represent ());
3343 src#bool "ignore case in searches"
3344 (fun () -> conf.icase)
3345 (fun v -> conf.icase <- v);
3347 src#bool "preload"
3348 (fun () -> conf.preload)
3349 (fun v -> conf.preload <- v);
3351 src#bool "highlight links"
3352 (fun () -> conf.hlinks)
3353 (fun v -> conf.hlinks <- v);
3355 src#bool "under info"
3356 (fun () -> conf.underinfo)
3357 (fun v -> conf.underinfo <- v);
3359 src#bool "persistent bookmarks"
3360 (fun () -> conf.savebmarks)
3361 (fun v -> conf.savebmarks <- v);
3363 src#bool "proportional display"
3364 (fun () -> conf.proportional)
3365 (fun v -> reqlayout conf.angle v);
3367 src#bool "trim margins"
3368 (fun () -> conf.trimmargins)
3369 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3371 src#bool "persistent location"
3372 (fun () -> conf.jumpback)
3373 (fun v -> conf.jumpback <- v);
3375 sep ();
3376 src#int "vertical margin"
3377 (fun () -> conf.interpagespace)
3378 (fun n ->
3379 conf.interpagespace <- n;
3380 let pageno, py =
3381 match state.layout with
3382 | [] -> 0, 0
3383 | l :: _ ->
3384 l.pageno, l.pagey
3386 state.maxy <- calcheight ();
3387 let y = getpagey pageno in
3388 gotoy (y + py)
3391 src#int "page bias"
3392 (fun () -> conf.pagebias)
3393 (fun v -> conf.pagebias <- v);
3395 src#int "scroll step"
3396 (fun () -> conf.scrollstep)
3397 (fun n -> conf.scrollstep <- n);
3399 src#int "auto scroll step"
3400 (fun () ->
3401 match state.autoscroll with
3402 | Some step -> step
3403 | _ -> conf.autoscrollstep)
3404 (fun n ->
3405 if state.autoscroll <> None
3406 then state.autoscroll <- Some n;
3407 conf.autoscrollstep <- n);
3409 src#int "zoom"
3410 (fun () -> truncate (conf.zoom *. 100.))
3411 (fun v -> setzoom ((float v) /. 100.));
3413 src#int "rotation"
3414 (fun () -> conf.angle)
3415 (fun v -> reqlayout v conf.proportional);
3417 src#int "scroll bar width"
3418 (fun () -> state.scrollw)
3419 (fun v ->
3420 state.scrollw <- v;
3421 conf.scrollbw <- v;
3422 reshape conf.winw conf.winh;
3425 src#int "scroll handle height"
3426 (fun () -> conf.scrollh)
3427 (fun v -> conf.scrollh <- v;);
3429 src#int "thumbnail width"
3430 (fun () -> conf.thumbw)
3431 (fun v ->
3432 conf.thumbw <- min 4096 v;
3433 match oldmode with
3434 | Birdseye beye ->
3435 leavebirdseye beye false;
3436 enterbirdseye ()
3437 | _ -> ()
3440 sep ();
3441 src#caption "Presentation mode" 0;
3442 src#bool "scrollbar visible"
3443 (fun () -> conf.scrollbarinpm)
3444 (fun v ->
3445 if v != conf.scrollbarinpm
3446 then (
3447 conf.scrollbarinpm <- v;
3448 if conf.presentation
3449 then (
3450 state.scrollw <- if v then conf.scrollbw else 0;
3451 reshape conf.winw conf.winh;
3456 sep ();
3457 src#caption "Pixmap cache" 0;
3458 src#int_with_suffix "size (advisory)"
3459 (fun () -> conf.memlimit)
3460 (fun v -> conf.memlimit <- v);
3462 src#caption2 "used"
3463 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3464 (string_with_suffix_of_int state.memused)
3465 (Hashtbl.length state.tilemap)) 1;
3467 sep ();
3468 src#caption "Layout" 0;
3469 src#caption2 "Dimension"
3470 (fun () ->
3471 Printf.sprintf "%dx%d (virtual %dx%d)"
3472 conf.winw conf.winh
3473 state.w state.maxy)
3475 if conf.debug
3476 then
3477 src#caption2 "Position" (fun () ->
3478 Printf.sprintf "%dx%d" state.x state.y
3480 else
3481 src#caption2 "Visible" (fun () -> describe_location ()) 1
3484 sep ();
3485 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3486 "Save these parameters as global defaults at exit"
3487 (fun () -> conf.bedefault)
3488 (fun v -> conf.bedefault <- v)
3491 sep ();
3492 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3493 src#bool ~offset:0 ~btos "Extended parameters"
3494 (fun () -> !showextended)
3495 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3496 if !showextended
3497 then (
3498 src#bool "checkers"
3499 (fun () -> conf.checkers)
3500 (fun v -> conf.checkers <- v; setcheckers v);
3501 src#bool "verbose"
3502 (fun () -> conf.verbose)
3503 (fun v -> conf.verbose <- v);
3504 src#bool "invert colors"
3505 (fun () -> conf.invert)
3506 (fun v -> conf.invert <- v);
3507 src#bool "max fit"
3508 (fun () -> conf.maxhfit)
3509 (fun v -> conf.maxhfit <- v);
3510 src#bool "redirect stderr"
3511 (fun () -> conf.redirectstderr)
3512 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3513 src#string "uri launcher"
3514 (fun () -> conf.urilauncher)
3515 (fun v -> conf.urilauncher <- v);
3516 src#string "tile size"
3517 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3518 (fun v ->
3520 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3521 conf.tileh <- max 64 w;
3522 conf.tilew <- max 64 h;
3523 flushtiles ();
3524 with exn ->
3525 state.text <- Printf.sprintf "bad tile size `%s': %s"
3526 v (Printexc.to_string exn));
3527 src#int "texture count"
3528 (fun () -> conf.texcount)
3529 (fun v ->
3530 if realloctexts v
3531 then conf.texcount <- v
3532 else showtext '!' " Failed to set texture count please retry later"
3534 src#int "slice height"
3535 (fun () -> conf.sliceheight)
3536 (fun v ->
3537 conf.sliceheight <- v;
3538 wcmd "sliceh" [`i conf.sliceheight];
3540 src#int "anti-aliasing level"
3541 (fun () -> conf.aalevel)
3542 (fun v ->
3543 conf.aalevel <- bound v 0 8;
3544 state.anchor <- getanchor ();
3545 opendoc state.path state.password;
3547 src#int "ui font size"
3548 (fun () -> fstate.fontsize)
3549 (fun v -> setfontsize (bound v 5 100));
3550 colorp "background color"
3551 (fun () -> conf.bgcolor)
3552 (fun v -> conf.bgcolor <- v);
3553 src#bool "crop hack"
3554 (fun () -> conf.crophack)
3555 (fun v -> conf.crophack <- v);
3556 src#string "trim fuzz"
3557 (fun () -> irect_to_string conf.trimfuzz)
3558 (fun v ->
3560 conf.trimfuzz <- irect_of_string v;
3561 if conf.trimmargins
3562 then settrim true conf.trimfuzz;
3563 with exn ->
3564 state.text <- Printf.sprintf "bad irect `%s': %s"
3565 v (Printexc.to_string exn)
3567 src#string "throttle"
3568 (fun () ->
3569 match conf.maxwait with
3570 | None -> "show place holder if page is not ready"
3571 | Some time ->
3572 if time = infinity
3573 then "wait for page to fully render"
3574 else
3575 "wait " ^ string_of_float time
3576 ^ " seconds before showing placeholder"
3578 (fun v ->
3580 let f = float_of_string v in
3581 if f <= 0.0
3582 then conf.maxwait <- None
3583 else conf.maxwait <- Some f
3584 with exn ->
3585 state.text <- Printf.sprintf "bad time `%s': %s"
3586 v (Printexc.to_string exn)
3588 src#colorspace "color space"
3589 (fun () -> colorspace_to_string conf.colorspace)
3590 (fun v ->
3591 conf.colorspace <- colorspace_of_int v;
3592 wcmd "cs" [`i v];
3593 load state.layout;
3597 sep ();
3598 src#caption "Document" 0;
3599 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3600 src#caption2 "Pages"
3601 (fun () -> string_of_int state.pagecount) 1;
3602 src#caption2 "Dimensions"
3603 (fun () -> string_of_int (List.length state.pdims)) 1;
3604 if conf.trimmargins
3605 then (
3606 sep ();
3607 src#caption "Trimmed margins" 0;
3608 src#caption2 "Dimensions"
3609 (fun () -> string_of_int (List.length state.pdims)) 1;
3612 src#reset prevmode prevuioh;
3614 fun () ->
3615 state.text <- "";
3616 let prevmode = state.mode
3617 and prevuioh = state.uioh in
3618 fillsrc prevmode prevuioh;
3619 let source = (src :> lvsource) in
3620 state.uioh <- coe (object (self)
3621 inherit listview ~source ~trusted:true as super
3622 val mutable m_prevmemused = 0
3623 method infochanged = function
3624 | Memused ->
3625 if m_prevmemused != state.memused
3626 then (
3627 m_prevmemused <- state.memused;
3628 G.postRedisplay "memusedchanged";
3630 | Pdim -> G.postRedisplay "pdimchanged"
3631 | Docinfo -> fillsrc prevmode prevuioh
3633 method special key =
3634 if Glut.getModifiers () land Glut.active_ctrl = 0
3635 then
3636 match key with
3637 | Glut.KEY_LEFT -> coe (self#updownlevel ~-1)
3638 | Glut.KEY_RIGHT -> coe (self#updownlevel 1)
3639 | _ -> super#special key
3640 else super#special key
3641 end);
3642 G.postRedisplay "info";
3645 let enterhelpmode =
3646 let source =
3647 (object
3648 inherit lvsourcebase
3649 method getitemcount = Array.length state.help
3650 method getitem n =
3651 let s, n, _ = state.help.(n) in
3652 (s, n)
3654 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3655 let optuioh =
3656 if not cancel
3657 then (
3658 m_qsearch <- qsearch;
3659 match state.help.(active) with
3660 | _, _, Action f -> Some (f uioh)
3661 | _ -> Some (uioh)
3663 else None
3665 m_active <- active;
3666 m_first <- first;
3667 m_pan <- pan;
3668 optuioh
3670 method hasaction n =
3671 match state.help.(n) with
3672 | _, _, Action _ -> true
3673 | _ -> false
3675 initializer
3676 m_active <- -1
3677 end)
3678 in fun () ->
3679 state.uioh <- coe (new listview ~source ~trusted:true);
3680 G.postRedisplay "help";
3683 let entermsgsmode =
3684 let msgsource =
3685 let re = Str.regexp "[\r\n]" in
3686 (object
3687 inherit lvsourcebase
3688 val mutable m_items = [||]
3690 method getitemcount = 1 + Array.length m_items
3692 method getitem n =
3693 if n = 0
3694 then "[Clear]", 0
3695 else m_items.(n-1), 0
3697 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3698 ignore uioh;
3699 if not cancel
3700 then (
3701 if active = 0
3702 then Buffer.clear state.errmsgs;
3703 m_qsearch <- qsearch;
3705 m_active <- active;
3706 m_first <- first;
3707 m_pan <- pan;
3708 None
3710 method hasaction n =
3711 n = 0
3713 method reset =
3714 state.newerrmsgs <- false;
3715 let l = Str.split re (Buffer.contents state.errmsgs) in
3716 m_items <- Array.of_list l
3718 initializer
3719 m_active <- 0
3720 end)
3721 in fun () ->
3722 state.text <- "";
3723 msgsource#reset;
3724 let source = (msgsource :> lvsource) in
3725 state.uioh <- coe (object
3726 inherit listview ~source ~trusted:false as super
3727 method display =
3728 if state.newerrmsgs
3729 then msgsource#reset;
3730 super#display
3731 end);
3732 G.postRedisplay "msgs";
3735 let quickbookmark ?title () =
3736 match state.layout with
3737 | [] -> ()
3738 | l :: _ ->
3739 let title =
3740 match title with
3741 | None ->
3742 let sec = Unix.gettimeofday () in
3743 let tm = Unix.localtime sec in
3744 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
3745 (l.pageno+1)
3746 tm.Unix.tm_mday
3747 tm.Unix.tm_mon
3748 (tm.Unix.tm_year + 1900)
3749 tm.Unix.tm_hour
3750 tm.Unix.tm_min
3751 | Some title -> title
3753 state.bookmarks <-
3754 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
3755 :: state.bookmarks
3758 let doreshape w h =
3759 state.fullscreen <- None;
3760 Glut.reshapeWindow w h;
3763 let viewkeyboard key =
3764 let enttext te =
3765 let mode = state.mode in
3766 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3767 state.text <- "";
3768 enttext ();
3769 G.postRedisplay "view:enttext"
3771 let c = Char.chr key in
3772 match c with
3773 | '\027' | 'q' -> (* escape *)
3774 begin match state.mstate with
3775 | Mzoomrect _ ->
3776 state.mstate <- Mnone;
3777 Glut.setCursor Glut.CURSOR_INHERIT;
3778 G.postRedisplay "kill zoom rect";
3779 | _ ->
3780 raise Quit
3781 end;
3783 | '\008' -> (* backspace *)
3784 let y = getnav ~-1 in
3785 gotoy_and_clear_text y
3787 | 'o' ->
3788 enteroutlinemode ()
3790 | 'u' ->
3791 state.rects <- [];
3792 state.text <- "";
3793 G.postRedisplay "dehighlight";
3795 | '/' | '?' ->
3796 let ondone isforw s =
3797 cbput state.hists.pat s;
3798 state.searchpattern <- s;
3799 search s isforw
3801 let s = String.create 1 in
3802 s.[0] <- c;
3803 enttext (s, "", Some (onhist state.hists.pat),
3804 textentry, ondone (c ='/'))
3806 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3807 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3808 setzoom (conf.zoom +. incr)
3810 | '+' ->
3811 let ondone s =
3812 let n =
3813 try int_of_string s with exc ->
3814 state.text <- Printf.sprintf "bad integer `%s': %s"
3815 s (Printexc.to_string exc);
3816 max_int
3818 if n != max_int
3819 then (
3820 conf.pagebias <- n;
3821 state.text <- "page bias is now " ^ string_of_int n;
3824 enttext ("page bias: ", "", None, intentry, ondone)
3826 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3827 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3828 setzoom (max 0.01 (conf.zoom -. decr))
3830 | '-' ->
3831 let ondone msg = state.text <- msg in
3832 enttext (
3833 "option [acfhilpstvAPRSZTI]: ", "", None,
3834 optentry state.mode, ondone
3837 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3838 setzoom 1.0
3840 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3841 let zoom = zoomforh conf.winw conf.winh state.scrollw in
3842 if zoom < 1.0
3843 then setzoom zoom
3845 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3846 togglebirdseye ()
3848 | '0' .. '9' ->
3849 let ondone s =
3850 let n =
3851 try int_of_string s with exc ->
3852 state.text <- Printf.sprintf "bad integer `%s': %s"
3853 s (Printexc.to_string exc);
3856 if n >= 0
3857 then (
3858 addnav ();
3859 cbput state.hists.pag (string_of_int n);
3860 gotoy_and_clear_text (getpagey (n + conf.pagebias - 1))
3863 let pageentry text key =
3864 match Char.unsafe_chr key with
3865 | 'g' -> TEdone text
3866 | _ -> intentry text key
3868 let text = "x" in text.[0] <- c;
3869 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
3871 | 'b' ->
3872 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
3873 reshape conf.winw conf.winh;
3875 | 'l' ->
3876 conf.hlinks <- not conf.hlinks;
3877 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3878 G.postRedisplay "toggle highlightlinks";
3880 | 'a' ->
3881 begin match state.autoscroll with
3882 | Some step ->
3883 conf.autoscrollstep <- step;
3884 state.autoscroll <- None
3885 | None ->
3886 if conf.autoscrollstep = 0
3887 then state.autoscroll <- Some 1
3888 else state.autoscroll <- Some conf.autoscrollstep
3891 | 'P' ->
3892 conf.presentation <- not conf.presentation;
3893 if conf.presentation
3894 then (
3895 if not conf.scrollbarinpm
3896 then state.scrollw <- 0;
3898 else
3899 state.scrollw <- conf.scrollbw;
3901 showtext ' ' ("presentation mode " ^
3902 if conf.presentation then "on" else "off");
3903 state.anchor <- getanchor ();
3904 represent ()
3906 | 'f' ->
3907 begin match state.fullscreen with
3908 | None ->
3909 state.fullscreen <- Some (conf.winw, conf.winh);
3910 Glut.fullScreen ()
3911 | Some (w, h) ->
3912 state.fullscreen <- None;
3913 doreshape w h
3916 | 'g' ->
3917 gotoy_and_clear_text 0
3919 | 'G' ->
3920 gotopage1 (state.pagecount - 1) 0
3922 | 'n' ->
3923 search state.searchpattern true
3925 | 'p' | 'N' ->
3926 search state.searchpattern false
3928 | 't' ->
3929 begin match state.layout with
3930 | [] -> ()
3931 | l :: _ ->
3932 gotoy_and_clear_text (getpagey l.pageno)
3935 | ' ' ->
3936 begin match List.rev state.layout with
3937 | [] -> ()
3938 | l :: _ ->
3939 let pageno = min (l.pageno+1) (state.pagecount-1) in
3940 gotoy_and_clear_text (getpagey pageno)
3943 | '\127' -> (* del *)
3944 begin match state.layout with
3945 | [] -> ()
3946 | l :: _ ->
3947 let pageno = max 0 (l.pageno-1) in
3948 gotoy_and_clear_text (getpagey pageno)
3951 | '=' ->
3952 showtext ' ' (describe_location ());
3954 | 'w' ->
3955 begin match state.layout with
3956 | [] -> ()
3957 | l :: _ ->
3958 doreshape (l.pagew + state.scrollw) l.pageh;
3959 G.postRedisplay "w"
3962 | '\'' ->
3963 enterbookmarkmode ()
3965 | 'h' ->
3966 enterhelpmode ()
3968 | 'i' ->
3969 enterinfomode ()
3971 | 'e' when conf.redirectstderr ->
3972 entermsgsmode ()
3974 | 'm' ->
3975 let ondone s =
3976 match state.layout with
3977 | l :: _ ->
3978 state.bookmarks <-
3979 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
3980 :: state.bookmarks
3981 | _ -> ()
3983 enttext ("bookmark: ", "", None, textentry, ondone)
3985 | '~' ->
3986 quickbookmark ();
3987 showtext ' ' "Quick bookmark added";
3989 | 'z' ->
3990 begin match state.layout with
3991 | l :: _ ->
3992 let rect = getpdimrect l.pagedimno in
3993 let w, h =
3994 if conf.crophack
3995 then
3996 (truncate (1.8 *. (rect.(1) -. rect.(0))),
3997 truncate (1.2 *. (rect.(3) -. rect.(0))))
3998 else
3999 (truncate (rect.(1) -. rect.(0)),
4000 truncate (rect.(3) -. rect.(0)))
4002 let w = truncate ((float w)*.conf.zoom)
4003 and h = truncate ((float h)*.conf.zoom) in
4004 if w != 0 && h != 0
4005 then (
4006 state.anchor <- getanchor ();
4007 doreshape (w + state.scrollw) (h + conf.interpagespace)
4009 G.postRedisplay "z";
4011 | [] -> ()
4014 | '\000' -> (* ctrl-2 *)
4015 let maxw = getmaxw () in
4016 if maxw > 0.0
4017 then setzoom (maxw /. float conf.winw)
4019 | '<' | '>' ->
4020 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
4022 | '[' | ']' ->
4023 conf.colorscale <-
4024 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
4026 G.postRedisplay "brightness";
4028 | 'k' ->
4029 begin match state.mode with
4030 | Birdseye beye -> upbirdseye beye
4031 | _ -> gotoy (clamp (-conf.scrollstep))
4034 | 'j' ->
4035 begin match state.mode with
4036 | Birdseye beye -> downbirdseye beye
4037 | _ -> gotoy (clamp conf.scrollstep)
4040 | 'r' ->
4041 state.anchor <- getanchor ();
4042 opendoc state.path state.password
4044 | 'v' when conf.debug ->
4045 state.rects <- [];
4046 List.iter (fun l ->
4047 match getopaque l.pageno with
4048 | None -> ()
4049 | Some opaque ->
4050 let x0, y0, x1, y1 = pagebbox opaque in
4051 let a,b = float x0, float y0 in
4052 let c,d = float x1, float y0 in
4053 let e,f = float x1, float y1 in
4054 let h,j = float x0, float y1 in
4055 let rect = (a,b,c,d,e,f,h,j) in
4056 debugrect rect;
4057 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4058 ) state.layout;
4059 G.postRedisplay "v";
4061 | _ ->
4062 vlog "huh? %d %c" key (Char.chr key);
4065 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
4066 match key with
4067 | 27 -> (* escape *)
4068 leavebirdseye beye true
4070 | 12 -> (* ctrl-l *)
4071 let y, h = getpageyh pageno in
4072 let top = (conf.winh - h) / 2 in
4073 gotoy (max 0 (y - top))
4075 | 13 -> (* enter *)
4076 leavebirdseye beye false
4078 | _ ->
4079 viewkeyboard key
4082 let keyboard ~key ~x ~y =
4083 ignore x;
4084 ignore y;
4085 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
4086 then wcmd "interrupt" []
4087 else state.uioh <- state.uioh#key key
4090 let birdseyespecial key ((conf, leftx, _, hooverpageno, anchor) as beye) =
4091 match key with
4092 | Glut.KEY_UP -> upbirdseye beye
4093 | Glut.KEY_DOWN -> downbirdseye beye
4095 | Glut.KEY_PAGE_UP ->
4096 begin match state.layout with
4097 | l :: _ ->
4098 if l.pagey != 0
4099 then (
4100 state.mode <- Birdseye (
4101 conf, leftx, l.pageno, hooverpageno, anchor
4103 gotopage1 l.pageno 0;
4105 else (
4106 let layout = layout (state.y-conf.winh) conf.winh in
4107 match layout with
4108 | [] -> gotoy (clamp (-conf.winh))
4109 | l :: _ ->
4110 state.mode <- Birdseye (
4111 conf, leftx, l.pageno, hooverpageno, anchor
4113 gotopage1 l.pageno 0
4116 | [] -> gotoy (clamp (-conf.winh))
4117 end;
4119 | Glut.KEY_PAGE_DOWN ->
4120 begin match List.rev state.layout with
4121 | l :: _ ->
4122 let layout = layout (state.y + conf.winh) conf.winh in
4123 begin match layout with
4124 | [] ->
4125 let incr = l.pageh - l.pagevh in
4126 if incr = 0
4127 then (
4128 state.mode <-
4129 Birdseye (
4130 conf, leftx, state.pagecount - 1, hooverpageno, anchor
4132 G.postRedisplay "birdseye pagedown";
4134 else gotoy (clamp (incr + conf.interpagespace*2));
4136 | l :: _ ->
4137 state.mode <-
4138 Birdseye (conf, leftx, l.pageno, hooverpageno, anchor);
4139 gotopage1 l.pageno 0;
4142 | [] -> gotoy (clamp conf.winh)
4143 end;
4145 | Glut.KEY_HOME ->
4146 state.mode <- Birdseye (conf, leftx, 0, hooverpageno, anchor);
4147 gotopage1 0 0
4149 | Glut.KEY_END ->
4150 let pageno = state.pagecount - 1 in
4151 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
4152 if not (pagevisible state.layout pageno)
4153 then
4154 let h =
4155 match List.rev state.pdims with
4156 | [] -> conf.winh
4157 | (_, _, h, _) :: _ -> h
4159 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4160 else G.postRedisplay "birdseye end";
4161 | _ -> ()
4164 let setautoscrollspeed step goingdown =
4165 let incr = max 1 ((abs step) / 2) in
4166 let incr = if goingdown then incr else -incr in
4167 let astep = step + incr in
4168 state.autoscroll <- Some astep;
4171 let special ~key ~x ~y =
4172 ignore x;
4173 ignore y;
4174 state.uioh <- state.uioh#special key
4177 let drawpage l =
4178 let color =
4179 match state.mode with
4180 | Textentry _ -> scalecolor 0.4
4181 | View -> scalecolor 1.0
4182 | Birdseye (_, _, pageno, hooverpageno, _) ->
4183 if l.pageno = hooverpageno
4184 then scalecolor 0.9
4185 else (
4186 if l.pageno = pageno
4187 then scalecolor 1.0
4188 else scalecolor 0.8
4191 drawtiles l color;
4192 begin match getopaque l.pageno with
4193 | Some opaque ->
4194 if tileready l l.pagex l.pagey
4195 then
4196 let x = l.pagedispx - l.pagex
4197 and y = l.pagedispy - l.pagey in
4198 postprocess opaque conf.hlinks x y;
4200 | _ -> ()
4201 end;
4204 let scrollindicator () =
4205 let sbw, ph, sh = state.uioh#scrollph in
4206 let sbh, pw, sw = state.uioh#scrollpw in
4208 GlDraw.color (0.64, 0.64, 0.64);
4209 GlDraw.rect
4210 (float (conf.winw - sbw), 0.)
4211 (float conf.winw, float conf.winh)
4213 GlDraw.rect
4214 (0., float (conf.winh - sbh))
4215 (float (conf.winw - state.scrollw - 1), float conf.winh)
4217 GlDraw.color (0.0, 0.0, 0.0);
4219 GlDraw.rect
4220 (float (conf.winw - sbw), ph)
4221 (float conf.winw, ph +. sh)
4223 GlDraw.rect
4224 (pw, float (conf.winh - sbh))
4225 (pw +. sw, float conf.winh)
4229 let pagetranslatepoint l x y =
4230 let dy = y - l.pagedispy in
4231 let y = dy + l.pagey in
4232 let dx = x - l.pagedispx in
4233 let x = dx + l.pagex in
4234 (x, y);
4237 let showsel () =
4238 match state.mstate with
4239 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4242 | Msel ((x0, y0), (x1, y1)) ->
4243 let rec loop = function
4244 | l :: ls ->
4245 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4246 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4247 then
4248 match getopaque l.pageno with
4249 | Some opaque ->
4250 let dx, dy = pagetranslatepoint l 0 0 in
4251 let x0 = x0 + dx
4252 and y0 = y0 + dy
4253 and x1 = x1 + dx
4254 and y1 = y1 + dy in
4255 GlMat.mode `modelview;
4256 GlMat.push ();
4257 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
4258 seltext opaque (x0, y0, x1, y1);
4259 GlMat.pop ();
4260 | _ -> ()
4261 else loop ls
4262 | [] -> ()
4264 loop state.layout
4267 let showrects () =
4268 Gl.enable `blend;
4269 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4270 GlDraw.polygon_mode `both `fill;
4271 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4272 List.iter
4273 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4274 List.iter (fun l ->
4275 if l.pageno = pageno
4276 then (
4277 let dx = float (l.pagedispx - l.pagex) in
4278 let dy = float (l.pagedispy - l.pagey) in
4279 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4280 GlDraw.begins `quads;
4282 GlDraw.vertex2 (x0+.dx, y0+.dy);
4283 GlDraw.vertex2 (x1+.dx, y1+.dy);
4284 GlDraw.vertex2 (x2+.dx, y2+.dy);
4285 GlDraw.vertex2 (x3+.dx, y3+.dy);
4287 GlDraw.ends ();
4289 ) state.layout
4290 ) state.rects
4292 Gl.disable `blend;
4295 let display () =
4296 GlClear.color (scalecolor2 conf.bgcolor);
4297 GlClear.clear [`color];
4298 List.iter drawpage state.layout;
4299 showrects ();
4300 showsel ();
4301 state.uioh#display;
4302 scrollindicator ();
4303 begin match state.mstate with
4304 | Mzoomrect ((x0, y0), (x1, y1)) ->
4305 Gl.enable `blend;
4306 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4307 GlDraw.polygon_mode `both `fill;
4308 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4309 GlDraw.rect (float x0, float y0)
4310 (float x1, float y1);
4311 Gl.disable `blend;
4312 | _ -> ()
4313 end;
4314 enttext ();
4315 Glut.swapBuffers ();
4318 let getunder x y =
4319 let rec f = function
4320 | l :: rest ->
4321 begin match getopaque l.pageno with
4322 | Some opaque ->
4323 let x0 = l.pagedispx in
4324 let x1 = x0 + l.pagevw in
4325 let y0 = l.pagedispy in
4326 let y1 = y0 + l.pagevh in
4327 if y >= y0 && y <= y1 && x >= x0 && x <= x1
4328 then
4329 let px, py = pagetranslatepoint l x y in
4330 match whatsunder opaque px py with
4331 | Unone -> f rest
4332 | under -> under
4333 else f rest
4334 | _ ->
4335 f rest
4337 | [] -> Unone
4339 f state.layout
4342 let zoomrect x y x1 y1 =
4343 let x0 = min x x1
4344 and x1 = max x x1
4345 and y0 = min y y1 in
4346 gotoy (state.y + y0);
4347 state.anchor <- getanchor ();
4348 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4349 let margin =
4350 if state.w < conf.winw - state.scrollw
4351 then (conf.winw - state.scrollw - state.w) / 2
4352 else 0
4354 state.x <- (state.x + margin) - x0;
4355 setzoom zoom;
4356 Glut.setCursor Glut.CURSOR_INHERIT;
4357 state.mstate <- Mnone;
4360 let scrollx x =
4361 let winw = conf.winw - state.scrollw - 1 in
4362 let s = float x /. float winw in
4363 let destx = truncate (float (state.w + winw) *. s) in
4364 state.x <- winw - destx;
4365 gotoy_and_clear_text state.y;
4366 state.mstate <- Mscrollx;
4369 let scrolly y =
4370 let s = float y /. float conf.winh in
4371 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4372 gotoy_and_clear_text desty;
4373 state.mstate <- Mscrolly;
4376 let viewmouse button bstate x y =
4377 match button with
4378 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4379 if Glut.getModifiers () land Glut.active_ctrl != 0
4380 then (
4381 match state.mstate with
4382 | Mzoom (oldn, i) ->
4383 if oldn = n
4384 then (
4385 if i = 2
4386 then
4387 let incr =
4388 match n with
4389 | 4 ->
4390 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4391 | _ ->
4392 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4394 let zoom = conf.zoom -. incr in
4395 setzoom zoom;
4396 state.mstate <- Mzoom (n, 0);
4397 else
4398 state.mstate <- Mzoom (n, i+1);
4400 else state.mstate <- Mzoom (n, 0)
4402 | _ -> state.mstate <- Mzoom (n, 0)
4404 else (
4405 match state.autoscroll with
4406 | Some step -> setautoscrollspeed step (n=4)
4407 | None ->
4408 let incr =
4409 if n = 3
4410 then -conf.scrollstep
4411 else conf.scrollstep
4413 let incr = incr * 2 in
4414 let y = clamp incr in
4415 gotoy_and_clear_text y
4418 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4419 if bstate = Glut.DOWN
4420 then (
4421 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4422 state.mstate <- Mpan (x, y)
4424 else
4425 state.mstate <- Mnone
4427 | Glut.RIGHT_BUTTON ->
4428 if bstate = Glut.DOWN
4429 then (
4430 Glut.setCursor Glut.CURSOR_CYCLE;
4431 let p = (x, y) in
4432 state.mstate <- Mzoomrect (p, p)
4434 else (
4435 match state.mstate with
4436 | Mzoomrect ((x0, y0), _) ->
4437 if abs (x-x0) > 10 && abs (y - y0) > 10
4438 then zoomrect x0 y0 x y
4439 else (
4440 state.mstate <- Mnone;
4441 Glut.setCursor Glut.CURSOR_INHERIT;
4442 G.postRedisplay "kill accidental zoom rect";
4444 | _ ->
4445 Glut.setCursor Glut.CURSOR_INHERIT;
4446 state.mstate <- Mnone
4449 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4450 if bstate = Glut.DOWN
4451 then
4452 let _, position, sh = state.uioh#scrollph in
4453 if y > truncate position && y < truncate (position +. sh)
4454 then state.mstate <- Mscrolly
4455 else scrolly y
4456 else
4457 state.mstate <- Mnone
4459 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4460 if bstate = Glut.DOWN
4461 then
4462 let _, position, sw = state.uioh#scrollpw in
4463 if x > truncate position && x < truncate (position +. sw)
4464 then state.mstate <- Mscrollx
4465 else scrollx x
4466 else
4467 state.mstate <- Mnone
4469 | Glut.LEFT_BUTTON ->
4470 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4471 begin match dest with
4472 | Ulinkgoto (pageno, top) ->
4473 if pageno >= 0
4474 then (
4475 addnav ();
4476 gotopage1 pageno top;
4479 | Ulinkuri s ->
4480 gotouri s
4482 | Unone when bstate = Glut.DOWN ->
4483 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4484 state.mstate <- Mpan (x, y);
4486 | Unone | Utext _ ->
4487 if bstate = Glut.DOWN
4488 then (
4489 if conf.angle mod 360 = 0
4490 then (
4491 state.mstate <- Msel ((x, y), (x, y));
4492 G.postRedisplay "mouse select";
4495 else (
4496 match state.mstate with
4497 | Mnone -> ()
4499 | Mzoom _ | Mscrollx | Mscrolly ->
4500 state.mstate <- Mnone
4502 | Mzoomrect ((x0, y0), _) ->
4503 zoomrect x0 y0 x y
4505 | Mpan _ ->
4506 Glut.setCursor Glut.CURSOR_INHERIT;
4507 state.mstate <- Mnone
4509 | Msel ((_, y0), (_, y1)) ->
4510 let f l =
4511 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4512 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4513 then
4514 match getopaque l.pageno with
4515 | Some opaque ->
4516 copysel opaque
4517 | _ -> ()
4519 List.iter f state.layout;
4520 copysel ""; (* ugly *)
4521 Glut.setCursor Glut.CURSOR_INHERIT;
4522 state.mstate <- Mnone;
4526 | _ -> ()
4529 let birdseyemouse button bstate x y
4530 (conf, leftx, _, hooverpageno, anchor) =
4531 match button with
4532 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4533 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4534 let rec loop = function
4535 | [] -> ()
4536 | l :: rest ->
4537 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4538 && x > margin && x < margin + l.pagew
4539 then (
4540 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4542 else loop rest
4544 loop state.layout
4545 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
4546 | _ -> ()
4549 let mouse bstate button x y =
4550 state.uioh <- state.uioh#button button bstate x y;
4553 let mouse ~button ~state ~x ~y = mouse state button x y;;
4555 let motion ~x ~y =
4556 state.uioh <- state.uioh#motion x y
4559 let pmotion ~x ~y =
4560 state.uioh <- state.uioh#pmotion x y;
4563 let uioh = object
4564 method display = ()
4566 method key key =
4567 begin match state.mode with
4568 | Textentry textentry -> textentrykeyboard key textentry
4569 | Birdseye birdseye -> birdseyekeyboard key birdseye
4570 | View -> viewkeyboard key
4571 end;
4572 state.uioh
4574 method special key =
4575 begin match state.mode with
4576 | View | (Birdseye _) when key = Glut.KEY_F9 ->
4577 togglebirdseye ()
4579 | Birdseye vals ->
4580 birdseyespecial key vals
4582 | View when key = Glut.KEY_F1 ->
4583 enterhelpmode ()
4585 | View ->
4586 begin match state.autoscroll with
4587 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
4588 setautoscrollspeed step (key = Glut.KEY_DOWN)
4590 | _ ->
4591 let y =
4592 match key with
4593 | Glut.KEY_F3 -> search state.searchpattern true; state.y
4594 | Glut.KEY_UP ->
4595 if Glut.getModifiers () land Glut.active_ctrl != 0
4596 then
4597 if Glut.getModifiers () land Glut.active_shift != 0
4598 then (setzoom state.prevzoom; state.y)
4599 else clamp (-conf.winh/2)
4600 else clamp (-conf.scrollstep)
4601 | Glut.KEY_DOWN ->
4602 if Glut.getModifiers () land Glut.active_ctrl != 0
4603 then
4604 if Glut.getModifiers () land Glut.active_shift != 0
4605 then (setzoom state.prevzoom; state.y)
4606 else clamp (conf.winh/2)
4607 else clamp (conf.scrollstep)
4608 | Glut.KEY_PAGE_UP ->
4609 if Glut.getModifiers () land Glut.active_ctrl != 0
4610 then
4611 match state.layout with
4612 | [] -> state.y
4613 | l :: _ -> state.y - l.pagey
4614 else
4615 clamp (-conf.winh)
4616 | Glut.KEY_PAGE_DOWN ->
4617 if Glut.getModifiers () land Glut.active_ctrl != 0
4618 then
4619 match List.rev state.layout with
4620 | [] -> state.y
4621 | l :: _ -> getpagey l.pageno
4622 else
4623 clamp conf.winh
4624 | Glut.KEY_HOME ->
4625 addnav ();
4627 | Glut.KEY_END ->
4628 addnav ();
4629 state.maxy - (if conf.maxhfit then conf.winh else 0)
4631 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
4632 Glut.getModifiers () land Glut.active_alt != 0 ->
4633 getnav (if key = Glut.KEY_LEFT then 1 else -1)
4635 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
4636 let dx =
4637 if Glut.getModifiers () land Glut.active_ctrl != 0
4638 then (conf.winw / 2)
4639 else 10
4641 state.x <- state.x - dx;
4642 state.y
4643 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
4644 let dx =
4645 if Glut.getModifiers () land Glut.active_ctrl != 0
4646 then (conf.winw / 2)
4647 else 10
4649 state.x <- state.x + dx;
4650 state.y
4652 | _ -> state.y
4654 gotoy_and_clear_text y
4657 | Textentry te -> textentryspecial key te
4658 end;
4659 state.uioh
4661 method button button bstate x y =
4662 begin match state.mode with
4663 | View -> viewmouse button bstate x y
4664 | Birdseye beye -> birdseyemouse button bstate x y beye
4665 | Textentry _ -> ()
4666 end;
4667 state.uioh
4669 method motion x y =
4670 begin match state.mode with
4671 | Textentry _ -> ()
4672 | View | Birdseye _ ->
4673 match state.mstate with
4674 | Mzoom _ | Mnone -> ()
4676 | Mpan (x0, y0) ->
4677 let dx = x - x0
4678 and dy = y0 - y in
4679 state.mstate <- Mpan (x, y);
4680 if conf.zoom > 1.0 then state.x <- state.x + dx;
4681 let y = clamp dy in
4682 gotoy_and_clear_text y
4684 | Msel (a, _) ->
4685 state.mstate <- Msel (a, (x, y));
4686 G.postRedisplay "motion select";
4688 | Mscrolly ->
4689 let y = min conf.winh (max 0 y) in
4690 scrolly y
4692 | Mscrollx ->
4693 let x = min conf.winw (max 0 x) in
4694 scrollx x
4696 | Mzoomrect (p0, _) ->
4697 state.mstate <- Mzoomrect (p0, (x, y));
4698 G.postRedisplay "motion zoomrect";
4699 end;
4700 state.uioh
4702 method pmotion x y =
4703 begin match state.mode with
4704 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4705 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4706 let rec loop = function
4707 | [] ->
4708 if hooverpageno != -1
4709 then (
4710 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4711 G.postRedisplay "pmotion birdseye no hoover";
4713 | l :: rest ->
4714 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4715 && x > margin && x < margin + l.pagew
4716 then (
4717 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4718 G.postRedisplay "pmotion birdseye hoover";
4720 else loop rest
4722 loop state.layout
4724 | Textentry _ -> ()
4726 | View ->
4727 match state.mstate with
4728 | Mnone ->
4729 begin match getunder x y with
4730 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
4731 | Ulinkuri uri ->
4732 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
4733 Glut.setCursor Glut.CURSOR_INFO
4734 | Ulinkgoto (page, _) ->
4735 if conf.underinfo
4736 then showtext 'p' ("age: " ^ string_of_int (page+1));
4737 Glut.setCursor Glut.CURSOR_INFO
4738 | Utext s ->
4739 if conf.underinfo then showtext 'f' ("ont: " ^ s);
4740 Glut.setCursor Glut.CURSOR_TEXT
4743 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
4745 end;
4746 state.uioh
4748 method infochanged _ = ()
4750 method scrollph =
4751 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
4752 let p, h = scrollph state.y maxy in
4753 state.scrollw, p, h
4755 method scrollpw =
4756 let winw = conf.winw - state.scrollw - 1 in
4757 let fwinw = float winw in
4758 let sw =
4759 let sw = fwinw /. float state.w in
4760 let sw = fwinw *. sw in
4761 max sw (float conf.scrollh)
4763 let position, sw =
4764 let f = state.w+winw in
4765 let r = float (winw-state.x) /. float f in
4766 let p = fwinw *. r in
4767 p-.sw/.2., sw
4769 let sw =
4770 if position +. sw > fwinw
4771 then fwinw -. position
4772 else sw
4774 state.hscrollh, position, sw
4775 end;;
4777 module Config =
4778 struct
4779 open Parser
4781 let fontpath = ref "";;
4782 let wmclasshack = ref false;;
4784 let unent s =
4785 let l = String.length s in
4786 let b = Buffer.create l in
4787 unent b s 0 l;
4788 Buffer.contents b;
4791 let home =
4793 match platform with
4794 | Pwindows | Pmingw -> Sys.getenv "HOMEPATH"
4795 | _ -> Sys.getenv "HOME"
4796 with exn ->
4797 prerr_endline
4798 ("Can not determine home directory location: " ^
4799 Printexc.to_string exn);
4803 let config_of c attrs =
4804 let apply c k v =
4806 match k with
4807 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
4808 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
4809 | "case-insensitive-search" -> { c with icase = bool_of_string v }
4810 | "preload" -> { c with preload = bool_of_string v }
4811 | "page-bias" -> { c with pagebias = int_of_string v }
4812 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
4813 | "auto-scroll-step" ->
4814 { c with autoscrollstep = max 0 (int_of_string v) }
4815 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
4816 | "crop-hack" -> { c with crophack = bool_of_string v }
4817 | "throttle" ->
4818 let mw =
4819 match String.lowercase v with
4820 | "true" -> Some infinity
4821 | "false" -> None
4822 | f -> Some (float_of_string f)
4824 { c with maxwait = mw}
4825 | "highlight-links" -> { c with hlinks = bool_of_string v }
4826 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
4827 | "vertical-margin" ->
4828 { c with interpagespace = max 0 (int_of_string v) }
4829 | "zoom" ->
4830 let zoom = float_of_string v /. 100. in
4831 let zoom = max zoom 0.0 in
4832 { c with zoom = zoom }
4833 | "presentation" -> { c with presentation = bool_of_string v }
4834 | "rotation-angle" -> { c with angle = int_of_string v }
4835 | "width" -> { c with winw = max 20 (int_of_string v) }
4836 | "height" -> { c with winh = max 20 (int_of_string v) }
4837 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
4838 | "proportional-display" -> { c with proportional = bool_of_string v }
4839 | "pixmap-cache-size" ->
4840 { c with memlimit = max 2 (int_of_string_with_suffix v) }
4841 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
4842 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
4843 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
4844 | "persistent-location" -> { c with jumpback = bool_of_string v }
4845 | "background-color" -> { c with bgcolor = color_of_string v }
4846 | "scrollbar-in-presentation" ->
4847 { c with scrollbarinpm = bool_of_string v }
4848 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
4849 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
4850 | "mupdf-memlimit" ->
4851 { c with mumemlimit = max 1024 (int_of_string_with_suffix v) }
4852 | "checkers" -> { c with checkers = bool_of_string v }
4853 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
4854 | "trim-margins" -> { c with trimmargins = bool_of_string v }
4855 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
4856 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
4857 | "uri-launcher" -> { c with urilauncher = unent v }
4858 | "color-space" -> { c with colorspace = colorspace_of_string v }
4859 | "invert-colors" -> { c with invert = bool_of_string v }
4860 | "brightness" -> { c with colorscale = float_of_string v }
4861 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
4862 | _ -> c
4863 with exn ->
4864 prerr_endline ("Error processing attribute (`" ^
4865 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
4868 let rec fold c = function
4869 | [] -> c
4870 | (k, v) :: rest ->
4871 let c = apply c k v in
4872 fold c rest
4874 fold c attrs;
4877 let fromstring f pos n v d =
4878 try f v
4879 with exn ->
4880 dolog "Error processing attribute (%S=%S) at %d\n%s"
4881 n v pos (Printexc.to_string exn)
4886 let bookmark_of attrs =
4887 let rec fold title page rely = function
4888 | ("title", v) :: rest -> fold v page rely rest
4889 | ("page", v) :: rest -> fold title v rely rest
4890 | ("rely", v) :: rest -> fold title page v rest
4891 | _ :: rest -> fold title page rely rest
4892 | [] -> title, page, rely
4894 fold "invalid" "0" "0" attrs
4897 let doc_of attrs =
4898 let rec fold path page rely pan = function
4899 | ("path", v) :: rest -> fold v page rely pan rest
4900 | ("page", v) :: rest -> fold path v rely pan rest
4901 | ("rely", v) :: rest -> fold path page v pan rest
4902 | ("pan", v) :: rest -> fold path page rely v rest
4903 | _ :: rest -> fold path page rely pan rest
4904 | [] -> path, page, rely, pan
4906 fold "" "0" "0" "0" attrs
4909 let setconf dst src =
4910 dst.scrollbw <- src.scrollbw;
4911 dst.scrollh <- src.scrollh;
4912 dst.icase <- src.icase;
4913 dst.preload <- src.preload;
4914 dst.pagebias <- src.pagebias;
4915 dst.verbose <- src.verbose;
4916 dst.scrollstep <- src.scrollstep;
4917 dst.maxhfit <- src.maxhfit;
4918 dst.crophack <- src.crophack;
4919 dst.autoscrollstep <- src.autoscrollstep;
4920 dst.maxwait <- src.maxwait;
4921 dst.hlinks <- src.hlinks;
4922 dst.underinfo <- src.underinfo;
4923 dst.interpagespace <- src.interpagespace;
4924 dst.zoom <- src.zoom;
4925 dst.presentation <- src.presentation;
4926 dst.angle <- src.angle;
4927 dst.winw <- src.winw;
4928 dst.winh <- src.winh;
4929 dst.savebmarks <- src.savebmarks;
4930 dst.memlimit <- src.memlimit;
4931 dst.proportional <- src.proportional;
4932 dst.texcount <- src.texcount;
4933 dst.sliceheight <- src.sliceheight;
4934 dst.thumbw <- src.thumbw;
4935 dst.jumpback <- src.jumpback;
4936 dst.bgcolor <- src.bgcolor;
4937 dst.scrollbarinpm <- src.scrollbarinpm;
4938 dst.tilew <- src.tilew;
4939 dst.tileh <- src.tileh;
4940 dst.mumemlimit <- src.mumemlimit;
4941 dst.checkers <- src.checkers;
4942 dst.aalevel <- src.aalevel;
4943 dst.trimmargins <- src.trimmargins;
4944 dst.trimfuzz <- src.trimfuzz;
4945 dst.urilauncher <- src.urilauncher;
4946 dst.colorspace <- src.colorspace;
4947 dst.invert <- src.invert;
4948 dst.colorscale <- src.colorscale;
4949 dst.redirectstderr <- src.redirectstderr;
4952 let get s =
4953 let h = Hashtbl.create 10 in
4954 let dc = { defconf with angle = defconf.angle } in
4955 let rec toplevel v t spos _ =
4956 match t with
4957 | Vdata | Vcdata | Vend -> v
4958 | Vopen ("llppconfig", _, closed) ->
4959 if closed
4960 then v
4961 else { v with f = llppconfig }
4962 | Vopen _ ->
4963 error "unexpected subelement at top level" s spos
4964 | Vclose _ -> error "unexpected close at top level" s spos
4966 and llppconfig v t spos _ =
4967 match t with
4968 | Vdata | Vcdata -> v
4969 | Vend -> error "unexpected end of input in llppconfig" s spos
4970 | Vopen ("defaults", attrs, closed) ->
4971 let c = config_of dc attrs in
4972 setconf dc c;
4973 if closed
4974 then v
4975 else { v with f = skip "defaults" (fun () -> v) }
4977 | Vopen ("ui-font", attrs, closed) ->
4978 let rec getsize size = function
4979 | [] -> size
4980 | ("size", v) :: rest ->
4981 let size =
4982 fromstring int_of_string spos "size" v fstate.fontsize in
4983 getsize size rest
4984 | l -> getsize size l
4986 fstate.fontsize <- getsize fstate.fontsize attrs;
4987 if closed
4988 then v
4989 else { v with f = uifont (Buffer.create 10) }
4991 | Vopen ("doc", attrs, closed) ->
4992 let pathent, spage, srely, span = doc_of attrs in
4993 let path = unent pathent
4994 and pageno = fromstring int_of_string spos "page" spage 0
4995 and rely = fromstring float_of_string spos "rely" srely 0.0
4996 and pan = fromstring int_of_string spos "pan" span 0 in
4997 let c = config_of dc attrs in
4998 let anchor = (pageno, rely) in
4999 if closed
5000 then (Hashtbl.add h path (c, [], pan, anchor); v)
5001 else { v with f = doc path pan anchor c [] }
5003 | Vopen _ ->
5004 error "unexpected subelement in llppconfig" s spos
5006 | Vclose "llppconfig" -> { v with f = toplevel }
5007 | Vclose _ -> error "unexpected close in llppconfig" s spos
5009 and uifont b v t spos epos =
5010 match t with
5011 | Vdata | Vcdata ->
5012 Buffer.add_substring b s spos (epos - spos);
5014 | Vopen (_, _, _) ->
5015 error "unexpected subelement in ui-font" s spos
5016 | Vclose "ui-font" ->
5017 if String.length !fontpath = 0
5018 then fontpath := Buffer.contents b;
5019 { v with f = llppconfig }
5020 | Vclose _ -> error "unexpected close in ui-font" s spos
5021 | Vend -> error "unexpected end of input in ui-font" s spos
5023 and doc path pan anchor c bookmarks v t spos _ =
5024 match t with
5025 | Vdata | Vcdata -> v
5026 | Vend -> error "unexpected end of input in doc" s spos
5027 | Vopen ("bookmarks", _, closed) ->
5028 if closed
5029 then v
5030 else { v with f = pbookmarks path pan anchor c bookmarks }
5032 | Vopen (_, _, _) ->
5033 error "unexpected subelement in doc" s spos
5035 | Vclose "doc" ->
5036 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5037 { v with f = llppconfig }
5039 | Vclose _ -> error "unexpected close in doc" s spos
5041 and pbookmarks path pan anchor c bookmarks v t spos _ =
5042 match t with
5043 | Vdata | Vcdata -> v
5044 | Vend -> error "unexpected end of input in bookmarks" s spos
5045 | Vopen ("item", attrs, closed) ->
5046 let titleent, spage, srely = bookmark_of attrs in
5047 let page = fromstring int_of_string spos "page" spage 0
5048 and rely = fromstring float_of_string spos "rely" srely 0.0 in
5049 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
5050 if closed
5051 then { v with f = pbookmarks path pan anchor c bookmarks }
5052 else
5053 let f () = v in
5054 { v with f = skip "item" f }
5056 | Vopen _ ->
5057 error "unexpected subelement in bookmarks" s spos
5059 | Vclose "bookmarks" ->
5060 { v with f = doc path pan anchor c bookmarks }
5062 | Vclose _ -> error "unexpected close in bookmarks" s spos
5064 and skip tag f v t spos _ =
5065 match t with
5066 | Vdata | Vcdata -> v
5067 | Vend ->
5068 error ("unexpected end of input in skipped " ^ tag) s spos
5069 | Vopen (tag', _, closed) ->
5070 if closed
5071 then v
5072 else
5073 let f' () = { v with f = skip tag f } in
5074 { v with f = skip tag' f' }
5075 | Vclose ctag ->
5076 if tag = ctag
5077 then f ()
5078 else error ("unexpected close in skipped " ^ tag) s spos
5081 parse { f = toplevel; accu = () } s;
5082 h, dc;
5085 let do_load f ic =
5087 let len = in_channel_length ic in
5088 let s = String.create len in
5089 really_input ic s 0 len;
5090 f s;
5091 with
5092 | Parse_error (msg, s, pos) ->
5093 let subs = subs s pos in
5094 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
5095 failwith ("parse error: " ^ s)
5097 | exn ->
5098 failwith ("config load error: " ^ Printexc.to_string exn)
5101 let defconfpath =
5102 let dir =
5104 let dir = Filename.concat home ".config" in
5105 if Sys.is_directory dir then dir else home
5106 with _ -> home
5108 Filename.concat dir "llpp.conf"
5111 let confpath = ref defconfpath;;
5113 let load1 f =
5114 if Sys.file_exists !confpath
5115 then
5116 match
5117 (try Some (open_in_bin !confpath)
5118 with exn ->
5119 prerr_endline
5120 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5121 Printexc.to_string exn);
5122 None
5124 with
5125 | Some ic ->
5126 begin try
5127 f (do_load get ic)
5128 with exn ->
5129 prerr_endline
5130 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5131 Printexc.to_string exn);
5132 end;
5133 close_in ic;
5135 | None -> ()
5136 else
5137 f (Hashtbl.create 0, defconf)
5140 let load () =
5141 let f (h, dc) =
5142 let pc, pb, px, pa =
5144 Hashtbl.find h (Filename.basename state.path)
5145 with Not_found -> dc, [], 0, (0, 0.0)
5147 setconf defconf dc;
5148 setconf conf pc;
5149 state.bookmarks <- pb;
5150 state.x <- px;
5151 state.scrollw <- conf.scrollbw;
5152 if conf.jumpback
5153 then state.anchor <- pa;
5154 cbput state.hists.nav pa;
5156 load1 f
5159 let add_attrs bb always dc c =
5160 let ob s a b =
5161 if always || a != b
5162 then Printf.bprintf bb "\n %s='%b'" s a
5163 and oi s a b =
5164 if always || a != b
5165 then Printf.bprintf bb "\n %s='%d'" s a
5166 and oI s a b =
5167 if always || a != b
5168 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5169 and oz s a b =
5170 if always || a <> b
5171 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5172 and oF s a b =
5173 if always || a <> b
5174 then Printf.bprintf bb "\n %s='%f'" s a
5175 and oc s a b =
5176 if always || a <> b
5177 then
5178 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5179 and oC s a b =
5180 if always || a <> b
5181 then
5182 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5183 and oR s a b =
5184 if always || a <> b
5185 then
5186 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5187 and os s a b =
5188 if always || a <> b
5189 then
5190 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5191 and oW s a b =
5192 if always || a <> b
5193 then
5194 let v =
5195 match a with
5196 | None -> "false"
5197 | Some f ->
5198 if f = infinity
5199 then "true"
5200 else string_of_float f
5202 Printf.bprintf bb "\n %s='%s'" s v
5204 let w, h =
5205 if always
5206 then dc.winw, dc.winh
5207 else
5208 match state.fullscreen with
5209 | Some wh -> wh
5210 | None -> c.winw, c.winh
5212 let zoom, presentation, interpagespace, maxwait =
5213 if always
5214 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
5215 else
5216 match state.mode with
5217 | Birdseye (bc, _, _, _, _) ->
5218 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
5219 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
5221 oi "width" w dc.winw;
5222 oi "height" h dc.winh;
5223 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
5224 oi "scroll-handle-height" c.scrollh dc.scrollh;
5225 ob "case-insensitive-search" c.icase dc.icase;
5226 ob "preload" c.preload dc.preload;
5227 oi "page-bias" c.pagebias dc.pagebias;
5228 oi "scroll-step" c.scrollstep dc.scrollstep;
5229 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
5230 ob "max-height-fit" c.maxhfit dc.maxhfit;
5231 ob "crop-hack" c.crophack dc.crophack;
5232 oW "throttle" maxwait dc.maxwait;
5233 ob "highlight-links" c.hlinks dc.hlinks;
5234 ob "under-cursor-info" c.underinfo dc.underinfo;
5235 oi "vertical-margin" interpagespace dc.interpagespace;
5236 oz "zoom" zoom dc.zoom;
5237 ob "presentation" presentation dc.presentation;
5238 oi "rotation-angle" c.angle dc.angle;
5239 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
5240 ob "proportional-display" c.proportional dc.proportional;
5241 oI "pixmap-cache-size" c.memlimit dc.memlimit;
5242 oi "tex-count" c.texcount dc.texcount;
5243 oi "slice-height" c.sliceheight dc.sliceheight;
5244 oi "thumbnail-width" c.thumbw dc.thumbw;
5245 ob "persistent-location" c.jumpback dc.jumpback;
5246 oc "background-color" c.bgcolor dc.bgcolor;
5247 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
5248 oi "tile-width" c.tilew dc.tilew;
5249 oi "tile-height" c.tileh dc.tileh;
5250 oI "mupdf-memlimit" c.mumemlimit dc.mumemlimit;
5251 ob "checkers" c.checkers dc.checkers;
5252 oi "aalevel" c.aalevel dc.aalevel;
5253 ob "trim-margins" c.trimmargins dc.trimmargins;
5254 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
5255 os "uri-launcher" c.urilauncher dc.urilauncher;
5256 oC "color-space" c.colorspace dc.colorspace;
5257 ob "invert-colors" c.invert dc.invert;
5258 oF "brightness" c.colorscale dc.colorscale;
5259 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
5260 if always
5261 then ob "wmclass-hack" !wmclasshack false;
5264 let save () =
5265 let uifontsize = fstate.fontsize in
5266 let bb = Buffer.create 32768 in
5267 let f (h, dc) =
5268 let dc = if conf.bedefault then conf else dc in
5269 Buffer.add_string bb "<llppconfig>\n";
5271 if String.length !fontpath > 0
5272 then
5273 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
5274 uifontsize
5275 !fontpath
5276 else (
5277 if uifontsize <> 14
5278 then
5279 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
5282 Buffer.add_string bb "<defaults ";
5283 add_attrs bb true dc dc;
5284 Buffer.add_string bb "/>\n";
5286 let adddoc path pan anchor c bookmarks =
5287 if bookmarks == [] && c = dc && anchor = emptyanchor
5288 then ()
5289 else (
5290 Printf.bprintf bb "<doc path='%s'"
5291 (enent path 0 (String.length path));
5293 if anchor <> emptyanchor
5294 then (
5295 let n, y = anchor in
5296 Printf.bprintf bb " page='%d'" n;
5297 if y > 1e-6
5298 then
5299 Printf.bprintf bb " rely='%f'" y
5303 if pan != 0
5304 then Printf.bprintf bb " pan='%d'" pan;
5306 add_attrs bb false dc c;
5308 begin match bookmarks with
5309 | [] -> Buffer.add_string bb "/>\n"
5310 | _ ->
5311 Buffer.add_string bb ">\n<bookmarks>\n";
5312 List.iter (fun (title, _level, (page, rely)) ->
5313 Printf.bprintf bb
5314 "<item title='%s' page='%d'"
5315 (enent title 0 (String.length title))
5316 page
5318 if rely > 1e-6
5319 then
5320 Printf.bprintf bb " rely='%f'" rely
5322 Buffer.add_string bb "/>\n";
5323 ) bookmarks;
5324 Buffer.add_string bb "</bookmarks>\n</doc>\n";
5325 end;
5329 let pan =
5330 match state.mode with
5331 | Birdseye (_, pan, _, _, _) -> pan
5332 | _ -> state.x
5334 let basename = Filename.basename state.path in
5335 adddoc basename pan (getanchor ())
5336 { conf with
5337 autoscrollstep =
5338 match state.autoscroll with
5339 | Some step -> step
5340 | None -> conf.autoscrollstep }
5341 (if conf.savebmarks then state.bookmarks else []);
5343 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
5344 if basename <> path
5345 then adddoc path x y c bookmarks
5346 ) h;
5347 Buffer.add_string bb "</llppconfig>";
5349 load1 f;
5350 if Buffer.length bb > 0
5351 then
5353 let tmp = !confpath ^ ".tmp" in
5354 let oc = open_out_bin tmp in
5355 Buffer.output_buffer oc bb;
5356 close_out oc;
5357 Unix.rename tmp !confpath;
5358 with exn ->
5359 prerr_endline
5360 ("error while saving configuration: " ^ Printexc.to_string exn)
5362 end;;
5364 let () =
5365 Arg.parse
5366 (Arg.align
5367 [("-p", Arg.String (fun s -> state.password <- s) ,
5368 "<password> Set password");
5370 ("-f", Arg.String (fun s -> Config.fontpath := s),
5371 "<path> Set path to the user interface font");
5373 ("-c", Arg.String (fun s -> Config.confpath := s),
5374 "<path> Set path to the configuration file");
5376 ("-v", Arg.Unit (fun () ->
5377 Printf.printf
5378 "%s\nconfiguration path: %s\n"
5379 (version ())
5380 Config.defconfpath
5382 exit 0), " Print version and exit");
5385 (fun s -> state.path <- s)
5386 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
5388 if String.length state.path = 0
5389 then (prerr_endline "file name missing"; exit 1);
5391 Config.load ();
5393 let _ = Glut.init Sys.argv in
5394 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
5395 let () = Glut.initWindowSize conf.winw conf.winh in
5396 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
5398 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
5399 || Glut.extensionSupported "GL_EXT_texture_rectangle")
5400 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
5402 let csock, ssock =
5403 if not is_windows
5404 then
5405 Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0
5406 else
5407 let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, 1337) in
5408 let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5409 Unix.setsockopt sock Unix.SO_REUSEADDR true;
5410 Unix.bind sock addr;
5411 Unix.listen sock 1;
5412 let csock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5413 Unix.connect csock addr;
5414 let ssock, _ = Unix.accept sock in
5415 Unix.close sock;
5416 let opts sock =
5417 Unix.setsockopt sock Unix.TCP_NODELAY true;
5418 Unix.setsockopt_optint sock Unix.SO_LINGER None;
5420 opts ssock;
5421 opts csock;
5422 ssock, csock
5425 let () = Glut.displayFunc display in
5426 let () = Glut.reshapeFunc reshape in
5427 let () = Glut.keyboardFunc keyboard in
5428 let () = Glut.specialFunc special in
5429 let () = Glut.idleFunc (Some idle) in
5430 let () = Glut.mouseFunc mouse in
5431 let () = Glut.motionFunc motion in
5432 let () = Glut.passiveMotionFunc pmotion in
5434 setcheckers conf.checkers;
5435 init ssock (
5436 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5437 conf.texcount, conf.sliceheight, conf.mumemlimit, conf.colorspace,
5438 !Config.wmclasshack, !Config.fontpath
5440 state.csock <- csock;
5441 state.ssock <- ssock;
5442 state.text <- "Opening " ^ state.path;
5443 setaalevel conf.aalevel;
5444 writeopen state.path state.password;
5445 state.uioh <- uioh;
5446 setfontsize fstate.fontsize;
5448 redirectstderr ();
5450 while true do
5452 Glut.mainLoop ();
5453 with
5454 | Glut.BadEnum "key in special_of_int" ->
5455 showtext '!' " LablGlut bug: special key not recognized";
5457 | Quit ->
5458 wcmd "quit" [];
5459 Config.save ();
5460 exit 0
5462 | exn when conf.redirectstderr ->
5463 let s =
5464 Printf.sprintf "exception %s\n%s"
5465 (Printexc.to_string exn)
5466 (Printexc.get_backtrace ())
5468 ignore (try
5469 Unix.single_write state.stderr s 0 (String.length s);
5470 with _ -> 0);
5471 exit 1
5472 done;