Usability tweak
[llpp.git] / main.ml
bloba71184a6f58545ee14d5637044fa2bc61ea482f0
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 dolog2 fmt = Printf.kprintf print_endline fmt;;
10 let now = Unix.gettimeofday;;
12 exception Quit;;
14 type params = (angle * proportional * trimparams
15 * texcount * sliceheight * memsize
16 * colorspace * wmclasshack * fontpath)
17 and pageno = int
18 and width = int
19 and height = int
20 and leftx = int
21 and opaque = string
22 and recttype = int
23 and pixmapsize = int
24 and angle = int
25 and proportional = bool
26 and trimmargins = bool
27 and interpagespace = int
28 and texcount = int
29 and sliceheight = int
30 and gen = int
31 and top = float
32 and fontpath = string
33 and memsize = int
34 and aalevel = int
35 and wmclasshack = bool
36 and irect = (int * int * int * int)
37 and trimparams = (trimmargins * irect)
38 and colorspace = | Rgb | Bgr | Gray
41 type platform = | Punknown | Plinux | Pwindows | Posx | Psun
42 | Pfreebsd | Pdragonflybsd | Popenbsd | Pmingw | Pcygwin;;
44 external init : Unix.file_descr -> params -> unit = "ml_init";;
45 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
46 external copysel : string -> unit = "ml_copysel";;
47 external getpdimrect : int -> float array = "ml_getpdimrect";;
48 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
49 external zoomforh : int -> int -> int -> float = "ml_zoom_for_height";;
50 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
51 external measurestr : int -> string -> float = "ml_measure_string";;
52 external getmaxw : unit -> float = "ml_getmaxw";;
53 external postprocess : opaque -> bool -> int -> int -> unit = "ml_postprocess";;
54 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
55 external platform : unit -> platform = "ml_platform";;
56 external setaalevel : int -> unit = "ml_setaalevel";;
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 -> string -> 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 end;;
290 type mode =
291 | Birdseye of (conf * leftx * pageno * pageno * anchor)
292 | Textentry of (textentry * onleave)
293 | View
294 and onleave = leavetextentrystatus -> unit
295 and leavetextentrystatus = | Cancel | Confirm
296 and helpitem = string * int * action
297 and action =
298 | Noaction
299 | Action of (uioh -> uioh)
302 let isbirdseye = function Birdseye _ -> true | _ -> false;;
303 let istextentry = function Textentry _ -> true | _ -> false;;
305 type currently =
306 | Idle
307 | Loading of (page * gen)
308 | Tiling of (
309 page * opaque * colorspace * angle * gen * col * row * width * height
311 | Outlining of outline list
314 let nouioh : uioh = object (self)
315 method display = ()
316 method key _ = self
317 method special _ = self
318 method button _ _ _ _ = self
319 method motion _ _ = self
320 method pmotion _ _ = self
321 method infochanged _ = ()
322 end;;
324 type state =
325 { mutable csock : Unix.file_descr
326 ; mutable ssock : Unix.file_descr
327 ; mutable errfd : Unix.file_descr
328 ; mutable stderr : Unix.file_descr
329 ; mutable errmsgs : Buffer.t
330 ; mutable newerrmsgs : bool
331 ; mutable w : int
332 ; mutable x : int
333 ; mutable y : int
334 ; mutable scrollw : int
335 ; mutable hscrollh : int
336 ; mutable anchor : anchor
337 ; mutable maxy : int
338 ; mutable layout : page list
339 ; pagemap : (pagemapkey, opaque) Hashtbl.t
340 ; tilemap : (tilemapkey, tile) Hashtbl.t
341 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
342 ; mutable pdims : (pageno * width * height * leftx) list
343 ; mutable pagecount : int
344 ; mutable currently : currently
345 ; mutable mstate : mstate
346 ; mutable searchpattern : string
347 ; mutable rects : (pageno * recttype * rect) list
348 ; mutable rects1 : (pageno * recttype * rect) list
349 ; mutable text : string
350 ; mutable fullscreen : (width * height) option
351 ; mutable mode : mode
352 ; mutable uioh : uioh
353 ; mutable outlines : outline array
354 ; mutable bookmarks : outline list
355 ; mutable path : string
356 ; mutable password : string
357 ; mutable invalidated : int
358 ; mutable memused : memsize
359 ; mutable gen : gen
360 ; mutable throttle : (page list * int * float) option
361 ; mutable autoscroll : int option
362 ; mutable help : helpitem array
363 ; mutable docinfo : (int * string) list
364 ; mutable deadline : float
365 ; mutable texid : GlTex.texture_id option
366 ; hists : hists
367 ; mutable prevzoom : float
368 ; mutable progress : float
370 and hists =
371 { pat : string circbuf
372 ; pag : string circbuf
373 ; nav : anchor circbuf
377 let defconf =
378 { scrollbw = 7
379 ; scrollh = 12
380 ; icase = true
381 ; preload = true
382 ; pagebias = 0
383 ; verbose = false
384 ; debug = false
385 ; scrollstep = 24
386 ; maxhfit = true
387 ; crophack = false
388 ; autoscrollstep = 2
389 ; maxwait = None
390 ; hlinks = false
391 ; underinfo = false
392 ; interpagespace = 2
393 ; zoom = 1.0
394 ; presentation = false
395 ; angle = 0
396 ; winw = 900
397 ; winh = 900
398 ; savebmarks = true
399 ; proportional = true
400 ; trimmargins = false
401 ; trimfuzz = (0,0,0,0)
402 ; memlimit = 32 lsl 20
403 ; texcount = 256
404 ; sliceheight = 24
405 ; thumbw = 76
406 ; jumpback = true
407 ; bgcolor = (0.5, 0.5, 0.5)
408 ; bedefault = false
409 ; scrollbarinpm = true
410 ; tilew = 2048
411 ; tileh = 2048
412 ; mumemlimit = 128 lsl 20
413 ; checkers = true
414 ; aalevel = 8
415 ; urilauncher =
416 (match platform with
417 | Plinux | Pfreebsd | Pdragonflybsd | Popenbsd | Psun -> "xdg-open \"%s\""
418 | Posx -> "open \"%s\""
419 | Pwindows | Pcygwin | Pmingw -> "iexplore \"%s\""
420 | _ -> "")
421 ; colorspace = Rgb
422 ; invert = false
423 ; colorscale = 1.0
424 ; redirectstderr = false
428 let conf = { defconf with angle = defconf.angle };;
430 type fontstate =
431 { mutable fontsize : int
432 ; mutable wwidth : float
433 ; mutable maxrows : int
437 let fstate =
438 { fontsize = 14
439 ; wwidth = nan
440 ; maxrows = -1
444 let setfontsize n =
445 fstate.fontsize <- n;
446 fstate.wwidth <- measurestr fstate.fontsize "w";
447 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
450 let gotouri uri =
451 if String.length conf.urilauncher = 0
452 then print_endline uri
453 else
454 let re = Str.regexp "%s" in
455 let command = Str.global_replace re uri conf.urilauncher in
456 let optic =
457 try Some (Unix.open_process_in command)
458 with exn ->
459 Printf.eprintf
460 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
461 flush stderr;
462 None
464 match optic with
465 | Some ic -> close_in ic
466 | None -> ()
469 let version () =
470 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
471 (platform_to_string platform) Sys.word_size Sys.ocaml_version
474 let makehelp () =
475 let strings = version () :: "" :: Help.keys in
476 Array.of_list (
477 let r = Str.regexp "\\(http://[^ ]+\\)" in
478 List.map (fun s ->
479 if (try Str.search_forward r s 0 with Not_found -> -1) >= 0
480 then
481 let uri = Str.matched_string s in
482 (s, 0, Action (fun u -> gotouri uri; u))
483 else s, 0, Noaction) strings
487 let state =
488 { csock = Unix.stdin
489 ; ssock = Unix.stdin
490 ; errfd = Unix.stdin
491 ; stderr = Unix.stderr
492 ; errmsgs = Buffer.create 0
493 ; newerrmsgs = false
494 ; x = 0
495 ; y = 0
496 ; w = 0
497 ; scrollw = 0
498 ; hscrollh = 0
499 ; anchor = emptyanchor
500 ; layout = []
501 ; maxy = max_int
502 ; tilelru = Queue.create ()
503 ; pagemap = Hashtbl.create 10
504 ; tilemap = Hashtbl.create 10
505 ; pdims = []
506 ; pagecount = 0
507 ; currently = Idle
508 ; mstate = Mnone
509 ; rects = []
510 ; rects1 = []
511 ; text = ""
512 ; mode = View
513 ; fullscreen = None
514 ; searchpattern = ""
515 ; outlines = [||]
516 ; bookmarks = []
517 ; path = ""
518 ; password = ""
519 ; invalidated = 0
520 ; hists =
521 { nav = cbnew 10 (0, 0.0)
522 ; pat = cbnew 1 ""
523 ; pag = cbnew 1 ""
525 ; memused = 0
526 ; gen = 0
527 ; throttle = None
528 ; autoscroll = None
529 ; help = makehelp ()
530 ; docinfo = []
531 ; deadline = nan
532 ; texid = None
533 ; prevzoom = 1.0
534 ; progress = -1.0
535 ; uioh = nouioh
539 let vlog fmt =
540 if conf.verbose
541 then
542 Printf.kprintf prerr_endline fmt
543 else
544 Printf.kprintf ignore fmt
547 let redirectstderr () =
548 if conf.redirectstderr
549 then
550 let rfd, wfd = Unix.pipe () in
551 state.stderr <- Unix.dup Unix.stderr;
552 state.errfd <- rfd;
553 Unix.dup2 wfd Unix.stderr;
554 else (
555 state.newerrmsgs <- false;
556 Unix.dup2 state.stderr Unix.stderr;
557 prerr_string (Buffer.contents state.errmsgs);
558 flush stderr;
559 Buffer.clear state.errmsgs;
563 module G =
564 struct
565 let postRedisplay who =
566 if conf.verbose
567 then prerr_endline ("redisplay for " ^ who);
568 Glut.postRedisplay ();
570 end;;
572 let addchar s c =
573 let b = Buffer.create (String.length s + 1) in
574 Buffer.add_string b s;
575 Buffer.add_char b c;
576 Buffer.contents b;
579 let colorspace_of_string s =
580 match String.lowercase s with
581 | "rgb" -> Rgb
582 | "bgr" -> Bgr
583 | "gray" -> Gray
584 | _ -> failwith "invalid colorspace"
587 let int_of_colorspace = function
588 | Rgb -> 0
589 | Bgr -> 1
590 | Gray -> 2
593 let colorspace_of_int = function
594 | 0 -> Rgb
595 | 1 -> Bgr
596 | 2 -> Gray
597 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
600 let colorspace_to_string = function
601 | Rgb -> "rgb"
602 | Bgr -> "bgr"
603 | Gray -> "gray"
606 let intentry_with_suffix text key =
607 let c = Char.unsafe_chr key in
608 match Char.lowercase c with
609 | '0' .. '9' ->
610 let text = addchar text c in
611 TEcont text
613 | 'k' | 'm' | 'g' ->
614 let text = addchar text c in
615 TEcont text
617 | _ ->
618 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
619 TEcont text
622 let writecmd fd s =
623 let len = String.length s in
624 let n = 4 + len in
625 let b = Buffer.create n in
626 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
627 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
628 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
629 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
630 Buffer.add_string b s;
631 let s' = Buffer.contents b in
632 let n' = Unix.write fd s' 0 n in
633 if n' != n then failwith "write failed";
636 let readcmd fd =
637 let s = "xxxx" in
638 let n = Unix.read fd s 0 4 in
639 if n != 4 then failwith "incomplete read(len)";
640 let len = 0
641 lor (Char.code s.[0] lsl 24)
642 lor (Char.code s.[1] lsl 16)
643 lor (Char.code s.[2] lsl 8)
644 lor (Char.code s.[3] lsl 0)
646 let s = String.create len in
647 let n = Unix.read fd s 0 len in
648 if n != len then failwith "incomplete read(data)";
652 let makecmd s l =
653 let b = Buffer.create 10 in
654 Buffer.add_string b s;
655 let rec combine = function
656 | [] -> b
657 | x :: xs ->
658 Buffer.add_char b ' ';
659 let s =
660 match x with
661 | `b b -> if b then "1" else "0"
662 | `s s -> s
663 | `i i -> string_of_int i
664 | `f f -> string_of_float f
665 | `I f -> string_of_int (truncate f)
667 Buffer.add_string b s;
668 combine xs;
670 combine l;
673 let wcmd s l =
674 let cmd = Buffer.contents (makecmd s l) in
675 writecmd state.csock cmd;
678 let calcips h =
679 if conf.presentation
680 then
681 let d = conf.winh - h in
682 max 0 ((d + 1) / 2)
683 else
684 conf.interpagespace
687 let calcheight () =
688 let rec f pn ph pi fh l =
689 match l with
690 | (n, _, h, _) :: rest ->
691 let ips = calcips h in
692 let fh =
693 if conf.presentation
694 then fh+ips
695 else (
696 if isbirdseye state.mode && pn = 0
697 then fh + ips
698 else fh
701 let fh = fh + ((n - pn) * (ph + pi)) in
702 f n h ips fh rest;
704 | [] ->
705 let inc =
706 if conf.presentation || (isbirdseye state.mode && pn = 0)
707 then 0
708 else -pi
710 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
711 max 0 fh
713 let fh = f 0 0 0 0 state.pdims in
717 let getpageyh pageno =
718 let rec f pn ph pi y l =
719 match l with
720 | (n, _, h, _) :: rest ->
721 let ips = calcips h in
722 if n >= pageno
723 then
724 let h = if n = pageno then h else ph in
725 if conf.presentation && n = pageno
726 then
727 y + (pageno - pn) * (ph + pi) + pi, h
728 else
729 y + (pageno - pn) * (ph + pi), h
730 else
731 let y = y + (if conf.presentation then pi else 0) in
732 let y = y + (n - pn) * (ph + pi) in
733 f n h ips y rest
735 | [] ->
736 y + (pageno - pn) * (ph + pi), ph
738 f 0 0 0 0 state.pdims
741 let getpagedim pageno =
742 let rec f ppdim l =
743 match l with
744 | (n, _, _, _) as pdim :: rest ->
745 if n >= pageno
746 then (if n = pageno then pdim else ppdim)
747 else f pdim rest
749 | [] -> ppdim
751 f (-1, -1, -1, -1) state.pdims
754 let getpageh pageno =
755 let _, _, h, _ = getpagedim pageno in
759 let getpagew pageno =
760 let _, w, _, _ = getpagedim pageno in
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 ww = fstate.wwidth 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) +. ww, 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
1412 gen = state.gen
1413 && colorspace = conf.colorspace
1414 && angle = conf.angle
1415 && pagew = getpagew n
1416 && pageh = getpageh n
1417 && (
1418 let layout =
1419 if conf.preload
1420 then preloadlayout state.layout
1421 else state.layout
1423 let x = col*conf.tilew
1424 and y = row*conf.tileh in
1425 tilevisible layout n x y
1427 then Queue.push lruitem state.tilelru
1428 else (
1429 wcmd "freetile" [`s p];
1430 state.memused <- state.memused - s;
1431 state.uioh#infochanged Memused;
1432 Hashtbl.remove state.tilemap k;
1434 loop (qpos+1)
1437 loop 0
1440 let flushtiles () =
1441 Queue.iter (fun (k, p, s) ->
1442 wcmd "freetile" [`s p];
1443 state.memused <- state.memused - s;
1444 state.uioh#infochanged Memused;
1445 Hashtbl.remove state.tilemap k;
1446 ) state.tilelru;
1447 Queue.clear state.tilelru;
1448 load state.layout;
1451 let logcurrently = function
1452 | Idle -> dolog "Idle"
1453 | Loading (l, gen) ->
1454 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1455 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1456 dolog
1457 "Tiling %d[%d,%d] page=%s cs=%s angle"
1458 l.pageno col row pageopaque
1459 (colorspace_to_string colorspace)
1461 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1462 angle gen conf.angle state.gen
1463 tilew tileh
1464 conf.tilew conf.tileh
1466 | Outlining _ ->
1467 dolog "outlining"
1470 let act cmds =
1471 (* dolog "%S" cmds; *)
1472 let op, args =
1473 let spacepos =
1474 try String.index cmds ' '
1475 with Not_found -> -1
1477 if spacepos = -1
1478 then cmds, ""
1479 else
1480 let l = String.length cmds in
1481 let op = String.sub cmds 0 spacepos in
1482 op, begin
1483 if l - spacepos < 2 then ""
1484 else String.sub cmds (spacepos+1) (l-spacepos-1)
1487 match op with
1488 | "clear" ->
1489 state.uioh#infochanged Pdim;
1490 state.pdims <- [];
1492 | "clearrects" ->
1493 state.rects <- state.rects1;
1494 G.postRedisplay "clearrects";
1496 | "continue" ->
1497 let n =
1498 try Scanf.sscanf args "%u" (fun n -> n)
1499 with exn ->
1500 dolog "error processing 'continue' %S: %s"
1501 cmds (Printexc.to_string exn);
1502 exit 1;
1504 state.pagecount <- n;
1505 state.invalidated <- state.invalidated - 1;
1506 begin match state.currently with
1507 | Outlining l ->
1508 state.currently <- Idle;
1509 state.outlines <- Array.of_list (List.rev l)
1510 | _ -> ()
1511 end;
1512 if state.invalidated = 0
1513 then represent ();
1514 if conf.maxwait = None
1515 then G.postRedisplay "continue";
1517 | "title" ->
1518 Glut.setWindowTitle args
1520 | "msg" ->
1521 showtext ' ' args
1523 | "vmsg" ->
1524 if conf.verbose
1525 then showtext ' ' args
1527 | "progress" ->
1528 let progress, text =
1530 Scanf.sscanf args "%f %n"
1531 (fun f pos ->
1532 f, String.sub args pos (String.length args - pos))
1533 with exn ->
1534 dolog "error processing 'progress' %S: %s"
1535 cmds (Printexc.to_string exn);
1536 exit 1;
1538 state.text <- text;
1539 state.progress <- progress;
1540 G.postRedisplay "progress"
1542 | "firstmatch" ->
1543 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1545 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1546 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1547 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1548 with exn ->
1549 dolog "error processing 'firstmatch' %S: %s"
1550 cmds (Printexc.to_string exn);
1551 exit 1;
1553 let y = (getpagey pageno) + truncate y0 in
1554 addnav ();
1555 gotoy y;
1556 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1558 | "match" ->
1559 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1561 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1562 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1563 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1564 with exn ->
1565 dolog "error processing 'match' %S: %s"
1566 cmds (Printexc.to_string exn);
1567 exit 1;
1569 state.rects1 <-
1570 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1572 | "page" ->
1573 let pageopaque, t =
1575 Scanf.sscanf args "%s %f" (fun p t -> p, t)
1576 with exn ->
1577 dolog "error processing 'page' %S: %s"
1578 cmds (Printexc.to_string exn);
1579 exit 1;
1581 begin match state.currently with
1582 | Loading (l, gen) ->
1583 vlog "page %d took %f sec" l.pageno t;
1584 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1585 begin match state.throttle with
1586 | None ->
1587 let preloadedpages =
1588 if conf.preload
1589 then preloadlayout state.layout
1590 else state.layout
1592 let evict () =
1593 let module IntSet =
1594 Set.Make (struct type t = int let compare = (-) end) in
1595 let set =
1596 List.fold_left (fun s l -> IntSet.add l.pageno s)
1597 IntSet.empty preloadedpages
1599 let evictedpages =
1600 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1601 if not (IntSet.mem pageno set)
1602 then (
1603 wcmd "freepage" [`s opaque];
1604 key :: accu
1606 else accu
1607 ) state.pagemap []
1609 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1611 evict ();
1612 state.currently <- Idle;
1613 if gen = state.gen
1614 then (
1615 tilepage l.pageno pageopaque state.layout;
1616 load state.layout;
1617 load preloadedpages;
1618 if pagevisible state.layout l.pageno
1619 && layoutready state.layout
1620 then G.postRedisplay "page";
1623 | Some (layout, _, _) ->
1624 state.currently <- Idle;
1625 tilepage l.pageno pageopaque layout;
1626 load state.layout
1627 end;
1629 | _ ->
1630 dolog "Inconsistent loading state";
1631 logcurrently state.currently;
1632 raise Quit;
1635 | "tile" ->
1636 let (x, y, opaque, size, t) =
1638 Scanf.sscanf args "%u %u %s %u %f"
1639 (fun x y p size t -> (x, y, p, size, t))
1640 with exn ->
1641 dolog "error processing 'tile' %S: %s"
1642 cmds (Printexc.to_string exn);
1643 exit 1;
1645 begin match state.currently with
1646 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1647 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1649 if tilew != conf.tilew || tileh != conf.tileh
1650 then (
1651 wcmd "freetile" [`s opaque];
1652 state.currently <- Idle;
1653 load state.layout;
1655 else (
1656 puttileopaque l col row gen cs angle opaque size t;
1657 state.memused <- state.memused + size;
1658 state.uioh#infochanged Memused;
1659 gctiles ();
1660 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1661 opaque, size) state.tilelru;
1663 state.currently <- Idle;
1664 if gen = state.gen
1665 && conf.colorspace = cs
1666 && conf.angle = angle
1667 && tilevisible state.layout l.pageno x y
1668 then conttiling l.pageno pageopaque;
1670 begin match state.throttle with
1671 | None ->
1672 preload state.layout;
1673 if gen = state.gen
1674 && conf.colorspace = cs
1675 && conf.angle = angle
1676 && tilevisible state.layout l.pageno x y
1677 then G.postRedisplay "tile nothrottle";
1679 | Some (layout, y, _) ->
1680 let ready = layoutready layout in
1681 if ready
1682 then (
1683 state.y <- y;
1684 state.layout <- layout;
1685 state.throttle <- None;
1686 G.postRedisplay "throttle";
1688 else load layout;
1689 end;
1692 | _ ->
1693 dolog "Inconsistent tiling state";
1694 logcurrently state.currently;
1695 raise Quit;
1698 | "pdim" ->
1699 let pdim =
1701 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1702 with exn ->
1703 dolog "error processing 'pdim' %S: %s"
1704 cmds (Printexc.to_string exn);
1705 exit 1;
1707 state.uioh#infochanged Pdim;
1708 state.pdims <- pdim :: state.pdims
1710 | "o" ->
1711 let (l, n, t, h, pos) =
1713 Scanf.sscanf args "%u %u %d %u %n"
1714 (fun l n t h pos -> l, n, t, h, pos)
1715 with exn ->
1716 dolog "error processing 'o' %S: %s"
1717 cmds (Printexc.to_string exn);
1718 exit 1;
1720 let s = String.sub args pos (String.length args - pos) in
1721 let outline = (s, l, (n, float t /. float h)) in
1722 begin match state.currently with
1723 | Outlining outlines ->
1724 state.currently <- Outlining (outline :: outlines)
1725 | Idle ->
1726 state.currently <- Outlining [outline]
1727 | currently ->
1728 dolog "invalid outlining state";
1729 logcurrently currently
1732 | "info" ->
1733 state.docinfo <- (1, args) :: state.docinfo
1735 | "infoend" ->
1736 state.uioh#infochanged Docinfo;
1737 state.docinfo <- List.rev state.docinfo
1739 | _ ->
1740 dolog "unknown cmd `%S'" cmds
1743 let idle () =
1744 if state.deadline == nan then state.deadline <- now ();
1745 let rec loop delay =
1746 let timeout =
1747 if delay > 0.0
1748 then max 0.0 (state.deadline -. now ())
1749 else 0.0
1751 let r, _, _ = Unix.select [state.csock; state.errfd] [] [] timeout in
1752 begin match r with
1753 | [] ->
1754 begin match state.autoscroll with
1755 | Some step when step != 0 ->
1756 let y = state.y + step in
1757 let y =
1758 if y < 0
1759 then state.maxy
1760 else if y >= state.maxy then 0 else y
1762 gotoy y;
1763 if state.mode = View
1764 then state.text <- "";
1765 state.deadline <- state.deadline +. 0.005;
1767 | _ ->
1768 state.deadline <- state.deadline +. delay;
1769 end;
1771 | l ->
1772 let rec checkfds c = function
1773 | [] -> c
1774 | fd :: rest when fd = state.csock ->
1775 let cmd = readcmd state.csock in
1776 act cmd;
1777 checkfds true rest
1778 | fd :: rest when fd = state.errfd ->
1779 let s = String.create 80 in
1780 let n = Unix.read fd s 0 80 in
1781 if conf.redirectstderr
1782 then (
1783 Buffer.add_substring state.errmsgs s 0 n;
1784 state.newerrmsgs <- true;
1785 Glut.postRedisplay ();
1787 else (
1788 prerr_string (String.sub s 0 n);
1789 flush stderr;
1791 checkfds c rest
1793 | _ ->
1794 failwith "me? fail english? that's unpossible!"
1796 if checkfds false l
1797 then loop 0.0
1798 end;
1799 in loop 0.007
1802 let onhist cb =
1803 let rc = cb.rc in
1804 let action = function
1805 | HCprev -> cbget cb ~-1
1806 | HCnext -> cbget cb 1
1807 | HCfirst -> cbget cb ~-(cb.rc)
1808 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1809 and cancel () = cb.rc <- rc
1810 in (action, cancel)
1813 let search pattern forward =
1814 if String.length pattern > 0
1815 then
1816 let pn, py =
1817 match state.layout with
1818 | [] -> 0, 0
1819 | l :: _ ->
1820 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1822 let cmd =
1823 let b = makecmd "search"
1824 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
1826 Buffer.add_char b ',';
1827 Buffer.add_string b pattern;
1828 Buffer.add_char b '\000';
1829 Buffer.contents b;
1831 writecmd state.csock cmd;
1834 let intentry text key =
1835 let c = Char.unsafe_chr key in
1836 match c with
1837 | '0' .. '9' ->
1838 let text = addchar text c in
1839 TEcont text
1841 | _ ->
1842 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
1843 TEcont text
1846 let textentry text key =
1847 let c = Char.unsafe_chr key in
1848 match c with
1849 | _ when key >= 32 && key < 127 ->
1850 let text = addchar text c in
1851 TEcont text
1853 | _ ->
1854 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
1855 TEcont text
1858 let reqlayout angle proportional =
1859 match state.throttle with
1860 | None ->
1861 if state.invalidated = 0 then state.anchor <- getanchor ();
1862 conf.angle <- angle mod 360;
1863 conf.proportional <- proportional;
1864 invalidate ();
1865 wcmd "reqlayout" [`i conf.angle; `b proportional];
1866 | _ -> ()
1869 let settrim trimmargins trimfuzz =
1870 if state.invalidated = 0 then state.anchor <- getanchor ();
1871 conf.trimmargins <- trimmargins;
1872 conf.trimfuzz <- trimfuzz;
1873 let x0, y0, x1, y1 = trimfuzz in
1874 invalidate ();
1875 wcmd "settrim" [
1876 `b conf.trimmargins;
1877 `i x0;
1878 `i y0;
1879 `i x1;
1880 `i y1;
1882 Hashtbl.iter (fun _ opaque ->
1883 wcmd "freepage" [`s opaque];
1884 ) state.pagemap;
1885 Hashtbl.clear state.pagemap;
1888 let setzoom zoom =
1889 match state.throttle with
1890 | None ->
1891 let zoom = max 0.01 zoom in
1892 if zoom <> conf.zoom
1893 then (
1894 state.prevzoom <- conf.zoom;
1895 let relx =
1896 if zoom <= 1.0
1897 then (state.x <- 0; 0.0)
1898 else float state.x /. float state.w
1900 conf.zoom <- zoom;
1901 reshape conf.winw conf.winh;
1902 if zoom > 1.0
1903 then (
1904 let x = relx *. float state.w in
1905 state.x <- truncate x;
1907 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
1910 | Some (layout, y, started) ->
1911 let time =
1912 match conf.maxwait with
1913 | None -> 0.0
1914 | Some t -> t
1916 let dt = now () -. started in
1917 if dt > time
1918 then (
1919 state.y <- y;
1920 load layout;
1924 let enterbirdseye () =
1925 let zoom = float conf.thumbw /. float conf.winw in
1926 let birdseyepageno =
1927 let cy = conf.winh / 2 in
1928 let fold = function
1929 | [] -> 0
1930 | l :: rest ->
1931 let rec fold best = function
1932 | [] -> best.pageno
1933 | l :: rest ->
1934 let d = cy - (l.pagedispy + l.pagevh/2)
1935 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1936 if abs d < abs dbest
1937 then fold l rest
1938 else best.pageno
1939 in fold l rest
1941 fold state.layout
1943 state.mode <- Birdseye (
1944 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
1946 conf.zoom <- zoom;
1947 conf.presentation <- false;
1948 conf.interpagespace <- 10;
1949 conf.hlinks <- false;
1950 state.x <- 0;
1951 state.mstate <- Mnone;
1952 conf.maxwait <- None;
1953 Glut.setCursor Glut.CURSOR_INHERIT;
1954 if conf.verbose
1955 then
1956 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1957 (100.0*.zoom)
1958 else
1959 state.text <- ""
1961 reshape conf.winw conf.winh;
1964 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1965 state.mode <- View;
1966 conf.zoom <- c.zoom;
1967 conf.presentation <- c.presentation;
1968 conf.interpagespace <- c.interpagespace;
1969 conf.maxwait <- c.maxwait;
1970 conf.hlinks <- c.hlinks;
1971 state.x <- leftx;
1972 if conf.verbose
1973 then
1974 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1975 (100.0*.conf.zoom)
1977 reshape conf.winw conf.winh;
1978 state.anchor <- if goback then anchor else (pageno, 0.0);
1981 let togglebirdseye () =
1982 match state.mode with
1983 | Birdseye vals -> leavebirdseye vals true
1984 | View -> enterbirdseye ()
1985 | _ -> ()
1988 let upbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
1989 let pageno = max 0 (pageno - 1) in
1990 let rec loop = function
1991 | [] -> gotopage1 pageno 0
1992 | l :: _ when l.pageno = pageno ->
1993 if l.pagedispy >= 0 && l.pagey = 0
1994 then G.postRedisplay "upbirdseye"
1995 else gotopage1 pageno 0
1996 | _ :: rest -> loop rest
1998 loop state.layout;
1999 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2002 let downbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
2003 let pageno = min (state.pagecount - 1) (pageno + 1) in
2004 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2005 let rec loop = function
2006 | [] ->
2007 let y, h = getpageyh pageno in
2008 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2009 gotoy (clamp dy)
2010 | l :: _ when l.pageno = pageno ->
2011 if l.pagevh != l.pageh
2012 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2013 else G.postRedisplay "downbirdseye"
2014 | _ :: rest -> loop rest
2016 loop state.layout
2019 let optentry mode _ key =
2020 let btos b = if b then "on" else "off" in
2021 let c = Char.unsafe_chr key in
2022 match c with
2023 | 's' ->
2024 let ondone s =
2025 try conf.scrollstep <- int_of_string s with exc ->
2026 state.text <- Printf.sprintf "bad integer `%s': %s"
2027 s (Printexc.to_string exc)
2029 TEswitch ("scroll step: ", "", None, intentry, ondone)
2031 | 'A' ->
2032 let ondone s =
2034 conf.autoscrollstep <- int_of_string s;
2035 if state.autoscroll <> None
2036 then state.autoscroll <- Some conf.autoscrollstep
2037 with exc ->
2038 state.text <- Printf.sprintf "bad integer `%s': %s"
2039 s (Printexc.to_string exc)
2041 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2043 | 'Z' ->
2044 let ondone s =
2046 let zoom = float (int_of_string s) /. 100.0 in
2047 setzoom zoom
2048 with exc ->
2049 state.text <- Printf.sprintf "bad integer `%s': %s"
2050 s (Printexc.to_string exc)
2052 TEswitch ("zoom: ", "", None, intentry, ondone)
2054 | 't' ->
2055 let ondone s =
2057 conf.thumbw <- bound (int_of_string s) 2 4096;
2058 state.text <-
2059 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2060 begin match mode with
2061 | Birdseye beye ->
2062 leavebirdseye beye false;
2063 enterbirdseye ();
2064 | _ -> ();
2066 with exc ->
2067 state.text <- Printf.sprintf "bad integer `%s': %s"
2068 s (Printexc.to_string exc)
2070 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2072 | 'R' ->
2073 let ondone s =
2074 match try
2075 Some (int_of_string s)
2076 with exc ->
2077 state.text <- Printf.sprintf "bad integer `%s': %s"
2078 s (Printexc.to_string exc);
2079 None
2080 with
2081 | Some angle -> reqlayout angle conf.proportional
2082 | None -> ()
2084 TEswitch ("rotation: ", "", None, intentry, ondone)
2086 | 'i' ->
2087 conf.icase <- not conf.icase;
2088 TEdone ("case insensitive search " ^ (btos conf.icase))
2090 | 'p' ->
2091 conf.preload <- not conf.preload;
2092 gotoy state.y;
2093 TEdone ("preload " ^ (btos conf.preload))
2095 | 'v' ->
2096 conf.verbose <- not conf.verbose;
2097 TEdone ("verbose " ^ (btos conf.verbose))
2099 | 'd' ->
2100 conf.debug <- not conf.debug;
2101 TEdone ("debug " ^ (btos conf.debug))
2103 | 'h' ->
2104 conf.maxhfit <- not conf.maxhfit;
2105 state.maxy <-
2106 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2107 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2109 | 'c' ->
2110 conf.crophack <- not conf.crophack;
2111 TEdone ("crophack " ^ btos conf.crophack)
2113 | 'a' ->
2114 let s =
2115 match conf.maxwait with
2116 | None ->
2117 conf.maxwait <- Some infinity;
2118 "always wait for page to complete"
2119 | Some _ ->
2120 conf.maxwait <- None;
2121 "show placeholder if page is not ready"
2123 TEdone s
2125 | 'f' ->
2126 conf.underinfo <- not conf.underinfo;
2127 TEdone ("underinfo " ^ btos conf.underinfo)
2129 | 'P' ->
2130 conf.savebmarks <- not conf.savebmarks;
2131 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2133 | 'S' ->
2134 let ondone s =
2136 let pageno, py =
2137 match state.layout with
2138 | [] -> 0, 0
2139 | l :: _ ->
2140 l.pageno, l.pagey
2142 conf.interpagespace <- int_of_string s;
2143 state.maxy <- calcheight ();
2144 let y = getpagey pageno in
2145 gotoy (y + py)
2146 with exc ->
2147 state.text <- Printf.sprintf "bad integer `%s': %s"
2148 s (Printexc.to_string exc)
2150 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2152 | 'l' ->
2153 reqlayout conf.angle (not conf.proportional);
2154 TEdone ("proportional display " ^ btos conf.proportional)
2156 | 'T' ->
2157 settrim (not conf.trimmargins) conf.trimfuzz;
2158 TEdone ("trim margins " ^ btos conf.trimmargins)
2160 | 'I' ->
2161 conf.invert <- not conf.invert;
2162 TEdone ("invert colors " ^ btos conf.invert)
2164 | _ ->
2165 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2166 TEstop
2169 class type lvsource = object
2170 method getitemcount : int
2171 method getitem : int -> (string * int)
2172 method hasaction : int -> bool
2173 method exit :
2174 uioh:uioh ->
2175 cancel:bool ->
2176 active:int ->
2177 first:int ->
2178 pan:int ->
2179 qsearch:string ->
2180 uioh option
2181 method getactive : int
2182 method getfirst : int
2183 method getqsearch : string
2184 method setqsearch : string -> unit
2185 method getpan : int
2186 end;;
2188 class virtual lvsourcebase = object
2189 val mutable m_active = 0
2190 val mutable m_first = 0
2191 val mutable m_qsearch = ""
2192 val mutable m_pan = 0
2193 method getactive = m_active
2194 method getfirst = m_first
2195 method getqsearch = m_qsearch
2196 method getpan = m_pan
2197 method setqsearch s = m_qsearch <- s
2198 end;;
2200 let textentryspecial key = function
2201 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2202 let s =
2203 match key with
2204 | Glut.KEY_UP -> action HCprev
2205 | Glut.KEY_DOWN -> action HCnext
2206 | Glut.KEY_HOME -> action HCfirst
2207 | Glut.KEY_END -> action HClast
2208 | _ -> state.text
2210 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2211 G.postRedisplay "special textentry";
2212 | _ -> ()
2215 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2216 let enttext te =
2217 state.mode <- Textentry (te, onleave);
2218 state.text <- "";
2219 enttext ();
2220 G.postRedisplay "textentrykeyboard enttext";
2222 match Char.unsafe_chr key with
2223 | '\008' -> (* backspace *)
2224 let len = String.length text in
2225 if len = 0
2226 then (
2227 onleave Cancel;
2228 G.postRedisplay "textentrykeyboard after cancel";
2230 else (
2231 let s = String.sub text 0 (len - 1) in
2232 enttext (c, s, opthist, onkey, ondone)
2235 | '\r' | '\n' ->
2236 ondone text;
2237 onleave Confirm;
2238 G.postRedisplay "textentrykeyboard after confirm"
2240 | '\007' (* ctrl-g *)
2241 | '\027' -> (* escape *)
2242 if String.length text = 0
2243 then (
2244 begin match opthist with
2245 | None -> ()
2246 | Some (_, onhistcancel) -> onhistcancel ()
2247 end;
2248 onleave Cancel;
2249 state.text <- "";
2250 G.postRedisplay "textentrykeyboard after cancel2"
2252 else (
2253 enttext (c, "", opthist, onkey, ondone)
2256 | '\127' -> () (* delete *)
2258 | _ ->
2259 begin match onkey text key with
2260 | TEdone text ->
2261 ondone text;
2262 onleave Confirm;
2263 G.postRedisplay "textentrykeyboard after confirm2";
2265 | TEcont text ->
2266 enttext (c, text, opthist, onkey, ondone);
2268 | TEstop ->
2269 onleave Cancel;
2270 state.text <- "";
2271 G.postRedisplay "textentrykeyboard after cancel3"
2273 | TEswitch te ->
2274 state.mode <- Textentry (te, onleave);
2275 G.postRedisplay "textentrykeyboard switch";
2276 end;
2279 let firstof first active =
2280 if first > active || abs (first - active) > fstate.maxrows - 1
2281 then max 0 (active - (fstate.maxrows/2))
2282 else first
2285 let calcfirst first active =
2286 if active > first
2287 then
2288 let rows = active - first in
2289 if rows > fstate.maxrows then active - fstate.maxrows else first
2290 else active
2293 let coe s = (s :> uioh);;
2295 class listview ~(source:lvsource) ~trusted =
2296 object (self)
2297 val m_pan = source#getpan
2298 val m_first = source#getfirst
2299 val m_active = source#getactive
2300 val m_qsearch = source#getqsearch
2301 val m_prev_uioh = state.uioh
2303 method private elemunder y =
2304 let n = y / (fstate.fontsize+1) in
2305 if m_first + n < source#getitemcount
2306 then (
2307 if source#hasaction (m_first + n)
2308 then Some (m_first + n)
2309 else None
2311 else None
2313 method display =
2314 Gl.enable `blend;
2315 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2316 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2317 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2318 GlDraw.color (1., 1., 1.);
2319 Gl.enable `texture_2d;
2320 let fs = fstate.fontsize in
2321 let nfs = fs + 1 in
2322 let ww = fstate.wwidth in
2323 let tabw = 30.0*.ww in
2324 let rec loop row =
2325 if (row - m_first) * nfs > conf.winh
2326 then ()
2327 else (
2328 if row >= 0 && row < source#getitemcount
2329 then (
2330 let (s, level) = source#getitem row in
2331 let y = (row - m_first) * nfs in
2332 let x = 5.0 +. float (level + m_pan) *. ww in
2333 if row = m_active
2334 then (
2335 Gl.disable `texture_2d;
2336 GlDraw.polygon_mode `both `line;
2337 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2338 GlDraw.rect (1., float (y + 1))
2339 (float (conf.winw - 1), float (y + fs + 3));
2340 GlDraw.polygon_mode `both `fill;
2341 GlDraw.color (1., 1., 1.);
2342 Gl.enable `texture_2d;
2345 let drawtabularstring s =
2346 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2347 if trusted
2348 then
2349 let tabpos = try String.index s '\t' with Not_found -> -1 in
2350 if tabpos > 0
2351 then
2352 let len = String.length s - tabpos - 1 in
2353 let s1 = String.sub s 0 tabpos
2354 and s2 = String.sub s (tabpos + 1) len in
2355 let nx = drawstr x s1 in
2356 let sw = nx -. x in
2357 let x = x +. (max tabw sw) in
2358 drawstr x s2
2359 else
2360 drawstr x s
2361 else
2362 drawstr x s
2364 let _ = drawtabularstring s in
2365 loop (row+1)
2369 loop 0;
2370 Gl.disable `blend;
2371 Gl.disable `texture_2d;
2373 method updownlevel incr =
2374 let len = source#getitemcount in
2375 let _, curlevel = source#getitem m_active in
2376 let rec flow i =
2377 if i = len then i-1 else if i = -1 then 0 else
2378 let _, l = source#getitem i in
2379 if l != curlevel then i else flow (i+incr)
2381 let active = flow m_active in
2382 let first = calcfirst m_first active in
2383 G.postRedisplay "special outline updownlevel";
2384 {< m_active = active; m_first = first >}
2386 method private key1 key =
2387 let set active first qsearch =
2388 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2390 let search active pattern incr =
2391 let dosearch re =
2392 let rec loop n =
2393 if n >= 0 && n < source#getitemcount
2394 then (
2395 let s, _ = source#getitem n in
2397 (try ignore (Str.search_forward re s 0); true
2398 with Not_found -> false)
2399 then Some n
2400 else loop (n + incr)
2402 else None
2404 loop active
2407 let re = Str.regexp_case_fold pattern in
2408 dosearch re
2409 with Failure s ->
2410 state.text <- s;
2411 None
2413 match key with
2414 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2415 let incr = if key = 18 then -1 else 1 in
2416 let active, first =
2417 match search (m_active + incr) m_qsearch incr with
2418 | None ->
2419 state.text <- m_qsearch ^ " [not found]";
2420 m_active, m_first
2421 | Some active ->
2422 state.text <- m_qsearch;
2423 active, firstof m_first active
2425 G.postRedisplay "listview ctrl-r/s";
2426 set active first m_qsearch;
2428 | 8 -> (* backspace *)
2429 let len = String.length m_qsearch in
2430 if len = 0
2431 then coe self
2432 else (
2433 if len = 1
2434 then (
2435 state.text <- "";
2436 G.postRedisplay "listview empty qsearch";
2437 set m_active m_first "";
2439 else
2440 let qsearch = String.sub m_qsearch 0 (len - 1) in
2441 let active, first =
2442 match search m_active qsearch ~-1 with
2443 | None ->
2444 state.text <- qsearch ^ " [not found]";
2445 m_active, m_first
2446 | Some active ->
2447 state.text <- qsearch;
2448 active, firstof m_first active
2450 G.postRedisplay "listview backspace qsearch";
2451 set active first qsearch
2454 | _ when key >= 32 && key < 127 ->
2455 let pattern = addchar m_qsearch (Char.chr key) in
2456 let active, first =
2457 match search m_active pattern 1 with
2458 | None ->
2459 state.text <- pattern ^ " [not found]";
2460 m_active, m_first
2461 | Some active ->
2462 state.text <- pattern;
2463 active, firstof m_first active
2465 G.postRedisplay "listview qsearch add";
2466 set active first pattern;
2468 | 27 -> (* escape *)
2469 state.text <- "";
2470 if String.length m_qsearch = 0
2471 then (
2472 G.postRedisplay "list view escape";
2473 begin
2474 match
2475 source#exit (coe self) true m_active m_first m_pan m_qsearch
2476 with
2477 | None -> m_prev_uioh
2478 | Some uioh -> uioh
2481 else (
2482 G.postRedisplay "list view kill qsearch";
2483 source#setqsearch "";
2484 coe {< m_qsearch = "" >}
2487 | 13 -> (* enter *)
2488 state.text <- "";
2489 let self = {< m_qsearch = "" >} in
2490 source#setqsearch "";
2491 let opt =
2492 G.postRedisplay "listview enter";
2493 if m_active >= 0 && m_active < source#getitemcount
2494 then (
2495 source#exit (coe self) false m_active m_first m_pan "";
2497 else (
2498 source#exit (coe self) true m_active m_first m_pan "";
2501 begin match opt with
2502 | None -> m_prev_uioh
2503 | Some uioh -> uioh
2506 | 127 -> (* delete *)
2507 coe self
2509 | _ -> dolog "unknown key %d" key; coe self
2511 method private special1 key =
2512 let itemcount = source#getitemcount in
2513 let find start incr =
2514 let rec find i =
2515 if i = -1 || i = itemcount
2516 then -1
2517 else (
2518 if source#hasaction i
2519 then i
2520 else find (i + incr)
2523 find start
2525 let set active first =
2526 let first = bound first 0 (itemcount - fstate.maxrows) in
2527 state.text <- "";
2528 coe {< m_active = active; m_first = first >}
2530 let navigate incr =
2531 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2532 let active, first =
2533 let incr1 = if incr > 0 then 1 else -1 in
2534 if isvisible m_first m_active
2535 then
2536 let next =
2537 let next = m_active + incr in
2538 let next =
2539 if next < 0 || next >= itemcount
2540 then -1
2541 else find next incr1
2543 if next = -1 || abs (m_active - next) > fstate.maxrows
2544 then -1
2545 else next
2547 if next = -1
2548 then
2549 let first = m_first + incr in
2550 let first = bound first 0 (itemcount - 1) in
2551 let next =
2552 let next = m_active + incr in
2553 let next = bound next 0 (itemcount - 1) in
2554 find next ~-incr1
2556 let active = if next = -1 then m_active else next in
2557 active, first
2558 else
2559 let first = min next m_first in
2560 next, first
2561 else
2562 let first = m_first + incr in
2563 let first = bound first 0 (itemcount - 1) in
2564 let active =
2565 let next = m_active + incr in
2566 let next = bound next 0 (itemcount - 1) in
2567 let next = find next incr1 in
2568 if next = -1 || abs (m_active - first) > fstate.maxrows
2569 then m_active
2570 else next
2572 active, first
2574 G.postRedisplay "listview navigate";
2575 set active first;
2577 begin match key with
2578 | Glut.KEY_UP -> navigate ~-1
2579 | Glut.KEY_DOWN -> navigate 1
2580 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2581 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2583 | Glut.KEY_RIGHT ->
2584 state.text <- "";
2585 G.postRedisplay "listview right";
2586 coe {< m_pan = m_pan - 1 >}
2588 | Glut.KEY_LEFT ->
2589 state.text <- "";
2590 G.postRedisplay "listview left";
2591 coe {< m_pan = m_pan + 1 >}
2593 | Glut.KEY_HOME ->
2594 let active = find 0 1 in
2595 G.postRedisplay "listview home";
2596 set active 0;
2598 | Glut.KEY_END ->
2599 let first = max 0 (itemcount - fstate.maxrows) in
2600 let active = find (itemcount - 1) ~-1 in
2601 G.postRedisplay "listview end";
2602 set active first;
2604 | _ -> coe self
2605 end;
2607 method key key =
2608 match state.mode with
2609 | Textentry te -> textentrykeyboard key te; coe self
2610 | _ -> self#key1 key
2612 method special key =
2613 match state.mode with
2614 | Textentry te -> textentryspecial key te; coe self
2615 | _ -> self#special1 key
2617 method button button bstate _ y =
2618 let opt =
2619 match button with
2620 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
2621 begin match self#elemunder y with
2622 | Some n ->
2623 G.postRedisplay "listview click";
2624 source#exit
2625 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
2626 | _ ->
2627 Some (coe self)
2629 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
2630 let len = source#getitemcount in
2631 let first =
2632 if m_first + fstate.maxrows >= len
2633 then
2634 m_first
2635 else
2636 let first = m_first + (if n == 3 then -1 else 1) in
2637 bound first 0 (len - 1)
2639 G.postRedisplay "listview wheel";
2640 Some (coe {< m_first = first >})
2641 | _ ->
2642 Some (coe self)
2644 match opt with
2645 | None -> m_prev_uioh
2646 | Some uioh -> uioh
2648 method motion _ _ = coe self
2650 method pmotion _ y =
2651 let n =
2652 match self#elemunder y with
2653 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
2654 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
2656 let o =
2657 if n != m_active
2658 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
2659 else self
2661 coe o
2663 method infochanged _ = ()
2664 end;;
2666 class outlinelistview ~source =
2667 object (self)
2668 inherit listview ~source:(source :> lvsource) ~trusted:false as super
2670 method key key =
2671 match key with
2672 | 14 -> (* ctrl-n *)
2673 source#narrow m_qsearch;
2674 G.postRedisplay "outline ctrl-n";
2675 coe {< m_first = 0; m_active = 0 >}
2677 | 21 -> (* ctrl-u *)
2678 source#denarrow;
2679 G.postRedisplay "outline ctrl-u";
2680 coe {< m_first = 0; m_active = 0 >}
2682 | 12 -> (* ctrl-l *)
2683 let first = m_active - (fstate.maxrows / 2) in
2684 G.postRedisplay "outline ctrl-l";
2685 coe {< m_first = first >}
2687 | 127 -> (* delete *)
2688 source#remove m_active;
2689 G.postRedisplay "outline delete";
2690 let active = max 0 (m_active-1) in
2691 coe {< m_first = firstof m_first active;
2692 m_active = active >}
2694 | key -> super#key key
2696 method special key =
2697 let calcfirst first active =
2698 if active > first
2699 then
2700 let rows = active - first in
2701 if rows > fstate.maxrows then active - fstate.maxrows else first
2702 else active
2704 let navigate incr =
2705 let active = m_active + incr in
2706 let active = bound active 0 (source#getitemcount - 1) in
2707 let first = calcfirst m_first active in
2708 G.postRedisplay "special outline navigate";
2709 coe {< m_active = active; m_first = first >}
2711 match key with
2712 | Glut.KEY_UP -> navigate ~-1
2713 | Glut.KEY_DOWN -> navigate 1
2714 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2715 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2717 | Glut.KEY_RIGHT ->
2718 let o =
2719 if Glut.getModifiers () land Glut.active_ctrl != 0
2720 then (
2721 G.postRedisplay "special outline right";
2722 {< m_pan = m_pan + 1 >}
2724 else self#updownlevel 1
2726 coe o
2728 | Glut.KEY_LEFT ->
2729 let o =
2730 if Glut.getModifiers () land Glut.active_ctrl != 0
2731 then (
2732 G.postRedisplay "special outline left";
2733 {< m_pan = m_pan - 1 >}
2735 else self#updownlevel ~-1
2737 coe o
2739 | Glut.KEY_HOME ->
2740 G.postRedisplay "special outline home";
2741 coe {< m_first = 0; m_active = 0 >}
2743 | Glut.KEY_END ->
2744 let active = source#getitemcount - 1 in
2745 let first = max 0 (active - fstate.maxrows) in
2746 G.postRedisplay "special outline end";
2747 coe {< m_active = active; m_first = first >}
2749 | _ -> super#special key
2752 let outlinesource usebookmarks =
2753 let empty = [||] in
2754 (object
2755 inherit lvsourcebase
2756 val mutable m_items = empty
2757 val mutable m_orig_items = empty
2758 val mutable m_prev_items = empty
2759 val mutable m_narrow_pattern = ""
2760 val mutable m_hadremovals = false
2762 method getitemcount =
2763 Array.length m_items + (if m_hadremovals then 1 else 0)
2765 method getitem n =
2766 if n == Array.length m_items && m_hadremovals
2767 then
2768 ("[Confirm removal]", 0)
2769 else
2770 let s, n, _ = m_items.(n) in
2771 (s, n)
2773 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
2774 ignore (uioh, first, pan, qsearch);
2775 let confrimremoval = m_hadremovals && active = Array.length m_items in
2776 let items =
2777 if String.length m_narrow_pattern = 0
2778 then m_orig_items
2779 else m_items
2781 if not cancel
2782 then (
2783 if not confrimremoval
2784 then(
2785 let _, _, anchor = m_items.(active) in
2786 gotoanchor anchor;
2787 m_items <- items;
2789 else (
2790 state.bookmarks <- Array.to_list m_items;
2791 m_orig_items <- m_items;
2794 else m_items <- items;
2795 None
2797 method hasaction _ = true
2799 method greetmsg =
2800 if Array.length m_items != Array.length m_orig_items
2801 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
2802 else ""
2804 method narrow pattern =
2805 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
2806 match reopt with
2807 | None -> ()
2808 | Some re ->
2809 let rec loop accu n =
2810 if n = -1
2811 then (
2812 m_narrow_pattern <- pattern;
2813 m_items <- Array.of_list accu
2815 else
2816 let (s, _, _) as o = m_items.(n) in
2817 let accu =
2818 if (try ignore (Str.search_forward re s 0); true
2819 with Not_found -> false)
2820 then o :: accu
2821 else accu
2823 loop accu (n-1)
2825 loop [] (Array.length m_items - 1)
2827 method denarrow =
2828 m_orig_items <- (
2829 if usebookmarks
2830 then Array.of_list state.bookmarks
2831 else state.outlines
2833 m_items <- m_orig_items
2835 method remove m =
2836 if usebookmarks
2837 then
2838 if m >= 0 && m < Array.length m_items
2839 then (
2840 m_hadremovals <- true;
2841 m_items <- Array.init (Array.length m_items - 1) (fun n ->
2842 let n = if n >= m then n+1 else n in
2843 m_items.(n)
2847 method reset pageno items =
2848 m_hadremovals <- false;
2849 if m_orig_items == empty || m_prev_items != items
2850 then (
2851 m_orig_items <- items;
2852 if String.length m_narrow_pattern = 0
2853 then m_items <- items;
2855 m_prev_items <- items;
2856 let active =
2857 let rec loop n best bestd =
2858 if n = Array.length m_items
2859 then best
2860 else
2861 let (_, _, (outlinepageno, _)) = m_items.(n) in
2862 let d = abs (outlinepageno - pageno) in
2863 if d < bestd
2864 then loop (n+1) n d
2865 else loop (n+1) best bestd
2867 loop 0 ~-1 max_int
2869 m_active <- active;
2870 m_first <- firstof m_first active
2871 end)
2874 let enterselector usebookmarks =
2875 let source = outlinesource usebookmarks in
2876 fun errmsg ->
2877 let outlines =
2878 if usebookmarks
2879 then Array.of_list state.bookmarks
2880 else state.outlines
2882 if Array.length outlines = 0
2883 then (
2884 showtext ' ' errmsg;
2886 else (
2887 state.text <- source#greetmsg;
2888 Glut.setCursor Glut.CURSOR_INHERIT;
2889 let pageno =
2890 match state.layout with
2891 | [] -> -1
2892 | {pageno=pageno} :: _ -> pageno
2894 source#reset pageno outlines;
2895 state.uioh <- coe (new outlinelistview ~source);
2896 G.postRedisplay "enter selector";
2900 let enteroutlinemode =
2901 let f = enterselector false in
2902 fun ()-> f "Document has no outline";
2905 let enterbookmarkmode =
2906 let f = enterselector true in
2907 fun () -> f "Document has no bookmarks (yet)";
2910 let color_of_string s =
2911 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
2912 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
2916 let color_to_string (r, g, b) =
2917 let r = truncate (r *. 256.0)
2918 and g = truncate (g *. 256.0)
2919 and b = truncate (b *. 256.0) in
2920 Printf.sprintf "%d/%d/%d" r g b
2923 let irect_of_string s =
2924 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
2927 let irect_to_string (x0,y0,x1,y1) =
2928 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
2931 let makecheckers () =
2932 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
2933 following to say:
2934 converted by Issac Trotts. July 25, 2002 *)
2935 let image_height = 64
2936 and image_width = 64 in
2938 let make_image () =
2939 let image =
2940 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
2942 for i = 0 to image_width - 1 do
2943 for j = 0 to image_height - 1 do
2944 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
2945 (if (i land 8 ) lxor (j land 8) = 0
2946 then [|255;255;255|] else [|200;200;200|])
2947 done
2948 done;
2949 image
2951 let image = make_image () in
2952 let id = GlTex.gen_texture () in
2953 GlTex.bind_texture `texture_2d id;
2954 GlPix.store (`unpack_alignment 1);
2955 GlTex.image2d image;
2956 List.iter (GlTex.parameter ~target:`texture_2d)
2957 [ `wrap_s `repeat;
2958 `wrap_t `repeat;
2959 `mag_filter `nearest;
2960 `min_filter `nearest ];
2964 let setcheckers enabled =
2965 match state.texid with
2966 | None ->
2967 if enabled then state.texid <- Some (makecheckers ())
2969 | Some texid ->
2970 if not enabled
2971 then (
2972 GlTex.delete_texture texid;
2973 state.texid <- None;
2977 let int_of_string_with_suffix s =
2978 let l = String.length s in
2979 let s1, shift =
2980 if l > 1
2981 then
2982 let suffix = Char.lowercase s.[l-1] in
2983 match suffix with
2984 | 'k' -> String.sub s 0 (l-1), 10
2985 | 'm' -> String.sub s 0 (l-1), 20
2986 | 'g' -> String.sub s 0 (l-1), 30
2987 | _ -> s, 0
2988 else s, 0
2990 let n = int_of_string s1 in
2991 let m = n lsl shift in
2992 if m < 0 || m < n
2993 then raise (Failure "value too large")
2994 else m
2997 let string_with_suffix_of_int n =
2998 if n = 0
2999 then "0"
3000 else
3001 let n, s =
3002 if n = 0
3003 then 0, ""
3004 else (
3005 if n land ((1 lsl 20) - 1) = 0
3006 then n lsr 20, "M"
3007 else (
3008 if n land ((1 lsl 10) - 1) = 0
3009 then n lsr 10, "K"
3010 else n, ""
3014 let rec loop s n =
3015 let h = n mod 1000 in
3016 let n = n / 1000 in
3017 if n = 0
3018 then string_of_int h ^ s
3019 else (
3020 let s = Printf.sprintf "_%03d%s" h s in
3021 loop s n
3024 loop "" n ^ s;
3027 let describe_location () =
3028 let f (fn, _) l =
3029 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3031 let fn, ln = List.fold_left f (-1, -1) state.layout in
3032 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3033 let percent =
3034 if maxy <= 0
3035 then 100.
3036 else (100. *. (float state.y /. float maxy))
3038 if fn = ln
3039 then
3040 Printf.sprintf "page %d of %d [%.2f%%]"
3041 (fn+1) state.pagecount percent
3042 else
3043 Printf.sprintf
3044 "pages %d-%d of %d [%.2f%%]"
3045 (fn+1) (ln+1) state.pagecount percent
3048 let enterinfomode =
3049 let btos b = if b then "\xe2\x88\x9a" else "" in
3050 let showextended = ref false in
3051 let leave mode = function
3052 | Confirm -> state.mode <- mode
3053 | Cancel -> state.mode <- mode in
3054 let src =
3055 (object
3056 val mutable m_first_time = true
3057 val mutable m_l = []
3058 val mutable m_a = [||]
3059 val mutable m_prev_uioh = nouioh
3060 val mutable m_prev_mode = View
3062 inherit lvsourcebase
3064 method reset prev_mode prev_uioh =
3065 m_a <- Array.of_list (List.rev m_l);
3066 m_l <- [];
3067 m_prev_mode <- prev_mode;
3068 m_prev_uioh <- prev_uioh;
3069 if m_first_time
3070 then (
3071 let rec loop n =
3072 if n >= Array.length m_a
3073 then ()
3074 else
3075 match m_a.(n) with
3076 | _, _, _, Action _ -> m_active <- n
3077 | _ -> loop (n+1)
3079 loop 0;
3080 m_first_time <- false;
3083 method int name get set =
3084 m_l <-
3085 (name, `int get, 1, Action (
3086 fun u ->
3087 let ondone s =
3088 try set (int_of_string s)
3089 with exn ->
3090 state.text <- Printf.sprintf "bad integer `%s': %s"
3091 s (Printexc.to_string exn)
3093 state.text <- "";
3094 let te = name ^ ": ", "", None, intentry, ondone in
3095 state.mode <- Textentry (te, leave m_prev_mode);
3097 )) :: m_l
3099 method int_with_suffix name get set =
3100 m_l <-
3101 (name, `intws get, 1, Action (
3102 fun u ->
3103 let ondone s =
3104 try set (int_of_string_with_suffix s)
3105 with exn ->
3106 state.text <- Printf.sprintf "bad integer `%s': %s"
3107 s (Printexc.to_string exn)
3109 state.text <- "";
3110 let te =
3111 name ^ ": ", "", None, intentry_with_suffix, ondone
3113 state.mode <- Textentry (te, leave m_prev_mode);
3115 )) :: m_l
3117 method bool ?(offset=1) ?(btos=btos) name get set =
3118 m_l <-
3119 (name, `bool (btos, get), offset, Action (
3120 fun u ->
3121 let v = get () in
3122 set (not v);
3124 )) :: m_l
3126 method color name get set =
3127 m_l <-
3128 (name, `color get, 1, Action (
3129 fun u ->
3130 let invalid = (nan, nan, nan) in
3131 let ondone s =
3132 let c =
3133 try color_of_string s
3134 with exn ->
3135 state.text <- Printf.sprintf "bad color `%s': %s"
3136 s (Printexc.to_string exn);
3137 invalid
3139 if c <> invalid
3140 then set c;
3142 let te = name ^ ": ", "", None, textentry, ondone in
3143 state.text <- color_to_string (get ());
3144 state.mode <- Textentry (te, leave m_prev_mode);
3146 )) :: m_l
3148 method string name get set =
3149 m_l <-
3150 (name, `string get, 1, Action (
3151 fun u ->
3152 let ondone s = set s in
3153 let te = name ^ ": ", "", None, textentry, ondone in
3154 state.mode <- Textentry (te, leave m_prev_mode);
3156 )) :: m_l
3158 method colorspace name get set =
3159 m_l <-
3160 (name, `string get, 1, Action (
3161 fun _ ->
3162 let source =
3163 let vals = [| "rgb"; "bgr"; "gray" |] in
3164 (object
3165 inherit lvsourcebase
3167 initializer
3168 m_active <- int_of_colorspace conf.colorspace;
3169 m_first <- 0;
3171 method getitemcount = Array.length vals
3172 method getitem n = (vals.(n), 0)
3173 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3174 ignore (uioh, first, pan, qsearch);
3175 if not cancel then set active;
3176 None
3177 method hasaction _ = true
3178 end)
3180 state.text <- "";
3181 coe (new listview ~source ~trusted:true)
3182 )) :: m_l
3184 method caption s offset =
3185 m_l <- (s, `empty, offset, Noaction) :: m_l
3187 method caption2 s f offset =
3188 m_l <- (s, `string f, offset, Noaction) :: m_l
3190 method getitemcount = Array.length m_a
3192 method getitem n =
3193 let tostr = function
3194 | `int f -> string_of_int (f ())
3195 | `intws f -> string_with_suffix_of_int (f ())
3196 | `string f -> f ()
3197 | `color f -> color_to_string (f ())
3198 | `bool (btos, f) -> btos (f ())
3199 | `empty -> ""
3201 let name, t, offset, _ = m_a.(n) in
3202 ((let s = tostr t in
3203 if String.length s > 0
3204 then Printf.sprintf "%s\t%s" name s
3205 else name),
3206 offset)
3208 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3209 let uiohopt =
3210 if not cancel
3211 then (
3212 m_qsearch <- qsearch;
3213 let uioh =
3214 match m_a.(active) with
3215 | _, _, _, Action f -> f uioh
3216 | _ -> uioh
3218 Some uioh
3220 else None
3222 m_active <- active;
3223 m_first <- first;
3224 m_pan <- pan;
3225 uiohopt
3227 method hasaction n =
3228 match m_a.(n) with
3229 | _, _, _, Action _ -> true
3230 | _ -> false
3231 end)
3233 let rec fillsrc prevmode prevuioh =
3234 let sep () = src#caption "" 0 in
3235 let colorp name get set =
3236 src#string name
3237 (fun () -> color_to_string (get ()))
3238 (fun v ->
3240 let c = color_of_string v in
3241 set c
3242 with exn ->
3243 state.text <- Printf.sprintf "bad color `%s': %s"
3244 v (Printexc.to_string exn);
3247 let oldmode = state.mode in
3248 let birdseye = isbirdseye state.mode in
3250 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3252 src#bool "presentation mode"
3253 (fun () -> conf.presentation)
3254 (fun v ->
3255 conf.presentation <- v;
3256 state.anchor <- getanchor ();
3257 represent ());
3259 src#bool "ignore case in searches"
3260 (fun () -> conf.icase)
3261 (fun v -> conf.icase <- v);
3263 src#bool "preload"
3264 (fun () -> conf.preload)
3265 (fun v -> conf.preload <- v);
3267 src#bool "highlight links"
3268 (fun () -> conf.hlinks)
3269 (fun v -> conf.hlinks <- v);
3271 src#bool "under info"
3272 (fun () -> conf.underinfo)
3273 (fun v -> conf.underinfo <- v);
3275 src#bool "persistent bookmarks"
3276 (fun () -> conf.savebmarks)
3277 (fun v -> conf.savebmarks <- v);
3279 src#bool "proportional display"
3280 (fun () -> conf.proportional)
3281 (fun v -> reqlayout conf.angle v);
3283 src#bool "trim margins"
3284 (fun () -> conf.trimmargins)
3285 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3287 src#bool "persistent location"
3288 (fun () -> conf.jumpback)
3289 (fun v -> conf.jumpback <- v);
3291 sep ();
3292 src#int "vertical margin"
3293 (fun () -> conf.interpagespace)
3294 (fun n ->
3295 conf.interpagespace <- n;
3296 let pageno, py =
3297 match state.layout with
3298 | [] -> 0, 0
3299 | l :: _ ->
3300 l.pageno, l.pagey
3302 state.maxy <- calcheight ();
3303 let y = getpagey pageno in
3304 gotoy (y + py)
3307 src#int "page bias"
3308 (fun () -> conf.pagebias)
3309 (fun v -> conf.pagebias <- v);
3311 src#int "scroll step"
3312 (fun () -> conf.scrollstep)
3313 (fun n -> conf.scrollstep <- n);
3315 src#int "auto scroll step"
3316 (fun () ->
3317 match state.autoscroll with
3318 | Some step -> step
3319 | _ -> conf.autoscrollstep)
3320 (fun n ->
3321 if state.autoscroll <> None
3322 then state.autoscroll <- Some n;
3323 conf.autoscrollstep <- n);
3325 src#int "zoom"
3326 (fun () -> truncate (conf.zoom *. 100.))
3327 (fun v -> setzoom ((float v) /. 100.));
3329 src#int "rotation"
3330 (fun () -> conf.angle)
3331 (fun v -> reqlayout v conf.proportional);
3333 src#int "scroll bar width"
3334 (fun () -> state.scrollw)
3335 (fun v ->
3336 state.scrollw <- v;
3337 conf.scrollbw <- v;
3338 reshape conf.winw conf.winh;
3341 src#int "scroll handle height"
3342 (fun () -> conf.scrollh)
3343 (fun v -> conf.scrollh <- v;);
3345 src#int "thumbnail width"
3346 (fun () -> conf.thumbw)
3347 (fun v ->
3348 conf.thumbw <- min 4096 v;
3349 match oldmode with
3350 | Birdseye beye ->
3351 leavebirdseye beye false;
3352 enterbirdseye ()
3353 | _ -> ()
3356 sep ();
3357 src#caption "Presentation mode" 0;
3358 src#bool "scrollbar visible"
3359 (fun () -> conf.scrollbarinpm)
3360 (fun v ->
3361 if v != conf.scrollbarinpm
3362 then (
3363 conf.scrollbarinpm <- v;
3364 if conf.presentation
3365 then (
3366 state.scrollw <- if v then conf.scrollbw else 0;
3367 reshape conf.winw conf.winh;
3372 sep ();
3373 src#caption "Pixmap cache" 0;
3374 src#int_with_suffix "size (advisory)"
3375 (fun () -> conf.memlimit)
3376 (fun v -> conf.memlimit <- v);
3378 src#caption2 "used"
3379 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3380 (string_with_suffix_of_int state.memused)
3381 (Hashtbl.length state.tilemap)) 1;
3383 sep ();
3384 src#caption "Layout" 0;
3385 src#caption2 "Dimension"
3386 (fun () ->
3387 Printf.sprintf "%dx%d (virtual %dx%d)"
3388 conf.winw conf.winh
3389 state.w state.maxy)
3391 if conf.debug
3392 then
3393 src#caption2 "Position" (fun () ->
3394 Printf.sprintf "%dx%d" state.x state.y
3396 else
3397 src#caption2 "Visible" (fun () -> describe_location ()) 1
3400 sep ();
3401 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3402 "Save these parameters as global defaults at exit"
3403 (fun () -> conf.bedefault)
3404 (fun v -> conf.bedefault <- v)
3407 sep ();
3408 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3409 src#bool ~offset:0 ~btos "Extended parameters"
3410 (fun () -> !showextended)
3411 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3412 if !showextended
3413 then (
3414 src#bool "checkers"
3415 (fun () -> conf.checkers)
3416 (fun v -> conf.checkers <- v; setcheckers v);
3417 src#bool "verbose"
3418 (fun () -> conf.verbose)
3419 (fun v -> conf.verbose <- v);
3420 src#bool "invert colors"
3421 (fun () -> conf.invert)
3422 (fun v -> conf.invert <- v);
3423 src#bool "max fit"
3424 (fun () -> conf.maxhfit)
3425 (fun v -> conf.maxhfit <- v);
3426 src#bool "redirect stderr"
3427 (fun () -> conf.redirectstderr)
3428 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3429 src#string "uri launcher"
3430 (fun () -> conf.urilauncher)
3431 (fun v -> conf.urilauncher <- v);
3432 src#string "tile size"
3433 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3434 (fun v ->
3436 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3437 conf.tileh <- max 64 w;
3438 conf.tilew <- max 64 h;
3439 flushtiles ();
3440 with exn ->
3441 state.text <- Printf.sprintf "bad tile size `%s': %s"
3442 v (Printexc.to_string exn));
3443 src#int "anti-aliasing level"
3444 (fun () -> conf.aalevel)
3445 (fun v ->
3446 conf.aalevel <- bound v 0 8;
3447 state.anchor <- getanchor ();
3448 opendoc state.path state.password;
3450 src#int "ui font size"
3451 (fun () -> fstate.fontsize)
3452 (fun v -> setfontsize (bound v 5 100));
3453 colorp "background color"
3454 (fun () -> conf.bgcolor)
3455 (fun v -> conf.bgcolor <- v);
3456 src#bool "crop hack"
3457 (fun () -> conf.crophack)
3458 (fun v -> conf.crophack <- v);
3459 src#string "trim fuzz"
3460 (fun () -> irect_to_string conf.trimfuzz)
3461 (fun v ->
3463 conf.trimfuzz <- irect_of_string v;
3464 if conf.trimmargins
3465 then settrim true conf.trimfuzz;
3466 with exn ->
3467 state.text <- Printf.sprintf "bad irect `%s': %s"
3468 v (Printexc.to_string exn)
3470 src#string "throttle"
3471 (fun () ->
3472 match conf.maxwait with
3473 | None -> "show place holder if page is not ready"
3474 | Some time ->
3475 if time = infinity
3476 then "wait for page to fully render"
3477 else
3478 "wait " ^ string_of_float time
3479 ^ " seconds before showing placeholder"
3481 (fun v ->
3483 let f = float_of_string v in
3484 if f <= 0.0
3485 then conf.maxwait <- None
3486 else conf.maxwait <- Some f
3487 with exn ->
3488 state.text <- Printf.sprintf "bad time `%s': %s"
3489 v (Printexc.to_string exn)
3491 src#colorspace "color space"
3492 (fun () -> colorspace_to_string conf.colorspace)
3493 (fun v ->
3494 conf.colorspace <- colorspace_of_int v;
3495 wcmd "cs" [`i v];
3496 load state.layout;
3500 sep ();
3501 src#caption "Document" 0;
3502 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3503 if conf.trimmargins
3504 then (
3505 sep ();
3506 src#caption "Trimmed margins" 0;
3507 src#caption2 "Dimensions"
3508 (fun () -> string_of_int (List.length state.pdims)) 1;
3511 src#reset prevmode prevuioh;
3513 fun () ->
3514 state.text <- "";
3515 let prevmode = state.mode
3516 and prevuioh = state.uioh in
3517 fillsrc prevmode prevuioh;
3518 let source = (src :> lvsource) in
3519 state.uioh <- coe (object (self)
3520 inherit listview ~source ~trusted:true as super
3521 val mutable m_prevmemused = 0
3522 method infochanged = function
3523 | Memused ->
3524 if m_prevmemused != state.memused
3525 then (
3526 m_prevmemused <- state.memused;
3527 G.postRedisplay "memusedchanged";
3529 | Pdim -> G.postRedisplay "pdimchanged"
3530 | Docinfo -> fillsrc prevmode prevuioh
3532 method special key =
3533 if Glut.getModifiers () land Glut.active_ctrl = 0
3534 then
3535 match key with
3536 | Glut.KEY_LEFT -> coe (self#updownlevel ~-1)
3537 | Glut.KEY_RIGHT -> coe (self#updownlevel 1)
3538 | _ -> super#special key
3539 else super#special key
3540 end);
3541 G.postRedisplay "info";
3544 let enterhelpmode =
3545 let source =
3546 (object
3547 inherit lvsourcebase
3548 method getitemcount = Array.length state.help
3549 method getitem n =
3550 let s, n, _ = state.help.(n) in
3551 (s, n)
3553 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3554 let optuioh =
3555 if not cancel
3556 then (
3557 m_qsearch <- qsearch;
3558 match state.help.(active) with
3559 | _, _, Action f -> Some (f uioh)
3560 | _ -> Some (uioh)
3562 else None
3564 m_active <- active;
3565 m_first <- first;
3566 m_pan <- pan;
3567 optuioh
3569 method hasaction n =
3570 match state.help.(n) with
3571 | _, _, Action _ -> true
3572 | _ -> false
3574 initializer
3575 m_active <- -1
3576 end)
3577 in fun () ->
3578 state.uioh <- coe (new listview ~source ~trusted:true);
3579 G.postRedisplay "help";
3582 let entermsgsmode =
3583 let msgsource =
3584 let re = Str.regexp "[\r\n]" in
3585 (object (self)
3586 inherit lvsourcebase
3587 val mutable m_items = [||]
3589 method getitemcount =
3590 if state.newerrmsgs
3591 then self#reset;
3592 1 + Array.length m_items
3593 method getitem n =
3594 if n = 0
3595 then "[Clear]", 0
3596 else m_items.(n-1), 0
3598 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3599 ignore uioh;
3600 if not cancel
3601 then (
3602 if active = 0
3603 then Buffer.clear state.errmsgs;
3604 m_qsearch <- qsearch;
3606 m_active <- active;
3607 m_first <- first;
3608 m_pan <- pan;
3609 None
3611 method hasaction n = n = 0
3612 method reset =
3613 state.newerrmsgs <- false;
3614 let l = Str.split re (Buffer.contents state.errmsgs) in
3615 m_items <- Array.of_list l
3617 initializer
3618 m_active <- 0
3619 end)
3620 in fun () ->
3621 state.text <- "";
3622 msgsource#reset;
3623 let source = (msgsource :> lvsource) in
3624 state.uioh <- coe (new listview ~source ~trusted:false);
3625 G.postRedisplay "msgs";
3628 let quickbookmark ?title () =
3629 match state.layout with
3630 | [] -> ()
3631 | l :: _ ->
3632 let title =
3633 match title with
3634 | None ->
3635 let sec = Unix.gettimeofday () in
3636 let tm = Unix.localtime sec in
3637 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
3638 (l.pageno+1)
3639 tm.Unix.tm_mday
3640 tm.Unix.tm_mon
3641 (tm.Unix.tm_year + 1900)
3642 tm.Unix.tm_hour
3643 tm.Unix.tm_min
3644 | Some title -> title
3646 state.bookmarks <-
3647 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
3648 :: state.bookmarks
3651 let doreshape w h =
3652 state.fullscreen <- None;
3653 Glut.reshapeWindow w h;
3656 let viewkeyboard key =
3657 let enttext te =
3658 let mode = state.mode in
3659 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3660 state.text <- "";
3661 enttext ();
3662 G.postRedisplay "view:enttext"
3664 let c = Char.chr key in
3665 match c with
3666 | '\027' | 'q' -> (* escape *)
3667 begin match state.mstate with
3668 | Mzoomrect _ ->
3669 state.mstate <- Mnone;
3670 Glut.setCursor Glut.CURSOR_INHERIT;
3671 G.postRedisplay "kill zoom rect";
3672 | _ ->
3673 raise Quit
3674 end;
3676 | '\008' -> (* backspace *)
3677 let y = getnav ~-1 in
3678 gotoy_and_clear_text y
3680 | 'o' ->
3681 enteroutlinemode ()
3683 | 'u' ->
3684 state.rects <- [];
3685 state.text <- "";
3686 G.postRedisplay "dehighlight";
3688 | '/' | '?' ->
3689 let ondone isforw s =
3690 cbput state.hists.pat s;
3691 state.searchpattern <- s;
3692 search s isforw
3694 let s = String.create 1 in
3695 s.[0] <- c;
3696 enttext (s, "", Some (onhist state.hists.pat),
3697 textentry, ondone (c ='/'))
3699 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3700 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3701 setzoom (conf.zoom +. incr)
3703 | '+' ->
3704 let ondone s =
3705 let n =
3706 try int_of_string s with exc ->
3707 state.text <- Printf.sprintf "bad integer `%s': %s"
3708 s (Printexc.to_string exc);
3709 max_int
3711 if n != max_int
3712 then (
3713 conf.pagebias <- n;
3714 state.text <- "page bias is now " ^ string_of_int n;
3717 enttext ("page bias: ", "", None, intentry, ondone)
3719 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3720 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3721 setzoom (max 0.01 (conf.zoom -. decr))
3723 | '-' ->
3724 let ondone msg = state.text <- msg in
3725 enttext (
3726 "option [acfhilpstvAPRSZTI]: ", "", None,
3727 optentry state.mode, ondone
3730 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3731 setzoom 1.0
3733 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3734 let zoom = zoomforh conf.winw conf.winh state.scrollw in
3735 if zoom < 1.0
3736 then setzoom zoom
3738 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3739 togglebirdseye ()
3741 | '0' .. '9' ->
3742 let ondone s =
3743 let n =
3744 try int_of_string s with exc ->
3745 state.text <- Printf.sprintf "bad integer `%s': %s"
3746 s (Printexc.to_string exc);
3749 if n >= 0
3750 then (
3751 addnav ();
3752 cbput state.hists.pag (string_of_int n);
3753 gotoy_and_clear_text (getpagey (n + conf.pagebias - 1))
3756 let pageentry text key =
3757 match Char.unsafe_chr key with
3758 | 'g' -> TEdone text
3759 | _ -> intentry text key
3761 let text = "x" in text.[0] <- c;
3762 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
3764 | 'b' ->
3765 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
3766 reshape conf.winw conf.winh;
3768 | 'l' ->
3769 conf.hlinks <- not conf.hlinks;
3770 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3771 G.postRedisplay "toggle highlightlinks";
3773 | 'a' ->
3774 begin match state.autoscroll with
3775 | Some step ->
3776 conf.autoscrollstep <- step;
3777 state.autoscroll <- None
3778 | None ->
3779 if conf.autoscrollstep = 0
3780 then state.autoscroll <- Some 1
3781 else state.autoscroll <- Some conf.autoscrollstep
3784 | 'P' ->
3785 conf.presentation <- not conf.presentation;
3786 if conf.presentation
3787 then (
3788 if not conf.scrollbarinpm
3789 then state.scrollw <- 0;
3791 else
3792 state.scrollw <- conf.scrollbw;
3794 showtext ' ' ("presentation mode " ^
3795 if conf.presentation then "on" else "off");
3796 state.anchor <- getanchor ();
3797 represent ()
3799 | 'f' ->
3800 begin match state.fullscreen with
3801 | None ->
3802 state.fullscreen <- Some (conf.winw, conf.winh);
3803 Glut.fullScreen ()
3804 | Some (w, h) ->
3805 state.fullscreen <- None;
3806 doreshape w h
3809 | 'g' ->
3810 gotoy_and_clear_text 0
3812 | 'G' ->
3813 gotopage1 (state.pagecount - 1) 0
3815 | 'n' ->
3816 search state.searchpattern true
3818 | 'p' | 'N' ->
3819 search state.searchpattern false
3821 | 't' ->
3822 begin match state.layout with
3823 | [] -> ()
3824 | l :: _ ->
3825 gotoy_and_clear_text (getpagey l.pageno)
3828 | ' ' ->
3829 begin match List.rev state.layout with
3830 | [] -> ()
3831 | l :: _ ->
3832 let pageno = min (l.pageno+1) (state.pagecount-1) in
3833 gotoy_and_clear_text (getpagey pageno)
3836 | '\127' -> (* del *)
3837 begin match state.layout with
3838 | [] -> ()
3839 | l :: _ ->
3840 let pageno = max 0 (l.pageno-1) in
3841 gotoy_and_clear_text (getpagey pageno)
3844 | '=' ->
3845 showtext ' ' (describe_location ());
3847 | 'w' ->
3848 begin match state.layout with
3849 | [] -> ()
3850 | l :: _ ->
3851 doreshape (l.pagew + state.scrollw) l.pageh;
3852 G.postRedisplay "w"
3855 | '\'' ->
3856 enterbookmarkmode ()
3858 | 'h' ->
3859 enterhelpmode ()
3861 | 'i' ->
3862 enterinfomode ()
3864 | 'e' when conf.redirectstderr ->
3865 entermsgsmode ()
3867 | 'm' ->
3868 let ondone s =
3869 match state.layout with
3870 | l :: _ ->
3871 state.bookmarks <-
3872 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
3873 :: state.bookmarks
3874 | _ -> ()
3876 enttext ("bookmark: ", "", None, textentry, ondone)
3878 | '~' ->
3879 quickbookmark ();
3880 showtext ' ' "Quick bookmark added";
3882 | 'z' ->
3883 begin match state.layout with
3884 | l :: _ ->
3885 let rect = getpdimrect l.pagedimno in
3886 let w, h =
3887 if conf.crophack
3888 then
3889 (truncate (1.8 *. (rect.(1) -. rect.(0))),
3890 truncate (1.2 *. (rect.(3) -. rect.(0))))
3891 else
3892 (truncate (rect.(1) -. rect.(0)),
3893 truncate (rect.(3) -. rect.(0)))
3895 let w = truncate ((float w)*.conf.zoom)
3896 and h = truncate ((float h)*.conf.zoom) in
3897 if w != 0 && h != 0
3898 then (
3899 state.anchor <- getanchor ();
3900 doreshape (w + state.scrollw) (h + conf.interpagespace)
3902 G.postRedisplay "z";
3904 | [] -> ()
3907 | '\000' -> (* ctrl-2 *)
3908 let maxw = getmaxw () in
3909 if maxw > 0.0
3910 then setzoom (maxw /. float conf.winw)
3912 | '<' | '>' ->
3913 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
3915 | '[' | ']' ->
3916 conf.colorscale <-
3917 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
3919 G.postRedisplay "brightness";
3921 | 'k' ->
3922 begin match state.mode with
3923 | Birdseye beye -> upbirdseye beye
3924 | _ -> gotoy (clamp (-conf.scrollstep))
3927 | 'j' ->
3928 begin match state.mode with
3929 | Birdseye beye -> downbirdseye beye
3930 | _ -> gotoy (clamp conf.scrollstep)
3933 | 'r' ->
3934 state.anchor <- getanchor ();
3935 opendoc state.path state.password
3937 | 'v' when conf.debug ->
3938 state.rects <- [];
3939 List.iter (fun l ->
3940 match getopaque l.pageno with
3941 | None -> ()
3942 | Some opaque ->
3943 let x0, y0, x1, y1 = pagebbox opaque in
3944 let a,b = float x0, float y0 in
3945 let c,d = float x1, float y0 in
3946 let e,f = float x1, float y1 in
3947 let h,j = float x0, float y1 in
3948 let rect = (a,b,c,d,e,f,h,j) in
3949 debugrect rect;
3950 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
3951 ) state.layout;
3952 G.postRedisplay "v";
3954 | _ ->
3955 vlog "huh? %d %c" key (Char.chr key);
3958 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
3959 match key with
3960 | 27 -> (* escape *)
3961 leavebirdseye beye true
3963 | 12 -> (* ctrl-l *)
3964 let y, h = getpageyh pageno in
3965 let top = (conf.winh - h) / 2 in
3966 gotoy (max 0 (y - top))
3968 | 13 -> (* enter *)
3969 leavebirdseye beye false
3971 | _ ->
3972 viewkeyboard key
3975 let keyboard ~key ~x ~y =
3976 ignore x;
3977 ignore y;
3978 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
3979 then wcmd "interrupt" []
3980 else state.uioh <- state.uioh#key key
3983 let birdseyespecial key ((conf, leftx, _, hooverpageno, anchor) as beye) =
3984 match key with
3985 | Glut.KEY_UP -> upbirdseye beye
3986 | Glut.KEY_DOWN -> downbirdseye beye
3988 | Glut.KEY_PAGE_UP ->
3989 begin match state.layout with
3990 | l :: _ ->
3991 if l.pagey != 0
3992 then (
3993 state.mode <- Birdseye (
3994 conf, leftx, l.pageno, hooverpageno, anchor
3996 gotopage1 l.pageno 0;
3998 else (
3999 let layout = layout (state.y-conf.winh) conf.winh in
4000 match layout with
4001 | [] -> gotoy (clamp (-conf.winh))
4002 | l :: _ ->
4003 state.mode <- Birdseye (
4004 conf, leftx, l.pageno, hooverpageno, anchor
4006 gotopage1 l.pageno 0
4009 | [] -> gotoy (clamp (-conf.winh))
4010 end;
4012 | Glut.KEY_PAGE_DOWN ->
4013 begin match List.rev state.layout with
4014 | l :: _ ->
4015 let layout = layout (state.y + conf.winh) conf.winh in
4016 begin match layout with
4017 | [] ->
4018 let incr = l.pageh - l.pagevh in
4019 if incr = 0
4020 then (
4021 state.mode <-
4022 Birdseye (
4023 conf, leftx, state.pagecount - 1, hooverpageno, anchor
4025 G.postRedisplay "birdseye pagedown";
4027 else gotoy (clamp (incr + conf.interpagespace*2));
4029 | l :: _ ->
4030 state.mode <-
4031 Birdseye (conf, leftx, l.pageno, hooverpageno, anchor);
4032 gotopage1 l.pageno 0;
4035 | [] -> gotoy (clamp conf.winh)
4036 end;
4038 | Glut.KEY_HOME ->
4039 state.mode <- Birdseye (conf, leftx, 0, hooverpageno, anchor);
4040 gotopage1 0 0
4042 | Glut.KEY_END ->
4043 let pageno = state.pagecount - 1 in
4044 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
4045 if not (pagevisible state.layout pageno)
4046 then
4047 let h =
4048 match List.rev state.pdims with
4049 | [] -> conf.winh
4050 | (_, _, h, _) :: _ -> h
4052 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4053 else G.postRedisplay "birdseye end";
4054 | _ -> ()
4057 let setautoscrollspeed step goingdown =
4058 let incr = max 1 ((abs step) / 2) in
4059 let incr = if goingdown then incr else -incr in
4060 let astep = step + incr in
4061 state.autoscroll <- Some astep;
4064 let special ~key ~x ~y =
4065 ignore x;
4066 ignore y;
4067 state.uioh <- state.uioh#special key
4070 let drawpage l =
4071 let color =
4072 match state.mode with
4073 | Textentry _ -> scalecolor 0.4
4074 | View -> scalecolor 1.0
4075 | Birdseye (_, _, pageno, hooverpageno, _) ->
4076 if l.pageno = hooverpageno
4077 then scalecolor 0.9
4078 else (
4079 if l.pageno = pageno
4080 then scalecolor 1.0
4081 else scalecolor 0.8
4084 drawtiles l color;
4085 begin match getopaque l.pageno with
4086 | Some opaque ->
4087 if tileready l l.pagex l.pagey
4088 then
4089 let x = l.pagedispx - l.pagex
4090 and y = l.pagedispy - l.pagey in
4091 postprocess opaque conf.hlinks x y;
4093 | _ -> ()
4094 end;
4097 let scrollph y =
4098 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
4099 let sh = (float (maxy + conf.winh) /. float conf.winh) in
4100 let sh = float conf.winh /. sh in
4101 let sh = max sh (float conf.scrollh) in
4103 let percent =
4104 if y = state.maxy
4105 then 1.0
4106 else float y /. float maxy
4108 let position = (float conf.winh -. sh) *. percent in
4110 let position =
4111 if position +. sh > float conf.winh
4112 then float conf.winh -. sh
4113 else position
4115 position, sh;
4118 let scrollpw x =
4119 let winw = conf.winw - state.scrollw - 1 in
4120 let fwinw = float winw in
4121 let sw =
4122 let sw = fwinw /. float state.w in
4123 let sw = fwinw *. sw in
4124 max sw (float conf.scrollh)
4126 let position, sw =
4127 let f = state.w+winw in
4128 let r = float (winw-x) /. float f in
4129 let p = fwinw *. r in
4130 p-.sw/.2., sw
4132 let sw =
4133 if position +. sw > fwinw
4134 then fwinw -. position
4135 else sw
4137 position, sw;
4140 let scrollindicator () =
4141 GlDraw.color (0.64 , 0.64, 0.64);
4142 GlDraw.rect
4143 (float (conf.winw - state.scrollw), 0.)
4144 (float conf.winw, float conf.winh)
4146 GlDraw.rect
4147 (0., float (conf.winh - state.hscrollh))
4148 (float (conf.winw - state.scrollw - 1), float conf.winh)
4150 GlDraw.color (0.0, 0.0, 0.0);
4152 let position, sh = scrollph state.y in
4153 GlDraw.rect
4154 (float (conf.winw - state.scrollw), position)
4155 (float conf.winw, position +. sh)
4157 let position, sw = scrollpw state.x in
4158 GlDraw.rect
4159 (position, float (conf.winh - state.hscrollh))
4160 (position +. sw, float conf.winh)
4164 let pagetranslatepoint l x y =
4165 let dy = y - l.pagedispy in
4166 let y = dy + l.pagey in
4167 let dx = x - l.pagedispx in
4168 let x = dx + l.pagex in
4169 (x, y);
4172 let showsel () =
4173 match state.mstate with
4174 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4177 | Msel ((x0, y0), (x1, y1)) ->
4178 let rec loop = function
4179 | l :: ls ->
4180 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4181 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4182 then
4183 match getopaque l.pageno with
4184 | Some opaque ->
4185 let dx, dy = pagetranslatepoint l 0 0 in
4186 let x0 = x0 + dx
4187 and y0 = y0 + dy
4188 and x1 = x1 + dx
4189 and y1 = y1 + dy in
4190 GlMat.mode `modelview;
4191 GlMat.push ();
4192 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
4193 seltext opaque (x0, y0, x1, y1);
4194 GlMat.pop ();
4195 | _ -> ()
4196 else loop ls
4197 | [] -> ()
4199 loop state.layout
4202 let showrects () =
4203 Gl.enable `blend;
4204 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4205 GlDraw.polygon_mode `both `fill;
4206 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4207 List.iter
4208 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4209 List.iter (fun l ->
4210 if l.pageno = pageno
4211 then (
4212 let dx = float (l.pagedispx - l.pagex) in
4213 let dy = float (l.pagedispy - l.pagey) in
4214 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4215 GlDraw.begins `quads;
4217 GlDraw.vertex2 (x0+.dx, y0+.dy);
4218 GlDraw.vertex2 (x1+.dx, y1+.dy);
4219 GlDraw.vertex2 (x2+.dx, y2+.dy);
4220 GlDraw.vertex2 (x3+.dx, y3+.dy);
4222 GlDraw.ends ();
4224 ) state.layout
4225 ) state.rects
4227 Gl.disable `blend;
4230 let display () =
4231 GlClear.color (scalecolor2 conf.bgcolor);
4232 GlClear.clear [`color];
4233 List.iter drawpage state.layout;
4234 showrects ();
4235 showsel ();
4236 scrollindicator ();
4237 state.uioh#display;
4238 begin match state.mstate with
4239 | Mzoomrect ((x0, y0), (x1, y1)) ->
4240 Gl.enable `blend;
4241 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4242 GlDraw.polygon_mode `both `fill;
4243 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4244 GlDraw.rect (float x0, float y0)
4245 (float x1, float y1);
4246 Gl.disable `blend;
4247 | _ -> ()
4248 end;
4249 enttext ();
4250 Glut.swapBuffers ();
4253 let getunder x y =
4254 let rec f = function
4255 | l :: rest ->
4256 begin match getopaque l.pageno with
4257 | Some opaque ->
4258 let x0 = l.pagedispx in
4259 let x1 = x0 + l.pagevw in
4260 let y0 = l.pagedispy in
4261 let y1 = y0 + l.pagevh in
4262 if y >= y0 && y <= y1 && x >= x0 && x <= x1
4263 then
4264 let px, py = pagetranslatepoint l x y in
4265 match whatsunder opaque px py with
4266 | Unone -> f rest
4267 | under -> under
4268 else f rest
4269 | _ ->
4270 f rest
4272 | [] -> Unone
4274 f state.layout
4277 let zoomrect x y x1 y1 =
4278 let x0 = min x x1
4279 and x1 = max x x1
4280 and y0 = min y y1 in
4281 gotoy (state.y + y0);
4282 state.anchor <- getanchor ();
4283 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4284 let margin =
4285 if state.w < conf.winw - state.scrollw
4286 then (conf.winw - state.scrollw - state.w) / 2
4287 else 0
4289 state.x <- (state.x + margin) - x0;
4290 setzoom zoom;
4291 Glut.setCursor Glut.CURSOR_INHERIT;
4292 state.mstate <- Mnone;
4295 let scrollx x =
4296 let winw = conf.winw - state.scrollw - 1 in
4297 let s = float x /. float winw in
4298 let destx = truncate (float (state.w + winw) *. s) in
4299 state.x <- winw - destx;
4300 gotoy_and_clear_text state.y;
4301 state.mstate <- Mscrollx;
4304 let scrolly y =
4305 let s = float y /. float conf.winh in
4306 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4307 gotoy_and_clear_text desty;
4308 state.mstate <- Mscrolly;
4311 let viewmouse button bstate x y =
4312 match button with
4313 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4314 if Glut.getModifiers () land Glut.active_ctrl != 0
4315 then (
4316 match state.mstate with
4317 | Mzoom (oldn, i) ->
4318 if oldn = n
4319 then (
4320 if i = 2
4321 then
4322 let incr =
4323 match n with
4324 | 4 ->
4325 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4326 | _ ->
4327 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4329 let zoom = conf.zoom -. incr in
4330 setzoom zoom;
4331 state.mstate <- Mzoom (n, 0);
4332 else
4333 state.mstate <- Mzoom (n, i+1);
4335 else state.mstate <- Mzoom (n, 0)
4337 | _ -> state.mstate <- Mzoom (n, 0)
4339 else (
4340 match state.autoscroll with
4341 | Some step -> setautoscrollspeed step (n=4)
4342 | None ->
4343 let incr =
4344 if n = 3
4345 then -conf.scrollstep
4346 else conf.scrollstep
4348 let incr = incr * 2 in
4349 let y = clamp incr in
4350 gotoy_and_clear_text y
4353 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4354 if bstate = Glut.DOWN
4355 then (
4356 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4357 state.mstate <- Mpan (x, y)
4359 else
4360 state.mstate <- Mnone
4362 | Glut.RIGHT_BUTTON ->
4363 if bstate = Glut.DOWN
4364 then (
4365 Glut.setCursor Glut.CURSOR_CYCLE;
4366 let p = (x, y) in
4367 state.mstate <- Mzoomrect (p, p)
4369 else (
4370 match state.mstate with
4371 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4372 | _ ->
4373 Glut.setCursor Glut.CURSOR_INHERIT;
4374 state.mstate <- Mnone
4377 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4378 if bstate = Glut.DOWN
4379 then
4380 let position, sh = scrollph state.y in
4381 if y > truncate position && y < truncate (position +. sh)
4382 then state.mstate <- Mscrolly
4383 else scrolly y
4384 else
4385 state.mstate <- Mnone
4387 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4388 if bstate = Glut.DOWN
4389 then
4390 let position, sw = scrollpw state.x in
4391 if x > truncate position && x < truncate (position +. sw)
4392 then state.mstate <- Mscrollx
4393 else scrollx x
4394 else
4395 state.mstate <- Mnone
4397 | Glut.LEFT_BUTTON ->
4398 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4399 begin match dest with
4400 | Ulinkgoto (pageno, top) ->
4401 if pageno >= 0
4402 then (
4403 addnav ();
4404 gotopage1 pageno top;
4407 | Ulinkuri s ->
4408 gotouri s
4410 | Unone when bstate = Glut.DOWN ->
4411 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4412 state.mstate <- Mpan (x, y);
4414 | Unone | Utext _ ->
4415 if bstate = Glut.DOWN
4416 then (
4417 if conf.angle mod 360 = 0
4418 then (
4419 state.mstate <- Msel ((x, y), (x, y));
4420 G.postRedisplay "mouse select";
4423 else (
4424 match state.mstate with
4425 | Mnone -> ()
4427 | Mzoom _ | Mscrollx | Mscrolly ->
4428 state.mstate <- Mnone
4430 | Mzoomrect ((x0, y0), _) ->
4431 zoomrect x0 y0 x y
4433 | Mpan _ ->
4434 Glut.setCursor Glut.CURSOR_INHERIT;
4435 state.mstate <- Mnone
4437 | Msel ((_, y0), (_, y1)) ->
4438 let f l =
4439 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4440 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4441 then
4442 match getopaque l.pageno with
4443 | Some opaque ->
4444 copysel opaque
4445 | _ -> ()
4447 List.iter f state.layout;
4448 copysel ""; (* ugly *)
4449 Glut.setCursor Glut.CURSOR_INHERIT;
4450 state.mstate <- Mnone;
4454 | _ -> ()
4457 let birdseyemouse button bstate x y
4458 (conf, leftx, _, hooverpageno, anchor) =
4459 match button with
4460 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4461 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4462 let rec loop = function
4463 | [] -> ()
4464 | l :: rest ->
4465 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4466 && x > margin && x < margin + l.pagew
4467 then (
4468 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4470 else loop rest
4472 loop state.layout
4473 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
4474 | _ -> ()
4477 let mouse bstate button x y =
4478 state.uioh <- state.uioh#button button bstate x y;
4481 let mouse ~button ~state ~x ~y = mouse state button x y;;
4483 let motion ~x ~y =
4484 state.uioh <- state.uioh#motion x y
4487 let pmotion ~x ~y =
4488 state.uioh <- state.uioh#pmotion x y;
4491 let uioh = object
4492 method display = ()
4494 method key key =
4495 begin match state.mode with
4496 | Textentry textentry -> textentrykeyboard key textentry
4497 | Birdseye birdseye -> birdseyekeyboard key birdseye
4498 | View -> viewkeyboard key
4499 end;
4500 state.uioh
4502 method special key =
4503 begin match state.mode with
4504 | View | (Birdseye _) when key = Glut.KEY_F9 ->
4505 togglebirdseye ()
4507 | Birdseye vals ->
4508 birdseyespecial key vals
4510 | View when key = Glut.KEY_F1 ->
4511 enterhelpmode ()
4513 | View ->
4514 begin match state.autoscroll with
4515 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
4516 setautoscrollspeed step (key = Glut.KEY_DOWN)
4518 | _ ->
4519 let y =
4520 match key with
4521 | Glut.KEY_F3 -> search state.searchpattern true; state.y
4522 | Glut.KEY_UP ->
4523 if Glut.getModifiers () land Glut.active_ctrl != 0
4524 then
4525 if Glut.getModifiers () land Glut.active_shift != 0
4526 then (setzoom state.prevzoom; state.y)
4527 else clamp (-conf.winh/2)
4528 else clamp (-conf.scrollstep)
4529 | Glut.KEY_DOWN ->
4530 if Glut.getModifiers () land Glut.active_ctrl != 0
4531 then
4532 if Glut.getModifiers () land Glut.active_shift != 0
4533 then (setzoom state.prevzoom; state.y)
4534 else clamp (conf.winh/2)
4535 else clamp (conf.scrollstep)
4536 | Glut.KEY_PAGE_UP ->
4537 if Glut.getModifiers () land Glut.active_ctrl != 0
4538 then
4539 match state.layout with
4540 | [] -> state.y
4541 | l :: _ -> state.y - l.pagey
4542 else
4543 clamp (-conf.winh)
4544 | Glut.KEY_PAGE_DOWN ->
4545 if Glut.getModifiers () land Glut.active_ctrl != 0
4546 then
4547 match List.rev state.layout with
4548 | [] -> state.y
4549 | l :: _ -> getpagey l.pageno
4550 else
4551 clamp conf.winh
4552 | Glut.KEY_HOME ->
4553 addnav ();
4555 | Glut.KEY_END ->
4556 addnav ();
4557 state.maxy - (if conf.maxhfit then conf.winh else 0)
4559 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
4560 Glut.getModifiers () land Glut.active_alt != 0 ->
4561 getnav (if key = Glut.KEY_LEFT then 1 else -1)
4563 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
4564 let dx =
4565 if Glut.getModifiers () land Glut.active_ctrl != 0
4566 then (conf.winw / 2)
4567 else 10
4569 state.x <- state.x - dx;
4570 state.y
4571 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
4572 let dx =
4573 if Glut.getModifiers () land Glut.active_ctrl != 0
4574 then (conf.winw / 2)
4575 else 10
4577 state.x <- state.x + dx;
4578 state.y
4580 | _ -> state.y
4582 gotoy_and_clear_text y
4585 | Textentry te -> textentryspecial key te
4586 end;
4587 state.uioh
4589 method button button bstate x y =
4590 begin match state.mode with
4591 | View -> viewmouse button bstate x y
4592 | Birdseye beye -> birdseyemouse button bstate x y beye
4593 | Textentry _ -> ()
4594 end;
4595 state.uioh
4597 method motion x y =
4598 begin match state.mode with
4599 | Textentry _ -> ()
4600 | View | Birdseye _ ->
4601 match state.mstate with
4602 | Mzoom _ | Mnone -> ()
4604 | Mpan (x0, y0) ->
4605 let dx = x - x0
4606 and dy = y0 - y in
4607 state.mstate <- Mpan (x, y);
4608 if conf.zoom > 1.0 then state.x <- state.x + dx;
4609 let y = clamp dy in
4610 gotoy_and_clear_text y
4612 | Msel (a, _) ->
4613 state.mstate <- Msel (a, (x, y));
4614 G.postRedisplay "motion select";
4616 | Mscrolly ->
4617 let y = min conf.winh (max 0 y) in
4618 scrolly y
4620 | Mscrollx ->
4621 let x = min conf.winw (max 0 x) in
4622 scrollx x
4624 | Mzoomrect (p0, _) ->
4625 state.mstate <- Mzoomrect (p0, (x, y));
4626 G.postRedisplay "motion zoomrect";
4627 end;
4628 state.uioh
4630 method pmotion x y =
4631 begin match state.mode with
4632 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4633 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4634 let rec loop = function
4635 | [] ->
4636 if hooverpageno != -1
4637 then (
4638 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4639 G.postRedisplay "pmotion birdseye no hoover";
4641 | l :: rest ->
4642 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4643 && x > margin && x < margin + l.pagew
4644 then (
4645 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4646 G.postRedisplay "pmotion birdseye hoover";
4648 else loop rest
4650 loop state.layout
4652 | Textentry _ -> ()
4654 | View ->
4655 match state.mstate with
4656 | Mnone ->
4657 begin match getunder x y with
4658 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
4659 | Ulinkuri uri ->
4660 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
4661 Glut.setCursor Glut.CURSOR_INFO
4662 | Ulinkgoto (page, _) ->
4663 if conf.underinfo
4664 then showtext 'p' ("age: " ^ string_of_int (page+1));
4665 Glut.setCursor Glut.CURSOR_INFO
4666 | Utext s ->
4667 if conf.underinfo then showtext 'f' ("ont: " ^ s);
4668 Glut.setCursor Glut.CURSOR_TEXT
4671 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
4673 end;
4674 state.uioh
4676 method infochanged _ = ()
4677 end;;
4679 module Config =
4680 struct
4681 open Parser
4683 let fontpath = ref "";;
4684 let wmclasshack = ref false;;
4686 let unent s =
4687 let l = String.length s in
4688 let b = Buffer.create l in
4689 unent b s 0 l;
4690 Buffer.contents b;
4693 let home =
4695 match platform with
4696 | Pwindows | Pmingw -> Sys.getenv "HOMEPATH"
4697 | _ -> Sys.getenv "HOME"
4698 with exn ->
4699 prerr_endline
4700 ("Can not determine home directory location: " ^
4701 Printexc.to_string exn);
4705 let config_of c attrs =
4706 let apply c k v =
4708 match k with
4709 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
4710 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
4711 | "case-insensitive-search" -> { c with icase = bool_of_string v }
4712 | "preload" -> { c with preload = bool_of_string v }
4713 | "page-bias" -> { c with pagebias = int_of_string v }
4714 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
4715 | "auto-scroll-step" ->
4716 { c with autoscrollstep = max 0 (int_of_string v) }
4717 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
4718 | "crop-hack" -> { c with crophack = bool_of_string v }
4719 | "throttle" ->
4720 let mw =
4721 match String.lowercase v with
4722 | "true" -> Some infinity
4723 | "false" -> None
4724 | f -> Some (float_of_string f)
4726 { c with maxwait = mw}
4727 | "highlight-links" -> { c with hlinks = bool_of_string v }
4728 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
4729 | "vertical-margin" ->
4730 { c with interpagespace = max 0 (int_of_string v) }
4731 | "zoom" ->
4732 let zoom = float_of_string v /. 100. in
4733 let zoom = max zoom 0.0 in
4734 { c with zoom = zoom }
4735 | "presentation" -> { c with presentation = bool_of_string v }
4736 | "rotation-angle" -> { c with angle = int_of_string v }
4737 | "width" -> { c with winw = max 20 (int_of_string v) }
4738 | "height" -> { c with winh = max 20 (int_of_string v) }
4739 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
4740 | "proportional-display" -> { c with proportional = bool_of_string v }
4741 | "pixmap-cache-size" ->
4742 { c with memlimit = max 2 (int_of_string_with_suffix v) }
4743 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
4744 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
4745 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
4746 | "persistent-location" -> { c with jumpback = bool_of_string v }
4747 | "background-color" -> { c with bgcolor = color_of_string v }
4748 | "scrollbar-in-presentation" ->
4749 { c with scrollbarinpm = bool_of_string v }
4750 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
4751 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
4752 | "memlimit" ->
4753 { c with mumemlimit = max 1024 (int_of_string_with_suffix v) }
4754 | "checkers" -> { c with checkers = bool_of_string v }
4755 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
4756 | "trim-margins" -> { c with trimmargins = bool_of_string v }
4757 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
4758 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
4759 | "uri-launcher" -> { c with urilauncher = unent v }
4760 | "color-space" -> { c with colorspace = colorspace_of_string v }
4761 | "invert-colors" -> { c with invert = bool_of_string v }
4762 | "brightness" -> { c with colorscale = float_of_string v }
4763 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
4764 | _ -> c
4765 with exn ->
4766 prerr_endline ("Error processing attribute (`" ^
4767 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
4770 let rec fold c = function
4771 | [] -> c
4772 | (k, v) :: rest ->
4773 let c = apply c k v in
4774 fold c rest
4776 fold c attrs;
4779 let fromstring f pos n v d =
4780 try f v
4781 with exn ->
4782 dolog "Error processing attribute (%S=%S) at %d\n%s"
4783 n v pos (Printexc.to_string exn)
4788 let bookmark_of attrs =
4789 let rec fold title page rely = function
4790 | ("title", v) :: rest -> fold v page rely rest
4791 | ("page", v) :: rest -> fold title v rely rest
4792 | ("rely", v) :: rest -> fold title page v rest
4793 | _ :: rest -> fold title page rely rest
4794 | [] -> title, page, rely
4796 fold "invalid" "0" "0" attrs
4799 let doc_of attrs =
4800 let rec fold path page rely pan = function
4801 | ("path", v) :: rest -> fold v page rely pan rest
4802 | ("page", v) :: rest -> fold path v rely pan rest
4803 | ("rely", v) :: rest -> fold path page v pan rest
4804 | ("pan", v) :: rest -> fold path page rely v rest
4805 | _ :: rest -> fold path page rely pan rest
4806 | [] -> path, page, rely, pan
4808 fold "" "0" "0" "0" attrs
4811 let setconf dst src =
4812 dst.scrollbw <- src.scrollbw;
4813 dst.scrollh <- src.scrollh;
4814 dst.icase <- src.icase;
4815 dst.preload <- src.preload;
4816 dst.pagebias <- src.pagebias;
4817 dst.verbose <- src.verbose;
4818 dst.scrollstep <- src.scrollstep;
4819 dst.maxhfit <- src.maxhfit;
4820 dst.crophack <- src.crophack;
4821 dst.autoscrollstep <- src.autoscrollstep;
4822 dst.maxwait <- src.maxwait;
4823 dst.hlinks <- src.hlinks;
4824 dst.underinfo <- src.underinfo;
4825 dst.interpagespace <- src.interpagespace;
4826 dst.zoom <- src.zoom;
4827 dst.presentation <- src.presentation;
4828 dst.angle <- src.angle;
4829 dst.winw <- src.winw;
4830 dst.winh <- src.winh;
4831 dst.savebmarks <- src.savebmarks;
4832 dst.memlimit <- src.memlimit;
4833 dst.proportional <- src.proportional;
4834 dst.texcount <- src.texcount;
4835 dst.sliceheight <- src.sliceheight;
4836 dst.thumbw <- src.thumbw;
4837 dst.jumpback <- src.jumpback;
4838 dst.bgcolor <- src.bgcolor;
4839 dst.scrollbarinpm <- src.scrollbarinpm;
4840 dst.tilew <- src.tilew;
4841 dst.tileh <- src.tileh;
4842 dst.mumemlimit <- src.mumemlimit;
4843 dst.checkers <- src.checkers;
4844 dst.aalevel <- src.aalevel;
4845 dst.trimmargins <- src.trimmargins;
4846 dst.trimfuzz <- src.trimfuzz;
4847 dst.urilauncher <- src.urilauncher;
4848 dst.colorspace <- src.colorspace;
4849 dst.invert <- src.invert;
4850 dst.colorscale <- src.colorscale;
4851 dst.redirectstderr <- src.redirectstderr;
4854 let get s =
4855 let h = Hashtbl.create 10 in
4856 let dc = { defconf with angle = defconf.angle } in
4857 let rec toplevel v t spos _ =
4858 match t with
4859 | Vdata | Vcdata | Vend -> v
4860 | Vopen ("llppconfig", _, closed) ->
4861 if closed
4862 then v
4863 else { v with f = llppconfig }
4864 | Vopen _ ->
4865 error "unexpected subelement at top level" s spos
4866 | Vclose _ -> error "unexpected close at top level" s spos
4868 and llppconfig v t spos _ =
4869 match t with
4870 | Vdata | Vcdata -> v
4871 | Vend -> error "unexpected end of input in llppconfig" s spos
4872 | Vopen ("defaults", attrs, closed) ->
4873 let c = config_of dc attrs in
4874 setconf dc c;
4875 if closed
4876 then v
4877 else { v with f = skip "defaults" (fun () -> v) }
4879 | Vopen ("ui-font", attrs, closed) ->
4880 let rec getsize size = function
4881 | [] -> size
4882 | ("size", v) :: rest ->
4883 let size =
4884 fromstring int_of_string spos "size" v fstate.fontsize in
4885 getsize size rest
4886 | l -> getsize size l
4888 fstate.fontsize <- getsize fstate.fontsize attrs;
4889 if closed
4890 then v
4891 else { v with f = uifont (Buffer.create 10) }
4893 | Vopen ("doc", attrs, closed) ->
4894 let pathent, spage, srely, span = doc_of attrs in
4895 let path = unent pathent
4896 and pageno = fromstring int_of_string spos "page" spage 0
4897 and rely = fromstring float_of_string spos "rely" srely 0.0
4898 and pan = fromstring int_of_string spos "pan" span 0 in
4899 let c = config_of dc attrs in
4900 let anchor = (pageno, rely) in
4901 if closed
4902 then (Hashtbl.add h path (c, [], pan, anchor); v)
4903 else { v with f = doc path pan anchor c [] }
4905 | Vopen _ ->
4906 error "unexpected subelement in llppconfig" s spos
4908 | Vclose "llppconfig" -> { v with f = toplevel }
4909 | Vclose _ -> error "unexpected close in llppconfig" s spos
4911 and uifont b v t spos epos =
4912 match t with
4913 | Vdata | Vcdata ->
4914 Buffer.add_substring b s spos (epos - spos);
4916 | Vopen (_, _, _) ->
4917 error "unexpected subelement in ui-font" s spos
4918 | Vclose "ui-font" ->
4919 if String.length !fontpath = 0
4920 then fontpath := Buffer.contents b;
4921 { v with f = llppconfig }
4922 | Vclose _ -> error "unexpected close in ui-font" s spos
4923 | Vend -> error "unexpected end of input in ui-font" s spos
4925 and doc path pan anchor c bookmarks v t spos _ =
4926 match t with
4927 | Vdata | Vcdata -> v
4928 | Vend -> error "unexpected end of input in doc" s spos
4929 | Vopen ("bookmarks", _, closed) ->
4930 if closed
4931 then v
4932 else { v with f = pbookmarks path pan anchor c bookmarks }
4934 | Vopen (_, _, _) ->
4935 error "unexpected subelement in doc" s spos
4937 | Vclose "doc" ->
4938 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
4939 { v with f = llppconfig }
4941 | Vclose _ -> error "unexpected close in doc" s spos
4943 and pbookmarks path pan anchor c bookmarks v t spos _ =
4944 match t with
4945 | Vdata | Vcdata -> v
4946 | Vend -> error "unexpected end of input in bookmarks" s spos
4947 | Vopen ("item", attrs, closed) ->
4948 let titleent, spage, srely = bookmark_of attrs in
4949 let page = fromstring int_of_string spos "page" spage 0
4950 and rely = fromstring float_of_string spos "rely" srely 0.0 in
4951 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
4952 if closed
4953 then { v with f = pbookmarks path pan anchor c bookmarks }
4954 else
4955 let f () = v in
4956 { v with f = skip "item" f }
4958 | Vopen _ ->
4959 error "unexpected subelement in bookmarks" s spos
4961 | Vclose "bookmarks" ->
4962 { v with f = doc path pan anchor c bookmarks }
4964 | Vclose _ -> error "unexpected close in bookmarks" s spos
4966 and skip tag f v t spos _ =
4967 match t with
4968 | Vdata | Vcdata -> v
4969 | Vend ->
4970 error ("unexpected end of input in skipped " ^ tag) s spos
4971 | Vopen (tag', _, closed) ->
4972 if closed
4973 then v
4974 else
4975 let f' () = { v with f = skip tag f } in
4976 { v with f = skip tag' f' }
4977 | Vclose ctag ->
4978 if tag = ctag
4979 then f ()
4980 else error ("unexpected close in skipped " ^ tag) s spos
4983 parse { f = toplevel; accu = () } s;
4984 h, dc;
4987 let do_load f ic =
4989 let len = in_channel_length ic in
4990 let s = String.create len in
4991 really_input ic s 0 len;
4992 f s;
4993 with
4994 | Parse_error (msg, s, pos) ->
4995 let subs = subs s pos in
4996 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
4997 failwith ("parse error: " ^ s)
4999 | exn ->
5000 failwith ("config load error: " ^ Printexc.to_string exn)
5003 let defconfpath =
5004 let dir =
5006 let dir = Filename.concat home ".config" in
5007 if Sys.is_directory dir then dir else home
5008 with _ -> home
5010 Filename.concat dir "llpp.conf"
5013 let confpath = ref defconfpath;;
5015 let load1 f =
5016 if Sys.file_exists !confpath
5017 then
5018 match
5019 (try Some (open_in_bin !confpath)
5020 with exn ->
5021 prerr_endline
5022 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5023 Printexc.to_string exn);
5024 None
5026 with
5027 | Some ic ->
5028 begin try
5029 f (do_load get ic)
5030 with exn ->
5031 prerr_endline
5032 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5033 Printexc.to_string exn);
5034 end;
5035 close_in ic;
5037 | None -> ()
5038 else
5039 f (Hashtbl.create 0, defconf)
5042 let load () =
5043 let f (h, dc) =
5044 let pc, pb, px, pa =
5046 Hashtbl.find h (Filename.basename state.path)
5047 with Not_found -> dc, [], 0, (0, 0.0)
5049 setconf defconf dc;
5050 setconf conf pc;
5051 state.bookmarks <- pb;
5052 state.x <- px;
5053 state.scrollw <- conf.scrollbw;
5054 if conf.jumpback
5055 then state.anchor <- pa;
5056 cbput state.hists.nav pa;
5058 load1 f
5061 let add_attrs bb always dc c =
5062 let ob s a b =
5063 if always || a != b
5064 then Printf.bprintf bb "\n %s='%b'" s a
5065 and oi s a b =
5066 if always || a != b
5067 then Printf.bprintf bb "\n %s='%d'" s a
5068 and oI s a b =
5069 if always || a != b
5070 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5071 and oz s a b =
5072 if always || a <> b
5073 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5074 and oF s a b =
5075 if always || a <> b
5076 then Printf.bprintf bb "\n %s='%f'" s a
5077 and oc s a b =
5078 if always || a <> b
5079 then
5080 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5081 and oC s a b =
5082 if always || a <> b
5083 then
5084 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5085 and oR s a b =
5086 if always || a <> b
5087 then
5088 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5089 and os s a b =
5090 if always || a <> b
5091 then
5092 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5093 and oW s a b =
5094 if always || a <> b
5095 then
5096 let v =
5097 match a with
5098 | None -> "false"
5099 | Some f ->
5100 if f = infinity
5101 then "true"
5102 else string_of_float f
5104 Printf.bprintf bb "\n %s='%s'" s v
5106 let w, h =
5107 if always
5108 then dc.winw, dc.winh
5109 else
5110 match state.fullscreen with
5111 | Some wh -> wh
5112 | None -> c.winw, c.winh
5114 let zoom, presentation, interpagespace, maxwait =
5115 if always
5116 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
5117 else
5118 match state.mode with
5119 | Birdseye (bc, _, _, _, _) ->
5120 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
5121 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
5123 oi "width" w dc.winw;
5124 oi "height" h dc.winh;
5125 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
5126 oi "scroll-handle-height" c.scrollh dc.scrollh;
5127 ob "case-insensitive-search" c.icase dc.icase;
5128 ob "preload" c.preload dc.preload;
5129 oi "page-bias" c.pagebias dc.pagebias;
5130 oi "scroll-step" c.scrollstep dc.scrollstep;
5131 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
5132 ob "max-height-fit" c.maxhfit dc.maxhfit;
5133 ob "crop-hack" c.crophack dc.crophack;
5134 oW "throttle" maxwait dc.maxwait;
5135 ob "highlight-links" c.hlinks dc.hlinks;
5136 ob "under-cursor-info" c.underinfo dc.underinfo;
5137 oi "vertical-margin" interpagespace dc.interpagespace;
5138 oz "zoom" zoom dc.zoom;
5139 ob "presentation" presentation dc.presentation;
5140 oi "rotation-angle" c.angle dc.angle;
5141 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
5142 ob "proportional-display" c.proportional dc.proportional;
5143 oI "pixmap-cache-size" c.memlimit dc.memlimit;
5144 oi "tex-count" c.texcount dc.texcount;
5145 oi "slice-height" c.sliceheight dc.sliceheight;
5146 oi "thumbnail-width" c.thumbw dc.thumbw;
5147 ob "persistent-location" c.jumpback dc.jumpback;
5148 oc "background-color" c.bgcolor dc.bgcolor;
5149 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
5150 oi "tile-width" c.tilew dc.tilew;
5151 oi "tile-height" c.tileh dc.tileh;
5152 oI "mupdf-memlimit" c.mumemlimit dc.mumemlimit;
5153 ob "checkers" c.checkers dc.checkers;
5154 oi "aalevel" c.aalevel dc.aalevel;
5155 ob "trim-margins" c.trimmargins dc.trimmargins;
5156 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
5157 os "uri-launcher" c.urilauncher dc.urilauncher;
5158 oC "color-space" c.colorspace dc.colorspace;
5159 ob "invert-colors" c.invert dc.invert;
5160 oF "brightness" c.colorscale dc.colorscale;
5161 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
5162 if always
5163 then ob "wmclass-hack" !wmclasshack false;
5166 let save () =
5167 let uifontsize = fstate.fontsize in
5168 let bb = Buffer.create 32768 in
5169 let f (h, dc) =
5170 let dc = if conf.bedefault then conf else dc in
5171 Buffer.add_string bb "<llppconfig>\n";
5173 if String.length !fontpath > 0
5174 then
5175 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
5176 uifontsize
5177 !fontpath
5178 else (
5179 if uifontsize <> 14
5180 then
5181 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
5184 Buffer.add_string bb "<defaults ";
5185 add_attrs bb true dc dc;
5186 Buffer.add_string bb "/>\n";
5188 let adddoc path pan anchor c bookmarks =
5189 if bookmarks == [] && c = dc && anchor = emptyanchor
5190 then ()
5191 else (
5192 Printf.bprintf bb "<doc path='%s'"
5193 (enent path 0 (String.length path));
5195 if anchor <> emptyanchor
5196 then (
5197 let n, y = anchor in
5198 Printf.bprintf bb " page='%d'" n;
5199 if y > 1e-6
5200 then
5201 Printf.bprintf bb " rely='%f'" y
5205 if pan != 0
5206 then Printf.bprintf bb " pan='%d'" pan;
5208 add_attrs bb false dc c;
5210 begin match bookmarks with
5211 | [] -> Buffer.add_string bb "/>\n"
5212 | _ ->
5213 Buffer.add_string bb ">\n<bookmarks>\n";
5214 List.iter (fun (title, _level, (page, rely)) ->
5215 Printf.bprintf bb
5216 "<item title='%s' page='%d'"
5217 (enent title 0 (String.length title))
5218 page
5220 if rely > 1e-6
5221 then
5222 Printf.bprintf bb " rely='%f'" rely
5224 Buffer.add_string bb "/>\n";
5225 ) bookmarks;
5226 Buffer.add_string bb "</bookmarks>\n</doc>\n";
5227 end;
5231 let pan =
5232 match state.mode with
5233 | Birdseye (_, pan, _, _, _) -> pan
5234 | _ -> state.x
5236 let basename = Filename.basename state.path in
5237 adddoc basename pan (getanchor ())
5238 { conf with
5239 autoscrollstep =
5240 match state.autoscroll with
5241 | Some step -> step
5242 | None -> conf.autoscrollstep }
5243 (if conf.savebmarks then state.bookmarks else []);
5245 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
5246 if basename <> path
5247 then adddoc path x y c bookmarks
5248 ) h;
5249 Buffer.add_string bb "</llppconfig>";
5251 load1 f;
5252 if Buffer.length bb > 0
5253 then
5255 let tmp = !confpath ^ ".tmp" in
5256 let oc = open_out_bin tmp in
5257 Buffer.output_buffer oc bb;
5258 close_out oc;
5259 Unix.rename tmp !confpath;
5260 with exn ->
5261 prerr_endline
5262 ("error while saving configuration: " ^ Printexc.to_string exn)
5264 end;;
5266 let () =
5267 Arg.parse
5268 (Arg.align
5269 [("-p", Arg.String (fun s -> state.password <- s) ,
5270 "<password> Set password");
5272 ("-f", Arg.String (fun s -> Config.fontpath := s),
5273 "<path> Set path to the user interface font");
5275 ("-c", Arg.String (fun s -> Config.confpath := s),
5276 "<path> Set path to the configuration file");
5278 ("-v", Arg.Unit (fun () ->
5279 Printf.printf
5280 "%s\nconfiguration path: %s\n"
5281 (version ())
5282 Config.defconfpath
5284 exit 0), " Print version and exit");
5287 (fun s -> state.path <- s)
5288 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
5290 if String.length state.path = 0
5291 then (prerr_endline "file name missing"; exit 1);
5293 Config.load ();
5295 let _ = Glut.init Sys.argv in
5296 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
5297 let () = Glut.initWindowSize conf.winw conf.winh in
5298 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
5300 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
5301 || Glut.extensionSupported "GL_EXT_texture_rectangle")
5302 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
5304 let csock, ssock =
5305 if not is_windows
5306 then
5307 Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0
5308 else
5309 let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, 1337) in
5310 let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5311 Unix.setsockopt sock Unix.SO_REUSEADDR true;
5312 Unix.bind sock addr;
5313 Unix.listen sock 1;
5314 let csock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5315 Unix.connect csock addr;
5316 let ssock, _ = Unix.accept sock in
5317 Unix.close sock;
5318 let opts sock =
5319 Unix.setsockopt sock Unix.TCP_NODELAY true;
5320 Unix.setsockopt_optint sock Unix.SO_LINGER None;
5322 opts ssock;
5323 opts csock;
5324 ssock, csock
5327 let () = Glut.displayFunc display in
5328 let () = Glut.reshapeFunc reshape in
5329 let () = Glut.keyboardFunc keyboard in
5330 let () = Glut.specialFunc special in
5331 let () = Glut.idleFunc (Some idle) in
5332 let () = Glut.mouseFunc mouse in
5333 let () = Glut.motionFunc motion in
5334 let () = Glut.passiveMotionFunc pmotion in
5336 setcheckers conf.checkers;
5337 init ssock (
5338 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5339 conf.texcount, conf.sliceheight, conf.mumemlimit, conf.colorspace,
5340 !Config.wmclasshack, !Config.fontpath
5342 state.csock <- csock;
5343 state.ssock <- ssock;
5344 state.text <- "Opening " ^ state.path;
5345 setaalevel conf.aalevel;
5346 writeopen state.path state.password;
5347 state.uioh <- uioh;
5348 setfontsize fstate.fontsize;
5350 redirectstderr ();
5352 while true do
5354 Glut.mainLoop ();
5355 with
5356 | Glut.BadEnum "key in special_of_int" ->
5357 showtext '!' " LablGlut bug: special key not recognized";
5359 | Quit ->
5360 wcmd "quit" [];
5361 Config.save ();
5362 exit 0
5364 | exn when conf.redirectstderr ->
5365 let s =
5366 Printf.sprintf "exception %s\n%s"
5367 (Printexc.to_string exn)
5368 (Printexc.get_backtrace ())
5370 ignore (try
5371 Unix.single_write state.stderr s 0 (String.length s);
5372 with _ -> 0);
5373 exit 1
5374 done;