Move UniSyms to utils
[llpp.git] / main.ml
blobb9a9d8ef2798533873fac99ed1f16f2fba038645
1 open Utils;;
2 open Config;;
4 exception Quit;;
6 external init : Unix.file_descr -> initparams -> unit = "ml_init";;
7 external seltext : opaque -> (int * int * int * int) -> unit = "ml_seltext";;
8 external hassel : opaque -> bool = "ml_hassel";;
9 external getpdimrect : int -> float array = "ml_getpdimrect";;
10 external whatsunder : opaque -> int -> int -> under = "ml_whatsunder";;
11 external markunder : opaque -> int -> int -> mark -> bool = "ml_markunder";;
12 external clearmark : opaque -> unit = "ml_clearmark";;
13 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
14 external getmaxw : unit -> float = "ml_getmaxw";;
15 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
16 external measurestr : int -> string -> float = "ml_measure_string";;
17 external postprocess : opaque -> int -> int -> int -> (int * string * int)
18 -> int = "ml_postprocess";;
19 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
20 external setaalevel : int -> unit = "ml_setaalevel";;
21 external realloctexts : int -> bool = "ml_realloctexts";;
22 external findlink : opaque -> linkdir -> link = "ml_findlink";;
23 external getlink : opaque -> int -> under = "ml_getlink";;
24 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
25 external getlinkcount : opaque -> int = "ml_getlinkcount";;
26 external findpwl : int -> int -> pagewithlinks = "ml_find_page_with_links";;
27 external getpbo : width -> height -> colorspace -> opaque = "ml_getpbo";;
28 external freepbo : opaque -> unit = "ml_freepbo";;
29 external unmappbo : opaque -> unit = "ml_unmappbo";;
30 external bousable : unit -> bool = "ml_bo_usable";;
31 external unproject : opaque -> int -> int
32 -> (int * int) option = "ml_unproject";;
33 external project : opaque -> int -> int -> float -> float
34 -> (float * float) = "ml_project";;
35 external drawtile : tileparams -> opaque
36 -> unit = "ml_drawtile";;
37 external rectofblock : opaque -> int -> int
38 -> float array option = "ml_rectofblock";;
39 external begintiles : unit -> unit = "ml_begintiles";;
40 external endtiles : unit -> unit = "ml_endtiles";;
41 external addannot : opaque -> int -> int -> string -> unit = "ml_addannot";;
42 external modannot : opaque -> slinkindex -> string -> unit = "ml_modannot";;
43 external delannot : opaque -> slinkindex -> unit = "ml_delannot";;
44 external hasunsavedchanges : unit -> bool = "ml_hasunsavedchanges";;
45 external savedoc : string -> unit = "ml_savedoc";;
46 external getannotcontents : opaque -> slinkindex
47 -> string = "ml_getannotcontents";;
48 external drawprect : opaque -> int -> int -> float array
49 -> unit = "ml_drawprect";;
50 external wcmd : Unix.file_descr -> bytes -> int -> unit = "ml_wcmd";;
51 external rcmd : Unix.file_descr -> string = "ml_rcmd";;
52 external uritolocation : string
53 -> (pageno * float * float) = "ml_uritolocation";;
54 external isexternallink : string -> bool = "ml_isexternallink";;
56 (* copysel _will_ close the supplied descriptor *)
57 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
59 let selfexec = ref E.s;;
60 let ignoredoctitlte = ref false;;
61 let opengl_has_pbo = ref false;;
62 let layouth = ref ~-1;;
64 let drawstring size x y s =
65 Gl.enable `blend;
66 Gl.enable `texture_2d;
67 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
68 ignore (drawstr size x y s);
69 Gl.disable `blend;
70 Gl.disable `texture_2d;
73 let drawstring1 size x y s =
74 drawstr size x y s;
77 let drawstring2 size x y fmt =
78 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
81 let _debugl l =
82 dolog {|l %d dim=%d {
83 WxH %dx%d
84 vWxH %dx%d
85 pagex,y %d,%d
86 dispx,y %d,%d
87 column %d
88 }|}
89 l.pageno l.pagedimno
90 l.pagew l.pageh
91 l.pagevw l.pagevh
92 l.pagex l.pagey
93 l.pagedispx l.pagedispy
94 l.pagecol
97 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
98 dolog {|rect {
99 x0,y0=(% f, % f)
100 x1,y1=(% f, % f)
101 x2,y2=(% f, % f)
102 x3,y3=(% f, % f)
103 }|} x0 y0 x1 y1 x2 y2 x3 y3;
106 let isbirdseye = function
107 | Birdseye _ -> true
108 | Textentry _ | View | LinkNav _ -> false
111 let istextentry = function
112 | Textentry _ -> true
113 | Birdseye _ | View | LinkNav _ -> false
116 let pgscale h = truncate (float h *. conf.pgscale);;
118 let hscrollh () =
119 if state.uioh#alwaysscrolly || ((conf.scrollb land scrollbhv != 0)
120 && (state.w > state.winw))
121 then conf.scrollbw
122 else 0
125 let vscrollw () =
126 if state.uioh#alwaysscrolly || ((conf.scrollb land scrollbvv != 0)
127 && (state.maxy > state.winh))
128 then conf.scrollbw
129 else 0
132 let vscrollhit x =
133 if conf.leftscroll
134 then x < vscrollw ()
135 else x > state.winw - vscrollw ()
138 let setfontsize n =
139 fstate.fontsize <- n;
140 fstate.wwidth <- measurestr fstate.fontsize "w";
141 fstate.maxrows <- (state.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
144 let vlog fmt =
145 if conf.verbose
146 then dolog fmt
147 else Printf.kprintf ignore fmt
150 let launchpath () =
151 if emptystr conf.pathlauncher
152 then dolog "%s" state.path
153 else (
154 let command = Str.global_replace percentsre state.path conf.pathlauncher in
155 match spawn command [] with
156 | _pid -> ()
157 | exception exn ->
158 dolog "failed to execute `%s': %s" command @@ exntos exn
162 module G =
163 struct
164 let postRedisplay who =
165 vlog "redisplay for [%S]" who;
166 state.redisplay <- true;
168 end;;
170 let getopaque pageno =
171 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
172 with Not_found -> None
175 let pagetranslatepoint l x y =
176 let dy = y - l.pagedispy in
177 let y = dy + l.pagey in
178 let dx = x - l.pagedispx in
179 let x = dx + l.pagex in
180 (x, y);
183 let onppundermouse g x y d =
184 let rec f = function
185 | l :: rest ->
186 begin match getopaque l.pageno with
187 | Some opaque ->
188 let x0 = l.pagedispx in
189 let x1 = x0 + l.pagevw in
190 let y0 = l.pagedispy in
191 let y1 = y0 + l.pagevh in
192 if y >= y0 && y <= y1 && x >= x0 && x <= x1
193 then
194 let px, py = pagetranslatepoint l x y in
195 match g opaque l px py with
196 | Some res -> res
197 | None -> f rest
198 else f rest
199 | _ ->
200 f rest
202 | [] -> d
204 f state.layout
207 let getunder x y =
208 let g opaque l px py =
209 if state.bzoom
210 then (
211 match rectofblock opaque px py with
212 | Some [|x0;x1;y0;y1|] ->
213 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
214 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
215 state.rects <- [l.pageno, color, rect];
216 G.postRedisplay "getunder";
217 | _ -> ()
219 let under = whatsunder opaque px py in
220 if under = Unone then None else Some under
222 onppundermouse g x y Unone
225 let unproject x y =
226 let g opaque l x y =
227 match unproject opaque x y with
228 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
229 | None -> None
231 onppundermouse g x y None;
234 let showtext c s =
235 state.text <- Printf.sprintf "%c%s" c s;
236 G.postRedisplay "showtext";
239 let impmsg fmt =
240 Format.ksprintf (fun s -> showtext '!' s) fmt;
243 let pipef ?(closew=true) cap f cmd =
244 match Unix.pipe () with
245 | exception exn -> dolog "%s cannot create pipe: %S" cap @@ exntos exn
246 | (r, w) ->
247 begin match spawn cmd [r, 0; w, -1] with
248 | exception exn -> dolog "%s: cannot execute %S: %s" cap cmd @@ exntos exn
249 | _pid -> f w
250 end;
251 Ne.clo r (dolog "%s failed to close r: %s" cap);
252 if closew then Ne.clo w (dolog "%s failed to close w: %s" cap);
255 let pipesel opaque cmd =
256 if hassel opaque
257 then pipef ~closew:false "pipesel"
258 (fun w ->
259 copysel w opaque;
260 G.postRedisplay "pipesel"
261 ) cmd
264 let paxunder x y =
265 let g opaque l px py =
266 if markunder opaque px py conf.paxmark
267 then (
268 Some (fun () ->
269 match getopaque l.pageno with
270 | None -> ()
271 | Some opaque -> pipesel opaque conf.paxcmd
274 else None
276 G.postRedisplay "paxunder";
277 if conf.paxmark = Mark_page
278 then
279 List.iter (fun l ->
280 match getopaque l.pageno with
281 | None -> ()
282 | Some opaque -> clearmark opaque) state.layout;
283 state.roam <- onppundermouse g x y (fun () -> impmsg "whoopsie daisy");
286 let selstring s =
287 pipef
288 "selstring" (fun w ->
290 let l = String.length s in
291 let bytes = Bytes.unsafe_of_string s in
292 let n = tempfailureretry (Unix.write w bytes 0) l in
293 if n != l
294 then impmsg "failed to write %d characters to sel pipe, wrote %d" l n;
295 with exn -> impmsg "failed to write to sel pipe: %s" @@ exntos exn
296 ) conf.selcmd
299 let undertext = function
300 | Unone -> "none"
301 | Ulinkuri s -> s
302 | Utext s -> "font: " ^ s
303 | Uannotation (opaque, slinkindex) ->
304 "annotation: " ^ getannotcontents opaque slinkindex
307 let updateunder x y =
308 match getunder x y with
309 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
310 | Ulinkuri uri ->
311 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
312 Wsi.setcursor Wsi.CURSOR_INFO
313 | Utext s ->
314 if conf.underinfo then showtext 'f' ("ont: " ^ s);
315 Wsi.setcursor Wsi.CURSOR_TEXT
316 | Uannotation _ ->
317 if conf.underinfo then showtext 'a' "nnotation";
318 Wsi.setcursor Wsi.CURSOR_INFO
321 let showlinktype under =
322 if conf.underinfo && under != Unone
323 then showtext ' ' @@ undertext under
326 let [@warning "-4"] intentry_with_suffix text key =
327 let text =
328 match key with
329 | Keys.Ascii ('0'..'9' as c) -> addchar text c
330 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
331 addchar text @@ asciilower c
332 | _ ->
333 state.text <- Printf.sprintf "invalid key";
334 text
336 TEcont text
339 let wcmd fmt =
340 let b = Buffer.create 16 in
341 Printf.kbprintf
342 (fun b ->
343 let b = Buffer.to_bytes b in
344 wcmd state.ss b @@ Bytes.length b
345 ) b fmt
348 let nogeomcmds cmds =
349 match cmds with
350 | s, [] -> emptystr s
351 | _ -> false
354 let layoutN ((columns, coverA, coverB), b) x y sw sh =
355 let rec fold accu n =
356 if n = Array.length b
357 then accu
358 else
359 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
360 if (vy - y) > sh &&
361 (n = coverA - 1
362 || n = state.pagecount - coverB
363 || (n - coverA) mod columns = columns - 1)
364 then accu
365 else
366 let accu =
367 if vy + h > y
368 then
369 let pagey = max 0 (y - vy) in
370 let pagedispy = if pagey > 0 then 0 else vy - y in
371 let pagedispx, pagex =
372 let pdx =
373 if n = coverA - 1 || n = state.pagecount - coverB
374 then x + (sw - w) / 2
375 else dx + xoff + x
377 if pdx < 0
378 then 0, -pdx
379 else pdx, 0
381 let pagevw =
382 let vw = sw - pagedispx in
383 let pw = w - pagex in
384 min vw pw
386 let pagevh = min (h - pagey) (sh - pagedispy) in
387 if pagevw > 0 && pagevh > 0
388 then
389 let e =
390 { pageno = n
391 ; pagedimno = pdimno
392 ; pagew = w
393 ; pageh = h
394 ; pagex = pagex
395 ; pagey = pagey
396 ; pagevw = pagevw
397 ; pagevh = pagevh
398 ; pagedispx = pagedispx
399 ; pagedispy = pagedispy
400 ; pagecol = 0
403 e :: accu
404 else
405 accu
406 else
407 accu
409 fold accu (n+1)
411 if Array.length b = 0
412 then []
413 else List.rev (fold [] (page_of_y y))
416 let layoutS (columns, b) x y sw sh =
417 let rec fold accu n =
418 if n = Array.length b
419 then accu
420 else
421 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
422 if (vy - y) > sh
423 then accu
424 else
425 let accu =
426 if vy + pageh > y
427 then
428 let x = xoff + x in
429 let pagey = max 0 (y - vy) in
430 let pagedispy = if pagey > 0 then 0 else vy - y in
431 let pagedispx, pagex =
432 if px = 0
433 then (
434 if x < 0
435 then 0, -x
436 else x, 0
438 else (
439 let px = px - x in
440 if px < 0
441 then -px, 0
442 else 0, px
445 let pagecolw = pagew/columns in
446 let pagedispx =
447 if pagecolw < sw
448 then pagedispx + ((sw - pagecolw) / 2)
449 else pagedispx
451 let pagevw =
452 let vw = sw - pagedispx in
453 let pw = pagew - pagex in
454 min vw pw
456 let pagevw = min pagevw pagecolw in
457 let pagevh = min (pageh - pagey) (sh - pagedispy) in
458 if pagevw > 0 && pagevh > 0
459 then
460 let e =
461 { pageno = n/columns
462 ; pagedimno = pdimno
463 ; pagew = pagew
464 ; pageh = pageh
465 ; pagex = pagex
466 ; pagey = pagey
467 ; pagevw = pagevw
468 ; pagevh = pagevh
469 ; pagedispx = pagedispx
470 ; pagedispy = pagedispy
471 ; pagecol = n mod columns
474 e :: accu
475 else
476 accu
477 else
478 accu
480 fold accu (n+1)
482 List.rev (fold [] 0)
485 let layout x y sw sh =
486 if nogeomcmds state.geomcmds
487 then
488 match conf.columns with
489 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
490 | Cmulti c -> layoutN c x y sw sh
491 | Csplit s -> layoutS s x y sw sh
492 else []
495 let maxy () = state.maxy - if conf.maxhfit then state.winh else 0;;
497 let clamp incr = bound (state.y + incr) 0 @@ maxy ();;
499 let itertiles l f =
500 let tilex = l.pagex mod conf.tilew in
501 let tiley = l.pagey mod conf.tileh in
503 let col = l.pagex / conf.tilew in
504 let row = l.pagey / conf.tileh in
506 let rec rowloop row y0 dispy h =
507 if h = 0
508 then ()
509 else (
510 let dh = conf.tileh - y0 in
511 let dh = min h dh in
512 let rec colloop col x0 dispx w =
513 if w = 0
514 then ()
515 else (
516 let dw = conf.tilew - x0 in
517 let dw = min w dw in
518 f col row dispx dispy x0 y0 dw dh;
519 colloop (col+1) 0 (dispx+dw) (w-dw)
522 colloop col tilex l.pagedispx l.pagevw;
523 rowloop (row+1) 0 (dispy+dh) (h-dh)
526 if l.pagevw > 0 && l.pagevh > 0
527 then rowloop row tiley l.pagedispy l.pagevh;
530 let gettileopaque l col row =
531 let key =
532 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
534 try Some (Hashtbl.find state.tilemap key)
535 with Not_found -> None
538 let puttileopaque l col row gen colorspace angle opaque size elapsed =
539 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
540 Hashtbl.add state.tilemap key (opaque, size, elapsed)
543 let filledrect2 x0 y0 x1 y1 x2 y2 x3 y3 =
544 Raw.sets_float state.vraw ~pos:0 [| x0; y0; x1; y1; x2; y2; x3; y3 |];
545 GlArray.vertex `two state.vraw;
546 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
549 let filledrect1 x0 y0 x1 y1 = filledrect2 x0 y0 x0 y1 x1 y0 x1 y1;;
551 let filledrect x0 y0 x1 y1 =
552 GlArray.disable `texture_coord;
553 filledrect1 x0 y0 x1 y1;
554 GlArray.enable `texture_coord;
557 let linerect x0 y0 x1 y1 =
558 GlArray.disable `texture_coord;
559 Raw.sets_float state.vraw ~pos:0 [| x0; y0; x0; y1; x1; y1; x1; y0 |];
560 GlArray.vertex `two state.vraw;
561 GlArray.draw_arrays `line_loop ~first:0 ~count:4;
562 GlArray.enable `texture_coord;
565 let drawtiles l color =
566 GlDraw.color color;
567 begintiles ();
568 let f col row x y tilex tiley w h =
569 match gettileopaque l col row with
570 | Some (opaque, _, t) ->
571 let params = x, y, w, h, tilex, tiley in
572 if conf.invert
573 then GlTex.env (`mode `blend);
574 drawtile params opaque;
575 if conf.invert
576 then GlTex.env (`mode `modulate);
577 if conf.debug
578 then (
579 endtiles ();
580 let s = Printf.sprintf
581 "%d[%d,%d] %f sec"
582 l.pageno col row t
584 let w = measurestr fstate.fontsize s in
585 GlDraw.color (0.0, 0.0, 0.0);
586 filledrect (float (x-2))
587 (float (y-2))
588 (float (x+2) +. w)
589 (float (y + fstate.fontsize + 2));
590 GlDraw.color color;
591 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
592 begintiles ();
595 | None ->
596 endtiles ();
597 let w =
598 let lw = state.winw - x in
599 min lw w
600 and h =
601 let lh = state.winh - y in
602 min lh h
604 if conf.invert
605 then GlTex.env (`mode `blend);
606 begin match state.checkerstexid with
607 | Some id ->
608 Gl.enable `texture_2d;
609 GlTex.bind_texture ~target:`texture_2d id;
610 let x0 = float x
611 and y0 = float y
612 and x1 = float (x+w)
613 and y1 = float (y+h) in
615 let tw = float w /. 16.0
616 and th = float h /. 16.0 in
617 let tx0 = float tilex /. 16.0
618 and ty0 = float tiley /. 16.0 in
619 let tx1 = tx0 +. tw
620 and ty1 = ty0 +. th in
621 Raw.sets_float state.vraw ~pos:0
622 [| x0; y0; x0; y1; x1; y0; x1; y1 |];
623 Raw.sets_float state.traw ~pos:0
624 [| tx0; ty0; tx0; ty1; tx1; ty0; tx1; ty1 |];
625 GlArray.vertex `two state.vraw;
626 GlArray.tex_coord `two state.traw;
627 GlArray.draw_arrays `triangle_strip ~first:0 ~count:4;
628 Gl.disable `texture_2d;
630 | None ->
631 GlDraw.color (1.0, 1.0, 1.0);
632 filledrect (float x) (float y) (float (x+w)) (float (y+h));
633 end;
634 if conf.invert
635 then GlTex.env (`mode `modulate);
636 if w > 128 && h > fstate.fontsize + 10
637 then (
638 let c = if conf.invert then 1.0 else 0.0 in
639 GlDraw.color (c, c, c);
640 let c, r =
641 if conf.verbose
642 then (col*conf.tilew, row*conf.tileh)
643 else col, row
645 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
647 GlDraw.color color;
648 begintiles ();
650 itertiles l f;
651 endtiles ();
654 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
656 let tilevisible1 l x y =
657 let ax0 = l.pagex
658 and ax1 = l.pagex + l.pagevw
659 and ay0 = l.pagey
660 and ay1 = l.pagey + l.pagevh in
662 let bx0 = x
663 and by0 = y in
664 let bx1 = min (bx0 + conf.tilew) l.pagew
665 and by1 = min (by0 + conf.tileh) l.pageh in
667 let rx0 = max ax0 bx0
668 and ry0 = max ay0 by0
669 and rx1 = min ax1 bx1
670 and ry1 = min ay1 by1 in
672 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
673 nonemptyintersection
676 let tilevisible layout n x y =
677 let rec findpageinlayout m = function
678 | l :: rest when l.pageno = n ->
679 tilevisible1 l x y || (
680 match conf.columns with
681 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
682 | Csplit _ | Csingle _ | Cmulti _ -> false
684 | _ :: rest -> findpageinlayout 0 rest
685 | [] -> false
687 findpageinlayout 0 layout;
690 let tileready l x y =
691 tilevisible1 l x y &&
692 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
695 let tilepage n p layout =
696 let rec loop = function
697 | l :: rest ->
698 if l.pageno = n
699 then
700 let f col row _ _ _ _ _ _ =
701 if state.currently = Idle
702 then
703 match gettileopaque l col row with
704 | Some _ -> ()
705 | None ->
706 let x = col*conf.tilew
707 and y = row*conf.tileh in
708 let w =
709 let w = l.pagew - x in
710 min w conf.tilew
712 let h =
713 let h = l.pageh - y in
714 min h conf.tileh
716 let pbo =
717 if conf.usepbo
718 then getpbo w h conf.colorspace
719 else ~< "0"
721 wcmd "tile %s %d %d %d %d %s"
722 (~> p) x y w h (~> pbo);
723 state.currently <-
724 Tiling (
725 l, p, conf.colorspace, conf.angle,
726 state.gen, col, row, conf.tilew, conf.tileh
729 itertiles l f;
730 else
731 loop rest
733 | [] -> ()
735 if nogeomcmds state.geomcmds
736 then loop layout;
739 let preloadlayout x y sw sh =
740 let y = if y < sh then 0 else y - sh in
741 let x = min 0 (x + sw) in
742 let h = sh*3 in
743 let w = sw*3 in
744 layout x y w h;
747 let load pages =
748 let rec loop pages =
749 if state.currently != Idle
750 then ()
751 else
752 match pages with
753 | l :: rest ->
754 begin match getopaque l.pageno with
755 | None ->
756 wcmd "page %d %d" l.pageno l.pagedimno;
757 state.currently <- Loading (l, state.gen);
758 | Some opaque ->
759 tilepage l.pageno opaque pages;
760 loop rest
761 end;
762 | _ -> ()
764 if nogeomcmds state.geomcmds
765 then loop pages
768 let preload pages =
769 load pages;
770 if conf.preload && state.currently = Idle
771 then load (preloadlayout state.x state.y state.winw state.winh);
774 let layoutready layout =
775 let rec fold all ls =
776 all && match ls with
777 | l :: rest ->
778 let seen = ref false in
779 let allvisible = ref true in
780 let foo col row _ _ _ _ _ _ =
781 seen := true;
782 allvisible := !allvisible &&
783 begin match gettileopaque l col row with
784 | Some _ -> true
785 | None -> false
788 itertiles l foo;
789 fold (!seen && !allvisible) rest
790 | [] -> true
792 let alltilesvisible = fold true layout in
793 alltilesvisible;
796 let gotoxy x y =
797 let y = bound y 0 state.maxy in
798 let y, layout, proceed =
799 match conf.maxwait with
800 | Some time ->
801 begin match state.throttle with
802 | None ->
803 let layout = layout x y state.winw state.winh in
804 let ready = layoutready layout in
805 if not ready
806 then (
807 load layout;
808 state.throttle <- Some (layout, y, now ());
810 else G.postRedisplay "gotoxy showall (None)";
811 y, layout, ready
812 | Some (_, _, started) ->
813 let dt = now () -. started in
814 if dt > time
815 then (
816 state.throttle <- None;
817 let layout = layout x y state.winw state.winh in
818 load layout;
819 G.postRedisplay "maxwait";
820 y, layout, true
822 else -1, [], false
825 | _ ->
826 let layout = layout x y state.winw state.winh in
827 G.postRedisplay "gotoxy ready";
828 y, layout, true
830 if proceed
831 then (
832 state.x <- x;
833 state.y <- y;
834 state.layout <- layout;
835 begin match state.mode with
836 | LinkNav ln ->
837 begin match ln with
838 | Ltexact (pageno, linkno) ->
839 let rec loop = function
840 | [] ->
841 state.lnava <- Some (pageno, linkno);
842 state.mode <- LinkNav (Ltgendir 0)
843 | l :: _ when l.pageno = pageno ->
844 begin match getopaque pageno with
845 | None -> state.mode <- LinkNav (Ltnotready (pageno, 0))
846 | Some opaque ->
847 let x0, y0, x1, y1 = getlinkrect opaque linkno in
848 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
849 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
850 then state.mode <- LinkNav (Ltgendir 0)
852 | _ :: rest -> loop rest
854 loop layout
855 | Ltnotready _ | Ltgendir _ -> ()
857 | Birdseye _ | Textentry _ | View -> ()
858 end;
859 begin match state.mode with
860 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
861 if not (pagevisible layout pageno)
862 then (
863 match state.layout with
864 | [] -> ()
865 | l :: _ ->
866 state.mode <- Birdseye (
867 conf, leftx, l.pageno, hooverpageno, anchor
870 | LinkNav lt ->
871 begin match lt with
872 | Ltnotready (_, dir)
873 | Ltgendir dir ->
874 let linknav =
875 let rec loop = function
876 | [] -> lt
877 | l :: rest ->
878 match getopaque l.pageno with
879 | None -> Ltnotready (l.pageno, dir)
880 | Some opaque ->
881 let link =
882 let ld =
883 if dir = 0
884 then LDfirstvisible (l.pagex, l.pagey, dir)
885 else (
886 if dir > 0 then LDfirst else LDlast
889 findlink opaque ld
891 match link with
892 | Lnotfound -> loop rest
893 | Lfound n ->
894 showlinktype (getlink opaque n);
895 Ltexact (l.pageno, n)
897 loop state.layout
899 state.mode <- LinkNav linknav
900 | Ltexact _ -> ()
902 | Textentry _ | View -> ()
903 end;
904 preload layout;
906 if conf.updatecurs
907 then (
908 let mx, my = state.mpos in
909 updateunder mx my;
913 let conttiling pageno opaque =
914 tilepage pageno opaque
915 (if conf.preload
916 then preloadlayout state.x state.y state.winw state.winh
917 else state.layout)
920 let gotoxy x y =
921 if not conf.verbose then state.text <- E.s;
922 gotoxy x y;
925 let getanchory (n, top, dtop) =
926 let y, h = getpageyh n in
927 if conf.presentation
928 then
929 let ips = calcips h in
930 y + truncate (top*.float h -. dtop*.float ips) + ips;
931 else
932 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
935 let gotoanchor anchor =
936 gotoxy state.x (getanchory anchor);
939 let addnav () =
940 getanchor () |> cbput state.hists.nav;
943 let addnavnorc () =
944 getanchor () |> cbput_dont_update_rc state.hists.nav;
947 let getnav dir =
948 let anchor = cbgetc state.hists.nav dir in
949 getanchory anchor;
952 let gotopage n top =
953 let y, h = getpageyh n in
954 let y = y + (truncate (top *. float h)) in
955 gotoxy state.x y
958 let gotopage1 n top =
959 let y = getpagey n in
960 let y = y + top in
961 gotoxy state.x y
964 let invalidate s f =
965 state.redisplay <- false;
966 state.layout <- [];
967 state.pdims <- [];
968 state.rects <- [];
969 state.rects1 <- [];
970 match state.geomcmds with
971 | ps, [] when emptystr ps ->
972 f ();
973 state.geomcmds <- s, [];
975 | ps, [] ->
976 state.geomcmds <- ps, [s, f];
978 | ps, (s', _) :: rest when s' = s ->
979 state.geomcmds <- ps, ((s, f) :: rest);
981 | ps, cmds ->
982 state.geomcmds <- ps, ((s, f) :: cmds);
985 let flushpages () =
986 Hashtbl.iter (fun _ opaque ->
987 wcmd "freepage %s" (~> opaque);
988 ) state.pagemap;
989 Hashtbl.clear state.pagemap;
992 let flushtiles () =
993 if not (Queue.is_empty state.tilelru)
994 then (
995 Queue.iter (fun (k, p, s) ->
996 wcmd "freetile %s" (~> p);
997 state.memused <- state.memused - s;
998 Hashtbl.remove state.tilemap k;
999 ) state.tilelru;
1000 state.uioh#infochanged Memused;
1001 Queue.clear state.tilelru;
1003 load state.layout;
1006 let stateh h =
1007 let h = truncate (float h*.conf.zoom) in
1008 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
1009 h - d
1012 let fillhelp () =
1013 state.help <-
1014 let sl = keystostrlist conf in
1015 let rec loop accu =
1016 function | [] -> accu
1017 | s :: rest -> loop ((s, 0, Noaction) :: accu) rest
1018 in makehelp () @ (("", 0, Noaction) :: loop [] sl) |> Array.of_list
1021 let opendoc path password =
1022 state.path <- path;
1023 state.password <- password;
1024 state.gen <- state.gen + 1;
1025 state.docinfo <- [];
1026 state.outlines <- [||];
1028 flushpages ();
1029 setaalevel conf.aalevel;
1030 let titlepath =
1031 if emptystr state.origin
1032 then path
1033 else state.origin
1035 Wsi.settitle ("llpp " ^ (mbtoutf8 (Filename.basename titlepath)));
1036 wcmd "open %d %d %s\000%s\000%s\000"
1037 (btod conf.usedoccss) !layouth
1038 path password conf.css;
1039 invalidate "reqlayout"
1040 (fun () ->
1041 begin state.statkeyhack <-
1042 try ((Unix.stat state.path).st_mtime, conf.key)
1043 with _ -> (nan, E.s)
1044 end;
1045 wcmd "reqlayout %d %d %d %s\000"
1046 conf.angle (FMTE.to_int conf.fitmodel)
1047 (stateh state.winh) state.nameddest
1049 fillhelp ();
1052 let reload () =
1053 state.anchor <- getanchor ();
1054 opendoc state.path state.password;
1057 let scalecolor c =
1058 let c = c *. conf.colorscale in
1059 (c, c, c);
1062 let scalecolor2 (r, g, b) =
1063 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1066 let docolumns columns =
1067 match columns with
1068 | Csingle _ ->
1069 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1070 let rec loop pageno pdimno pdim y ph pdims =
1071 if pageno = state.pagecount
1072 then ()
1073 else
1074 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1075 match pdims with
1076 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1077 pdimno+1, pdim, rest
1078 | _ ->
1079 pdimno, pdim, pdims
1081 let x = max 0 (((state.winw - w) / 2) - xoff) in
1082 let y =
1083 y + (if conf.presentation
1084 then (if pageno = 0 then calcips h else calcips ph + calcips h)
1085 else (if pageno = 0 then 0 else conf.interpagespace)
1088 a.(pageno) <- (pdimno, x, y, pdim);
1089 loop (pageno+1) pdimno pdim (y + h) h pdims
1091 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
1092 conf.columns <- Csingle a;
1094 | Cmulti ((columns, coverA, coverB), _) ->
1095 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1096 let rec loop pageno pdimno pdim x y rowh pdims =
1097 let rec fixrow m =
1098 if m = pageno then () else
1099 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1100 if h < rowh
1101 then (
1102 let y = y + (rowh - h) / 2 in
1103 a.(m) <- (pdimno, x, y, pdim);
1105 fixrow (m+1)
1107 if pageno = state.pagecount
1108 then fixrow (((pageno - 1) / columns) * columns)
1109 else
1110 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1111 match pdims with
1112 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1113 pdimno+1, pdim, rest
1114 | _ ->
1115 pdimno, pdim, pdims
1117 let x, y, rowh' =
1118 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1119 then (
1120 let x = (state.winw - w) / 2 in
1121 let ips =
1122 if conf.presentation then calcips h else conf.interpagespace in
1123 x, y + ips + rowh, h
1125 else (
1126 if (pageno - coverA) mod columns = 0
1127 then (
1128 let x = max 0 (state.winw - state.w) / 2 in
1129 let y =
1130 if conf.presentation
1131 then
1132 let ips = calcips h in
1133 y + (if pageno = 0 then 0 else calcips rowh + ips)
1134 else
1135 y + (if pageno = 0 then 0 else conf.interpagespace)
1137 x, y + rowh, h
1139 else x, y, max rowh h
1142 let y =
1143 if pageno > 1 && (pageno - coverA) mod columns = 0
1144 then (
1145 let y =
1146 if pageno = columns && conf.presentation
1147 then (
1148 let ips = calcips rowh in
1149 for i = 0 to pred columns
1151 let (pdimno, x, y, pdim) = a.(i) in
1152 a.(i) <- (pdimno, x, y+ips, pdim)
1153 done;
1154 y+ips;
1156 else y
1158 fixrow (pageno - columns);
1161 else y
1163 a.(pageno) <- (pdimno, x, y, pdim);
1164 let x = x + w + xoff*2 + conf.interpagespace in
1165 loop (pageno+1) pdimno pdim x y rowh' pdims
1167 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1168 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1170 | Csplit (c, _) ->
1171 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1172 let rec loop pageno pdimno pdim y pdims =
1173 if pageno = state.pagecount
1174 then ()
1175 else
1176 let pdimno, ((_, w, h, _) as pdim), pdims =
1177 match pdims with
1178 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1179 pdimno+1, pdim, rest
1180 | _ ->
1181 pdimno, pdim, pdims
1183 let cw = w / c in
1184 let rec loop1 n x y =
1185 if n = c then y else (
1186 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1187 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
1190 let y = loop1 0 0 y in
1191 loop (pageno+1) pdimno pdim y pdims
1193 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
1194 conf.columns <- Csplit (c, a);
1197 let represent () =
1198 docolumns conf.columns;
1199 state.maxy <- calcheight ();
1200 if state.reprf == noreprf
1201 then (
1202 match state.mode with
1203 | Birdseye (_, _, pageno, _, _) ->
1204 let y, h = getpageyh pageno in
1205 let top = (state.winh - h) / 2 in
1206 gotoxy state.x (max 0 (y - top))
1207 | Textentry _ | View | LinkNav _ ->
1208 let y = getanchory state.anchor in
1209 let y = min y (state.maxy - state.winh) in
1210 gotoxy state.x y;
1212 else (
1213 state.reprf ();
1214 state.reprf <- noreprf;
1218 let reshape ?(firsttime=false) w h =
1219 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
1220 if not firsttime && nogeomcmds state.geomcmds
1221 then state.anchor <- getanchor ();
1223 state.winw <- w;
1224 let w = truncate (float w *. conf.zoom) in
1225 let w = max w 2 in
1226 state.winh <- h;
1227 setfontsize fstate.fontsize;
1228 GlMat.mode `modelview;
1229 GlMat.load_identity ();
1231 GlMat.mode `projection;
1232 GlMat.load_identity ();
1233 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1234 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1235 GlMat.scale3 (2.0 /. float state.winw, 2.0 /. float state.winh, 1.0);
1237 let relx =
1238 if conf.zoom <= 1.0
1239 then 0.0
1240 else float state.x /. float state.w
1242 invalidate "geometry"
1243 (fun () ->
1244 state.w <- w;
1245 if not firsttime
1246 then state.x <- truncate (relx *. float w);
1247 let w =
1248 match conf.columns with
1249 | Csingle _ -> w
1250 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
1251 | Csplit (c, _) -> w * c
1253 wcmd "geometry %d %d %d"
1254 w (stateh h) (FMTE.to_int conf.fitmodel)
1258 let enttext () =
1259 let len = String.length state.text in
1260 let x0 = if conf.leftscroll then vscrollw () else 0 in
1261 let drawstring s =
1262 let hscrollh =
1263 match state.mode with
1264 | Textentry _ | View | LinkNav _ ->
1265 let h, _, _ = state.uioh#scrollpw in
1267 | Birdseye _ -> 0
1269 let rect x w =
1270 filledrect
1271 x (float (state.winh - (fstate.fontsize + 4) - hscrollh))
1272 (x+.w) (float (state.winh - hscrollh))
1275 let w = float (state.winw - 1 - vscrollw ()) in
1276 if state.progress >= 0.0 && state.progress < 1.0
1277 then (
1278 GlDraw.color (0.3, 0.3, 0.3);
1279 let w1 = w *. state.progress in
1280 rect (float x0) w1;
1281 GlDraw.color (0.0, 0.0, 0.0);
1282 rect (float x0+.w1) (float x0+.w-.w1)
1284 else (
1285 GlDraw.color (0.0, 0.0, 0.0);
1286 rect (float x0) w;
1289 GlDraw.color (1.0, 1.0, 1.0);
1290 drawstring
1291 fstate.fontsize
1292 (if conf.leftscroll then x0 + 2 else x0 + if len > 0 then 8 else 2)
1293 (state.winh - hscrollh - 5) s;
1295 let s =
1296 match state.mode with
1297 | Textentry ((prefix, text, _, _, _, _), _) ->
1298 let s =
1299 if len > 0
1300 then Printf.sprintf "%s%s_ [%s]" prefix text state.text
1301 else Printf.sprintf "%s%s_" prefix text
1305 | Birdseye _ | View | LinkNav _ -> state.text
1307 let s =
1308 if state.newerrmsgs
1309 then (
1310 if not (istextentry state.mode) && state.uioh#eformsgs
1311 then
1312 let s1 = "(press 'e' to review error messasges)" in
1313 if nonemptystr s then s ^ " " ^ s1 else s1
1314 else s
1316 else s
1318 if nonemptystr s
1319 then drawstring s
1322 let gctiles () =
1323 let len = Queue.length state.tilelru in
1324 let layout = lazy (
1325 match state.throttle with
1326 | None ->
1327 if conf.preload
1328 then preloadlayout state.x state.y state.winw state.winh
1329 else state.layout
1330 | Some (layout, _, _) ->
1331 layout
1332 ) in
1333 let rec loop qpos =
1334 if state.memused > conf.memlimit
1335 then (
1336 if qpos < len
1337 then
1338 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1339 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1340 let (_, pw, ph, _) = getpagedim n in
1341 if gen = state.gen
1342 && colorspace = conf.colorspace
1343 && angle = conf.angle
1344 && pagew = pw
1345 && pageh = ph
1346 && (
1347 let x = col*conf.tilew
1348 and y = row*conf.tileh in
1349 tilevisible (Lazy.force_val layout) n x y
1351 then Queue.push lruitem state.tilelru
1352 else (
1353 freepbo p;
1354 wcmd "freetile %s" (~> p);
1355 state.memused <- state.memused - s;
1356 state.uioh#infochanged Memused;
1357 Hashtbl.remove state.tilemap k;
1359 loop (qpos+1)
1362 loop 0
1365 let onpagerect pageno f =
1366 let b =
1367 match conf.columns with
1368 | Cmulti (_, b) -> b
1369 | Csingle b -> b
1370 | Csplit (_, b) -> b
1372 if pageno >= 0 && pageno < Array.length b
1373 then
1374 let (_, _, _, (_, w, h, _)) = b.(pageno) in
1375 f w h
1378 let gotopagexy1 pageno x y =
1379 let _,w1,h1,leftx = getpagedim pageno in
1380 let top = y /. (float h1) in
1381 let left = x /. (float w1) in
1382 let py, w, h = getpageywh pageno in
1383 let wh = state.winh in
1384 let x = left *. (float w) in
1385 let x = leftx + state.x + truncate x in
1386 let sx =
1387 if x < 0 || x >= state.winw
1388 then state.x - x
1389 else state.x
1391 let pdy = truncate (top *. float h) in
1392 let y' = py + pdy in
1393 let dy = y' - state.y in
1394 let sy =
1395 if x != state.x || not (dy > 0 && dy < wh)
1396 then (
1397 if conf.presentation
1398 then
1399 if abs (py - y') > wh
1400 then y'
1401 else py
1402 else y';
1404 else state.y
1406 if state.x != sx || state.y != sy
1407 then gotoxy sx sy
1408 else gotoxy state.x state.y;
1411 let gotopagexy pageno x y =
1412 match state.mode with
1413 | Birdseye _ -> gotopage pageno 0.0
1414 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
1417 let getpassword () =
1418 let passcmd = getenvwithdef "LLPP_ASKPASS" conf.passcmd in
1419 if emptystr passcmd
1420 then E.s
1421 else getcmdoutput
1422 (fun s ->
1423 impmsg "error getting password: %s" s;
1424 dolog "%s" s) passcmd;
1427 let pgoto opaque pageno x y =
1428 let pdimno = getpdimno pageno in
1429 let x, y = project opaque pageno pdimno x y in
1430 gotopagexy pageno x y;
1433 let act cmds =
1434 (* dolog "%S" cmds; *)
1435 let spl = splitatchar cmds ' ' in
1436 let scan s fmt f =
1437 try Scanf.sscanf s fmt f
1438 with exn ->
1439 dolog "error processing '%S': %s" cmds @@ exntos exn;
1440 exit 1
1442 let addoutline outline =
1443 match state.currently with
1444 | Outlining outlines -> state.currently <- Outlining (outline :: outlines)
1445 | Idle -> state.currently <- Outlining [outline]
1446 | Loading _ | Tiling _ ->
1447 dolog "invalid outlining state";
1448 logcurrently state.currently
1450 match spl with
1451 | "clear", "" ->
1452 state.pdims <- [];
1453 state.uioh#infochanged Pdim;
1455 | "clearrects", "" ->
1456 state.rects <- state.rects1;
1457 G.postRedisplay "clearrects";
1459 | "continue", args ->
1460 let n = scan args "%u" (fun n -> n) in
1461 state.pagecount <- n;
1462 begin match state.currently with
1463 | Outlining l ->
1464 state.currently <- Idle;
1465 state.outlines <- Array.of_list (List.rev l)
1466 | Idle | Loading _ | Tiling _ -> ()
1467 end;
1469 let cur, cmds = state.geomcmds in
1470 if emptystr cur
1471 then failwith "umpossible";
1473 begin match List.rev cmds with
1474 | [] ->
1475 state.geomcmds <- E.s, [];
1476 state.throttle <- None;
1477 represent ();
1478 | (s, f) :: rest ->
1479 f ();
1480 state.geomcmds <- s, List.rev rest;
1481 end;
1482 if conf.maxwait = None
1483 then G.postRedisplay "continue";
1485 | "msg", args ->
1486 showtext ' ' args
1488 | "vmsg", args ->
1489 if conf.verbose
1490 then showtext ' ' args
1492 | "emsg", args ->
1493 Buffer.add_string state.errmsgs args;
1494 state.newerrmsgs <- true;
1495 G.postRedisplay "error message"
1497 | "progress", args ->
1498 let progress, text =
1499 scan args "%f %n"
1500 (fun f pos ->
1501 f, String.sub args pos (String.length args - pos))
1503 state.text <- text;
1504 state.progress <- progress;
1505 G.postRedisplay "progress"
1507 | "firstmatch", args ->
1508 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1509 scan args "%u %d %f %f %f %f %f %f %f %f"
1510 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1511 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1513 let y = (getpagey pageno) + truncate y0 in
1514 let x =
1515 if (state.x < - truncate x0) || (state.x > state.winw - truncate x1)
1516 then state.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1517 else state.x
1519 addnav ();
1520 gotoxy x y;
1521 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1522 state.rects1 <- [pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)]
1524 | "match", args ->
1525 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1526 scan args "%u %d %f %f %f %f %f %f %f %f"
1527 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1528 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1530 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
1531 state.rects1 <-
1532 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1534 | "page", args ->
1535 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1536 let pageopaque = ~< pageopaques in
1537 begin match state.currently with
1538 | Loading (l, gen) ->
1539 vlog "page %d took %f sec" l.pageno t;
1540 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1541 begin match state.throttle with
1542 | None ->
1543 let preloadedpages =
1544 if conf.preload
1545 then preloadlayout state.x state.y state.winw state.winh
1546 else state.layout
1548 let evict () =
1549 let set =
1550 List.fold_left (fun s l -> IntSet.add l.pageno s)
1551 IntSet.empty preloadedpages
1553 let evictedpages =
1554 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1555 if not (IntSet.mem pageno set)
1556 then (
1557 wcmd "freepage %s" (~> opaque);
1558 key :: accu
1560 else accu
1561 ) state.pagemap []
1563 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1565 evict ();
1566 state.currently <- Idle;
1567 if gen = state.gen
1568 then (
1569 tilepage l.pageno pageopaque state.layout;
1570 load state.layout;
1571 load preloadedpages;
1572 let visible = pagevisible state.layout l.pageno in
1573 if visible
1574 then (
1575 match state.mode with
1576 | LinkNav (Ltnotready (pageno, dir)) ->
1577 if pageno = l.pageno
1578 then (
1579 let link =
1580 let ld =
1581 if dir = 0
1582 then LDfirstvisible (l.pagex, l.pagey, dir)
1583 else (
1584 if dir > 0 then LDfirst else LDlast
1587 findlink pageopaque ld
1589 match link with
1590 | Lnotfound -> ()
1591 | Lfound n ->
1592 showlinktype (getlink pageopaque n);
1593 state.mode <- LinkNav (Ltexact (l.pageno, n))
1595 | LinkNav (Ltgendir _)
1596 | LinkNav (Ltexact _)
1597 | View
1598 | Birdseye _
1599 | Textentry _ -> ()
1602 if visible && layoutready state.layout
1603 then (
1604 G.postRedisplay "page";
1608 | Some (layout, _, _) ->
1609 state.currently <- Idle;
1610 tilepage l.pageno pageopaque layout;
1611 load state.layout
1612 end;
1614 | Idle | Tiling _ | Outlining _ ->
1615 dolog "Inconsistent loading state";
1616 logcurrently state.currently;
1617 exit 1
1620 | "tile" , args ->
1621 let (x, y, opaques, size, t) =
1622 scan args "%u %u %s %u %f"
1623 (fun x y p size t -> (x, y, p, size, t))
1625 let opaque = ~< opaques in
1626 begin match state.currently with
1627 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1628 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1630 unmappbo opaque;
1631 if tilew != conf.tilew || tileh != conf.tileh
1632 then (
1633 wcmd "freetile %s" (~> opaque);
1634 state.currently <- Idle;
1635 load state.layout;
1637 else (
1638 puttileopaque l col row gen cs angle opaque size t;
1639 state.memused <- state.memused + size;
1640 state.uioh#infochanged Memused;
1641 gctiles ();
1642 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1643 opaque, size) state.tilelru;
1645 let layout =
1646 match state.throttle with
1647 | None -> state.layout
1648 | Some (layout, _, _) -> layout
1651 state.currently <- Idle;
1652 if gen = state.gen
1653 && conf.colorspace = cs
1654 && conf.angle = angle
1655 && tilevisible layout l.pageno x y
1656 then conttiling l.pageno pageopaque;
1658 begin match state.throttle with
1659 | None ->
1660 preload state.layout;
1661 if gen = state.gen
1662 && conf.colorspace = cs
1663 && conf.angle = angle
1664 && tilevisible state.layout l.pageno x y
1665 && layoutready state.layout
1666 then G.postRedisplay "tile nothrottle";
1668 | Some (layout, y, _) ->
1669 let ready = layoutready layout in
1670 if ready
1671 then (
1672 state.y <- y;
1673 state.layout <- layout;
1674 state.throttle <- None;
1675 G.postRedisplay "throttle";
1677 else load layout;
1678 end;
1681 | Idle | Loading _ | Outlining _ ->
1682 dolog "Inconsistent tiling state";
1683 logcurrently state.currently;
1684 exit 1
1687 | "pdim", args ->
1688 let (n, w, h, _) as pdim =
1689 scan args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1691 let pdim =
1692 match conf.fitmodel with
1693 | FitWidth -> pdim
1694 | FitPage | FitProportional ->
1695 match conf.columns with
1696 | Csplit _ -> (n, w, h, 0)
1697 | Csingle _ | Cmulti _ -> pdim
1699 state.pdims <- pdim :: state.pdims;
1700 state.uioh#infochanged Pdim
1702 | "o", args ->
1703 let (l, n, t, h, pos) =
1704 scan args "%u %u %d %u %n"
1705 (fun l n t h pos -> l, n, t, h, pos)
1707 let s = String.sub args pos (String.length args - pos) in
1708 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1710 | "ou", args ->
1711 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1712 let s = String.sub args pos len in
1713 let pos2 = pos + len + 1 in
1714 let uri = String.sub args pos2 (String.length args - pos2) in
1715 addoutline (s, l, Ouri uri)
1717 | "on", args ->
1718 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1719 let s = String.sub args pos (String.length args - pos) in
1720 addoutline (s, l, Onone)
1722 | "a", args ->
1723 let (n, l, t) =
1724 scan args "%u %d %d" (fun n l t -> n, l, t)
1726 state.reprf <- (fun () -> gotopagexy n (float l) (float t))
1728 | "info", args ->
1729 let c, v = splitatchar args '\t' in
1730 let s =
1731 if nonemptystr v
1732 then
1733 if c = "Title"
1734 then (
1735 conf.title <- v;
1736 if not !ignoredoctitlte
1737 then Wsi.settitle v;
1738 args
1740 else
1741 if let len = String.length c in
1742 len > 6 && ((String.sub c (len-4) 4) = "date")
1743 then (
1744 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1745 then
1746 let b = Buffer.create 10 in
1747 Printf.bprintf b "%s\t" c;
1748 let sub p l c =
1750 Buffer.add_substring b v p l;
1751 Buffer.add_char b c;
1752 with exn -> Buffer.add_string b @@ exntos exn
1754 sub 2 4 '/';
1755 sub 6 2 '/';
1756 sub 8 2 ' ';
1757 sub 10 2 ':';
1758 sub 12 2 ':';
1759 sub 14 2 ' ';
1760 Buffer.add_char b '[';
1761 Buffer.add_string b v;
1762 Buffer.add_char b ']';
1763 Buffer.contents b
1764 else args
1766 else args
1767 else args
1769 state.docinfo <- (1, s) :: state.docinfo
1771 | "infoend", "" ->
1772 state.docinfo <- List.rev state.docinfo;
1773 state.uioh#infochanged Docinfo
1775 | "pass", args ->
1776 if args = "fail"
1777 then Wsi.settitle "Wrong password";
1778 let password = getpassword () in
1779 if emptystr password
1780 then error "document is password protected"
1781 else opendoc state.path password
1783 | _ ->
1784 error "unknown cmd `%S'" cmds
1787 let onhist cb =
1788 let rc = cb.rc in
1789 let action = function
1790 | HCprev -> cbget cb ~-1
1791 | HCnext -> cbget cb 1
1792 | HCfirst -> cbget cb ~-(cb.rc)
1793 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1794 and cancel () = cb.rc <- rc
1795 in (action, cancel)
1798 let search pattern forward =
1799 match conf.columns with
1800 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1801 | Csingle _ | Cmulti _ ->
1802 if nonemptystr pattern
1803 then
1804 let pn, py =
1805 match state.layout with
1806 | [] -> 0, 0
1807 | l :: _ ->
1808 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1810 wcmd "search %d %d %d %d,%s\000"
1811 (btod conf.icase) pn py (btod forward) pattern;
1814 let [@warning "-4"] intentry text key =
1815 let text =
1816 if emptystr text && key = Keys.Ascii '-'
1817 then addchar text '-'
1818 else
1819 match key with
1820 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1821 | _ ->
1822 state.text <- "invalid key";
1823 text
1825 TEcont text
1828 let linknact f s =
1829 if nonemptystr s
1830 then (
1831 let n =
1832 let l = String.length s in
1833 let rec loop pos n =
1834 if pos = l
1835 then n
1836 else
1837 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
1838 loop (pos+1) (n*26 + m)
1839 in loop 0 0
1841 let rec loop n = function
1842 | [] -> ()
1843 | l :: rest ->
1844 match getopaque l.pageno with
1845 | None -> loop n rest
1846 | Some opaque ->
1847 let m = getlinkcount opaque in
1848 if n < m
1849 then (
1850 let under = getlink opaque n in
1851 f under
1853 else loop (n-m) rest
1855 loop n state.layout;
1859 let [@warning "-4"] linknentry text = function
1860 | Keys.Ascii c ->
1861 let text = addchar text c in
1862 linknact (fun under -> state.text <- undertext under) text;
1863 TEcont text
1864 | _ ->
1865 state.text <- Printf.sprintf "invalid key";
1866 TEcont text
1869 let [@warning "-4"] textentry text = function
1870 | Keys.Ascii c -> TEcont (addchar text c)
1871 | Keys.Code c -> TEcont (text ^ toutf8 c)
1872 | _ -> TEcont text
1875 let reqlayout angle fitmodel =
1876 match state.throttle with
1877 | None ->
1878 if nogeomcmds state.geomcmds
1879 then state.anchor <- getanchor ();
1880 conf.angle <- angle mod 360;
1881 if conf.angle != 0
1882 then (
1883 match state.mode with
1884 | LinkNav _ -> state.mode <- View
1885 | Birdseye _ | Textentry _ | View -> ()
1887 conf.fitmodel <- fitmodel;
1888 invalidate
1889 "reqlayout"
1890 (fun () ->
1891 wcmd "reqlayout %d %d %d"
1892 conf.angle (FMTE.to_int conf.fitmodel) (stateh state.winh)
1894 | _ -> ()
1897 let settrim trimmargins trimfuzz =
1898 if nogeomcmds state.geomcmds
1899 then state.anchor <- getanchor ();
1900 conf.trimmargins <- trimmargins;
1901 conf.trimfuzz <- trimfuzz;
1902 let x0, y0, x1, y1 = trimfuzz in
1903 invalidate
1904 "settrim" (fun () ->
1905 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
1906 flushpages ();
1909 let setzoom zoom =
1910 match state.throttle with
1911 | None ->
1912 let zoom = max 0.0001 zoom in
1913 if zoom <> conf.zoom
1914 then (
1915 state.prevzoom <- (conf.zoom, state.x);
1916 conf.zoom <- zoom;
1917 reshape state.winw state.winh;
1918 state.text <- Printf.sprintf "zoom is now %-5.2f" (zoom *. 100.0);
1921 | Some (layout, y, started) ->
1922 let time =
1923 match conf.maxwait with
1924 | None -> 0.0
1925 | Some t -> t
1927 let dt = now () -. started in
1928 if dt > time
1929 then (
1930 state.y <- y;
1931 load layout;
1935 let pivotzoom ?(vw=min state.w state.winw)
1936 ?(vh=min (state.maxy-state.y) state.winh)
1937 ?(x=vw/2) ?(y=vh/2) zoom =
1938 let w = float state.w /. zoom in
1939 let hw = w /. 2.0 in
1940 let ratio = float vh /. float vw in
1941 let hh = hw *. ratio in
1942 let x0 = float x -. hw
1943 and y0 = float y -. hh in
1944 gotoxy (state.x - truncate x0) (state.y + truncate y0);
1945 setzoom zoom;
1948 let pivotzoom ?vw ?vh ?x ?y zoom =
1949 if nogeomcmds state.geomcmds
1950 then
1951 if zoom > 1.0
1952 then pivotzoom ?vw ?vh ?x ?y zoom
1953 else setzoom zoom
1956 let setcolumns mode columns coverA coverB =
1957 state.prevcolumns <- Some (conf.columns, conf.zoom);
1958 if columns < 0
1959 then (
1960 if isbirdseye mode
1961 then impmsg "split mode doesn't work in bird's eye"
1962 else (
1963 conf.columns <- Csplit (-columns, E.a);
1964 state.x <- 0;
1965 conf.zoom <- 1.0;
1968 else (
1969 if columns < 2
1970 then (
1971 conf.columns <- Csingle E.a;
1972 state.x <- 0;
1973 setzoom 1.0;
1975 else (
1976 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1977 conf.zoom <- 1.0;
1980 reshape state.winw state.winh;
1983 let resetmstate () =
1984 state.mstate <- Mnone;
1985 Wsi.setcursor Wsi.CURSOR_INHERIT;
1988 let enterbirdseye () =
1989 let zoom = float conf.thumbw /. float state.winw in
1990 let birdseyepageno =
1991 let cy = state.winh / 2 in
1992 let fold = function
1993 | [] -> 0
1994 | l :: rest ->
1995 let rec fold best = function
1996 | [] -> best.pageno
1997 | l :: rest ->
1998 let d = cy - (l.pagedispy + l.pagevh/2)
1999 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2000 if abs d < abs dbest
2001 then fold l rest
2002 else best.pageno
2003 in fold l rest
2005 fold state.layout
2007 state.mode <-
2008 Birdseye (
2009 { conf with zoom = conf.zoom },
2010 state.x, birdseyepageno, -1, getanchor ()
2012 resetmstate ();
2013 conf.zoom <- zoom;
2014 conf.presentation <- false;
2015 conf.interpagespace <- 10;
2016 conf.hlinks <- false;
2017 conf.fitmodel <- FitPage;
2018 state.x <- 0;
2019 conf.maxwait <- None;
2020 conf.columns <- (
2021 match conf.beyecolumns with
2022 | Some c ->
2023 conf.zoom <- 1.0;
2024 Cmulti ((c, 0, 0), E.a)
2025 | None -> Csingle E.a
2027 if conf.verbose
2028 then
2029 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2030 (100.0*.zoom)
2031 else
2032 state.text <- E.s
2034 reshape state.winw state.winh;
2037 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2038 state.mode <- View;
2039 conf.zoom <- c.zoom;
2040 conf.presentation <- c.presentation;
2041 conf.interpagespace <- c.interpagespace;
2042 conf.maxwait <- c.maxwait;
2043 conf.hlinks <- c.hlinks;
2044 conf.fitmodel <- c.fitmodel;
2045 conf.beyecolumns <- (
2046 match conf.columns with
2047 | Cmulti ((c, _, _), _) -> Some c
2048 | Csingle _ -> None
2049 | Csplit _ -> failwith "leaving bird's eye split mode"
2051 conf.columns <- (
2052 match c.columns with
2053 | Cmulti (c, _) -> Cmulti (c, E.a)
2054 | Csingle _ -> Csingle E.a
2055 | Csplit (c, _) -> Csplit (c, E.a)
2057 if conf.verbose
2058 then
2059 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2060 (100.0*.conf.zoom)
2062 reshape state.winw state.winh;
2063 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
2064 state.x <- leftx;
2067 let togglebirdseye () =
2068 match state.mode with
2069 | Birdseye vals -> leavebirdseye vals true
2070 | View -> enterbirdseye ()
2071 | Textentry _ | LinkNav _ -> ()
2074 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2075 let pageno = max 0 (pageno - incr) in
2076 let rec loop = function
2077 | [] -> gotopage1 pageno 0
2078 | l :: _ when l.pageno = pageno ->
2079 if l.pagedispy >= 0 && l.pagey = 0
2080 then G.postRedisplay "upbirdseye"
2081 else gotopage1 pageno 0
2082 | _ :: rest -> loop rest
2084 loop state.layout;
2085 state.text <- E.s;
2086 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2089 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2090 let pageno = min (state.pagecount - 1) (pageno + incr) in
2091 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2092 let rec loop = function
2093 | [] ->
2094 let y, h = getpageyh pageno in
2095 let dy = (y - state.y) - (state.winh - h - conf.interpagespace) in
2096 gotoxy state.x (clamp dy)
2097 | l :: _ when l.pageno = pageno ->
2098 if l.pagevh != l.pageh
2099 then gotoxy state.x (clamp (l.pageh - l.pagevh + conf.interpagespace))
2100 else G.postRedisplay "downbirdseye"
2101 | _ :: rest -> loop rest
2103 loop state.layout;
2104 state.text <- E.s;
2107 let [@warning "-4"] optentry mode _ key =
2108 let btos b = if b then "on" else "off" in
2109 match key with
2110 | Keys.Ascii 's' ->
2111 let ondone s =
2112 try conf.scrollstep <- int_of_string s with exn ->
2113 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
2115 TEswitch ("scroll step: ", E.s, None, intentry, ondone, true)
2117 | Keys.Ascii 'A' ->
2118 let ondone s =
2120 conf.autoscrollstep <- boundastep state.winh (int_of_string s);
2121 if state.autoscroll <> None
2122 then state.autoscroll <- Some conf.autoscrollstep
2123 with exn ->
2124 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
2126 TEswitch ("auto scroll step: ", E.s, None, intentry, ondone, true)
2128 | Keys.Ascii 'C' ->
2129 let ondone s =
2131 let n, a, b = multicolumns_of_string s in
2132 setcolumns mode n a b;
2133 with exn ->
2134 state.text <- Printf.sprintf "bad columns `%s': %s" s @@ exntos exn
2136 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
2138 | Keys.Ascii 'Z' ->
2139 let ondone s =
2141 let zoom = float (int_of_string s) /. 100.0 in
2142 pivotzoom zoom
2143 with exn ->
2144 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
2146 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
2148 | Keys.Ascii 't' ->
2149 let ondone s =
2151 conf.thumbw <- bound (int_of_string s) 2 4096;
2152 state.text <-
2153 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2154 begin match mode with
2155 | Birdseye beye ->
2156 leavebirdseye beye false;
2157 enterbirdseye ();
2158 | Textentry _ | View | LinkNav _ -> ();
2160 with exn ->
2161 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
2163 TEswitch ("thumbnail width: ", E.s, None, intentry, ondone, true)
2165 | Keys.Ascii 'R' ->
2166 let ondone s =
2167 match int_of_string s with
2168 | angle -> reqlayout angle conf.fitmodel
2169 | exception exn ->
2170 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
2172 TEswitch ("rotation: ", E.s, None, intentry, ondone, true)
2174 | Keys.Ascii 'i' ->
2175 conf.icase <- not conf.icase;
2176 TEdone ("case insensitive search " ^ (btos conf.icase))
2178 | Keys.Ascii 'p' ->
2179 conf.preload <- not conf.preload;
2180 gotoxy state.x state.y;
2181 TEdone ("preload " ^ (btos conf.preload))
2183 | Keys.Ascii 'v' ->
2184 conf.verbose <- not conf.verbose;
2185 TEdone ("verbose " ^ (btos conf.verbose))
2187 | Keys.Ascii 'd' ->
2188 conf.debug <- not conf.debug;
2189 TEdone ("debug " ^ (btos conf.debug))
2191 | Keys.Ascii 'h' ->
2192 conf.maxhfit <- not conf.maxhfit;
2193 state.maxy <- calcheight ();
2194 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2196 | Keys.Ascii 'c' ->
2197 conf.crophack <- not conf.crophack;
2198 TEdone ("crophack " ^ btos conf.crophack)
2200 | Keys.Ascii 'a' ->
2201 let s =
2202 match conf.maxwait with
2203 | None ->
2204 conf.maxwait <- Some infinity;
2205 "always wait for page to complete"
2206 | Some _ ->
2207 conf.maxwait <- None;
2208 "show placeholder if page is not ready"
2210 TEdone s
2212 | Keys.Ascii 'f' ->
2213 conf.underinfo <- not conf.underinfo;
2214 TEdone ("underinfo " ^ btos conf.underinfo)
2216 | Keys.Ascii 'P' ->
2217 conf.savebmarks <- not conf.savebmarks;
2218 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2220 | Keys.Ascii 'S' ->
2221 let ondone s =
2223 let pageno, py =
2224 match state.layout with
2225 | [] -> 0, 0
2226 | l :: _ ->
2227 l.pageno, l.pagey
2229 conf.interpagespace <- int_of_string s;
2230 docolumns conf.columns;
2231 state.maxy <- calcheight ();
2232 let y = getpagey pageno in
2233 gotoxy state.x (y + py)
2234 with exn ->
2235 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn
2237 TEswitch ("vertical margin: ", E.s, None, intentry, ondone, true)
2239 | Keys.Ascii 'l' ->
2240 let fm =
2241 match conf.fitmodel with
2242 | FitProportional -> FitWidth
2243 | FitWidth | FitPage -> FitProportional
2245 reqlayout conf.angle fm;
2246 TEdone ("proportional display " ^ btos (fm == FitProportional))
2248 | Keys.Ascii 'T' ->
2249 settrim (not conf.trimmargins) conf.trimfuzz;
2250 TEdone ("trim margins " ^ btos conf.trimmargins)
2252 | Keys.Ascii 'I' ->
2253 conf.invert <- not conf.invert;
2254 TEdone ("invert colors " ^ btos conf.invert)
2256 | Keys.Ascii 'x' ->
2257 let ondone s =
2258 cbput state.hists.sel s;
2259 conf.selcmd <- s;
2261 TEswitch ("selection command: ", E.s, Some (onhist state.hists.sel),
2262 textentry, ondone, true)
2264 | Keys.Ascii 'M' ->
2265 if conf.pax == None
2266 then conf.pax <- Some 0.0
2267 else conf.pax <- None;
2268 TEdone ("PAX " ^ btos (conf.pax != None))
2270 | (Keys.Ascii c) ->
2271 state.text <- Printf.sprintf "bad option %d `%c'"
2272 (Char.code c) c;
2273 TEstop
2275 | _ ->
2276 TEcont state.text
2279 class type lvsource =
2280 object
2281 method getitemcount : int
2282 method getitem : int -> (string * int)
2283 method hasaction : int -> bool
2284 method exit : uioh:uioh ->
2285 cancel:bool ->
2286 active:int ->
2287 first:int ->
2288 pan:int ->
2289 uioh option
2290 method getactive : int
2291 method getfirst : int
2292 method getpan : int
2293 method getminfo : (int * int) array
2294 end;;
2296 class virtual lvsourcebase = object
2297 val mutable m_active = 0
2298 val mutable m_first = 0
2299 val mutable m_pan = 0
2300 method getactive = m_active
2301 method getfirst = m_first
2302 method getpan = m_pan
2303 method getminfo : (int * int) array = E.a
2304 end;;
2306 let [@warning "-4"]
2307 textentrykeyboard
2308 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
2309 state.text <- E.s;
2310 let enttext te =
2311 state.mode <- Textentry (te, onleave);
2312 enttext ();
2313 G.postRedisplay "textentrykeyboard enttext";
2315 let histaction cmd =
2316 match opthist with
2317 | None -> ()
2318 | Some (action, _) ->
2319 state.mode <-
2320 Textentry (
2321 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
2323 G.postRedisplay "textentry histaction"
2325 let open Keys in
2326 let kt = Wsi.kc2kt key in
2327 match kt with
2328 | Backspace ->
2329 if emptystr text && cancelonempty
2330 then (
2331 onleave Cancel;
2332 G.postRedisplay "textentrykeyboard after cancel";
2334 else
2335 let s = withoutlastutf8 text in
2336 enttext (c, s, opthist, onkey, ondone, cancelonempty)
2338 | Enter ->
2339 ondone text;
2340 onleave Confirm;
2341 G.postRedisplay "textentrykeyboard after confirm"
2343 | Up -> histaction HCprev
2344 | Down -> histaction HCnext
2345 | Home -> histaction HCfirst
2346 | End -> histaction HClast
2348 | Escape ->
2349 if emptystr text
2350 then (
2351 begin match opthist with
2352 | None -> ()
2353 | Some (_, onhistcancel) -> onhistcancel ()
2354 end;
2355 onleave Cancel;
2356 state.text <- E.s;
2357 G.postRedisplay "textentrykeyboard after cancel2"
2359 else (
2360 enttext (c, E.s, opthist, onkey, ondone, cancelonempty)
2363 | Delete -> ()
2365 | Code _ | Ascii _ ->
2366 begin match onkey text kt with
2367 | TEdone text ->
2368 ondone text;
2369 onleave Confirm;
2370 G.postRedisplay "textentrykeyboard after confirm2";
2372 | TEcont text ->
2373 enttext (c, text, opthist, onkey, ondone, cancelonempty);
2375 | TEstop ->
2376 onleave Cancel;
2377 G.postRedisplay "textentrykeyboard after cancel3"
2379 | TEswitch te ->
2380 state.mode <- Textentry (te, onleave);
2381 G.postRedisplay "textentrykeyboard switch";
2383 | _ -> vlog "unhandled key"
2386 let firstof first active =
2387 if first > active || abs (first - active) > fstate.maxrows - 1
2388 then max 0 (active - (fstate.maxrows/2))
2389 else first
2392 let calcfirst first active =
2393 if active > first
2394 then
2395 let rows = active - first in
2396 if rows > fstate.maxrows then active - fstate.maxrows else first
2397 else active
2400 let scrollph y maxy =
2401 let sh = float (maxy + state.winh) /. float state.winh in
2402 let sh = float state.winh /. sh in
2403 let sh = max sh (float conf.scrollh) in
2405 let percent = float y /. float maxy in
2406 let position = (float state.winh -. sh) *. percent in
2408 let position =
2409 if position +. sh > float state.winh
2410 then float state.winh -. sh
2411 else position
2413 position, sh;
2416 let adderrmsg src msg =
2417 Buffer.add_string state.errmsgs msg;
2418 state.newerrmsgs <- true;
2419 G.postRedisplay src
2422 let adderrfmt src fmt =
2423 Format.ksprintf (fun s -> adderrmsg src s) fmt;
2426 let coe s = (s :> uioh);;
2428 class listview ~zebra ~helpmode ~(source:lvsource) ~trusted ~modehash =
2429 object (self)
2430 val m_pan = source#getpan
2431 val m_first = source#getfirst
2432 val m_active = source#getactive
2433 val m_qsearch = E.s
2434 val m_prev_uioh = state.uioh
2436 method private elemunder y =
2437 if y < 0
2438 then None
2439 else
2440 let n = y / (fstate.fontsize+1) in
2441 if m_first + n < source#getitemcount
2442 then (
2443 if source#hasaction (m_first + n)
2444 then Some (m_first + n)
2445 else None
2447 else None
2449 method display =
2450 Gl.enable `blend;
2451 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
2452 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2453 filledrect 0. 0. (float state.winw) (float state.winh);
2454 GlDraw.color (1., 1., 1.);
2455 Gl.enable `texture_2d;
2456 let fs = fstate.fontsize in
2457 let nfs = fs + 1 in
2458 let hw = state.winw/3 in
2459 let ww = fstate.wwidth in
2460 let tabw = 17.0*.ww in
2461 let itemcount = source#getitemcount in
2462 let minfo = source#getminfo in
2463 if conf.leftscroll
2464 then (
2465 GlMat.push ();
2466 GlMat.translate ~x:(float conf.scrollbw) ();
2468 let x0 = 0.0 and x1 = float (state.winw - conf.scrollbw - 1) in
2469 let rec loop row =
2470 if (row - m_first) > fstate.maxrows
2471 then ()
2472 else (
2473 if row >= 0 && row < itemcount
2474 then (
2475 let (s, level) = source#getitem row in
2476 let y = (row - m_first) * nfs in
2477 let x = 5.0 +. (float (level + m_pan)) *. ww in
2478 if helpmode
2479 then GlDraw.color
2480 (let c = if row land 1 = 0 then 1.0 else 0.92 in (c,c,c));
2482 if row = m_active
2483 then (
2484 Gl.disable `texture_2d;
2485 let alpha = if source#hasaction row then 0.9 else 0.3 in
2486 GlDraw.color (1., 1., 1.) ~alpha;
2487 linerect (x0 +. 1.) (float (y + 1)) (x1) (float (y + fs + 3));
2488 Gl.enable `texture_2d;
2490 let c =
2491 if zebra && row land 1 = 1
2492 then 0.8
2493 else 1.0
2495 GlDraw.color (c,c,c);
2496 let drawtabularstring s =
2497 let drawstr x s =
2498 let x' = truncate (x0 +. x) in
2499 let s1, s2 = splitatchar s '\000' in
2500 if emptystr s2
2501 then drawstring1 fs x' (y+nfs) s
2502 else
2503 let rec e s =
2504 if emptystr s
2505 then s
2506 else
2507 let s' = withoutlastutf8 s in
2508 let s = s' ^ UniSyms.ellipsis in
2509 let w = measurestr fs s in
2510 if float x' +. w +. ww < float (hw + x')
2511 then s
2512 else e s'
2514 let s1 =
2515 if float x' +. ww +. measurestr fs s1 > float (hw + x')
2516 then e s1
2517 else s1
2519 ignore (drawstring1 fs x' (y+nfs) s1);
2520 drawstring1 fs (hw + x') (y+nfs) s2
2522 if trusted
2523 then
2524 let x = if helpmode && row > 0 then x +. ww else x in
2525 let s1, s2 = splitatchar s '\t' in
2526 if nonemptystr s2
2527 then
2528 let nx = drawstr x s1 in
2529 let sw = nx -. x in
2530 let x = x +. (max tabw sw) in
2531 drawstr x s2
2532 else
2533 let len = String.length s - 2 in
2534 if len > 0 && s.[0] = '\xc2' && s.[1] = '\xb7'
2535 then
2536 let s = String.sub s 2 len in
2537 let x = if not helpmode then x +. ww else x in
2538 GlDraw.color (1.2, 1.2, 1.2);
2539 let vinc = drawstring1 (fs+fs/4)
2540 (truncate (x -. ww)) (y+nfs) s in
2541 GlDraw.color (1., 1., 1.);
2542 vinc +. (float fs *. 0.8)
2543 else
2544 drawstr x s
2545 else
2546 drawstr x s
2548 ignore (drawtabularstring s);
2549 loop (row+1)
2553 loop m_first;
2554 GlDraw.color (1.0, 1.0, 1.0) ~alpha:0.5;
2555 let xadj = 5.0 in
2556 let rec loop row =
2557 if (row - m_first) <= fstate.maxrows
2558 then
2559 if row >= 0 && row < itemcount
2560 then (
2561 let (s, level) = source#getitem row in
2562 let pos0 = nindex s '\000' in
2563 let y = (row - m_first) * nfs in
2564 let x = float (level + m_pan) *. ww in
2565 let (first, last) = minfo.(row) in
2566 let prefix =
2567 if pos0 > 0 && first > pos0
2568 then String.sub s (pos0+1) (first-pos0-1)
2569 else String.sub s 0 first
2571 let suffix = String.sub s first (last - first) in
2572 let w1 = measurestr fstate.fontsize prefix in
2573 let w2 = measurestr fstate.fontsize suffix in
2574 let x = x +. if conf.leftscroll then xadj else 5.0 in
2575 let x = if pos0 > 0 && first > pos0 then x +. float hw else x in
2576 let x0 = x +. w1
2577 and y0 = float (y+2) in
2578 let x1 = x0 +. w2
2579 and y1 = float (y+fs+3) in
2580 filledrect x0 y0 x1 y1;
2581 loop (row+1)
2584 Gl.disable `texture_2d;
2585 if Array.length minfo > 0 then loop m_first;
2586 Gl.disable `blend;
2587 if conf.leftscroll
2588 then GlMat.pop ()
2590 method updownlevel incr =
2591 let len = source#getitemcount in
2592 let curlevel =
2593 if m_active >= 0 && m_active < len
2594 then snd (source#getitem m_active)
2595 else -1
2597 let rec flow i =
2598 if i = len then i-1 else if i = -1 then 0 else
2599 let _, l = source#getitem i in
2600 if l != curlevel then i else flow (i+incr)
2602 let active = flow m_active in
2603 let first = calcfirst m_first active in
2604 G.postRedisplay "outline updownlevel";
2605 {< m_active = active; m_first = first >}
2607 method private key1 key mask =
2608 let set1 active first qsearch =
2609 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2611 let search active pattern incr =
2612 let active = if active = -1 then m_first else active in
2613 let dosearch re =
2614 let rec loop n =
2615 if n >= 0 && n < source#getitemcount
2616 then (
2617 let s, _ = source#getitem n in
2618 match Str.search_forward re s 0 with
2619 | exception Not_found -> loop (n + incr)
2620 | _ -> Some n
2622 else None
2624 loop active
2626 let qpat = Str.quote pattern in
2627 match Str.regexp_case_fold qpat with
2628 | s -> dosearch s
2629 | exception exn ->
2630 adderrfmt "listview key1" "regexp_case_fold for `%S' failed: %S\n"
2631 qpat @@ Printexc.to_string exn;
2632 None
2634 let itemcount = source#getitemcount in
2635 let find start incr =
2636 let rec find i =
2637 if i = -1 || i = itemcount
2638 then -1
2639 else (
2640 if source#hasaction i
2641 then i
2642 else find (i + incr)
2645 find start
2647 let set active first =
2648 let first = bound first 0 (itemcount - fstate.maxrows) in
2649 state.text <- E.s;
2650 coe {< m_active = active; m_first = first; m_qsearch = E.s >}
2652 let navigate incr =
2653 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2654 let active, first =
2655 let incr1 = if incr > 0 then 1 else -1 in
2656 if isvisible m_first m_active
2657 then
2658 let next =
2659 let next = m_active + incr in
2660 let next =
2661 if next < 0 || next >= itemcount
2662 then -1
2663 else find next incr1
2665 if abs (m_active - next) > fstate.maxrows
2666 then -1
2667 else next
2669 if next = -1
2670 then
2671 let first = m_first + incr in
2672 let first = bound first 0 (itemcount - fstate.maxrows) in
2673 let next =
2674 let next = m_active + incr in
2675 let next = bound next 0 (itemcount - 1) in
2676 find next ~-incr1
2678 let active =
2679 if next = -1
2680 then m_active
2681 else (
2682 if isvisible first next
2683 then next
2684 else m_active
2687 active, first
2688 else
2689 let first = min next m_first in
2690 let first =
2691 if abs (next - first) > fstate.maxrows
2692 then first + incr
2693 else first
2695 next, first
2696 else
2697 let first = m_first + incr in
2698 let first = bound first 0 (itemcount - 1) in
2699 let active =
2700 let next = m_active + incr in
2701 let next = bound next 0 (itemcount - 1) in
2702 let next = find next incr1 in
2703 let active =
2704 if next = -1 || abs (m_active - first) > fstate.maxrows
2705 then (
2706 let active = if m_active = -1 then next else m_active in
2707 active
2709 else next
2711 if isvisible first active
2712 then active
2713 else -1
2715 active, first
2717 G.postRedisplay "listview navigate";
2718 set active first;
2720 let open Keys in
2721 let kt = Wsi.kc2kt key in
2722 match [@warning "-4"] kt with
2723 | Ascii (('r'|'s') as c) when Wsi.withctrl mask ->
2724 let incr = if c = 'r' then -1 else 1 in
2725 let active, first =
2726 match search (m_active + incr) m_qsearch incr with
2727 | None ->
2728 state.text <- m_qsearch ^ " [not found]";
2729 m_active, m_first
2730 | Some active ->
2731 state.text <- m_qsearch;
2732 active, firstof m_first active
2734 G.postRedisplay "listview ctrl-r/s";
2735 set1 active first m_qsearch;
2737 | Insert when Wsi.withctrl mask ->
2738 if m_active >= 0 && m_active < source#getitemcount
2739 then (
2740 let s, _ = source#getitem m_active in
2741 selstring s;
2743 coe self
2745 | Backspace ->
2746 if emptystr m_qsearch
2747 then coe self
2748 else (
2749 let qsearch = withoutlastutf8 m_qsearch in
2750 if emptystr qsearch
2751 then (
2752 state.text <- E.s;
2753 G.postRedisplay "listview empty qsearch";
2754 set1 m_active m_first E.s;
2756 else
2757 let active, first =
2758 match search m_active qsearch ~-1 with
2759 | None ->
2760 state.text <- qsearch ^ " [not found]";
2761 m_active, m_first
2762 | Some active ->
2763 state.text <- qsearch;
2764 active, firstof m_first active
2766 G.postRedisplay "listview backspace qsearch";
2767 set1 active first qsearch
2770 | Ascii _ | Code _ ->
2771 let utf8 =
2772 match [@warning "-8"] kt with
2773 | Ascii c -> String.make 1 c
2774 | Code code -> toutf8 code
2776 let pattern = m_qsearch ^ utf8 in
2777 let active, first =
2778 match search m_active pattern 1 with
2779 | None ->
2780 state.text <- pattern ^ " [not found]";
2781 m_active, m_first
2782 | Some active ->
2783 state.text <- pattern;
2784 active, firstof m_first active
2786 G.postRedisplay "listview qsearch add";
2787 set1 active first pattern;
2789 | Escape ->
2790 state.text <- E.s;
2791 if emptystr m_qsearch
2792 then (
2793 G.postRedisplay "list view escape";
2794 let mx, my = state.mpos in
2795 updateunder mx my;
2796 match source#exit ~uioh:(coe self) ~cancel:true ~active:m_active
2797 ~first:m_first ~pan:m_pan with
2798 | None -> m_prev_uioh
2799 | Some uioh -> uioh
2801 else (
2802 G.postRedisplay "list view kill qsearch";
2803 coe {< m_qsearch = E.s >}
2806 | Enter ->
2807 state.text <- E.s;
2808 let self = {< m_qsearch = E.s >} in
2809 let opt =
2810 G.postRedisplay "listview enter";
2811 let cancel = not (m_active >= 0 && m_active < source#getitemcount) in
2812 source#exit ~uioh:(coe self) ~cancel
2813 ~active:m_active ~first:m_first ~pan:m_pan;
2815 begin match opt with
2816 | None -> m_prev_uioh
2817 | Some uioh -> uioh
2820 | Delete ->
2821 coe self
2823 | Up -> navigate ~-1
2824 | Down -> navigate 1
2825 | Prior -> navigate ~-(fstate.maxrows)
2826 | Next -> navigate fstate.maxrows
2828 | Right ->
2829 state.text <- E.s;
2830 G.postRedisplay "listview right";
2831 coe {< m_pan = m_pan - 1 >}
2833 | Left ->
2834 state.text <- E.s;
2835 G.postRedisplay "listview left";
2836 coe {< m_pan = m_pan + 1 >}
2838 | Home ->
2839 let active = find 0 1 in
2840 G.postRedisplay "listview home";
2841 set active 0;
2843 | End ->
2844 let first = max 0 (itemcount - fstate.maxrows) in
2845 let active = find (itemcount - 1) ~-1 in
2846 G.postRedisplay "listview end";
2847 set active first;
2849 | _ -> coe self
2851 method key key mask =
2852 match state.mode with
2853 | Textentry te ->
2854 textentrykeyboard key mask te;
2855 coe self
2856 | Birdseye _ | View | LinkNav _ -> self#key1 key mask
2858 method button button down x y _ =
2859 let opt =
2860 match button with
2861 | 1 when vscrollhit x ->
2862 G.postRedisplay "listview scroll";
2863 if down
2864 then
2865 let _, position, sh = self#scrollph in
2866 if y > truncate position && y < truncate (position +. sh)
2867 then (
2868 state.mstate <- Mscrolly;
2869 Some (coe self)
2871 else
2872 let s = float (max 0 (y - conf.scrollh)) /. float state.winh in
2873 let first = truncate (s *. float source#getitemcount) in
2874 let first = min source#getitemcount first in
2875 Some (coe {< m_first = first; m_active = first >})
2876 else (
2877 state.mstate <- Mnone;
2878 Some (coe self);
2880 | 1 when down ->
2881 begin match self#elemunder y with
2882 | Some n ->
2883 G.postRedisplay "listview click";
2884 source#exit ~uioh:(coe {< m_active = n >})
2885 ~cancel:false ~active:n ~first:m_first ~pan:m_pan
2886 | _ ->
2887 Some (coe self)
2889 | n when (n == 4 || n == 5) && not down ->
2890 let len = source#getitemcount in
2891 let first =
2892 if n = 5 && m_first + fstate.maxrows >= len
2893 then
2894 m_first
2895 else
2896 let first = m_first + (if n == 4 then -1 else 1) in
2897 bound first 0 (len - 1)
2899 G.postRedisplay "listview wheel";
2900 Some (coe {< m_first = first >})
2901 | n when (n = 6 || n = 7) && not down ->
2902 let inc = if n = 7 then -1 else 1 in
2903 G.postRedisplay "listview hwheel";
2904 Some (coe {< m_pan = m_pan + inc >})
2905 | _ ->
2906 Some (coe self)
2908 match opt with
2909 | None -> m_prev_uioh
2910 | Some uioh -> uioh
2912 method multiclick _ x y = self#button 1 true x y
2914 method motion _ y =
2915 match state.mstate with
2916 | Mscrolly ->
2917 let s = float (max 0 (y - conf.scrollh)) /. float state.winh in
2918 let first = truncate (s *. float source#getitemcount) in
2919 let first = min source#getitemcount first in
2920 G.postRedisplay "listview motion";
2921 coe {< m_first = first; m_active = first >}
2922 | Msel _
2923 | Mpan _
2924 | Mscrollx
2925 | Mzoom _
2926 | Mzoomrect _
2927 | Mnone -> coe self
2929 method pmotion x y =
2930 if x < state.winw - conf.scrollbw
2931 then
2932 let n =
2933 match self#elemunder y with
2934 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
2935 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
2937 let o =
2938 if n != m_active
2939 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
2940 else self
2942 coe o
2943 else (
2944 Wsi.setcursor Wsi.CURSOR_INHERIT;
2945 coe self
2948 method infochanged _ = ()
2950 method scrollpw = (0, 0.0, 0.0)
2951 method scrollph =
2952 let nfs = fstate.fontsize + 1 in
2953 let y = m_first * nfs in
2954 let itemcount = source#getitemcount in
2955 let maxi = max 0 (itemcount - fstate.maxrows) in
2956 let maxy = maxi * nfs in
2957 let p, h = scrollph y maxy in
2958 conf.scrollbw, p, h
2960 method modehash = modehash
2961 method eformsgs = false
2962 method alwaysscrolly = true
2963 method scroll _ dy =
2964 let self =
2965 if dy != 0 then begin
2966 let len = source#getitemcount in
2967 let first =
2968 if dy > 0 && m_first + fstate.maxrows >= len
2969 then
2970 m_first
2971 else
2972 let first = m_first + dy / 10 in
2973 bound first 0 (len - 1)
2975 G.postRedisplay "listview wheel";
2976 {< m_first = first >}
2977 end else
2978 self
2980 coe self
2982 method zoom _ _ _ = ()
2983 end;;
2985 class outlinelistview ~zebra ~source =
2986 let settext autonarrow s =
2987 if autonarrow
2988 then
2989 let ss = source#statestr in
2990 state.text <-
2991 if emptystr ss
2992 then "[" ^ s ^ "]"
2993 else "{" ^ ss ^ "} [" ^ s ^ "]"
2994 else state.text <- s
2996 object (self)
2997 inherit listview
2998 ~zebra
2999 ~helpmode:false
3000 ~source:(source :> lvsource)
3001 ~trusted:false
3002 ~modehash:(findkeyhash conf "outline")
3003 as super
3005 val m_autonarrow = false
3007 method! key key mask =
3008 let maxrows =
3009 if emptystr state.text
3010 then fstate.maxrows
3011 else fstate.maxrows - 2
3013 let calcfirst first active =
3014 if active > first
3015 then
3016 let rows = active - first in
3017 if rows > maxrows then active - maxrows else first
3018 else active
3020 let navigate incr =
3021 let active = m_active + incr in
3022 let active = bound active 0 (source#getitemcount - 1) in
3023 let first = calcfirst m_first active in
3024 G.postRedisplay "outline navigate";
3025 coe {< m_active = active; m_first = first >}
3027 let navscroll first =
3028 let active =
3029 let dist = m_active - first in
3030 if dist < 0
3031 then first
3032 else (
3033 if dist < maxrows
3034 then m_active
3035 else first + maxrows
3038 G.postRedisplay "outline navscroll";
3039 coe {< m_first = first; m_active = active >}
3041 let ctrl = Wsi.withctrl mask in
3042 let open Keys in
3043 match Wsi.kc2kt key with
3044 | Ascii 'a' when ctrl ->
3045 let text =
3046 if m_autonarrow
3047 then (source#denarrow; E.s)
3048 else (
3049 let pattern = source#renarrow in
3050 if nonemptystr m_qsearch
3051 then (source#narrow m_qsearch; m_qsearch)
3052 else pattern
3055 settext (not m_autonarrow) text;
3056 G.postRedisplay "toggle auto narrowing";
3057 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
3059 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
3060 settext true E.s;
3061 G.postRedisplay "toggle auto narrowing";
3062 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
3064 | Ascii 'n' when ctrl ->
3065 source#narrow m_qsearch;
3066 if not m_autonarrow
3067 then source#add_narrow_pattern m_qsearch;
3068 G.postRedisplay "outline ctrl-n";
3069 coe {< m_first = 0; m_active = 0 >}
3071 | Ascii 'S' when ctrl ->
3072 let active = source#calcactive (getanchor ()) in
3073 let first = firstof m_first active in
3074 G.postRedisplay "outline ctrl-s";
3075 coe {< m_first = first; m_active = active >}
3077 | Ascii 'u' when ctrl ->
3078 G.postRedisplay "outline ctrl-u";
3079 if m_autonarrow && nonemptystr m_qsearch
3080 then (
3081 ignore (source#renarrow);
3082 settext m_autonarrow E.s;
3083 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
3085 else (
3086 source#del_narrow_pattern;
3087 let pattern = source#renarrow in
3088 let text =
3089 if emptystr pattern then E.s else "Narrowed to " ^ pattern
3091 settext m_autonarrow text;
3092 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
3095 | Ascii 'l' when ctrl ->
3096 let first = max 0 (m_active - (fstate.maxrows / 2)) in
3097 G.postRedisplay "outline ctrl-l";
3098 coe {< m_first = first >}
3100 | Ascii '\t' when m_autonarrow ->
3101 if nonemptystr m_qsearch
3102 then (
3103 G.postRedisplay "outline list view tab";
3104 source#add_narrow_pattern m_qsearch;
3105 settext true E.s;
3106 coe {< m_qsearch = E.s >}
3108 else coe self
3110 | Escape when m_autonarrow ->
3111 if nonemptystr m_qsearch
3112 then source#add_narrow_pattern m_qsearch;
3113 super#key key mask
3115 | Enter when m_autonarrow ->
3116 if nonemptystr m_qsearch
3117 then source#add_narrow_pattern m_qsearch;
3118 super#key key mask
3120 | (Ascii _ | Code _) when m_autonarrow ->
3121 let pattern = m_qsearch ^ toutf8 key in
3122 G.postRedisplay "outlinelistview autonarrow add";
3123 source#narrow pattern;
3124 settext true pattern;
3125 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
3127 | Backspace when m_autonarrow ->
3128 if emptystr m_qsearch
3129 then coe self
3130 else
3131 let pattern = withoutlastutf8 m_qsearch in
3132 G.postRedisplay "outlinelistview autonarrow backspace";
3133 ignore (source#renarrow);
3134 source#narrow pattern;
3135 settext true pattern;
3136 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
3138 | Up when ctrl ->
3139 navscroll (max 0 (m_first - 1))
3141 | Down when ctrl ->
3142 navscroll (min (source#getitemcount - 1) (m_first + 1))
3144 | Up -> navigate ~-1
3145 | Down -> navigate 1
3146 | Prior -> navigate ~-(fstate.maxrows)
3147 | Next -> navigate fstate.maxrows
3149 | Right ->
3150 let o =
3151 if ctrl
3152 then (
3153 G.postRedisplay "outline ctrl right";
3154 {< m_pan = m_pan + 1 >}
3156 else self#updownlevel 1
3158 coe o
3160 | Left ->
3161 let o =
3162 if ctrl
3163 then (
3164 G.postRedisplay "outline ctrl left";
3165 {< m_pan = m_pan - 1 >}
3167 else self#updownlevel ~-1
3169 coe o
3171 | Home ->
3172 G.postRedisplay "outline home";
3173 coe {< m_first = 0; m_active = 0 >}
3175 | End ->
3176 let active = source#getitemcount - 1 in
3177 let first = max 0 (active - fstate.maxrows) in
3178 G.postRedisplay "outline end";
3179 coe {< m_active = active; m_first = first >}
3181 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
3182 super#key key mask
3183 end;;
3185 let genhistoutlines () =
3186 Config.gethist ()
3187 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
3188 compare c2.lastvisit c1.lastvisit)
3189 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
3190 let path = if nonemptystr origin then origin else path in
3191 let base = mbtoutf8 @@ Filename.basename path in
3192 (base ^ "\000" ^ c.title, 1, Ohistory hist)
3196 let gotohist (path, c, bookmarks, x, anchor, origin) =
3197 Config.save leavebirdseye;
3198 state.anchor <- anchor;
3199 state.bookmarks <- bookmarks;
3200 state.origin <- origin;
3201 state.x <- x;
3202 setconf conf c;
3203 let x0, y0, x1, y1 = conf.trimfuzz in
3204 wcmd "trimset %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
3205 reshape ~firsttime:true state.winw state.winh;
3206 opendoc path origin;
3207 setzoom c.zoom;
3210 let makecheckers () =
3211 (* Based on lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3212 following to say:
3213 converted by Issac Trotts. July 25, 2002 *)
3214 let image = GlPix.create `ubyte ~format:`luminance ~width:2 ~height:2 in
3215 Raw.sets_string (GlPix.to_raw image) ~pos:0 "\255\200\200\255";
3216 let id = GlTex.gen_texture () in
3217 GlTex.bind_texture ~target:`texture_2d id;
3218 GlPix.store (`unpack_alignment 1);
3219 GlTex.image2d image;
3220 List.iter (GlTex.parameter ~target:`texture_2d)
3221 [ `mag_filter `nearest; `min_filter `nearest ];
3225 let setcheckers enabled =
3226 match state.checkerstexid with
3227 | None ->
3228 if enabled then state.checkerstexid <- Some (makecheckers ())
3230 | Some checkerstexid ->
3231 if not enabled
3232 then (
3233 GlTex.delete_texture checkerstexid;
3234 state.checkerstexid <- None;
3238 let describe_layout layout =
3239 let d =
3240 match layout with
3241 | [] -> "Page 0"
3242 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
3243 | l :: rest ->
3244 let rangestr a b =
3245 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
3246 else
3247 let sep = if a.pageno+1 = b.pageno then ", " else UniSyms.ellipsis in
3248 Printf.sprintf "%d%s%d" (a.pageno+1) sep (b.pageno+1)
3250 let rec fold s la lb = function
3251 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
3252 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
3253 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
3255 fold "Pages" l l rest
3257 let percent =
3258 let maxy = maxy () in
3259 if maxy <= 0
3260 then 100.
3261 else 100. *. (float state.y /. float maxy)
3263 Printf.sprintf "%s of %d [%.2f%%]" d state.pagecount percent
3266 let setpresentationmode v =
3267 let n = page_of_y state.y in
3268 state.anchor <- (n, 0.0, 1.0);
3269 conf.presentation <- v;
3270 if conf.fitmodel = FitPage
3271 then reqlayout conf.angle conf.fitmodel;
3272 represent ();
3275 let enterinfomode =
3276 let btos b = if b then UniSyms.radical else E.s in
3277 let showextended = ref false in
3278 let showcolors = ref false in
3279 let leave mode _ = state.mode <- mode in
3280 let src =
3281 (object
3282 val mutable m_l = []
3283 val mutable m_a = E.a
3284 val mutable m_prev_uioh = nouioh
3285 val mutable m_prev_mode = View
3287 inherit lvsourcebase
3289 method reset prev_mode prev_uioh =
3290 m_a <- Array.of_list (List.rev m_l);
3291 m_l <- [];
3292 m_prev_mode <- prev_mode;
3293 m_prev_uioh <- prev_uioh;
3295 method int name get set =
3296 m_l <-
3297 (name, `int get, 1,
3298 Action (
3299 fun u ->
3300 let ondone s =
3301 try set (int_of_string s)
3302 with exn ->
3303 state.text <- Printf.sprintf "bad integer `%s': %s"
3304 s @@ exntos exn
3306 state.text <- E.s;
3307 let te = name ^ ": ", E.s, None, intentry, ondone, true in
3308 state.mode <- Textentry (te, leave m_prev_mode);
3310 )) :: m_l
3312 method int_with_suffix name get set =
3313 m_l <-
3314 (name, `intws get, 1,
3315 Action (
3316 fun u ->
3317 let ondone s =
3318 try set (int_of_string_with_suffix s)
3319 with exn ->
3320 state.text <- Printf.sprintf "bad integer `%s': %s"
3321 s @@ exntos exn
3323 state.text <- E.s;
3324 let te =
3325 name ^ ": ", E.s, None, intentry_with_suffix, ondone, true
3327 state.mode <- Textentry (te, leave m_prev_mode);
3329 )) :: m_l
3331 method bool ?(offset=1) ?(btos=btos) name get set =
3332 m_l <-
3333 (name, `bool (btos, get), offset, Action (
3334 fun u ->
3335 let v = get () in
3336 set (not v);
3338 )) :: m_l
3340 method color name get set =
3341 m_l <-
3342 (name, `color get, 1,
3343 Action (
3344 fun u ->
3345 let invalid = (nan, nan, nan) in
3346 let ondone s =
3347 let c =
3348 try color_of_string s
3349 with exn ->
3350 state.text <- Printf.sprintf "bad color `%s': %s"
3351 s @@ exntos exn;
3352 invalid
3354 if c <> invalid
3355 then set c;
3357 let te = name ^ ": ", E.s, None, textentry, ondone, true in
3358 state.text <- color_to_string (get ());
3359 state.mode <- Textentry (te, leave m_prev_mode);
3361 )) :: m_l
3363 method string name get set =
3364 m_l <-
3365 (name, `string get, 1,
3366 Action (
3367 fun u ->
3368 let ondone s = set s in
3369 let te = name ^ ": ", E.s, None, textentry, ondone, true in
3370 state.mode <- Textentry (te, leave m_prev_mode);
3372 )) :: m_l
3374 method colorspace name get set =
3375 m_l <-
3376 (name, `string get, 1,
3377 Action (
3378 fun _ ->
3379 let source =
3380 (object
3381 inherit lvsourcebase
3383 initializer
3384 m_active <- CSTE.to_int conf.colorspace;
3385 m_first <- 0;
3387 method getitemcount =
3388 Array.length CSTE.names
3389 method getitem n =
3390 (CSTE.names.(n), 0)
3391 method exit ~uioh ~cancel ~active ~first ~pan =
3392 ignore (uioh, first, pan);
3393 if not cancel then set active;
3394 None
3395 method hasaction _ = true
3396 end)
3398 state.text <- E.s;
3399 let modehash = findkeyhash conf "info" in
3400 coe (new listview ~zebra:false ~helpmode:false
3401 ~source ~trusted:true ~modehash)
3402 )) :: m_l
3404 method paxmark name get set =
3405 m_l <-
3406 (name, `string get, 1,
3407 Action (
3408 fun _ ->
3409 let source =
3410 (object
3411 inherit lvsourcebase
3413 initializer
3414 m_active <- MTE.to_int conf.paxmark;
3415 m_first <- 0;
3417 method getitemcount = Array.length MTE.names
3418 method getitem n = (MTE.names.(n), 0)
3419 method exit ~uioh ~cancel ~active ~first ~pan =
3420 ignore (uioh, first, pan);
3421 if not cancel then set active;
3422 None
3423 method hasaction _ = true
3424 end)
3426 state.text <- E.s;
3427 let modehash = findkeyhash conf "info" in
3428 coe (new listview ~zebra:false ~helpmode:false
3429 ~source ~trusted:true ~modehash)
3430 )) :: m_l
3432 method fitmodel name get set =
3433 m_l <-
3434 (name, `string get, 1,
3435 Action (
3436 fun _ ->
3437 let source =
3438 (object
3439 inherit lvsourcebase
3441 initializer
3442 m_active <- FMTE.to_int conf.fitmodel;
3443 m_first <- 0;
3445 method getitemcount = Array.length FMTE.names
3446 method getitem n = (FMTE.names.(n), 0)
3447 method exit ~uioh ~cancel ~active ~first ~pan =
3448 ignore (uioh, first, pan);
3449 if not cancel then set active;
3450 None
3451 method hasaction _ = true
3452 end)
3454 state.text <- E.s;
3455 let modehash = findkeyhash conf "info" in
3456 coe (new listview ~zebra:false ~helpmode:false
3457 ~source ~trusted:true ~modehash)
3458 )) :: m_l
3460 method caption s offset =
3461 m_l <- (s, `empty, offset, Noaction) :: m_l
3463 method caption2 s f offset =
3464 m_l <- (s, `string f, offset, Noaction) :: m_l
3466 method getitemcount = Array.length m_a
3468 method getitem n =
3469 let tostr = function
3470 | `int f -> string_of_int (f ())
3471 | `intws f -> string_with_suffix_of_int (f ())
3472 | `string f -> f ()
3473 | `color f -> color_to_string (f ())
3474 | `bool (btos, f) -> btos (f ())
3475 | `empty -> E.s
3477 let name, t, offset, _ = m_a.(n) in
3478 ((let s = tostr t in
3479 if nonemptystr s
3480 then Printf.sprintf "%s\t%s" name s
3481 else name),
3482 offset)
3484 method exit ~uioh ~cancel ~active ~first ~pan =
3485 let uiohopt =
3486 if not cancel
3487 then (
3488 let uioh =
3489 match m_a.(active) with
3490 | _, _, _, Action f -> f uioh
3491 | _, _, _, Noaction -> uioh
3493 Some uioh
3495 else None
3497 m_active <- active;
3498 m_first <- first;
3499 m_pan <- pan;
3500 uiohopt
3502 method hasaction n =
3503 match m_a.(n) with
3504 | _, _, _, Action _ -> true
3505 | _, _, _, Noaction -> false
3507 initializer m_active <- 1
3508 end)
3510 let rec fillsrc prevmode prevuioh =
3511 let sep () = src#caption E.s 0 in
3512 let colorp name get set =
3513 src#string name
3514 (fun () -> color_to_string (get ()))
3515 (fun v ->
3517 let c = color_of_string v in
3518 set c
3519 with exn ->
3520 state.text <-
3521 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
3524 let rgba name get set =
3525 src#string name
3526 (fun () -> rgba_to_string (get ()))
3527 (fun v ->
3529 let c = rgba_of_string v in
3530 set c
3531 with exn ->
3532 state.text <-
3533 Printf.sprintf "bad color `%s': %s" v @@ exntos exn
3536 let oldmode = state.mode in
3537 let birdseye = isbirdseye state.mode in
3539 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3541 src#bool "presentation mode"
3542 (fun () -> conf.presentation)
3543 (fun v -> setpresentationmode v);
3545 src#bool "ignore case in searches"
3546 (fun () -> conf.icase)
3547 (fun v -> conf.icase <- v);
3549 src#bool "preload"
3550 (fun () -> conf.preload)
3551 (fun v -> conf.preload <- v);
3553 src#bool "highlight links"
3554 (fun () -> conf.hlinks)
3555 (fun v -> conf.hlinks <- v);
3557 src#bool "under info"
3558 (fun () -> conf.underinfo)
3559 (fun v -> conf.underinfo <- v);
3561 src#bool "persistent bookmarks"
3562 (fun () -> conf.savebmarks)
3563 (fun v -> conf.savebmarks <- v);
3565 src#fitmodel "fit model"
3566 (fun () -> FMTE.to_string conf.fitmodel)
3567 (fun v -> reqlayout conf.angle (FMTE.of_int v));
3569 src#bool "trim margins"
3570 (fun () -> conf.trimmargins)
3571 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3573 src#bool "persistent location"
3574 (fun () -> conf.jumpback)
3575 (fun v -> conf.jumpback <- v);
3577 sep ();
3578 src#int "inter-page space"
3579 (fun () -> conf.interpagespace)
3580 (fun n ->
3581 conf.interpagespace <- n;
3582 docolumns conf.columns;
3583 let pageno, py =
3584 match state.layout with
3585 | [] -> 0, 0
3586 | l :: _ ->
3587 l.pageno, l.pagey
3589 state.maxy <- calcheight ();
3590 let y = getpagey pageno in
3591 gotoxy state.x (y + py)
3594 src#int "page bias"
3595 (fun () -> conf.pagebias)
3596 (fun v -> conf.pagebias <- v);
3598 src#int "scroll step"
3599 (fun () -> conf.scrollstep)
3600 (fun n -> conf.scrollstep <- n);
3602 src#int "horizontal scroll step"
3603 (fun () -> conf.hscrollstep)
3604 (fun v -> conf.hscrollstep <- v);
3606 src#int "auto scroll step"
3607 (fun () ->
3608 match state.autoscroll with
3609 | Some step -> step
3610 | _ -> conf.autoscrollstep)
3611 (fun n ->
3612 let n = boundastep state.winh n in
3613 if state.autoscroll <> None
3614 then state.autoscroll <- Some n;
3615 conf.autoscrollstep <- n);
3617 src#int "zoom"
3618 (fun () -> truncate (conf.zoom *. 100.))
3619 (fun v -> pivotzoom ((float v) /. 100.));
3621 src#int "rotation"
3622 (fun () -> conf.angle)
3623 (fun v -> reqlayout v conf.fitmodel);
3625 src#int "scroll bar width"
3626 (fun () -> conf.scrollbw)
3627 (fun v ->
3628 conf.scrollbw <- v;
3629 reshape state.winw state.winh;
3632 src#int "scroll handle height"
3633 (fun () -> conf.scrollh)
3634 (fun v -> conf.scrollh <- v;);
3636 src#int "thumbnail width"
3637 (fun () -> conf.thumbw)
3638 (fun v ->
3639 conf.thumbw <- min 4096 v;
3640 match oldmode with
3641 | Birdseye beye ->
3642 leavebirdseye beye false;
3643 enterbirdseye ()
3644 | Textentry _
3645 | View
3646 | LinkNav _ -> ()
3649 let mode = state.mode in
3650 src#string "columns"
3651 (fun () ->
3652 match conf.columns with
3653 | Csingle _ -> "1"
3654 | Cmulti (multi, _) -> multicolumns_to_string multi
3655 | Csplit (count, _) -> "-" ^ string_of_int count
3657 (fun v ->
3658 let n, a, b = multicolumns_of_string v in
3659 setcolumns mode n a b);
3661 sep ();
3662 src#caption "Pixmap cache" 0;
3663 src#int_with_suffix "size (advisory)"
3664 (fun () -> conf.memlimit)
3665 (fun v -> conf.memlimit <- v);
3667 src#caption2 "used"
3668 (fun () ->
3669 Printf.sprintf "%s bytes, %d tiles"
3670 (string_with_suffix_of_int state.memused)
3671 (Hashtbl.length state.tilemap)) 1;
3673 sep ();
3674 src#caption "Layout" 0;
3675 src#caption2 "Dimension"
3676 (fun () ->
3677 Printf.sprintf "%dx%d (virtual %dx%d)"
3678 state.winw state.winh
3679 state.w state.maxy)
3681 if conf.debug
3682 then
3683 src#caption2 "Position" (fun () ->
3684 Printf.sprintf "%dx%d" state.x state.y
3686 else
3687 src#caption2 "Position" (fun () -> describe_layout state.layout) 1;
3689 sep ();
3690 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3691 "Save these parameters as global defaults at exit"
3692 (fun () -> conf.bedefault)
3693 (fun v -> conf.bedefault <- v);
3695 sep ();
3696 let btos b = if b then UniSyms.lguillemet else UniSyms.rguillemet in
3697 src#bool ~offset:0 ~btos "Extended parameters"
3698 (fun () -> !showextended)
3699 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3700 if !showextended
3701 then (
3702 src#bool "checkers"
3703 (fun () -> conf.checkers)
3704 (fun v -> conf.checkers <- v; setcheckers v);
3705 src#bool "update cursor"
3706 (fun () -> conf.updatecurs)
3707 (fun v -> conf.updatecurs <- v);
3708 src#bool "scroll-bar on the left"
3709 (fun () -> conf.leftscroll)
3710 (fun v -> conf.leftscroll <- v);
3711 src#bool "verbose"
3712 (fun () -> conf.verbose)
3713 (fun v -> conf.verbose <- v);
3714 src#bool "invert colors"
3715 (fun () -> conf.invert)
3716 (fun v -> conf.invert <- v);
3717 src#bool "max fit"
3718 (fun () -> conf.maxhfit)
3719 (fun v -> conf.maxhfit <- v);
3720 src#bool "pax mode"
3721 (fun () -> conf.pax != None)
3722 (fun v ->
3723 if v
3724 then conf.pax <- Some (now ())
3725 else conf.pax <- None);
3726 src#string "uri launcher"
3727 (fun () -> conf.urilauncher)
3728 (fun v -> conf.urilauncher <- v);
3729 src#string "path launcher"
3730 (fun () -> conf.pathlauncher)
3731 (fun v -> conf.pathlauncher <- v);
3732 src#string "tile size"
3733 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3734 (fun v ->
3736 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3737 conf.tilew <- max 64 w;
3738 conf.tileh <- max 64 h;
3739 flushtiles ();
3740 with exn ->
3741 state.text <- Printf.sprintf "bad tile size `%s': %s"
3742 v @@ exntos exn
3744 src#int "texture count"
3745 (fun () -> conf.texcount)
3746 (fun v ->
3747 if realloctexts v
3748 then conf.texcount <- v
3749 else impmsg "failed to set texture count please retry later"
3751 src#int "slice height"
3752 (fun () -> conf.sliceheight)
3753 (fun v ->
3754 conf.sliceheight <- v;
3755 wcmd "sliceh %d" conf.sliceheight;
3757 src#int "anti-aliasing level"
3758 (fun () -> conf.aalevel)
3759 (fun v ->
3760 conf.aalevel <- bound v 0 8;
3761 state.anchor <- getanchor ();
3762 opendoc state.path state.password;
3764 src#string "page scroll scaling factor"
3765 (fun () -> string_of_float conf.pgscale)
3766 (fun v ->
3768 let s = float_of_string v in
3769 conf.pgscale <- s
3770 with exn ->
3771 state.text <- Printf.sprintf
3772 "bad page scroll scaling factor `%s': %s" v
3773 @@ exntos exn
3776 src#int "ui font size"
3777 (fun () -> fstate.fontsize)
3778 (fun v -> setfontsize (bound v 5 100));
3779 src#int "hint font size"
3780 (fun () -> conf.hfsize)
3781 (fun v -> conf.hfsize <- bound v 5 100);
3782 src#bool "crop hack"
3783 (fun () -> conf.crophack)
3784 (fun v -> conf.crophack <- v);
3785 src#string "trim fuzz"
3786 (fun () -> irect_to_string conf.trimfuzz)
3787 (fun v ->
3789 conf.trimfuzz <- irect_of_string v;
3790 if conf.trimmargins
3791 then settrim true conf.trimfuzz;
3792 with exn ->
3793 state.text <- Printf.sprintf "bad irect `%s': %s" v
3794 @@ exntos exn
3796 src#string "throttle"
3797 (fun () ->
3798 match conf.maxwait with
3799 | None -> "show place holder if page is not ready"
3800 | Some time ->
3801 if time = infinity
3802 then "wait for page to fully render"
3803 else
3804 "wait " ^ string_of_float time
3805 ^ " seconds before showing placeholder"
3807 (fun v ->
3809 let f = float_of_string v in
3810 if f <= 0.0
3811 then conf.maxwait <- None
3812 else conf.maxwait <- Some f
3813 with exn ->
3814 state.text <- Printf.sprintf "bad time `%s': %s" v
3815 @@ exntos exn
3817 src#string "selection command"
3818 (fun () -> conf.selcmd)
3819 (fun v -> conf.selcmd <- v);
3820 src#string "synctex command"
3821 (fun () -> conf.stcmd)
3822 (fun v -> conf.stcmd <- v);
3823 src#string "pax command"
3824 (fun () -> conf.paxcmd)
3825 (fun v -> conf.paxcmd <- v);
3826 src#string "ask password command"
3827 (fun () -> conf.passcmd)
3828 (fun v -> conf.passcmd <- v);
3829 src#string "save path command"
3830 (fun () -> conf.savecmd)
3831 (fun v -> conf.savecmd <- v);
3832 src#colorspace "color space"
3833 (fun () -> CSTE.to_string conf.colorspace)
3834 (fun v ->
3835 conf.colorspace <- CSTE.of_int v;
3836 wcmd "cs %d" v;
3837 load state.layout;
3839 src#paxmark "pax mark method"
3840 (fun () -> MTE.to_string conf.paxmark)
3841 (fun v -> conf.paxmark <- MTE.of_int v);
3842 if bousable () && !opengl_has_pbo
3843 then
3844 src#bool "use PBO"
3845 (fun () -> conf.usepbo)
3846 (fun v -> conf.usepbo <- v);
3847 src#bool "mouse wheel scrolls pages"
3848 (fun () -> conf.wheelbypage)
3849 (fun v -> conf.wheelbypage <- v);
3850 src#bool "open remote links in a new instance"
3851 (fun () -> conf.riani)
3852 (fun v -> conf.riani <- v);
3853 src#bool "edit annotations inline"
3854 (fun () -> conf.annotinline)
3855 (fun v -> conf.annotinline <- v);
3856 src#bool "coarse positioning in presentation mode"
3857 (fun () -> conf.coarseprespos)
3858 (fun v -> conf.coarseprespos <- v);
3859 src#bool "use document CSS"
3860 (fun () -> conf.usedoccss)
3861 (fun v ->
3862 conf.usedoccss <- v;
3863 state.anchor <- getanchor ();
3864 opendoc state.path state.password;
3866 src#bool ~btos "colors"
3867 (fun () -> !showcolors)
3868 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
3869 if !showcolors
3870 then (
3871 colorp " background"
3872 (fun () -> conf.bgcolor)
3873 (fun v -> conf.bgcolor <- v);
3874 rgba " scrollbar"
3875 (fun () -> conf.sbarcolor)
3876 (fun v -> conf.sbarcolor <- v);
3877 rgba " scrollbar handle"
3878 (fun () -> conf.sbarhndlcolor)
3879 (fun v -> conf.sbarhndlcolor <- v);
3883 sep ();
3884 src#caption "Document" 0;
3885 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3886 src#caption2 "Pages"
3887 (fun () -> string_of_int state.pagecount) 1;
3888 src#caption2 "Dimensions"
3889 (fun () -> string_of_int (List.length state.pdims)) 1;
3890 if nonemptystr conf.css
3891 then src#caption2 "CSS" (fun () -> conf.css) 1;
3892 if conf.trimmargins
3893 then (
3894 sep ();
3895 src#caption "Trimmed margins" 0;
3896 src#caption2 "Dimensions"
3897 (fun () -> string_of_int (List.length state.pdims)) 1;
3900 sep ();
3901 src#caption "OpenGL" 0;
3902 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
3903 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
3905 sep ();
3906 src#caption "Location" 0;
3907 if nonemptystr state.origin
3908 then src#caption ("Orign\t" ^ mbtoutf8 state.origin) 1;
3909 src#caption ("Path\t" ^ mbtoutf8 state.path) 1;
3911 src#reset prevmode prevuioh;
3913 fun () ->
3914 state.text <- E.s;
3915 resetmstate ();
3916 let prevmode = state.mode
3917 and prevuioh = state.uioh in
3918 fillsrc prevmode prevuioh;
3919 let source = (src :> lvsource) in
3920 let modehash = findkeyhash conf "info" in
3921 state.uioh <-
3922 coe (object (self)
3923 inherit listview ~zebra:false ~helpmode:false
3924 ~source ~trusted:true ~modehash as super
3925 val mutable m_prevmemused = 0
3926 method! infochanged = function
3927 | Memused ->
3928 if m_prevmemused != state.memused
3929 then (
3930 m_prevmemused <- state.memused;
3931 G.postRedisplay "memusedchanged";
3933 | Pdim -> G.postRedisplay "pdimchanged"
3934 | Docinfo -> fillsrc prevmode prevuioh
3936 method! key key mask =
3937 if not (Wsi.withctrl mask)
3938 then
3939 match [@warning "-4"] Wsi.kc2kt key with
3940 | Keys.Left -> coe (self#updownlevel ~-1)
3941 | Keys.Right -> coe (self#updownlevel 1)
3942 | _ -> super#key key mask
3943 else super#key key mask
3944 end);
3945 G.postRedisplay "info";
3948 let enterhelpmode =
3949 let source =
3950 (object
3951 inherit lvsourcebase
3952 method getitemcount = Array.length state.help
3953 method getitem n =
3954 let s, l, _ = state.help.(n) in
3955 (s, l)
3957 method exit ~uioh ~cancel ~active ~first ~pan =
3958 let optuioh =
3959 if not cancel
3960 then (
3961 match state.help.(active) with
3962 | _, _, Action f -> Some (f uioh)
3963 | _, _, Noaction -> Some uioh
3965 else None
3967 m_active <- active;
3968 m_first <- first;
3969 m_pan <- pan;
3970 optuioh
3972 method hasaction n =
3973 match state.help.(n) with
3974 | _, _, Action _ -> true
3975 | _, _, Noaction -> false
3977 initializer
3978 m_active <- -1
3979 end)
3980 in fun () ->
3981 let modehash = findkeyhash conf "help" in
3982 resetmstate ();
3983 state.uioh <- coe (new listview
3984 ~zebra:false ~helpmode:true
3985 ~source ~trusted:true ~modehash);
3986 G.postRedisplay "help";
3989 let entermsgsmode =
3990 let msgsource =
3991 (object
3992 inherit lvsourcebase
3993 val mutable m_items = E.a
3995 method getitemcount = 1 + Array.length m_items
3997 method getitem n =
3998 if n = 0
3999 then "[Clear]", 0
4000 else m_items.(n-1), 0
4002 method exit ~uioh ~cancel ~active ~first ~pan =
4003 ignore uioh;
4004 if not cancel
4005 then (
4006 if active = 0
4007 then Buffer.clear state.errmsgs;
4009 m_active <- active;
4010 m_first <- first;
4011 m_pan <- pan;
4012 None
4014 method hasaction n =
4015 n = 0
4017 method reset =
4018 state.newerrmsgs <- false;
4019 let l = Str.split newlinere (Buffer.contents state.errmsgs) in
4020 m_items <- Array.of_list l
4022 initializer
4023 m_active <- 0
4024 end)
4025 in fun () ->
4026 state.text <- E.s;
4027 resetmstate ();
4028 msgsource#reset;
4029 let source = (msgsource :> lvsource) in
4030 let modehash = findkeyhash conf "listview" in
4031 state.uioh <-
4032 coe (object
4033 inherit listview ~zebra:false ~helpmode:false
4034 ~source ~trusted:false ~modehash as super
4035 method! display =
4036 if state.newerrmsgs
4037 then msgsource#reset;
4038 super#display
4039 end);
4040 G.postRedisplay "msgs";
4043 let getusertext s =
4044 let editor = getenvwithdef "EDITOR" E.s in
4045 if emptystr editor
4046 then E.s
4047 else
4048 let tmppath = Filename.temp_file "llpp" "note" in
4049 if nonemptystr s
4050 then (
4051 let oc = open_out tmppath in
4052 output_string oc s;
4053 close_out oc;
4055 let execstr = editor ^ " " ^ tmppath in
4056 let s =
4057 match spawn execstr [] with
4058 | exception exn ->
4059 impmsg "spawn(%S) failed: %s" execstr @@ exntos exn;
4061 | pid ->
4062 match Unix.waitpid [] pid with
4063 | exception exn ->
4064 impmsg "waitpid(%d) failed: %s" pid @@ exntos exn;
4066 | (_pid, status) ->
4067 match status with
4068 | Unix.WEXITED 0 -> filecontents tmppath
4069 | Unix.WEXITED n ->
4070 impmsg "editor process(%s) exited abnormally: %d" execstr n;
4072 | Unix.WSIGNALED n ->
4073 impmsg "editor process(%s) was killed by signal %d" execstr n;
4075 | Unix.WSTOPPED n ->
4076 impmsg "editor(%s) process was stopped by signal %d" execstr n;
4079 match Unix.unlink tmppath with
4080 | exception exn ->
4081 impmsg "failed to ulink %S: %s" tmppath @@ exntos exn;
4083 | () -> s
4086 let enterannotmode opaque slinkindex =
4087 let msgsource =
4088 (object
4089 inherit lvsourcebase
4090 val mutable m_text = E.s
4091 val mutable m_items = E.a
4093 method getitemcount = Array.length m_items
4095 method getitem n =
4096 let label, _func = m_items.(n) in
4097 label, 0
4099 method exit ~uioh ~cancel ~active ~first ~pan =
4100 ignore (uioh, first, pan);
4101 if not cancel
4102 then (
4103 let _label, func = m_items.(active) in
4104 func ()
4106 None
4108 method hasaction n = nonemptystr @@ fst m_items.(n)
4110 method reset s =
4111 let rec split accu b i =
4112 let p = b+i in
4113 if p = String.length s
4114 then (String.sub s b (p-b), unit) :: accu
4115 else
4116 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
4117 then
4118 let ss = if i = 0 then E.s else String.sub s b i in
4119 split ((ss, unit)::accu) (p+1) 0
4120 else
4121 split accu b (i+1)
4123 let cleanup () =
4124 wcmd "freepage %s" (~> opaque);
4125 let keys =
4126 Hashtbl.fold (fun key opaque' accu ->
4127 if opaque' = opaque'
4128 then key :: accu else accu) state.pagemap []
4130 List.iter (Hashtbl.remove state.pagemap) keys;
4131 flushtiles ();
4132 gotoxy state.x state.y
4134 let dele () =
4135 delannot opaque slinkindex;
4136 cleanup ();
4138 let edit inline () =
4139 let update s =
4140 if emptystr s
4141 then dele ()
4142 else (
4143 modannot opaque slinkindex s;
4144 cleanup ();
4147 if inline
4148 then
4149 let mode = state.mode in
4150 state.mode <-
4151 Textentry (
4152 ("annotation: ", m_text, None, textentry, update, true),
4153 fun _ -> state.mode <- mode);
4154 state.text <- E.s;
4155 enttext ();
4156 else
4157 let s = getusertext m_text in
4158 update s
4160 m_text <- s;
4161 m_items <-
4162 ( "[Copy]", fun () -> selstring m_text)
4163 :: ("[Delete]", dele)
4164 :: ("[Edit]", edit conf.annotinline)
4165 :: (E.s, unit)
4166 :: split [] 0 0 |> List.rev |> Array.of_list
4168 initializer
4169 m_active <- 0
4170 end)
4172 state.text <- E.s;
4173 let s = getannotcontents opaque slinkindex in
4174 resetmstate ();
4175 msgsource#reset s;
4176 let source = (msgsource :> lvsource) in
4177 let modehash = findkeyhash conf "listview" in
4178 state.uioh <- coe (object
4179 inherit listview ~zebra:false ~helpmode:false
4180 ~source ~trusted:false ~modehash
4181 end);
4182 G.postRedisplay "enterannotmode";
4185 let gotoremote spec =
4186 let filename, dest = splitatchar spec '#' in
4187 let getpath filename =
4188 let path =
4189 if nonemptystr filename
4190 then
4191 if Filename.is_relative filename
4192 then
4193 let dir = Filename.dirname state.path in
4194 let dir =
4195 if Filename.is_implicit dir
4196 then Filename.concat (Sys.getcwd ()) dir
4197 else dir
4199 Filename.concat dir filename
4200 else filename
4201 else E.s
4203 if Sys.file_exists path
4204 then path
4205 else E.s
4207 let path = getpath filename in
4208 let dospawn lcmd =
4209 if conf.riani
4210 then
4211 let cmd = Lazy.force_val lcmd in
4212 match spawn cmd with
4213 | _pid -> ()
4214 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
4215 else
4216 let anchor = getanchor () in
4217 let ranchor = state.path, state.password, anchor, state.origin in
4218 state.origin <- E.s;
4219 state.ranchors <- ranchor :: state.ranchors;
4220 opendoc path E.s;
4222 if substratis spec 0 "page="
4223 then
4224 match Scanf.sscanf spec "page=%d" (fun n -> n) with
4225 | pageno ->
4226 state.anchor <- (pageno, 0.0, 0.0);
4227 dospawn @@ lazy (Printf.sprintf "%s -page %d %S" !selfexec pageno path);
4228 | exception exn ->
4229 adderrfmt "error parsing remote destination" "page: %s" @@ exntos exn
4230 else (
4231 state.nameddest <- dest;
4232 dospawn @@ lazy (!selfexec ^ " " ^ path ^ " -dest " ^ dest)
4236 let gotounder = function
4237 | Ulinkuri s when isexternallink s ->
4238 if substratis s 0 "file://"
4239 then gotoremote @@ String.sub s 7 (String.length s - 7)
4240 else gotouri s
4241 | Ulinkuri s ->
4242 let pageno, x, y = uritolocation s in
4243 addnav ();
4244 gotopagexy pageno x y
4245 | Utext _ | Unone -> ()
4246 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
4249 let gotooutline (_, _, kind) =
4250 match kind with
4251 | Onone -> ()
4252 | Oanchor anchor ->
4253 let (pageno, y, _) = anchor in
4254 let y = getanchory
4255 (if conf.presentation then (pageno, y, 1.0) else anchor)
4257 addnav ();
4258 gotoxy state.x y
4259 | Ouri uri -> gotounder (Ulinkuri uri)
4260 | Olaunch _cmd -> failwith "gotounder (Ulaunch cmd)"
4261 | Oremote _remote -> failwith "gotounder (Uremote remote)"
4262 | Ohistory hist -> gotohist hist
4263 | Oremotedest _remotedest -> failwith "gotounder (Uremotedest remotedest)"
4266 class outlinesoucebase fetchoutlines = object (self)
4267 inherit lvsourcebase
4268 val mutable m_items = E.a
4269 val mutable m_minfo = E.a
4270 val mutable m_orig_items = E.a
4271 val mutable m_orig_minfo = E.a
4272 val mutable m_narrow_patterns = []
4273 val mutable m_gen = -1
4275 method getitemcount = Array.length m_items
4277 method getitem n =
4278 let s, n, _ = m_items.(n) in
4279 (s, n+0)
4281 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
4282 ignore (uioh, first);
4283 let items, minfo =
4284 if m_narrow_patterns = []
4285 then m_orig_items, m_orig_minfo
4286 else m_items, m_minfo
4288 m_pan <- pan;
4289 if not cancel
4290 then (
4291 m_items <- items;
4292 m_minfo <- minfo;
4293 gotooutline m_items.(active);
4295 else (
4296 m_items <- items;
4297 m_minfo <- minfo;
4299 None
4301 method hasaction (_:int) = true
4303 method greetmsg =
4304 if Array.length m_items != Array.length m_orig_items
4305 then
4306 let s =
4307 match m_narrow_patterns with
4308 | one :: [] -> one
4309 | many -> String.concat UniSyms.ellipsis (List.rev many)
4311 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
4312 else E.s
4314 method statestr =
4315 match m_narrow_patterns with
4316 | [] -> E.s
4317 | one :: [] -> one
4318 | head :: _ -> UniSyms.ellipsis ^ head
4320 method narrow pattern =
4321 match Str.regexp_case_fold pattern with
4322 | exception _ -> ()
4323 | re ->
4324 let rec loop accu minfo n =
4325 if n = -1
4326 then (
4327 m_items <- Array.of_list accu;
4328 m_minfo <- Array.of_list minfo;
4330 else
4331 let (s, _, _) as o = m_items.(n) in
4332 let accu, minfo =
4333 match Str.search_forward re s 0 with
4334 | exception Not_found -> accu, minfo
4335 | first -> o :: accu, (first, Str.match_end ()) :: minfo
4337 loop accu minfo (n-1)
4339 loop [] [] (Array.length m_items - 1)
4341 method! getminfo = m_minfo
4343 method denarrow =
4344 m_orig_items <- fetchoutlines ();
4345 m_minfo <- m_orig_minfo;
4346 m_items <- m_orig_items
4348 method add_narrow_pattern pattern =
4349 m_narrow_patterns <- pattern :: m_narrow_patterns
4351 method del_narrow_pattern =
4352 match m_narrow_patterns with
4353 | _ :: rest -> m_narrow_patterns <- rest
4354 | [] -> ()
4356 method renarrow =
4357 self#denarrow;
4358 match m_narrow_patterns with
4359 | pattern :: [] -> self#narrow pattern; pattern
4360 | list ->
4361 List.fold_left (fun accu pattern ->
4362 self#narrow pattern;
4363 pattern ^ UniSyms.ellipsis ^ accu) E.s list
4365 method calcactive (_:anchor) = 0
4367 method reset anchor items =
4368 if state.gen != m_gen
4369 then (
4370 m_orig_items <- items;
4371 m_items <- items;
4372 m_narrow_patterns <- [];
4373 m_minfo <- E.a;
4374 m_orig_minfo <- E.a;
4375 m_gen <- state.gen;
4377 else (
4378 if items != m_orig_items
4379 then (
4380 m_orig_items <- items;
4381 if m_narrow_patterns == []
4382 then m_items <- items;
4385 let active = self#calcactive anchor in
4386 m_active <- active;
4387 m_first <- firstof m_first active
4391 let outlinesource fetchoutlines =
4392 (object
4393 inherit outlinesoucebase fetchoutlines
4394 method! calcactive anchor =
4395 let rely = getanchory anchor in
4396 let rec loop n best bestd =
4397 if n = Array.length m_items
4398 then best
4399 else
4400 let _, _, kind = m_items.(n) in
4401 match kind with
4402 | Oanchor anchor ->
4403 let orely = getanchory anchor in
4404 let d = abs (orely - rely) in
4405 if d < bestd
4406 then loop (n+1) n d
4407 else loop (n+1) best bestd
4408 | Onone | Oremote _ | Olaunch _
4409 | Oremotedest _ | Ouri _ | Ohistory _ ->
4410 loop (n+1) best bestd
4412 loop 0 ~-1 max_int
4413 end)
4416 let enteroutlinemode, enterbookmarkmode, enterhistmode =
4417 let mkselector sourcetype =
4418 let fetchoutlines () =
4419 match sourcetype with
4420 | `bookmarks -> Array.of_list state.bookmarks
4421 | `outlines -> state.outlines
4422 | `history -> genhistoutlines () |> Array.of_list
4424 let source =
4425 if sourcetype = `history
4426 then new outlinesoucebase fetchoutlines
4427 else outlinesource fetchoutlines
4429 (fun errmsg ->
4430 let outlines = fetchoutlines () in
4431 if Array.length outlines = 0
4432 then showtext ' ' errmsg
4433 else (
4434 resetmstate ();
4435 Wsi.setcursor Wsi.CURSOR_INHERIT;
4436 let anchor = getanchor () in
4437 source#reset anchor outlines;
4438 state.text <- source#greetmsg;
4439 state.uioh <-
4440 coe (new outlinelistview ~zebra:(sourcetype=`history) ~source);
4441 G.postRedisplay "enter selector";
4445 let mkenter sourcetype errmsg =
4446 let enter = mkselector sourcetype in
4447 fun () -> enter errmsg
4449 ( mkenter `outlines "document has no outline"
4450 , mkenter `bookmarks "document has no bookmarks (yet)"
4451 , mkenter `history "history is empty" )
4454 let quickbookmark ?title () =
4455 match state.layout with
4456 | [] -> ()
4457 | l :: _ ->
4458 let title =
4459 match title with
4460 | None ->
4461 Unix.(
4462 let tm = localtime (now ()) in
4463 Printf.sprintf
4464 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
4465 (l.pageno+1)
4466 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
4468 | Some title -> title
4470 state.bookmarks <- (title, 0, Oanchor (getanchor1 l)) :: state.bookmarks
4473 let setautoscrollspeed step goingdown =
4474 let incr = max 1 ((abs step) / 2) in
4475 let incr = if goingdown then incr else -incr in
4476 let astep = boundastep state.winh (step + incr) in
4477 state.autoscroll <- Some astep;
4480 let canpan () =
4481 match conf.columns with
4482 | Csplit _ -> true
4483 | Csingle _ | Cmulti _ -> state.x != 0 || conf.zoom > 1.0
4486 let panbound x = bound x (-state.w) state.winw;;
4488 let existsinrow pageno (columns, coverA, coverB) p =
4489 let last = ((pageno - coverA) mod columns) + columns in
4490 let rec any = function
4491 | [] -> false
4492 | l :: rest ->
4493 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
4494 then p l
4495 else (
4496 if not (p l)
4497 then (if l.pageno = last then false else any rest)
4498 else true
4501 any state.layout
4504 let nextpage () =
4505 match state.layout with
4506 | [] ->
4507 let pageno = page_of_y state.y in
4508 gotoxy state.x (getpagey (pageno+1))
4509 | l :: rest ->
4510 match conf.columns with
4511 | Csingle _ ->
4512 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4513 then
4514 let y = clamp (pgscale state.winh) in
4515 gotoxy state.x y
4516 else
4517 let pageno = min (l.pageno+1) (state.pagecount-1) in
4518 gotoxy state.x (getpagey pageno)
4519 | Cmulti ((c, _, _) as cl, _) ->
4520 if conf.presentation
4521 && (existsinrow l.pageno cl
4522 (fun l -> l.pageh > l.pagey + l.pagevh))
4523 then
4524 let y = clamp (pgscale state.winh) in
4525 gotoxy state.x y
4526 else
4527 let pageno = min (l.pageno+c) (state.pagecount-1) in
4528 gotoxy state.x (getpagey pageno)
4529 | Csplit (n, _) ->
4530 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
4531 then
4532 let pagey, pageh = getpageyh l.pageno in
4533 let pagey = pagey + pageh * l.pagecol in
4534 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
4535 gotoxy state.x (pagey + pageh + ips)
4538 let prevpage () =
4539 match state.layout with
4540 | [] ->
4541 let pageno = page_of_y state.y in
4542 gotoxy state.x (getpagey (pageno-1))
4543 | l :: _ ->
4544 match conf.columns with
4545 | Csingle _ ->
4546 if conf.presentation && l.pagey != 0
4547 then
4548 gotoxy state.x (clamp (pgscale ~-(state.winh)))
4549 else
4550 let pageno = max 0 (l.pageno-1) in
4551 gotoxy state.x (getpagey pageno)
4552 | Cmulti ((c, _, coverB) as cl, _) ->
4553 if conf.presentation &&
4554 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
4555 then
4556 gotoxy state.x (clamp (pgscale ~-(state.winh)))
4557 else
4558 let decr =
4559 if l.pageno = state.pagecount - coverB
4560 then 1
4561 else c
4563 let pageno = max 0 (l.pageno-decr) in
4564 gotoxy state.x (getpagey pageno)
4565 | Csplit (n, _) ->
4566 let y =
4567 if l.pagecol = 0
4568 then
4569 if l.pageno = 0
4570 then l.pagey
4571 else
4572 let pageno = max 0 (l.pageno-1) in
4573 let pagey, pageh = getpageyh pageno in
4574 pagey + (n-1)*pageh
4575 else
4576 let pagey, pageh = getpageyh l.pageno in
4577 pagey + pageh * (l.pagecol-1) - conf.interpagespace
4579 gotoxy state.x y
4582 let save () =
4583 if emptystr conf.savecmd
4584 then adderrmsg "savepath-command is empty"
4585 "don't know where to save modified document"
4586 else
4587 let savecmd = Str.global_replace percentsre state.path conf.savecmd in
4588 let path =
4589 getcmdoutput
4590 (fun exn ->
4591 adderrfmt savecmd "failed to produce path to the saved copy: %s" exn)
4592 savecmd
4594 if nonemptystr path
4595 then
4596 let tmp = path ^ ".tmp" in
4597 savedoc tmp;
4598 Unix.rename tmp path;
4601 let viewkeyboard key mask =
4602 let enttext te =
4603 let mode = state.mode in
4604 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4605 state.text <- E.s;
4606 enttext ();
4607 G.postRedisplay "view:enttext"
4609 let ctrl = Wsi.withctrl mask in
4610 let open Keys in
4611 match Wsi.kc2kt key with
4612 | Ascii 'S' -> state.slideshow <- state.slideshow lxor 1
4614 | Ascii 'Q' -> exit 0
4616 | Ascii 'W' ->
4617 if hasunsavedchanges ()
4618 then save ()
4620 | Insert ->
4621 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
4622 then (
4623 state.mode <- (
4624 match state.lnava with
4625 | None -> LinkNav (Ltgendir 0)
4626 | Some pn -> LinkNav (Ltexact pn)
4628 gotoxy state.x state.y;
4630 else impmsg "keyboard link navigation does not work under rotation"
4632 | Escape | Ascii 'q' ->
4633 begin match state.mstate with
4634 | Mzoomrect _ ->
4635 resetmstate ();
4636 G.postRedisplay "kill rect";
4637 | Msel _
4638 | Mpan _
4639 | Mscrolly | Mscrollx
4640 | Mzoom _
4641 | Mnone ->
4642 begin match state.mode with
4643 | LinkNav ln ->
4644 begin match ln with
4645 | Ltexact pl -> state.lnava <- Some pl
4646 | Ltgendir _ | Ltnotready _ -> state.lnava <- None
4647 end;
4648 state.mode <- View;
4649 G.postRedisplay "esc leave linknav"
4650 | Birdseye _ | Textentry _ | View ->
4651 match state.ranchors with
4652 | [] -> raise Quit
4653 | (path, password, anchor, origin) :: rest ->
4654 state.ranchors <- rest;
4655 state.anchor <- anchor;
4656 state.origin <- origin;
4657 state.nameddest <- E.s;
4658 opendoc path password
4659 end;
4660 end;
4662 | Backspace ->
4663 addnavnorc ();
4664 gotoxy state.x (getnav ~-1)
4666 | Ascii 'o' ->
4667 enteroutlinemode ()
4669 | Ascii 'H' ->
4670 enterhistmode ()
4672 | Ascii 'u' ->
4673 state.rects <- [];
4674 state.text <- E.s;
4675 Hashtbl.iter (fun _ opaque ->
4676 clearmark opaque;
4677 Hashtbl.clear state.prects) state.pagemap;
4678 G.postRedisplay "dehighlight";
4680 | Ascii (('/' | '?') as c) ->
4681 let ondone isforw s =
4682 cbput state.hists.pat s;
4683 state.searchpattern <- s;
4684 search s isforw
4686 let s = String.make 1 c in
4687 enttext (s, E.s, Some (onhist state.hists.pat),
4688 textentry, ondone (c = '/'), true)
4690 | Ascii '+' | Ascii '=' when ctrl ->
4691 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4692 pivotzoom (conf.zoom +. incr)
4694 | Ascii '+' ->
4695 let ondone s =
4696 let n =
4697 try int_of_string s with exn ->
4698 state.text <-
4699 Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
4700 max_int
4702 if n != max_int
4703 then (
4704 conf.pagebias <- n;
4705 state.text <- "page bias is now " ^ string_of_int n;
4708 enttext ("page bias: ", E.s, None, intentry, ondone, true)
4710 | Ascii '-' when ctrl ->
4711 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4712 pivotzoom (max 0.01 (conf.zoom -. decr))
4714 | Ascii '-' ->
4715 let ondone msg = state.text <- msg in
4716 enttext (
4717 "option [acfhilpstvxACFPRSZTISM]: ", E.s, None,
4718 optentry state.mode, ondone, true
4721 | Ascii '0' when ctrl ->
4722 if conf.zoom = 1.0
4723 then gotoxy 0 state.y
4724 else setzoom 1.0
4726 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
4727 let cols =
4728 match conf.columns with
4729 | Csingle _ | Cmulti _ -> 1
4730 | Csplit (n, _) -> n
4732 let h = state.winh -
4733 conf.interpagespace lsl (if conf.presentation then 1 else 0)
4735 let zoom = zoomforh state.winw h 0 cols in
4736 if zoom > 0.0 && (c = '2' || zoom < 1.0)
4737 then setzoom zoom
4739 | Ascii '3' when ctrl ->
4740 let fm =
4741 match conf.fitmodel with
4742 | FitWidth -> FitProportional
4743 | FitProportional -> FitPage
4744 | FitPage -> FitWidth
4746 state.text <- "fit model: " ^ FMTE.to_string fm;
4747 reqlayout conf.angle fm
4749 | Ascii '4' when ctrl ->
4750 let zoom = getmaxw () /. float state.winw in
4751 if zoom > 0.0 then setzoom zoom
4753 | Fn 9 ->
4754 togglebirdseye ()
4756 | Ascii '9' when ctrl ->
4757 togglebirdseye ()
4759 | Ascii ('0'..'9' as c) when not ctrl ->
4760 let ondone s =
4761 let n =
4762 try int_of_string s with exn ->
4763 state.text <- Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
4766 if n >= 0
4767 then (
4768 addnav ();
4769 cbput state.hists.pag (string_of_int n);
4770 gotopage1 (n + conf.pagebias - 1) 0;
4773 let [@warning "-4"] pageentry text = function
4774 | Keys.Ascii 'g' -> TEdone text
4775 | key -> intentry text key
4777 let text = String.make 1 c in
4778 enttext (":", text, Some (onhist state.hists.pag),
4779 pageentry, ondone, true)
4781 | Ascii 'b' ->
4782 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
4783 G.postRedisplay "toggle scrollbar";
4785 | Ascii 'B' ->
4786 state.bzoom <- not state.bzoom;
4787 state.rects <- [];
4788 showtext ' ' ("block zoom " ^ if state.bzoom then "on" else "off")
4790 | Ascii 'l' ->
4791 conf.hlinks <- not conf.hlinks;
4792 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4793 G.postRedisplay "toggle highlightlinks";
4795 | Ascii 'F' ->
4796 if conf.angle mod 360 = 0
4797 then (
4798 state.glinks <- true;
4799 let mode = state.mode in
4800 state.mode <-
4801 Textentry (
4802 (":", E.s, None, linknentry, linknact gotounder, false),
4803 (fun _ ->
4804 state.glinks <- false;
4805 state.mode <- mode)
4807 state.text <- E.s;
4808 G.postRedisplay "view:linkent(F)"
4810 else impmsg "hint mode does not work under rotation"
4812 | Ascii 'y' ->
4813 state.glinks <- true;
4814 let mode = state.mode in
4815 state.mode <-
4816 Textentry (
4817 (":", E.s, None, linknentry,
4818 linknact (fun under -> selstring (undertext under)), false),
4819 (fun _ ->
4820 state.glinks <- false;
4821 state.mode <- mode)
4823 state.text <- E.s;
4824 G.postRedisplay "view:linkent"
4826 | Ascii 'a' ->
4827 begin match state.autoscroll with
4828 | Some step ->
4829 conf.autoscrollstep <- step;
4830 state.autoscroll <- None
4831 | None ->
4832 state.autoscroll <- Some conf.autoscrollstep;
4833 state.slideshow <- state.slideshow land lnot 2
4836 | Ascii 'p' when ctrl ->
4837 launchpath () (* XXX where do error messages go? *)
4839 | Ascii 'P' ->
4840 setpresentationmode (not conf.presentation);
4841 showtext ' ' ("presentation mode " ^
4842 if conf.presentation then "on" else "off");
4844 | Ascii 'f' ->
4845 if List.mem Wsi.Fullscreen state.winstate
4846 then Wsi.reshape conf.cwinw conf.cwinh
4847 else Wsi.fullscreen ()
4849 | Ascii ('p'|'N') ->
4850 search state.searchpattern false
4852 | Ascii 'n' | Fn 3 ->
4853 search state.searchpattern true
4855 | Ascii 't' ->
4856 begin match state.layout with
4857 | [] -> ()
4858 | l :: _ ->
4859 gotoxy state.x (getpagey l.pageno)
4862 | Ascii ' ' ->
4863 nextpage ()
4865 | Delete ->
4866 prevpage ()
4868 | Ascii '=' ->
4869 showtext ' ' (describe_layout state.layout);
4871 | Ascii 'w' ->
4872 begin match state.layout with
4873 | [] -> ()
4874 | l :: _ ->
4875 Wsi.reshape l.pagew l.pageh;
4876 G.postRedisplay "w"
4879 | Ascii '\'' ->
4880 enterbookmarkmode ()
4882 | Ascii 'h' | Fn 1 ->
4883 enterhelpmode ()
4885 | Ascii 'i' ->
4886 enterinfomode ()
4888 | Ascii 'e' when Buffer.length state.errmsgs > 0 ->
4889 entermsgsmode ()
4891 | Ascii 'm' ->
4892 let ondone s =
4893 match state.layout with
4894 | l :: _ ->
4895 if nonemptystr s
4896 then
4897 state.bookmarks <-
4898 (s, 0, Oanchor (getanchor1 l)) :: state.bookmarks
4899 | _ -> ()
4901 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
4903 | Ascii '~' ->
4904 quickbookmark ();
4905 showtext ' ' "Quick bookmark added";
4907 | Ascii 'z' ->
4908 begin match state.layout with
4909 | l :: _ ->
4910 let rect = getpdimrect l.pagedimno in
4911 let w, h =
4912 if conf.crophack
4913 then
4914 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4915 truncate (1.2 *. (rect.(3) -. rect.(0))))
4916 else
4917 (truncate (rect.(1) -. rect.(0)),
4918 truncate (rect.(3) -. rect.(0)))
4920 let w = truncate ((float w)*.conf.zoom)
4921 and h = truncate ((float h)*.conf.zoom) in
4922 if w != 0 && h != 0
4923 then (
4924 state.anchor <- getanchor ();
4925 Wsi.reshape w (h + conf.interpagespace)
4927 G.postRedisplay "z";
4929 | [] -> ()
4932 | Ascii 'x' -> state.roam ()
4934 | Ascii ('<'|'>' as c) ->
4935 reqlayout
4936 (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
4938 | Ascii ('['|']' as c) ->
4939 conf.colorscale <-
4940 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
4941 G.postRedisplay "brightness";
4943 | Ascii 'c' when state.mode = View ->
4944 if Wsi.withalt mask
4945 then (
4946 if conf.zoom > 1.0
4947 then
4948 let m = (state.winw - state.w) / 2 in
4949 gotoxy m state.y
4951 else
4952 let (c, a, b), z =
4953 match state.prevcolumns with
4954 | None -> (1, 0, 0), 1.0
4955 | Some (columns, z) ->
4956 let cab =
4957 match columns with
4958 | Csplit (c, _) -> -c, 0, 0
4959 | Cmulti ((c, a, b), _) -> c, a, b
4960 | Csingle _ -> 1, 0, 0
4962 cab, z
4964 setcolumns View c a b;
4965 setzoom z
4967 | Down | Up when ctrl && Wsi.withshift mask ->
4968 let zoom, x = state.prevzoom in
4969 setzoom zoom;
4970 state.x <- x;
4972 | Ascii 'k' | Up ->
4973 begin match state.autoscroll with
4974 | None ->
4975 begin match state.mode with
4976 | Birdseye beye -> upbirdseye 1 beye
4977 | Textentry _ | View | LinkNav _ ->
4978 if ctrl
4979 then gotoxy state.x (clamp ~-(state.winh/2))
4980 else (
4981 if not (Wsi.withshift mask) && conf.presentation
4982 then prevpage ()
4983 else gotoxy state.x (clamp (-conf.scrollstep))
4986 | Some n ->
4987 setautoscrollspeed n false
4990 | Ascii 'j' | Down ->
4991 begin match state.autoscroll with
4992 | None ->
4993 begin match state.mode with
4994 | Birdseye beye -> downbirdseye 1 beye
4995 | Textentry _ | View | LinkNav _ ->
4996 if ctrl
4997 then gotoxy state.x (clamp (state.winh/2))
4998 else (
4999 if not (Wsi.withshift mask) && conf.presentation
5000 then nextpage ()
5001 else gotoxy state.x (clamp (conf.scrollstep))
5004 | Some n ->
5005 setautoscrollspeed n true
5008 | Left | Right when not (Wsi.withalt mask) ->
5009 if canpan ()
5010 then
5011 let dx =
5012 if ctrl
5013 then state.winw / 2
5014 else conf.hscrollstep
5016 let dx =
5017 let pv = Wsi.kc2kt key in
5018 if pv = Keys.Left then dx else -dx
5020 gotoxy (panbound (state.x + dx)) state.y
5021 else (
5022 state.text <- E.s;
5023 G.postRedisplay "left/right"
5026 | Prior ->
5027 let y =
5028 if ctrl
5029 then
5030 match state.layout with
5031 | [] -> state.y
5032 | l :: _ -> state.y - l.pagey
5033 else
5034 clamp (pgscale (-state.winh))
5036 gotoxy state.x y
5038 | Next ->
5039 let y =
5040 if ctrl
5041 then
5042 match List.rev state.layout with
5043 | [] -> state.y
5044 | l :: _ -> getpagey l.pageno
5045 else
5046 clamp (pgscale state.winh)
5048 gotoxy state.x y
5050 | Ascii 'g' | Home ->
5051 addnav ();
5052 gotoxy 0 0
5053 | Ascii 'G' | End ->
5054 addnav ();
5055 gotoxy 0 (clamp state.maxy)
5057 | Right when Wsi.withalt mask ->
5058 addnavnorc ();
5059 gotoxy state.x (getnav 1)
5060 | Left when Wsi.withalt mask ->
5061 addnavnorc ();
5062 gotoxy state.x (getnav ~-1)
5064 | Ascii 'r' ->
5065 reload ()
5067 | Ascii 'v' when conf.debug ->
5068 state.rects <- [];
5069 List.iter (fun l ->
5070 match getopaque l.pageno with
5071 | None -> ()
5072 | Some opaque ->
5073 let x0, y0, x1, y1 = pagebbox opaque in
5074 let rect = (float x0, float y0,
5075 float x1, float y0,
5076 float x1, float y1,
5077 float x0, float y1) in
5078 debugrect rect;
5079 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
5080 state.rects <- (l.pageno, color, rect) :: state.rects;
5081 ) state.layout;
5082 G.postRedisplay "v";
5084 | Ascii '|' ->
5085 let mode = state.mode in
5086 let cmd = ref E.s in
5087 let onleave = function
5088 | Cancel -> state.mode <- mode
5089 | Confirm ->
5090 List.iter (fun l ->
5091 match getopaque l.pageno with
5092 | Some opaque -> pipesel opaque !cmd
5093 | None -> ()) state.layout;
5094 state.mode <- mode
5096 let ondone s =
5097 cbput state.hists.sel s;
5098 cmd := s
5100 let te =
5101 "| ", !cmd, Some (onhist state.hists.sel), textentry, ondone, true
5103 G.postRedisplay "|";
5104 state.mode <- Textentry (te, onleave);
5106 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
5107 vlog "huh? %s" (Wsi.keyname key)
5110 let linknavkeyboard key mask linknav =
5111 let pv = Wsi.kc2kt key in
5112 let getpage pageno =
5113 let rec loop = function
5114 | [] -> None
5115 | l :: _ when l.pageno = pageno -> Some l
5116 | _ :: rest -> loop rest
5117 in loop state.layout
5119 let doexact (pageno, n) =
5120 match getopaque pageno, getpage pageno with
5121 | Some opaque, Some l ->
5122 if pv = Keys.Enter
5123 then
5124 let under = getlink opaque n in
5125 G.postRedisplay "link gotounder";
5126 gotounder under;
5127 state.mode <- View;
5128 else
5129 let opt, dir =
5130 let open Keys in
5131 match pv with
5132 | Home -> Some (findlink opaque LDfirst), -1
5133 | End -> Some (findlink opaque LDlast), 1
5134 | Left -> Some (findlink opaque (LDleft n)), -1
5135 | Right -> Some (findlink opaque (LDright n)), 1
5136 | Up -> Some (findlink opaque (LDup n)), -1
5137 | Down -> Some (findlink opaque (LDdown n)), 1
5139 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
5140 | Code _|Fn _|Ctrl _|Backspace -> None, 0
5142 let pwl l dir =
5143 begin match findpwl l.pageno dir with
5144 | Pwlnotfound -> ()
5145 | Pwl pageno ->
5146 let notfound dir =
5147 state.mode <- LinkNav (Ltgendir dir);
5148 let y, h = getpageyh pageno in
5149 let y =
5150 if dir < 0
5151 then y + h - state.winh
5152 else y
5154 gotoxy state.x y
5156 begin match getopaque pageno, getpage pageno with
5157 | Some opaque, Some _ ->
5158 let link =
5159 let ld = if dir > 0 then LDfirst else LDlast in
5160 findlink opaque ld
5162 begin match link with
5163 | Lfound m ->
5164 showlinktype (getlink opaque m);
5165 state.mode <- LinkNav (Ltexact (pageno, m));
5166 G.postRedisplay "linknav jpage";
5167 | Lnotfound -> notfound dir
5168 end;
5169 | _ -> notfound dir
5170 end;
5171 end;
5173 begin match opt with
5174 | Some Lnotfound -> pwl l dir;
5175 | Some (Lfound m) ->
5176 if m = n
5177 then pwl l dir
5178 else (
5179 let _, y0, _, y1 = getlinkrect opaque m in
5180 if y0 < l.pagey
5181 then gotopage1 l.pageno y0
5182 else (
5183 let d = fstate.fontsize + 1 in
5184 if y1 - l.pagey > l.pagevh - d
5185 then gotopage1 l.pageno (y1 - state.winh + d)
5186 else G.postRedisplay "linknav";
5188 showlinktype (getlink opaque m);
5189 state.mode <- LinkNav (Ltexact (l.pageno, m));
5192 | None -> viewkeyboard key mask
5193 end;
5194 | _ -> viewkeyboard key mask
5196 if pv = Keys.Insert
5197 then (
5198 begin match linknav with
5199 | Ltexact pa -> state.lnava <- Some pa
5200 | Ltgendir _ | Ltnotready _ -> ()
5201 end;
5202 state.mode <- View;
5203 G.postRedisplay "leave linknav"
5205 else
5206 match linknav with
5207 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
5208 | Ltexact exact -> doexact exact
5211 let keyboard key mask =
5212 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry state.mode)
5213 then wcmd "interrupt"
5214 else state.uioh <- state.uioh#key key mask
5217 let birdseyekeyboard key mask
5218 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5219 let incr =
5220 match conf.columns with
5221 | Csingle _ -> 1
5222 | Cmulti ((c, _, _), _) -> c
5223 | Csplit _ -> failwith "bird's eye split mode"
5225 let pgh layout = List.fold_left
5226 (fun m l -> max l.pageh m) state.winh layout in
5227 let open Keys in
5228 match Wsi.kc2kt key with
5229 | Ascii 'l' when Wsi.withctrl mask ->
5230 let y, h = getpageyh pageno in
5231 let top = (state.winh - h) / 2 in
5232 gotoxy state.x (max 0 (y - top))
5233 | Enter -> leavebirdseye beye false
5234 | Escape -> leavebirdseye beye true
5235 | Up -> upbirdseye incr beye
5236 | Down -> downbirdseye incr beye
5237 | Left -> upbirdseye 1 beye
5238 | Right -> downbirdseye 1 beye
5240 | Prior ->
5241 begin match state.layout with
5242 | l :: _ ->
5243 if l.pagey != 0
5244 then (
5245 state.mode <- Birdseye (
5246 oconf, leftx, l.pageno, hooverpageno, anchor
5248 gotopage1 l.pageno 0;
5250 else (
5251 let layout = layout state.x (state.y-state.winh)
5252 state.winw
5253 (pgh state.layout) in
5254 match layout with
5255 | [] -> gotoxy state.x (clamp (-state.winh))
5256 | l :: _ ->
5257 state.mode <- Birdseye (
5258 oconf, leftx, l.pageno, hooverpageno, anchor
5260 gotopage1 l.pageno 0
5263 | [] -> gotoxy state.x (clamp (-state.winh))
5264 end;
5266 | Next ->
5267 begin match List.rev state.layout with
5268 | l :: _ ->
5269 let layout = layout state.x
5270 (state.y + (pgh state.layout))
5271 state.winw state.winh in
5272 begin match layout with
5273 | [] ->
5274 let incr = l.pageh - l.pagevh in
5275 if incr = 0
5276 then (
5277 state.mode <-
5278 Birdseye (
5279 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5281 G.postRedisplay "birdseye pagedown";
5283 else gotoxy state.x (clamp (incr + conf.interpagespace*2));
5285 | l :: _ ->
5286 state.mode <-
5287 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5288 gotopage1 l.pageno 0;
5291 | [] -> gotoxy state.x (clamp state.winh)
5292 end;
5294 | Home ->
5295 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5296 gotopage1 0 0
5298 | End ->
5299 let pageno = state.pagecount - 1 in
5300 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5301 if not (pagevisible state.layout pageno)
5302 then
5303 let h =
5304 match List.rev state.pdims with
5305 | [] -> state.winh
5306 | (_, _, h, _) :: _ -> h
5308 gotoxy
5309 state.x
5310 (max 0 (getpagey pageno - (state.winh - h - conf.interpagespace)))
5311 else G.postRedisplay "birdseye end";
5313 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
5316 let drawpage l =
5317 let color =
5318 match state.mode with
5319 | Textentry _ -> scalecolor 0.4
5320 | LinkNav _ | View -> scalecolor 1.0
5321 | Birdseye (_, _, pageno, hooverpageno, _) ->
5322 if l.pageno = hooverpageno
5323 then scalecolor 0.9
5324 else (
5325 if l.pageno = pageno
5326 then (
5327 let c = scalecolor 1.0 in
5328 GlDraw.color c;
5329 GlDraw.line_width 3.0;
5330 let dispx = l.pagedispx in
5331 linerect
5332 (float (dispx-1)) (float (l.pagedispy-1))
5333 (float (dispx+l.pagevw+1))
5334 (float (l.pagedispy+l.pagevh+1))
5336 GlDraw.line_width 1.0;
5339 else scalecolor 0.8
5342 drawtiles l color;
5345 let postdrawpage l linkindexbase =
5346 match getopaque l.pageno with
5347 | Some opaque ->
5348 if tileready l l.pagex l.pagey
5349 then
5350 let x = l.pagedispx - l.pagex
5351 and y = l.pagedispy - l.pagey in
5352 let hlmask =
5353 match conf.columns with
5354 | Csingle _ | Cmulti _ ->
5355 (if conf.hlinks then 1 else 0)
5356 + (if state.glinks
5357 && not (isbirdseye state.mode) then 2 else 0)
5358 | Csplit _ -> 0
5360 let s =
5361 match state.mode with
5362 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5363 | Textentry _
5364 | Birdseye _
5365 | View
5366 | LinkNav _ -> E.s
5368 Hashtbl.find_all state.prects l.pageno |>
5369 List.iter (fun vals -> drawprect opaque x y vals);
5370 let n = postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize) in
5371 if n < 0
5372 then (state.redisplay <- true; 0)
5373 else n
5374 else 0
5375 | _ -> 0
5378 let scrollindicator () =
5379 let sbw, ph, sh = state.uioh#scrollph in
5380 let sbh, pw, sw = state.uioh#scrollpw in
5382 let x0,x1,hx0 =
5383 if conf.leftscroll
5384 then (0, sbw, sbw)
5385 else ((state.winw - sbw), state.winw, 0)
5388 Gl.enable `blend;
5389 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
5390 let (r, g, b, alpha) = conf.sbarcolor in
5391 GlDraw.color (r, g, b) ~alpha;
5392 filledrect (float x0) 0. (float x1) (float state.winh);
5393 filledrect
5394 (float hx0) (float (state.winh - sbh))
5395 (float (hx0 + state.winw)) (float state.winh);
5396 let (r, g, b, alpha) = conf.sbarhndlcolor in
5397 GlDraw.color (r, g, b) ~alpha;
5399 filledrect (float x0) ph (float x1) (ph +. sh);
5400 let pw = pw +. float hx0 in
5401 filledrect pw (float (state.winh - sbh)) (pw +. sw) (float state.winh);
5402 Gl.disable `blend;
5405 let showsel () =
5406 match state.mstate with
5407 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5410 | Msel ((x0, y0), (x1, y1)) ->
5411 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
5412 let o0,n0,px0,py0 = onppundermouse identify x0 y0 (~< E.s, -1, 0, 0) in
5413 let _o1,n1,px1,py1 = onppundermouse identify x1 y1 (~< E.s, -1, 0, 0) in
5414 if n0 != -1 && n0 = n1 then seltext o0 (px0, py0, px1, py1);
5417 let showrects =
5418 function [] -> ()
5419 | rects ->
5420 Gl.enable `blend;
5421 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5422 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
5423 List.iter
5424 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5425 List.iter (fun l ->
5426 if l.pageno = pageno
5427 then (
5428 let dx = float (l.pagedispx - l.pagex) in
5429 let dy = float (l.pagedispy - l.pagey) in
5430 let r, g, b, alpha = c in
5431 GlDraw.color (r, g, b) ~alpha;
5432 filledrect2 (x0+.dx) (y0+.dy)
5433 (x1+.dx) (y1+.dy)
5434 (x3+.dx) (y3+.dy)
5435 (x2+.dx) (y2+.dy);
5437 ) state.layout
5438 ) rects;
5439 Gl.disable `blend;
5442 let display () =
5443 GlDraw.color (scalecolor2 conf.bgcolor);
5444 GlClear.color (scalecolor2 conf.bgcolor);
5445 GlClear.clear [`color];
5446 List.iter drawpage state.layout;
5447 let rects =
5448 match state.mode with
5449 | LinkNav (Ltexact (pageno, linkno)) ->
5450 begin match getopaque pageno with
5451 | Some opaque ->
5452 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5453 let color = (0.0, 0.0, 0.5, 0.5) in
5454 (pageno, color,
5455 (float x0, float y0,
5456 float x1, float y0,
5457 float x1, float y1,
5458 float x0, float y1)
5459 ) :: state.rects
5460 | None -> state.rects
5462 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
5463 | Birdseye _
5464 | Textentry _
5465 | View -> state.rects
5467 showrects rects;
5468 let rec postloop linkindexbase = function
5469 | l :: rest ->
5470 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
5471 postloop linkindexbase rest
5472 | [] -> ()
5474 showsel ();
5475 postloop 0 state.layout;
5476 state.uioh#display;
5477 begin match state.mstate with
5478 | Mzoomrect ((x0, y0), (x1, y1)) ->
5479 Gl.enable `blend;
5480 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5481 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
5482 filledrect (float x0) (float y0) (float x1) (float y1);
5483 Gl.disable `blend;
5484 | Msel _
5485 | Mpan _
5486 | Mscrolly | Mscrollx
5487 | Mzoom _
5488 | Mnone -> ()
5489 end;
5490 enttext ();
5491 scrollindicator ();
5492 Wsi.swapb ();
5495 let zoomrect x y x1 y1 =
5496 let x0 = min x x1
5497 and x1 = max x x1
5498 and y0 = min y y1 in
5499 let zoom = (float state.w) /. float (x1 - x0) in
5500 let margin =
5501 let simple () =
5502 if state.w < state.winw
5503 then (state.winw - state.w) / 2
5504 else 0
5506 match conf.fitmodel with
5507 | FitWidth | FitProportional -> simple ()
5508 | FitPage ->
5509 match conf.columns with
5510 | Csplit _ ->
5511 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
5512 | Cmulti _ | Csingle _ -> simple ()
5514 gotoxy ((state.x + margin) - x0) (state.y + y0);
5515 state.anchor <- getanchor ();
5516 setzoom zoom;
5517 resetmstate ();
5520 let annot inline x y =
5521 match unproject x y with
5522 | Some (opaque, n, ux, uy) ->
5523 let add text =
5524 addannot opaque ux uy text;
5525 wcmd "freepage %s" (~> opaque);
5526 Hashtbl.remove state.pagemap (n, state.gen);
5527 flushtiles ();
5528 gotoxy state.x state.y
5530 if inline
5531 then
5532 let ondone s = add s in
5533 let mode = state.mode in
5534 state.mode <- Textentry (
5535 ("annotation: ", E.s, None, textentry, ondone, true),
5536 fun _ -> state.mode <- mode);
5537 state.text <- E.s;
5538 enttext ();
5539 G.postRedisplay "annot"
5540 else
5541 add @@ getusertext E.s
5542 | _ -> ()
5545 let zoomblock x y =
5546 let g opaque l px py =
5547 match rectofblock opaque px py with
5548 | Some a ->
5549 let x0 = a.(0) -. 20. in
5550 let x1 = a.(1) +. 20. in
5551 let y0 = a.(2) -. 20. in
5552 let zoom = (float state.w) /. (x1 -. x0) in
5553 let pagey = getpagey l.pageno in
5554 let margin = (state.w - l.pagew)/2 in
5555 let nx = -truncate x0 - margin in
5556 gotoxy nx (pagey + truncate y0);
5557 state.anchor <- getanchor ();
5558 setzoom zoom;
5559 None
5560 | None -> None
5562 match conf.columns with
5563 | Csplit _ ->
5564 impmsg "block zooming does not work properly in split columns mode"
5565 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
5568 let scrollx x =
5569 let winw = state.winw - 1 in
5570 let s = float x /. float winw in
5571 let destx = truncate (float (state.w + winw) *. s) in
5572 gotoxy (winw - destx) state.y;
5573 state.mstate <- Mscrollx;
5576 let scrolly y =
5577 let s = float y /. float state.winh in
5578 let desty = truncate (s *. float (maxy ())) in
5579 gotoxy state.x desty;
5580 state.mstate <- Mscrolly;
5583 let viewmulticlick clicks x y mask =
5584 let g opaque l px py =
5585 let mark =
5586 match clicks with
5587 | 2 -> Mark_word
5588 | 3 -> Mark_line
5589 | 4 -> Mark_block
5590 | _ -> Mark_page
5592 if markunder opaque px py mark
5593 then (
5594 Some (fun () ->
5595 let dopipe cmd =
5596 match getopaque l.pageno with
5597 | None -> ()
5598 | Some opaque -> pipesel opaque cmd
5600 state.roam <- (fun () -> dopipe conf.paxcmd);
5601 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
5604 else None
5606 G.postRedisplay "viewmulticlick";
5607 onppundermouse g x y (fun () -> impmsg "nothing to select") ();
5610 let canselect () =
5611 match conf.columns with
5612 | Csplit _ -> false
5613 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
5616 let viewmouse button down x y mask =
5617 match button with
5618 | n when (n == 4 || n == 5) && not down ->
5619 if Wsi.withctrl mask
5620 then (
5621 let incr =
5622 if n = 5
5623 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5624 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5626 let fx, fy =
5627 match state.mstate with
5628 | Mzoom (oldn, _, pos) when n = oldn -> pos
5629 | Mzoomrect _ | Mnone | Mpan _
5630 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
5632 let zoom = conf.zoom -. incr in
5633 state.mstate <- Mzoom (n, 0, (x, y));
5634 if false && abs (fx - x) > 5 || abs (fy - y) > 5
5635 then pivotzoom ~x ~y zoom
5636 else pivotzoom zoom
5638 else (
5639 match state.autoscroll with
5640 | Some step -> setautoscrollspeed step (n=4)
5641 | None ->
5642 if conf.wheelbypage || conf.presentation
5643 then (
5644 if n = 4
5645 then prevpage ()
5646 else nextpage ()
5648 else
5649 let incr =
5650 if n = 4
5651 then -conf.scrollstep
5652 else conf.scrollstep
5654 let incr = incr * 2 in
5655 let y = clamp incr in
5656 gotoxy state.x y
5659 | n when (n = 6 || n = 7) && not down && canpan () ->
5660 let x =
5661 panbound (state.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
5662 gotoxy x state.y
5664 | 1 when Wsi.withshift mask ->
5665 state.mstate <- Mnone;
5666 if not down
5667 then (
5668 match unproject x y with
5669 | None -> ()
5670 | Some (_, pageno, ux, uy) ->
5671 let cmd = Printf.sprintf
5672 "%s %s %d %d %d"
5673 conf.stcmd state.path pageno ux uy
5675 match spawn cmd [] with
5676 | exception exn ->
5677 impmsg "execution of synctex command(%S) failed: %S"
5678 conf.stcmd @@ exntos exn
5679 | _pid -> ()
5682 | 1 when Wsi.withctrl mask ->
5683 if down
5684 then (
5685 Wsi.setcursor Wsi.CURSOR_FLEUR;
5686 state.mstate <- Mpan (x, y)
5688 else
5689 state.mstate <- Mnone
5691 | 3 ->
5692 if down
5693 then (
5694 if Wsi.withshift mask
5695 then (
5696 annot conf.annotinline x y;
5697 G.postRedisplay "addannot"
5699 else
5700 let p = (x, y) in
5701 Wsi.setcursor Wsi.CURSOR_CYCLE;
5702 state.mstate <- Mzoomrect (p, p)
5704 else (
5705 match state.mstate with
5706 | Mzoomrect ((x0, y0), _) ->
5707 if abs (x-x0) > 10 && abs (y - y0) > 10
5708 then zoomrect x0 y0 x y
5709 else (
5710 resetmstate ();
5711 G.postRedisplay "kill accidental zoom rect";
5713 | Msel _
5714 | Mpan _
5715 | Mscrolly | Mscrollx
5716 | Mzoom _
5717 | Mnone -> resetmstate ()
5720 | 1 when vscrollhit x ->
5721 if down
5722 then
5723 let _, position, sh = state.uioh#scrollph in
5724 if y > truncate position && y < truncate (position +. sh)
5725 then state.mstate <- Mscrolly
5726 else scrolly y
5727 else
5728 state.mstate <- Mnone
5730 | 1 when y > state.winh - hscrollh () ->
5731 if down
5732 then
5733 let _, position, sw = state.uioh#scrollpw in
5734 if x > truncate position && x < truncate (position +. sw)
5735 then state.mstate <- Mscrollx
5736 else scrollx x
5737 else
5738 state.mstate <- Mnone
5740 | 1 when state.bzoom -> if not down then zoomblock x y
5742 | 1 ->
5743 let dest = if down then getunder x y else Unone in
5744 begin match dest with
5745 | Ulinkuri _ ->
5746 gotounder dest
5748 | Unone when down ->
5749 Wsi.setcursor Wsi.CURSOR_FLEUR;
5750 state.mstate <- Mpan (x, y);
5752 | Uannotation (opaque, slinkindex) -> enterannotmode opaque slinkindex
5754 | Unone | Utext _ ->
5755 if down
5756 then (
5757 if canselect ()
5758 then (
5759 state.mstate <- Msel ((x, y), (x, y));
5760 G.postRedisplay "mouse select";
5763 else (
5764 match state.mstate with
5765 | Mnone -> ()
5767 | Mzoom _ | Mscrollx | Mscrolly ->
5768 state.mstate <- Mnone
5770 | Mzoomrect ((x0, y0), _) ->
5771 zoomrect x0 y0 x y
5773 | Mpan _ ->
5774 Wsi.setcursor Wsi.CURSOR_INHERIT;
5775 state.mstate <- Mnone
5777 | Msel ((x0, y0), (x1, y1)) ->
5778 let rec loop = function
5779 | [] -> ()
5780 | l :: rest ->
5781 let inside =
5782 let a0 = l.pagedispy in
5783 let a1 = a0 + l.pagevh in
5784 let b0 = l.pagedispx in
5785 let b1 = b0 + l.pagevw in
5786 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
5787 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
5789 if inside
5790 then
5791 match getopaque l.pageno with
5792 | Some opaque ->
5793 let dosel cmd () =
5794 pipef ~closew:false "Msel"
5795 (fun w ->
5796 copysel w opaque;
5797 G.postRedisplay "Msel") cmd
5799 dosel conf.selcmd ();
5800 state.roam <- dosel conf.paxcmd;
5801 | None -> ()
5802 else loop rest
5804 loop state.layout;
5805 resetmstate ();
5809 | _ -> ()
5812 let birdseyemouse button down x y mask
5813 (conf, leftx, _, hooverpageno, anchor) =
5814 match button with
5815 | 1 when down ->
5816 let rec loop = function
5817 | [] -> ()
5818 | l :: rest ->
5819 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5820 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5821 then (
5822 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5824 else loop rest
5826 loop state.layout
5827 | 3 -> ()
5828 | _ -> viewmouse button down x y mask
5831 let uioh = object
5832 method display = ()
5834 method key key mask =
5835 begin match state.mode with
5836 | Textentry textentry -> textentrykeyboard key mask textentry
5837 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5838 | View -> viewkeyboard key mask
5839 | LinkNav linknav -> linknavkeyboard key mask linknav
5840 end;
5841 state.uioh
5843 method button button bstate x y mask =
5844 begin match state.mode with
5845 | LinkNav _ | View -> viewmouse button bstate x y mask
5846 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5847 | Textentry _ -> ()
5848 end;
5849 state.uioh
5851 method multiclick clicks x y mask =
5852 begin match state.mode with
5853 | LinkNav _ | View -> viewmulticlick clicks x y mask
5854 | Birdseye _ | Textentry _ -> ()
5855 end;
5856 state.uioh
5858 method motion x y =
5859 begin match state.mode with
5860 | Textentry _ -> ()
5861 | View | Birdseye _ | LinkNav _ ->
5862 match state.mstate with
5863 | Mzoom _ | Mnone -> ()
5865 | Mpan (x0, y0) ->
5866 let dx = x - x0
5867 and dy = y0 - y in
5868 state.mstate <- Mpan (x, y);
5869 let x = if canpan () then panbound (state.x + dx) else state.x in
5870 let y = clamp dy in
5871 gotoxy x y
5873 | Msel (a, _) ->
5874 state.mstate <- Msel (a, (x, y));
5875 G.postRedisplay "motion select";
5877 | Mscrolly ->
5878 let y = min state.winh (max 0 y) in
5879 scrolly y
5881 | Mscrollx ->
5882 let x = min state.winw (max 0 x) in
5883 scrollx x
5885 | Mzoomrect (p0, _) ->
5886 state.mstate <- Mzoomrect (p0, (x, y));
5887 G.postRedisplay "motion zoomrect";
5888 end;
5889 state.uioh
5891 method pmotion x y =
5892 begin match state.mode with
5893 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5894 let rec loop = function
5895 | [] ->
5896 if hooverpageno != -1
5897 then (
5898 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5899 G.postRedisplay "pmotion birdseye no hoover";
5901 | l :: rest ->
5902 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5903 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5904 then (
5905 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5906 G.postRedisplay "pmotion birdseye hoover";
5908 else loop rest
5910 loop state.layout
5912 | Textentry _ -> ()
5914 | LinkNav _ | View ->
5915 match state.mstate with
5916 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
5917 | Mnone ->
5918 updateunder x y;
5919 if canselect ()
5920 then
5921 match conf.pax with
5922 | None -> ()
5923 | Some past ->
5924 let now = now () in
5925 let delta = now -. past in
5926 if delta > 0.01
5927 then paxunder x y
5928 else conf.pax <- Some now
5929 end;
5930 state.uioh
5932 method infochanged _ = ()
5934 method scrollph =
5935 let maxy = maxy () in
5936 let p, h =
5937 if maxy = 0
5938 then 0.0, float state.winh
5939 else scrollph state.y maxy
5941 vscrollw (), p, h
5943 method scrollpw =
5944 let fwinw = float (state.winw - vscrollw ()) in
5945 let sw =
5946 let sw = fwinw /. float state.w in
5947 let sw = fwinw *. sw in
5948 max sw (float conf.scrollh)
5950 let position =
5951 let maxx = state.w + state.winw in
5952 let x = state.winw - state.x in
5953 let percent = float x /. float maxx in
5954 (fwinw -. sw) *. percent
5956 hscrollh (), position, sw
5958 method modehash =
5959 let modename =
5960 match state.mode with
5961 | LinkNav _ -> "links"
5962 | Textentry _ -> "textentry"
5963 | Birdseye _ -> "birdseye"
5964 | View -> "view"
5966 findkeyhash conf modename
5968 method eformsgs = true
5969 method alwaysscrolly = false
5970 method scroll dx dy =
5971 let x = if canpan () then panbound (state.x + dx) else state.x in
5972 gotoxy x (clamp (2 * dy));
5973 state.uioh
5974 method zoom z x y =
5975 pivotzoom ~x ~y (conf.zoom *. exp z);
5976 end;;
5978 let addrect pageno r g b a x0 y0 x1 y1 =
5979 Hashtbl.add state.prects pageno [|r; g; b; a; x0; y0; x1; y1|];
5982 let ract cmds =
5983 let cl = splitatchar cmds ' ' in
5984 let scan s fmt f =
5985 try Scanf.sscanf s fmt f
5986 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
5987 cmds @@ exntos exn
5989 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
5990 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
5991 s pageno r g b a x0 y0 x1 y1;
5992 onpagerect
5993 pageno
5994 (fun w h ->
5995 let _,w1,h1,_ = getpagedim pageno in
5996 let sw = float w1 /. float w
5997 and sh = float h1 /. float h in
5998 let x0s = x0 *. sw
5999 and x1s = x1 *. sw
6000 and y0s = y0 *. sh
6001 and y1s = y1 *. sh in
6002 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
6003 let color = (r, g, b, a) in
6004 if conf.verbose then debugrect rect;
6005 state.rects <- (pageno, color, rect) :: state.rects;
6006 G.postRedisplay s;
6009 match cl with
6010 | "reload", "" -> reload ()
6011 | "goto", args ->
6012 scan args "%u %f %f"
6013 (fun pageno x y ->
6014 let cmd, _ = state.geomcmds in
6015 if emptystr cmd
6016 then gotopagexy pageno x y
6017 else
6018 let f prevf () =
6019 gotopagexy pageno x y;
6020 prevf ()
6022 state.reprf <- f state.reprf
6024 | "goto1", args -> scan args "%u %f" gotopage
6025 | "gotor", args -> scan args "%S" gotoremote
6026 | "rect", args ->
6027 scan args "%u %u %f %f %f %f"
6028 (fun pageno c x0 y0 x1 y1 ->
6029 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
6030 rectx "rect" pageno color x0 y0 x1 y1;
6032 | "prect", args ->
6033 scan args "%u %f %f %f %f %f %f %f %f"
6034 (fun pageno r g b alpha x0 y0 x1 y1 ->
6035 addrect pageno r g b alpha x0 y0 x1 y1;
6036 G.postRedisplay "prect"
6038 | "pgoto", args ->
6039 scan args "%u %f %f"
6040 (fun pageno x y ->
6041 let optopaque =
6042 match getopaque pageno with
6043 | Some opaque -> opaque
6044 | None -> ~< E.s
6046 pgoto optopaque pageno x y;
6047 let rec fixx = function
6048 | [] -> ()
6049 | l :: rest ->
6050 if l.pageno = pageno
6051 then gotoxy (state.x - l.pagedispx) state.y
6052 else fixx rest
6054 let layout =
6055 let mult =
6056 match conf.columns with
6057 | Csingle _ | Csplit _ -> 1
6058 | Cmulti ((n, _, _), _) -> n
6060 layout 0 state.y (state.winw * mult) state.winh
6062 fixx layout
6064 | "activatewin", "" -> Wsi.activatewin ()
6065 | "quit", "" -> raise Quit
6066 | "keys", keys ->
6067 begin try
6068 let l = Config.keys_of_string keys in
6069 List.iter (fun (k, m) -> keyboard k m) l
6070 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
6071 cmds @@ exntos exn
6073 | "clearrects", "" ->
6074 Hashtbl.clear state.prects;
6075 G.postRedisplay "clearrects"
6076 | _ ->
6077 adderrfmt "remote command"
6078 "error processing remote command: %S\n" cmds;
6081 let remote =
6082 let scratch = Bytes.create 80 in
6083 let buf = Buffer.create 80 in
6084 fun fd ->
6085 match tempfailureretry (Unix.read fd scratch 0) 80 with
6086 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
6087 | 0 ->
6088 Unix.close fd;
6089 if Buffer.length buf > 0
6090 then (
6091 let s = Buffer.contents buf in
6092 Buffer.clear buf;
6093 ract s;
6095 None
6096 | n ->
6097 let rec eat ppos =
6098 let nlpos =
6099 match Bytes.index_from scratch ppos '\n' with
6100 | pos -> if pos >= n then -1 else pos
6101 | exception Not_found -> -1
6103 if nlpos >= 0
6104 then (
6105 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
6106 let s = Buffer.contents buf in
6107 Buffer.clear buf;
6108 ract s;
6109 eat (nlpos+1);
6111 else (
6112 Buffer.add_subbytes buf scratch ppos (n-ppos);
6113 Some fd
6115 in eat 0
6118 let remoteopen path =
6119 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
6120 with exn ->
6121 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
6122 None
6125 let () =
6126 let gcconfig = ref false in
6127 let trimcachepath = ref E.s in
6128 let rcmdpath = ref E.s in
6129 let pageno = ref None in
6130 let openlast = ref false in
6131 let doreap = ref false in
6132 let csspath = ref None in
6133 selfexec := Sys.executable_name;
6134 Arg.parse
6135 (Arg.align
6136 [("-p", Arg.String (fun s -> state.password <- s),
6137 "<password> Set password");
6139 ("-f", Arg.String
6140 (fun s ->
6141 Config.fontpath := s;
6142 selfexec := !selfexec ^ " -f " ^ Filename.quote s;
6144 "<path> Set path to the user interface font");
6146 ("-c", Arg.String
6147 (fun s ->
6148 selfexec := !selfexec ^ " -c " ^ Filename.quote s;
6149 Config.confpath := s),
6150 "<path> Set path to the configuration file");
6152 ("-last", Arg.Set openlast, " Open last document");
6154 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
6155 "<page-number> Jump to page");
6157 ("-tcf", Arg.String (fun s -> trimcachepath := s),
6158 "<path> Set path to the trim cache file");
6160 ("-dest", Arg.String (fun s -> state.nameddest <- s),
6161 "<named-destination> Set named destination");
6163 ("-remote", Arg.String (fun s -> rcmdpath := s),
6164 "<path> Set path to the source of remote commands");
6166 ("-gc", Arg.Set gcconfig, " Collect config garbage");
6168 ("-v", Arg.Unit (fun () ->
6169 Printf.printf
6170 "%s\nconfiguration path: %s\n"
6171 (version ())
6172 Config.defconfpath;
6173 exit 0), " Print version and exit");
6175 ("-css", Arg.String (fun s -> csspath := Some s),
6176 "<path> Set path to the style sheet to use with EPUB/HTML");
6178 ("-origin", Arg.String (fun s -> state.origin <- s),
6179 "<origin> <undocumented>");
6181 ("-no-title", Arg.Set ignoredoctitlte, " ignore document title");
6182 ("-layout-height", Arg.Set_int layouth,
6183 "<height> layout height html/epub/etc (-1, 0, N)");
6186 (fun s -> state.path <- s)
6187 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
6189 let histmode = emptystr state.path && not !openlast in
6191 if not (Config.load !openlast)
6192 then dolog "failed to load configuration";
6194 begin match !pageno with
6195 | Some pageno -> state.anchor <- (pageno, 0.0, 0.0)
6196 | None -> ()
6197 end;
6199 fillhelp ();
6200 if !gcconfig
6201 then (
6202 Config.gc ();
6203 exit 0
6206 let mu =
6207 object (self)
6208 val mutable m_clicks = 0
6209 val mutable m_click_x = 0
6210 val mutable m_click_y = 0
6211 val mutable m_lastclicktime = infinity
6213 method private cleanup =
6214 state.roam <- noroam;
6215 Hashtbl.iter (fun _ opaque -> clearmark opaque) state.pagemap
6216 method expose = G.postRedisplay "expose"
6217 method visible v =
6218 let name =
6219 match v with
6220 | Wsi.Unobscured -> "unobscured"
6221 | Wsi.PartiallyObscured -> "partiallyobscured"
6222 | Wsi.FullyObscured -> "fullyobscured"
6224 vlog "visibility change %s" name
6225 method display = display ()
6226 method map mapped = vlog "mapped %b" mapped
6227 method reshape w h =
6228 self#cleanup;
6229 reshape w h
6230 method mouse b d x y m =
6231 if d && canselect ()
6232 then (
6234 * http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
6236 m_click_x <- x;
6237 m_click_y <- y;
6238 if b = 1
6239 then (
6240 let t = now () in
6241 if abs x - m_click_x > 10
6242 || abs y - m_click_y > 10
6243 || abs_float (t -. m_lastclicktime) > 0.3
6244 then m_clicks <- 0;
6245 m_clicks <- m_clicks + 1;
6246 m_lastclicktime <- t;
6247 if m_clicks = 1
6248 then (
6249 self#cleanup;
6250 G.postRedisplay "cleanup";
6251 state.uioh <- state.uioh#button b d x y m;
6253 else state.uioh <- state.uioh#multiclick m_clicks x y m
6255 else (
6256 self#cleanup;
6257 m_clicks <- 0;
6258 m_lastclicktime <- infinity;
6259 state.uioh <- state.uioh#button b d x y m
6262 else (
6263 state.uioh <- state.uioh#button b d x y m
6265 method motion x y =
6266 state.mpos <- (x, y);
6267 state.uioh <- state.uioh#motion x y
6268 method pmotion x y =
6269 state.mpos <- (x, y);
6270 state.uioh <- state.uioh#pmotion x y
6271 method key k m =
6272 vlog "k=%#x m=%#x" k m;
6273 let mascm = m land (
6274 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
6275 ) in
6276 let keyboard k m =
6277 let x = state.x and y = state.y in
6278 keyboard k m;
6279 if x != state.x || y != state.y then self#cleanup
6281 match state.keystate with
6282 | KSnone ->
6283 let km = k, mascm in
6284 begin
6285 match
6286 let modehash = state.uioh#modehash in
6287 try Hashtbl.find modehash km
6288 with Not_found ->
6289 try Hashtbl.find (findkeyhash conf "global") km
6290 with Not_found -> KMinsrt (k, m)
6291 with
6292 | KMinsrt (k, m) -> keyboard k m
6293 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6294 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6296 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
6297 List.iter (fun (k, m) -> keyboard k m) insrt;
6298 state.keystate <- KSnone
6299 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
6300 state.keystate <- KSinto (keys, insrt)
6301 | KSinto _ -> state.keystate <- KSnone
6303 method enter x y =
6304 state.mpos <- (x, y);
6305 state.uioh <- state.uioh#pmotion x y
6306 method leave = state.mpos <- (-1, -1)
6307 method winstate wsl = state.winstate <- wsl
6308 method quit : 'a. 'a = raise Quit
6309 method scroll dx dy = state.uioh <- state.uioh#scroll dx dy
6310 method zoom z x y = state.uioh#zoom z x y
6311 method opendoc path =
6312 state.mode <- View;
6313 state.uioh <- uioh;
6314 G.postRedisplay "opendoc";
6315 opendoc path state.password
6318 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh platform in
6319 state.wsfd <- wsfd;
6321 if not @@ List.exists GlMisc.check_extension
6322 [ "GL_ARB_texture_rectangle"
6323 ; "GL_EXT_texture_recangle"
6324 ; "GL_NV_texture_rectangle" ]
6325 then (dolog "OpenGL does not suppport rectangular textures"; exit 1);
6327 if substratis (GlMisc.get_string `renderer) 0 "Mesa DRI Intel("
6328 then (
6329 defconf.sliceheight <- 1024;
6330 defconf.texcount <- 32;
6331 defconf.usepbo <- true;
6334 let cs, ss =
6335 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
6336 | exception exn ->
6337 dolog "socketpair failed: %s" @@ exntos exn;
6338 exit 1
6339 | (r, w) ->
6340 cloexec r;
6341 cloexec w;
6342 r, w
6345 setcheckers conf.checkers;
6347 opengl_has_pbo := GlMisc.check_extension "GL_ARB_pixel_buffer_object";
6349 begin match !csspath with
6350 | None -> ()
6351 | Some "" -> conf.css <- E.s
6352 | Some path ->
6353 let css = filecontents path in
6354 let l = String.length css in
6355 conf.css <-
6356 if substratis css (l-2) "\r\n"
6357 then String.sub css 0 (l-2)
6358 else (if css.[l-1] = '\n'
6359 then String.sub css 0 (l-1)
6360 else css);
6361 end;
6362 init cs (
6363 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
6364 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6365 !Config.fontpath, !trimcachepath, !opengl_has_pbo
6367 List.iter GlArray.enable [`texture_coord; `vertex];
6368 state.ss <- ss;
6369 reshape ~firsttime:true winw winh;
6370 state.uioh <- uioh;
6371 if histmode
6372 then (
6373 Wsi.settitle "llpp (history)";
6374 enterhistmode ();
6376 else (
6377 state.text <- "Opening " ^ (mbtoutf8 state.path);
6378 opendoc state.path state.password;
6380 display ();
6381 Wsi.mapwin ();
6382 Wsi.setcursor Wsi.CURSOR_INHERIT;
6383 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
6385 let rec reap () =
6386 match Unix.waitpid [Unix.WNOHANG] ~-1 with
6387 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
6388 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
6389 | 0, _ -> ()
6390 | _pid, _status -> reap ()
6392 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
6394 let optrfd =
6395 ref (
6396 if nonemptystr !rcmdpath
6397 then remoteopen !rcmdpath
6398 else None
6402 let rec loop deadline =
6403 if !doreap
6404 then (
6405 doreap := false;
6406 reap ()
6408 let r = [state.ss; state.wsfd] in
6409 let r =
6410 match !optrfd with
6411 | None -> r
6412 | Some fd -> fd :: r
6414 if state.redisplay
6415 then (
6416 state.redisplay <- false;
6417 display ();
6419 let timeout =
6420 let now = now () in
6421 if deadline > now
6422 then (
6423 if deadline = infinity
6424 then ~-.1.0
6425 else max 0.0 (deadline -. now)
6427 else 0.0
6429 let r, _, _ =
6430 try Unix.select r [] [] timeout
6431 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
6433 begin match r with
6434 | [] ->
6435 let newdeadline =
6436 match state.autoscroll with
6437 | Some step when step != 0 ->
6438 if state.slideshow land 1 = 1
6439 then (
6440 if state.slideshow land 2 = 0
6441 then state.slideshow <- state.slideshow lor 2
6442 else if step < 0 then prevpage () else nextpage ();
6443 deadline +. (float (abs step))
6445 else
6446 let y = state.y + step in
6447 let fy = if conf.maxhfit then state.winh else 0 in
6448 let y =
6449 if y < 0
6450 then state.maxy - fy
6451 else if y >= state.maxy - fy then 0 else y
6453 gotoxy state.x y;
6454 deadline +. 0.01
6455 | _ -> infinity
6457 loop newdeadline
6459 | l ->
6460 let rec checkfds = function
6461 | [] -> ()
6462 | fd :: rest when fd = state.ss ->
6463 let cmd = rcmd state.ss in
6464 act cmd;
6465 checkfds rest
6467 | fd :: rest when fd = state.wsfd ->
6468 Wsi.readresp fd;
6469 checkfds rest
6471 | fd :: rest when Some fd = !optrfd ->
6472 begin match remote fd with
6473 | None -> optrfd := remoteopen !rcmdpath;
6474 | opt -> optrfd := opt
6475 end;
6476 checkfds rest
6478 | _ :: rest ->
6479 dolog "select returned unknown descriptor";
6480 checkfds rest
6482 checkfds l;
6483 let newdeadline =
6484 let deadline1 =
6485 if deadline = infinity
6486 then now () +. 0.01
6487 else deadline
6489 match state.autoscroll with
6490 | Some step when step != 0 -> deadline1
6491 | _ -> infinity
6493 loop newdeadline
6494 end;
6496 match loop infinity with
6497 | exception Quit ->
6498 Config.save leavebirdseye;
6499 if hasunsavedchanges ()
6500 then save ()
6501 | _ -> error "umpossible - infinity reached"