Web · Wiki · Activities · Blog · Lists · Chat · Meeting · Bugs · Git · Translate · Archive · People · Donate
1
from gettext import gettext as _
2
import os
3
import logging
4
import time
5
6
from gi.repository import GObject
7
from gi.repository import Gtk
8
from gi.repository import Gdk
9
10
from gi.repository import EvinceDocument
11
from gi.repository import EvinceView
12
13
from sugar3 import profile
14
from sugar3.activity.activity import get_activity_root, show_object_in_journal
15
from sugar3.datastore import datastore
16
17
_logger = logging.getLogger('read-activity')
18
19
20
class EvinceViewer():
21
22
    def __init__(self):
23
        self._view_notify_zoom_handler = None
24
        EvinceDocument.init()
25
        self._view = EvinceView.View()
26
27
    def setup(self, activity):
28
        self._activity = activity
29
        self._view.connect('selection-changed',
30
                            activity._view_selection_changed_cb)
31
        self._view.connect('external-link', self.__handle_link_cb)
32
33
        activity._scrolled = Gtk.ScrolledWindow()
34
        activity._scrolled.set_policy(Gtk.PolicyType.AUTOMATIC,
35
                Gtk.PolicyType.AUTOMATIC)
36
        activity._scrolled.props.shadow_type = Gtk.ShadowType.NONE
37
38
        activity._scrolled.add(self._view)
39
        self._view.show()
40
41
        self._view.set_events(self._view.get_events() |
42
                Gdk.EventMask.TOUCH_MASK)
43
        self._view.connect('event', self.__view_touch_event_cb)
44
45
        activity._hbox.pack_start(activity._scrolled, True, True, 0)
46
        activity._scrolled.show()
47
48
        self.dpi = activity.dpi
49
50
    def load_document(self, file_path):
51
        try:
52
            self._document = \
53
                    EvinceDocument.Document.factory_get_document(file_path)
54
        except GObject.GError, e:
55
            _logger.error('Can not load document: %s', e)
56
            return
57
        else:
58
            self._model = EvinceView.DocumentModel()
59
            self._model.set_document(self._document)
60
            self._view.set_model(self._model)
61
62
            # set dpi
63
            # TODO why we need set this?
64
            """
65
            min_scale = self._model.get_min_scale()
66
            max_scale = self._model.get_max_scale()
67
            logging.error("min scale %s max_scale %s", min_scale, max_scale)
68
            logging.error("setting min scale %s", min_scale * self.dpi / 72.0)
69
            logging.error("setting max scale %s", max_scale * self.dpi / 72.0)
70
            self._model.set_min_scale(min_scale * self.dpi / 72.0)
71
            self._model.set_max_scale(max_scale * self.dpi / 72.0)
72
            """
73
74
    def __view_touch_event_cb(self, widget, event):
75
        if event.type == Gdk.EventType.TOUCH_BEGIN:
76
            x = event.touch.x
77
            view_width = widget.get_allocation().width
78
            if x > view_width * 3 / 4:
79
                self._view.scroll(Gtk.ScrollType.PAGE_FORWARD, False)
80
            elif x < view_width * 1 / 4:
81
                self._view.scroll(Gtk.ScrollType.PAGE_BACKWARD, False)
82
83
    def __handle_link_cb(self, widget, url_object):
84
        url = url_object.get_uri()
85
        logging.debug('Create journal entry for URL: %s', url)
86
        jobject = datastore.create()
87
        metadata = {
88
            'title': "%s: %s" % (_('URL from Read'), url),
89
            'title_set_by_user': '1',
90
            'icon-color': profile.get_color().to_string(),
91
            'mime_type': 'text/uri-list',
92
            }
93
        for k, v in metadata.items():
94
            jobject.metadata[k] = v
95
        file_path = os.path.join(get_activity_root(),
96
                'instance', '%i_' % time.time())
97
        open(file_path, 'w').write(url + '\r\n')
98
        os.chmod(file_path, 0755)
99
        jobject.set_file_path(file_path)
100
        datastore.write(jobject)
101
        show_object_in_journal(jobject.object_id)
102
        jobject.destroy()
103
        os.unlink(file_path)
104
105
    def get_current_page(self):
106
        return self._model.props.page
107
108
    def get_current_link(self):
109
        # TODO
110
        return ""
111
112
    def set_current_page(self, page):
113
        if page >= self._document.get_n_pages():
114
            page = self._document.get_n_pages() - 1
115
        elif page < 0:
116
            page = 0
117
        self._model.props.page = page
118
119
    def next_page(self):
120
        self._view.next_page()
121
122
    def previous_page(self):
123
        self._view.previous_page()
124
125
    def get_pagecount(self):
126
        '''
127
        Returns the pagecount of the loaded file
128
        '''
129
        return self._document.get_n_pages()
130
131
    def load_metadata(self, activity):
132
133
        self.metadata = activity.metadata
134
135
        if not self.metadata['title_set_by_user'] == '1':
136
            title = self._document.get_title()
137
            if title:
138
                self.metadata['title'] = title
139
140
        sizing_mode = self.metadata.get('Read_sizing_mode', 'fit-width')
141
        _logger.debug('Found sizing mode: %s', sizing_mode)
142
        if sizing_mode == "best-fit":
143
            self._model.set_sizing_mode(EvinceView.SizingMode.BEST_FIT)
144
            if hasattr(self._view, 'update_view_size'):
145
                self._view.update_view_size(self._scrolled)
146
        elif sizing_mode == "free":
147
            self._model.set_sizing_mode(EvinceView.SizingMode.FREE)
148
            self._model.set_scale(float(self.metadata.get('Read_zoom', '1.0')))
149
            _logger.debug('Set zoom to %f', self._model.props.scale)
150
        elif sizing_mode == "fit-width":
151
            self._model.set_sizing_mode(EvinceView.SizingMode.FIT_WIDTH)
152
            if hasattr(self._view, 'update_view_size'):
153
                self._view.update_view_size(self._scrolled)
154
        else:
155
            # this may happen when we get a document from a buddy with a later
156
            # version of Read, for example.
157
            _logger.warning("Unknown sizing_mode state '%s'", sizing_mode)
158
            if self.metadata.get('Read_zoom', None) is not None:
159
                self._model.set_scale(float(self.metadata['Read_zoom']))
160
161
    def update_metadata(self, activity):
162
        self.metadata = activity.metadata
163
        self.metadata['Read_zoom'] = str(self._model.props.scale)
164
165
        if self._model.get_sizing_mode() == EvinceView.SizingMode.BEST_FIT:
166
            self.metadata['Read_sizing_mode'] = "best-fit"
167
        elif self._model.get_sizing_mode() == EvinceView.SizingMode.FREE:
168
            self.metadata['Read_sizing_mode'] = "free"
169
        elif self._model.get_sizing_mode() == EvinceView.SizingMode.FIT_WIDTH:
170
            self.metadata['Read_sizing_mode'] = "fit-width"
171
        else:
172
            _logger.error("Don't know how to save sizing_mode state '%s'" %
173
                          self._model.get_sizing_mode())
174
175
    def can_highlight(self):
176
        return False
177
178
    def can_do_text_to_speech(self):
179
        return False
180
181
    def get_zoom(self):
182
        '''
183
        Returns the current zoom level
184
        '''
185
        return self._model.props.scale * 100
186
187
    def set_zoom(self, value):
188
        '''
189
        Sets the current zoom level
190
        '''
191
        self._model.props.sizing_mode = EvinceView.SizingMode.FREE
192
193
        if not self._view_notify_zoom_handler:
194
            return
195
196
        self._model.disconnect(self._view_notify_zoom_handler)
197
        try:
198
            self._model.props.scale = value / 100.0
199
        finally:
200
            self._view_notify_zoom_handler = self._model.connect(
201
                'notify::scale', self._zoom_handler)
202
203
    def zoom_in(self):
204
        '''
205
        Zooms in (increases zoom level by 0.1)
206
        '''
207
        self._model.props.sizing_mode = EvinceView.SizingMode.FREE
208
        self._view.zoom_in()
209
210
    def zoom_out(self):
211
        '''
212
        Zooms out (decreases zoom level by 0.1)
213
        '''
214
        self._model.props.sizing_mode = EvinceView.SizingMode.FREE
215
        self._view.zoom_out()
216
217
    def zoom_to_width(self):
218
        self._model.props.sizing_mode = EvinceView.SizingMode.FIT_WIDTH
219
220
    def can_zoom_in(self):
221
        '''
222
        Returns True if it is possible to zoom in further
223
        '''
224
        return self._view.can_zoom_in()
225
226
    def can_zoom_out(self):
227
        '''
228
        Returns True if it is possible to zoom out further
229
        '''
230
        return self._view.can_zoom_out()
231
232
    def can_zoom_to_width(self):
233
        return True
234
235
    def zoom_to_best_fit(self):
236
        self._model.props.sizing_mode = EvinceView.SizingMode.BEST_FIT
237
238
    def zoom_to_actual_size(self):
239
        self._model.props.sizing_mode = EvinceView.SizingMode.FREE
240
        self._model.props.scale = 1.0
241
242
    def connect_zoom_handler(self, handler):
243
        self._zoom_handler = handler
244
        self._view_notify_zoom_handler = \
245
                self._model.connect('notify::scale', handler)
246
        return self._view_notify_zoom_handler
247
248
    def setup_find_job(self, text, updated_cb):
249
        self._find_job = EvinceView.JobFind.new(document=self._document,
250
                start_page=0,
251
                n_pages=self._document.get_n_pages(),
252
                text=text, case_sensitive=False)
253
        self._find_updated_handler = self._find_job.connect('updated',
254
                updated_cb)
255
        self._view.find_started(self._find_job)
256
        EvinceView.Job.scheduler_push_job(self._find_job,
257
                EvinceView.JobPriority.PRIORITY_NONE)
258
        return self._find_job, self._find_updated_handler
259
260
    def connect_page_changed_handler(self, handler):
261
        self._model.connect('page-changed', handler)
262
263
    def update_toc(self, activity):
264
        return False
265
        """
266
        Commented because does not work and crash with old evince
267
        doc = self._model.get_document()
268
        if not doc.has_document_links():
269
            logging.error('The pdf file does not have a index')
270
            return False
271
        else:
272
            self._job_links = EvinceView.JobLinks.new(document=doc)
273
            self._job_links.connect('finished', self.__index_loaded_cb,
274
                    activity)
275
        EvinceView.Job.scheduler_push_job(self._job_links,
276
                EvinceView.JobPriority.PRIORITY_NONE)
277
        """
278
279
    def __index_loaded_cb(self, job, activity):
280
        logging.error('__index_loaded_cb %s %s', job.__class__, dir(job))
281
        logging.error('job.succeeded %s', job.succeeded())
282
        logging.error('job.is_finished %s', job.is_finished())
283
284
        self._index_model = job.model
285
286
        logging.error('index_model loaded %s', job.model.__class__)
287
        if job.model is None:
288
            return False
289
        """
290
        _iter = job.model.get_iter_first()
291
        while True:
292
            value = job.model.get_value(_iter, 0)
293
            logging.error('value %s', value)
294
            _iter = job.model.iter_next(_iter)
295
            if _iter is None:
296
                break
297
        """
298
299
        activity.show_navigator_button()
300
        activity.set_navigator_model(self._epub.get_links_model())
301
        return True
302
303
    def find_set_highlight_search(self, set_highlight_search):
304
        self._view.find_set_highlight_search(set_highlight_search)
305
306
    def find_next(self):
307
        '''
308
        Highlights the next matching item for current search
309
        '''
310
        self._view.find_next()
311
312
    def find_previous(self):
313
        '''
314
        Highlights the previous matching item for current search
315
        '''
316
        self._view.find_previous()
317
318
    def find_changed(self, job, page=None):
319
        pass
320
321
    def scroll(self, scrolltype, horizontal):
322
        '''
323
        Scrolls through the pages.
324
        Scrolling is horizontal if horizontal is set to True
325
        Valid scrolltypes are:
326
        Gtk.ScrollType.PAGE_BACKWARD, Gtk.ScrollType.PAGE_FORWARD,
327
        Gtk.ScrollType.STEP_BACKWARD, Gtk.ScrollType.STEP_FORWARD,
328
        Gtk.ScrollType.START and Gtk.ScrollType.END
329
        '''
330
        _logger.error('scroll: %s', scrolltype)
331
332
        if scrolltype == Gtk.ScrollType.PAGE_BACKWARD:
333
            self._view.scroll(Gtk.ScrollType.PAGE_BACKWARD, horizontal)
334
        elif scrolltype == Gtk.ScrollType.PAGE_FORWARD:
335
            self._view.scroll(Gtk.ScrollType.PAGE_FORWARD, horizontal)
336
        elif scrolltype == Gtk.ScrollType.STEP_BACKWARD:
337
            self._scroll_step(False, horizontal)
338
        elif scrolltype == Gtk.ScrollType.STEP_FORWARD:
339
            self._scroll_step(True, horizontal)
340
        elif scrolltype == Gtk.ScrollType.START:
341
            self.set_current_page(0)
342
        elif scrolltype == Gtk.ScrollType.END:
343
            self.set_current_page(self._document.get_n_pages())
344
        else:
345
            print ('Got unsupported scrolltype %s' % str(scrolltype))
346
347
    def _scroll_step(self, forward, horizontal):
348
        if horizontal:
349
            adj = self._activity._scrolled.get_hadjustment()
350
        else:
351
            adj = self._activity._scrolled.get_vadjustment()
352
        value = adj.get_value()
353
        step = adj.get_step_increment()
354
        if forward:
355
            adj.set_value(value + step)
356
        else:
357
            adj.set_value(value - step)
358
359
    def copy(self):
360
        self._view.copy()