What can i say...
[llpp.git] / main.ml
blobddd9439d5b43b12b2bee56d23f155658e4424588
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;;
10 exception Quit;;
12 type params = (angle * proportional * trimparams
13 * texcount * sliceheight * memsize
14 * colorspace * wmclasshack * fontpath)
15 and pageno = int
16 and width = int
17 and height = int
18 and leftx = int
19 and opaque = string
20 and recttype = int
21 and pixmapsize = int
22 and angle = int
23 and proportional = bool
24 and trimmargins = bool
25 and interpagespace = int
26 and texcount = int
27 and sliceheight = int
28 and gen = int
29 and top = float
30 and fontpath = string
31 and memsize = int
32 and aalevel = int
33 and wmclasshack = bool
34 and irect = (int * int * int * int)
35 and trimparams = (trimmargins * irect)
36 and colorspace = | Rgb | Bgr | Gray
39 type platform = | Punknown | Plinux | Pwindows | Posx | Psun
40 | Pfreebsd | Pdragonflybsd | Popenbsd | Pmingw | Pcygwin;;
42 external init : Unix.file_descr -> params -> unit = "ml_init";;
43 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
44 external copysel : string -> unit = "ml_copysel";;
45 external getpdimrect : int -> float array = "ml_getpdimrect";;
46 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
47 external zoomforh : int -> int -> int -> float = "ml_zoom_for_height";;
48 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
49 external measurestr : int -> string -> float = "ml_measure_string";;
50 external getmaxw : unit -> float = "ml_getmaxw";;
51 external postprocess : opaque -> bool -> int -> int -> unit = "ml_postprocess";;
52 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
53 external platform : unit -> platform = "ml_platform";;
54 external setaalevel : int -> unit = "ml_setaalevel";;
56 let platform_to_string = function
57 | Punknown -> "unknown"
58 | Plinux -> "Linux"
59 | Pwindows -> "Windows"
60 | Posx -> "OSX"
61 | Psun -> "Sun"
62 | Pfreebsd -> "FreeBSD"
63 | Pdragonflybsd -> "DragonflyBSD"
64 | Popenbsd -> "OpenBSD"
65 | Pcygwin -> "Cygwin"
66 | Pmingw -> "MingW"
69 let platform = platform ();;
71 let is_windows =
72 match platform with
73 | Pwindows | Pmingw -> true
74 | _ -> false
77 type x = int
78 and y = int
79 and tilex = int
80 and tiley = int
81 and tileparams = (x * y * width * height * tilex * tiley)
84 external drawtile : tileparams -> string -> unit = "ml_drawtile";;
86 type mpos = int * int
87 and mstate =
88 | Msel of (mpos * mpos)
89 | Mpan of mpos
90 | Mscrolly | Mscrollx
91 | Mzoom of (int * int)
92 | Mzoomrect of (mpos * mpos)
93 | Mnone
96 type textentry = string * string * onhist option * onkey * ondone
97 and onkey = string -> int -> te
98 and ondone = string -> unit
99 and histcancel = unit -> unit
100 and onhist = ((histcmd -> string) * histcancel)
101 and histcmd = HCnext | HCprev | HCfirst | HClast
102 and te =
103 | TEstop
104 | TEdone of string
105 | TEcont of string
106 | TEswitch of textentry
109 type 'a circbuf =
110 { store : 'a array
111 ; mutable rc : int
112 ; mutable wc : int
113 ; mutable len : int
117 let bound v minv maxv =
118 max minv (min maxv v);
121 let cbnew n v =
122 { store = Array.create n v
123 ; rc = 0
124 ; wc = 0
125 ; len = 0
129 let drawstring size x y s =
130 Gl.enable `blend;
131 Gl.enable `texture_2d;
132 ignore (drawstr size x y s);
133 Gl.disable `blend;
134 Gl.disable `texture_2d;
137 let drawstring1 size x y s =
138 drawstr size x y s;
141 let drawstring2 size x y fmt =
142 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
145 let cbcap b = Array.length b.store;;
147 let cbput b v =
148 let cap = cbcap b in
149 b.store.(b.wc) <- v;
150 b.wc <- (b.wc + 1) mod cap;
151 b.rc <- b.wc;
152 b.len <- min (b.len + 1) cap;
155 let cbempty b = b.len = 0;;
157 let cbgetg b circular dir =
158 if cbempty b
159 then b.store.(0)
160 else
161 let rc = b.rc + dir in
162 let rc =
163 if circular
164 then (
165 if rc = -1
166 then b.len-1
167 else (
168 if rc = b.len
169 then 0
170 else rc
173 else max 0 (min rc (b.len-1))
175 b.rc <- rc;
176 b.store.(rc);
179 let cbget b = cbgetg b false;;
180 let cbgetc b = cbgetg b true;;
182 type page =
183 { pageno : int
184 ; pagedimno : int
185 ; pagew : int
186 ; pageh : int
187 ; pagex : int
188 ; pagey : int
189 ; pagevw : int
190 ; pagevh : int
191 ; pagedispx : int
192 ; pagedispy : int
196 let debugl l =
197 dolog "l %d dim=%d {" l.pageno l.pagedimno;
198 dolog " WxH %dx%d" l.pagew l.pageh;
199 dolog " vWxH %dx%d" l.pagevw l.pagevh;
200 dolog " pagex,y %d,%d" l.pagex l.pagey;
201 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
202 dolog "}";
205 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
206 dolog "rect {";
207 dolog " x0,y0=(% f, % f)" x0 y0;
208 dolog " x1,y1=(% f, % f)" x1 y1;
209 dolog " x2,y2=(% f, % f)" x2 y2;
210 dolog " x3,y3=(% f, % f)" x3 y3;
211 dolog "}";
214 type conf =
215 { mutable scrollbw : int
216 ; mutable scrollh : int
217 ; mutable icase : bool
218 ; mutable preload : bool
219 ; mutable pagebias : int
220 ; mutable verbose : bool
221 ; mutable debug : bool
222 ; mutable scrollstep : int
223 ; mutable maxhfit : bool
224 ; mutable crophack : bool
225 ; mutable autoscrollstep : int
226 ; mutable showall : bool
227 ; mutable hlinks : bool
228 ; mutable underinfo : bool
229 ; mutable interpagespace : interpagespace
230 ; mutable zoom : float
231 ; mutable presentation : bool
232 ; mutable angle : angle
233 ; mutable winw : int
234 ; mutable winh : int
235 ; mutable savebmarks : bool
236 ; mutable proportional : proportional
237 ; mutable trimmargins : trimmargins
238 ; mutable trimfuzz : irect
239 ; mutable memlimit : memsize
240 ; mutable texcount : texcount
241 ; mutable sliceheight : sliceheight
242 ; mutable thumbw : width
243 ; mutable jumpback : bool
244 ; mutable bgcolor : float * float * float
245 ; mutable bedefault : bool
246 ; mutable scrollbarinpm : bool
247 ; mutable tilew : int
248 ; mutable tileh : int
249 ; mutable mumemlimit : memsize
250 ; mutable checkers : bool
251 ; mutable aalevel : int
252 ; mutable urilauncher : string
253 ; mutable colorspace : colorspace
254 ; mutable invert : bool
258 type anchor = pageno * top;;
260 type outline = string * int * anchor;;
262 type rect = float * float * float * float * float * float * float * float;;
264 type tile = opaque * pixmapsize * elapsed
265 and elapsed = float;;
266 type pagemapkey = pageno * gen;;
267 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
268 and row = int
269 and col = int;;
271 let emptyanchor = (0, 0.0);;
273 class type uioh = object
274 method display : unit
275 method key : int -> uioh
276 method special : Glut.special_key_t -> uioh
277 method button :
278 Glut.button_t -> Glut.mouse_button_state_t -> int -> int -> uioh
279 method motion : int -> int -> uioh
280 method pmotion : int -> int -> uioh
281 end;;
283 type mode =
284 | Birdseye of (conf * leftx * pageno * pageno * anchor)
285 | Textentry of (textentry * onleave)
286 | View
287 and onleave = leavetextentrystatus -> unit
288 and leavetextentrystatus = | Cancel | Confirm
289 and helpitem = string * int * action
290 and action =
291 | Noaction
292 | Action of (uioh -> uioh)
295 let isbirdseye = function Birdseye _ -> true | _ -> false;;
296 let istextentry = function Textentry _ -> true | _ -> false;;
298 type currently =
299 | Idle
300 | Loading of (page * gen)
301 | Tiling of (
302 page * opaque * colorspace * angle * gen * col * row * width * height
304 | Outlining of outline list
307 let nouioh : uioh = object (self)
308 method display = ()
309 method key _ = self
310 method special _ = self
311 method button _ _ _ _ = self
312 method motion _ _ = self
313 method pmotion _ _ = self
314 end;;
316 type state =
317 { mutable csock : Unix.file_descr
318 ; mutable ssock : Unix.file_descr
319 ; mutable w : int
320 ; mutable x : int
321 ; mutable y : int
322 ; mutable scrollw : int
323 ; mutable hscrollh : int
324 ; mutable anchor : anchor
325 ; mutable maxy : int
326 ; mutable layout : page list
327 ; pagemap : (pagemapkey, opaque) Hashtbl.t
328 ; tilemap : (tilemapkey, tile) Hashtbl.t
329 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
330 ; mutable pdims : (pageno * width * height * leftx) list
331 ; mutable pagecount : int
332 ; mutable currently : currently
333 ; mutable mstate : mstate
334 ; mutable searchpattern : string
335 ; mutable rects : (pageno * recttype * rect) list
336 ; mutable rects1 : (pageno * recttype * rect) list
337 ; mutable text : string
338 ; mutable fullscreen : (width * height) option
339 ; mutable mode : mode
340 ; mutable uioh : uioh
341 ; mutable outlines : outline array
342 ; mutable bookmarks : outline list
343 ; mutable path : string
344 ; mutable password : string
345 ; mutable invalidated : int
346 ; mutable colorscale : float
347 ; mutable memused : memsize
348 ; mutable gen : gen
349 ; mutable throttle : (page list * int) option
350 ; mutable autoscroll : int option
351 ; mutable help : helpitem array
352 ; mutable docinfo : (int * string) list
353 ; mutable deadline : float
354 ; mutable texid : GlTex.texture_id option
355 ; hists : hists
356 ; mutable prevzoom : float
357 ; mutable progress : float
359 and hists =
360 { pat : string circbuf
361 ; pag : string circbuf
362 ; nav : anchor circbuf
366 let defconf =
367 { scrollbw = 7
368 ; scrollh = 12
369 ; icase = true
370 ; preload = true
371 ; pagebias = 0
372 ; verbose = false
373 ; debug = false
374 ; scrollstep = 24
375 ; maxhfit = true
376 ; crophack = false
377 ; autoscrollstep = 2
378 ; showall = false
379 ; hlinks = false
380 ; underinfo = false
381 ; interpagespace = 2
382 ; zoom = 1.0
383 ; presentation = false
384 ; angle = 0
385 ; winw = 900
386 ; winh = 900
387 ; savebmarks = true
388 ; proportional = true
389 ; trimmargins = false
390 ; trimfuzz = (0,0,0,0)
391 ; memlimit = 32 lsl 20
392 ; texcount = 256
393 ; sliceheight = 24
394 ; thumbw = 76
395 ; jumpback = true
396 ; bgcolor = (0.5, 0.5, 0.5)
397 ; bedefault = false
398 ; scrollbarinpm = true
399 ; tilew = 2048
400 ; tileh = 2048
401 ; mumemlimit = 128 lsl 20
402 ; checkers = true
403 ; aalevel = 8
404 ; urilauncher =
405 (match platform with
406 | Plinux | Pfreebsd | Pdragonflybsd | Popenbsd | Psun -> "xdg-open \"%s\""
407 | Posx -> "open \"%s\""
408 | Pwindows | Pcygwin | Pmingw -> "iexplore \"%s\""
409 | _ -> "")
410 ; colorspace = Rgb
411 ; invert = false
415 let conf = { defconf with angle = defconf.angle };;
417 let uifontsize = ref 14;;
419 let gotouri uri =
420 if String.length conf.urilauncher = 0
421 then print_endline uri
422 else
423 let re = Str.regexp "%s" in
424 let command = Str.global_replace re uri conf.urilauncher in
425 let optic =
426 try Some (Unix.open_process_in command)
427 with exn ->
428 Printf.eprintf
429 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
430 flush stderr;
431 None
433 match optic with
434 | Some ic -> close_in ic
435 | None -> ()
438 let makehelp () =
439 let strings = ("llpp version " ^ Help.version) :: "" :: Help.keys in
440 Array.of_list (
441 let r = Str.regexp "\\(http://[^ ]+\\)" in
442 List.map (fun s ->
443 if (try Str.search_forward r s 0 with Not_found -> -1) >= 0
444 then
445 let uri = Str.matched_string s in
446 (s, 0, Action (fun u -> gotouri uri; u))
447 else s, 0, Noaction) strings
451 let state =
452 { csock = Unix.stdin
453 ; ssock = Unix.stdin
454 ; x = 0
455 ; y = 0
456 ; w = 0
457 ; scrollw = 0
458 ; hscrollh = 0
459 ; anchor = emptyanchor
460 ; layout = []
461 ; maxy = max_int
462 ; tilelru = Queue.create ()
463 ; pagemap = Hashtbl.create 10
464 ; tilemap = Hashtbl.create 10
465 ; pdims = []
466 ; pagecount = 0
467 ; currently = Idle
468 ; mstate = Mnone
469 ; rects = []
470 ; rects1 = []
471 ; text = ""
472 ; mode = View
473 ; fullscreen = None
474 ; searchpattern = ""
475 ; outlines = [||]
476 ; bookmarks = []
477 ; path = ""
478 ; password = ""
479 ; invalidated = 0
480 ; hists =
481 { nav = cbnew 10 (0, 0.0)
482 ; pat = cbnew 1 ""
483 ; pag = cbnew 1 ""
485 ; colorscale = 1.0
486 ; memused = 0
487 ; gen = 0
488 ; throttle = None
489 ; autoscroll = None
490 ; help = makehelp ()
491 ; docinfo = []
492 ; deadline = nan
493 ; texid = None
494 ; prevzoom = 1.0
495 ; progress = -1.0
496 ; uioh = nouioh
500 let vlog fmt =
501 if conf.verbose
502 then
503 Printf.kprintf prerr_endline fmt
504 else
505 Printf.kprintf ignore fmt
508 module G =
509 struct
510 let postRedisplay who =
511 vlog "redisplay for %s" who;
512 Glut.postRedisplay ();
514 end;;
516 let addchar s c =
517 let b = Buffer.create (String.length s + 1) in
518 Buffer.add_string b s;
519 Buffer.add_char b c;
520 Buffer.contents b;
523 let colorspace_of_string s =
524 match String.lowercase s with
525 | "rgb" -> Rgb
526 | "bgr" -> Bgr
527 | "gray" -> Gray
528 | _ -> failwith "invalid colorspace"
531 let int_of_colorspace = function
532 | Rgb -> 0
533 | Bgr -> 1
534 | Gray -> 2
537 let colorspace_of_int = function
538 | 0 -> Rgb
539 | 1 -> Bgr
540 | 2 -> Gray
541 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
544 let colorspace_to_string = function
545 | Rgb -> "rgb"
546 | Bgr -> "bgr"
547 | Gray -> "gray"
550 let intentry_with_suffix text key =
551 let c = Char.unsafe_chr key in
552 match Char.lowercase c with
553 | '0' .. '9' ->
554 let text = addchar text c in
555 TEcont text
557 | 'k' | 'm' | 'g' ->
558 let text = addchar text c in
559 TEcont text
561 | _ ->
562 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
563 TEcont text
566 let writecmd fd s =
567 let len = String.length s in
568 let n = 4 + len in
569 let b = Buffer.create n in
570 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
571 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
572 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
573 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
574 Buffer.add_string b s;
575 let s' = Buffer.contents b in
576 let n' = Unix.write fd s' 0 n in
577 if n' != n then failwith "write failed";
580 let readcmd fd =
581 let s = "xxxx" in
582 let n = Unix.read fd s 0 4 in
583 if n != 4 then failwith "incomplete read(len)";
584 let len = 0
585 lor (Char.code s.[0] lsl 24)
586 lor (Char.code s.[1] lsl 16)
587 lor (Char.code s.[2] lsl 8)
588 lor (Char.code s.[3] lsl 0)
590 let s = String.create len in
591 let n = Unix.read fd s 0 len in
592 if n != len then failwith "incomplete read(data)";
596 let makecmd s l =
597 let b = Buffer.create 10 in
598 Buffer.add_string b s;
599 let rec combine = function
600 | [] -> b
601 | x :: xs ->
602 Buffer.add_char b ' ';
603 let s =
604 match x with
605 | `b b -> if b then "1" else "0"
606 | `s s -> s
607 | `i i -> string_of_int i
608 | `f f -> string_of_float f
609 | `I f -> string_of_int (truncate f)
611 Buffer.add_string b s;
612 combine xs;
614 combine l;
617 let wcmd s l =
618 let cmd = Buffer.contents (makecmd s l) in
619 writecmd state.csock cmd;
622 let calcips h =
623 if conf.presentation
624 then
625 let d = conf.winh - h in
626 max 0 ((d + 1) / 2)
627 else
628 conf.interpagespace
631 let calcheight () =
632 let rec f pn ph pi fh l =
633 match l with
634 | (n, _, h, _) :: rest ->
635 let ips = calcips h in
636 let fh =
637 if conf.presentation
638 then fh+ips
639 else (
640 if isbirdseye state.mode && pn = 0
641 then fh + ips
642 else fh
645 let fh = fh + ((n - pn) * (ph + pi)) in
646 f n h ips fh rest;
648 | [] ->
649 let inc =
650 if conf.presentation || (isbirdseye state.mode && pn = 0)
651 then 0
652 else -pi
654 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
655 max 0 fh
657 let fh = f 0 0 0 0 state.pdims in
661 let getpageyh pageno =
662 let rec f pn ph pi y l =
663 match l with
664 | (n, _, h, _) :: rest ->
665 let ips = calcips h in
666 if n >= pageno
667 then
668 let h = if n = pageno then h else ph in
669 if conf.presentation && n = pageno
670 then
671 y + (pageno - pn) * (ph + pi) + pi, h
672 else
673 y + (pageno - pn) * (ph + pi), h
674 else
675 let y = y + (if conf.presentation then pi else 0) in
676 let y = y + (n - pn) * (ph + pi) in
677 f n h ips y rest
679 | [] ->
680 y + (pageno - pn) * (ph + pi), ph
682 f 0 0 0 0 state.pdims
685 let getpagedim pageno =
686 let rec f ppdim l =
687 match l with
688 | (n, _, _, _) as pdim :: rest ->
689 if n >= pageno
690 then (if n = pageno then pdim else ppdim)
691 else f pdim rest
693 | [] -> ppdim
695 f (-1, -1, -1, -1) state.pdims
698 let getpageh pageno =
699 let _, _, h, _ = getpagedim pageno in
703 let getpagew pageno =
704 let _, w, _, _ = getpagedim pageno in
708 let getpagey pageno = fst (getpageyh pageno);;
710 let layout y sh =
711 let sh = sh - state.hscrollh in
712 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
713 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
714 match pdims with
715 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
716 let ips = calcips h in
717 let yinc =
718 if conf.presentation || (isbirdseye state.mode && pageno = 0)
719 then ips
720 else 0
722 (w, h, ips, xoff), rest, pdimno + 1, yinc
723 | _ ->
724 prev, pdims, pdimno, 0
726 let dy = dy + yinc in
727 let py = py + yinc in
728 if pageno = state.pagecount || dy >= sh
729 then
730 accu
731 else
732 let vy = y + dy in
733 if py + h <= vy - yinc
734 then
735 let py = py + h + ips in
736 let dy = max 0 (py - y) in
737 f ~pageno:(pageno+1)
738 ~pdimno
739 ~prev:curr
742 ~pdims:rest
743 ~accu
744 else
745 let pagey = vy - py in
746 let pagevh = h - pagey in
747 let pagevh = min (sh - dy) pagevh in
748 let off = if yinc > 0 then py - vy else 0 in
749 let py = py + h + ips in
750 let pagex, dx =
751 let xoff = xoff +
752 if state.w < conf.winw - state.scrollw
753 then (conf.winw - state.scrollw - state.w) / 2
754 else 0
756 let dispx = xoff + state.x in
757 if dispx < 0
758 then (-dispx, 0)
759 else (0, dispx)
761 let pagevw =
762 let lw = w - pagex in
763 min lw (conf.winw - state.scrollw)
765 let e =
766 { pageno = pageno
767 ; pagedimno = pdimno
768 ; pagew = w
769 ; pageh = h
770 ; pagex = pagex
771 ; pagey = pagey + off
772 ; pagevw = pagevw
773 ; pagevh = pagevh - off
774 ; pagedispx = dx
775 ; pagedispy = dy + off
778 let accu = e :: accu in
779 f ~pageno:(pageno+1)
780 ~pdimno
781 ~prev:curr
783 ~dy:(dy+pagevh+ips)
784 ~pdims:rest
785 ~accu
787 if state.invalidated = 0
788 then (
789 let accu =
791 ~pageno:0
792 ~pdimno:~-1
793 ~prev:(0,0,0,0)
794 ~py:0
795 ~dy:0
796 ~pdims:state.pdims
797 ~accu:[]
799 List.rev accu
801 else
805 let clamp incr =
806 let y = state.y + incr in
807 let y = max 0 y in
808 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
812 let getopaque pageno =
813 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
814 with Not_found -> None
817 let putopaque pageno opaque =
818 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
821 let itertiles l f =
822 let tilex = l.pagex mod conf.tilew in
823 let tiley = l.pagey mod conf.tileh in
825 let col = l.pagex / conf.tilew in
826 let row = l.pagey / conf.tileh in
828 let vw =
829 let a = l.pagew - l.pagex in
830 let b = conf.winw - state.scrollw in
831 min a b
832 and vh = l.pagevh in
834 let rec rowloop row y0 dispy h =
835 if h = 0
836 then ()
837 else (
838 let dh = conf.tileh - y0 in
839 let dh = min h dh in
840 let rec colloop col x0 dispx w =
841 if w = 0
842 then ()
843 else (
844 let dw = conf.tilew - x0 in
845 let dw = min w dw in
847 f col row dispx dispy x0 y0 dw dh;
848 colloop (col+1) 0 (dispx+dw) (w-dw)
851 colloop col tilex l.pagedispx vw;
852 rowloop (row+1) 0 (dispy+dh) (h-dh)
855 if vw > 0 && vh > 0
856 then rowloop row tiley l.pagedispy vh;
859 let gettileopaque l col row =
860 let key =
861 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
863 try Some (Hashtbl.find state.tilemap key)
864 with Not_found -> None
867 let puttileopaque l col row gen colorspace angle opaque size elapsed =
868 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
869 Hashtbl.add state.tilemap key (opaque, size, elapsed)
872 let drawtiles l color =
873 GlDraw.color color;
874 let f col row x y tilex tiley w h =
875 match gettileopaque l col row with
876 | Some (opaque, _, t) ->
877 let params = x, y, w, h, tilex, tiley in
878 if conf.invert
879 then (
880 Gl.enable `blend;
881 GlFunc.blend_func `zero `one_minus_src_color;
883 drawtile params opaque;
884 if conf.invert
885 then Gl.disable `blend;
886 if conf.debug
887 then (
888 let s = Printf.sprintf
889 "%d[%d,%d] %f sec"
890 l.pageno col row t
892 let w = measurestr !uifontsize s in
893 GlMisc.push_attrib [`current];
894 GlDraw.color (0.0, 0.0, 0.0);
895 GlDraw.rect
896 (float (x-2), float (y-2))
897 (float (x+2) +. w, float (y + !uifontsize + 2));
898 GlDraw.color (1.0, 1.0, 1.0);
899 drawstring !uifontsize x (y + !uifontsize - 1) s;
900 GlMisc.pop_attrib ();
903 | _ ->
904 let w =
905 let lw = conf.winw - state.scrollw - x in
906 min lw w
907 and h =
908 let lh = conf.winh - y in
909 min lh h
911 Gl.enable `texture_2d;
912 begin match state.texid with
913 | Some id ->
914 GlTex.bind_texture `texture_2d id;
915 let x0 = float x
916 and y0 = float y
917 and x1 = float (x+w)
918 and y1 = float (y+h) in
920 let tw = float w /. 64.0
921 and th = float h /. 64.0 in
922 let tx0 = float tilex /. 64.0
923 and ty0 = float tiley /. 64.0 in
924 let tx1 = tx0 +. tw
925 and ty1 = ty0 +. th in
926 GlDraw.begins `quads;
927 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
928 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
929 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
930 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
931 GlDraw.ends ();
933 Gl.disable `texture_2d;
934 | None ->
935 GlDraw.color (1.0, 1.0, 1.0);
936 GlDraw.rect
937 (float x, float y)
938 (float (x+w), float (y+h));
939 end;
940 if w > 128 && h > !uifontsize + 10
941 then (
942 GlDraw.color (0.0, 0.0, 0.0);
943 let c, r =
944 if conf.verbose
945 then (col*conf.tilew, row*conf.tileh)
946 else col, row
948 drawstring2 !uifontsize x y "Loading %d [%d,%d]" l.pageno c r;
950 GlDraw.color color;
952 itertiles l f
955 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
957 let tilevisible1 l x y =
958 let ax0 = l.pagex
959 and ax1 = l.pagex + l.pagevw
960 and ay0 = l.pagey
961 and ay1 = l.pagey + l.pagevh in
963 let bx0 = x
964 and by0 = y in
965 let bx1 = min (bx0 + conf.tilew) l.pagew
966 and by1 = min (by0 + conf.tileh) l.pageh in
968 let rx0 = max ax0 bx0
969 and ry0 = max ay0 by0
970 and rx1 = min ax1 bx1
971 and ry1 = min ay1 by1 in
973 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
974 nonemptyintersection
977 let tilevisible layout n x y =
978 let rec findpageinlayout = function
979 | l :: _ when l.pageno = n -> tilevisible1 l x y
980 | _ :: rest -> findpageinlayout rest
981 | [] -> false
983 findpageinlayout layout
986 let tileready l x y =
987 tilevisible1 l x y &&
988 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
991 let tilepage n p layout =
992 let rec loop = function
993 | l :: rest ->
994 if l.pageno = n
995 then
996 let f col row _ _ _ _ _ _ =
997 if state.currently = Idle
998 then
999 match gettileopaque l col row with
1000 | Some _ -> ()
1001 | None ->
1002 let x = col*conf.tilew
1003 and y = row*conf.tileh in
1004 let w =
1005 let w = l.pagew - x in
1006 min w conf.tilew
1008 let h =
1009 let h = l.pageh - y in
1010 min h conf.tileh
1012 wcmd "tile"
1013 [`s p
1014 ;`i x
1015 ;`i y
1016 ;`i w
1017 ;`i h
1019 state.currently <-
1020 Tiling (
1021 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1022 conf.tilew, conf.tileh
1025 itertiles l f;
1026 else
1027 loop rest
1029 | [] -> ()
1031 if state.invalidated = 0 then loop layout;
1034 let preloadlayout visiblepages =
1035 let presentation = conf.presentation in
1036 let interpagespace = conf.interpagespace in
1037 let maxy = state.maxy in
1038 conf.presentation <- false;
1039 conf.interpagespace <- 0;
1040 state.maxy <- calcheight ();
1041 let y =
1042 match visiblepages with
1043 | [] -> 0
1044 | l :: _ -> getpagey l.pageno + l.pagey
1046 let y = if y < conf.winh then 0 else y - conf.winh in
1047 let h = state.y - y + conf.winh*3 in
1048 let pages = layout y h in
1049 conf.presentation <- presentation;
1050 conf.interpagespace <- interpagespace;
1051 state.maxy <- maxy;
1052 pages
1055 let load pages =
1056 let rec loop pages =
1057 if state.currently != Idle
1058 then ()
1059 else
1060 match pages with
1061 | l :: rest ->
1062 begin match getopaque l.pageno with
1063 | None ->
1064 wcmd "page" [`i l.pageno; `i l.pagedimno];
1065 state.currently <- Loading (l, state.gen);
1066 | Some opaque ->
1067 tilepage l.pageno opaque pages;
1068 loop rest
1069 end;
1070 | _ -> ()
1072 if state.invalidated = 0 then loop pages
1075 let preload pages =
1076 load pages;
1077 if conf.preload && state.currently = Idle
1078 then load (preloadlayout pages);
1081 let layoutready layout =
1082 let rec fold all ls =
1083 all && match ls with
1084 | l :: rest ->
1085 let seen = ref false in
1086 let allvisible = ref true in
1087 let foo col row _ _ _ _ _ _ =
1088 seen := true;
1089 allvisible := !allvisible &&
1090 begin match gettileopaque l col row with
1091 | Some _ -> true
1092 | None -> false
1095 itertiles l foo;
1096 fold (!seen && !allvisible) rest
1097 | [] -> true
1099 let alltilesvisible = fold true layout in
1100 alltilesvisible;
1103 let gotoy y =
1104 let y = bound y 0 state.maxy in
1105 let y, layout, proceed =
1106 if conf.showall
1107 then
1108 match state.throttle with
1109 | None ->
1110 let layout = layout y conf.winh in
1111 let ready = layoutready layout in
1112 if not ready
1113 then (
1114 load layout;
1115 state.throttle <- Some (layout, y);
1117 else G.postRedisplay "gotoy showall (None)";
1118 y, layout, ready
1119 | Some _ -> -1, [], false
1120 else
1121 let layout = layout y conf.winh in
1122 if true || layoutready layout
1123 then G.postRedisplay "gotoy ready";
1124 y, layout, true
1126 if proceed
1127 then (
1128 state.y <- y;
1129 state.layout <- layout;
1130 begin match state.mode with
1131 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1132 if not (pagevisible layout pageno)
1133 then (
1134 match state.layout with
1135 | [] -> ()
1136 | l :: _ ->
1137 state.mode <- Birdseye (
1138 conf, leftx, l.pageno, hooverpageno, anchor
1141 | _ -> ()
1142 end;
1143 preload layout;
1147 let conttiling pageno opaque =
1148 tilepage pageno opaque
1149 (if conf.preload then preloadlayout state.layout else state.layout)
1152 let gotoy_and_clear_text y =
1153 gotoy y;
1154 if not conf.verbose then state.text <- "";
1157 let getanchor () =
1158 match state.layout with
1159 | [] -> emptyanchor
1160 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1163 let getanchory (n, top) =
1164 let y, h = getpageyh n in
1165 y + (truncate (top *. float h));
1168 let gotoanchor anchor =
1169 gotoy (getanchory anchor);
1172 let addnav () =
1173 cbput state.hists.nav (getanchor ());
1176 let getnav dir =
1177 let anchor = cbgetc state.hists.nav dir in
1178 getanchory anchor;
1181 let gotopage n top =
1182 let y, h = getpageyh n in
1183 gotoy_and_clear_text (y + (truncate (top *. float h)));
1186 let gotopage1 n top =
1187 let y = getpagey n in
1188 gotoy_and_clear_text (y + top);
1191 let invalidate () =
1192 state.layout <- [];
1193 state.pdims <- [];
1194 state.rects <- [];
1195 state.rects1 <- [];
1196 state.invalidated <- state.invalidated + 1;
1199 let writeopen path password =
1200 writecmd state.csock ("open " ^ path ^ "\000" ^ password ^ "\000");
1203 let opendoc path password =
1204 invalidate ();
1205 state.path <- path;
1206 state.password <- password;
1207 state.gen <- state.gen + 1;
1208 state.docinfo <- [];
1210 setaalevel conf.aalevel;
1211 writeopen path password;
1212 Glut.setWindowTitle ("llpp " ^ Filename.basename path);
1213 wcmd "geometry" [`i state.w; `i conf.winh];
1216 let scalecolor c =
1217 let c = c *. state.colorscale in
1218 (c, c, c);
1221 let scalecolor2 (r, g, b) =
1222 (r *. state.colorscale, g *. state.colorscale, b *. state.colorscale);
1225 let represent () =
1226 state.maxy <- calcheight ();
1227 state.hscrollh <-
1228 if state.w <= conf.winw - state.scrollw
1229 then 0
1230 else state.scrollw
1232 match state.mode with
1233 | Birdseye (_, _, pageno, _, _) ->
1234 let y, h = getpageyh pageno in
1235 let top = (conf.winh - h) / 2 in
1236 gotoy (max 0 (y - top))
1237 | _ -> gotoanchor state.anchor
1240 let reshape =
1241 let firsttime = ref true in
1242 fun ~w ~h ->
1243 GlDraw.viewport 0 0 w h;
1244 if state.invalidated = 0 && not !firsttime
1245 then state.anchor <- getanchor ();
1247 firsttime := false;
1248 conf.winw <- w;
1249 let w = truncate (float w *. conf.zoom) - state.scrollw in
1250 let w = max w 2 in
1251 state.w <- w;
1252 conf.winh <- h;
1253 GlMat.mode `modelview;
1254 GlMat.load_identity ();
1256 GlMat.mode `projection;
1257 GlMat.load_identity ();
1258 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1259 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1260 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1262 invalidate ();
1263 wcmd "geometry" [`i w; `i h];
1266 let enttext () =
1267 let len = String.length state.text in
1268 let drawstring s =
1269 let hscrollh =
1270 match state.mode with
1271 | View -> state.hscrollh
1272 | _ -> 0
1274 let rect x w =
1275 GlDraw.rect
1276 (x, float (conf.winh - (!uifontsize + 4) - hscrollh))
1277 (x+.w, float (conf.winh - hscrollh))
1280 let w = float (conf.winw - state.scrollw - 1) in
1281 if state.progress >= 0.0 && state.progress < 1.0
1282 then (
1283 GlDraw.color (0.3, 0.3, 0.3);
1284 let w1 = w *. state.progress in
1285 rect 0.0 w1;
1286 GlDraw.color (0.0, 0.0, 0.0);
1287 rect w1 (w-.w1)
1289 else (
1290 GlDraw.color (0.0, 0.0, 0.0);
1291 rect 0.0 w;
1294 GlDraw.color (1.0, 1.0, 1.0);
1295 drawstring !uifontsize
1296 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1298 match state.mode with
1299 | Textentry ((prefix, text, _, _, _), _) ->
1300 let s =
1301 if len > 0
1302 then
1303 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1304 else
1305 Printf.sprintf "%s%s_" prefix text
1307 drawstring s
1309 | _ ->
1310 if len > 0 then drawstring state.text
1313 let showtext c s =
1314 state.text <- Printf.sprintf "%c%s" c s;
1315 G.postRedisplay "showtext";
1318 let gctiles () =
1319 let len = Queue.length state.tilelru in
1320 let rec loop qpos =
1321 if state.memused <= conf.memlimit
1322 then ()
1323 else (
1324 if qpos < len
1325 then
1326 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1327 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1329 gen = state.gen
1330 && colorspace = conf.colorspace
1331 && angle = conf.angle
1332 && pagew = getpagew n
1333 && pageh = getpageh n
1334 && (
1335 let layout =
1336 if conf.preload
1337 then preloadlayout state.layout
1338 else state.layout
1340 let x = col*conf.tilew
1341 and y = row*conf.tileh in
1342 tilevisible layout n x y
1344 then Queue.push lruitem state.tilelru
1345 else (
1346 wcmd "freetile" [`s p];
1347 state.memused <- state.memused - s;
1348 Hashtbl.remove state.tilemap k;
1350 loop (qpos+1)
1353 loop 0
1356 let flushtiles () =
1357 Queue.iter (fun (k, p, s) ->
1358 wcmd "freetile" [`s p];
1359 state.memused <- state.memused - s;
1360 Hashtbl.remove state.tilemap k;
1361 ) state.tilelru;
1362 Queue.clear state.tilelru;
1363 load state.layout;
1366 let logcurrently = function
1367 | Idle -> dolog "Idle"
1368 | Loading (l, gen) ->
1369 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1370 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1371 dolog
1372 "Tiling %d[%d,%d] page=%s cs=%s angle"
1373 l.pageno col row pageopaque
1374 (colorspace_to_string colorspace)
1376 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1377 angle gen conf.angle state.gen
1378 tilew tileh
1379 conf.tilew conf.tileh
1381 | Outlining _ ->
1382 dolog "outlining"
1385 let act cmds =
1386 (* dolog "%S" cmds; *)
1387 let op, args =
1388 let spacepos =
1389 try String.index cmds ' '
1390 with Not_found -> -1
1392 if spacepos = -1
1393 then cmds, ""
1394 else
1395 let l = String.length cmds in
1396 let op = String.sub cmds 0 spacepos in
1397 op, begin
1398 if l - spacepos < 2 then ""
1399 else String.sub cmds (spacepos+1) (l-spacepos-1)
1402 match op with
1403 | "clear" ->
1404 state.pdims <- [];
1406 | "clearrects" ->
1407 state.rects <- state.rects1;
1408 G.postRedisplay "clearrects";
1410 | "continue" ->
1411 let n = Scanf.sscanf args "%u" (fun n -> n) in
1412 state.pagecount <- n;
1413 state.invalidated <- state.invalidated - 1;
1414 begin match state.currently with
1415 | Outlining l ->
1416 state.currently <- Idle;
1417 state.outlines <- Array.of_list (List.rev l)
1418 | _ -> ()
1419 end;
1420 if state.invalidated = 0
1421 then represent ();
1422 if not conf.showall
1423 then G.postRedisplay "continue";
1425 | "title" ->
1426 Glut.setWindowTitle args
1428 | "msg" ->
1429 showtext ' ' args
1431 | "vmsg" ->
1432 if conf.verbose
1433 then showtext ' ' args
1435 | "progress" ->
1436 let progress, text = Scanf.sscanf args "%f %n"
1437 (fun f pos ->
1438 f, String.sub args pos (String.length args - pos)
1441 state.text <- text;
1442 state.progress <- progress;
1443 G.postRedisplay "progress"
1445 | "firstmatch" ->
1446 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1447 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1448 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1449 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1451 let y = (getpagey pageno) + truncate y0 in
1452 addnav ();
1453 gotoy y;
1454 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1456 | "match" ->
1457 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1458 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1459 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1460 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1462 state.rects1 <-
1463 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1465 | "page" ->
1466 let pageopaque, t = Scanf.sscanf args "%s %f" (fun p t -> p, t) in
1467 begin match state.currently with
1468 | Loading (l, gen) ->
1469 vlog "page %d took %f sec" l.pageno t;
1470 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1471 begin match state.throttle with
1472 | None ->
1473 let preloadedpages =
1474 if conf.preload
1475 then preloadlayout state.layout
1476 else state.layout
1478 let evict () =
1479 let module IntSet =
1480 Set.Make (struct type t = int let compare = (-) end) in
1481 let set =
1482 List.fold_left (fun s l -> IntSet.add l.pageno s)
1483 IntSet.empty preloadedpages
1485 let evictedpages =
1486 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1487 if not (IntSet.mem pageno set)
1488 then (
1489 wcmd "freepage" [`s opaque];
1490 key :: accu
1492 else accu
1493 ) state.pagemap []
1495 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1497 evict ();
1498 state.currently <- Idle;
1499 if gen = state.gen
1500 then (
1501 tilepage l.pageno pageopaque state.layout;
1502 load state.layout;
1503 load preloadedpages;
1504 if pagevisible state.layout l.pageno
1505 && layoutready state.layout
1506 then G.postRedisplay "page";
1509 | Some (layout, _) ->
1510 state.currently <- Idle;
1511 tilepage l.pageno pageopaque layout;
1512 load state.layout
1513 end;
1515 | _ ->
1516 dolog "Inconsistent loading state";
1517 logcurrently state.currently;
1518 raise Quit;
1521 | "tile" ->
1522 let (x, y, opaque, size, t) =
1523 Scanf.sscanf args "%u %u %s %u %f"
1524 (fun x y p size t -> (x, y, p, size, t))
1526 begin match state.currently with
1527 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1528 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1530 if tilew != conf.tilew || tileh != conf.tileh
1531 then (
1532 wcmd "freetile" [`s opaque];
1533 state.currently <- Idle;
1534 load state.layout;
1536 else (
1537 puttileopaque l col row gen cs angle opaque size t;
1538 state.memused <- state.memused + size;
1539 gctiles ();
1540 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1541 opaque, size) state.tilelru;
1543 state.currently <- Idle;
1544 if gen = state.gen
1545 && conf.colorspace = cs
1546 && conf.angle = angle
1547 && tilevisible state.layout l.pageno x y
1548 then conttiling l.pageno pageopaque;
1550 begin match state.throttle with
1551 | None ->
1552 preload state.layout;
1553 if gen = state.gen
1554 && conf.colorspace = cs
1555 && conf.angle = angle
1556 && tilevisible state.layout l.pageno x y
1557 then G.postRedisplay "tile nothrottle";
1559 | Some (layout, y) ->
1560 let ready = layoutready layout in
1561 if ready
1562 then (
1563 state.y <- y;
1564 state.layout <- layout;
1565 state.throttle <- None;
1566 G.postRedisplay "throttle";
1568 else load layout;
1569 end;
1572 | _ ->
1573 dolog "Inconsistent tiling state";
1574 logcurrently state.currently;
1575 raise Quit;
1578 | "pdim" ->
1579 let pdim =
1580 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1582 state.pdims <- pdim :: state.pdims
1584 | "o" ->
1585 let (l, n, t, h, pos) =
1586 Scanf.sscanf args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1588 let s = String.sub args pos (String.length args - pos) in
1589 let outline = (s, l, (n, float t /. float h)) in
1590 begin match state.currently with
1591 | Outlining outlines ->
1592 state.currently <- Outlining (outline :: outlines)
1593 | Idle ->
1594 state.currently <- Outlining [outline]
1595 | currently ->
1596 dolog "invalid outlining state";
1597 logcurrently currently
1600 | "info" ->
1601 state.docinfo <- (1, args) :: state.docinfo
1603 | "infoend" ->
1604 state.docinfo <- List.rev state.docinfo
1606 | _ ->
1607 dolog "unknown cmd `%S'" cmds
1610 let now = Unix.gettimeofday;;
1612 let idle () =
1613 if state.deadline == nan then state.deadline <- now ();
1614 let rec loop delay =
1615 let timeout =
1616 if delay > 0.0
1617 then max 0.0 (state.deadline -. now ())
1618 else 0.0
1620 let r, _, _ = Unix.select [state.csock] [] [] timeout in
1621 begin match r with
1622 | [] ->
1623 begin match state.autoscroll with
1624 | Some step when step != 0 ->
1625 let y = state.y + step in
1626 let y =
1627 if y < 0
1628 then state.maxy
1629 else if y >= state.maxy then 0 else y
1631 gotoy y;
1632 if state.mode = View
1633 then state.text <- "";
1634 state.deadline <- state.deadline +. 0.005;
1636 | _ ->
1637 state.deadline <- state.deadline +. delay;
1638 end;
1640 | _ ->
1641 let cmd = readcmd state.csock in
1642 act cmd;
1643 loop 0.0
1644 end;
1645 in loop 0.007
1648 let onhist cb =
1649 let rc = cb.rc in
1650 let action = function
1651 | HCprev -> cbget cb ~-1
1652 | HCnext -> cbget cb 1
1653 | HCfirst -> cbget cb ~-(cb.rc)
1654 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1655 and cancel () = cb.rc <- rc
1656 in (action, cancel)
1659 let search pattern forward =
1660 if String.length pattern > 0
1661 then
1662 let pn, py =
1663 match state.layout with
1664 | [] -> 0, 0
1665 | l :: _ ->
1666 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1668 let cmd =
1669 let b = makecmd "search"
1670 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
1672 Buffer.add_char b ',';
1673 Buffer.add_string b pattern;
1674 Buffer.add_char b '\000';
1675 Buffer.contents b;
1677 writecmd state.csock cmd;
1680 let intentry text key =
1681 let c = Char.unsafe_chr key in
1682 match c with
1683 | '0' .. '9' ->
1684 let text = addchar text c in
1685 TEcont text
1687 | _ ->
1688 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
1689 TEcont text
1692 let textentry text key =
1693 let c = Char.unsafe_chr key in
1694 match c with
1695 | _ when key >= 32 && key < 127 ->
1696 let text = addchar text c in
1697 TEcont text
1699 | _ ->
1700 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
1701 TEcont text
1704 let reqlayout angle proportional =
1705 match state.throttle with
1706 | None ->
1707 if state.invalidated = 0 then state.anchor <- getanchor ();
1708 conf.angle <- angle mod 360;
1709 conf.proportional <- proportional;
1710 invalidate ();
1711 wcmd "reqlayout" [`i conf.angle; `b proportional];
1712 | _ -> ()
1715 let settrim trimmargins trimfuzz =
1716 if state.invalidated = 0 then state.anchor <- getanchor ();
1717 conf.trimmargins <- trimmargins;
1718 conf.trimfuzz <- trimfuzz;
1719 let x0, y0, x1, y1 = trimfuzz in
1720 invalidate ();
1721 wcmd "settrim" [
1722 `b conf.trimmargins;
1723 `i x0;
1724 `i y0;
1725 `i x1;
1726 `i y1;
1728 Hashtbl.iter (fun _ opaque ->
1729 wcmd "freepage" [`s opaque];
1730 ) state.pagemap;
1731 Hashtbl.clear state.pagemap;
1734 let setzoom zoom =
1735 match state.throttle with
1736 | None ->
1737 let zoom = max 0.01 zoom in
1738 if zoom <> conf.zoom
1739 then (
1740 state.prevzoom <- conf.zoom;
1741 let relx =
1742 if zoom <= 1.0
1743 then (state.x <- 0; 0.0)
1744 else float state.x /. float state.w
1746 conf.zoom <- zoom;
1747 reshape conf.winw conf.winh;
1748 if zoom > 1.0
1749 then (
1750 let x = relx *. float state.w in
1751 state.x <- truncate x;
1753 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
1756 | _ -> ()
1759 let enterbirdseye () =
1760 let zoom = float conf.thumbw /. float conf.winw in
1761 let birdseyepageno =
1762 let cy = conf.winh / 2 in
1763 let fold = function
1764 | [] -> 0
1765 | l :: rest ->
1766 let rec fold best = function
1767 | [] -> best.pageno
1768 | l :: rest ->
1769 let d = cy - (l.pagedispy + l.pagevh/2)
1770 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1771 if abs d < abs dbest
1772 then fold l rest
1773 else best.pageno
1774 in fold l rest
1776 fold state.layout
1778 state.mode <- Birdseye (
1779 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
1781 conf.zoom <- zoom;
1782 conf.presentation <- false;
1783 conf.interpagespace <- 10;
1784 conf.hlinks <- false;
1785 state.x <- 0;
1786 state.mstate <- Mnone;
1787 conf.showall <- false;
1788 Glut.setCursor Glut.CURSOR_INHERIT;
1789 if conf.verbose
1790 then
1791 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
1792 (100.0*.zoom)
1793 else
1794 state.text <- ""
1796 reshape conf.winw conf.winh;
1799 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1800 state.mode <- View;
1801 conf.zoom <- c.zoom;
1802 conf.presentation <- c.presentation;
1803 conf.interpagespace <- c.interpagespace;
1804 conf.showall <- c.showall;
1805 conf.hlinks <- c.hlinks;
1806 state.x <- leftx;
1807 if conf.verbose
1808 then
1809 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
1810 (100.0*.conf.zoom)
1812 reshape conf.winw conf.winh;
1813 state.anchor <- if goback then anchor else (pageno, 0.0);
1816 let togglebirdseye () =
1817 match state.mode with
1818 | Birdseye vals -> leavebirdseye vals true
1819 | View -> enterbirdseye ()
1820 | _ -> ()
1823 let upbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
1824 let pageno = max 0 (pageno - 1) in
1825 let rec loop = function
1826 | [] -> gotopage1 pageno 0
1827 | l :: _ when l.pageno = pageno ->
1828 if l.pagedispy >= 0 && l.pagey = 0
1829 then G.postRedisplay "upbirdseye"
1830 else gotopage1 pageno 0
1831 | _ :: rest -> loop rest
1833 loop state.layout;
1834 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1837 let downbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
1838 let pageno = min (state.pagecount - 1) (pageno + 1) in
1839 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1840 let rec loop = function
1841 | [] ->
1842 let y, h = getpageyh pageno in
1843 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
1844 gotoy (clamp dy)
1845 | l :: _ when l.pageno = pageno ->
1846 if l.pagevh != l.pageh
1847 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
1848 else G.postRedisplay "downbirdseye"
1849 | _ :: rest -> loop rest
1851 loop state.layout
1854 let optentry mode _ key =
1855 let btos b = if b then "on" else "off" in
1856 let c = Char.unsafe_chr key in
1857 match c with
1858 | 's' ->
1859 let ondone s =
1860 try conf.scrollstep <- int_of_string s with exc ->
1861 state.text <- Printf.sprintf "bad integer `%s': %s"
1862 s (Printexc.to_string exc)
1864 TEswitch ("scroll step: ", "", None, intentry, ondone)
1866 | 'A' ->
1867 let ondone s =
1869 conf.autoscrollstep <- int_of_string s;
1870 if state.autoscroll <> None
1871 then state.autoscroll <- Some conf.autoscrollstep
1872 with exc ->
1873 state.text <- Printf.sprintf "bad integer `%s': %s"
1874 s (Printexc.to_string exc)
1876 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
1878 | 'Z' ->
1879 let ondone s =
1881 let zoom = float (int_of_string s) /. 100.0 in
1882 setzoom zoom
1883 with exc ->
1884 state.text <- Printf.sprintf "bad integer `%s': %s"
1885 s (Printexc.to_string exc)
1887 TEswitch ("zoom: ", "", None, intentry, ondone)
1889 | 't' ->
1890 let ondone s =
1892 conf.thumbw <- bound (int_of_string s) 2 4096;
1893 state.text <-
1894 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
1895 begin match mode with
1896 | Birdseye beye ->
1897 leavebirdseye beye false;
1898 enterbirdseye ();
1899 | _ -> ();
1901 with exc ->
1902 state.text <- Printf.sprintf "bad integer `%s': %s"
1903 s (Printexc.to_string exc)
1905 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
1907 | 'R' ->
1908 let ondone s =
1909 match try
1910 Some (int_of_string s)
1911 with exc ->
1912 state.text <- Printf.sprintf "bad integer `%s': %s"
1913 s (Printexc.to_string exc);
1914 None
1915 with
1916 | Some angle -> reqlayout angle conf.proportional
1917 | None -> ()
1919 TEswitch ("rotation: ", "", None, intentry, ondone)
1921 | 'i' ->
1922 conf.icase <- not conf.icase;
1923 TEdone ("case insensitive search " ^ (btos conf.icase))
1925 | 'p' ->
1926 conf.preload <- not conf.preload;
1927 gotoy state.y;
1928 TEdone ("preload " ^ (btos conf.preload))
1930 | 'v' ->
1931 conf.verbose <- not conf.verbose;
1932 TEdone ("verbose " ^ (btos conf.verbose))
1934 | 'd' ->
1935 conf.debug <- not conf.debug;
1936 TEdone ("debug " ^ (btos conf.debug))
1938 | 'h' ->
1939 conf.maxhfit <- not conf.maxhfit;
1940 state.maxy <- state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
1941 TEdone ("maxhfit " ^ (btos conf.maxhfit))
1943 | 'c' ->
1944 conf.crophack <- not conf.crophack;
1945 TEdone ("crophack " ^ btos conf.crophack)
1947 | 'a' ->
1948 conf.showall <- not conf.showall;
1949 TEdone ("throttle " ^ btos conf.showall)
1951 | 'f' ->
1952 conf.underinfo <- not conf.underinfo;
1953 TEdone ("underinfo " ^ btos conf.underinfo)
1955 | 'P' ->
1956 conf.savebmarks <- not conf.savebmarks;
1957 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
1959 | 'S' ->
1960 let ondone s =
1962 let pageno, py =
1963 match state.layout with
1964 | [] -> 0, 0
1965 | l :: _ ->
1966 l.pageno, l.pagey
1968 conf.interpagespace <- int_of_string s;
1969 state.maxy <- calcheight ();
1970 let y = getpagey pageno in
1971 gotoy (y + py)
1972 with exc ->
1973 state.text <- Printf.sprintf "bad integer `%s': %s"
1974 s (Printexc.to_string exc)
1976 TEswitch ("vertical margin: ", "", None, intentry, ondone)
1978 | 'l' ->
1979 reqlayout conf.angle (not conf.proportional);
1980 TEdone ("proportional display " ^ btos conf.proportional)
1982 | 'T' ->
1983 settrim (not conf.trimmargins) conf.trimfuzz;
1984 TEdone ("trim margins " ^ btos conf.trimmargins)
1986 | 'I' ->
1987 conf.invert <- not conf.invert;
1988 TEdone ("invert colors " ^ btos conf.invert)
1990 | _ ->
1991 state.text <- Printf.sprintf "bad option %d `%c'" key c;
1992 TEstop
1995 let maxoutlinerows () = (conf.winh - !uifontsize - 1) / (!uifontsize + 1);;
1997 class type lvsource = object
1998 method getitemcount : int
1999 method getitem : int -> (string * int) option
2000 method hasaction : int -> bool
2001 method exit :
2002 uioh:uioh ->
2003 cancel:bool ->
2004 active:int ->
2005 first:int ->
2006 pan:int ->
2007 qsearch:string ->
2008 uioh option
2009 method getactive : int
2010 method getfirst : int
2011 method getqsearch : string
2012 method setqsearch : string -> unit
2013 method getpan : int
2014 end;;
2016 class virtual lvsourcebase = object
2017 val mutable m_active = 0
2018 val mutable m_first = 0
2019 val mutable m_qsearch = ""
2020 val mutable m_pan = 0
2021 method getactive = m_active
2022 method getfirst = m_first
2023 method getqsearch = m_qsearch
2024 method getpan = m_pan
2025 method setqsearch s = m_qsearch <- s
2026 end;;
2028 let textentryspecial key = function
2029 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2030 let s =
2031 match key with
2032 | Glut.KEY_UP -> action HCprev
2033 | Glut.KEY_DOWN -> action HCnext
2034 | Glut.KEY_HOME -> action HCfirst
2035 | Glut.KEY_END -> action HClast
2036 | _ -> state.text
2038 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2039 G.postRedisplay "special textentry";
2040 | _ -> ()
2043 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2044 let enttext te =
2045 state.mode <- Textentry (te, onleave);
2046 state.text <- "";
2047 enttext ();
2048 G.postRedisplay "textentrykeyboard enttext";
2050 match Char.unsafe_chr key with
2051 | '\008' -> (* backspace *)
2052 let len = String.length text in
2053 if len = 0
2054 then (
2055 onleave Cancel;
2056 G.postRedisplay "textentrykeyboard after cancel";
2058 else (
2059 let s = String.sub text 0 (len - 1) in
2060 enttext (c, s, opthist, onkey, ondone)
2063 | '\r' | '\n' ->
2064 ondone text;
2065 onleave Confirm;
2066 G.postRedisplay "textentrykeyboard after confirm"
2068 | '\007' (* ctrl-g *)
2069 | '\027' -> (* escape *)
2070 if String.length text = 0
2071 then (
2072 begin match opthist with
2073 | None -> ()
2074 | Some (_, onhistcancel) -> onhistcancel ()
2075 end;
2076 onleave Cancel;
2077 state.text <- "";
2078 G.postRedisplay "textentrykeyboard after cancel2"
2080 else (
2081 enttext (c, "", opthist, onkey, ondone)
2084 | '\127' -> () (* delete *)
2086 | _ ->
2087 begin match onkey text key with
2088 | TEdone text ->
2089 ondone text;
2090 onleave Confirm;
2091 G.postRedisplay "textentrykeyboard after confirm2";
2093 | TEcont text ->
2094 enttext (c, text, opthist, onkey, ondone);
2096 | TEstop ->
2097 onleave Cancel;
2098 state.text <- "";
2099 G.postRedisplay "textentrykeyboard after cancel3"
2101 | TEswitch te ->
2102 state.mode <- Textentry (te, onleave);
2103 G.postRedisplay "textentrykeyboard switch";
2104 end;
2107 let firstof first active =
2108 let maxrows = maxoutlinerows () in
2109 if first > active || abs (first - active) > maxrows - 1
2110 then max 0 (active - (maxrows/2))
2111 else first
2114 class listview ~(source:lvsource) ~trusted =
2115 let coe s = (s :> uioh) in
2116 object (self)
2117 val m_pan = source#getpan
2118 val m_first = source#getfirst
2119 val m_active = source#getactive
2120 val m_qsearch = source#getqsearch
2121 val m_prev_uioh = state.uioh
2123 method private elemunder y =
2124 let n = y / (!uifontsize+1) in
2125 if m_first + n < source#getitemcount
2126 then (
2127 if source#hasaction (m_first + n)
2128 then Some (m_first + n)
2129 else None
2131 else None
2133 method display =
2134 Gl.enable `blend;
2135 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2136 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2137 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2138 GlDraw.color (1., 1., 1.);
2139 Gl.enable `texture_2d;
2140 let fs = !uifontsize in
2141 let nfs = fs + 1 in
2142 let wx = measurestr fs "w" in
2143 let tabx = 30.0*.wx +. float (m_pan * fs) in
2144 let rec loop row =
2145 if (row - m_first) * nfs > conf.winh
2146 then ()
2147 else
2148 match
2149 if row >= 0 && row < source#getitemcount
2150 then source#getitem row
2151 else None
2152 with
2153 | None -> ()
2154 | Some (s, level) ->
2155 let y = (row - m_first) * nfs in
2156 let x = 5 + fs*(max 0 (level+m_pan)) in
2157 if row = m_active
2158 then (
2159 Gl.disable `texture_2d;
2160 GlDraw.polygon_mode `both `line;
2161 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2162 GlDraw.rect (1., float (y + 1))
2163 (float (conf.winw - 1), float (y + fs + 3));
2164 GlDraw.polygon_mode `both `fill;
2165 GlDraw.color (1., 1., 1.);
2166 Gl.enable `texture_2d;
2169 let drawtabularstring x s =
2170 if trusted
2171 then
2172 let tabpos = try String.index s '\t' with Not_found -> -1 in
2173 if tabpos > 0
2174 then
2175 let len = String.length s - tabpos - 1 in
2176 let s1 = String.sub s 0 tabpos
2177 and s2 = String.sub s (tabpos + 1) len in
2178 let xx = wx +. drawstring1 fs x (y + !uifontsize+1) s1 in
2179 let x = truncate (max xx tabx) in
2180 drawstring1 nfs x (y + (!uifontsize+1)) s2
2181 else
2182 drawstring1 fs x (y + nfs) s
2183 else
2184 drawstring1 fs x (y + nfs) s
2186 let _w = drawtabularstring (x + m_pan*nfs) s in
2187 loop (row+1)
2189 loop 0;
2190 Gl.disable `blend;
2191 Gl.disable `texture_2d;
2193 method private key1 key =
2194 let set active first qsearch =
2195 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2197 let search active pattern incr =
2198 let dosearch re =
2199 let rec loop n =
2200 if n >= 0 && n < source#getitemcount
2201 then
2202 match source#getitem n with
2203 | None -> None
2204 | Some (s, _) ->
2206 (try ignore (Str.search_forward re s 0); true
2207 with Not_found -> false)
2208 then Some n
2209 else loop (n + incr)
2210 else None
2212 loop active
2215 let re = Str.regexp_case_fold pattern in
2216 dosearch re
2217 with Failure s ->
2218 state.text <- s;
2219 None
2221 match key with
2222 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2223 let incr = if key = 18 then -1 else 1 in
2224 let active, first =
2225 match search (m_active + incr) m_qsearch incr with
2226 | None ->
2227 state.text <- m_qsearch ^ " [not found]";
2228 m_active, m_first
2229 | Some active ->
2230 state.text <- m_qsearch;
2231 active, firstof m_first active
2233 G.postRedisplay "listview ctrl-r/s";
2234 set active first m_qsearch;
2236 | 8 -> (* backspace *)
2237 let len = String.length m_qsearch in
2238 if len = 0
2239 then coe self
2240 else (
2241 if len = 1
2242 then (
2243 state.text <- "";
2244 G.postRedisplay "listview empty qsearch";
2245 set m_active m_first "";
2247 else
2248 let qsearch = String.sub m_qsearch 0 (len - 1) in
2249 let active, first =
2250 match search m_active qsearch ~-1 with
2251 | None ->
2252 state.text <- qsearch ^ " [not found]";
2253 m_active, m_first
2254 | Some active ->
2255 state.text <- qsearch;
2256 active, firstof m_first active
2258 G.postRedisplay "listview backspace qsearch";
2259 set active first qsearch
2262 | _ when key >= 32 && key < 127 ->
2263 let pattern = addchar m_qsearch (Char.chr key) in
2264 let active, first =
2265 match search m_active pattern 1 with
2266 | None ->
2267 state.text <- pattern ^ " [not found]";
2268 m_active, m_first
2269 | Some active ->
2270 state.text <- pattern;
2271 active, firstof m_first active
2273 G.postRedisplay "listview qsearch add";
2274 set active first pattern;
2276 | 27 -> (* escape *)
2277 state.text <- "";
2278 if String.length m_qsearch = 0
2279 then (
2280 G.postRedisplay "list view escape";
2281 begin
2282 match
2283 source#exit (coe self) true m_active m_first m_pan m_qsearch
2284 with
2285 | None -> m_prev_uioh
2286 | Some uioh -> uioh
2289 else (
2290 G.postRedisplay "list view kill qsearch";
2291 source#setqsearch "";
2292 coe {< m_qsearch = "" >}
2295 | 13 -> (* enter *)
2296 state.text <- "";
2297 let self = {< m_qsearch = "" >} in
2298 source#setqsearch "";
2299 let opt =
2300 G.postRedisplay "listview enter";
2301 if m_active >= 0 && m_active < source#getitemcount
2302 then (
2303 source#exit (coe self) false m_active m_first m_pan "";
2305 else (
2306 source#exit (coe self) true m_active m_first m_pan "";
2309 begin match opt with
2310 | None -> m_prev_uioh
2311 | Some uioh -> uioh
2314 | 127 -> (* delete *)
2315 coe self
2317 | _ -> dolog "unknown key %d" key; coe self
2319 method private special1 key =
2320 let maxrows = maxoutlinerows () in
2321 let itemcount = source#getitemcount in
2322 let find start incr =
2323 let rec find i =
2324 if i = -1 || i = itemcount
2325 then -1
2326 else (
2327 if source#hasaction i
2328 then i
2329 else find (i + incr)
2332 find start
2334 let set active first =
2335 let first = bound first 0 (itemcount - maxrows) in
2336 state.text <- "";
2337 coe {< m_active = active; m_first = first >}
2339 let navigate incr =
2340 let isvisible first n = n >= first && n - first <= maxrows in
2341 let active, first =
2342 let incr1 = if incr > 0 then 1 else -1 in
2343 if isvisible m_first m_active
2344 then
2345 let next =
2346 let next = m_active + incr in
2347 let next =
2348 if next < 0 || next >= itemcount
2349 then -1
2350 else find next incr1
2352 if next = -1 || abs (m_active - next) > maxrows
2353 then -1
2354 else next
2356 if next = -1
2357 then
2358 let first = m_first + incr in
2359 let first = bound first 0 (itemcount - 1) in
2360 let next =
2361 let next = m_active + incr in
2362 let next = bound next 0 (itemcount - 1) in
2363 find next ~-incr1
2365 let active = if next = -1 then m_active else next in
2366 active, first
2367 else
2368 let first = min next m_first in
2369 next, first
2370 else
2371 let first = m_first + incr in
2372 let first = bound first 0 (itemcount - 1) in
2373 let active =
2374 let next = m_active + incr in
2375 let next = bound next 0 (itemcount - 1) in
2376 let next = find next incr1 in
2377 if next = -1 || abs (m_active - first) > maxrows
2378 then m_active
2379 else next
2381 active, first
2383 G.postRedisplay "listview navigate";
2384 set active first;
2386 begin match key with
2387 | Glut.KEY_UP -> navigate ~-1
2388 | Glut.KEY_DOWN -> navigate 1
2389 | Glut.KEY_PAGE_UP -> navigate ~-maxrows
2390 | Glut.KEY_PAGE_DOWN -> navigate maxrows
2392 | Glut.KEY_RIGHT ->
2393 state.text <- "";
2394 G.postRedisplay "listview right";
2395 coe {< m_pan = m_pan - 1 >}
2397 | Glut.KEY_LEFT ->
2398 state.text <- "";
2399 G.postRedisplay "listview left";
2400 coe {< m_pan = m_pan + 1 >}
2402 | Glut.KEY_HOME ->
2403 let active = find 0 1 in
2404 G.postRedisplay "listview home";
2405 set active 0;
2407 | Glut.KEY_END ->
2408 let first = max 0 (itemcount - maxrows) in
2409 let active = find (itemcount - 1) ~-1 in
2410 G.postRedisplay "listview end";
2411 set active first;
2413 | _ -> coe self
2414 end;
2416 method key key =
2417 match state.mode with
2418 | Textentry te -> textentrykeyboard key te; coe self
2419 | _ -> self#key1 key
2421 method special key =
2422 match state.mode with
2423 | Textentry te -> textentryspecial key te; coe self
2424 | _ -> self#special1 key
2426 method button button bstate _ y =
2427 let opt =
2428 match button with
2429 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
2430 begin match self#elemunder y with
2431 | Some n ->
2432 G.postRedisplay "listview click";
2433 source#exit (coe {< m_active = n >}) false n m_first m_pan m_qsearch
2434 | _ ->
2435 Some (coe self)
2437 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
2438 let len = source#getitemcount in
2439 let first =
2440 if m_first + maxoutlinerows () >= len
2441 then
2442 m_first
2443 else
2444 let first = m_first + (if n == 3 then -1 else 1) in
2445 bound first 0 (len - 1)
2447 G.postRedisplay "listview wheel";
2448 Some (coe {< m_first = first >})
2449 | _ ->
2450 Some (coe self)
2452 match opt with
2453 | None -> m_prev_uioh
2454 | Some uioh -> uioh
2456 method motion _ _ = coe self
2458 method pmotion _ y =
2459 let n =
2460 match self#elemunder y with
2461 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
2462 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
2464 let o =
2465 if n != m_active
2466 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
2467 else self
2469 coe o
2470 end;;
2472 class outlinelistview ~source : uioh =
2473 let coe o = (o :> uioh) in
2474 object
2475 inherit listview ~source:(source :> lvsource) ~trusted:false as super
2477 method key key =
2478 match key with
2479 | 14 -> (* ctrl-n *)
2480 source#narrow m_qsearch;
2481 G.postRedisplay "outline ctrl-n";
2482 coe {< m_first = 0; m_active = 0 >}
2484 | 21 -> (* ctrl-u *)
2485 source#denarrow;
2486 G.postRedisplay "outline ctrl-u";
2487 coe {< m_first = 0; m_active = 0 >}
2489 | 12 -> (* ctrl-l *)
2490 let first = m_active - (maxoutlinerows () / 2) in
2491 G.postRedisplay "outline ctrl-l";
2492 coe {< m_first = first >}
2494 | 127 -> (* delete *)
2495 source#remove m_active;
2496 G.postRedisplay "outline delete";
2497 let active = max 0 (m_active-1) in
2498 coe {< m_first = firstof m_first active; m_active = active >}
2500 | key -> super#key key
2502 method special key =
2503 let maxrows = maxoutlinerows () in
2504 let calcfirst first active =
2505 if active > first
2506 then
2507 let rows = active - first in
2508 if rows > maxrows then active - maxrows else first
2509 else active
2511 let navigate incr =
2512 let active = m_active + incr in
2513 let active = bound active 0 (source#getitemcount - 1) in
2514 let first = calcfirst m_first active in
2515 G.postRedisplay "special outline navigate";
2516 coe {< m_active = active; m_first = first >}
2518 let updownlevel incr =
2519 let len = source#getitemcount in
2520 let curlevel =
2521 match source#getitem m_active with
2522 | None -> assert false
2523 | Some (_, level) -> level
2525 let rec flow i =
2526 if i = len then i-1 else if i = -1 then 0 else
2527 let l =
2528 match source#getitem i with
2529 | None -> -1
2530 | Some (_, l) -> l
2532 if l != curlevel then i else flow (i+incr)
2534 let active = flow m_active in
2535 let first = calcfirst m_first active in
2536 G.postRedisplay "special outline updownlevel";
2537 {< m_active = active; m_first = first >}
2539 match key with
2540 | Glut.KEY_UP -> navigate ~-1
2541 | Glut.KEY_DOWN -> navigate 1
2542 | Glut.KEY_PAGE_UP -> navigate ~-maxrows
2543 | Glut.KEY_PAGE_DOWN -> navigate maxrows
2545 | Glut.KEY_RIGHT ->
2546 let o =
2547 if Glut.getModifiers () land Glut.active_ctrl != 0
2548 then (
2549 G.postRedisplay "special outline right";
2550 {< m_pan = m_pan + 1 >}
2552 else updownlevel 1
2554 coe o
2556 | Glut.KEY_LEFT ->
2557 let o =
2558 if Glut.getModifiers () land Glut.active_ctrl != 0
2559 then (
2560 G.postRedisplay "special outline left";
2561 {< m_pan = m_pan - 1 >}
2563 else updownlevel ~-1
2565 coe o
2567 | Glut.KEY_HOME ->
2568 G.postRedisplay "special outline home";
2569 coe {< m_first = 0; m_active = 0 >}
2571 | Glut.KEY_END ->
2572 let active = source#getitemcount - 1 in
2573 let first = max 0 (active - maxrows) in
2574 G.postRedisplay "special outline end";
2575 coe {< m_active = active; m_first = first >}
2577 | _ -> super#special key
2580 let outlinesource usebookmarks =
2581 let empty = [||] in
2582 (object
2583 inherit lvsourcebase
2584 val mutable m_items = empty
2585 val mutable m_orig_items = empty
2586 val mutable m_prev_items = empty
2587 val mutable m_narrow_pattern = ""
2588 val mutable m_hadremovals = false
2590 method getitemcount = Array.length m_items + (if m_hadremovals then 1 else 0)
2592 method getitem n =
2593 if n == Array.length m_items && m_hadremovals
2594 then
2595 Some ("[Confirm removal]", 0)
2596 else
2597 let s, n, _ = m_items.(n) in
2598 Some (s, n)
2600 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
2601 ignore (uioh, first, pan, qsearch);
2602 let confrimremoval = m_hadremovals && active = Array.length m_items in
2603 let items =
2604 if String.length m_narrow_pattern = 0
2605 then m_orig_items
2606 else m_items
2608 if not cancel
2609 then (
2610 if not confrimremoval
2611 then(
2612 let _, _, anchor = m_items.(active) in
2613 gotoanchor anchor;
2614 m_items <- items;
2616 else (
2617 state.bookmarks <- Array.to_list m_items;
2618 m_orig_items <- m_items;
2621 else m_items <- items;
2622 None
2624 method hasaction _ = true
2626 method greetmsg =
2627 if Array.length m_items != Array.length m_orig_items
2628 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
2629 else ""
2631 method narrow pattern =
2632 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
2633 match reopt with
2634 | None -> ()
2635 | Some re ->
2636 let rec loop accu n =
2637 if n = -1
2638 then (
2639 m_narrow_pattern <- pattern;
2640 m_items <- Array.of_list accu
2642 else
2643 let (s, _, _) as o = m_items.(n) in
2644 let accu =
2645 if (try ignore (Str.search_forward re s 0); true
2646 with Not_found -> false)
2647 then o :: accu
2648 else accu
2650 loop accu (n-1)
2652 loop [] (Array.length m_items - 1)
2654 method denarrow =
2655 m_orig_items <- (
2656 if usebookmarks
2657 then Array.of_list state.bookmarks
2658 else state.outlines
2660 m_items <- m_orig_items
2662 method remove m =
2663 if usebookmarks
2664 then
2665 if m >= 0 && m < Array.length m_items
2666 then (
2667 m_hadremovals <- true;
2668 m_items <- Array.init (Array.length m_items - 1) (fun n ->
2669 let n = if n >= m then n+1 else n in
2670 m_items.(n)
2674 method reset pageno items =
2675 m_hadremovals <- false;
2676 if m_orig_items == empty || m_prev_items != items
2677 then (
2678 m_orig_items <- items;
2679 if String.length m_narrow_pattern = 0
2680 then m_items <- items;
2682 m_prev_items <- items;
2683 let active =
2684 let rec loop n best bestd =
2685 if n = Array.length m_items
2686 then best
2687 else
2688 let (_, _, (outlinepageno, _)) = m_items.(n) in
2689 let d = abs (outlinepageno - pageno) in
2690 if d < bestd
2691 then loop (n+1) n d
2692 else loop (n+1) best bestd
2694 loop 0 ~-1 max_int
2696 m_active <- active;
2697 m_first <- firstof m_first active
2698 end)
2701 let enterselector usebookmarks =
2702 let source = outlinesource usebookmarks in
2703 fun errmsg ->
2704 let outlines =
2705 if usebookmarks
2706 then Array.of_list state.bookmarks
2707 else state.outlines
2709 if Array.length outlines = 0
2710 then (
2711 showtext ' ' errmsg;
2713 else (
2714 state.text <- source#greetmsg;
2715 Glut.setCursor Glut.CURSOR_INHERIT;
2716 let pageno =
2717 match state.layout with
2718 | [] -> -1
2719 | {pageno=pageno} :: _ -> pageno
2721 source#reset pageno outlines;
2722 state.uioh <- new outlinelistview ~source;
2723 G.postRedisplay "enter selector";
2727 let enteroutlinemode =
2728 let f = enterselector false in
2729 fun ()-> f "Document has no outline";
2732 let enterbookmarkmode =
2733 let f = enterselector true in
2734 fun () -> f "Document has no bookmarks (yet)";
2737 let color_of_string s =
2738 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
2739 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
2743 let color_to_string (r, g, b) =
2744 let r = truncate (r *. 256.0)
2745 and g = truncate (g *. 256.0)
2746 and b = truncate (b *. 256.0) in
2747 Printf.sprintf "%d/%d/%d" r g b
2750 let irect_of_string s =
2751 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
2754 let irect_to_string (x0,y0,x1,y1) =
2755 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
2758 let makecheckers () =
2759 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
2760 following to say:
2761 converted by Issac Trotts. July 25, 2002 *)
2762 let image_height = 64
2763 and image_width = 64 in
2765 let make_image () =
2766 let image =
2767 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height in
2768 for i = 0 to image_width - 1 do
2769 for j = 0 to image_height - 1 do
2770 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
2771 (if (i land 8 ) lxor (j land 8) = 0
2772 then [|255;255;255|] else [|200;200;200|])
2773 done
2774 done;
2775 image
2777 let image = make_image () in
2778 let id = GlTex.gen_texture () in
2779 GlTex.bind_texture `texture_2d id;
2780 GlPix.store (`unpack_alignment 1);
2781 GlTex.image2d image;
2782 List.iter (GlTex.parameter ~target:`texture_2d)
2783 [ `wrap_s `repeat;
2784 `wrap_t `repeat;
2785 `mag_filter `nearest;
2786 `min_filter `nearest ];
2790 let setcheckers enabled =
2791 match state.texid with
2792 | None ->
2793 if enabled then state.texid <- Some (makecheckers ())
2795 | Some texid ->
2796 if not enabled
2797 then (
2798 GlTex.delete_texture texid;
2799 state.texid <- None;
2803 let int_of_string_with_suffix s =
2804 let l = String.length s in
2805 let s1, shift =
2806 if l > 1
2807 then
2808 let suffix = Char.lowercase s.[l-1] in
2809 match suffix with
2810 | 'k' -> String.sub s 0 (l-1), 10
2811 | 'm' -> String.sub s 0 (l-1), 20
2812 | 'g' -> String.sub s 0 (l-1), 30
2813 | _ -> s, 0
2814 else s, 0
2816 let n = int_of_string s1 in
2817 let m = n lsl shift in
2818 if m < 0 || m < n
2819 then raise (Failure "value too large")
2820 else m
2823 let string_with_suffix_of_int n =
2824 if n = 0
2825 then "0"
2826 else
2827 let n, s =
2828 if n = 0
2829 then 0, ""
2830 else (
2831 if n land ((1 lsl 20) - 1) = 0
2832 then n lsr 20, "M"
2833 else (
2834 if n land ((1 lsl 10) - 1) = 0
2835 then n lsr 10, "K"
2836 else n, ""
2840 let rec loop s n =
2841 let h = n mod 1000 in
2842 let n = n / 1000 in
2843 if n = 0
2844 then string_of_int h ^ s
2845 else (
2846 let s = Printf.sprintf "_%03d%s" h s in
2847 loop s n
2850 loop "" n ^ s;
2853 let describe_location () =
2854 let f (fn, _) l =
2855 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
2857 let fn, ln = List.fold_left f (-1, -1) state.layout in
2858 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
2859 let percent =
2860 if maxy <= 0
2861 then 100.
2862 else (100. *. (float state.y /. float maxy))
2864 if fn = ln
2865 then
2866 Printf.sprintf "page %d of %d [%.2f%%]"
2867 (fn+1) state.pagecount percent
2868 else
2869 Printf.sprintf
2870 "pages %d-%d of %d [%.2f%%]"
2871 (fn+1) (ln+1) state.pagecount percent
2874 let rec enterinfomode =
2875 let btos b = if b then "\xe2\x88\x9a" else "" in
2876 let showextended = ref false in
2877 let leave mode = function
2878 | Confirm -> state.mode <- mode
2879 | Cancel -> state.mode <- mode in
2880 let src =
2881 (object
2882 val mutable m_first_time = true
2883 val mutable m_l = []
2884 val mutable m_a = [||]
2885 val mutable m_prev_uioh = nouioh
2886 val mutable m_prev_mode = View
2888 inherit lvsourcebase
2890 method reset prev_mode prev_uioh =
2891 m_a <- Array.of_list (List.rev m_l);
2892 m_l <- [];
2893 m_prev_mode <- prev_mode;
2894 m_prev_uioh <- prev_uioh;
2895 if m_first_time
2896 then (
2897 let rec loop n =
2898 if n >= Array.length m_a
2899 then ()
2900 else
2901 match m_a.(n) with
2902 | _, _, _, Action _ -> m_active <- n
2903 | _ -> loop (n+1)
2905 loop 0;
2906 m_first_time <- false;
2909 method int name get set =
2910 m_l <-
2911 (name, `int get, 1, Action (
2912 fun u ->
2913 let ondone s =
2914 try set (int_of_string s)
2915 with exn ->
2916 state.text <- Printf.sprintf "bad integer `%s': %s"
2917 s (Printexc.to_string exn)
2919 state.text <- "";
2920 let te = name ^ ": ", "", None, intentry, ondone in
2921 state.mode <- Textentry (te, leave m_prev_mode);
2923 )) :: m_l
2925 method int_with_suffix name get set =
2926 m_l <-
2927 (name, `intws get, 1, Action (
2928 fun u ->
2929 let ondone s =
2930 try set (int_of_string_with_suffix s)
2931 with exn ->
2932 state.text <- Printf.sprintf "bad integer `%s': %s"
2933 s (Printexc.to_string exn)
2935 state.text <- "";
2936 let te =
2937 name ^ ": ", "", None, intentry_with_suffix, ondone
2939 state.mode <- Textentry (te, leave m_prev_mode);
2941 )) :: m_l
2943 method bool ?(offset=1) ?(btos=btos) name get set =
2944 m_l <-
2945 (name, `bool (btos, get), offset, Action (
2946 fun u ->
2947 let v = get () in
2948 set (not v);
2950 )) :: m_l
2952 method color name get set =
2953 m_l <-
2954 (name, `color get, 1, Action (
2955 fun u ->
2956 let invalid = (nan, nan, nan) in
2957 let ondone s =
2958 let c =
2959 try color_of_string s
2960 with exn ->
2961 state.text <- Printf.sprintf "bad color `%s': %s"
2962 s (Printexc.to_string exn);
2963 invalid
2965 if c <> invalid
2966 then set c;
2968 let te = name ^ ": ", "", None, textentry, ondone in
2969 state.text <- color_to_string (get ());
2970 state.mode <- Textentry (te, leave m_prev_mode);
2972 )) :: m_l
2974 method string name get set =
2975 m_l <-
2976 (name, `string get, 1, Action (
2977 fun u ->
2978 let ondone s = set s in
2979 let te = name ^ ": ", "", None, textentry, ondone in
2980 state.mode <- Textentry (te, leave m_prev_mode);
2982 )) :: m_l
2984 method colorspace name get set =
2985 m_l <-
2986 (name, `string get, 1, Action (
2987 fun _ ->
2988 let source =
2989 let vals = [| "rgb"; "bgr"; "gray" |] in
2990 (object
2991 inherit lvsourcebase
2993 initializer
2994 m_active <- int_of_colorspace conf.colorspace;
2995 m_first <- 0;
2997 method getitemcount = Array.length vals
2998 method getitem n = Some (vals.(n), 0)
2999 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3000 ignore (uioh, first, pan, qsearch);
3001 if not cancel then set active;
3002 None
3003 method hasaction _ = true
3004 end)
3006 state.text <- "";
3007 new listview ~source ~trusted:true
3008 )) :: m_l
3010 method caption s offset =
3011 m_l <- (s, `empty, offset, Noaction) :: m_l
3013 method caption2 s f offset =
3014 m_l <- (s, `string f, offset, Noaction) :: m_l
3016 method getitemcount = Array.length m_a
3018 method getitem n =
3019 let tostr = function
3020 | `int f -> string_of_int (f ())
3021 | `intws f -> string_with_suffix_of_int (f ())
3022 | `string f -> f ()
3023 | `color f -> color_to_string (f ())
3024 | `bool (btos, f) -> btos (f ())
3025 | `empty -> ""
3027 let name, t, offset, _ = m_a.(n) in
3028 Some (
3029 (let s = tostr t in
3030 if String.length s > 0
3031 then Printf.sprintf "%s\t%s" name s
3032 else name),
3033 offset
3036 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3037 let uiohopt =
3038 if not cancel
3039 then (
3040 m_qsearch <- qsearch;
3041 let uioh =
3042 match m_a.(active) with
3043 | _, _, _, Action f -> f uioh
3044 | _ -> uioh
3046 Some uioh
3048 else None
3050 m_active <- active;
3051 m_first <- first;
3052 m_pan <- pan;
3053 uiohopt
3055 method hasaction n =
3056 match m_a.(n) with
3057 | _, _, _, Action _ -> true
3058 | _ -> false
3059 end)
3061 fun () ->
3062 let sep () = src#caption "" 0 in
3063 let colorp name get set =
3064 src#string name
3065 (fun () -> color_to_string (get ()))
3066 (fun v ->
3068 let c = color_of_string v in
3069 set c
3070 with exn ->
3071 state.text <- Printf.sprintf "bad color `%s': %s"
3072 v (Printexc.to_string exn);
3075 let oldmode = state.mode in
3076 let birdseye = isbirdseye state.mode in
3077 state.text <- "";
3079 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3081 src#bool "presentation mode"
3082 (fun () -> conf.presentation)
3083 (fun v ->
3084 conf.presentation <- v;
3085 state.anchor <- getanchor ();
3086 represent ());
3088 src#bool "ignore case in searches"
3089 (fun () -> conf.icase)
3090 (fun v -> conf.icase <- v);
3092 src#bool "preload"
3093 (fun () -> conf.preload)
3094 (fun v -> conf.preload <- v);
3096 src#bool "throttle"
3097 (fun () -> conf.showall)
3098 (fun v -> conf.showall <- v);
3100 src#bool "highlight links"
3101 (fun () -> conf.hlinks)
3102 (fun v -> conf.hlinks <- v);
3104 src#bool "under info"
3105 (fun () -> conf.underinfo)
3106 (fun v -> conf.underinfo <- v);
3108 src#bool "persistent bookmarks"
3109 (fun () -> conf.savebmarks)
3110 (fun v -> conf.savebmarks <- v);
3112 src#bool "proportional display"
3113 (fun () -> conf.proportional)
3114 (fun v -> reqlayout conf.angle v);
3116 src#bool "trim margins"
3117 (fun () -> conf.trimmargins)
3118 (fun v -> settrim v conf.trimfuzz);
3120 src#bool "persistent location"
3121 (fun () -> conf.jumpback)
3122 (fun v -> conf.jumpback <- v);
3124 sep ();
3125 src#int "vertical margin"
3126 (fun () -> conf.interpagespace)
3127 (fun n ->
3128 conf.interpagespace <- n;
3129 let pageno, py =
3130 match state.layout with
3131 | [] -> 0, 0
3132 | l :: _ ->
3133 l.pageno, l.pagey
3135 state.maxy <- calcheight ();
3136 let y = getpagey pageno in
3137 gotoy (y + py)
3140 src#int "page bias"
3141 (fun () -> conf.pagebias)
3142 (fun v -> conf.pagebias <- v);
3144 src#int "scroll step"
3145 (fun () -> conf.scrollstep)
3146 (fun n -> conf.scrollstep <- n);
3148 src#int "auto scroll step"
3149 (fun () ->
3150 match state.autoscroll with
3151 | Some step -> step
3152 | _ -> conf.autoscrollstep)
3153 (fun n ->
3154 if state.autoscroll <> None
3155 then state.autoscroll <- Some n;
3156 conf.autoscrollstep <- n);
3158 src#int "zoom"
3159 (fun () -> truncate (conf.zoom *. 100.))
3160 (fun v -> setzoom ((float v) /. 100.));
3162 src#int "rotation"
3163 (fun () -> conf.angle)
3164 (fun v -> reqlayout v conf.proportional);
3166 src#int "scroll bar width"
3167 (fun () -> state.scrollw)
3168 (fun v ->
3169 state.scrollw <- v;
3170 conf.scrollbw <- v;
3171 reshape conf.winw conf.winh;
3174 src#int "scroll handle height"
3175 (fun () -> conf.scrollh)
3176 (fun v -> conf.scrollh <- v;);
3178 src#int "thumbnail width"
3179 (fun () -> conf.thumbw)
3180 (fun v ->
3181 conf.thumbw <- min 4096 v;
3182 match oldmode with
3183 | Birdseye beye ->
3184 leavebirdseye beye false;
3185 enterbirdseye ()
3186 | _ -> ()
3189 sep ();
3190 src#caption "Presentation mode" 0;
3191 src#bool "scrollbar visible"
3192 (fun () -> conf.scrollbarinpm)
3193 (fun v ->
3194 if v != conf.scrollbarinpm
3195 then (
3196 conf.scrollbarinpm <- v;
3197 if conf.presentation
3198 then (
3199 state.scrollw <- if v then conf.scrollbw else 0;
3200 reshape conf.winw conf.winh;
3205 sep ();
3206 src#caption "Pixmap cache" 0;
3207 src#int_with_suffix "size (advisory)"
3208 (fun () -> conf.memlimit)
3209 (fun v -> conf.memlimit <- v);
3211 src#caption2 "used"
3212 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3213 (string_with_suffix_of_int state.memused)
3214 (Hashtbl.length state.tilemap)) 1;
3216 sep ();
3217 src#caption "Layout" 0;
3218 src#caption2 "Dimension"
3219 (fun () ->
3220 Printf.sprintf "%dx%d (virtual %dx%d)"
3221 conf.winw conf.winh
3222 state.w state.maxy)
3224 if conf.debug
3225 then
3226 src#caption2 "Position" (fun () ->
3227 Printf.sprintf "%dx%d" state.x state.y
3229 else
3230 src#caption2 "Visible" (fun () -> describe_location ()) 1
3233 sep ();
3234 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3235 "Save these parameters as global defaults at exit"
3236 (fun () -> conf.bedefault)
3237 (fun v -> conf.bedefault <- v)
3240 sep ();
3241 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3242 src#bool ~offset:0 ~btos "Extended parameters"
3243 (fun () -> !showextended)
3244 (fun v -> showextended := v; enterinfomode ());
3245 if !showextended
3246 then (
3247 src#bool "checkers"
3248 (fun () -> conf.checkers)
3249 (fun v -> conf.checkers <- v; setcheckers v);
3250 src#bool "verbose"
3251 (fun () -> conf.verbose)
3252 (fun v -> conf.verbose <- v);
3253 src#bool "invert colors"
3254 (fun () -> conf.invert)
3255 (fun v -> conf.invert <- v);
3256 src#bool "max fit"
3257 (fun () -> conf.maxhfit)
3258 (fun v -> conf.maxhfit <- v);
3259 src#string "uri launcher"
3260 (fun () -> conf.urilauncher)
3261 (fun v -> conf.urilauncher <- v);
3262 src#string "tile size"
3263 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3264 (fun v ->
3266 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3267 conf.tileh <- max 64 w;
3268 conf.tilew <- max 64 h;
3269 flushtiles ();
3270 with exn ->
3271 state.text <- Printf.sprintf "bad tile size `%s': %s"
3272 v (Printexc.to_string exn));
3273 src#int "anti-aliasing level"
3274 (fun () -> conf.aalevel)
3275 (fun v ->
3276 conf.aalevel <- bound v 0 8;
3277 state.anchor <- getanchor ();
3278 opendoc state.path state.password;
3280 src#int "ui font size"
3281 (fun () -> !uifontsize)
3282 (fun v -> uifontsize := bound v 5 100);
3283 colorp "background color"
3284 (fun () -> conf.bgcolor)
3285 (fun v -> conf.bgcolor <- v);
3286 src#bool "crop hack"
3287 (fun () -> conf.crophack)
3288 (fun v -> conf.crophack <- v);
3289 src#string "trim fuzz"
3290 (fun () -> irect_to_string conf.trimfuzz)
3291 (fun v ->
3293 conf.trimfuzz <- irect_of_string v;
3294 if conf.trimmargins
3295 then settrim true conf.trimfuzz;
3296 with exn ->
3297 state.text <- Printf.sprintf "bad irect `%s': %s"
3298 v (Printexc.to_string exn)
3300 src#colorspace "color space"
3301 (fun () -> colorspace_to_string conf.colorspace)
3302 (fun v ->
3303 conf.colorspace <- colorspace_of_int v;
3304 wcmd "cs" [`i v];
3305 load state.layout;
3309 sep ();
3310 src#caption "Document" 0;
3311 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3313 src#reset state.mode state.uioh;
3314 let source = (src :> lvsource) in
3315 state.uioh <- new listview ~source ~trusted:true;
3316 G.postRedisplay "info";
3319 let enterhelpmode =
3320 let source =
3321 (object
3322 inherit lvsourcebase
3323 method getitemcount = Array.length state.help
3324 method getitem n =
3325 let s, n, _ = state.help.(n) in
3326 Some (s, n)
3328 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3329 let optuioh =
3330 if not cancel
3331 then (
3332 m_qsearch <- qsearch;
3333 match state.help.(active) with
3334 | _, _, Action f -> Some (f uioh)
3335 | _ -> Some (uioh)
3337 else None
3339 m_active <- active;
3340 m_first <- first;
3341 m_pan <- pan;
3342 optuioh
3344 method hasaction n =
3345 match state.help.(n) with
3346 | _, _, Action _ -> true
3347 | _ -> false
3349 initializer
3350 m_active <- -1
3351 end)
3352 in fun () ->
3353 state.uioh <- new listview ~source ~trusted:true;
3354 G.postRedisplay "help";
3357 let quickbookmark ?title () =
3358 match state.layout with
3359 | [] -> ()
3360 | l :: _ ->
3361 let title =
3362 match title with
3363 | None ->
3364 let sec = Unix.gettimeofday () in
3365 let tm = Unix.localtime sec in
3366 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
3367 (l.pageno+1)
3368 tm.Unix.tm_mday
3369 tm.Unix.tm_mon
3370 (tm.Unix.tm_year + 1900)
3371 tm.Unix.tm_hour
3372 tm.Unix.tm_min
3373 | Some title -> title
3375 state.bookmarks <-
3376 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
3377 :: state.bookmarks
3380 let doreshape w h =
3381 state.fullscreen <- None;
3382 Glut.reshapeWindow w h;
3385 let viewkeyboard key =
3386 let enttext te =
3387 let mode = state.mode in
3388 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3389 state.text <- "";
3390 enttext ();
3391 G.postRedisplay "view:enttext"
3393 let c = Char.chr key in
3394 match c with
3395 | '\027' | 'q' -> (* escape *)
3396 begin match state.mstate with
3397 | Mzoomrect _ ->
3398 state.mstate <- Mnone;
3399 Glut.setCursor Glut.CURSOR_INHERIT;
3400 G.postRedisplay "kill zoom rect";
3401 | _ ->
3402 raise Quit
3403 end;
3405 | '\008' -> (* backspace *)
3406 let y = getnav ~-1 in
3407 gotoy_and_clear_text y
3409 | 'o' ->
3410 enteroutlinemode ()
3412 | 'u' ->
3413 state.rects <- [];
3414 state.text <- "";
3415 G.postRedisplay "dehighlight";
3417 | '/' | '?' ->
3418 let ondone isforw s =
3419 cbput state.hists.pat s;
3420 state.searchpattern <- s;
3421 search s isforw
3423 let s = String.create 1 in
3424 s.[0] <- c;
3425 enttext (s, "", Some (onhist state.hists.pat),
3426 textentry, ondone (c ='/'))
3428 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3429 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3430 setzoom (conf.zoom +. incr)
3432 | '+' ->
3433 let ondone s =
3434 let n =
3435 try int_of_string s with exc ->
3436 state.text <- Printf.sprintf "bad integer `%s': %s"
3437 s (Printexc.to_string exc);
3438 max_int
3440 if n != max_int
3441 then (
3442 conf.pagebias <- n;
3443 state.text <- "page bias is now " ^ string_of_int n;
3446 enttext ("page bias: ", "", None, intentry, ondone)
3448 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3449 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3450 setzoom (max 0.01 (conf.zoom -. decr))
3452 | '-' ->
3453 let ondone msg = state.text <- msg in
3454 enttext (
3455 "option [acfhilpstvAPRSZTI]: ", "", None,
3456 optentry state.mode, ondone
3459 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3460 setzoom 1.0
3462 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3463 let zoom = zoomforh conf.winw conf.winh state.scrollw in
3464 if zoom < 1.0
3465 then setzoom zoom
3467 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3468 togglebirdseye ()
3470 | '0' .. '9' ->
3471 let ondone s =
3472 let n =
3473 try int_of_string s with exc ->
3474 state.text <- Printf.sprintf "bad integer `%s': %s"
3475 s (Printexc.to_string exc);
3478 if n >= 0
3479 then (
3480 addnav ();
3481 cbput state.hists.pag (string_of_int n);
3482 gotoy_and_clear_text (getpagey (n + conf.pagebias - 1))
3485 let pageentry text key =
3486 match Char.unsafe_chr key with
3487 | 'g' -> TEdone text
3488 | _ -> intentry text key
3490 let text = "x" in text.[0] <- c;
3491 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
3493 | 'b' ->
3494 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
3495 reshape conf.winw conf.winh;
3497 | 'l' ->
3498 conf.hlinks <- not conf.hlinks;
3499 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3500 G.postRedisplay "toggle highlightlinks";
3502 | 'a' ->
3503 begin match state.autoscroll with
3504 | Some step ->
3505 conf.autoscrollstep <- step;
3506 state.autoscroll <- None
3507 | None ->
3508 if conf.autoscrollstep = 0
3509 then state.autoscroll <- Some 1
3510 else state.autoscroll <- Some conf.autoscrollstep
3513 | 'P' ->
3514 conf.presentation <- not conf.presentation;
3515 if conf.presentation
3516 then (
3517 if not conf.scrollbarinpm
3518 then state.scrollw <- 0;
3520 else
3521 state.scrollw <- conf.scrollbw;
3523 showtext ' ' ("presentation mode " ^
3524 if conf.presentation then "on" else "off");
3525 state.anchor <- getanchor ();
3526 represent ()
3528 | 'f' ->
3529 begin match state.fullscreen with
3530 | None ->
3531 state.fullscreen <- Some (conf.winw, conf.winh);
3532 Glut.fullScreen ()
3533 | Some (w, h) ->
3534 state.fullscreen <- None;
3535 doreshape w h
3538 | 'g' ->
3539 gotoy_and_clear_text 0
3541 | 'G' ->
3542 gotopage1 (state.pagecount - 1) 0
3544 | 'n' ->
3545 search state.searchpattern true
3547 | 'p' | 'N' ->
3548 search state.searchpattern false
3550 | 't' ->
3551 begin match state.layout with
3552 | [] -> ()
3553 | l :: _ ->
3554 gotoy_and_clear_text (getpagey l.pageno)
3557 | ' ' ->
3558 begin match List.rev state.layout with
3559 | [] -> ()
3560 | l :: _ ->
3561 let pageno = min (l.pageno+1) (state.pagecount-1) in
3562 gotoy_and_clear_text (getpagey pageno)
3565 | '\127' -> (* del *)
3566 begin match state.layout with
3567 | [] -> ()
3568 | l :: _ ->
3569 let pageno = max 0 (l.pageno-1) in
3570 gotoy_and_clear_text (getpagey pageno)
3573 | '=' ->
3574 showtext ' ' (describe_location ());
3576 | 'w' ->
3577 begin match state.layout with
3578 | [] -> ()
3579 | l :: _ ->
3580 doreshape (l.pagew + state.scrollw) l.pageh;
3581 G.postRedisplay "w"
3584 | '\'' ->
3585 enterbookmarkmode ()
3587 | 'h' ->
3588 enterhelpmode ()
3590 | 'i' ->
3591 enterinfomode ()
3593 | 'm' ->
3594 let ondone s =
3595 match state.layout with
3596 | l :: _ ->
3597 state.bookmarks <-
3598 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
3599 :: state.bookmarks
3600 | _ -> ()
3602 enttext ("bookmark: ", "", None, textentry, ondone)
3604 | '~' ->
3605 quickbookmark ();
3606 showtext ' ' "Quick bookmark added";
3608 | 'z' ->
3609 begin match state.layout with
3610 | l :: _ ->
3611 let rect = getpdimrect l.pagedimno in
3612 let w, h =
3613 if conf.crophack
3614 then
3615 (truncate (1.8 *. (rect.(1) -. rect.(0))),
3616 truncate (1.2 *. (rect.(3) -. rect.(0))))
3617 else
3618 (truncate (rect.(1) -. rect.(0)),
3619 truncate (rect.(3) -. rect.(0)))
3621 let w = truncate ((float w)*.conf.zoom)
3622 and h = truncate ((float h)*.conf.zoom) in
3623 if w != 0 && h != 0
3624 then (
3625 state.anchor <- getanchor ();
3626 doreshape (w + state.scrollw) (h + conf.interpagespace)
3628 G.postRedisplay "z";
3630 | [] -> ()
3633 | '\000' -> (* ctrl-2 *)
3634 let maxw = getmaxw () in
3635 if maxw > 0.0
3636 then setzoom (maxw /. float conf.winw)
3638 | '<' | '>' ->
3639 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
3641 | '[' | ']' ->
3642 state.colorscale <-
3643 bound (state.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
3645 G.postRedisplay "brightness";
3647 | 'k' ->
3648 begin match state.mode with
3649 | Birdseye beye -> upbirdseye beye
3650 | _ -> gotoy (clamp (-conf.scrollstep))
3653 | 'j' ->
3654 begin match state.mode with
3655 | Birdseye beye -> downbirdseye beye
3656 | _ -> gotoy (clamp conf.scrollstep)
3659 | 'r' ->
3660 state.anchor <- getanchor ();
3661 opendoc state.path state.password
3663 | 'v' when conf.debug ->
3664 state.rects <- [];
3665 List.iter (fun l ->
3666 match getopaque l.pageno with
3667 | None -> ()
3668 | Some opaque ->
3669 let x0, y0, x1, y1 = pagebbox opaque in
3670 let a,b = float x0, float y0 in
3671 let c,d = float x1, float y0 in
3672 let e,f = float x1, float y1 in
3673 let h,j = float x0, float y1 in
3674 let rect = (a,b,c,d,e,f,h,j) in
3675 debugrect rect;
3676 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
3677 ) state.layout;
3678 G.postRedisplay "v";
3680 | _ ->
3681 vlog "huh? %d %c" key (Char.chr key);
3684 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
3685 match key with
3686 | 27 -> (* escape *)
3687 leavebirdseye beye true
3689 | 12 -> (* ctrl-l *)
3690 let y, h = getpageyh pageno in
3691 let top = (conf.winh - h) / 2 in
3692 gotoy (max 0 (y - top))
3694 | 13 -> (* enter *)
3695 leavebirdseye beye false
3697 | _ ->
3698 viewkeyboard key
3701 let keyboard ~key ~x ~y =
3702 ignore x;
3703 ignore y;
3704 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
3705 then wcmd "interrupt" []
3706 else state.uioh <- state.uioh#key key
3709 let birdseyespecial key ((conf, leftx, _, hooverpageno, anchor) as beye) =
3710 match key with
3711 | Glut.KEY_UP -> upbirdseye beye
3712 | Glut.KEY_DOWN -> downbirdseye beye
3714 | Glut.KEY_PAGE_UP ->
3715 begin match state.layout with
3716 | l :: _ ->
3717 if l.pagey != 0
3718 then (
3719 state.mode <- Birdseye (
3720 conf, leftx, l.pageno, hooverpageno, anchor
3722 gotopage1 l.pageno 0;
3724 else (
3725 let layout = layout (state.y-conf.winh) conf.winh in
3726 match layout with
3727 | [] -> gotoy (clamp (-conf.winh))
3728 | l :: _ ->
3729 state.mode <- Birdseye (
3730 conf, leftx, l.pageno, hooverpageno, anchor
3732 gotopage1 l.pageno 0
3735 | [] -> gotoy (clamp (-conf.winh))
3736 end;
3738 | Glut.KEY_PAGE_DOWN ->
3739 begin match List.rev state.layout with
3740 | l :: _ ->
3741 let layout = layout (state.y + conf.winh) conf.winh in
3742 begin match layout with
3743 | [] ->
3744 let incr = l.pageh - l.pagevh in
3745 if incr = 0
3746 then (
3747 state.mode <-
3748 Birdseye (
3749 conf, leftx, state.pagecount - 1, hooverpageno, anchor
3751 G.postRedisplay "birdseye pagedown";
3753 else gotoy (clamp (incr + conf.interpagespace*2));
3755 | l :: _ ->
3756 state.mode <-
3757 Birdseye (conf, leftx, l.pageno, hooverpageno, anchor);
3758 gotopage1 l.pageno 0;
3761 | [] -> gotoy (clamp conf.winh)
3762 end;
3764 | Glut.KEY_HOME ->
3765 state.mode <- Birdseye (conf, leftx, 0, hooverpageno, anchor);
3766 gotopage1 0 0
3768 | Glut.KEY_END ->
3769 let pageno = state.pagecount - 1 in
3770 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
3771 if not (pagevisible state.layout pageno)
3772 then
3773 let h =
3774 match List.rev state.pdims with
3775 | [] -> conf.winh
3776 | (_, _, h, _) :: _ -> h
3778 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
3779 else G.postRedisplay "birdseye end";
3780 | _ -> ()
3783 let setautoscrollspeed step goingdown =
3784 let incr = max 1 ((abs step) / 2) in
3785 let incr = if goingdown then incr else -incr in
3786 let astep = step + incr in
3787 state.autoscroll <- Some astep;
3790 let special ~key ~x ~y =
3791 ignore x;
3792 ignore y;
3793 state.uioh <- state.uioh#special key
3796 let drawpage l =
3797 let color =
3798 match state.mode with
3799 | Textentry _ -> scalecolor 0.4
3800 | View -> scalecolor 1.0
3801 | Birdseye (_, _, pageno, hooverpageno, _) ->
3802 if l.pageno = hooverpageno
3803 then scalecolor 0.9
3804 else (
3805 if l.pageno = pageno
3806 then scalecolor 1.0
3807 else scalecolor 0.8
3810 drawtiles l color;
3811 begin match getopaque l.pageno with
3812 | Some opaque ->
3813 if tileready l l.pagex l.pagey
3814 then
3815 let x = l.pagedispx - l.pagex
3816 and y = l.pagedispy - l.pagey in
3817 postprocess opaque conf.hlinks x y;
3819 | _ -> ()
3820 end;
3823 let scrollph y =
3824 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3825 let sh = (float (maxy + conf.winh) /. float conf.winh) in
3826 let sh = float conf.winh /. sh in
3827 let sh = max sh (float conf.scrollh) in
3829 let percent =
3830 if y = state.maxy
3831 then 1.0
3832 else float y /. float maxy
3834 let position = (float conf.winh -. sh) *. percent in
3836 let position =
3837 if position +. sh > float conf.winh
3838 then float conf.winh -. sh
3839 else position
3841 position, sh;
3844 let scrollpw x =
3845 let winw = conf.winw - state.scrollw - 1 in
3846 let fwinw = float winw in
3847 let sw =
3848 let sw = fwinw /. float state.w in
3849 let sw = fwinw *. sw in
3850 max sw (float conf.scrollh)
3852 let position, sw =
3853 let f = state.w+winw in
3854 let r = float (winw-x) /. float f in
3855 let p = fwinw *. r in
3856 p-.sw/.2., sw
3858 let sw =
3859 if position +. sw > fwinw
3860 then fwinw -. position
3861 else sw
3863 position, sw;
3866 let scrollindicator () =
3867 GlDraw.color (0.64 , 0.64, 0.64);
3868 GlDraw.rect
3869 (float (conf.winw - state.scrollw), 0.)
3870 (float conf.winw, float conf.winh)
3872 GlDraw.rect
3873 (0., float (conf.winh - state.hscrollh))
3874 (float (conf.winw - state.scrollw - 1), float conf.winh)
3876 GlDraw.color (0.0, 0.0, 0.0);
3878 let position, sh = scrollph state.y in
3879 GlDraw.rect
3880 (float (conf.winw - state.scrollw), position)
3881 (float conf.winw, position +. sh)
3883 let position, sw = scrollpw state.x in
3884 GlDraw.rect
3885 (position, float (conf.winh - state.hscrollh))
3886 (position +. sw, float conf.winh)
3890 let pagetranslatepoint l x y =
3891 let dy = y - l.pagedispy in
3892 let y = dy + l.pagey in
3893 let dx = x - l.pagedispx in
3894 let x = dx + l.pagex in
3895 (x, y);
3898 let showsel () =
3899 match state.mstate with
3900 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
3903 | Msel ((x0, y0), (x1, y1)) ->
3904 let rec loop = function
3905 | l :: ls ->
3906 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
3907 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
3908 then
3909 match getopaque l.pageno with
3910 | Some opaque ->
3911 let dx, dy = pagetranslatepoint l 0 0 in
3912 let x0 = x0 + dx
3913 and y0 = y0 + dy
3914 and x1 = x1 + dx
3915 and y1 = y1 + dy in
3916 GlMat.mode `modelview;
3917 GlMat.push ();
3918 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
3919 seltext opaque (x0, y0, x1, y1);
3920 GlMat.pop ();
3921 | _ -> ()
3922 else loop ls
3923 | [] -> ()
3925 loop state.layout
3928 let showrects () =
3929 Gl.enable `blend;
3930 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
3931 GlDraw.polygon_mode `both `fill;
3932 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3933 List.iter
3934 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
3935 List.iter (fun l ->
3936 if l.pageno = pageno
3937 then (
3938 let dx = float (l.pagedispx - l.pagex) in
3939 let dy = float (l.pagedispy - l.pagey) in
3940 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
3941 GlDraw.begins `quads;
3943 GlDraw.vertex2 (x0+.dx, y0+.dy);
3944 GlDraw.vertex2 (x1+.dx, y1+.dy);
3945 GlDraw.vertex2 (x2+.dx, y2+.dy);
3946 GlDraw.vertex2 (x3+.dx, y3+.dy);
3948 GlDraw.ends ();
3950 ) state.layout
3951 ) state.rects
3953 Gl.disable `blend;
3956 let display () =
3957 GlClear.color (scalecolor2 conf.bgcolor);
3958 GlClear.clear [`color];
3959 List.iter drawpage state.layout;
3960 showrects ();
3961 showsel ();
3962 scrollindicator ();
3963 state.uioh#display;
3964 begin match state.mstate with
3965 | Mzoomrect ((x0, y0), (x1, y1)) ->
3966 Gl.enable `blend;
3967 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
3968 GlDraw.polygon_mode `both `fill;
3969 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3970 GlDraw.rect (float x0, float y0)
3971 (float x1, float y1);
3972 Gl.disable `blend;
3973 | _ -> ()
3974 end;
3975 enttext ();
3976 Glut.swapBuffers ();
3979 let getunder x y =
3980 let rec f = function
3981 | l :: rest ->
3982 begin match getopaque l.pageno with
3983 | Some opaque ->
3984 let x0 = l.pagedispx in
3985 let x1 = x0 + l.pagevw in
3986 let y0 = l.pagedispy in
3987 let y1 = y0 + l.pagevh in
3988 if y >= y0 && y <= y1 && x >= x0 && x <= x1
3989 then
3990 let px, py = pagetranslatepoint l x y in
3991 match whatsunder opaque px py with
3992 | Unone -> f rest
3993 | under -> under
3994 else f rest
3995 | _ ->
3996 f rest
3998 | [] -> Unone
4000 f state.layout
4003 let zoomrect x y x1 y1 =
4004 let x0 = min x x1
4005 and x1 = max x x1
4006 and y0 = min y y1 in
4007 gotoy (state.y + y0);
4008 state.anchor <- getanchor ();
4009 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4010 state.x <- state.x - x0;
4011 setzoom zoom;
4012 Glut.setCursor Glut.CURSOR_INHERIT;
4013 state.mstate <- Mnone;
4016 let scrollx x =
4017 let winw = conf.winw - state.scrollw - 1 in
4018 let s = float x /. float winw in
4019 let destx = truncate (float (state.w + winw) *. s) in
4020 state.x <- winw - destx;
4021 gotoy_and_clear_text state.y;
4022 state.mstate <- Mscrollx;
4025 let scrolly y =
4026 let s = float y /. float conf.winh in
4027 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4028 gotoy_and_clear_text desty;
4029 state.mstate <- Mscrolly;
4032 let viewmouse button bstate x y =
4033 match button with
4034 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4035 if Glut.getModifiers () land Glut.active_ctrl != 0
4036 then (
4037 match state.mstate with
4038 | Mzoom (oldn, i) ->
4039 if oldn = n
4040 then (
4041 if i = 2
4042 then
4043 let incr =
4044 match n with
4045 | 4 ->
4046 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4047 | _ ->
4048 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4050 let zoom = conf.zoom -. incr in
4051 setzoom zoom;
4052 state.mstate <- Mzoom (n, 0);
4053 else
4054 state.mstate <- Mzoom (n, i+1);
4056 else state.mstate <- Mzoom (n, 0)
4058 | _ -> state.mstate <- Mzoom (n, 0)
4060 else (
4061 match state.autoscroll with
4062 | Some step -> setautoscrollspeed step (n=4)
4063 | None ->
4064 let incr =
4065 if n = 3
4066 then -conf.scrollstep
4067 else conf.scrollstep
4069 let incr = incr * 2 in
4070 let y = clamp incr in
4071 gotoy_and_clear_text y
4074 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4075 if bstate = Glut.DOWN
4076 then (
4077 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4078 state.mstate <- Mpan (x, y)
4080 else
4081 state.mstate <- Mnone
4083 | Glut.RIGHT_BUTTON ->
4084 if bstate = Glut.DOWN
4085 then (
4086 Glut.setCursor Glut.CURSOR_CYCLE;
4087 let p = (x, y) in
4088 state.mstate <- Mzoomrect (p, p)
4090 else (
4091 match state.mstate with
4092 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4093 | _ ->
4094 Glut.setCursor Glut.CURSOR_INHERIT;
4095 state.mstate <- Mnone
4098 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4099 if bstate = Glut.DOWN
4100 then
4101 let position, sh = scrollph state.y in
4102 if y > truncate position && y < truncate (position +. sh)
4103 then state.mstate <- Mscrolly
4104 else scrolly y
4105 else
4106 state.mstate <- Mnone
4108 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4109 if bstate = Glut.DOWN
4110 then
4111 let position, sw = scrollpw state.x in
4112 if x > truncate position && x < truncate (position +. sw)
4113 then state.mstate <- Mscrollx
4114 else scrollx x
4115 else
4116 state.mstate <- Mnone
4118 | Glut.LEFT_BUTTON ->
4119 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4120 begin match dest with
4121 | Ulinkgoto (pageno, top) ->
4122 if pageno >= 0
4123 then (
4124 addnav ();
4125 gotopage1 pageno top;
4128 | Ulinkuri s ->
4129 gotouri s
4131 | Unone when bstate = Glut.DOWN ->
4132 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4133 state.mstate <- Mpan (x, y);
4135 | Unone | Utext _ ->
4136 if bstate = Glut.DOWN
4137 then (
4138 if conf.angle mod 360 = 0
4139 then (
4140 state.mstate <- Msel ((x, y), (x, y));
4141 G.postRedisplay "mouse select";
4144 else (
4145 match state.mstate with
4146 | Mnone -> ()
4148 | Mzoom _ | Mscrollx | Mscrolly ->
4149 state.mstate <- Mnone
4151 | Mzoomrect ((x0, y0), _) ->
4152 zoomrect x0 y0 x y
4154 | Mpan _ ->
4155 Glut.setCursor Glut.CURSOR_INHERIT;
4156 state.mstate <- Mnone
4158 | Msel ((_, y0), (_, y1)) ->
4159 let f l =
4160 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4161 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4162 then
4163 match getopaque l.pageno with
4164 | Some opaque ->
4165 copysel opaque
4166 | _ -> ()
4168 List.iter f state.layout;
4169 copysel ""; (* ugly *)
4170 Glut.setCursor Glut.CURSOR_INHERIT;
4171 state.mstate <- Mnone;
4175 | _ -> ()
4178 let birdseyemouse button bstate x y
4179 (conf, leftx, _, hooverpageno, anchor) =
4180 match button with
4181 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4182 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4183 let rec loop = function
4184 | [] -> ()
4185 | l :: rest ->
4186 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4187 && x > margin && x < margin + l.pagew
4188 then (
4189 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4191 else loop rest
4193 loop state.layout
4194 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
4195 | _ -> ()
4198 let mouse bstate button x y =
4199 state.uioh <- state.uioh#button button bstate x y;
4202 let mouse ~button ~state ~x ~y = mouse state button x y;;
4204 let motion ~x ~y =
4205 state.uioh <- state.uioh#motion x y
4208 let pmotion ~x ~y =
4209 state.uioh <- state.uioh#pmotion x y;
4212 let uioh = object
4213 method display = ()
4215 method key key =
4216 begin match state.mode with
4217 | Textentry textentry -> textentrykeyboard key textentry
4218 | Birdseye birdseye -> birdseyekeyboard key birdseye
4219 | View -> viewkeyboard key
4220 end;
4221 state.uioh
4223 method special key =
4224 begin match state.mode with
4225 | View | (Birdseye _) when key = Glut.KEY_F9 ->
4226 togglebirdseye ()
4228 | Birdseye vals ->
4229 birdseyespecial key vals
4231 | View when key = Glut.KEY_F1 ->
4232 enterhelpmode ()
4234 | View ->
4235 begin match state.autoscroll with
4236 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
4237 setautoscrollspeed step (key = Glut.KEY_DOWN)
4239 | _ ->
4240 let y =
4241 match key with
4242 | Glut.KEY_F3 -> search state.searchpattern true; state.y
4243 | Glut.KEY_UP ->
4244 if Glut.getModifiers () land Glut.active_ctrl != 0
4245 then
4246 if Glut.getModifiers () land Glut.active_shift != 0
4247 then (setzoom state.prevzoom; state.y)
4248 else clamp (-conf.winh/2)
4249 else clamp (-conf.scrollstep)
4250 | Glut.KEY_DOWN ->
4251 if Glut.getModifiers () land Glut.active_ctrl != 0
4252 then
4253 if Glut.getModifiers () land Glut.active_shift != 0
4254 then (setzoom state.prevzoom; state.y)
4255 else clamp (conf.winh/2)
4256 else clamp (conf.scrollstep)
4257 | Glut.KEY_PAGE_UP ->
4258 if Glut.getModifiers () land Glut.active_ctrl != 0
4259 then
4260 match state.layout with
4261 | [] -> state.y
4262 | l :: _ -> state.y - l.pagey
4263 else
4264 clamp (-conf.winh)
4265 | Glut.KEY_PAGE_DOWN ->
4266 if Glut.getModifiers () land Glut.active_ctrl != 0
4267 then
4268 match List.rev state.layout with
4269 | [] -> state.y
4270 | l :: _ -> getpagey l.pageno
4271 else
4272 clamp conf.winh
4273 | Glut.KEY_HOME ->
4274 addnav ();
4276 | Glut.KEY_END ->
4277 addnav ();
4278 state.maxy - (if conf.maxhfit then conf.winh else 0)
4280 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
4281 Glut.getModifiers () land Glut.active_alt != 0 ->
4282 getnav (if key = Glut.KEY_LEFT then 1 else -1)
4284 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
4285 let dx =
4286 if Glut.getModifiers () land Glut.active_ctrl != 0
4287 then (conf.winw / 2)
4288 else 10
4290 state.x <- state.x - dx;
4291 state.y
4292 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
4293 let dx =
4294 if Glut.getModifiers () land Glut.active_ctrl != 0
4295 then (conf.winw / 2)
4296 else 10
4298 state.x <- state.x + dx;
4299 state.y
4301 | _ -> state.y
4303 gotoy_and_clear_text y
4306 | Textentry te -> textentryspecial key te
4307 end;
4308 state.uioh
4310 method button button bstate x y =
4311 begin match state.mode with
4312 | View -> viewmouse button bstate x y
4313 | Birdseye beye -> birdseyemouse button bstate x y beye
4314 | Textentry _ -> ()
4315 end;
4316 state.uioh
4318 method motion x y =
4319 begin match state.mode with
4320 | Textentry _ -> ()
4321 | View | Birdseye _ ->
4322 match state.mstate with
4323 | Mzoom _ | Mnone -> ()
4325 | Mpan (x0, y0) ->
4326 let dx = x - x0
4327 and dy = y0 - y in
4328 state.mstate <- Mpan (x, y);
4329 if conf.zoom > 1.0 then state.x <- state.x + dx;
4330 let y = clamp dy in
4331 gotoy_and_clear_text y
4333 | Msel (a, _) ->
4334 state.mstate <- Msel (a, (x, y));
4335 G.postRedisplay "motion select";
4337 | Mscrolly ->
4338 let y = min conf.winh (max 0 y) in
4339 scrolly y
4341 | Mscrollx ->
4342 let x = min conf.winw (max 0 x) in
4343 scrollx x
4345 | Mzoomrect (p0, _) ->
4346 state.mstate <- Mzoomrect (p0, (x, y));
4347 G.postRedisplay "motion zoomrect";
4348 end;
4349 state.uioh
4351 method pmotion x y =
4352 begin match state.mode with
4353 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4354 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4355 let rec loop = function
4356 | [] ->
4357 if hooverpageno != -1
4358 then (
4359 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4360 G.postRedisplay "pmotion birdseye no hoover";
4362 | l :: rest ->
4363 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4364 && x > margin && x < margin + l.pagew
4365 then (
4366 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4367 G.postRedisplay "pmotion birdseye hoover";
4369 else loop rest
4371 loop state.layout
4373 | Textentry _ -> ()
4375 | View ->
4376 match state.mstate with
4377 | Mnone ->
4378 begin match getunder x y with
4379 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
4380 | Ulinkuri uri ->
4381 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
4382 Glut.setCursor Glut.CURSOR_INFO
4383 | Ulinkgoto (page, _) ->
4384 if conf.underinfo
4385 then showtext 'p' ("age: " ^ string_of_int (page+1));
4386 Glut.setCursor Glut.CURSOR_INFO
4387 | Utext s ->
4388 if conf.underinfo then showtext 'f' ("ont: " ^ s);
4389 Glut.setCursor Glut.CURSOR_TEXT
4392 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
4394 end;
4395 state.uioh
4396 end;;
4398 module Config =
4399 struct
4400 open Parser
4402 let fontpath = ref "";;
4403 let wmclasshack = ref false;;
4405 let unent s =
4406 let l = String.length s in
4407 let b = Buffer.create l in
4408 unent b s 0 l;
4409 Buffer.contents b;
4412 let home =
4414 match platform with
4415 | Pwindows | Pmingw -> Sys.getenv "HOMEPATH"
4416 | _ -> Sys.getenv "HOME"
4417 with exn ->
4418 prerr_endline
4419 ("Can not determine home directory location: " ^
4420 Printexc.to_string exn);
4424 let config_of c attrs =
4425 let apply c k v =
4427 match k with
4428 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
4429 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
4430 | "case-insensitive-search" -> { c with icase = bool_of_string v }
4431 | "preload" -> { c with preload = bool_of_string v }
4432 | "page-bias" -> { c with pagebias = int_of_string v }
4433 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
4434 | "auto-scroll-step" ->
4435 { c with autoscrollstep = max 0 (int_of_string v) }
4436 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
4437 | "crop-hack" -> { c with crophack = bool_of_string v }
4438 | "throttle" -> { c with showall = bool_of_string v }
4439 | "highlight-links" -> { c with hlinks = bool_of_string v }
4440 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
4441 | "vertical-margin" ->
4442 { c with interpagespace = max 0 (int_of_string v) }
4443 | "zoom" ->
4444 let zoom = float_of_string v /. 100. in
4445 let zoom = max zoom 0.0 in
4446 { c with zoom = zoom }
4447 | "presentation" -> { c with presentation = bool_of_string v }
4448 | "rotation-angle" -> { c with angle = int_of_string v }
4449 | "width" -> { c with winw = max 20 (int_of_string v) }
4450 | "height" -> { c with winh = max 20 (int_of_string v) }
4451 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
4452 | "proportional-display" -> { c with proportional = bool_of_string v }
4453 | "pixmap-cache-size" ->
4454 { c with memlimit = max 2 (int_of_string_with_suffix v) }
4455 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
4456 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
4457 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
4458 | "persistent-location" -> { c with jumpback = bool_of_string v }
4459 | "background-color" -> { c with bgcolor = color_of_string v }
4460 | "scrollbar-in-presentation" ->
4461 { c with scrollbarinpm = bool_of_string v }
4462 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
4463 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
4464 | "memlimit" ->
4465 { c with mumemlimit = max 1024 (int_of_string_with_suffix v) }
4466 | "checkers" -> { c with checkers = bool_of_string v }
4467 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
4468 | "trim-margins" -> { c with trimmargins = bool_of_string v }
4469 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
4470 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
4471 | "uri-launcher" -> { c with urilauncher = unent v }
4472 | "color-space" -> { c with colorspace = colorspace_of_string v }
4473 | "invert-colors" -> { c with invert = bool_of_string v }
4474 | _ -> c
4475 with exn ->
4476 prerr_endline ("Error processing attribute (`" ^
4477 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
4480 let rec fold c = function
4481 | [] -> c
4482 | (k, v) :: rest ->
4483 let c = apply c k v in
4484 fold c rest
4486 fold c attrs;
4489 let fromstring f pos n v d =
4490 try f v
4491 with exn ->
4492 dolog "Error processing attribute (%S=%S) at %d\n%s"
4493 n v pos (Printexc.to_string exn)
4498 let bookmark_of attrs =
4499 let rec fold title page rely = function
4500 | ("title", v) :: rest -> fold v page rely rest
4501 | ("page", v) :: rest -> fold title v rely rest
4502 | ("rely", v) :: rest -> fold title page v rest
4503 | _ :: rest -> fold title page rely rest
4504 | [] -> title, page, rely
4506 fold "invalid" "0" "0" attrs
4509 let doc_of attrs =
4510 let rec fold path page rely pan = function
4511 | ("path", v) :: rest -> fold v page rely pan rest
4512 | ("page", v) :: rest -> fold path v rely pan rest
4513 | ("rely", v) :: rest -> fold path page v pan rest
4514 | ("pan", v) :: rest -> fold path page rely v rest
4515 | _ :: rest -> fold path page rely pan rest
4516 | [] -> path, page, rely, pan
4518 fold "" "0" "0" "0" attrs
4521 let setconf dst src =
4522 dst.scrollbw <- src.scrollbw;
4523 dst.scrollh <- src.scrollh;
4524 dst.icase <- src.icase;
4525 dst.preload <- src.preload;
4526 dst.pagebias <- src.pagebias;
4527 dst.verbose <- src.verbose;
4528 dst.scrollstep <- src.scrollstep;
4529 dst.maxhfit <- src.maxhfit;
4530 dst.crophack <- src.crophack;
4531 dst.autoscrollstep <- src.autoscrollstep;
4532 dst.showall <- src.showall;
4533 dst.hlinks <- src.hlinks;
4534 dst.underinfo <- src.underinfo;
4535 dst.interpagespace <- src.interpagespace;
4536 dst.zoom <- src.zoom;
4537 dst.presentation <- src.presentation;
4538 dst.angle <- src.angle;
4539 dst.winw <- src.winw;
4540 dst.winh <- src.winh;
4541 dst.savebmarks <- src.savebmarks;
4542 dst.memlimit <- src.memlimit;
4543 dst.proportional <- src.proportional;
4544 dst.texcount <- src.texcount;
4545 dst.sliceheight <- src.sliceheight;
4546 dst.thumbw <- src.thumbw;
4547 dst.jumpback <- src.jumpback;
4548 dst.bgcolor <- src.bgcolor;
4549 dst.scrollbarinpm <- src.scrollbarinpm;
4550 dst.tilew <- src.tilew;
4551 dst.tileh <- src.tileh;
4552 dst.mumemlimit <- src.mumemlimit;
4553 dst.checkers <- src.checkers;
4554 dst.aalevel <- src.aalevel;
4555 dst.trimmargins <- src.trimmargins;
4556 dst.trimfuzz <- src.trimfuzz;
4557 dst.urilauncher <- src.urilauncher;
4558 dst.colorspace <- src.colorspace;
4559 dst.invert <- src.invert;
4562 let get s =
4563 let h = Hashtbl.create 10 in
4564 let dc = { defconf with angle = defconf.angle } in
4565 let rec toplevel v t spos _ =
4566 match t with
4567 | Vdata | Vcdata | Vend -> v
4568 | Vopen ("llppconfig", _, closed) ->
4569 if closed
4570 then v
4571 else { v with f = llppconfig }
4572 | Vopen _ ->
4573 error "unexpected subelement at top level" s spos
4574 | Vclose _ -> error "unexpected close at top level" s spos
4576 and llppconfig v t spos _ =
4577 match t with
4578 | Vdata | Vcdata -> v
4579 | Vend -> error "unexpected end of input in llppconfig" s spos
4580 | Vopen ("defaults", attrs, closed) ->
4581 let c = config_of dc attrs in
4582 setconf dc c;
4583 if closed
4584 then v
4585 else { v with f = skip "defaults" (fun () -> v) }
4587 | Vopen ("ui-font", attrs, closed) ->
4588 let rec getsize size = function
4589 | [] -> size
4590 | ("size", v) :: rest ->
4591 let size =
4592 fromstring int_of_string spos "size" v !uifontsize in
4593 getsize size rest
4594 | l -> getsize size l
4596 uifontsize := getsize !uifontsize attrs;
4597 if closed
4598 then v
4599 else { v with f = uifont (Buffer.create 10) }
4601 | Vopen ("doc", attrs, closed) ->
4602 let pathent, spage, srely, span = doc_of attrs in
4603 let path = unent pathent
4604 and pageno = fromstring int_of_string spos "page" spage 0
4605 and rely = fromstring float_of_string spos "rely" srely 0.0
4606 and pan = fromstring int_of_string spos "pan" span 0 in
4607 let c = config_of dc attrs in
4608 let anchor = (pageno, rely) in
4609 if closed
4610 then (Hashtbl.add h path (c, [], pan, anchor); v)
4611 else { v with f = doc path pan anchor c [] }
4613 | Vopen _ ->
4614 error "unexpected subelement in llppconfig" s spos
4616 | Vclose "llppconfig" -> { v with f = toplevel }
4617 | Vclose _ -> error "unexpected close in llppconfig" s spos
4619 and uifont b v t spos epos =
4620 match t with
4621 | Vdata | Vcdata ->
4622 Buffer.add_substring b s spos (epos - spos);
4624 | Vopen (_, _, _) ->
4625 error "unexpected subelement in ui-font" s spos
4626 | Vclose "ui-font" ->
4627 if String.length !fontpath = 0
4628 then fontpath := Buffer.contents b;
4629 { v with f = llppconfig }
4630 | Vclose _ -> error "unexpected close in ui-font" s spos
4631 | Vend -> error "unexpected end of input in ui-font" s spos
4633 and doc path pan anchor c bookmarks v t spos _ =
4634 match t with
4635 | Vdata | Vcdata -> v
4636 | Vend -> error "unexpected end of input in doc" s spos
4637 | Vopen ("bookmarks", _, closed) ->
4638 if closed
4639 then v
4640 else { v with f = pbookmarks path pan anchor c bookmarks }
4642 | Vopen (_, _, _) ->
4643 error "unexpected subelement in doc" s spos
4645 | Vclose "doc" ->
4646 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
4647 { v with f = llppconfig }
4649 | Vclose _ -> error "unexpected close in doc" s spos
4651 and pbookmarks path pan anchor c bookmarks v t spos _ =
4652 match t with
4653 | Vdata | Vcdata -> v
4654 | Vend -> error "unexpected end of input in bookmarks" s spos
4655 | Vopen ("item", attrs, closed) ->
4656 let titleent, spage, srely = bookmark_of attrs in
4657 let page = fromstring int_of_string spos "page" spage 0
4658 and rely = fromstring float_of_string spos "rely" srely 0.0 in
4659 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
4660 if closed
4661 then { v with f = pbookmarks path pan anchor c bookmarks }
4662 else
4663 let f () = v in
4664 { v with f = skip "item" f }
4666 | Vopen _ ->
4667 error "unexpected subelement in bookmarks" s spos
4669 | Vclose "bookmarks" ->
4670 { v with f = doc path pan anchor c bookmarks }
4672 | Vclose _ -> error "unexpected close in bookmarks" s spos
4674 and skip tag f v t spos _ =
4675 match t with
4676 | Vdata | Vcdata -> v
4677 | Vend ->
4678 error ("unexpected end of input in skipped " ^ tag) s spos
4679 | Vopen (tag', _, closed) ->
4680 if closed
4681 then v
4682 else
4683 let f' () = { v with f = skip tag f } in
4684 { v with f = skip tag' f' }
4685 | Vclose ctag ->
4686 if tag = ctag
4687 then f ()
4688 else error ("unexpected close in skipped " ^ tag) s spos
4691 parse { f = toplevel; accu = () } s;
4692 h, dc;
4695 let do_load f ic =
4697 let len = in_channel_length ic in
4698 let s = String.create len in
4699 really_input ic s 0 len;
4700 f s;
4701 with
4702 | Parse_error (msg, s, pos) ->
4703 let subs = subs s pos in
4704 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
4705 failwith ("parse error: " ^ s)
4707 | exn ->
4708 failwith ("config load error: " ^ Printexc.to_string exn)
4711 let defconfpath =
4712 let dir =
4714 let dir = Filename.concat home ".config" in
4715 if Sys.is_directory dir then dir else home
4716 with _ -> home
4718 Filename.concat dir "llpp.conf"
4721 let confpath = ref defconfpath;;
4723 let load1 f =
4724 if Sys.file_exists !confpath
4725 then
4726 match
4727 (try Some (open_in_bin !confpath)
4728 with exn ->
4729 prerr_endline
4730 ("Error opening configuation file `" ^ !confpath ^ "': " ^
4731 Printexc.to_string exn);
4732 None
4734 with
4735 | Some ic ->
4736 begin try
4737 f (do_load get ic)
4738 with exn ->
4739 prerr_endline
4740 ("Error loading configuation from `" ^ !confpath ^ "': " ^
4741 Printexc.to_string exn);
4742 end;
4743 close_in ic;
4745 | None -> ()
4746 else
4747 f (Hashtbl.create 0, defconf)
4750 let load () =
4751 let f (h, dc) =
4752 let pc, pb, px, pa =
4754 Hashtbl.find h (Filename.basename state.path)
4755 with Not_found -> dc, [], 0, (0, 0.0)
4757 setconf defconf dc;
4758 setconf conf pc;
4759 state.bookmarks <- pb;
4760 state.x <- px;
4761 state.scrollw <- conf.scrollbw;
4762 if conf.jumpback
4763 then state.anchor <- pa;
4764 cbput state.hists.nav pa;
4766 load1 f
4769 let add_attrs bb always dc c =
4770 let ob s a b =
4771 if always || a != b
4772 then Printf.bprintf bb "\n %s='%b'" s a
4773 and oi s a b =
4774 if always || a != b
4775 then Printf.bprintf bb "\n %s='%d'" s a
4776 and oI s a b =
4777 if always || a != b
4778 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
4779 and oz s a b =
4780 if always || a <> b
4781 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
4782 and oc s a b =
4783 if always || a <> b
4784 then
4785 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
4786 and oC s a b =
4787 if always || a <> b
4788 then
4789 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
4790 and oR s a b =
4791 if always || a <> b
4792 then
4793 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
4794 and os s a b =
4795 if always || a <> b
4796 then
4797 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
4799 let w, h =
4800 if always
4801 then dc.winw, dc.winh
4802 else
4803 match state.fullscreen with
4804 | Some wh -> wh
4805 | None -> c.winw, c.winh
4807 let zoom, presentation, interpagespace, showall=
4808 if always
4809 then dc.zoom, dc.presentation, dc.interpagespace, dc.showall
4810 else
4811 match state.mode with
4812 | Birdseye (bc, _, _, _, _) ->
4813 bc.zoom, bc.presentation, bc.interpagespace, bc.showall
4814 | _ -> c.zoom, c.presentation, c.interpagespace, c.showall
4816 oi "width" w dc.winw;
4817 oi "height" h dc.winh;
4818 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
4819 oi "scroll-handle-height" c.scrollh dc.scrollh;
4820 ob "case-insensitive-search" c.icase dc.icase;
4821 ob "preload" c.preload dc.preload;
4822 oi "page-bias" c.pagebias dc.pagebias;
4823 oi "scroll-step" c.scrollstep dc.scrollstep;
4824 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
4825 ob "max-height-fit" c.maxhfit dc.maxhfit;
4826 ob "crop-hack" c.crophack dc.crophack;
4827 ob "throttle" showall dc.showall;
4828 ob "highlight-links" c.hlinks dc.hlinks;
4829 ob "under-cursor-info" c.underinfo dc.underinfo;
4830 oi "vertical-margin" interpagespace dc.interpagespace;
4831 oz "zoom" zoom dc.zoom;
4832 ob "presentation" presentation dc.presentation;
4833 oi "rotation-angle" c.angle dc.angle;
4834 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
4835 ob "proportional-display" c.proportional dc.proportional;
4836 oI "pixmap-cache-size" c.memlimit dc.memlimit;
4837 oi "tex-count" c.texcount dc.texcount;
4838 oi "slice-height" c.sliceheight dc.sliceheight;
4839 oi "thumbnail-width" c.thumbw dc.thumbw;
4840 ob "persistent-location" c.jumpback dc.jumpback;
4841 oc "background-color" c.bgcolor dc.bgcolor;
4842 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
4843 oi "tile-width" c.tilew dc.tilew;
4844 oi "tile-height" c.tileh dc.tileh;
4845 oI "mupdf-memlimit" c.mumemlimit dc.mumemlimit;
4846 ob "checkers" c.checkers dc.checkers;
4847 oi "aalevel" c.aalevel dc.aalevel;
4848 ob "trim-margins" c.trimmargins dc.trimmargins;
4849 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
4850 os "uri-launcher" c.urilauncher dc.urilauncher;
4851 oC "color-space" c.colorspace dc.colorspace;
4852 ob "invert-colors" c.invert dc.invert;
4853 if always
4854 then ob "wmclass-hack" !wmclasshack false;
4857 let save () =
4858 let uifontsize = !uifontsize in
4859 let bb = Buffer.create 32768 in
4860 let f (h, dc) =
4861 let dc = if conf.bedefault then conf else dc in
4862 Buffer.add_string bb "<llppconfig>\n";
4864 if String.length !fontpath > 0
4865 then
4866 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
4867 uifontsize
4868 !fontpath
4869 else (
4870 if uifontsize <> 14
4871 then
4872 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
4875 Buffer.add_string bb "<defaults ";
4876 add_attrs bb true dc dc;
4877 Buffer.add_string bb "/>\n";
4879 let adddoc path pan anchor c bookmarks =
4880 if bookmarks == [] && c = dc && anchor = emptyanchor
4881 then ()
4882 else (
4883 Printf.bprintf bb "<doc path='%s'"
4884 (enent path 0 (String.length path));
4886 if anchor <> emptyanchor
4887 then (
4888 let n, y = anchor in
4889 Printf.bprintf bb " page='%d'" n;
4890 if y > 1e-6
4891 then
4892 Printf.bprintf bb " rely='%f'" y
4896 if pan != 0
4897 then Printf.bprintf bb " pan='%d'" pan;
4899 add_attrs bb false dc c;
4901 begin match bookmarks with
4902 | [] -> Buffer.add_string bb "/>\n"
4903 | _ ->
4904 Buffer.add_string bb ">\n<bookmarks>\n";
4905 List.iter (fun (title, _level, (page, rely)) ->
4906 Printf.bprintf bb
4907 "<item title='%s' page='%d'"
4908 (enent title 0 (String.length title))
4909 page
4911 if rely > 1e-6
4912 then
4913 Printf.bprintf bb " rely='%f'" rely
4915 Buffer.add_string bb "/>\n";
4916 ) bookmarks;
4917 Buffer.add_string bb "</bookmarks>\n</doc>\n";
4918 end;
4922 let pan =
4923 match state.mode with
4924 | Birdseye (_, pan, _, _, _) -> pan
4925 | _ -> state.x
4927 let basename = Filename.basename state.path in
4928 adddoc basename pan (getanchor ())
4929 { conf with
4930 autoscrollstep =
4931 match state.autoscroll with
4932 | Some step -> step
4933 | None -> conf.autoscrollstep }
4934 (if conf.savebmarks then state.bookmarks else []);
4936 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
4937 if basename <> path
4938 then adddoc path x y c bookmarks
4939 ) h;
4940 Buffer.add_string bb "</llppconfig>";
4942 load1 f;
4943 if Buffer.length bb > 0
4944 then
4946 let tmp = !confpath ^ ".tmp" in
4947 let oc = open_out_bin tmp in
4948 Buffer.output_buffer oc bb;
4949 close_out oc;
4950 Unix.rename tmp !confpath;
4951 with exn ->
4952 prerr_endline
4953 ("error while saving configuration: " ^ Printexc.to_string exn)
4955 end;;
4957 let () =
4958 Arg.parse
4959 (Arg.align
4960 [("-p", Arg.String (fun s -> state.password <- s) ,
4961 "<password> Set password");
4963 ("-f", Arg.String (fun s -> Config.fontpath := s),
4964 "<path> Set path to the user interface font");
4966 ("-c", Arg.String (fun s -> Config.confpath := s),
4967 "<path> Set path to the configuration file");
4969 ("-v", Arg.Unit (fun () ->
4970 Printf.printf
4971 "%s\nconfiguration path: %s\n"
4972 Help.version
4973 Config.defconfpath
4975 exit 0), " Print version and exit");
4978 (fun s -> state.path <- s)
4979 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
4981 if String.length state.path = 0
4982 then (prerr_endline "file name missing"; exit 1);
4984 Config.load ();
4986 let _ = Glut.init Sys.argv in
4987 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
4988 let () = Glut.initWindowSize conf.winw conf.winh in
4989 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
4991 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
4992 || Glut.extensionSupported "GL_EXT_texture_rectangle")
4993 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
4995 let csock, ssock =
4996 if not is_windows
4997 then
4998 Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0
4999 else
5000 let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, 1337) in
5001 let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5002 Unix.setsockopt sock Unix.SO_REUSEADDR true;
5003 Unix.bind sock addr;
5004 Unix.listen sock 1;
5005 let csock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5006 Unix.connect csock addr;
5007 let ssock, _ = Unix.accept sock in
5008 Unix.close sock;
5009 let opts sock =
5010 Unix.setsockopt sock Unix.TCP_NODELAY true;
5011 Unix.setsockopt_optint sock Unix.SO_LINGER None;
5013 opts ssock;
5014 opts csock;
5015 ssock, csock
5018 let () = Glut.displayFunc display in
5019 let () = Glut.reshapeFunc reshape in
5020 let () = Glut.keyboardFunc keyboard in
5021 let () = Glut.specialFunc special in
5022 let () = Glut.idleFunc (Some idle) in
5023 let () = Glut.mouseFunc mouse in
5024 let () = Glut.motionFunc motion in
5025 let () = Glut.passiveMotionFunc pmotion in
5027 setcheckers conf.checkers;
5028 init ssock (
5029 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5030 conf.texcount, conf.sliceheight, conf.mumemlimit, conf.colorspace,
5031 !Config.wmclasshack, !Config.fontpath
5033 state.csock <- csock;
5034 state.ssock <- ssock;
5035 state.text <- "Opening " ^ state.path;
5036 setaalevel conf.aalevel;
5037 writeopen state.path state.password;
5038 state.uioh <- uioh;
5040 while true do
5042 Glut.mainLoop ();
5043 with
5044 | Glut.BadEnum "key in special_of_int" ->
5045 showtext '!' " LablGlut bug: special key not recognized";
5047 | Quit ->
5048 wcmd "quit" [];
5049 Config.save ();
5050 exit 0
5051 done;