Added a lazy-loading dict of known, named libraries.
[automanga.git] / manga / reader.py
CommitLineData
03feea07
FT
1import threading, gtk, gio, gobject
2import lib
3
4class notdone(Exception): pass
5
6class future(threading.Thread):
7 prog = None
8
9 def __init__(self):
10 super(future, self).__init__()
11 self._val = None
12 self._exc = None
13 self.start()
14
15 def run(self):
16 try:
17 val = self.value()
18 except Exception as e:
19 self._exc = e
20 else:
21 self._val = [val]
22
23 @property
24 def val(self):
25 if self._exc is not None:
26 raise self._exc
27 if self._val is None:
28 raise notdone()
29 return self._val[0]
30
31 @property
32 def done(self):
33 return self._exc != None or self._val != None
34
35class imgload(future):
36 def __init__(self, page):
37 self.page = page
38 self.st = None
39 super(imgload, self).__init__()
40
41 def value(self):
42 buf = bytearray()
43 with self.page.open() as st:
44 self.p = 0
45 self.st = st
46 while True:
47 read = st.read(1024)
48 if read == "":
49 break
50 self.p += len(read)
51 buf.extend(read)
52 self.st = None
53 return gtk.gdk.pixbuf_new_from_stream(gio.memory_input_stream_new_from_data(str(buf)))
54
55 @property
56 def prog(self):
57 if self.st is None or self.st.clen is None:
58 return None
59 return float(self.p) / float(self.st.clen)
60
61class pageview(gtk.Widget):
62 def __init__(self, pixbuf):
63 super(pageview, self).__init__()
64 self.pixbuf = pixbuf
65 self.zoomed = None, None
66 self.fit = True
67 self.zoom = 1.0
68 self.interp = gtk.gdk.INTERP_HYPER
69 self.off = 0, 0
70
71 def get_osize(self):
72 return self.pixbuf.get_width(), self.pixbuf.get_height()
73
74 def get_asize(self):
75 return self.allocation.width, self.allocation.height
76
77 def do_realize(self):
78 self.set_flags(self.flags() | gtk.REALIZED)
79 alloc = self.allocation
80 self.window = gtk.gdk.Window(self.get_parent_window(),
81 width=alloc.width, height=alloc.height,
82 window_type = gtk.gdk.WINDOW_CHILD,
83 wclass = gtk.gdk.INPUT_OUTPUT,
84 event_mask = self.get_events() | gtk.gdk.EXPOSURE_MASK
85 )
86 self.window.set_user_data(self)
87 self.style.attach(self.window)
88 self.style.set_background(self.window, gtk.STATE_NORMAL)
89 self.window.move_resize(*alloc)
90
91 def do_unrealize(self):
92 self.window.set_user_data(None)
93
94 def do_size_request(self, req):
95 req.width, req.height = self.get_osize()
96
97 def fitzoom(self):
98 w, h = self.get_osize()
99 alloc = self.allocation
100 return min(float(alloc.width) / float(w), float(alloc.height) / float(h))
101
102 def do_size_allocate(self, alloc):
103 self.allocation = alloc
104 if self.fit:
105 self.zoom = self.fitzoom()
106 if self.flags() & gtk.REALIZED:
107 self.window.move_resize(*alloc)
108
109 def get_zoomed(self):
110 zoom = self.zoom
111 pz, zbuf = self.zoomed
112 if pz != zoom:
113 w, h = self.get_osize()
114 zbuf = self.pixbuf.scale_simple(int(w * zoom), int(h * zoom), self.interp)
115 self.zoomed = zoom, zbuf
116 return zbuf
117
118 def get_zsize(self):
119 zbuf = self.get_zoomed()
120 return zbuf.get_width(), zbuf.get_height()
121
122 def do_expose_event(self, event):
123 aw, ah = self.get_asize()
124 dw, dh = aw, ah
125 zbuf = self.get_zoomed()
126 zw, zh = self.get_zsize()
127 ox, oy = self.off
128 dx, dy = 0, 0
129 if zw < aw:
130 dx = (aw - zw) / 2
131 dw = zw
132 if zh < ah:
133 dy = (ah - zh) / 2
134 dh = zh
135 gc = self.style.fg_gc[gtk.STATE_NORMAL]
136 self.window.draw_pixbuf(gc, zbuf, ox, oy, dx, dy, dw, dh)
137
138 def set_off(self, off):
139 aw, ah = self.get_asize()
140 zw, zh = self.get_zsize()
141 ox, oy = off
142 ox, oy = int(ox), int(oy)
143 if ox > zw - aw: ox = zw - aw
144 if oy > zh - ah: oy = zh - ah
145 if ox < 0: ox = 0
146 if oy < 0: oy = 0
147 self.off = ox, oy
148 self.queue_draw()
149
150 def set_zoom(self, zoom):
151 if zoom is not None: zoom = float(zoom)
152 aw, ah = self.get_asize()
153 zw, zh = self.get_zsize()
154 dw, dh = zw - aw, zh - ah
155 ox, oy = self.off
156 xa = float(ox) / float(dw) if dw > 0 else 0.5
157 ya = float(oy) / float(dh) if dh > 0 else 0.5
158
159 if zoom is None:
160 self.fit = True
161 self.zoom = self.fitzoom()
162 else:
163 self.fit = False
164 self.zoom = zoom
165
166 zw, zh = self.get_zsize()
167 dw, dh = zw - aw, zh - ah
168 ox = int(xa * dw) if dw > 0 else 0
169 oy = int(ya * dh) if dh > 0 else 0
170 self.set_off((ox, oy))
171gobject.type_register(pageview)
172
43a12498 173class reader(gtk.Window):
03feea07 174 def __init__(self, manga):
43a12498
FT
175 super(reader, self).__init__(gtk.WINDOW_TOPLEVEL)
176 self.connect("delete_event", lambda wdg, ev, data=None: False)
177 self.connect("destroy", lambda wdg, data=None: self.quit())
178 self.connect("key_press_event", self.key)
179 self.pfr = gtk.Frame(None)
180 self.pfr.set_shadow_type(gtk.SHADOW_NONE)
181 self.add(self.pfr)
182 self.pfr.show()
183
184 self.manga = manga
03feea07
FT
185 self.page = None
186 self.cursor = lib.cursor(manga)
187 self.setpage(self.cursor.cur)
43a12498
FT
188 self.updtitle()
189
190 def setpage(self, page):
191 if self.page is not None:
192 self.pfr.remove(self.page)
193 self.page = None
194 if page is not None:
195 with self.cursor.cur.open() as inp:
196 pb = gtk.gdk.pixbuf_new_from_stream(gio.memory_input_stream_new_from_data(inp.read()))
197 self.page = pageview(pb)
198 self.pfr.add(self.page)
199 self.page.show()
200
201 def updtitle(self):
202 self.set_title(u"Automanga \u2013 " + self.manga.name)
03feea07
FT
203
204 @property
205 def zoom(self):
206 return self.page.zoom
207 @zoom.setter
208 def zoom(self, zoom):
209 self.page.set_zoom(zoom)
210
211 def pan(self, off):
212 ox, oy = self.page.off
213 px, py = off
214 self.page.set_off((ox + px, oy + py))
215
03feea07
FT
216 def key(self, wdg, ev, data=None):
217 if ev.keyval in [ord('Q'), ord('q'), 65307]:
218 self.quit()
43a12498
FT
219 if ev.keyval in [ord('O'), ord('o')]:
220 self.zoom = 1.0
221 elif ev.keyval in [ord('P'), ord('p')]:
222 self.zoom = None
223 elif ev.keyval in [ord('[')]:
224 self.zoom = min(self.zoom * 1.25, 3)
225 elif ev.keyval in [ord(']')]:
226 self.zoom /= 1.25
227 elif ev.keyval in [ord('h')]:
228 self.pan((-100, 0))
229 elif ev.keyval in [ord('j')]:
230 self.pan((0, 100))
231 elif ev.keyval in [ord('k')]:
232 self.pan((0, -100))
233 elif ev.keyval in [ord('l')]:
234 self.pan((100, 0))
235 elif ev.keyval in [ord('H')]:
236 self.page.set_off((0, self.page.off[1]))
237 elif ev.keyval in [ord('J')]:
238 self.page.set_off((self.page.off[0], self.page.get_asize()[1]))
239 elif ev.keyval in [ord('K')]:
240 self.page.set_off((self.page.off[1], 0))
241 elif ev.keyval in [ord('L')]:
242 self.page.set_off((self.page.get_asize()[0], self.page.off[1]))
243 elif ev.keyval in [ord(' ')]:
244 self.setpage(self.cursor.next())
245 elif ev.keyval in [65288]:
246 self.setpage(self.cursor.prev())
03feea07
FT
247
248 def quit(self):
249 self.hide()
43a12498 250gobject.type_register(reader)