Consistency
[llpp.git] / main.ml
blob2b1a602370274c5612f5b98e48a9187b4ed02c95
1 type under =
2 | Unone
3 | Ulinkuri of string
4 | Ulinkgoto of (int * int)
5 | Utext of facename
6 and facename = string;;
8 let dolog fmt = Printf.kprintf prerr_endline fmt;;
9 let now = Unix.gettimeofday;;
11 exception Quit;;
13 type params = (angle * proportional * trimparams
14 * texcount * sliceheight * memsize
15 * colorspace * wmclasshack * fontpath)
16 and pageno = int
17 and width = int
18 and height = int
19 and leftx = int
20 and opaque = string
21 and recttype = int
22 and pixmapsize = int
23 and angle = int
24 and proportional = bool
25 and trimmargins = bool
26 and interpagespace = int
27 and texcount = int
28 and sliceheight = int
29 and gen = int
30 and top = float
31 and fontpath = string
32 and memsize = int
33 and aalevel = int
34 and wmclasshack = bool
35 and irect = (int * int * int * int)
36 and trimparams = (trimmargins * irect)
37 and colorspace = | Rgb | Bgr | Gray
40 type platform = | Punknown | Plinux | Pwindows | Posx | Psun
41 | Pfreebsd | Pdragonflybsd | Popenbsd | Pnetbsd
42 | 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";;
57 external realloctexts : int -> bool = "ml_realloctexts";;
59 let platform_to_string = function
60 | Punknown -> "unknown"
61 | Plinux -> "Linux"
62 | Pwindows -> "Windows"
63 | Posx -> "OSX"
64 | Psun -> "Sun"
65 | Pfreebsd -> "FreeBSD"
66 | Pdragonflybsd -> "DragonflyBSD"
67 | Popenbsd -> "OpenBSD"
68 | Pnetbsd -> "NetBSD"
69 | Pcygwin -> "Cygwin"
70 | Pmingw -> "MingW"
73 let platform = platform ();;
75 let is_windows =
76 match platform with
77 | Pwindows | Pmingw -> true
78 | _ -> false
81 type x = int
82 and y = int
83 and tilex = int
84 and tiley = int
85 and tileparams = (x * y * width * height * tilex * tiley)
88 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
90 type mpos = int * int
91 and mstate =
92 | Msel of (mpos * mpos)
93 | Mpan of mpos
94 | Mscrolly | Mscrollx
95 | Mzoom of (int * int)
96 | Mzoomrect of (mpos * mpos)
97 | Mnone
100 type textentry = string * string * onhist option * onkey * ondone
101 and onkey = string -> int -> te
102 and ondone = string -> unit
103 and histcancel = unit -> unit
104 and onhist = ((histcmd -> string) * histcancel)
105 and histcmd = HCnext | HCprev | HCfirst | HClast
106 and te =
107 | TEstop
108 | TEdone of string
109 | TEcont of string
110 | TEswitch of textentry
113 type 'a circbuf =
114 { store : 'a array
115 ; mutable rc : int
116 ; mutable wc : int
117 ; mutable len : int
121 let bound v minv maxv =
122 max minv (min maxv v);
125 let cbnew n v =
126 { store = Array.create n v
127 ; rc = 0
128 ; wc = 0
129 ; len = 0
133 let drawstring size x y s =
134 Gl.enable `blend;
135 Gl.enable `texture_2d;
136 ignore (drawstr size x y s);
137 Gl.disable `blend;
138 Gl.disable `texture_2d;
141 let drawstring1 size x y s =
142 drawstr size x y s;
145 let drawstring2 size x y fmt =
146 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
149 let cbcap b = Array.length b.store;;
151 let cbput b v =
152 let cap = cbcap b in
153 b.store.(b.wc) <- v;
154 b.wc <- (b.wc + 1) mod cap;
155 b.rc <- b.wc;
156 b.len <- min (b.len + 1) cap;
159 let cbempty b = b.len = 0;;
161 let cbgetg b circular dir =
162 if cbempty b
163 then b.store.(0)
164 else
165 let rc = b.rc + dir in
166 let rc =
167 if circular
168 then (
169 if rc = -1
170 then b.len-1
171 else (
172 if rc = b.len
173 then 0
174 else rc
177 else max 0 (min rc (b.len-1))
179 b.rc <- rc;
180 b.store.(rc);
183 let cbget b = cbgetg b false;;
184 let cbgetc b = cbgetg b true;;
186 type page =
187 { pageno : int
188 ; pagedimno : int
189 ; pagew : int
190 ; pageh : int
191 ; pagex : int
192 ; pagey : int
193 ; pagevw : int
194 ; pagevh : int
195 ; pagedispx : int
196 ; pagedispy : int
200 let debugl l =
201 dolog "l %d dim=%d {" l.pageno l.pagedimno;
202 dolog " WxH %dx%d" l.pagew l.pageh;
203 dolog " vWxH %dx%d" l.pagevw l.pagevh;
204 dolog " pagex,y %d,%d" l.pagex l.pagey;
205 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
206 dolog "}";
209 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
210 dolog "rect {";
211 dolog " x0,y0=(% f, % f)" x0 y0;
212 dolog " x1,y1=(% f, % f)" x1 y1;
213 dolog " x2,y2=(% f, % f)" x2 y2;
214 dolog " x3,y3=(% f, % f)" x3 y3;
215 dolog "}";
218 type columns =
219 multicol * ((pdimno * x * y * (pageno * width * height * leftx)) array)
220 and multicol = columncount * covercount * covercount
221 and pdimno = int
222 and columncount = int
223 and covercount = int;;
225 type conf =
226 { mutable scrollbw : int
227 ; mutable scrollh : int
228 ; mutable icase : bool
229 ; mutable preload : bool
230 ; mutable pagebias : int
231 ; mutable verbose : bool
232 ; mutable debug : bool
233 ; mutable scrollstep : int
234 ; mutable maxhfit : bool
235 ; mutable crophack : bool
236 ; mutable autoscrollstep : int
237 ; mutable maxwait : float option
238 ; mutable hlinks : bool
239 ; mutable underinfo : bool
240 ; mutable interpagespace : interpagespace
241 ; mutable zoom : float
242 ; mutable presentation : bool
243 ; mutable angle : angle
244 ; mutable winw : int
245 ; mutable winh : int
246 ; mutable savebmarks : bool
247 ; mutable proportional : proportional
248 ; mutable trimmargins : trimmargins
249 ; mutable trimfuzz : irect
250 ; mutable memlimit : memsize
251 ; mutable texcount : texcount
252 ; mutable sliceheight : sliceheight
253 ; mutable thumbw : width
254 ; mutable jumpback : bool
255 ; mutable bgcolor : float * float * float
256 ; mutable bedefault : bool
257 ; mutable scrollbarinpm : bool
258 ; mutable tilew : int
259 ; mutable tileh : int
260 ; mutable mustoresize : memsize
261 ; mutable checkers : bool
262 ; mutable aalevel : int
263 ; mutable urilauncher : string
264 ; mutable colorspace : colorspace
265 ; mutable invert : bool
266 ; mutable colorscale : float
267 ; mutable redirectstderr : bool
268 ; mutable ghyllscroll : (int * int * int) option
269 ; mutable columns : columns option
270 ; mutable beyecolumns : columncount option
274 type anchor = pageno * top;;
276 type outline = string * int * anchor;;
278 type rect = float * float * float * float * float * float * float * float;;
280 type tile = opaque * pixmapsize * elapsed
281 and elapsed = float;;
282 type pagemapkey = pageno * gen;;
283 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
284 and row = int
285 and col = int;;
287 let emptyanchor = (0, 0.0);;
289 type infochange = | Memused | Docinfo | Pdim;;
291 class type uioh = object
292 method display : unit
293 method key : int -> uioh
294 method special : Glut.special_key_t -> uioh
295 method button :
296 Glut.button_t -> Glut.mouse_button_state_t -> int -> int -> uioh
297 method motion : int -> int -> uioh
298 method pmotion : int -> int -> uioh
299 method infochanged : infochange -> unit
300 method scrollpw : (int * float * float)
301 method scrollph : (int * float * float)
302 end;;
304 type mode =
305 | Birdseye of (conf * leftx * pageno * pageno * anchor)
306 | Textentry of (textentry * onleave)
307 | View
308 and onleave = leavetextentrystatus -> unit
309 and leavetextentrystatus = | Cancel | Confirm
310 and helpitem = string * int * action
311 and action =
312 | Noaction
313 | Action of (uioh -> uioh)
316 let isbirdseye = function Birdseye _ -> true | _ -> false;;
317 let istextentry = function Textentry _ -> true | _ -> false;;
319 type currently =
320 | Idle
321 | Loading of (page * gen)
322 | Tiling of (
323 page * opaque * colorspace * angle * gen * col * row * width * height
325 | Outlining of outline list
328 let nouioh : uioh = object (self)
329 method display = ()
330 method key _ = self
331 method special _ = self
332 method button _ _ _ _ = self
333 method motion _ _ = self
334 method pmotion _ _ = self
335 method infochanged _ = ()
336 method scrollpw = (0, nan, nan)
337 method scrollph = (0, nan, nan)
338 end;;
340 type state =
341 { mutable csock : Unix.file_descr
342 ; mutable ssock : Unix.file_descr
343 ; mutable errfd : Unix.file_descr option
344 ; mutable stderr : Unix.file_descr
345 ; mutable errmsgs : Buffer.t
346 ; mutable newerrmsgs : bool
347 ; mutable w : int
348 ; mutable x : int
349 ; mutable y : int
350 ; mutable scrollw : int
351 ; mutable hscrollh : int
352 ; mutable anchor : anchor
353 ; mutable maxy : int
354 ; mutable layout : page list
355 ; pagemap : (pagemapkey, opaque) Hashtbl.t
356 ; tilemap : (tilemapkey, tile) Hashtbl.t
357 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
358 ; mutable pdims : (pageno * width * height * leftx) list
359 ; mutable pagecount : int
360 ; mutable currently : currently
361 ; mutable mstate : mstate
362 ; mutable searchpattern : string
363 ; mutable rects : (pageno * recttype * rect) list
364 ; mutable rects1 : (pageno * recttype * rect) list
365 ; mutable text : string
366 ; mutable fullscreen : (width * height) option
367 ; mutable mode : mode
368 ; mutable uioh : uioh
369 ; mutable outlines : outline array
370 ; mutable bookmarks : outline list
371 ; mutable path : string
372 ; mutable password : string
373 ; mutable invalidated : int
374 ; mutable memused : memsize
375 ; mutable gen : gen
376 ; mutable throttle : (page list * int * float) option
377 ; mutable autoscroll : int option
378 ; mutable ghyll : int option -> unit
379 ; mutable help : helpitem array
380 ; mutable docinfo : (int * string) list
381 ; mutable deadline : float
382 ; mutable texid : GlTex.texture_id option
383 ; hists : hists
384 ; mutable prevzoom : float
385 ; mutable progress : float
387 and hists =
388 { pat : string circbuf
389 ; pag : string circbuf
390 ; nav : anchor circbuf
394 let defconf =
395 { scrollbw = 7
396 ; scrollh = 12
397 ; icase = true
398 ; preload = true
399 ; pagebias = 0
400 ; verbose = false
401 ; debug = false
402 ; scrollstep = 24
403 ; maxhfit = true
404 ; crophack = false
405 ; autoscrollstep = 2
406 ; maxwait = None
407 ; hlinks = false
408 ; underinfo = false
409 ; interpagespace = 2
410 ; zoom = 1.0
411 ; presentation = false
412 ; angle = 0
413 ; winw = 900
414 ; winh = 900
415 ; savebmarks = true
416 ; proportional = true
417 ; trimmargins = false
418 ; trimfuzz = (0,0,0,0)
419 ; memlimit = 32 lsl 20
420 ; texcount = 256
421 ; sliceheight = 24
422 ; thumbw = 76
423 ; jumpback = true
424 ; bgcolor = (0.5, 0.5, 0.5)
425 ; bedefault = false
426 ; scrollbarinpm = true
427 ; tilew = 2048
428 ; tileh = 2048
429 ; mustoresize = 128 lsl 20
430 ; checkers = true
431 ; aalevel = 8
432 ; urilauncher =
433 (match platform with
434 | Plinux | Pfreebsd | Pdragonflybsd | Popenbsd | Psun -> "xdg-open \"%s\""
435 | Posx -> "open \"%s\""
436 | Pwindows | Pcygwin | Pmingw -> "start %s"
437 | _ -> "")
438 ; colorspace = Rgb
439 ; invert = false
440 ; colorscale = 1.0
441 ; redirectstderr = false
442 ; ghyllscroll = None
443 ; columns = None
444 ; beyecolumns = None
448 let conf = { defconf with angle = defconf.angle };;
450 type fontstate =
451 { mutable fontsize : int
452 ; mutable wwidth : float
453 ; mutable maxrows : int
457 let fstate =
458 { fontsize = 14
459 ; wwidth = nan
460 ; maxrows = -1
464 let setfontsize n =
465 fstate.fontsize <- n;
466 fstate.wwidth <- measurestr fstate.fontsize "w";
467 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
470 let validuri s =
471 let colonpos = try String.index s ':' with Not_found -> -1 in
472 let len = String.length s in
473 if colonpos >= 0 && colonpos + 3 < len
474 then (
475 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
476 then
477 let scheme =
478 let schemestartpos =
479 try String.rindex_from s colonpos ' '
480 with Not_found -> -1
482 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
484 match scheme with
485 | "http" | "ftp" | "mailto" -> true
486 | _ -> false
487 else
488 false
490 else
491 false
494 let gotouri uri =
495 if String.length conf.urilauncher = 0
496 then print_endline uri
497 else (
498 if not (validuri uri)
499 then print_endline uri
500 else
501 let re = Str.regexp "%s" in
502 let command = Str.global_replace re uri conf.urilauncher in
503 let optic =
504 try Some (Unix.open_process_in command)
505 with exn ->
506 Printf.eprintf
507 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
508 flush stderr;
509 None
511 match optic with
512 | Some ic -> close_in ic
513 | None -> ()
517 let version () =
518 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
519 (platform_to_string platform) Sys.word_size Sys.ocaml_version
522 let makehelp () =
523 let strings = version () :: "" :: Help.keys in
524 Array.of_list (
525 List.map (fun s ->
526 if validuri s
527 then (s, 0, Action (fun u -> gotouri s; u))
528 else (s, 0, Noaction)
529 ) strings);
532 let noghyll _ = ();;
534 let state =
535 { csock = Unix.stdin
536 ; ssock = Unix.stdin
537 ; errfd = None
538 ; stderr = Unix.stderr
539 ; errmsgs = Buffer.create 0
540 ; newerrmsgs = false
541 ; x = 0
542 ; y = 0
543 ; w = 0
544 ; scrollw = 0
545 ; hscrollh = 0
546 ; anchor = emptyanchor
547 ; layout = []
548 ; maxy = max_int
549 ; tilelru = Queue.create ()
550 ; pagemap = Hashtbl.create 10
551 ; tilemap = Hashtbl.create 10
552 ; pdims = []
553 ; pagecount = 0
554 ; currently = Idle
555 ; mstate = Mnone
556 ; rects = []
557 ; rects1 = []
558 ; text = ""
559 ; mode = View
560 ; fullscreen = None
561 ; searchpattern = ""
562 ; outlines = [||]
563 ; bookmarks = []
564 ; path = ""
565 ; password = ""
566 ; invalidated = 0
567 ; hists =
568 { nav = cbnew 10 (0, 0.0)
569 ; pat = cbnew 1 ""
570 ; pag = cbnew 1 ""
572 ; memused = 0
573 ; gen = 0
574 ; throttle = None
575 ; autoscroll = None
576 ; ghyll = noghyll
577 ; help = makehelp ()
578 ; docinfo = []
579 ; deadline = nan
580 ; texid = None
581 ; prevzoom = 1.0
582 ; progress = -1.0
583 ; uioh = nouioh
587 let vlog fmt =
588 if conf.verbose
589 then
590 Printf.kprintf prerr_endline fmt
591 else
592 Printf.kprintf ignore fmt
595 let redirectstderr () =
596 if conf.redirectstderr
597 then
598 let rfd, wfd = Unix.pipe () in
599 state.stderr <- Unix.dup Unix.stderr;
600 state.errfd <- Some rfd;
601 Unix.dup2 wfd Unix.stderr;
602 else (
603 state.newerrmsgs <- false;
604 begin match state.errfd with
605 | Some fd ->
606 Unix.close fd;
607 Unix.dup2 state.stderr Unix.stderr;
608 state.errfd <- None;
609 | None -> ()
610 end;
611 prerr_string (Buffer.contents state.errmsgs);
612 flush stderr;
613 Buffer.clear state.errmsgs;
617 module G =
618 struct
619 let postRedisplay who =
620 if conf.verbose
621 then prerr_endline ("redisplay for " ^ who);
622 Glut.postRedisplay ();
624 end;;
626 let addchar s c =
627 let b = Buffer.create (String.length s + 1) in
628 Buffer.add_string b s;
629 Buffer.add_char b c;
630 Buffer.contents b;
633 let colorspace_of_string s =
634 match String.lowercase s with
635 | "rgb" -> Rgb
636 | "bgr" -> Bgr
637 | "gray" -> Gray
638 | _ -> failwith "invalid colorspace"
641 let int_of_colorspace = function
642 | Rgb -> 0
643 | Bgr -> 1
644 | Gray -> 2
647 let colorspace_of_int = function
648 | 0 -> Rgb
649 | 1 -> Bgr
650 | 2 -> Gray
651 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
654 let colorspace_to_string = function
655 | Rgb -> "rgb"
656 | Bgr -> "bgr"
657 | Gray -> "gray"
660 let intentry_with_suffix text key =
661 let c = Char.unsafe_chr key in
662 match Char.lowercase c with
663 | '0' .. '9' ->
664 let text = addchar text c in
665 TEcont text
667 | 'k' | 'm' | 'g' ->
668 let text = addchar text c in
669 TEcont text
671 | _ ->
672 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
673 TEcont text
676 let columns_to_string (n, a, b) =
677 if a = 0 && b = 0
678 then Printf.sprintf "%d" n
679 else Printf.sprintf "%d,%d,%d" n a b;
682 let columns_of_string s =
684 (int_of_string s, 0, 0)
685 with _ ->
686 Scanf.sscanf s "%u,%u,%u" (fun n a b -> (n, a, b));
689 let writecmd fd s =
690 let len = String.length s in
691 let n = 4 + len in
692 let b = Buffer.create n in
693 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
694 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
695 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
696 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
697 Buffer.add_string b s;
698 let s' = Buffer.contents b in
699 let n' = Unix.write fd s' 0 n in
700 if n' != n then failwith "write failed";
703 let readcmd fd =
704 let s = "xxxx" in
705 let n = Unix.read fd s 0 4 in
706 if n != 4 then failwith "incomplete read(len)";
707 let len = 0
708 lor (Char.code s.[0] lsl 24)
709 lor (Char.code s.[1] lsl 16)
710 lor (Char.code s.[2] lsl 8)
711 lor (Char.code s.[3] lsl 0)
713 let s = String.create len in
714 let n =
715 if is_windows
716 then
717 let rec loop n =
718 if n = 10
719 then failwith "EWOULDBLOCK encountered 10 times"
720 else
722 Unix.read fd s 0 len
723 with Unix.Unix_error (Unix.EWOULDBLOCK, _, _) ->
724 let _, _, _ = Unix.select [fd] [] [] 0.01 in
725 loop (n+1)
726 in loop 0
727 else
728 Unix.read fd s 0 len
730 if n != len then failwith "incomplete read(data)";
734 let makecmd s l =
735 let b = Buffer.create 10 in
736 Buffer.add_string b s;
737 let rec combine = function
738 | [] -> b
739 | x :: xs ->
740 Buffer.add_char b ' ';
741 let s =
742 match x with
743 | `b b -> if b then "1" else "0"
744 | `s s -> s
745 | `i i -> string_of_int i
746 | `f f -> string_of_float f
747 | `I f -> string_of_int (truncate f)
749 Buffer.add_string b s;
750 combine xs;
752 combine l;
755 let wcmd s l =
756 let cmd = Buffer.contents (makecmd s l) in
757 writecmd state.csock cmd;
760 let calcips h =
761 if conf.presentation
762 then
763 let d = conf.winh - h in
764 max 0 ((d + 1) / 2)
765 else
766 conf.interpagespace
769 let calcheight () =
770 let rec f pn ph pi fh l =
771 match l with
772 | (n, _, h, _) :: rest ->
773 let ips = calcips h in
774 let fh =
775 if conf.presentation
776 then fh+ips
777 else (
778 if isbirdseye state.mode && pn = 0
779 then fh + ips
780 else fh
783 let fh = fh + ((n - pn) * (ph + pi)) in
784 f n h ips fh rest;
786 | [] ->
787 let inc =
788 if conf.presentation || (isbirdseye state.mode && pn = 0)
789 then 0
790 else -pi
792 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
793 max 0 fh
795 let fh = f 0 0 0 0 state.pdims in
799 let calcheight () =
800 match conf.columns with
801 | None -> calcheight ()
802 | Some (_, b) ->
803 if Array.length b > 0
804 then
805 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
806 y + h
807 else 0
810 let getpageyh pageno =
811 let rec f pn ph pi y l =
812 match l with
813 | (n, _, h, _) :: rest ->
814 let ips = calcips h in
815 if n >= pageno
816 then
817 let h = if n = pageno then h else ph in
818 if conf.presentation && n = pageno
819 then
820 y + (pageno - pn) * (ph + pi) + pi, h
821 else
822 y + (pageno - pn) * (ph + pi), h
823 else
824 let y = y + (if conf.presentation then pi else 0) in
825 let y = y + (n - pn) * (ph + pi) in
826 f n h ips y rest
828 | [] ->
829 y + (pageno - pn) * (ph + pi), ph
831 f 0 0 0 0 state.pdims
834 let getpageyh pageno =
835 match conf.columns with
836 | None -> getpageyh pageno
837 | Some (_, b) ->
838 let (_, _, y, (_, _, h, _)) = b.(pageno) in
839 y, h
842 let getpagedim pageno =
843 let rec f ppdim l =
844 match l with
845 | (n, _, _, _) as pdim :: rest ->
846 if n >= pageno
847 then (if n = pageno then pdim else ppdim)
848 else f pdim rest
850 | [] -> ppdim
852 f (-1, -1, -1, -1) state.pdims
855 let getpagey pageno = fst (getpageyh pageno);;
857 let layout1 y sh =
858 let sh = sh - state.hscrollh in
859 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
860 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
861 match pdims with
862 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
863 let ips = calcips h in
864 let yinc =
865 if conf.presentation || (isbirdseye state.mode && pageno = 0)
866 then ips
867 else 0
869 (w, h, ips, xoff), rest, pdimno + 1, yinc
870 | _ ->
871 prev, pdims, pdimno, 0
873 let dy = dy + yinc in
874 let py = py + yinc in
875 if pageno = state.pagecount || dy >= sh
876 then
877 accu
878 else
879 let vy = y + dy in
880 if py + h <= vy - yinc
881 then
882 let py = py + h + ips in
883 let dy = max 0 (py - y) in
884 f ~pageno:(pageno+1)
885 ~pdimno
886 ~prev:curr
889 ~pdims:rest
890 ~accu
891 else
892 let pagey = vy - py in
893 let pagevh = h - pagey in
894 let pagevh = min (sh - dy) pagevh in
895 let off = if yinc > 0 then py - vy else 0 in
896 let py = py + h + ips in
897 let pagex, dx =
898 let xoff = xoff +
899 if state.w < conf.winw - state.scrollw
900 then (conf.winw - state.scrollw - state.w) / 2
901 else 0
903 let dispx = xoff + state.x in
904 if dispx < 0
905 then (-dispx, 0)
906 else (0, dispx)
908 let pagevw =
909 let lw = w - pagex in
910 min lw (conf.winw - state.scrollw)
912 let e =
913 { pageno = pageno
914 ; pagedimno = pdimno
915 ; pagew = w
916 ; pageh = h
917 ; pagex = pagex
918 ; pagey = pagey + off
919 ; pagevw = pagevw
920 ; pagevh = pagevh - off
921 ; pagedispx = dx
922 ; pagedispy = dy + off
925 let accu = e :: accu in
926 f ~pageno:(pageno+1)
927 ~pdimno
928 ~prev:curr
930 ~dy:(dy+pagevh+ips)
931 ~pdims:rest
932 ~accu
934 if state.invalidated = 0
935 then (
936 let accu =
938 ~pageno:0
939 ~pdimno:~-1
940 ~prev:(0,0,0,0)
941 ~py:0
942 ~dy:0
943 ~pdims:state.pdims
944 ~accu:[]
946 List.rev accu
948 else
952 let layoutN ((columns, coverA, coverB), b) y sh =
953 let sh = sh - state.hscrollh in
954 let rec fold accu n =
955 if n = Array.length b
956 then accu
957 else
958 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
959 if (vy - y) > sh &&
960 (n = coverA - 1
961 || n = state.pagecount - coverB
962 || (n - coverA) mod columns = columns - 1)
963 then accu
964 else
965 let accu =
966 if vy + h > y
967 then
968 let pagey = max 0 (y - vy) in
969 let pagedispy = if pagey > 0 then 0 else vy - y in
970 let pagedispx, pagex, pagevw =
971 let pdx =
972 if n = coverA - 1 || n = state.pagecount - coverB
973 then state.x + (conf.winw - state.scrollw - w) / 2
974 else dx + xoff + state.x
976 if pdx < 0
977 then 0, -pdx, w + pdx
978 else pdx, 0, min (conf.winw - state.scrollw) w
980 let pagevh = min (h - pagey) (sh - pagedispy) in
981 if pagedispx < conf.winw - state.scrollw && pagevw > 0 && pagevh > 0
982 then
983 let e =
984 { pageno = n
985 ; pagedimno = pdimno
986 ; pagew = w
987 ; pageh = h
988 ; pagex = pagex
989 ; pagey = pagey
990 ; pagevw = pagevw
991 ; pagevh = pagevh
992 ; pagedispx = pagedispx
993 ; pagedispy = pagedispy
996 e :: accu
997 else
998 accu
999 else
1000 accu
1002 fold accu (n+1)
1004 if state.invalidated = 0
1005 then List.rev (fold [] 0)
1006 else []
1009 let layout y sh =
1010 match conf.columns with
1011 | None -> layout1 y sh
1012 | Some c -> layoutN c y sh
1015 let clamp incr =
1016 let y = state.y + incr in
1017 let y = max 0 y in
1018 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1022 let getopaque pageno =
1023 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
1024 with Not_found -> None
1027 let putopaque pageno opaque =
1028 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
1031 let itertiles l f =
1032 let tilex = l.pagex mod conf.tilew in
1033 let tiley = l.pagey mod conf.tileh in
1035 let col = l.pagex / conf.tilew in
1036 let row = l.pagey / conf.tileh in
1038 let vw =
1039 let a = l.pagew - l.pagex in
1040 let b = conf.winw - state.scrollw in
1041 min a b
1042 and vh = l.pagevh in
1044 let rec rowloop row y0 dispy h =
1045 if h = 0
1046 then ()
1047 else (
1048 let dh = conf.tileh - y0 in
1049 let dh = min h dh in
1050 let rec colloop col x0 dispx w =
1051 if w = 0
1052 then ()
1053 else (
1054 let dw = conf.tilew - x0 in
1055 let dw = min w dw in
1057 f col row dispx dispy x0 y0 dw dh;
1058 colloop (col+1) 0 (dispx+dw) (w-dw)
1061 colloop col tilex l.pagedispx vw;
1062 rowloop (row+1) 0 (dispy+dh) (h-dh)
1065 if vw > 0 && vh > 0
1066 then rowloop row tiley l.pagedispy vh;
1069 let gettileopaque l col row =
1070 let key =
1071 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1073 try Some (Hashtbl.find state.tilemap key)
1074 with Not_found -> None
1077 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1078 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1079 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1082 let drawtiles l color =
1083 GlDraw.color color;
1084 let f col row x y tilex tiley w h =
1085 match gettileopaque l col row with
1086 | Some (opaque, _, t) ->
1087 let params = x, y, w, h, tilex, tiley in
1088 if conf.invert
1089 then (
1090 Gl.enable `blend;
1091 GlFunc.blend_func `zero `one_minus_src_color;
1093 drawtile params opaque;
1094 if conf.invert
1095 then Gl.disable `blend;
1096 if conf.debug
1097 then (
1098 let s = Printf.sprintf
1099 "%d[%d,%d] %f sec"
1100 l.pageno col row t
1102 let w = measurestr fstate.fontsize s in
1103 GlMisc.push_attrib [`current];
1104 GlDraw.color (0.0, 0.0, 0.0);
1105 GlDraw.rect
1106 (float (x-2), float (y-2))
1107 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1108 GlDraw.color (1.0, 1.0, 1.0);
1109 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1110 GlMisc.pop_attrib ();
1113 | _ ->
1114 let w =
1115 let lw = conf.winw - state.scrollw - x in
1116 min lw w
1117 and h =
1118 let lh = conf.winh - y in
1119 min lh h
1121 Gl.enable `texture_2d;
1122 begin match state.texid with
1123 | Some id ->
1124 GlTex.bind_texture `texture_2d id;
1125 let x0 = float x
1126 and y0 = float y
1127 and x1 = float (x+w)
1128 and y1 = float (y+h) in
1130 let tw = float w /. 64.0
1131 and th = float h /. 64.0 in
1132 let tx0 = float tilex /. 64.0
1133 and ty0 = float tiley /. 64.0 in
1134 let tx1 = tx0 +. tw
1135 and ty1 = ty0 +. th in
1136 GlDraw.begins `quads;
1137 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1138 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1139 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1140 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1141 GlDraw.ends ();
1143 Gl.disable `texture_2d;
1144 | None ->
1145 GlDraw.color (1.0, 1.0, 1.0);
1146 GlDraw.rect
1147 (float x, float y)
1148 (float (x+w), float (y+h));
1149 end;
1150 if w > 128 && h > fstate.fontsize + 10
1151 then (
1152 GlDraw.color (0.0, 0.0, 0.0);
1153 let c, r =
1154 if conf.verbose
1155 then (col*conf.tilew, row*conf.tileh)
1156 else col, row
1158 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1160 GlDraw.color color;
1162 itertiles l f
1165 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1167 let tilevisible1 l x y =
1168 let ax0 = l.pagex
1169 and ax1 = l.pagex + l.pagevw
1170 and ay0 = l.pagey
1171 and ay1 = l.pagey + l.pagevh in
1173 let bx0 = x
1174 and by0 = y in
1175 let bx1 = min (bx0 + conf.tilew) l.pagew
1176 and by1 = min (by0 + conf.tileh) l.pageh in
1178 let rx0 = max ax0 bx0
1179 and ry0 = max ay0 by0
1180 and rx1 = min ax1 bx1
1181 and ry1 = min ay1 by1 in
1183 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1184 nonemptyintersection
1187 let tilevisible layout n x y =
1188 let rec findpageinlayout = function
1189 | l :: _ when l.pageno = n -> tilevisible1 l x y
1190 | _ :: rest -> findpageinlayout rest
1191 | [] -> false
1193 findpageinlayout layout
1196 let tileready l x y =
1197 tilevisible1 l x y &&
1198 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1201 let tilepage n p layout =
1202 let rec loop = function
1203 | l :: rest ->
1204 if l.pageno = n
1205 then
1206 let f col row _ _ _ _ _ _ =
1207 if state.currently = Idle
1208 then
1209 match gettileopaque l col row with
1210 | Some _ -> ()
1211 | None ->
1212 let x = col*conf.tilew
1213 and y = row*conf.tileh in
1214 let w =
1215 let w = l.pagew - x in
1216 min w conf.tilew
1218 let h =
1219 let h = l.pageh - y in
1220 min h conf.tileh
1222 wcmd "tile"
1223 [`s p
1224 ;`i x
1225 ;`i y
1226 ;`i w
1227 ;`i h
1229 state.currently <-
1230 Tiling (
1231 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1232 conf.tilew, conf.tileh
1235 itertiles l f;
1236 else
1237 loop rest
1239 | [] -> ()
1241 if state.invalidated = 0 then loop layout;
1244 let preloadlayout visiblepages =
1245 let presentation = conf.presentation in
1246 let interpagespace = conf.interpagespace in
1247 let maxy = state.maxy in
1248 conf.presentation <- false;
1249 conf.interpagespace <- 0;
1250 state.maxy <- calcheight ();
1251 let y =
1252 match visiblepages with
1253 | [] -> 0
1254 | l :: _ -> getpagey l.pageno + l.pagey
1256 let y = if y < conf.winh then 0 else y - conf.winh in
1257 let h = state.y - y + conf.winh*3 in
1258 let pages = layout y h in
1259 conf.presentation <- presentation;
1260 conf.interpagespace <- interpagespace;
1261 state.maxy <- maxy;
1262 pages;
1265 let load pages =
1266 let rec loop pages =
1267 if state.currently != Idle
1268 then ()
1269 else
1270 match pages with
1271 | l :: rest ->
1272 begin match getopaque l.pageno with
1273 | None ->
1274 wcmd "page" [`i l.pageno; `i l.pagedimno];
1275 state.currently <- Loading (l, state.gen);
1276 | Some opaque ->
1277 tilepage l.pageno opaque pages;
1278 loop rest
1279 end;
1280 | _ -> ()
1282 if state.invalidated = 0 then loop pages
1285 let preload pages =
1286 load pages;
1287 if conf.preload && state.currently = Idle
1288 then load (preloadlayout pages);
1291 let layoutready layout =
1292 let rec fold all ls =
1293 all && match ls with
1294 | l :: rest ->
1295 let seen = ref false in
1296 let allvisible = ref true in
1297 let foo col row _ _ _ _ _ _ =
1298 seen := true;
1299 allvisible := !allvisible &&
1300 begin match gettileopaque l col row with
1301 | Some _ -> true
1302 | None -> false
1305 itertiles l foo;
1306 fold (!seen && !allvisible) rest
1307 | [] -> true
1309 let alltilesvisible = fold true layout in
1310 alltilesvisible;
1313 let gotoy y =
1314 let y = bound y 0 state.maxy in
1315 let y, layout, proceed =
1316 match conf.maxwait with
1317 | Some time when state.ghyll == noghyll ->
1318 begin match state.throttle with
1319 | None ->
1320 let layout = layout y conf.winh in
1321 let ready = layoutready layout in
1322 if not ready
1323 then (
1324 load layout;
1325 state.throttle <- Some (layout, y, now ());
1327 else G.postRedisplay "gotoy showall (None)";
1328 y, layout, ready
1329 | Some (_, _, started) ->
1330 let dt = now () -. started in
1331 if dt > time
1332 then (
1333 state.throttle <- None;
1334 let layout = layout y conf.winh in
1335 load layout;
1336 G.postRedisplay "maxwait";
1337 y, layout, true
1339 else -1, [], false
1342 | _ ->
1343 let layout = layout y conf.winh in
1344 if true || layoutready layout
1345 then G.postRedisplay "gotoy ready";
1346 y, layout, true
1348 if proceed
1349 then (
1350 state.y <- y;
1351 state.layout <- layout;
1352 begin match state.mode with
1353 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1354 if not (pagevisible layout pageno)
1355 then (
1356 match state.layout with
1357 | [] -> ()
1358 | l :: _ ->
1359 state.mode <- Birdseye (
1360 conf, leftx, l.pageno, hooverpageno, anchor
1363 | _ -> ()
1364 end;
1365 preload layout;
1367 state.ghyll <- noghyll;
1370 let conttiling pageno opaque =
1371 tilepage pageno opaque
1372 (if conf.preload then preloadlayout state.layout else state.layout)
1375 let gotoy_and_clear_text y =
1376 gotoy y;
1377 if not conf.verbose then state.text <- "";
1380 let getanchor () =
1381 match state.layout with
1382 | [] -> emptyanchor
1383 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1386 let getanchory (n, top) =
1387 let y, h = getpageyh n in
1388 y + (truncate (top *. float h));
1391 let gotoanchor anchor =
1392 gotoy (getanchory anchor);
1395 let addnav () =
1396 cbput state.hists.nav (getanchor ());
1399 let getnav dir =
1400 let anchor = cbgetc state.hists.nav dir in
1401 getanchory anchor;
1404 let gotoghyll y =
1405 let rec scroll f n a b =
1406 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1407 let snake f a b =
1408 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1409 if f < a
1410 then s (float f /. float a)
1411 else (
1412 if f > b
1413 then 1.0 -. s ((float (f-b) /. float (n-b)))
1414 else 1.0
1417 snake f a b
1418 and summa f n a b =
1419 (* courtesy:
1420 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1421 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1422 let iv1 = iv f in
1423 let ins = float a *. iv1
1424 and outs = float (n-b) *. iv1 in
1425 let ones = b - a in
1426 ins +. outs +. float ones
1428 let rec set (_N, _A, _B) y sy =
1429 let sum = summa 1.0 _N _A _B in
1430 let dy = float (y - sy) in
1431 state.ghyll <- (
1432 let rec gf n y1 o =
1433 if n >= _N
1434 then state.ghyll <- noghyll
1435 else
1436 let go n =
1437 let s = scroll n _N _A _B in
1438 let y1 = y1 +. ((s *. dy) /. sum) in
1439 gotoy_and_clear_text (truncate y1);
1440 state.ghyll <- gf (n+1) y1;
1442 match o with
1443 | None -> go n
1444 | Some y' -> set (_N/2, 0, 0) y' state.y
1446 gf 0 (float state.y)
1449 match conf.ghyllscroll with
1450 | None ->
1451 gotoy_and_clear_text y
1452 | Some nab ->
1453 if state.ghyll == noghyll
1454 then set nab y state.y
1455 else state.ghyll (Some y)
1458 let gotopage n top =
1459 let y, h = getpageyh n in
1460 let y = y + (truncate (top *. float h)) in
1461 gotoghyll y
1464 let gotopage1 n top =
1465 let y = getpagey n in
1466 let y = y + top in
1467 gotoghyll y
1470 let invalidate () =
1471 state.layout <- [];
1472 state.pdims <- [];
1473 state.rects <- [];
1474 state.rects1 <- [];
1475 state.invalidated <- state.invalidated + 1;
1478 let writeopen path password =
1479 writecmd state.csock ("open " ^ path ^ "\000" ^ password ^ "\000");
1482 let opendoc path password =
1483 invalidate ();
1484 state.path <- path;
1485 state.password <- password;
1486 state.gen <- state.gen + 1;
1487 state.docinfo <- [];
1489 setaalevel conf.aalevel;
1490 writeopen path password;
1491 Glut.setWindowTitle ("llpp " ^ Filename.basename path);
1492 wcmd "geometry" [`i state.w; `i conf.winh];
1495 let scalecolor c =
1496 let c = c *. conf.colorscale in
1497 (c, c, c);
1500 let scalecolor2 (r, g, b) =
1501 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1504 let represent () =
1505 let docolumns = function
1506 | None -> ()
1507 | Some ((columns, coverA, coverB), _) ->
1508 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1509 let rec loop pageno pdimno pdim x y rowh pdims =
1510 if pageno = state.pagecount
1511 then ()
1512 else
1513 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1514 match pdims with
1515 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1516 pdimno+1, pdim, rest
1517 | _ ->
1518 pdimno, pdim, pdims
1520 let x, y, rowh' =
1521 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1522 then (
1523 (conf.winw - state.scrollw - w) / 2,
1524 y + rowh + conf.interpagespace, h
1526 else (
1527 if (pageno - coverA) mod columns = 0
1528 then 0, y + rowh + conf.interpagespace, h
1529 else x, y, max rowh h
1532 let rec fixrow m = if m = pageno then () else
1533 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1534 if h < rowh
1535 then (
1536 let y = y + (rowh - h) / 2 in
1537 a.(m) <- (pdimno, x, y, pdim);
1539 fixrow (m+1)
1541 if pageno > 1 && (pageno - coverA) mod columns = 0
1542 then fixrow (pageno - columns);
1543 a.(pageno) <- (pdimno, x, y, pdim);
1544 let x = x + w + xoff*2 + conf.interpagespace in
1545 loop (pageno+1) pdimno pdim x y rowh' pdims
1547 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1548 conf.columns <- Some ((columns, coverA, coverB), a);
1550 docolumns conf.columns;
1551 state.maxy <- calcheight ();
1552 state.hscrollh <-
1553 if state.w <= conf.winw - state.scrollw
1554 then 0
1555 else state.scrollw
1557 match state.mode with
1558 | Birdseye (_, _, pageno, _, _) ->
1559 let y, h = getpageyh pageno in
1560 let top = (conf.winh - h) / 2 in
1561 gotoy (max 0 (y - top))
1562 | _ -> gotoanchor state.anchor
1565 let reshape =
1566 let firsttime = ref true in
1567 fun ~w ~h ->
1568 GlDraw.viewport 0 0 w h;
1569 if state.invalidated = 0 && not !firsttime
1570 then state.anchor <- getanchor ();
1572 firsttime := false;
1573 conf.winw <- w;
1574 let w = truncate (float w *. conf.zoom) - state.scrollw in
1575 let w = max w 2 in
1576 state.w <- w;
1577 conf.winh <- h;
1578 setfontsize fstate.fontsize;
1579 GlMat.mode `modelview;
1580 GlMat.load_identity ();
1582 GlMat.mode `projection;
1583 GlMat.load_identity ();
1584 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1585 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1586 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1588 let w =
1589 match conf.columns with
1590 | None -> w
1591 | Some ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1593 invalidate ();
1594 wcmd "geometry" [`i w; `i h];
1597 let enttext () =
1598 let len = String.length state.text in
1599 let drawstring s =
1600 let hscrollh =
1601 match state.mode with
1602 | View -> state.hscrollh
1603 | _ -> 0
1605 let rect x w =
1606 GlDraw.rect
1607 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1608 (x+.w, float (conf.winh - hscrollh))
1611 let w = float (conf.winw - state.scrollw - 1) in
1612 if state.progress >= 0.0 && state.progress < 1.0
1613 then (
1614 GlDraw.color (0.3, 0.3, 0.3);
1615 let w1 = w *. state.progress in
1616 rect 0.0 w1;
1617 GlDraw.color (0.0, 0.0, 0.0);
1618 rect w1 (w-.w1)
1620 else (
1621 GlDraw.color (0.0, 0.0, 0.0);
1622 rect 0.0 w;
1625 GlDraw.color (1.0, 1.0, 1.0);
1626 drawstring fstate.fontsize
1627 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1629 let s =
1630 match state.mode with
1631 | Textentry ((prefix, text, _, _, _), _) ->
1632 let s =
1633 if len > 0
1634 then
1635 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1636 else
1637 Printf.sprintf "%s%s_" prefix text
1641 | _ -> state.text
1643 let s =
1644 if state.newerrmsgs
1645 then (
1646 if not (istextentry state.mode)
1647 then
1648 let s1 = "(press 'e' to review error messasges)" in
1649 if String.length s > 0 then s ^ " " ^ s1 else s1
1650 else s
1652 else s
1654 if String.length s > 0
1655 then drawstring s
1658 let showtext c s =
1659 state.text <- Printf.sprintf "%c%s" c s;
1660 G.postRedisplay "showtext";
1663 let gctiles () =
1664 let len = Queue.length state.tilelru in
1665 let rec loop qpos =
1666 if state.memused <= conf.memlimit
1667 then ()
1668 else (
1669 if qpos < len
1670 then
1671 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1672 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1673 let (_, pw, ph, _) = getpagedim n in
1675 gen = state.gen
1676 && colorspace = conf.colorspace
1677 && angle = conf.angle
1678 && pagew = pw
1679 && pageh = ph
1680 && (
1681 let layout =
1682 match state.throttle with
1683 | None ->
1684 if conf.preload
1685 then preloadlayout state.layout
1686 else state.layout
1687 | Some (layout, _, _) ->
1688 layout
1690 let x = col*conf.tilew
1691 and y = row*conf.tileh in
1692 tilevisible layout n x y
1694 then Queue.push lruitem state.tilelru
1695 else (
1696 wcmd "freetile" [`s p];
1697 state.memused <- state.memused - s;
1698 state.uioh#infochanged Memused;
1699 Hashtbl.remove state.tilemap k;
1701 loop (qpos+1)
1704 loop 0
1707 let flushtiles () =
1708 Queue.iter (fun (k, p, s) ->
1709 wcmd "freetile" [`s p];
1710 state.memused <- state.memused - s;
1711 state.uioh#infochanged Memused;
1712 Hashtbl.remove state.tilemap k;
1713 ) state.tilelru;
1714 Queue.clear state.tilelru;
1715 load state.layout;
1718 let logcurrently = function
1719 | Idle -> dolog "Idle"
1720 | Loading (l, gen) ->
1721 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1722 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1723 dolog
1724 "Tiling %d[%d,%d] page=%s cs=%s angle"
1725 l.pageno col row pageopaque
1726 (colorspace_to_string colorspace)
1728 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1729 angle gen conf.angle state.gen
1730 tilew tileh
1731 conf.tilew conf.tileh
1733 | Outlining _ ->
1734 dolog "outlining"
1737 let act cmds =
1738 (* dolog "%S" cmds; *)
1739 let op, args =
1740 let spacepos =
1741 try String.index cmds ' '
1742 with Not_found -> -1
1744 if spacepos = -1
1745 then cmds, ""
1746 else
1747 let l = String.length cmds in
1748 let op = String.sub cmds 0 spacepos in
1749 op, begin
1750 if l - spacepos < 2 then ""
1751 else String.sub cmds (spacepos+1) (l-spacepos-1)
1754 match op with
1755 | "clear" ->
1756 state.uioh#infochanged Pdim;
1757 state.pdims <- [];
1759 | "clearrects" ->
1760 state.rects <- state.rects1;
1761 G.postRedisplay "clearrects";
1763 | "continue" ->
1764 let n =
1765 try Scanf.sscanf args "%u" (fun n -> n)
1766 with exn ->
1767 dolog "error processing 'continue' %S: %s"
1768 cmds (Printexc.to_string exn);
1769 exit 1;
1771 state.pagecount <- n;
1772 state.invalidated <- state.invalidated - 1;
1773 begin match state.currently with
1774 | Outlining l ->
1775 state.currently <- Idle;
1776 state.outlines <- Array.of_list (List.rev l)
1777 | _ -> ()
1778 end;
1779 if state.invalidated = 0
1780 then represent ();
1781 if conf.maxwait = None
1782 then G.postRedisplay "continue";
1784 | "title" ->
1785 Glut.setWindowTitle args
1787 | "msg" ->
1788 showtext ' ' args
1790 | "vmsg" ->
1791 if conf.verbose
1792 then showtext ' ' args
1794 | "progress" ->
1795 let progress, text =
1797 Scanf.sscanf args "%f %n"
1798 (fun f pos ->
1799 f, String.sub args pos (String.length args - pos))
1800 with exn ->
1801 dolog "error processing 'progress' %S: %s"
1802 cmds (Printexc.to_string exn);
1803 exit 1;
1805 state.text <- text;
1806 state.progress <- progress;
1807 G.postRedisplay "progress"
1809 | "firstmatch" ->
1810 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1812 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1813 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1814 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1815 with exn ->
1816 dolog "error processing 'firstmatch' %S: %s"
1817 cmds (Printexc.to_string exn);
1818 exit 1;
1820 let y = (getpagey pageno) + truncate y0 in
1821 addnav ();
1822 gotoy y;
1823 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1825 | "match" ->
1826 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1828 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1829 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1830 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1831 with exn ->
1832 dolog "error processing 'match' %S: %s"
1833 cmds (Printexc.to_string exn);
1834 exit 1;
1836 state.rects1 <-
1837 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1839 | "page" ->
1840 let pageopaque, t =
1842 Scanf.sscanf args "%s %f" (fun p t -> p, t)
1843 with exn ->
1844 dolog "error processing 'page' %S: %s"
1845 cmds (Printexc.to_string exn);
1846 exit 1;
1848 begin match state.currently with
1849 | Loading (l, gen) ->
1850 vlog "page %d took %f sec" l.pageno t;
1851 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1852 begin match state.throttle with
1853 | None ->
1854 let preloadedpages =
1855 if conf.preload
1856 then preloadlayout state.layout
1857 else state.layout
1859 let evict () =
1860 let module IntSet =
1861 Set.Make (struct type t = int let compare = (-) end) in
1862 let set =
1863 List.fold_left (fun s l -> IntSet.add l.pageno s)
1864 IntSet.empty preloadedpages
1866 let evictedpages =
1867 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1868 if not (IntSet.mem pageno set)
1869 then (
1870 wcmd "freepage" [`s opaque];
1871 key :: accu
1873 else accu
1874 ) state.pagemap []
1876 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1878 evict ();
1879 state.currently <- Idle;
1880 if gen = state.gen
1881 then (
1882 tilepage l.pageno pageopaque state.layout;
1883 load state.layout;
1884 load preloadedpages;
1885 if pagevisible state.layout l.pageno
1886 && layoutready state.layout
1887 then G.postRedisplay "page";
1890 | Some (layout, _, _) ->
1891 state.currently <- Idle;
1892 tilepage l.pageno pageopaque layout;
1893 load state.layout
1894 end;
1896 | _ ->
1897 dolog "Inconsistent loading state";
1898 logcurrently state.currently;
1899 raise Quit;
1902 | "tile" ->
1903 let (x, y, opaque, size, t) =
1905 Scanf.sscanf args "%u %u %s %u %f"
1906 (fun x y p size t -> (x, y, p, size, t))
1907 with exn ->
1908 dolog "error processing 'tile' %S: %s"
1909 cmds (Printexc.to_string exn);
1910 exit 1;
1912 begin match state.currently with
1913 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1914 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1916 if tilew != conf.tilew || tileh != conf.tileh
1917 then (
1918 wcmd "freetile" [`s opaque];
1919 state.currently <- Idle;
1920 load state.layout;
1922 else (
1923 puttileopaque l col row gen cs angle opaque size t;
1924 state.memused <- state.memused + size;
1925 state.uioh#infochanged Memused;
1926 gctiles ();
1927 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1928 opaque, size) state.tilelru;
1930 let layout =
1931 match state.throttle with
1932 | None -> state.layout
1933 | Some (layout, _, _) -> layout
1936 state.currently <- Idle;
1937 if gen = state.gen
1938 && conf.colorspace = cs
1939 && conf.angle = angle
1940 && tilevisible layout l.pageno x y
1941 then conttiling l.pageno pageopaque;
1943 begin match state.throttle with
1944 | None ->
1945 preload state.layout;
1946 if gen = state.gen
1947 && conf.colorspace = cs
1948 && conf.angle = angle
1949 && tilevisible state.layout l.pageno x y
1950 then G.postRedisplay "tile nothrottle";
1952 | Some (layout, y, _) ->
1953 let ready = layoutready layout in
1954 if ready
1955 then (
1956 state.y <- y;
1957 state.layout <- layout;
1958 state.throttle <- None;
1959 G.postRedisplay "throttle";
1961 else load layout;
1962 end;
1965 | _ ->
1966 dolog "Inconsistent tiling state";
1967 logcurrently state.currently;
1968 raise Quit;
1971 | "pdim" ->
1972 let pdim =
1974 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1975 with exn ->
1976 dolog "error processing 'pdim' %S: %s"
1977 cmds (Printexc.to_string exn);
1978 exit 1;
1980 state.uioh#infochanged Pdim;
1981 state.pdims <- pdim :: state.pdims
1983 | "o" ->
1984 let (l, n, t, h, pos) =
1986 Scanf.sscanf args "%u %u %d %u %n"
1987 (fun l n t h pos -> l, n, t, h, pos)
1988 with exn ->
1989 dolog "error processing 'o' %S: %s"
1990 cmds (Printexc.to_string exn);
1991 exit 1;
1993 let s = String.sub args pos (String.length args - pos) in
1994 let outline = (s, l, (n, float t /. float h)) in
1995 begin match state.currently with
1996 | Outlining outlines ->
1997 state.currently <- Outlining (outline :: outlines)
1998 | Idle ->
1999 state.currently <- Outlining [outline]
2000 | currently ->
2001 dolog "invalid outlining state";
2002 logcurrently currently
2005 | "info" ->
2006 state.docinfo <- (1, args) :: state.docinfo
2008 | "infoend" ->
2009 state.uioh#infochanged Docinfo;
2010 state.docinfo <- List.rev state.docinfo
2012 | _ ->
2013 dolog "unknown cmd `%S'" cmds
2016 let idle () =
2017 if state.deadline == nan then state.deadline <- now ();
2018 let r =
2019 match state.errfd with
2020 | None -> [state.csock]
2021 | Some fd -> [state.csock; fd]
2023 let rec loop delay =
2024 let deadline =
2025 if state.ghyll == noghyll
2026 then state.deadline
2027 else now () +. 0.02
2029 let timeout =
2030 if delay > 0.0
2031 then max 0.0 (deadline -. now ())
2032 else 0.0
2034 let r, _, _ = Unix.select r [] [] timeout in
2035 begin match r with
2036 | [] ->
2037 state.ghyll None;
2038 begin match state.autoscroll with
2039 | Some step when step != 0 ->
2040 let y = state.y + step in
2041 let y =
2042 if y < 0
2043 then state.maxy
2044 else if y >= state.maxy then 0 else y
2046 gotoy y;
2047 if state.mode = View
2048 then state.text <- "";
2049 state.deadline <- state.deadline +. 0.005;
2051 | _ ->
2052 state.deadline <- state.deadline +. delay;
2053 end;
2055 | l ->
2056 let rec checkfds c = function
2057 | [] -> c
2058 | fd :: rest when fd = state.csock ->
2059 let cmd = readcmd state.csock in
2060 act cmd;
2061 checkfds true rest
2062 | fd :: rest ->
2063 let s = String.create 80 in
2064 let n = Unix.read fd s 0 80 in
2065 if conf.redirectstderr
2066 then (
2067 Buffer.add_substring state.errmsgs s 0 n;
2068 state.newerrmsgs <- true;
2069 Glut.postRedisplay ();
2071 else (
2072 prerr_string (String.sub s 0 n);
2073 flush stderr;
2075 checkfds c rest
2077 if checkfds false l
2078 then loop 0.0
2079 end;
2080 in loop 0.007
2083 let onhist cb =
2084 let rc = cb.rc in
2085 let action = function
2086 | HCprev -> cbget cb ~-1
2087 | HCnext -> cbget cb 1
2088 | HCfirst -> cbget cb ~-(cb.rc)
2089 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2090 and cancel () = cb.rc <- rc
2091 in (action, cancel)
2094 let search pattern forward =
2095 if String.length pattern > 0
2096 then
2097 let pn, py =
2098 match state.layout with
2099 | [] -> 0, 0
2100 | l :: _ ->
2101 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2103 let cmd =
2104 let b = makecmd "search"
2105 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
2107 Buffer.add_char b ',';
2108 Buffer.add_string b pattern;
2109 Buffer.add_char b '\000';
2110 Buffer.contents b;
2112 writecmd state.csock cmd;
2115 let intentry text key =
2116 let c = Char.unsafe_chr key in
2117 match c with
2118 | '0' .. '9' ->
2119 let text = addchar text c in
2120 TEcont text
2122 | _ ->
2123 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2124 TEcont text
2127 let textentry text key =
2128 let c = Char.unsafe_chr key in
2129 match c with
2130 | _ when key >= 32 && key < 127 ->
2131 let text = addchar text c in
2132 TEcont text
2134 | _ ->
2135 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
2136 TEcont text
2139 let reqlayout angle proportional =
2140 match state.throttle with
2141 | None ->
2142 if state.invalidated = 0 then state.anchor <- getanchor ();
2143 conf.angle <- angle mod 360;
2144 conf.proportional <- proportional;
2145 invalidate ();
2146 wcmd "reqlayout" [`i conf.angle; `b proportional];
2147 | _ -> ()
2150 let settrim trimmargins trimfuzz =
2151 if state.invalidated = 0 then state.anchor <- getanchor ();
2152 conf.trimmargins <- trimmargins;
2153 conf.trimfuzz <- trimfuzz;
2154 let x0, y0, x1, y1 = trimfuzz in
2155 invalidate ();
2156 wcmd "settrim" [
2157 `b conf.trimmargins;
2158 `i x0;
2159 `i y0;
2160 `i x1;
2161 `i y1;
2163 Hashtbl.iter (fun _ opaque ->
2164 wcmd "freepage" [`s opaque];
2165 ) state.pagemap;
2166 Hashtbl.clear state.pagemap;
2169 let setzoom zoom =
2170 match state.throttle with
2171 | None ->
2172 let zoom = max 0.01 zoom in
2173 if zoom <> conf.zoom
2174 then (
2175 state.prevzoom <- conf.zoom;
2176 let relx =
2177 if zoom <= 1.0
2178 then (state.x <- 0; 0.0)
2179 else float state.x /. float state.w
2181 conf.zoom <- zoom;
2182 reshape conf.winw conf.winh;
2183 if zoom > 1.0
2184 then (
2185 let x = relx *. float state.w in
2186 state.x <- truncate x;
2188 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2191 | Some (layout, y, started) ->
2192 let time =
2193 match conf.maxwait with
2194 | None -> 0.0
2195 | Some t -> t
2197 let dt = now () -. started in
2198 if dt > time
2199 then (
2200 state.y <- y;
2201 load layout;
2205 let setcolumns columns coverA coverB =
2206 if columns < 2
2207 then (
2208 conf.columns <- None;
2209 state.x <- 0;
2210 setzoom 1.0;
2212 else (
2213 conf.columns <- Some ((columns, coverA, coverB), [||]);
2214 conf.zoom <- 1.0;
2216 reshape conf.winw conf.winh;
2219 let enterbirdseye () =
2220 let zoom = float conf.thumbw /. float conf.winw in
2221 let birdseyepageno =
2222 let cy = conf.winh / 2 in
2223 let fold = function
2224 | [] -> 0
2225 | l :: rest ->
2226 let rec fold best = function
2227 | [] -> best.pageno
2228 | l :: rest ->
2229 let d = cy - (l.pagedispy + l.pagevh/2)
2230 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2231 if abs d < abs dbest
2232 then fold l rest
2233 else best.pageno
2234 in fold l rest
2236 fold state.layout
2238 state.mode <- Birdseye (
2239 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2241 conf.zoom <- zoom;
2242 conf.presentation <- false;
2243 conf.interpagespace <- 10;
2244 conf.hlinks <- false;
2245 state.x <- 0;
2246 state.mstate <- Mnone;
2247 conf.maxwait <- None;
2248 conf.columns <- (
2249 match conf.beyecolumns with
2250 | Some c ->
2251 conf.zoom <- 1.0;
2252 Some ((c, 0, 0), [||])
2253 | None -> None
2255 Glut.setCursor Glut.CURSOR_INHERIT;
2256 if conf.verbose
2257 then
2258 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2259 (100.0*.zoom)
2260 else
2261 state.text <- ""
2263 reshape conf.winw conf.winh;
2266 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2267 state.mode <- View;
2268 conf.zoom <- c.zoom;
2269 conf.presentation <- c.presentation;
2270 conf.interpagespace <- c.interpagespace;
2271 conf.maxwait <- c.maxwait;
2272 conf.hlinks <- c.hlinks;
2273 conf.beyecolumns <- (
2274 match conf.columns with
2275 | Some ((c, _, _), _) -> Some c
2276 | None -> None
2278 conf.columns <- (
2279 match c.columns with
2280 | Some (c, _) -> Some (c, [||])
2281 | None -> None
2283 state.x <- leftx;
2284 if conf.verbose
2285 then
2286 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2287 (100.0*.conf.zoom)
2289 reshape conf.winw conf.winh;
2290 state.anchor <- if goback then anchor else (pageno, 0.0);
2293 let togglebirdseye () =
2294 match state.mode with
2295 | Birdseye vals -> leavebirdseye vals true
2296 | View -> enterbirdseye ()
2297 | _ -> ()
2300 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2301 let pageno = max 0 (pageno - incr) in
2302 let rec loop = function
2303 | [] -> gotopage1 pageno 0
2304 | l :: _ when l.pageno = pageno ->
2305 if l.pagedispy >= 0 && l.pagey = 0
2306 then G.postRedisplay "upbirdseye"
2307 else gotopage1 pageno 0
2308 | _ :: rest -> loop rest
2310 loop state.layout;
2311 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2314 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2315 let pageno = min (state.pagecount - 1) (pageno + incr) in
2316 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2317 let rec loop = function
2318 | [] ->
2319 let y, h = getpageyh pageno in
2320 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2321 gotoy (clamp dy)
2322 | l :: _ when l.pageno = pageno ->
2323 if l.pagevh != l.pageh
2324 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2325 else G.postRedisplay "downbirdseye"
2326 | _ :: rest -> loop rest
2328 loop state.layout
2331 let optentry mode _ key =
2332 let btos b = if b then "on" else "off" in
2333 let c = Char.unsafe_chr key in
2334 match c with
2335 | 's' ->
2336 let ondone s =
2337 try conf.scrollstep <- int_of_string s with exc ->
2338 state.text <- Printf.sprintf "bad integer `%s': %s"
2339 s (Printexc.to_string exc)
2341 TEswitch ("scroll step: ", "", None, intentry, ondone)
2343 | 'A' ->
2344 let ondone s =
2346 conf.autoscrollstep <- int_of_string s;
2347 if state.autoscroll <> None
2348 then state.autoscroll <- Some conf.autoscrollstep
2349 with exc ->
2350 state.text <- Printf.sprintf "bad integer `%s': %s"
2351 s (Printexc.to_string exc)
2353 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2355 | 'C' ->
2356 let ondone s =
2358 let n, a, b = columns_of_string s in
2359 setcolumns n a b;
2360 with exc ->
2361 state.text <- Printf.sprintf "bad columns `%s': %s"
2362 s (Printexc.to_string exc)
2364 TEswitch ("columns: ", "", None, textentry, ondone)
2366 | 'Z' ->
2367 let ondone s =
2369 let zoom = float (int_of_string s) /. 100.0 in
2370 setzoom zoom
2371 with exc ->
2372 state.text <- Printf.sprintf "bad integer `%s': %s"
2373 s (Printexc.to_string exc)
2375 TEswitch ("zoom: ", "", None, intentry, ondone)
2377 | 't' ->
2378 let ondone s =
2380 conf.thumbw <- bound (int_of_string s) 2 4096;
2381 state.text <-
2382 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2383 begin match mode with
2384 | Birdseye beye ->
2385 leavebirdseye beye false;
2386 enterbirdseye ();
2387 | _ -> ();
2389 with exc ->
2390 state.text <- Printf.sprintf "bad integer `%s': %s"
2391 s (Printexc.to_string exc)
2393 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2395 | 'R' ->
2396 let ondone s =
2397 match try
2398 Some (int_of_string s)
2399 with exc ->
2400 state.text <- Printf.sprintf "bad integer `%s': %s"
2401 s (Printexc.to_string exc);
2402 None
2403 with
2404 | Some angle -> reqlayout angle conf.proportional
2405 | None -> ()
2407 TEswitch ("rotation: ", "", None, intentry, ondone)
2409 | 'i' ->
2410 conf.icase <- not conf.icase;
2411 TEdone ("case insensitive search " ^ (btos conf.icase))
2413 | 'p' ->
2414 conf.preload <- not conf.preload;
2415 gotoy state.y;
2416 TEdone ("preload " ^ (btos conf.preload))
2418 | 'v' ->
2419 conf.verbose <- not conf.verbose;
2420 TEdone ("verbose " ^ (btos conf.verbose))
2422 | 'd' ->
2423 conf.debug <- not conf.debug;
2424 TEdone ("debug " ^ (btos conf.debug))
2426 | 'h' ->
2427 conf.maxhfit <- not conf.maxhfit;
2428 state.maxy <-
2429 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2430 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2432 | 'c' ->
2433 conf.crophack <- not conf.crophack;
2434 TEdone ("crophack " ^ btos conf.crophack)
2436 | 'a' ->
2437 let s =
2438 match conf.maxwait with
2439 | None ->
2440 conf.maxwait <- Some infinity;
2441 "always wait for page to complete"
2442 | Some _ ->
2443 conf.maxwait <- None;
2444 "show placeholder if page is not ready"
2446 TEdone s
2448 | 'f' ->
2449 conf.underinfo <- not conf.underinfo;
2450 TEdone ("underinfo " ^ btos conf.underinfo)
2452 | 'P' ->
2453 conf.savebmarks <- not conf.savebmarks;
2454 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2456 | 'S' ->
2457 let ondone s =
2459 let pageno, py =
2460 match state.layout with
2461 | [] -> 0, 0
2462 | l :: _ ->
2463 l.pageno, l.pagey
2465 conf.interpagespace <- int_of_string s;
2466 state.maxy <- calcheight ();
2467 let y = getpagey pageno in
2468 gotoy (y + py)
2469 with exc ->
2470 state.text <- Printf.sprintf "bad integer `%s': %s"
2471 s (Printexc.to_string exc)
2473 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2475 | 'l' ->
2476 reqlayout conf.angle (not conf.proportional);
2477 TEdone ("proportional display " ^ btos conf.proportional)
2479 | 'T' ->
2480 settrim (not conf.trimmargins) conf.trimfuzz;
2481 TEdone ("trim margins " ^ btos conf.trimmargins)
2483 | 'I' ->
2484 conf.invert <- not conf.invert;
2485 TEdone ("invert colors " ^ btos conf.invert)
2487 | _ ->
2488 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2489 TEstop
2492 class type lvsource = object
2493 method getitemcount : int
2494 method getitem : int -> (string * int)
2495 method hasaction : int -> bool
2496 method exit :
2497 uioh:uioh ->
2498 cancel:bool ->
2499 active:int ->
2500 first:int ->
2501 pan:int ->
2502 qsearch:string ->
2503 uioh option
2504 method getactive : int
2505 method getfirst : int
2506 method getqsearch : string
2507 method setqsearch : string -> unit
2508 method getpan : int
2509 end;;
2511 class virtual lvsourcebase = object
2512 val mutable m_active = 0
2513 val mutable m_first = 0
2514 val mutable m_qsearch = ""
2515 val mutable m_pan = 0
2516 method getactive = m_active
2517 method getfirst = m_first
2518 method getqsearch = m_qsearch
2519 method getpan = m_pan
2520 method setqsearch s = m_qsearch <- s
2521 end;;
2523 let textentryspecial key = function
2524 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2525 let s =
2526 match key with
2527 | Glut.KEY_UP -> action HCprev
2528 | Glut.KEY_DOWN -> action HCnext
2529 | Glut.KEY_HOME -> action HCfirst
2530 | Glut.KEY_END -> action HClast
2531 | _ -> state.text
2533 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2534 G.postRedisplay "special textentry";
2535 | _ -> ()
2538 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2539 let enttext te =
2540 state.mode <- Textentry (te, onleave);
2541 state.text <- "";
2542 enttext ();
2543 G.postRedisplay "textentrykeyboard enttext";
2545 match Char.unsafe_chr key with
2546 | '\008' -> (* backspace *)
2547 let len = String.length text in
2548 if len = 0
2549 then (
2550 onleave Cancel;
2551 G.postRedisplay "textentrykeyboard after cancel";
2553 else (
2554 let s = String.sub text 0 (len - 1) in
2555 enttext (c, s, opthist, onkey, ondone)
2558 | '\r' | '\n' ->
2559 ondone text;
2560 onleave Confirm;
2561 G.postRedisplay "textentrykeyboard after confirm"
2563 | '\007' (* ctrl-g *)
2564 | '\027' -> (* escape *)
2565 if String.length text = 0
2566 then (
2567 begin match opthist with
2568 | None -> ()
2569 | Some (_, onhistcancel) -> onhistcancel ()
2570 end;
2571 onleave Cancel;
2572 state.text <- "";
2573 G.postRedisplay "textentrykeyboard after cancel2"
2575 else (
2576 enttext (c, "", opthist, onkey, ondone)
2579 | '\127' -> () (* delete *)
2581 | _ ->
2582 begin match onkey text key with
2583 | TEdone text ->
2584 ondone text;
2585 onleave Confirm;
2586 G.postRedisplay "textentrykeyboard after confirm2";
2588 | TEcont text ->
2589 enttext (c, text, opthist, onkey, ondone);
2591 | TEstop ->
2592 onleave Cancel;
2593 G.postRedisplay "textentrykeyboard after cancel3"
2595 | TEswitch te ->
2596 state.mode <- Textentry (te, onleave);
2597 G.postRedisplay "textentrykeyboard switch";
2598 end;
2601 let firstof first active =
2602 if first > active || abs (first - active) > fstate.maxrows - 1
2603 then max 0 (active - (fstate.maxrows/2))
2604 else first
2607 let calcfirst first active =
2608 if active > first
2609 then
2610 let rows = active - first in
2611 if rows > fstate.maxrows then active - fstate.maxrows else first
2612 else active
2615 let scrollph y maxy =
2616 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2617 let sh = float conf.winh /. sh in
2618 let sh = max sh (float conf.scrollh) in
2620 let percent =
2621 if y = state.maxy
2622 then 1.0
2623 else float y /. float maxy
2625 let position = (float conf.winh -. sh) *. percent in
2627 let position =
2628 if position +. sh > float conf.winh
2629 then float conf.winh -. sh
2630 else position
2632 position, sh;
2635 let coe s = (s :> uioh);;
2637 class listview ~(source:lvsource) ~trusted =
2638 object (self)
2639 val m_pan = source#getpan
2640 val m_first = source#getfirst
2641 val m_active = source#getactive
2642 val m_qsearch = source#getqsearch
2643 val m_prev_uioh = state.uioh
2645 method private elemunder y =
2646 let n = y / (fstate.fontsize+1) in
2647 if m_first + n < source#getitemcount
2648 then (
2649 if source#hasaction (m_first + n)
2650 then Some (m_first + n)
2651 else None
2653 else None
2655 method display =
2656 Gl.enable `blend;
2657 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2658 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2659 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2660 GlDraw.color (1., 1., 1.);
2661 Gl.enable `texture_2d;
2662 let fs = fstate.fontsize in
2663 let nfs = fs + 1 in
2664 let ww = fstate.wwidth in
2665 let tabw = 30.0*.ww in
2666 let itemcount = source#getitemcount in
2667 let rec loop row =
2668 if (row - m_first) * nfs > conf.winh
2669 then ()
2670 else (
2671 if row >= 0 && row < itemcount
2672 then (
2673 let (s, level) = source#getitem row in
2674 let y = (row - m_first) * nfs in
2675 let x = 5.0 +. float (level + m_pan) *. ww in
2676 if row = m_active
2677 then (
2678 Gl.disable `texture_2d;
2679 GlDraw.polygon_mode `both `line;
2680 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2681 GlDraw.rect (1., float (y + 1))
2682 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2683 GlDraw.polygon_mode `both `fill;
2684 GlDraw.color (1., 1., 1.);
2685 Gl.enable `texture_2d;
2688 let drawtabularstring s =
2689 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2690 if trusted
2691 then
2692 let tabpos = try String.index s '\t' with Not_found -> -1 in
2693 if tabpos > 0
2694 then
2695 let len = String.length s - tabpos - 1 in
2696 let s1 = String.sub s 0 tabpos
2697 and s2 = String.sub s (tabpos + 1) len in
2698 let nx = drawstr x s1 in
2699 let sw = nx -. x in
2700 let x = x +. (max tabw sw) in
2701 drawstr x s2
2702 else
2703 drawstr x s
2704 else
2705 drawstr x s
2707 let _ = drawtabularstring s in
2708 loop (row+1)
2712 loop m_first;
2713 Gl.disable `blend;
2714 Gl.disable `texture_2d;
2716 method updownlevel incr =
2717 let len = source#getitemcount in
2718 let curlevel =
2719 if m_active >= 0 && m_active < len
2720 then snd (source#getitem m_active)
2721 else -1
2723 let rec flow i =
2724 if i = len then i-1 else if i = -1 then 0 else
2725 let _, l = source#getitem i in
2726 if l != curlevel then i else flow (i+incr)
2728 let active = flow m_active in
2729 let first = calcfirst m_first active in
2730 G.postRedisplay "special outline updownlevel";
2731 {< m_active = active; m_first = first >}
2733 method private key1 key =
2734 let set active first qsearch =
2735 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2737 let search active pattern incr =
2738 let dosearch re =
2739 let rec loop n =
2740 if n >= 0 && n < source#getitemcount
2741 then (
2742 let s, _ = source#getitem n in
2744 (try ignore (Str.search_forward re s 0); true
2745 with Not_found -> false)
2746 then Some n
2747 else loop (n + incr)
2749 else None
2751 loop active
2754 let re = Str.regexp_case_fold pattern in
2755 dosearch re
2756 with Failure s ->
2757 state.text <- s;
2758 None
2760 match key with
2761 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2762 let incr = if key = 18 then -1 else 1 in
2763 let active, first =
2764 match search (m_active + incr) m_qsearch incr with
2765 | None ->
2766 state.text <- m_qsearch ^ " [not found]";
2767 m_active, m_first
2768 | Some active ->
2769 state.text <- m_qsearch;
2770 active, firstof m_first active
2772 G.postRedisplay "listview ctrl-r/s";
2773 set active first m_qsearch;
2775 | 8 -> (* backspace *)
2776 let len = String.length m_qsearch in
2777 if len = 0
2778 then coe self
2779 else (
2780 if len = 1
2781 then (
2782 state.text <- "";
2783 G.postRedisplay "listview empty qsearch";
2784 set m_active m_first "";
2786 else
2787 let qsearch = String.sub m_qsearch 0 (len - 1) in
2788 let active, first =
2789 match search m_active qsearch ~-1 with
2790 | None ->
2791 state.text <- qsearch ^ " [not found]";
2792 m_active, m_first
2793 | Some active ->
2794 state.text <- qsearch;
2795 active, firstof m_first active
2797 G.postRedisplay "listview backspace qsearch";
2798 set active first qsearch
2801 | _ when key >= 32 && key < 127 ->
2802 let pattern = addchar m_qsearch (Char.chr key) in
2803 let active, first =
2804 match search m_active pattern 1 with
2805 | None ->
2806 state.text <- pattern ^ " [not found]";
2807 m_active, m_first
2808 | Some active ->
2809 state.text <- pattern;
2810 active, firstof m_first active
2812 G.postRedisplay "listview qsearch add";
2813 set active first pattern;
2815 | 27 -> (* escape *)
2816 state.text <- "";
2817 if String.length m_qsearch = 0
2818 then (
2819 G.postRedisplay "list view escape";
2820 begin
2821 match
2822 source#exit (coe self) true m_active m_first m_pan m_qsearch
2823 with
2824 | None -> m_prev_uioh
2825 | Some uioh -> uioh
2828 else (
2829 G.postRedisplay "list view kill qsearch";
2830 source#setqsearch "";
2831 coe {< m_qsearch = "" >}
2834 | 13 -> (* enter *)
2835 state.text <- "";
2836 let self = {< m_qsearch = "" >} in
2837 source#setqsearch "";
2838 let opt =
2839 G.postRedisplay "listview enter";
2840 if m_active >= 0 && m_active < source#getitemcount
2841 then (
2842 source#exit (coe self) false m_active m_first m_pan "";
2844 else (
2845 source#exit (coe self) true m_active m_first m_pan "";
2848 begin match opt with
2849 | None -> m_prev_uioh
2850 | Some uioh -> uioh
2853 | 127 -> (* delete *)
2854 coe self
2856 | _ -> dolog "unknown key %d" key; coe self
2858 method private special1 key =
2859 let itemcount = source#getitemcount in
2860 let find start incr =
2861 let rec find i =
2862 if i = -1 || i = itemcount
2863 then -1
2864 else (
2865 if source#hasaction i
2866 then i
2867 else find (i + incr)
2870 find start
2872 let set active first =
2873 let first = bound first 0 (itemcount - fstate.maxrows) in
2874 state.text <- "";
2875 coe {< m_active = active; m_first = first >}
2877 let navigate incr =
2878 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2879 let active, first =
2880 let incr1 = if incr > 0 then 1 else -1 in
2881 if isvisible m_first m_active
2882 then
2883 let next =
2884 let next = m_active + incr in
2885 let next =
2886 if next < 0 || next >= itemcount
2887 then -1
2888 else find next incr1
2890 if next = -1 || abs (m_active - next) > fstate.maxrows
2891 then -1
2892 else next
2894 if next = -1
2895 then
2896 let first = m_first + incr in
2897 let first = bound first 0 (itemcount - 1) in
2898 let next =
2899 let next = m_active + incr in
2900 let next = bound next 0 (itemcount - 1) in
2901 find next ~-incr1
2903 let active = if next = -1 then m_active else next in
2904 active, first
2905 else
2906 let first = min next m_first in
2907 let first =
2908 if abs (next - first) > fstate.maxrows
2909 then first + incr
2910 else first
2912 next, first
2913 else
2914 let first = m_first + incr in
2915 let first = bound first 0 (itemcount - 1) in
2916 let active =
2917 let next = m_active + incr in
2918 let next = bound next 0 (itemcount - 1) in
2919 let next = find next incr1 in
2920 let active =
2921 if next = -1 || abs (m_active - first) > fstate.maxrows
2922 then (
2923 let active = if m_active = -1 then next else m_active in
2924 active
2926 else next
2928 if isvisible first active
2929 then active
2930 else -1
2932 active, first
2934 G.postRedisplay "listview navigate";
2935 set active first;
2937 begin match key with
2938 | Glut.KEY_UP -> navigate ~-1
2939 | Glut.KEY_DOWN -> navigate 1
2940 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2941 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2943 | Glut.KEY_RIGHT ->
2944 state.text <- "";
2945 G.postRedisplay "listview right";
2946 coe {< m_pan = m_pan - 1 >}
2948 | Glut.KEY_LEFT ->
2949 state.text <- "";
2950 G.postRedisplay "listview left";
2951 coe {< m_pan = m_pan + 1 >}
2953 | Glut.KEY_HOME ->
2954 let active = find 0 1 in
2955 G.postRedisplay "listview home";
2956 set active 0;
2958 | Glut.KEY_END ->
2959 let first = max 0 (itemcount - fstate.maxrows) in
2960 let active = find (itemcount - 1) ~-1 in
2961 G.postRedisplay "listview end";
2962 set active first;
2964 | _ -> coe self
2965 end;
2967 method key key =
2968 match state.mode with
2969 | Textentry te -> textentrykeyboard key te; coe self
2970 | _ -> self#key1 key
2972 method special key =
2973 match state.mode with
2974 | Textentry te -> textentryspecial key te; coe self
2975 | _ -> self#special1 key
2977 method button button bstate x y =
2978 let opt =
2979 match button with
2980 | Glut.LEFT_BUTTON when x > conf.winw - conf.scrollbw ->
2981 G.postRedisplay "listview scroll";
2982 if bstate = Glut.DOWN
2983 then
2984 let _, position, sh = self#scrollph in
2985 if y > truncate position && y < truncate (position +. sh)
2986 then (
2987 state.mstate <- Mscrolly;
2988 Some (coe self)
2990 else
2991 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
2992 let first = truncate (s *. float source#getitemcount) in
2993 let first = min source#getitemcount first in
2994 Some (coe {< m_first = first; m_active = first >})
2995 else (
2996 state.mstate <- Mnone;
2997 Some (coe self);
2999 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
3000 begin match self#elemunder y with
3001 | Some n ->
3002 G.postRedisplay "listview click";
3003 source#exit
3004 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3005 | _ ->
3006 Some (coe self)
3008 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
3009 let len = source#getitemcount in
3010 let first =
3011 if n = 4 && m_first + fstate.maxrows >= len
3012 then
3013 m_first
3014 else
3015 let first = m_first + (if n == 3 then -1 else 1) in
3016 bound first 0 (len - 1)
3018 G.postRedisplay "listview wheel";
3019 Some (coe {< m_first = first >})
3020 | _ ->
3021 Some (coe self)
3023 match opt with
3024 | None -> m_prev_uioh
3025 | Some uioh -> uioh
3027 method motion _ y =
3028 match state.mstate with
3029 | Mscrolly ->
3030 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3031 let first = truncate (s *. float source#getitemcount) in
3032 let first = min source#getitemcount first in
3033 G.postRedisplay "listview motion";
3034 coe {< m_first = first; m_active = first >}
3035 | _ -> coe self
3037 method pmotion x y =
3038 if x < conf.winw - conf.scrollbw
3039 then
3040 let n =
3041 match self#elemunder y with
3042 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
3043 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
3045 let o =
3046 if n != m_active
3047 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3048 else self
3050 coe o
3051 else (
3052 Glut.setCursor Glut.CURSOR_INHERIT;
3053 coe self
3056 method infochanged _ = ()
3058 method scrollpw = (0, 0.0, 0.0)
3059 method scrollph =
3060 let nfs = fstate.fontsize + 1 in
3061 let y = m_first * nfs in
3062 let itemcount = source#getitemcount in
3063 let maxi = max 0 (itemcount - fstate.maxrows) in
3064 let maxy = maxi * nfs in
3065 let p, h = scrollph y maxy in
3066 conf.scrollbw, p, h
3067 end;;
3069 class outlinelistview ~source =
3070 object (self)
3071 inherit listview ~source:(source :> lvsource) ~trusted:false as super
3073 method key key =
3074 match key with
3075 | 14 -> (* ctrl-n *)
3076 source#narrow m_qsearch;
3077 G.postRedisplay "outline ctrl-n";
3078 coe {< m_first = 0; m_active = 0 >}
3080 | 21 -> (* ctrl-u *)
3081 source#denarrow;
3082 G.postRedisplay "outline ctrl-u";
3083 state.text <- "";
3084 coe {< m_first = 0; m_active = 0 >}
3086 | 12 -> (* ctrl-l *)
3087 let first = m_active - (fstate.maxrows / 2) in
3088 G.postRedisplay "outline ctrl-l";
3089 coe {< m_first = first >}
3091 | 127 -> (* delete *)
3092 source#remove m_active;
3093 G.postRedisplay "outline delete";
3094 let active = max 0 (m_active-1) in
3095 coe {< m_first = firstof m_first active;
3096 m_active = active >}
3098 | key -> super#key key
3100 method special key =
3101 let calcfirst first active =
3102 if active > first
3103 then
3104 let rows = active - first in
3105 if rows > fstate.maxrows then active - fstate.maxrows else first
3106 else active
3108 let navigate incr =
3109 let active = m_active + incr in
3110 let active = bound active 0 (source#getitemcount - 1) in
3111 let first = calcfirst m_first active in
3112 G.postRedisplay "special outline navigate";
3113 coe {< m_active = active; m_first = first >}
3115 match key with
3116 | Glut.KEY_UP -> navigate ~-1
3117 | Glut.KEY_DOWN -> navigate 1
3118 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
3119 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
3121 | Glut.KEY_RIGHT ->
3122 let o =
3123 if Glut.getModifiers () land Glut.active_ctrl != 0
3124 then (
3125 G.postRedisplay "special outline right";
3126 {< m_pan = m_pan + 1 >}
3128 else self#updownlevel 1
3130 coe o
3132 | Glut.KEY_LEFT ->
3133 let o =
3134 if Glut.getModifiers () land Glut.active_ctrl != 0
3135 then (
3136 G.postRedisplay "special outline left";
3137 {< m_pan = m_pan - 1 >}
3139 else self#updownlevel ~-1
3141 coe o
3143 | Glut.KEY_HOME ->
3144 G.postRedisplay "special outline home";
3145 coe {< m_first = 0; m_active = 0 >}
3147 | Glut.KEY_END ->
3148 let active = source#getitemcount - 1 in
3149 let first = max 0 (active - fstate.maxrows) in
3150 G.postRedisplay "special outline end";
3151 coe {< m_active = active; m_first = first >}
3153 | _ -> super#special key
3156 let outlinesource usebookmarks =
3157 let empty = [||] in
3158 (object
3159 inherit lvsourcebase
3160 val mutable m_items = empty
3161 val mutable m_orig_items = empty
3162 val mutable m_prev_items = empty
3163 val mutable m_narrow_pattern = ""
3164 val mutable m_hadremovals = false
3166 method getitemcount =
3167 Array.length m_items + (if m_hadremovals then 1 else 0)
3169 method getitem n =
3170 if n == Array.length m_items && m_hadremovals
3171 then
3172 ("[Confirm removal]", 0)
3173 else
3174 let s, n, _ = m_items.(n) in
3175 (s, n)
3177 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3178 ignore (uioh, first, qsearch);
3179 let confrimremoval = m_hadremovals && active = Array.length m_items in
3180 let items =
3181 if String.length m_narrow_pattern = 0
3182 then m_orig_items
3183 else m_items
3185 if not cancel
3186 then (
3187 if not confrimremoval
3188 then(
3189 let _, _, anchor = m_items.(active) in
3190 gotoanchor anchor;
3191 m_items <- items;
3193 else (
3194 state.bookmarks <- Array.to_list m_items;
3195 m_orig_items <- m_items;
3198 else m_items <- items;
3199 m_pan <- pan;
3200 None
3202 method hasaction _ = true
3204 method greetmsg =
3205 if Array.length m_items != Array.length m_orig_items
3206 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3207 else ""
3209 method narrow pattern =
3210 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3211 match reopt with
3212 | None -> ()
3213 | Some re ->
3214 let rec loop accu n =
3215 if n = -1
3216 then (
3217 m_narrow_pattern <- pattern;
3218 m_items <- Array.of_list accu
3220 else
3221 let (s, _, _) as o = m_items.(n) in
3222 let accu =
3223 if (try ignore (Str.search_forward re s 0); true
3224 with Not_found -> false)
3225 then o :: accu
3226 else accu
3228 loop accu (n-1)
3230 loop [] (Array.length m_items - 1)
3232 method denarrow =
3233 m_orig_items <- (
3234 if usebookmarks
3235 then Array.of_list state.bookmarks
3236 else state.outlines
3238 m_items <- m_orig_items
3240 method remove m =
3241 if usebookmarks
3242 then
3243 if m >= 0 && m < Array.length m_items
3244 then (
3245 m_hadremovals <- true;
3246 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3247 let n = if n >= m then n+1 else n in
3248 m_items.(n)
3252 method reset anchor items =
3253 m_hadremovals <- false;
3254 if m_orig_items == empty || m_prev_items != items
3255 then (
3256 m_orig_items <- items;
3257 if String.length m_narrow_pattern = 0
3258 then m_items <- items;
3260 m_prev_items <- items;
3261 let rely = getanchory anchor in
3262 let active =
3263 let rec loop n best bestd =
3264 if n = Array.length m_items
3265 then best
3266 else
3267 let (_, _, anchor) = m_items.(n) in
3268 let orely = getanchory anchor in
3269 let d = abs (orely - rely) in
3270 if d < bestd
3271 then loop (n+1) n d
3272 else loop (n+1) best bestd
3274 loop 0 ~-1 max_int
3276 m_active <- active;
3277 m_first <- firstof m_first active
3278 end)
3281 let enterselector usebookmarks =
3282 let source = outlinesource usebookmarks in
3283 fun errmsg ->
3284 let outlines =
3285 if usebookmarks
3286 then Array.of_list state.bookmarks
3287 else state.outlines
3289 if Array.length outlines = 0
3290 then (
3291 showtext ' ' errmsg;
3293 else (
3294 state.text <- source#greetmsg;
3295 Glut.setCursor Glut.CURSOR_INHERIT;
3296 let anchor = getanchor () in
3297 source#reset anchor outlines;
3298 state.uioh <- coe (new outlinelistview ~source);
3299 G.postRedisplay "enter selector";
3303 let enteroutlinemode =
3304 let f = enterselector false in
3305 fun ()-> f "Document has no outline";
3308 let enterbookmarkmode =
3309 let f = enterselector true in
3310 fun () -> f "Document has no bookmarks (yet)";
3313 let color_of_string s =
3314 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3315 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3319 let color_to_string (r, g, b) =
3320 let r = truncate (r *. 256.0)
3321 and g = truncate (g *. 256.0)
3322 and b = truncate (b *. 256.0) in
3323 Printf.sprintf "%d/%d/%d" r g b
3326 let irect_of_string s =
3327 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3330 let irect_to_string (x0,y0,x1,y1) =
3331 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3334 let makecheckers () =
3335 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3336 following to say:
3337 converted by Issac Trotts. July 25, 2002 *)
3338 let image_height = 64
3339 and image_width = 64 in
3341 let make_image () =
3342 let image =
3343 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3345 for i = 0 to image_width - 1 do
3346 for j = 0 to image_height - 1 do
3347 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3348 (if (i land 8 ) lxor (j land 8) = 0
3349 then [|255;255;255|] else [|200;200;200|])
3350 done
3351 done;
3352 image
3354 let image = make_image () in
3355 let id = GlTex.gen_texture () in
3356 GlTex.bind_texture `texture_2d id;
3357 GlPix.store (`unpack_alignment 1);
3358 GlTex.image2d image;
3359 List.iter (GlTex.parameter ~target:`texture_2d)
3360 [ `wrap_s `repeat;
3361 `wrap_t `repeat;
3362 `mag_filter `nearest;
3363 `min_filter `nearest ];
3367 let setcheckers enabled =
3368 match state.texid with
3369 | None ->
3370 if enabled then state.texid <- Some (makecheckers ())
3372 | Some texid ->
3373 if not enabled
3374 then (
3375 GlTex.delete_texture texid;
3376 state.texid <- None;
3380 let int_of_string_with_suffix s =
3381 let l = String.length s in
3382 let s1, shift =
3383 if l > 1
3384 then
3385 let suffix = Char.lowercase s.[l-1] in
3386 match suffix with
3387 | 'k' -> String.sub s 0 (l-1), 10
3388 | 'm' -> String.sub s 0 (l-1), 20
3389 | 'g' -> String.sub s 0 (l-1), 30
3390 | _ -> s, 0
3391 else s, 0
3393 let n = int_of_string s1 in
3394 let m = n lsl shift in
3395 if m < 0 || m < n
3396 then raise (Failure "value too large")
3397 else m
3400 let string_with_suffix_of_int n =
3401 if n = 0
3402 then "0"
3403 else
3404 let n, s =
3405 if n = 0
3406 then 0, ""
3407 else (
3408 if n land ((1 lsl 20) - 1) = 0
3409 then n lsr 20, "M"
3410 else (
3411 if n land ((1 lsl 10) - 1) = 0
3412 then n lsr 10, "K"
3413 else n, ""
3417 let rec loop s n =
3418 let h = n mod 1000 in
3419 let n = n / 1000 in
3420 if n = 0
3421 then string_of_int h ^ s
3422 else (
3423 let s = Printf.sprintf "_%03d%s" h s in
3424 loop s n
3427 loop "" n ^ s;
3430 let defghyllscroll = (40, 8, 32);;
3431 let ghyllscroll_of_string s =
3432 let (n, a, b) as nab =
3433 if s = "default"
3434 then defghyllscroll
3435 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3437 if n <= a || n <= b || a >= b
3438 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3439 nab;
3442 let ghyllscroll_to_string ((n, a, b) as nab) =
3443 if nab = defghyllscroll
3444 then "default"
3445 else Printf.sprintf "%d,%d,%d" n a b;
3448 let describe_location () =
3449 let f (fn, _) l =
3450 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3452 let fn, ln = List.fold_left f (-1, -1) state.layout in
3453 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3454 let percent =
3455 if maxy <= 0
3456 then 100.
3457 else (100. *. (float state.y /. float maxy))
3459 if fn = ln
3460 then
3461 Printf.sprintf "page %d of %d [%.2f%%]"
3462 (fn+1) state.pagecount percent
3463 else
3464 Printf.sprintf
3465 "pages %d-%d of %d [%.2f%%]"
3466 (fn+1) (ln+1) state.pagecount percent
3469 let enterinfomode =
3470 let btos b = if b then "\xe2\x88\x9a" else "" in
3471 let showextended = ref false in
3472 let leave mode = function
3473 | Confirm -> state.mode <- mode
3474 | Cancel -> state.mode <- mode in
3475 let src =
3476 (object
3477 val mutable m_first_time = true
3478 val mutable m_l = []
3479 val mutable m_a = [||]
3480 val mutable m_prev_uioh = nouioh
3481 val mutable m_prev_mode = View
3483 inherit lvsourcebase
3485 method reset prev_mode prev_uioh =
3486 m_a <- Array.of_list (List.rev m_l);
3487 m_l <- [];
3488 m_prev_mode <- prev_mode;
3489 m_prev_uioh <- prev_uioh;
3490 if m_first_time
3491 then (
3492 let rec loop n =
3493 if n >= Array.length m_a
3494 then ()
3495 else
3496 match m_a.(n) with
3497 | _, _, _, Action _ -> m_active <- n
3498 | _ -> loop (n+1)
3500 loop 0;
3501 m_first_time <- false;
3504 method int name get set =
3505 m_l <-
3506 (name, `int get, 1, Action (
3507 fun u ->
3508 let ondone s =
3509 try set (int_of_string s)
3510 with exn ->
3511 state.text <- Printf.sprintf "bad integer `%s': %s"
3512 s (Printexc.to_string exn)
3514 state.text <- "";
3515 let te = name ^ ": ", "", None, intentry, ondone in
3516 state.mode <- Textentry (te, leave m_prev_mode);
3518 )) :: m_l
3520 method int_with_suffix name get set =
3521 m_l <-
3522 (name, `intws get, 1, Action (
3523 fun u ->
3524 let ondone s =
3525 try set (int_of_string_with_suffix s)
3526 with exn ->
3527 state.text <- Printf.sprintf "bad integer `%s': %s"
3528 s (Printexc.to_string exn)
3530 state.text <- "";
3531 let te =
3532 name ^ ": ", "", None, intentry_with_suffix, ondone
3534 state.mode <- Textentry (te, leave m_prev_mode);
3536 )) :: m_l
3538 method bool ?(offset=1) ?(btos=btos) name get set =
3539 m_l <-
3540 (name, `bool (btos, get), offset, Action (
3541 fun u ->
3542 let v = get () in
3543 set (not v);
3545 )) :: m_l
3547 method color name get set =
3548 m_l <-
3549 (name, `color get, 1, Action (
3550 fun u ->
3551 let invalid = (nan, nan, nan) in
3552 let ondone s =
3553 let c =
3554 try color_of_string s
3555 with exn ->
3556 state.text <- Printf.sprintf "bad color `%s': %s"
3557 s (Printexc.to_string exn);
3558 invalid
3560 if c <> invalid
3561 then set c;
3563 let te = name ^ ": ", "", None, textentry, ondone in
3564 state.text <- color_to_string (get ());
3565 state.mode <- Textentry (te, leave m_prev_mode);
3567 )) :: m_l
3569 method string name get set =
3570 m_l <-
3571 (name, `string get, 1, Action (
3572 fun u ->
3573 let ondone s = set s in
3574 let te = name ^ ": ", "", None, textentry, ondone in
3575 state.mode <- Textentry (te, leave m_prev_mode);
3577 )) :: m_l
3579 method colorspace name get set =
3580 m_l <-
3581 (name, `string get, 1, Action (
3582 fun _ ->
3583 let source =
3584 let vals = [| "rgb"; "bgr"; "gray" |] in
3585 (object
3586 inherit lvsourcebase
3588 initializer
3589 m_active <- int_of_colorspace conf.colorspace;
3590 m_first <- 0;
3592 method getitemcount = Array.length vals
3593 method getitem n = (vals.(n), 0)
3594 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3595 ignore (uioh, first, pan, qsearch);
3596 if not cancel then set active;
3597 None
3598 method hasaction _ = true
3599 end)
3601 state.text <- "";
3602 coe (new listview ~source ~trusted:true)
3603 )) :: m_l
3605 method caption s offset =
3606 m_l <- (s, `empty, offset, Noaction) :: m_l
3608 method caption2 s f offset =
3609 m_l <- (s, `string f, offset, Noaction) :: m_l
3611 method getitemcount = Array.length m_a
3613 method getitem n =
3614 let tostr = function
3615 | `int f -> string_of_int (f ())
3616 | `intws f -> string_with_suffix_of_int (f ())
3617 | `string f -> f ()
3618 | `color f -> color_to_string (f ())
3619 | `bool (btos, f) -> btos (f ())
3620 | `empty -> ""
3622 let name, t, offset, _ = m_a.(n) in
3623 ((let s = tostr t in
3624 if String.length s > 0
3625 then Printf.sprintf "%s\t%s" name s
3626 else name),
3627 offset)
3629 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3630 let uiohopt =
3631 if not cancel
3632 then (
3633 m_qsearch <- qsearch;
3634 let uioh =
3635 match m_a.(active) with
3636 | _, _, _, Action f -> f uioh
3637 | _ -> uioh
3639 Some uioh
3641 else None
3643 m_active <- active;
3644 m_first <- first;
3645 m_pan <- pan;
3646 uiohopt
3648 method hasaction n =
3649 match m_a.(n) with
3650 | _, _, _, Action _ -> true
3651 | _ -> false
3652 end)
3654 let rec fillsrc prevmode prevuioh =
3655 let sep () = src#caption "" 0 in
3656 let colorp name get set =
3657 src#string name
3658 (fun () -> color_to_string (get ()))
3659 (fun v ->
3661 let c = color_of_string v in
3662 set c
3663 with exn ->
3664 state.text <- Printf.sprintf "bad color `%s': %s"
3665 v (Printexc.to_string exn);
3668 let oldmode = state.mode in
3669 let birdseye = isbirdseye state.mode in
3671 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3673 src#bool "presentation mode"
3674 (fun () -> conf.presentation)
3675 (fun v ->
3676 conf.presentation <- v;
3677 state.anchor <- getanchor ();
3678 represent ());
3680 src#bool "ignore case in searches"
3681 (fun () -> conf.icase)
3682 (fun v -> conf.icase <- v);
3684 src#bool "preload"
3685 (fun () -> conf.preload)
3686 (fun v -> conf.preload <- v);
3688 src#bool "highlight links"
3689 (fun () -> conf.hlinks)
3690 (fun v -> conf.hlinks <- v);
3692 src#bool "under info"
3693 (fun () -> conf.underinfo)
3694 (fun v -> conf.underinfo <- v);
3696 src#bool "persistent bookmarks"
3697 (fun () -> conf.savebmarks)
3698 (fun v -> conf.savebmarks <- v);
3700 src#bool "proportional display"
3701 (fun () -> conf.proportional)
3702 (fun v -> reqlayout conf.angle v);
3704 src#bool "trim margins"
3705 (fun () -> conf.trimmargins)
3706 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3708 src#bool "persistent location"
3709 (fun () -> conf.jumpback)
3710 (fun v -> conf.jumpback <- v);
3712 sep ();
3713 src#int "inter-page space"
3714 (fun () -> conf.interpagespace)
3715 (fun n ->
3716 conf.interpagespace <- n;
3717 let pageno, py =
3718 match state.layout with
3719 | [] -> 0, 0
3720 | l :: _ ->
3721 l.pageno, l.pagey
3723 state.maxy <- calcheight ();
3724 let y = getpagey pageno in
3725 gotoy (y + py)
3728 src#int "page bias"
3729 (fun () -> conf.pagebias)
3730 (fun v -> conf.pagebias <- v);
3732 src#int "scroll step"
3733 (fun () -> conf.scrollstep)
3734 (fun n -> conf.scrollstep <- n);
3736 src#int "auto scroll step"
3737 (fun () ->
3738 match state.autoscroll with
3739 | Some step -> step
3740 | _ -> conf.autoscrollstep)
3741 (fun n ->
3742 if state.autoscroll <> None
3743 then state.autoscroll <- Some n;
3744 conf.autoscrollstep <- n);
3746 src#int "zoom"
3747 (fun () -> truncate (conf.zoom *. 100.))
3748 (fun v -> setzoom ((float v) /. 100.));
3750 src#int "rotation"
3751 (fun () -> conf.angle)
3752 (fun v -> reqlayout v conf.proportional);
3754 src#int "scroll bar width"
3755 (fun () -> state.scrollw)
3756 (fun v ->
3757 state.scrollw <- v;
3758 conf.scrollbw <- v;
3759 reshape conf.winw conf.winh;
3762 src#int "scroll handle height"
3763 (fun () -> conf.scrollh)
3764 (fun v -> conf.scrollh <- v;);
3766 src#int "thumbnail width"
3767 (fun () -> conf.thumbw)
3768 (fun v ->
3769 conf.thumbw <- min 4096 v;
3770 match oldmode with
3771 | Birdseye beye ->
3772 leavebirdseye beye false;
3773 enterbirdseye ()
3774 | _ -> ()
3777 src#string "columns"
3778 (fun () ->
3779 match conf.columns with
3780 | None -> "1"
3781 | Some (multicol, _) -> columns_to_string multicol)
3782 (fun v ->
3783 let n, a, b = columns_of_string v in
3784 setcolumns n a b);
3786 sep ();
3787 src#caption "Presentation mode" 0;
3788 src#bool "scrollbar visible"
3789 (fun () -> conf.scrollbarinpm)
3790 (fun v ->
3791 if v != conf.scrollbarinpm
3792 then (
3793 conf.scrollbarinpm <- v;
3794 if conf.presentation
3795 then (
3796 state.scrollw <- if v then conf.scrollbw else 0;
3797 reshape conf.winw conf.winh;
3802 sep ();
3803 src#caption "Pixmap cache" 0;
3804 src#int_with_suffix "size (advisory)"
3805 (fun () -> conf.memlimit)
3806 (fun v -> conf.memlimit <- v);
3808 src#caption2 "used"
3809 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3810 (string_with_suffix_of_int state.memused)
3811 (Hashtbl.length state.tilemap)) 1;
3813 sep ();
3814 src#caption "Layout" 0;
3815 src#caption2 "Dimension"
3816 (fun () ->
3817 Printf.sprintf "%dx%d (virtual %dx%d)"
3818 conf.winw conf.winh
3819 state.w state.maxy)
3821 if conf.debug
3822 then
3823 src#caption2 "Position" (fun () ->
3824 Printf.sprintf "%dx%d" state.x state.y
3826 else
3827 src#caption2 "Visible" (fun () -> describe_location ()) 1
3830 sep ();
3831 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3832 "Save these parameters as global defaults at exit"
3833 (fun () -> conf.bedefault)
3834 (fun v -> conf.bedefault <- v)
3837 sep ();
3838 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3839 src#bool ~offset:0 ~btos "Extended parameters"
3840 (fun () -> !showextended)
3841 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3842 if !showextended
3843 then (
3844 src#bool "checkers"
3845 (fun () -> conf.checkers)
3846 (fun v -> conf.checkers <- v; setcheckers v);
3847 src#bool "verbose"
3848 (fun () -> conf.verbose)
3849 (fun v -> conf.verbose <- v);
3850 src#bool "invert colors"
3851 (fun () -> conf.invert)
3852 (fun v -> conf.invert <- v);
3853 src#bool "max fit"
3854 (fun () -> conf.maxhfit)
3855 (fun v -> conf.maxhfit <- v);
3856 src#bool "redirect stderr"
3857 (fun () -> conf.redirectstderr)
3858 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3859 src#string "uri launcher"
3860 (fun () -> conf.urilauncher)
3861 (fun v -> conf.urilauncher <- v);
3862 src#string "tile size"
3863 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3864 (fun v ->
3866 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3867 conf.tileh <- max 64 w;
3868 conf.tilew <- max 64 h;
3869 flushtiles ();
3870 with exn ->
3871 state.text <- Printf.sprintf "bad tile size `%s': %s"
3872 v (Printexc.to_string exn));
3873 src#int "texture count"
3874 (fun () -> conf.texcount)
3875 (fun v ->
3876 if realloctexts v
3877 then conf.texcount <- v
3878 else showtext '!' " Failed to set texture count please retry later"
3880 src#int "slice height"
3881 (fun () -> conf.sliceheight)
3882 (fun v ->
3883 conf.sliceheight <- v;
3884 wcmd "sliceh" [`i conf.sliceheight];
3886 src#int "anti-aliasing level"
3887 (fun () -> conf.aalevel)
3888 (fun v ->
3889 conf.aalevel <- bound v 0 8;
3890 state.anchor <- getanchor ();
3891 opendoc state.path state.password;
3893 src#int "ui font size"
3894 (fun () -> fstate.fontsize)
3895 (fun v -> setfontsize (bound v 5 100));
3896 colorp "background color"
3897 (fun () -> conf.bgcolor)
3898 (fun v -> conf.bgcolor <- v);
3899 src#bool "crop hack"
3900 (fun () -> conf.crophack)
3901 (fun v -> conf.crophack <- v);
3902 src#string "trim fuzz"
3903 (fun () -> irect_to_string conf.trimfuzz)
3904 (fun v ->
3906 conf.trimfuzz <- irect_of_string v;
3907 if conf.trimmargins
3908 then settrim true conf.trimfuzz;
3909 with exn ->
3910 state.text <- Printf.sprintf "bad irect `%s': %s"
3911 v (Printexc.to_string exn)
3913 src#string "throttle"
3914 (fun () ->
3915 match conf.maxwait with
3916 | None -> "show place holder if page is not ready"
3917 | Some time ->
3918 if time = infinity
3919 then "wait for page to fully render"
3920 else
3921 "wait " ^ string_of_float time
3922 ^ " seconds before showing placeholder"
3924 (fun v ->
3926 let f = float_of_string v in
3927 if f <= 0.0
3928 then conf.maxwait <- None
3929 else conf.maxwait <- Some f
3930 with exn ->
3931 state.text <- Printf.sprintf "bad time `%s': %s"
3932 v (Printexc.to_string exn)
3934 src#string "ghyll scroll"
3935 (fun () ->
3936 match conf.ghyllscroll with
3937 | None -> ""
3938 | Some nab -> ghyllscroll_to_string nab
3940 (fun v ->
3942 let gs =
3943 if String.length v = 0
3944 then None
3945 else Some (ghyllscroll_of_string v)
3947 conf.ghyllscroll <- gs
3948 with exn ->
3949 state.text <- Printf.sprintf "bad ghyll `%s': %s"
3950 v (Printexc.to_string exn)
3952 src#colorspace "color space"
3953 (fun () -> colorspace_to_string conf.colorspace)
3954 (fun v ->
3955 conf.colorspace <- colorspace_of_int v;
3956 wcmd "cs" [`i v];
3957 load state.layout;
3961 sep ();
3962 src#caption "Document" 0;
3963 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3964 src#caption2 "Pages"
3965 (fun () -> string_of_int state.pagecount) 1;
3966 src#caption2 "Dimensions"
3967 (fun () -> string_of_int (List.length state.pdims)) 1;
3968 if conf.trimmargins
3969 then (
3970 sep ();
3971 src#caption "Trimmed margins" 0;
3972 src#caption2 "Dimensions"
3973 (fun () -> string_of_int (List.length state.pdims)) 1;
3976 src#reset prevmode prevuioh;
3978 fun () ->
3979 state.text <- "";
3980 let prevmode = state.mode
3981 and prevuioh = state.uioh in
3982 fillsrc prevmode prevuioh;
3983 let source = (src :> lvsource) in
3984 state.uioh <- coe (object (self)
3985 inherit listview ~source ~trusted:true as super
3986 val mutable m_prevmemused = 0
3987 method infochanged = function
3988 | Memused ->
3989 if m_prevmemused != state.memused
3990 then (
3991 m_prevmemused <- state.memused;
3992 G.postRedisplay "memusedchanged";
3994 | Pdim -> G.postRedisplay "pdimchanged"
3995 | Docinfo -> fillsrc prevmode prevuioh
3997 method special key =
3998 if Glut.getModifiers () land Glut.active_ctrl = 0
3999 then
4000 match key with
4001 | Glut.KEY_LEFT -> coe (self#updownlevel ~-1)
4002 | Glut.KEY_RIGHT -> coe (self#updownlevel 1)
4003 | _ -> super#special key
4004 else super#special key
4005 end);
4006 G.postRedisplay "info";
4009 let enterhelpmode =
4010 let source =
4011 (object
4012 inherit lvsourcebase
4013 method getitemcount = Array.length state.help
4014 method getitem n =
4015 let s, n, _ = state.help.(n) in
4016 (s, n)
4018 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4019 let optuioh =
4020 if not cancel
4021 then (
4022 m_qsearch <- qsearch;
4023 match state.help.(active) with
4024 | _, _, Action f -> Some (f uioh)
4025 | _ -> Some (uioh)
4027 else None
4029 m_active <- active;
4030 m_first <- first;
4031 m_pan <- pan;
4032 optuioh
4034 method hasaction n =
4035 match state.help.(n) with
4036 | _, _, Action _ -> true
4037 | _ -> false
4039 initializer
4040 m_active <- -1
4041 end)
4042 in fun () ->
4043 state.uioh <- coe (new listview ~source ~trusted:true);
4044 G.postRedisplay "help";
4047 let entermsgsmode =
4048 let msgsource =
4049 let re = Str.regexp "[\r\n]" in
4050 (object
4051 inherit lvsourcebase
4052 val mutable m_items = [||]
4054 method getitemcount = 1 + Array.length m_items
4056 method getitem n =
4057 if n = 0
4058 then "[Clear]", 0
4059 else m_items.(n-1), 0
4061 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4062 ignore uioh;
4063 if not cancel
4064 then (
4065 if active = 0
4066 then Buffer.clear state.errmsgs;
4067 m_qsearch <- qsearch;
4069 m_active <- active;
4070 m_first <- first;
4071 m_pan <- pan;
4072 None
4074 method hasaction n =
4075 n = 0
4077 method reset =
4078 state.newerrmsgs <- false;
4079 let l = Str.split re (Buffer.contents state.errmsgs) in
4080 m_items <- Array.of_list l
4082 initializer
4083 m_active <- 0
4084 end)
4085 in fun () ->
4086 state.text <- "";
4087 msgsource#reset;
4088 let source = (msgsource :> lvsource) in
4089 state.uioh <- coe (object
4090 inherit listview ~source ~trusted:false as super
4091 method display =
4092 if state.newerrmsgs
4093 then msgsource#reset;
4094 super#display
4095 end);
4096 G.postRedisplay "msgs";
4099 let quickbookmark ?title () =
4100 match state.layout with
4101 | [] -> ()
4102 | l :: _ ->
4103 let title =
4104 match title with
4105 | None ->
4106 let sec = Unix.gettimeofday () in
4107 let tm = Unix.localtime sec in
4108 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4109 (l.pageno+1)
4110 tm.Unix.tm_mday
4111 tm.Unix.tm_mon
4112 (tm.Unix.tm_year + 1900)
4113 tm.Unix.tm_hour
4114 tm.Unix.tm_min
4115 | Some title -> title
4117 state.bookmarks <-
4118 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
4119 :: state.bookmarks
4122 let doreshape w h =
4123 state.fullscreen <- None;
4124 Glut.reshapeWindow w h;
4127 let viewkeyboard key =
4128 let enttext te =
4129 let mode = state.mode in
4130 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4131 state.text <- "";
4132 enttext ();
4133 G.postRedisplay "view:enttext"
4135 let c = Char.chr key in
4136 match c with
4137 | '\027' | 'q' -> (* escape *)
4138 begin match state.mstate with
4139 | Mzoomrect _ ->
4140 state.mstate <- Mnone;
4141 Glut.setCursor Glut.CURSOR_INHERIT;
4142 G.postRedisplay "kill zoom rect";
4143 | _ ->
4144 raise Quit
4145 end;
4147 | '\008' -> (* backspace *)
4148 let y = getnav ~-1 in
4149 gotoy_and_clear_text y
4151 | 'o' ->
4152 enteroutlinemode ()
4154 | 'u' ->
4155 state.rects <- [];
4156 state.text <- "";
4157 G.postRedisplay "dehighlight";
4159 | '/' | '?' ->
4160 let ondone isforw s =
4161 cbput state.hists.pat s;
4162 state.searchpattern <- s;
4163 search s isforw
4165 let s = String.create 1 in
4166 s.[0] <- c;
4167 enttext (s, "", Some (onhist state.hists.pat),
4168 textentry, ondone (c ='/'))
4170 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4171 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4172 setzoom (conf.zoom +. incr)
4174 | '+' ->
4175 let ondone s =
4176 let n =
4177 try int_of_string s with exc ->
4178 state.text <- Printf.sprintf "bad integer `%s': %s"
4179 s (Printexc.to_string exc);
4180 max_int
4182 if n != max_int
4183 then (
4184 conf.pagebias <- n;
4185 state.text <- "page bias is now " ^ string_of_int n;
4188 enttext ("page bias: ", "", None, intentry, ondone)
4190 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4191 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4192 setzoom (max 0.01 (conf.zoom -. decr))
4194 | '-' ->
4195 let ondone msg = state.text <- msg in
4196 enttext (
4197 "option [acfhilpstvACPRSZTI]: ", "", None,
4198 optentry state.mode, ondone
4201 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4202 setzoom 1.0
4204 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4205 let zoom = zoomforh conf.winw conf.winh state.scrollw in
4206 if zoom < 1.0
4207 then setzoom zoom
4209 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
4210 togglebirdseye ()
4212 | '0' .. '9' ->
4213 let ondone s =
4214 let n =
4215 try int_of_string s with exc ->
4216 state.text <- Printf.sprintf "bad integer `%s': %s"
4217 s (Printexc.to_string exc);
4220 if n >= 0
4221 then (
4222 addnav ();
4223 cbput state.hists.pag (string_of_int n);
4224 gotopage1 (n + conf.pagebias - 1) 0;
4227 let pageentry text key =
4228 match Char.unsafe_chr key with
4229 | 'g' -> TEdone text
4230 | _ -> intentry text key
4232 let text = "x" in text.[0] <- c;
4233 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
4235 | 'b' ->
4236 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4237 reshape conf.winw conf.winh;
4239 | 'l' ->
4240 conf.hlinks <- not conf.hlinks;
4241 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4242 G.postRedisplay "toggle highlightlinks";
4244 | 'a' ->
4245 begin match state.autoscroll with
4246 | Some step ->
4247 conf.autoscrollstep <- step;
4248 state.autoscroll <- None
4249 | None ->
4250 if conf.autoscrollstep = 0
4251 then state.autoscroll <- Some 1
4252 else state.autoscroll <- Some conf.autoscrollstep
4255 | 'P' ->
4256 conf.presentation <- not conf.presentation;
4257 if conf.presentation
4258 then (
4259 if not conf.scrollbarinpm
4260 then state.scrollw <- 0;
4262 else
4263 state.scrollw <- conf.scrollbw;
4265 showtext ' ' ("presentation mode " ^
4266 if conf.presentation then "on" else "off");
4267 state.anchor <- getanchor ();
4268 represent ()
4270 | 'f' ->
4271 begin match state.fullscreen with
4272 | None ->
4273 state.fullscreen <- Some (conf.winw, conf.winh);
4274 Glut.fullScreen ()
4275 | Some (w, h) ->
4276 state.fullscreen <- None;
4277 doreshape w h
4280 | 'g' ->
4281 gotoy_and_clear_text 0
4283 | 'G' ->
4284 gotopage1 (state.pagecount - 1) 0
4286 | 'n' ->
4287 search state.searchpattern true
4289 | 'p' | 'N' ->
4290 search state.searchpattern false
4292 | 't' ->
4293 begin match state.layout with
4294 | [] -> ()
4295 | l :: _ ->
4296 gotoy_and_clear_text (getpagey l.pageno)
4299 | ' ' ->
4300 begin match List.rev state.layout with
4301 | [] -> ()
4302 | l :: _ ->
4303 let pageno = min (l.pageno+1) (state.pagecount-1) in
4304 gotoy_and_clear_text (getpagey pageno)
4307 | '\127' -> (* del *)
4308 begin match state.layout with
4309 | [] -> ()
4310 | l :: _ ->
4311 let pageno = max 0 (l.pageno-1) in
4312 gotoy_and_clear_text (getpagey pageno)
4315 | '=' ->
4316 showtext ' ' (describe_location ());
4318 | 'w' ->
4319 begin match state.layout with
4320 | [] -> ()
4321 | l :: _ ->
4322 doreshape (l.pagew + state.scrollw) l.pageh;
4323 G.postRedisplay "w"
4326 | '\'' ->
4327 enterbookmarkmode ()
4329 | 'h' ->
4330 enterhelpmode ()
4332 | 'i' ->
4333 enterinfomode ()
4335 | 'e' when conf.redirectstderr ->
4336 entermsgsmode ()
4338 | 'm' ->
4339 let ondone s =
4340 match state.layout with
4341 | l :: _ ->
4342 state.bookmarks <-
4343 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4344 :: state.bookmarks
4345 | _ -> ()
4347 enttext ("bookmark: ", "", None, textentry, ondone)
4349 | '~' ->
4350 quickbookmark ();
4351 showtext ' ' "Quick bookmark added";
4353 | 'z' ->
4354 begin match state.layout with
4355 | l :: _ ->
4356 let rect = getpdimrect l.pagedimno in
4357 let w, h =
4358 if conf.crophack
4359 then
4360 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4361 truncate (1.2 *. (rect.(3) -. rect.(0))))
4362 else
4363 (truncate (rect.(1) -. rect.(0)),
4364 truncate (rect.(3) -. rect.(0)))
4366 let w = truncate ((float w)*.conf.zoom)
4367 and h = truncate ((float h)*.conf.zoom) in
4368 if w != 0 && h != 0
4369 then (
4370 state.anchor <- getanchor ();
4371 doreshape (w + state.scrollw) (h + conf.interpagespace)
4373 G.postRedisplay "z";
4375 | [] -> ()
4378 | '\000' -> (* ctrl-2 *)
4379 let maxw = getmaxw () in
4380 if maxw > 0.0
4381 then setzoom (maxw /. float conf.winw)
4383 | '<' | '>' ->
4384 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
4386 | '[' | ']' ->
4387 conf.colorscale <-
4388 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
4390 G.postRedisplay "brightness";
4392 | 'k' ->
4393 begin match state.mode with
4394 | Birdseye beye -> upbirdseye 1 beye
4395 | _ -> gotoy (clamp (-conf.scrollstep))
4398 | 'j' ->
4399 begin match state.mode with
4400 | Birdseye beye -> downbirdseye 1 beye
4401 | _ -> gotoy (clamp conf.scrollstep)
4404 | 'r' ->
4405 state.anchor <- getanchor ();
4406 opendoc state.path state.password
4408 | 'v' when not conf.debug ->
4409 List.iter debugl state.layout;
4411 | 'v' when conf.debug ->
4412 state.rects <- [];
4413 List.iter (fun l ->
4414 match getopaque l.pageno with
4415 | None -> ()
4416 | Some opaque ->
4417 let x0, y0, x1, y1 = pagebbox opaque in
4418 let a,b = float x0, float y0 in
4419 let c,d = float x1, float y0 in
4420 let e,f = float x1, float y1 in
4421 let h,j = float x0, float y1 in
4422 let rect = (a,b,c,d,e,f,h,j) in
4423 debugrect rect;
4424 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4425 ) state.layout;
4426 G.postRedisplay "v";
4428 | _ ->
4429 vlog "huh? %d %c" key (Char.chr key);
4432 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
4433 match key with
4434 | 27 -> (* escape *)
4435 leavebirdseye beye true
4437 | 12 -> (* ctrl-l *)
4438 let y, h = getpageyh pageno in
4439 let top = (conf.winh - h) / 2 in
4440 gotoy (max 0 (y - top))
4442 | 13 -> (* enter *)
4443 leavebirdseye beye false
4445 | _ ->
4446 viewkeyboard key
4449 let keyboard ~key ~x ~y =
4450 ignore x;
4451 ignore y;
4452 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
4453 then wcmd "interrupt" []
4454 else state.uioh <- state.uioh#key key
4457 let birdseyespecial key ((oconf, leftx, _, hooverpageno, anchor) as beye) =
4458 let incr =
4459 match conf.columns with
4460 | None -> 1
4461 | Some ((c, _, _), _) -> c
4463 match key with
4464 | Glut.KEY_UP -> upbirdseye incr beye
4465 | Glut.KEY_DOWN -> downbirdseye incr beye
4466 | Glut.KEY_LEFT -> upbirdseye 1 beye
4467 | Glut.KEY_RIGHT -> downbirdseye 1 beye
4469 | Glut.KEY_PAGE_UP ->
4470 begin match state.layout with
4471 | l :: _ ->
4472 if l.pagey != 0
4473 then (
4474 state.mode <- Birdseye (
4475 oconf, leftx, l.pageno, hooverpageno, anchor
4477 gotopage1 l.pageno 0;
4479 else (
4480 let layout = layout (state.y-conf.winh) conf.winh in
4481 match layout with
4482 | [] -> gotoy (clamp (-conf.winh))
4483 | l :: _ ->
4484 state.mode <- Birdseye (
4485 oconf, leftx, l.pageno, hooverpageno, anchor
4487 gotopage1 l.pageno 0
4490 | [] -> gotoy (clamp (-conf.winh))
4491 end;
4493 | Glut.KEY_PAGE_DOWN ->
4494 begin match List.rev state.layout with
4495 | l :: _ ->
4496 let layout = layout (state.y + conf.winh) conf.winh in
4497 begin match layout with
4498 | [] ->
4499 let incr = l.pageh - l.pagevh in
4500 if incr = 0
4501 then (
4502 state.mode <-
4503 Birdseye (
4504 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
4506 G.postRedisplay "birdseye pagedown";
4508 else gotoy (clamp (incr + conf.interpagespace*2));
4510 | l :: _ ->
4511 state.mode <-
4512 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
4513 gotopage1 l.pageno 0;
4516 | [] -> gotoy (clamp conf.winh)
4517 end;
4519 | Glut.KEY_HOME ->
4520 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
4521 gotopage1 0 0
4523 | Glut.KEY_END ->
4524 let pageno = state.pagecount - 1 in
4525 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
4526 if not (pagevisible state.layout pageno)
4527 then
4528 let h =
4529 match List.rev state.pdims with
4530 | [] -> conf.winh
4531 | (_, _, h, _) :: _ -> h
4533 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4534 else G.postRedisplay "birdseye end";
4535 | _ -> ()
4538 let setautoscrollspeed step goingdown =
4539 let incr = max 1 ((abs step) / 2) in
4540 let incr = if goingdown then incr else -incr in
4541 let astep = step + incr in
4542 state.autoscroll <- Some astep;
4545 let special ~key ~x ~y =
4546 ignore x;
4547 ignore y;
4548 state.uioh <- state.uioh#special key
4551 let drawpage l =
4552 let color =
4553 match state.mode with
4554 | Textentry _ -> scalecolor 0.4
4555 | View -> scalecolor 1.0
4556 | Birdseye (_, _, pageno, hooverpageno, _) ->
4557 if l.pageno = hooverpageno
4558 then scalecolor 0.9
4559 else (
4560 if l.pageno = pageno
4561 then scalecolor 1.0
4562 else scalecolor 0.8
4565 drawtiles l color;
4566 begin match getopaque l.pageno with
4567 | Some opaque ->
4568 if tileready l l.pagex l.pagey
4569 then
4570 let x = l.pagedispx - l.pagex
4571 and y = l.pagedispy - l.pagey in
4572 postprocess opaque conf.hlinks x y;
4574 | _ -> ()
4575 end;
4578 let scrollindicator () =
4579 let sbw, ph, sh = state.uioh#scrollph in
4580 let sbh, pw, sw = state.uioh#scrollpw in
4582 GlDraw.color (0.64, 0.64, 0.64);
4583 GlDraw.rect
4584 (float (conf.winw - sbw), 0.)
4585 (float conf.winw, float conf.winh)
4587 GlDraw.rect
4588 (0., float (conf.winh - sbh))
4589 (float (conf.winw - state.scrollw - 1), float conf.winh)
4591 GlDraw.color (0.0, 0.0, 0.0);
4593 GlDraw.rect
4594 (float (conf.winw - sbw), ph)
4595 (float conf.winw, ph +. sh)
4597 GlDraw.rect
4598 (pw, float (conf.winh - sbh))
4599 (pw +. sw, float conf.winh)
4603 let pagetranslatepoint l x y =
4604 let dy = y - l.pagedispy in
4605 let y = dy + l.pagey in
4606 let dx = x - l.pagedispx in
4607 let x = dx + l.pagex in
4608 (x, y);
4611 let showsel () =
4612 match state.mstate with
4613 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4616 | Msel ((x0, y0), (x1, y1)) ->
4617 let rec loop = function
4618 | l :: ls ->
4619 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4620 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
4621 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
4622 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
4623 then
4624 match getopaque l.pageno with
4625 | Some opaque ->
4626 let dx, dy = pagetranslatepoint l 0 0 in
4627 let x0 = x0 + dx
4628 and y0 = y0 + dy
4629 and x1 = x1 + dx
4630 and y1 = y1 + dy in
4631 GlMat.mode `modelview;
4632 GlMat.push ();
4633 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
4634 seltext opaque (x0, y0, x1, y1);
4635 GlMat.pop ();
4636 | _ -> ()
4637 else loop ls
4638 | [] -> ()
4640 loop state.layout
4643 let showrects () =
4644 Gl.enable `blend;
4645 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4646 GlDraw.polygon_mode `both `fill;
4647 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4648 List.iter
4649 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4650 List.iter (fun l ->
4651 if l.pageno = pageno
4652 then (
4653 let dx = float (l.pagedispx - l.pagex) in
4654 let dy = float (l.pagedispy - l.pagey) in
4655 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4656 GlDraw.begins `quads;
4658 GlDraw.vertex2 (x0+.dx, y0+.dy);
4659 GlDraw.vertex2 (x1+.dx, y1+.dy);
4660 GlDraw.vertex2 (x2+.dx, y2+.dy);
4661 GlDraw.vertex2 (x3+.dx, y3+.dy);
4663 GlDraw.ends ();
4665 ) state.layout
4666 ) state.rects
4668 Gl.disable `blend;
4671 let display () =
4672 GlClear.color (scalecolor2 conf.bgcolor);
4673 GlClear.clear [`color];
4674 List.iter drawpage state.layout;
4675 showrects ();
4676 showsel ();
4677 state.uioh#display;
4678 scrollindicator ();
4679 begin match state.mstate with
4680 | Mzoomrect ((x0, y0), (x1, y1)) ->
4681 Gl.enable `blend;
4682 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4683 GlDraw.polygon_mode `both `fill;
4684 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4685 GlDraw.rect (float x0, float y0)
4686 (float x1, float y1);
4687 Gl.disable `blend;
4688 | _ -> ()
4689 end;
4690 enttext ();
4691 Glut.swapBuffers ();
4694 let getunder x y =
4695 let rec f = function
4696 | l :: rest ->
4697 begin match getopaque l.pageno with
4698 | Some opaque ->
4699 let x0 = l.pagedispx in
4700 let x1 = x0 + l.pagevw in
4701 let y0 = l.pagedispy in
4702 let y1 = y0 + l.pagevh in
4703 if y >= y0 && y <= y1 && x >= x0 && x <= x1
4704 then
4705 let px, py = pagetranslatepoint l x y in
4706 match whatsunder opaque px py with
4707 | Unone -> f rest
4708 | under -> under
4709 else f rest
4710 | _ ->
4711 f rest
4713 | [] -> Unone
4715 f state.layout
4718 let zoomrect x y x1 y1 =
4719 let x0 = min x x1
4720 and x1 = max x x1
4721 and y0 = min y y1 in
4722 gotoy (state.y + y0);
4723 state.anchor <- getanchor ();
4724 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4725 let margin =
4726 if state.w < conf.winw - state.scrollw
4727 then (conf.winw - state.scrollw - state.w) / 2
4728 else 0
4730 state.x <- (state.x + margin) - x0;
4731 setzoom zoom;
4732 Glut.setCursor Glut.CURSOR_INHERIT;
4733 state.mstate <- Mnone;
4736 let scrollx x =
4737 let winw = conf.winw - state.scrollw - 1 in
4738 let s = float x /. float winw in
4739 let destx = truncate (float (state.w + winw) *. s) in
4740 state.x <- winw - destx;
4741 gotoy_and_clear_text state.y;
4742 state.mstate <- Mscrollx;
4745 let scrolly y =
4746 let s = float y /. float conf.winh in
4747 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4748 gotoy_and_clear_text desty;
4749 state.mstate <- Mscrolly;
4752 let viewmouse button bstate x y =
4753 match button with
4754 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4755 if Glut.getModifiers () land Glut.active_ctrl != 0
4756 then (
4757 match state.mstate with
4758 | Mzoom (oldn, i) ->
4759 if oldn = n
4760 then (
4761 if i = 2
4762 then
4763 let incr =
4764 match n with
4765 | 4 ->
4766 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4767 | _ ->
4768 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4770 let zoom = conf.zoom -. incr in
4771 setzoom zoom;
4772 state.mstate <- Mzoom (n, 0);
4773 else
4774 state.mstate <- Mzoom (n, i+1);
4776 else state.mstate <- Mzoom (n, 0)
4778 | _ -> state.mstate <- Mzoom (n, 0)
4780 else (
4781 match state.autoscroll with
4782 | Some step -> setautoscrollspeed step (n=4)
4783 | None ->
4784 let incr =
4785 if n = 3
4786 then -conf.scrollstep
4787 else conf.scrollstep
4789 let incr = incr * 2 in
4790 let y = clamp incr in
4791 gotoy_and_clear_text y
4794 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4795 if bstate = Glut.DOWN
4796 then (
4797 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4798 state.mstate <- Mpan (x, y)
4800 else
4801 state.mstate <- Mnone
4803 | Glut.RIGHT_BUTTON ->
4804 if bstate = Glut.DOWN
4805 then (
4806 Glut.setCursor Glut.CURSOR_CYCLE;
4807 let p = (x, y) in
4808 state.mstate <- Mzoomrect (p, p)
4810 else (
4811 match state.mstate with
4812 | Mzoomrect ((x0, y0), _) ->
4813 if abs (x-x0) > 10 && abs (y - y0) > 10
4814 then zoomrect x0 y0 x y
4815 else (
4816 state.mstate <- Mnone;
4817 Glut.setCursor Glut.CURSOR_INHERIT;
4818 G.postRedisplay "kill accidental zoom rect";
4820 | _ ->
4821 Glut.setCursor Glut.CURSOR_INHERIT;
4822 state.mstate <- Mnone
4825 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4826 if bstate = Glut.DOWN
4827 then
4828 let _, position, sh = state.uioh#scrollph in
4829 if y > truncate position && y < truncate (position +. sh)
4830 then state.mstate <- Mscrolly
4831 else scrolly y
4832 else
4833 state.mstate <- Mnone
4835 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4836 if bstate = Glut.DOWN
4837 then
4838 let _, position, sw = state.uioh#scrollpw in
4839 if x > truncate position && x < truncate (position +. sw)
4840 then state.mstate <- Mscrollx
4841 else scrollx x
4842 else
4843 state.mstate <- Mnone
4845 | Glut.LEFT_BUTTON ->
4846 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4847 begin match dest with
4848 | Ulinkgoto (pageno, top) ->
4849 if pageno >= 0
4850 then (
4851 addnav ();
4852 gotopage1 pageno top;
4855 | Ulinkuri s ->
4856 gotouri s
4858 | Unone when bstate = Glut.DOWN ->
4859 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4860 state.mstate <- Mpan (x, y);
4862 | Unone | Utext _ ->
4863 if bstate = Glut.DOWN
4864 then (
4865 if conf.angle mod 360 = 0
4866 then (
4867 state.mstate <- Msel ((x, y), (x, y));
4868 G.postRedisplay "mouse select";
4871 else (
4872 match state.mstate with
4873 | Mnone -> ()
4875 | Mzoom _ | Mscrollx | Mscrolly ->
4876 state.mstate <- Mnone
4878 | Mzoomrect ((x0, y0), _) ->
4879 zoomrect x0 y0 x y
4881 | Mpan _ ->
4882 Glut.setCursor Glut.CURSOR_INHERIT;
4883 state.mstate <- Mnone
4885 | Msel ((_, y0), (_, y1)) ->
4886 let f l =
4887 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4888 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4889 then
4890 match getopaque l.pageno with
4891 | Some opaque ->
4892 copysel opaque
4893 | _ -> ()
4895 List.iter f state.layout;
4896 copysel ""; (* ugly *)
4897 Glut.setCursor Glut.CURSOR_INHERIT;
4898 state.mstate <- Mnone;
4902 | _ -> ()
4905 let birdseyemouse button bstate x y
4906 (conf, leftx, _, hooverpageno, anchor) =
4907 match button with
4908 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4909 let rec loop = function
4910 | [] -> ()
4911 | l :: rest ->
4912 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4913 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4914 then (
4915 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4917 else loop rest
4919 loop state.layout
4920 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
4921 | _ -> ()
4924 let mouse bstate button x y =
4925 state.uioh <- state.uioh#button button bstate x y;
4928 let mouse ~button ~state ~x ~y = mouse state button x y;;
4930 let motion ~x ~y =
4931 state.uioh <- state.uioh#motion x y
4934 let pmotion ~x ~y =
4935 state.uioh <- state.uioh#pmotion x y;
4938 let uioh = object
4939 method display = ()
4941 method key key =
4942 begin match state.mode with
4943 | Textentry textentry -> textentrykeyboard key textentry
4944 | Birdseye birdseye -> birdseyekeyboard key birdseye
4945 | View -> viewkeyboard key
4946 end;
4947 state.uioh
4949 method special key =
4950 begin match state.mode with
4951 | View | (Birdseye _) when key = Glut.KEY_F9 ->
4952 togglebirdseye ()
4954 | Birdseye vals ->
4955 birdseyespecial key vals
4957 | View when key = Glut.KEY_F1 ->
4958 enterhelpmode ()
4960 | View ->
4961 begin match state.autoscroll with
4962 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
4963 setautoscrollspeed step (key = Glut.KEY_DOWN)
4965 | _ ->
4966 let y =
4967 match key with
4968 | Glut.KEY_F3 -> search state.searchpattern true; state.y
4969 | Glut.KEY_UP ->
4970 if Glut.getModifiers () land Glut.active_ctrl != 0
4971 then
4972 if Glut.getModifiers () land Glut.active_shift != 0
4973 then (setzoom state.prevzoom; state.y)
4974 else clamp (-conf.winh/2)
4975 else clamp (-conf.scrollstep)
4976 | Glut.KEY_DOWN ->
4977 if Glut.getModifiers () land Glut.active_ctrl != 0
4978 then
4979 if Glut.getModifiers () land Glut.active_shift != 0
4980 then (setzoom state.prevzoom; state.y)
4981 else clamp (conf.winh/2)
4982 else clamp (conf.scrollstep)
4983 | Glut.KEY_PAGE_UP ->
4984 if Glut.getModifiers () land Glut.active_ctrl != 0
4985 then
4986 match state.layout with
4987 | [] -> state.y
4988 | l :: _ -> state.y - l.pagey
4989 else
4990 clamp (-conf.winh)
4991 | Glut.KEY_PAGE_DOWN ->
4992 if Glut.getModifiers () land Glut.active_ctrl != 0
4993 then
4994 match List.rev state.layout with
4995 | [] -> state.y
4996 | l :: _ -> getpagey l.pageno
4997 else
4998 clamp conf.winh
4999 | Glut.KEY_HOME ->
5000 addnav ();
5002 | Glut.KEY_END ->
5003 addnav ();
5004 state.maxy - (if conf.maxhfit then conf.winh else 0)
5006 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
5007 Glut.getModifiers () land Glut.active_alt != 0 ->
5008 getnav (if key = Glut.KEY_LEFT then 1 else -1)
5010 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
5011 let dx =
5012 if Glut.getModifiers () land Glut.active_ctrl != 0
5013 then (conf.winw / 2)
5014 else 10
5016 state.x <- state.x - dx;
5017 state.y
5018 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
5019 let dx =
5020 if Glut.getModifiers () land Glut.active_ctrl != 0
5021 then (conf.winw / 2)
5022 else 10
5024 state.x <- state.x + dx;
5025 state.y
5027 | _ -> state.y
5029 if abs (state.y - y) > conf.scrollstep*2
5030 then gotoghyll y
5031 else gotoy_and_clear_text y
5034 | Textentry te -> textentryspecial key te
5035 end;
5036 state.uioh
5038 method button button bstate x y =
5039 begin match state.mode with
5040 | View -> viewmouse button bstate x y
5041 | Birdseye beye -> birdseyemouse button bstate x y beye
5042 | Textentry _ -> ()
5043 end;
5044 state.uioh
5046 method motion x y =
5047 begin match state.mode with
5048 | Textentry _ -> ()
5049 | View | Birdseye _ ->
5050 match state.mstate with
5051 | Mzoom _ | Mnone -> ()
5053 | Mpan (x0, y0) ->
5054 let dx = x - x0
5055 and dy = y0 - y in
5056 state.mstate <- Mpan (x, y);
5057 if conf.zoom > 1.0 then state.x <- state.x + dx;
5058 let y = clamp dy in
5059 gotoy_and_clear_text y
5061 | Msel (a, _) ->
5062 state.mstate <- Msel (a, (x, y));
5063 G.postRedisplay "motion select";
5065 | Mscrolly ->
5066 let y = min conf.winh (max 0 y) in
5067 scrolly y
5069 | Mscrollx ->
5070 let x = min conf.winw (max 0 x) in
5071 scrollx x
5073 | Mzoomrect (p0, _) ->
5074 state.mstate <- Mzoomrect (p0, (x, y));
5075 G.postRedisplay "motion zoomrect";
5076 end;
5077 state.uioh
5079 method pmotion x y =
5080 begin match state.mode with
5081 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5082 let rec loop = function
5083 | [] ->
5084 if hooverpageno != -1
5085 then (
5086 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5087 G.postRedisplay "pmotion birdseye no hoover";
5089 | l :: rest ->
5090 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5091 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5092 then (
5093 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5094 G.postRedisplay "pmotion birdseye hoover";
5096 else loop rest
5098 loop state.layout
5100 | Textentry _ -> ()
5102 | View ->
5103 match state.mstate with
5104 | Mnone ->
5105 begin match getunder x y with
5106 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
5107 | Ulinkuri uri ->
5108 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
5109 Glut.setCursor Glut.CURSOR_INFO
5110 | Ulinkgoto (page, _) ->
5111 if conf.underinfo
5112 then showtext 'p' ("age: " ^ string_of_int (page+1));
5113 Glut.setCursor Glut.CURSOR_INFO
5114 | Utext s ->
5115 if conf.underinfo then showtext 'f' ("ont: " ^ s);
5116 Glut.setCursor Glut.CURSOR_TEXT
5119 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5121 end;
5122 state.uioh
5124 method infochanged _ = ()
5126 method scrollph =
5127 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5128 let p, h = scrollph state.y maxy in
5129 state.scrollw, p, h
5131 method scrollpw =
5132 let winw = conf.winw - state.scrollw - 1 in
5133 let fwinw = float winw in
5134 let sw =
5135 let sw = fwinw /. float state.w in
5136 let sw = fwinw *. sw in
5137 max sw (float conf.scrollh)
5139 let position, sw =
5140 let f = state.w+winw in
5141 let r = float (winw-state.x) /. float f in
5142 let p = fwinw *. r in
5143 p-.sw/.2., sw
5145 let sw =
5146 if position +. sw > fwinw
5147 then fwinw -. position
5148 else sw
5150 state.hscrollh, position, sw
5151 end;;
5153 module Config =
5154 struct
5155 open Parser
5157 let fontpath = ref "";;
5158 let wmclasshack = ref false;;
5160 let unent s =
5161 let l = String.length s in
5162 let b = Buffer.create l in
5163 unent b s 0 l;
5164 Buffer.contents b;
5167 let home =
5169 match platform with
5170 | Pwindows | Pmingw -> Sys.getenv "HOMEPATH"
5171 | _ -> Sys.getenv "HOME"
5172 with exn ->
5173 prerr_endline
5174 ("Can not determine home directory location: " ^
5175 Printexc.to_string exn);
5179 let config_of c attrs =
5180 let apply c k v =
5182 match k with
5183 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
5184 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
5185 | "case-insensitive-search" -> { c with icase = bool_of_string v }
5186 | "preload" -> { c with preload = bool_of_string v }
5187 | "page-bias" -> { c with pagebias = int_of_string v }
5188 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
5189 | "auto-scroll-step" ->
5190 { c with autoscrollstep = max 0 (int_of_string v) }
5191 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
5192 | "crop-hack" -> { c with crophack = bool_of_string v }
5193 | "throttle" ->
5194 let mw =
5195 match String.lowercase v with
5196 | "true" -> Some infinity
5197 | "false" -> None
5198 | f -> Some (float_of_string f)
5200 { c with maxwait = mw}
5201 | "highlight-links" -> { c with hlinks = bool_of_string v }
5202 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
5203 | "vertical-margin" ->
5204 { c with interpagespace = max 0 (int_of_string v) }
5205 | "zoom" ->
5206 let zoom = float_of_string v /. 100. in
5207 let zoom = max zoom 0.0 in
5208 { c with zoom = zoom }
5209 | "presentation" -> { c with presentation = bool_of_string v }
5210 | "rotation-angle" -> { c with angle = int_of_string v }
5211 | "width" -> { c with winw = max 20 (int_of_string v) }
5212 | "height" -> { c with winh = max 20 (int_of_string v) }
5213 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
5214 | "proportional-display" -> { c with proportional = bool_of_string v }
5215 | "pixmap-cache-size" ->
5216 { c with memlimit = max 2 (int_of_string_with_suffix v) }
5217 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
5218 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
5219 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
5220 | "persistent-location" -> { c with jumpback = bool_of_string v }
5221 | "background-color" -> { c with bgcolor = color_of_string v }
5222 | "scrollbar-in-presentation" ->
5223 { c with scrollbarinpm = bool_of_string v }
5224 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
5225 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
5226 | "mupdf-store-size" ->
5227 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
5228 | "checkers" -> { c with checkers = bool_of_string v }
5229 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
5230 | "trim-margins" -> { c with trimmargins = bool_of_string v }
5231 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
5232 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
5233 | "uri-launcher" -> { c with urilauncher = unent v }
5234 | "color-space" -> { c with colorspace = colorspace_of_string v }
5235 | "invert-colors" -> { c with invert = bool_of_string v }
5236 | "brightness" -> { c with colorscale = float_of_string v }
5237 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
5238 | "ghyllscroll" ->
5239 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
5240 | "columns" ->
5241 let nab = columns_of_string v in
5242 { c with columns = Some (nab, [||]) }
5243 | "birds-eye-columns" ->
5244 { c with beyecolumns = Some (max (int_of_string v) 2) }
5245 | _ -> c
5246 with exn ->
5247 prerr_endline ("Error processing attribute (`" ^
5248 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
5251 let rec fold c = function
5252 | [] -> c
5253 | (k, v) :: rest ->
5254 let c = apply c k v in
5255 fold c rest
5257 fold c attrs;
5260 let fromstring f pos n v d =
5261 try f v
5262 with exn ->
5263 dolog "Error processing attribute (%S=%S) at %d\n%s"
5264 n v pos (Printexc.to_string exn)
5269 let bookmark_of attrs =
5270 let rec fold title page rely = function
5271 | ("title", v) :: rest -> fold v page rely rest
5272 | ("page", v) :: rest -> fold title v rely rest
5273 | ("rely", v) :: rest -> fold title page v rest
5274 | _ :: rest -> fold title page rely rest
5275 | [] -> title, page, rely
5277 fold "invalid" "0" "0" attrs
5280 let doc_of attrs =
5281 let rec fold path page rely pan = function
5282 | ("path", v) :: rest -> fold v page rely pan rest
5283 | ("page", v) :: rest -> fold path v rely pan rest
5284 | ("rely", v) :: rest -> fold path page v pan rest
5285 | ("pan", v) :: rest -> fold path page rely v rest
5286 | _ :: rest -> fold path page rely pan rest
5287 | [] -> path, page, rely, pan
5289 fold "" "0" "0" "0" attrs
5292 let setconf dst src =
5293 dst.scrollbw <- src.scrollbw;
5294 dst.scrollh <- src.scrollh;
5295 dst.icase <- src.icase;
5296 dst.preload <- src.preload;
5297 dst.pagebias <- src.pagebias;
5298 dst.verbose <- src.verbose;
5299 dst.scrollstep <- src.scrollstep;
5300 dst.maxhfit <- src.maxhfit;
5301 dst.crophack <- src.crophack;
5302 dst.autoscrollstep <- src.autoscrollstep;
5303 dst.maxwait <- src.maxwait;
5304 dst.hlinks <- src.hlinks;
5305 dst.underinfo <- src.underinfo;
5306 dst.interpagespace <- src.interpagespace;
5307 dst.zoom <- src.zoom;
5308 dst.presentation <- src.presentation;
5309 dst.angle <- src.angle;
5310 dst.winw <- src.winw;
5311 dst.winh <- src.winh;
5312 dst.savebmarks <- src.savebmarks;
5313 dst.memlimit <- src.memlimit;
5314 dst.proportional <- src.proportional;
5315 dst.texcount <- src.texcount;
5316 dst.sliceheight <- src.sliceheight;
5317 dst.thumbw <- src.thumbw;
5318 dst.jumpback <- src.jumpback;
5319 dst.bgcolor <- src.bgcolor;
5320 dst.scrollbarinpm <- src.scrollbarinpm;
5321 dst.tilew <- src.tilew;
5322 dst.tileh <- src.tileh;
5323 dst.mustoresize <- src.mustoresize;
5324 dst.checkers <- src.checkers;
5325 dst.aalevel <- src.aalevel;
5326 dst.trimmargins <- src.trimmargins;
5327 dst.trimfuzz <- src.trimfuzz;
5328 dst.urilauncher <- src.urilauncher;
5329 dst.colorspace <- src.colorspace;
5330 dst.invert <- src.invert;
5331 dst.colorscale <- src.colorscale;
5332 dst.redirectstderr <- src.redirectstderr;
5333 dst.ghyllscroll <- src.ghyllscroll;
5334 dst.columns <- src.columns;
5335 dst.beyecolumns <- src.beyecolumns;
5338 let get s =
5339 let h = Hashtbl.create 10 in
5340 let dc = { defconf with angle = defconf.angle } in
5341 let rec toplevel v t spos _ =
5342 match t with
5343 | Vdata | Vcdata | Vend -> v
5344 | Vopen ("llppconfig", _, closed) ->
5345 if closed
5346 then v
5347 else { v with f = llppconfig }
5348 | Vopen _ ->
5349 error "unexpected subelement at top level" s spos
5350 | Vclose _ -> error "unexpected close at top level" s spos
5352 and llppconfig v t spos _ =
5353 match t with
5354 | Vdata | Vcdata -> v
5355 | Vend -> error "unexpected end of input in llppconfig" s spos
5356 | Vopen ("defaults", attrs, closed) ->
5357 let c = config_of dc attrs in
5358 setconf dc c;
5359 if closed
5360 then v
5361 else { v with f = skip "defaults" (fun () -> v) }
5363 | Vopen ("ui-font", attrs, closed) ->
5364 let rec getsize size = function
5365 | [] -> size
5366 | ("size", v) :: rest ->
5367 let size =
5368 fromstring int_of_string spos "size" v fstate.fontsize in
5369 getsize size rest
5370 | l -> getsize size l
5372 fstate.fontsize <- getsize fstate.fontsize attrs;
5373 if closed
5374 then v
5375 else { v with f = uifont (Buffer.create 10) }
5377 | Vopen ("doc", attrs, closed) ->
5378 let pathent, spage, srely, span = doc_of attrs in
5379 let path = unent pathent
5380 and pageno = fromstring int_of_string spos "page" spage 0
5381 and rely = fromstring float_of_string spos "rely" srely 0.0
5382 and pan = fromstring int_of_string spos "pan" span 0 in
5383 let c = config_of dc attrs in
5384 let anchor = (pageno, rely) in
5385 if closed
5386 then (Hashtbl.add h path (c, [], pan, anchor); v)
5387 else { v with f = doc path pan anchor c [] }
5389 | Vopen _ ->
5390 error "unexpected subelement in llppconfig" s spos
5392 | Vclose "llppconfig" -> { v with f = toplevel }
5393 | Vclose _ -> error "unexpected close in llppconfig" s spos
5395 and uifont b v t spos epos =
5396 match t with
5397 | Vdata | Vcdata ->
5398 Buffer.add_substring b s spos (epos - spos);
5400 | Vopen (_, _, _) ->
5401 error "unexpected subelement in ui-font" s spos
5402 | Vclose "ui-font" ->
5403 if String.length !fontpath = 0
5404 then fontpath := Buffer.contents b;
5405 { v with f = llppconfig }
5406 | Vclose _ -> error "unexpected close in ui-font" s spos
5407 | Vend -> error "unexpected end of input in ui-font" s spos
5409 and doc path pan anchor c bookmarks v t spos _ =
5410 match t with
5411 | Vdata | Vcdata -> v
5412 | Vend -> error "unexpected end of input in doc" s spos
5413 | Vopen ("bookmarks", _, closed) ->
5414 if closed
5415 then v
5416 else { v with f = pbookmarks path pan anchor c bookmarks }
5418 | Vopen (_, _, _) ->
5419 error "unexpected subelement in doc" s spos
5421 | Vclose "doc" ->
5422 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5423 { v with f = llppconfig }
5425 | Vclose _ -> error "unexpected close in doc" s spos
5427 and pbookmarks path pan anchor c bookmarks v t spos _ =
5428 match t with
5429 | Vdata | Vcdata -> v
5430 | Vend -> error "unexpected end of input in bookmarks" s spos
5431 | Vopen ("item", attrs, closed) ->
5432 let titleent, spage, srely = bookmark_of attrs in
5433 let page = fromstring int_of_string spos "page" spage 0
5434 and rely = fromstring float_of_string spos "rely" srely 0.0 in
5435 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
5436 if closed
5437 then { v with f = pbookmarks path pan anchor c bookmarks }
5438 else
5439 let f () = v in
5440 { v with f = skip "item" f }
5442 | Vopen _ ->
5443 error "unexpected subelement in bookmarks" s spos
5445 | Vclose "bookmarks" ->
5446 { v with f = doc path pan anchor c bookmarks }
5448 | Vclose _ -> error "unexpected close in bookmarks" s spos
5450 and skip tag f v t spos _ =
5451 match t with
5452 | Vdata | Vcdata -> v
5453 | Vend ->
5454 error ("unexpected end of input in skipped " ^ tag) s spos
5455 | Vopen (tag', _, closed) ->
5456 if closed
5457 then v
5458 else
5459 let f' () = { v with f = skip tag f } in
5460 { v with f = skip tag' f' }
5461 | Vclose ctag ->
5462 if tag = ctag
5463 then f ()
5464 else error ("unexpected close in skipped " ^ tag) s spos
5467 parse { f = toplevel; accu = () } s;
5468 h, dc;
5471 let do_load f ic =
5473 let len = in_channel_length ic in
5474 let s = String.create len in
5475 really_input ic s 0 len;
5476 f s;
5477 with
5478 | Parse_error (msg, s, pos) ->
5479 let subs = subs s pos in
5480 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
5481 failwith ("parse error: " ^ s)
5483 | exn ->
5484 failwith ("config load error: " ^ Printexc.to_string exn)
5487 let defconfpath =
5488 let dir =
5490 let dir = Filename.concat home ".config" in
5491 if Sys.is_directory dir then dir else home
5492 with _ -> home
5494 Filename.concat dir "llpp.conf"
5497 let confpath = ref defconfpath;;
5499 let load1 f =
5500 if Sys.file_exists !confpath
5501 then
5502 match
5503 (try Some (open_in_bin !confpath)
5504 with exn ->
5505 prerr_endline
5506 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5507 Printexc.to_string exn);
5508 None
5510 with
5511 | Some ic ->
5512 begin try
5513 f (do_load get ic)
5514 with exn ->
5515 prerr_endline
5516 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5517 Printexc.to_string exn);
5518 end;
5519 close_in ic;
5521 | None -> ()
5522 else
5523 f (Hashtbl.create 0, defconf)
5526 let load () =
5527 let f (h, dc) =
5528 let pc, pb, px, pa =
5530 Hashtbl.find h (Filename.basename state.path)
5531 with Not_found -> dc, [], 0, (0, 0.0)
5533 setconf defconf dc;
5534 setconf conf pc;
5535 state.bookmarks <- pb;
5536 state.x <- px;
5537 state.scrollw <- conf.scrollbw;
5538 if conf.jumpback
5539 then state.anchor <- pa;
5540 cbput state.hists.nav pa;
5542 load1 f
5545 let add_attrs bb always dc c =
5546 let ob s a b =
5547 if always || a != b
5548 then Printf.bprintf bb "\n %s='%b'" s a
5549 and oi s a b =
5550 if always || a != b
5551 then Printf.bprintf bb "\n %s='%d'" s a
5552 and oI s a b =
5553 if always || a != b
5554 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5555 and oz s a b =
5556 if always || a <> b
5557 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5558 and oF s a b =
5559 if always || a <> b
5560 then Printf.bprintf bb "\n %s='%f'" s a
5561 and oc s a b =
5562 if always || a <> b
5563 then
5564 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5565 and oC s a b =
5566 if always || a <> b
5567 then
5568 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5569 and oR s a b =
5570 if always || a <> b
5571 then
5572 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5573 and os s a b =
5574 if always || a <> b
5575 then
5576 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5577 and og s a b =
5578 if always || a <> b
5579 then
5580 match a with
5581 | None -> ()
5582 | Some (_N, _A, _B) ->
5583 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
5584 and oW s a b =
5585 if always || a <> b
5586 then
5587 let v =
5588 match a with
5589 | None -> "false"
5590 | Some f ->
5591 if f = infinity
5592 then "true"
5593 else string_of_float f
5595 Printf.bprintf bb "\n %s='%s'" s v
5596 and oco s a b =
5597 if always || a <> b
5598 then
5599 match a with
5600 | Some ((n, a, b), _) when n > 1 ->
5601 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
5602 | _ -> ()
5603 and obeco s a b =
5604 if always || a <> b
5605 then
5606 match a with
5607 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
5608 | _ -> ()
5610 let w, h =
5611 if always
5612 then dc.winw, dc.winh
5613 else
5614 match state.fullscreen with
5615 | Some wh -> wh
5616 | None -> c.winw, c.winh
5618 let zoom, presentation, interpagespace, maxwait =
5619 if always
5620 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
5621 else
5622 match state.mode with
5623 | Birdseye (bc, _, _, _, _) ->
5624 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
5625 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
5627 oi "width" w dc.winw;
5628 oi "height" h dc.winh;
5629 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
5630 oi "scroll-handle-height" c.scrollh dc.scrollh;
5631 ob "case-insensitive-search" c.icase dc.icase;
5632 ob "preload" c.preload dc.preload;
5633 oi "page-bias" c.pagebias dc.pagebias;
5634 oi "scroll-step" c.scrollstep dc.scrollstep;
5635 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
5636 ob "max-height-fit" c.maxhfit dc.maxhfit;
5637 ob "crop-hack" c.crophack dc.crophack;
5638 oW "throttle" maxwait dc.maxwait;
5639 ob "highlight-links" c.hlinks dc.hlinks;
5640 ob "under-cursor-info" c.underinfo dc.underinfo;
5641 oi "vertical-margin" interpagespace dc.interpagespace;
5642 oz "zoom" zoom dc.zoom;
5643 ob "presentation" presentation dc.presentation;
5644 oi "rotation-angle" c.angle dc.angle;
5645 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
5646 ob "proportional-display" c.proportional dc.proportional;
5647 oI "pixmap-cache-size" c.memlimit dc.memlimit;
5648 oi "tex-count" c.texcount dc.texcount;
5649 oi "slice-height" c.sliceheight dc.sliceheight;
5650 oi "thumbnail-width" c.thumbw dc.thumbw;
5651 ob "persistent-location" c.jumpback dc.jumpback;
5652 oc "background-color" c.bgcolor dc.bgcolor;
5653 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
5654 oi "tile-width" c.tilew dc.tilew;
5655 oi "tile-height" c.tileh dc.tileh;
5656 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
5657 ob "checkers" c.checkers dc.checkers;
5658 oi "aalevel" c.aalevel dc.aalevel;
5659 ob "trim-margins" c.trimmargins dc.trimmargins;
5660 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
5661 os "uri-launcher" c.urilauncher dc.urilauncher;
5662 oC "color-space" c.colorspace dc.colorspace;
5663 ob "invert-colors" c.invert dc.invert;
5664 oF "brightness" c.colorscale dc.colorscale;
5665 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
5666 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
5667 oco "columns" c.columns dc.columns;
5668 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
5669 if always
5670 then ob "wmclass-hack" !wmclasshack false;
5673 let save () =
5674 let uifontsize = fstate.fontsize in
5675 let bb = Buffer.create 32768 in
5676 let f (h, dc) =
5677 let dc = if conf.bedefault then conf else dc in
5678 Buffer.add_string bb "<llppconfig>\n";
5680 if String.length !fontpath > 0
5681 then
5682 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
5683 uifontsize
5684 !fontpath
5685 else (
5686 if uifontsize <> 14
5687 then
5688 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
5691 Buffer.add_string bb "<defaults ";
5692 add_attrs bb true dc dc;
5693 Buffer.add_string bb "/>\n";
5695 let adddoc path pan anchor c bookmarks =
5696 if bookmarks == [] && c = dc && anchor = emptyanchor
5697 then ()
5698 else (
5699 Printf.bprintf bb "<doc path='%s'"
5700 (enent path 0 (String.length path));
5702 if anchor <> emptyanchor
5703 then (
5704 let n, y = anchor in
5705 Printf.bprintf bb " page='%d'" n;
5706 if y > 1e-6
5707 then
5708 Printf.bprintf bb " rely='%f'" y
5712 if pan != 0
5713 then Printf.bprintf bb " pan='%d'" pan;
5715 add_attrs bb false dc c;
5717 begin match bookmarks with
5718 | [] -> Buffer.add_string bb "/>\n"
5719 | _ ->
5720 Buffer.add_string bb ">\n<bookmarks>\n";
5721 List.iter (fun (title, _level, (page, rely)) ->
5722 Printf.bprintf bb
5723 "<item title='%s' page='%d'"
5724 (enent title 0 (String.length title))
5725 page
5727 if rely > 1e-6
5728 then
5729 Printf.bprintf bb " rely='%f'" rely
5731 Buffer.add_string bb "/>\n";
5732 ) bookmarks;
5733 Buffer.add_string bb "</bookmarks>\n</doc>\n";
5734 end;
5738 let pan, conf =
5739 match state.mode with
5740 | Birdseye (c, pan, _, _, _) ->
5741 let beyecolumns =
5742 match conf.columns with
5743 | Some ((c, _, _), _) -> Some c
5744 | None -> None
5745 and columns =
5746 match c.columns with
5747 | Some (c, _) -> Some (c, [||])
5748 | None -> None
5750 pan, { c with beyecolumns = beyecolumns; columns = columns }
5751 | _ -> state.x, conf
5753 let basename = Filename.basename state.path in
5754 adddoc basename pan (getanchor ())
5755 { conf with
5756 autoscrollstep =
5757 match state.autoscroll with
5758 | Some step -> step
5759 | None -> conf.autoscrollstep }
5760 (if conf.savebmarks then state.bookmarks else []);
5762 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
5763 if basename <> path
5764 then adddoc path x y c bookmarks
5765 ) h;
5766 Buffer.add_string bb "</llppconfig>";
5768 load1 f;
5769 if Buffer.length bb > 0
5770 then
5772 let tmp = !confpath ^ ".tmp" in
5773 let oc = open_out_bin tmp in
5774 Buffer.output_buffer oc bb;
5775 close_out oc;
5776 Unix.rename tmp !confpath;
5777 with exn ->
5778 prerr_endline
5779 ("error while saving configuration: " ^ Printexc.to_string exn)
5781 end;;
5783 let () =
5784 Arg.parse
5785 (Arg.align
5786 [("-p", Arg.String (fun s -> state.password <- s) ,
5787 "<password> Set password");
5789 ("-f", Arg.String (fun s -> Config.fontpath := s),
5790 "<path> Set path to the user interface font");
5792 ("-c", Arg.String (fun s -> Config.confpath := s),
5793 "<path> Set path to the configuration file");
5795 ("-v", Arg.Unit (fun () ->
5796 Printf.printf
5797 "%s\nconfiguration path: %s\n"
5798 (version ())
5799 Config.defconfpath
5801 exit 0), " Print version and exit");
5804 (fun s -> state.path <- s)
5805 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
5807 if String.length state.path = 0
5808 then (prerr_endline "file name missing"; exit 1);
5810 Config.load ();
5812 let _ = Glut.init Sys.argv in
5813 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
5814 let () = Glut.initWindowSize conf.winw conf.winh in
5815 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
5817 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
5818 || Glut.extensionSupported "GL_EXT_texture_rectangle")
5819 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
5821 let csock, ssock =
5822 if not is_windows
5823 then
5824 Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0
5825 else
5826 let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, 1337) in
5827 let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5828 Unix.setsockopt sock Unix.SO_REUSEADDR true;
5829 Unix.bind sock addr;
5830 Unix.listen sock 1;
5831 let csock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5832 Unix.connect csock addr;
5833 let ssock, _ = Unix.accept sock in
5834 Unix.close sock;
5835 let opts sock =
5836 Unix.setsockopt sock Unix.TCP_NODELAY true;
5837 Unix.setsockopt_optint sock Unix.SO_LINGER None;
5839 opts ssock;
5840 opts csock;
5841 ssock, csock
5844 let () = Glut.displayFunc display in
5845 let () = Glut.reshapeFunc reshape in
5846 let () = Glut.keyboardFunc keyboard in
5847 let () = Glut.specialFunc special in
5848 let () = Glut.idleFunc (Some idle) in
5849 let () = Glut.mouseFunc mouse in
5850 let () = Glut.motionFunc motion in
5851 let () = Glut.passiveMotionFunc pmotion in
5853 setcheckers conf.checkers;
5854 init ssock (
5855 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5856 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5857 !Config.wmclasshack, !Config.fontpath
5859 state.csock <- csock;
5860 state.ssock <- ssock;
5861 state.text <- "Opening " ^ state.path;
5862 setaalevel conf.aalevel;
5863 writeopen state.path state.password;
5864 state.uioh <- uioh;
5865 setfontsize fstate.fontsize;
5867 redirectstderr ();
5869 while true do
5871 Glut.mainLoop ();
5872 with
5873 | Glut.BadEnum "key in special_of_int" ->
5874 showtext '!' " LablGlut bug: special key not recognized";
5876 | Quit ->
5877 wcmd "quit" [];
5878 Config.save ();
5879 exit 0
5881 | exn when conf.redirectstderr ->
5882 let s =
5883 Printf.sprintf "exception %s\n%s"
5884 (Printexc.to_string exn)
5885 (Printexc.get_backtrace ())
5887 ignore (try
5888 Unix.single_write state.stderr s 0 (String.length s);
5889 with _ -> 0);
5890 exit 1
5891 done;