Codebase list sugar-read-activity / c411b44
Show the bookmarks in a tray like in Browse activity The preview is saved in another table in the sqlite db. Signed-off-by: Gonzalo Odiard <gonzalo@laptop.org> Gonzalo Odiard 11 years ago
4 changed file(s) with 295 addition(s) and 4 deletion(s). Raw diff Collapse all Expand all
0 # Copyright (C) 2007, One Laptop Per Child
1 #
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of the GNU Lesser General Public
4 # License as published by the Free Software Foundation; either
5 # version 2 of the License, or (at your option) any later version.
6 #
7 # This library is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # Lesser General Public License for more details.
11 #
12 # You should have received a copy of the GNU Lesser General Public
13 # License along with this library; if not, write to the
14 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
15 # Boston, MA 02111-1307, USA.
16
17 from gi.repository import Gtk
18 from gi.repository import Gdk
19 from gi.repository import GdkPixbuf
20 from gi.repository import GObject
21 from gi.repository import Rsvg
22
23 import os
24 import StringIO
25 import cairo
26 from gettext import gettext as _
27 import re
28 import gc
29 import logging
30
31 from sugar3.graphics.palette import Palette
32 from sugar3.graphics.tray import TrayButton
33 from sugar3.graphics import style
34
35
36 class LinkButton(TrayButton, GObject.GObject):
37 __gtype_name__ = 'LinkButton'
38 __gsignals__ = {
39 'remove_link': (GObject.SignalFlags.RUN_FIRST,
40 None, ([int])),
41 'go_to_bookmark': (GObject.SignalFlags.RUN_FIRST,
42 None, ([int])),
43 }
44
45 def __init__(self, buf, color, title, owner, page):
46 TrayButton.__init__(self)
47
48 # Color read from the Journal may be Unicode, but Rsvg needs
49 # it as single byte string:
50 if isinstance(color, unicode):
51 color = str(color)
52 self.set_image(buf, color.split(',')[1], color.split(',')[0])
53
54 self.page = int(page)
55 info = title + '\n' + owner
56 self.setup_rollover_options(info)
57
58 def set_image(self, buf, fill='#0000ff', stroke='#4d4c4f'):
59 img = Gtk.Image()
60 str_buf = StringIO.StringIO(buf)
61 thumb_surface = cairo.ImageSurface.create_from_png(str_buf)
62
63 bg_width, bg_height = style.zoom(120), style.zoom(110)
64 bg_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, bg_width,
65 bg_height)
66 context = cairo.Context(bg_surface)
67 # draw a rectangle in the background with the selected colors
68 context.set_line_width(style.zoom(10))
69 context.set_source_rgba(*style.Color(fill).get_rgba())
70 context.rectangle(0, 0, bg_width, bg_height)
71 context.fill_preserve()
72 context.set_source_rgba(*style.Color(stroke).get_rgba())
73 context.stroke()
74 # add the screenshot
75 dest_x = style.zoom(10)
76 dest_y = style.zoom(20)
77 context.set_source_surface(thumb_surface, dest_x, dest_y)
78 thumb_width, thumb_height = style.zoom(100), style.zoom(80)
79 context.rectangle(dest_x, dest_y, thumb_width, thumb_height)
80 context.fill()
81
82 pixbuf_bg = Gdk.pixbuf_get_from_surface(bg_surface, 0, 0,
83 bg_width, bg_height)
84
85 img.set_from_pixbuf(pixbuf_bg)
86 self.set_icon_widget(img)
87 img.show()
88
89 def setup_rollover_options(self, info):
90 palette = Palette(info, text_maxlen=50)
91 self.set_palette(palette)
92
93 menu_item = Gtk.MenuItem(_('Go to Bookmark'))
94 menu_item.connect('activate', self.go_to_bookmark_cb)
95 palette.menu.append(menu_item)
96 menu_item.show()
97
98 menu_item = Gtk.MenuItem(_('Remove'))
99 menu_item.connect('activate', self.item_remove_cb)
100 palette.menu.append(menu_item)
101 menu_item.show()
102
103 def item_remove_cb(self, widget):
104 self.emit('remove_link', self.page)
105
106 def go_to_bookmark_cb(self, widget):
107 self.emit('go_to_bookmark', self.page)
2222 from gettext import gettext as _
2323 import re
2424 import md5
25 import StringIO
26 import cairo
2527
2628 import dbus
2729 from gi.repository import GObject
4143 from sugar3.graphics.alert import ConfirmationAlert
4244 from sugar3.activity.widgets import ActivityToolbarButton
4345 from sugar3.activity.widgets import StopButton
46 from sugar3.graphics.tray import HTray
4447 from sugar3 import network
4548 from sugar3 import mime
49 from sugar3 import profile
4650
4751 from sugar3.datastore import datastore
4852 from sugar3.graphics.objectchooser import ObjectChooser
5357 from bookmarkview import BookmarkView
5458 from readdb import BookmarkManager
5559 from sugarmenuitem import SugarMenuItem
60 from linkbutton import LinkButton
5661
5762 _HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager'
5863 _HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager'
150155 self._bookmark_view.connect('bookmark-changed',
151156 self._update_bookmark_cb)
152157
158 tray = HTray()
159 self.set_tray(tray, Gtk.PositionType.BOTTOM)
160
153161 toolbar_box = ToolbarBox()
154162
155163 self.activity_button = ActivityToolbarButton(self)
176184 self.__view_toolbar_go_fullscreen_cb)
177185 self._view_toolbar.connect('toggle-index-show',
178186 self.__toogle_navigator_cb)
187 self._view_toolbar.connect('toggle-tray-show',
188 self.__toogle_tray_cb)
179189 view_toolbar_button = ToolbarButton(
180190 page=self._view_toolbar,
181191 icon_name='toolbar-view')
442452 self._toc_separator.hide()
443453 if scrollbar_pos > -1:
444454 self._view.set_vertical_pos(scrollbar_pos)
455
456 def __toogle_tray_cb(self, button, visible):
457 if visible:
458 logging.error('Show tray')
459 self.tray.show()
460 else:
461 logging.error('Hide tray')
462 self.tray.hide()
445463
446464 def __num_page_entry_insert_text_cb(self, entry, text, length, position):
447465 if not re.match('[0-9]', text):
852870
853871 filehash = get_md5(filepath)
854872 self._bookmarkmanager = BookmarkManager(filehash)
873 self._bookmarkmanager.connect('added_bookmark',
874 self._added_bookmark_cb)
875 self._bookmarkmanager.connect('removed_bookmark',
876 self._removed_bookmark_cb)
877
878 # Add the bookmarks to the tray
879 color = profile.get_color().to_string()
880 owner = profile.get_nick_name()
881 for bookmark in self._bookmarkmanager.get_bookmarks():
882 page = bookmark.page_no
883 title = _('Page %d') % page
884 thumb = self._bookmarkmanager.get_bookmark_preview(page)
885 if thumb is None:
886 logging.error('Preview NOT FOUND')
887 thumb = self._get_screenshot()
888 logging.error('Add link to tray page %d preview %s', page, thumb)
889 self._add_link_totray(page, thumb, color, title, owner)
890
855891 self._bookmark_view.set_bookmarkmanager(self._bookmarkmanager)
856892 self._update_toc()
857893 self._view.connect_page_changed_handler(self.__page_changed_cb)
10411077
10421078 def __view_toolbar_go_fullscreen_cb(self, view_toolbar):
10431079 self.fullscreen()
1080
1081 def _added_bookmark_cb(self, bookmarkmanager, page):
1082 logging.error('Bookmark added page %d', page)
1083 title = _('Page %d') % page
1084 color = profile.get_color().to_string()
1085 owner = profile.get_nick_name()
1086 thumb = self._get_screenshot()
1087 self._add_link_totray(page, thumb, color, title, owner)
1088 bookmarkmanager.add_bookmark_preview(page - 1, thumb)
1089
1090 def _removed_bookmark_cb(self, bookmarkmanager, page):
1091 logging.error('Bookmark removed page %d', page)
1092 # remove button from tray
1093 for button in self.tray.get_children():
1094 if button.page == page:
1095 self.tray.remove_item(button)
1096 if len(self.tray.get_children()) == 0:
1097 self.tray.hide()
1098 self._view_toolbar.traybutton.props.active = False
1099
1100 def _add_link_totray(self, page, buf, color, title, owner):
1101 ''' add a link to the tray '''
1102 item = LinkButton(buf, color, title, owner, page)
1103 item.connect('clicked', self._bookmark_button_clicked_cb, page)
1104 item.connect('remove_link', self._bookmark_button_removed_cb)
1105 self.tray.show()
1106 self.tray.add_item(item)
1107 item.show()
1108 self._view_toolbar.traybutton.props.active = True
1109
1110 def _bookmark_button_clicked_cb(self, button, page):
1111 self._view.set_current_page(page - 1)
1112
1113 def _bookmark_button_removed_cb(self, button, page):
1114 self._bookmark_view.del_bookmark(page - 1)
1115
1116 def _get_screenshot(self):
1117 """Copied from activity.get_preview()
1118 """
1119 if self.canvas is None or not hasattr(self.canvas, 'get_window'):
1120 return None
1121
1122 window = self.canvas.get_window()
1123 alloc = self.canvas.get_allocation()
1124
1125 dummy_cr = Gdk.cairo_create(window)
1126 target = dummy_cr.get_target()
1127 canvas_width, canvas_height = alloc.width, alloc.height
1128 screenshot_surface = target.create_similar(cairo.CONTENT_COLOR,
1129 canvas_width, canvas_height)
1130 del dummy_cr, target
1131
1132 cr = cairo.Context(screenshot_surface)
1133 r, g, b, a_ = style.COLOR_PANEL_GREY.get_rgba()
1134 cr.set_source_rgb(r, g, b)
1135 cr.paint()
1136 self.canvas.draw(cr)
1137 del cr
1138
1139 preview_width, preview_height = style.zoom(100), style.zoom(80)
1140 preview_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
1141 preview_width, preview_height)
1142 cr = cairo.Context(preview_surface)
1143
1144 scale_w = preview_width * 1.0 / canvas_width
1145 scale_h = preview_height * 1.0 / canvas_height
1146 scale = min(scale_w, scale_h)
1147
1148 translate_x = int((preview_width - (canvas_width * scale)) / 2)
1149 translate_y = int((preview_height - (canvas_height * scale)) / 2)
1150
1151 cr.translate(translate_x, translate_y)
1152 cr.scale(scale, scale)
1153
1154 cr.set_source_rgba(1, 1, 1, 0)
1155 cr.set_operator(cairo.OPERATOR_SOURCE)
1156 cr.paint()
1157 cr.set_source_surface(screenshot_surface)
1158 cr.paint()
1159
1160 preview_str = StringIO.StringIO()
1161 preview_surface.write_to_png(preview_str)
1162 return preview_str.getvalue()
2020 import shutil
2121 import sqlite3
2222 import time
23
23 import base64
24
25 from gi.repository import GObject
2426 from sugar3 import profile
2527
2628 from readbookmark import Bookmark
7476 conn.commit()
7577
7678
77 class BookmarkManager:
79 def _init_db_previews(conn):
80 conn.execute('CREATE TABLE IF NOT EXISTS PREVIEWS ' +
81 '(md5 TEXT, page INTEGER, ' +
82 'preview)')
83 conn.commit()
84
85
86 class BookmarkManager(GObject.GObject):
87
88 __gsignals__ = {
89 'added_bookmark': (GObject.SignalFlags.RUN_FIRST,
90 None, ([int])),
91 'removed_bookmark': (GObject.SignalFlags.RUN_FIRST,
92 None, ([int])),
93 }
7894
7995 def __init__(self, filehash):
96 GObject.GObject.__init__(self)
8097 self._filehash = filehash
8198
8299 dbpath = _init_db()
85102
86103 self._conn = sqlite3.connect(dbpath)
87104 _init_db_highlights(self._conn)
105 _init_db_previews(self._conn)
88106
89107 self._conn.text_factory = lambda x: unicode(x, "utf-8", "ignore")
90108
97115 self._color = profile.get_color().to_string()
98116
99117 def add_bookmark(self, page, content, local=1):
118 logging.debug('add_bookmark page %d', page)
100119 # locale = 0 means that this is a bookmark originally
101120 # created by the person who originally shared the file
102121 timestamp = time.time()
107126 self._conn.commit()
108127
109128 self._resync_bookmark_cache()
129 self.emit('added_bookmark', page + 1)
110130
111131 def del_bookmark(self, page):
112132 # We delete only the locally made bookmark
113
133 logging.debug('del_bookmark page %d', page)
114134 t = (self._filehash, page, self._user)
115135 self._conn.execute('delete from bookmarks ' + \
116136 'where md5=? and page=? and user=?', t)
117137 self._conn.commit()
118
138 self._del_bookmark_preview(page)
119139 self._resync_bookmark_cache()
140 self.emit('removed_bookmark', page + 1)
141
142 def add_bookmark_preview(self, page, preview):
143 logging.debug('add_bookmark_preview page %d', page)
144 t = (self._filehash, page, base64.b64encode(preview))
145 self._conn.execute('insert into previews values ' + \
146 '(?, ?, ?)', t)
147 self._conn.commit()
148
149 def _del_bookmark_preview(self, page):
150 logging.debug('del_bookmark_preview page %d', page)
151 t = (self._filehash, page)
152 self._conn.execute('delete from previews ' + \
153 'where md5=? and page=?', t)
154 self._conn.commit()
155
156 def get_bookmark_preview(self, page):
157 logging.debug('get_bookmark page %d', page)
158 rows = self._conn.execute('select preview from previews ' + \
159 'where md5=? and page=?', (self._filehash, page))
160 for row in rows:
161 return base64.b64decode(row[0])
162 return None
120163
121164 def _populate_bookmarks(self):
122165 # TODO: Figure out if caching the entire set of bookmarks
126169
127170 for row in rows:
128171 self._bookmarks.append(Bookmark(row))
172 logging.debug('loading %d bookmarks', len(self._bookmarks))
173
174 def get_bookmarks(self):
175 return self._bookmarks
129176
130177 def get_bookmarks_for_page(self, page):
131178 bookmarks = []
166166 ([])),
167167 'toggle-index-show': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
168168 ([bool])),
169 'toggle-tray-show': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
170 ([bool])),
169171 }
170172
171173 def __init__(self):
222224 self._fullscreen.connect('clicked', self._fullscreen_cb)
223225 self.insert(self._fullscreen, -1)
224226 self._fullscreen.show()
227
228 self.traybutton = ToggleToolButton('tray-show')
229 self.traybutton.set_icon_name('tray-favourite')
230 self.traybutton.connect('toggled', self.__tray_toggled_cb)
231 #self.traybutton.props.sensitive = False
232 self.traybutton.props.active = False
233 self.insert(self.traybutton, -1)
234 self.traybutton.show()
225235
226236 self._view_notify_zoom_handler = None
227237
274284
275285 def _fullscreen_cb(self, button):
276286 self.emit('go-fullscreen')
287
288 def __tray_toggled_cb(self, button):
289 self.emit('toggle-tray-show', button.get_active())
290 if button.props.active:
291 self.traybutton.set_tooltip(_('Show Tray'))
292 else:
293 self.traybutton.set_tooltip(_('Hide Tray'))