1 |
# Copyright (C) 2006, Red Hat, Inc. |
2 |
# Copyright (C) 2009 Martin Langhoff, Simon Schampijer, Daniel Drake, |
3 |
# Tomeu Vizoso |
4 |
# |
5 |
# This program is free software; you can redistribute it and/or modify |
6 |
# it under the terms of the GNU General Public License as published by |
7 |
# the Free Software Foundation; either version 2 of the License, or |
8 |
# (at your option) any later version. |
9 |
# |
10 |
# This program is distributed in the hope that it will be useful, |
11 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
# GNU General Public License for more details. |
14 |
# |
15 |
# You should have received a copy of the GNU General Public License |
16 |
# along with this program; if not, write to the Free Software |
17 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 |
|
19 |
import os |
20 |
import logging |
21 |
from gettext import gettext as _ |
22 |
|
23 |
import gobject |
24 |
gobject.threads_init() |
25 |
|
26 |
import gtk |
27 |
import base64 |
28 |
import time |
29 |
import shutil |
30 |
import sqlite3 |
31 |
import cjson |
32 |
import gconf |
33 |
import locale |
34 |
import cairo |
35 |
from hashlib import sha1 |
36 |
|
37 |
# HACK: Needed by http://dev.sugarlabs.org/ticket/456 |
38 |
import gnome |
39 |
gnome.init('Hulahop', '1.0') |
40 |
|
41 |
from sugar.activity import activity |
42 |
from sugar.graphics import style |
43 |
import telepathy |
44 |
import telepathy.client |
45 |
from sugar.presence import presenceservice |
46 |
from sugar.graphics.tray import HTray |
47 |
from sugar import profile |
48 |
from sugar.graphics.alert import Alert |
49 |
from sugar.graphics.icon import Icon |
50 |
from sugar import mime |
51 |
|
52 |
# Attempt to import the new toolbar classes. If the import fails, |
53 |
# fall back to the old toolbar style. |
54 |
try: |
55 |
from sugar.graphics.toolbarbox import ToolbarButton |
56 |
NEW_TOOLBARS = True |
57 |
except ImportError: |
58 |
NEW_TOOLBARS = False |
59 |
|
60 |
PROFILE_VERSION = 2 |
61 |
|
62 |
_profile_version = 0 |
63 |
_profile_path = os.path.join(activity.get_activity_root(), 'data/gecko') |
64 |
_version_file = os.path.join(_profile_path, 'version') |
65 |
|
66 |
if not NEW_TOOLBARS: |
67 |
_TOOLBAR_EDIT = 1 |
68 |
_TOOLBAR_BROWSE = 2 |
69 |
|
70 |
if os.path.exists(_version_file): |
71 |
f = open(_version_file) |
72 |
_profile_version = int(f.read()) |
73 |
f.close() |
74 |
|
75 |
if _profile_version < PROFILE_VERSION: |
76 |
if not os.path.exists(_profile_path): |
77 |
os.mkdir(_profile_path) |
78 |
|
79 |
shutil.copy('cert8.db', _profile_path) |
80 |
os.chmod(os.path.join(_profile_path, 'cert8.db'), 0660) |
81 |
|
82 |
f = open(_version_file, 'w') |
83 |
f.write(str(PROFILE_VERSION)) |
84 |
f.close() |
85 |
|
86 |
|
87 |
def _seed_xs_cookie(): |
88 |
''' Create a HTTP Cookie to authenticate with the Schoolserver |
89 |
''' |
90 |
client = gconf.client_get_default() |
91 |
backup_url = client.get_string('/desktop/sugar/backup_url') |
92 |
if not backup_url: |
93 |
_logger.debug('seed_xs_cookie: Not registered with Schoolserver') |
94 |
return |
95 |
|
96 |
jabber_server = client.get_string( |
97 |
'/desktop/sugar/collaboration/jabber_server') |
98 |
|
99 |
pubkey = profile.get_profile().pubkey |
100 |
cookie_data = {'color': profile.get_color().to_string(), |
101 |
'pkey_hash': sha1(pubkey).hexdigest()} |
102 |
|
103 |
db_path = os.path.join(_profile_path, 'cookies.sqlite') |
104 |
try: |
105 |
cookies_db = sqlite3.connect(db_path) |
106 |
c = cookies_db.cursor() |
107 |
|
108 |
c.execute('''CREATE TABLE IF NOT EXISTS |
109 |
moz_cookies |
110 |
(id INTEGER PRIMARY KEY, |
111 |
name TEXT, |
112 |
value TEXT, |
113 |
host TEXT, |
114 |
path TEXT, |
115 |
expiry INTEGER, |
116 |
lastAccessed INTEGER, |
117 |
isSecure INTEGER, |
118 |
isHttpOnly INTEGER)''') |
119 |
|
120 |
c.execute('''SELECT id |
121 |
FROM moz_cookies |
122 |
WHERE name=? AND host=? AND path=?''', |
123 |
('xoid', jabber_server, '/')) |
124 |
|
125 |
if c.fetchone(): |
126 |
_logger.debug('seed_xs_cookie: Cookie exists already') |
127 |
return |
128 |
|
129 |
expire = int(time.time()) + 10 * 365 * 24 * 60 * 60 |
130 |
c.execute('''INSERT INTO moz_cookies (name, value, host, |
131 |
path, expiry, lastAccessed, |
132 |
isSecure, isHttpOnly) |
133 |
VALUES(?,?,?,?,?,?,?,?)''', |
134 |
('xoid', cjson.encode(cookie_data), jabber_server, |
135 |
'/', expire, 0, 0, 0)) |
136 |
cookies_db.commit() |
137 |
cookies_db.close() |
138 |
except sqlite3.Error: |
139 |
_logger.exception('seed_xs_cookie: could not write cookie') |
140 |
else: |
141 |
_logger.debug('seed_xs_cookie: Updated cookie successfully') |
142 |
|
143 |
|
144 |
import hulahop |
145 |
hulahop.set_app_version(os.environ['SUGAR_BUNDLE_VERSION']) |
146 |
hulahop.startup(_profile_path) |
147 |
|
148 |
from xpcom import components |
149 |
|
150 |
|
151 |
def _set_accept_languages(): |
152 |
''' Set intl.accept_languages based on the locale |
153 |
''' |
154 |
|
155 |
lang = locale.getdefaultlocale()[0] |
156 |
if not lang: |
157 |
_logger.debug("Set_Accept_language: unrecognised LANG format") |
158 |
return |
159 |
lang = lang.split('_') |
160 |
|
161 |
# e.g. es-uy, es |
162 |
pref = lang[0] + "-" + lang[1].lower() + ", " + lang[0] |
163 |
cls = components.classes["@mozilla.org/preferences-service;1"] |
164 |
prefService = cls.getService(components.interfaces.nsIPrefService) |
165 |
branch = prefService.getBranch('') |
166 |
branch.setCharPref('intl.accept_languages', pref) |
167 |
logging.debug('LANG set') |
168 |
|
169 |
from browser import TabbedView |
170 |
from browser import Browser |
171 |
from webtoolbar import PrimaryToolbar |
172 |
from edittoolbar import EditToolbar |
173 |
from viewtoolbar import ViewToolbar |
174 |
import downloadmanager |
175 |
import globalhistory |
176 |
import filepicker |
177 |
|
178 |
_LIBRARY_PATH = '/usr/share/library-common/index.html' |
179 |
|
180 |
from model import Model |
181 |
from sugar.presence.tubeconn import TubeConnection |
182 |
from messenger import Messenger |
183 |
from linkbutton import LinkButton |
184 |
|
185 |
SERVICE = "org.laptop.WebActivity" |
186 |
IFACE = SERVICE |
187 |
PATH = "/org/laptop/WebActivity" |
188 |
|
189 |
_logger = logging.getLogger('web-activity') |
190 |
|
191 |
|
192 |
class WebActivity(activity.Activity): |
193 |
def __init__(self, handle): |
194 |
activity.Activity.__init__(self, handle) |
195 |
|
196 |
_logger.debug('Starting the web activity') |
197 |
|
198 |
self._force_close = False |
199 |
self._tabbed_view = TabbedView() |
200 |
|
201 |
_set_accept_languages() |
202 |
_seed_xs_cookie() |
203 |
|
204 |
# don't pick up the sugar theme - use the native mozilla one instead |
205 |
cls = components.classes['@mozilla.org/preferences-service;1'] |
206 |
pref_service = cls.getService(components.interfaces.nsIPrefService) |
207 |
branch = pref_service.getBranch("mozilla.widget.") |
208 |
branch.setBoolPref("disable-native-theme", True) |
209 |
|
210 |
# HACK |
211 |
# Currently, the multiple tabs feature crashes the Browse activity |
212 |
# on cairo versions 1.8.10 or later. The exact cause for this |
213 |
# isn't exactly known. Thus, disable the multiple tabs feature |
214 |
# if we come across cairo versions >= 1.08.10 |
215 |
# More information can be found here: |
216 |
# http://lists.sugarlabs.org/archive/sugar-devel/2010-July/025187.html |
217 |
self._disable_multiple_tabs = cairo.cairo_version() >= 10810 |
218 |
if self._disable_multiple_tabs: |
219 |
logging.warning('Not enabling the multiple tabs feature due' |
220 |
' to a bug in cairo/mozilla') |
221 |
|
222 |
self._tray = HTray() |
223 |
self.set_tray(self._tray, gtk.POS_BOTTOM) |
224 |
self._tray.show() |
225 |
|
226 |
self._primary_toolbar = PrimaryToolbar(self._tabbed_view, self, |
227 |
self._disable_multiple_tabs) |
228 |
self._edit_toolbar = EditToolbar(self) |
229 |
self._view_toolbar = ViewToolbar(self) |
230 |
|
231 |
self._primary_toolbar.connect('add-link', self._link_add_button_cb) |
232 |
|
233 |
self._primary_toolbar.connect('add-tab', self._new_tab_cb) |
234 |
|
235 |
self._primary_toolbar.connect('go-home', self._go_home_button_cb) |
236 |
|
237 |
if NEW_TOOLBARS: |
238 |
logging.debug('Using new toolbars') |
239 |
|
240 |
self._edit_toolbar_button = ToolbarButton( |
241 |
page=self._edit_toolbar, |
242 |
icon_name='toolbar-edit') |
243 |
self._primary_toolbar.toolbar.insert( |
244 |
self._edit_toolbar_button, 1) |
245 |
|
246 |
view_toolbar_button = ToolbarButton( |
247 |
page=self._view_toolbar, |
248 |
icon_name='toolbar-view') |
249 |
self._primary_toolbar.toolbar.insert( |
250 |
view_toolbar_button, 2) |
251 |
|
252 |
self._primary_toolbar.show_all() |
253 |
self.set_toolbar_box(self._primary_toolbar) |
254 |
else: |
255 |
_logger.debug('Using old toolbars') |
256 |
|
257 |
toolbox = activity.ActivityToolbox(self) |
258 |
|
259 |
toolbox.add_toolbar(_('Edit'), self._edit_toolbar) |
260 |
self._edit_toolbar.show() |
261 |
|
262 |
toolbox.add_toolbar(_('Browse'), self._primary_toolbar) |
263 |
self._primary_toolbar.show() |
264 |
|
265 |
toolbox.add_toolbar(_('View'), self._view_toolbar) |
266 |
self._view_toolbar.show() |
267 |
|
268 |
self.set_toolbox(toolbox) |
269 |
toolbox.show() |
270 |
|
271 |
self.toolbox.set_current_toolbar(_TOOLBAR_BROWSE) |
272 |
|
273 |
self.set_canvas(self._tabbed_view) |
274 |
self._tabbed_view.show() |
275 |
|
276 |
self.model = Model() |
277 |
self.model.connect('add_link', self._add_link_model_cb) |
278 |
|
279 |
self.connect('key-press-event', self._key_press_cb) |
280 |
|
281 |
if handle.uri: |
282 |
self._tabbed_view.current_browser.load_uri(handle.uri) |
283 |
elif not self._jobject.file_path: |
284 |
# TODO: we need this hack until we extend the activity API for |
285 |
# opening URIs and default docs. |
286 |
self._load_homepage() |
287 |
|
288 |
self.messenger = None |
289 |
self.connect('shared', self._shared_cb) |
290 |
|
291 |
# Get the Presence Service |
292 |
self.pservice = presenceservice.get_instance() |
293 |
try: |
294 |
name, path = self.pservice.get_preferred_connection() |
295 |
self.tp_conn_name = name |
296 |
self.tp_conn_path = path |
297 |
self.conn = telepathy.client.Connection(name, path) |
298 |
except TypeError: |
299 |
_logger.debug('Offline') |
300 |
self.initiating = None |
301 |
|
302 |
if self._shared_activity is not None: |
303 |
_logger.debug('shared: %s', self._shared_activity.props.joined) |
304 |
|
305 |
if self._shared_activity is not None: |
306 |
# We are joining the activity |
307 |
_logger.debug('Joined activity') |
308 |
self.connect('joined', self._joined_cb) |
309 |
if self.get_shared(): |
310 |
# We've already joined |
311 |
self._joined_cb() |
312 |
else: |
313 |
_logger.debug('Created activity') |
314 |
|
315 |
def _new_tab_cb(self, gobject): |
316 |
self._load_homepage(new_tab=True) |
317 |
|
318 |
def _shared_cb(self, activity_): |
319 |
_logger.debug('My activity was shared') |
320 |
self.initiating = True |
321 |
self._setup() |
322 |
|
323 |
_logger.debug('This is my activity: making a tube...') |
324 |
self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(SERVICE, |
325 |
{}) |
326 |
|
327 |
def _setup(self): |
328 |
if self._shared_activity is None: |
329 |
_logger.debug('Failed to share or join activity') |
330 |
return |
331 |
|
332 |
bus_name, conn_path, channel_paths = \ |
333 |
self._shared_activity.get_channels() |
334 |
|
335 |
# Work out what our room is called and whether we have Tubes already |
336 |
room = None |
337 |
tubes_chan = None |
338 |
text_chan = None |
339 |
for channel_path in channel_paths: |
340 |
channel = telepathy.client.Channel(bus_name, channel_path) |
341 |
htype, handle = channel.GetHandle() |
342 |
if htype == telepathy.HANDLE_TYPE_ROOM: |
343 |
_logger.debug('Found our room: it has handle#%d "%s"', |
344 |
handle, |
345 |
self.conn.InspectHandles(htype, [handle])[0]) |
346 |
room = handle |
347 |
ctype = channel.GetChannelType() |
348 |
if ctype == telepathy.CHANNEL_TYPE_TUBES: |
349 |
_logger.debug('Found our Tubes channel at %s', |
350 |
channel_path) |
351 |
tubes_chan = channel |
352 |
elif ctype == telepathy.CHANNEL_TYPE_TEXT: |
353 |
_logger.debug('Found our Text channel at %s', |
354 |
channel_path) |
355 |
text_chan = channel |
356 |
|
357 |
if room is None: |
358 |
_logger.debug("Presence service didn't create a room") |
359 |
return |
360 |
if text_chan is None: |
361 |
_logger.debug("Presence service didn't create a text channel") |
362 |
return |
363 |
|
364 |
# Make sure we have a Tubes channel - PS doesn't yet provide one |
365 |
if tubes_chan is None: |
366 |
_logger.debug("Didn't find our Tubes channel, requesting one...") |
367 |
tubes_chan = self.conn.request_channel( |
368 |
telepathy.CHANNEL_TYPE_TUBES, telepathy.HANDLE_TYPE_ROOM, |
369 |
room, True) |
370 |
|
371 |
self.tubes_chan = tubes_chan |
372 |
self.text_chan = text_chan |
373 |
|
374 |
tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( \ |
375 |
'NewTube', self._new_tube_cb) |
376 |
|
377 |
def _list_tubes_reply_cb(self, tubes): |
378 |
for tube_info in tubes: |
379 |
self._new_tube_cb(*tube_info) |
380 |
|
381 |
def _list_tubes_error_cb(self, e): |
382 |
_logger.debug('ListTubes() failed: %s', e) |
383 |
|
384 |
def _joined_cb(self, activity_): |
385 |
if not self._shared_activity: |
386 |
return |
387 |
|
388 |
_logger.debug('Joined an existing shared activity') |
389 |
|
390 |
self.initiating = False |
391 |
self._setup() |
392 |
|
393 |
_logger.debug('This is not my activity: waiting for a tube...') |
394 |
self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( |
395 |
reply_handler=self._list_tubes_reply_cb, |
396 |
error_handler=self._list_tubes_error_cb) |
397 |
|
398 |
def _new_tube_cb(self, identifier, initiator, type, service, params, |
399 |
state): |
400 |
_logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' |
401 |
'params=%r state=%d', identifier, initiator, type, |
402 |
service, params, state) |
403 |
|
404 |
if (type == telepathy.TUBE_TYPE_DBUS and |
405 |
service == SERVICE): |
406 |
if state == telepathy.TUBE_STATE_LOCAL_PENDING: |
407 |
self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube( |
408 |
identifier) |
409 |
|
410 |
self.tube_conn = TubeConnection(self.conn, |
411 |
self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], |
412 |
identifier, group_iface=self.text_chan[ |
413 |
telepathy.CHANNEL_INTERFACE_GROUP]) |
414 |
|
415 |
_logger.debug('Tube created') |
416 |
self.messenger = Messenger(self.tube_conn, self.initiating, |
417 |
self.model) |
418 |
|
419 |
def _load_homepage(self, new_tab=False): |
420 |
# If new_tab is True, open the homepage in a new tab. |
421 |
if new_tab: |
422 |
browser = Browser() |
423 |
self._tabbed_view._append_tab(browser) |
424 |
else: |
425 |
browser = self._tabbed_view.current_browser |
426 |
|
427 |
if os.path.isfile(_LIBRARY_PATH): |
428 |
browser.load_uri('file://' + _LIBRARY_PATH) |
429 |
else: |
430 |
default_page = os.path.join(activity.get_bundle_path(), |
431 |
"data/index.html") |
432 |
browser.load_uri(default_page) |
433 |
|
434 |
def _get_data_from_file_path(self, file_path): |
435 |
fd = open(file_path, 'r') |
436 |
try: |
437 |
data = fd.read() |
438 |
finally: |
439 |
fd.close() |
440 |
return data |
441 |
|
442 |
def read_file(self, file_path): |
443 |
if self.metadata['mime_type'] == 'text/plain': |
444 |
data = self._get_data_from_file_path(file_path) |
445 |
self.model.deserialize(data) |
446 |
|
447 |
for link in self.model.data['shared_links']: |
448 |
_logger.debug('read: url=%s title=%s d=%s' % (link['url'], |
449 |
link['title'], |
450 |
link['color'])) |
451 |
self._add_link_totray(link['url'], |
452 |
base64.b64decode(link['thumb']), |
453 |
link['color'], link['title'], |
454 |
link['owner'], -1, link['hash']) |
455 |
logging.debug('########## reading %s', data) |
456 |
self._tabbed_view.set_session(self.model.data['history']) |
457 |
self._tabbed_view.set_current_page(self.model.data['current_tab']) |
458 |
elif self.metadata['mime_type'] == 'text/uri-list': |
459 |
data = self._get_data_from_file_path(file_path) |
460 |
uris = mime.split_uri_list(data) |
461 |
if len(uris) == 1: |
462 |
self._tabbed_view.props.current_browser.load_uri(uris[0]) |
463 |
else: |
464 |
_logger.error('Open uri-list: Does not support' |
465 |
'list of multiple uris by now.') |
466 |
else: |
467 |
self._tabbed_view.props.current_browser.load_uri(file_path) |
468 |
self._load_urls() |
469 |
|
470 |
def _load_urls(self): |
471 |
if self.model.data['currents'] != None: |
472 |
first = True |
473 |
for current_tab in self.model.data['currents']: |
474 |
if first: |
475 |
browser = self._tabbed_view.current_browser |
476 |
first = False |
477 |
else: |
478 |
browser = Browser() |
479 |
self._tabbed_view._append_tab(browser) |
480 |
browser.load_uri(current_tab['url']) |
481 |
|
482 |
def write_file(self, file_path): |
483 |
if not self.metadata['mime_type']: |
484 |
self.metadata['mime_type'] = 'text/plain' |
485 |
|
486 |
if self.metadata['mime_type'] == 'text/plain': |
487 |
|
488 |
browser = self._tabbed_view.current_browser |
489 |
|
490 |
if not self._jobject.metadata['title_set_by_user'] == '1': |
491 |
if browser.props.title: |
492 |
self.metadata['title'] = browser.props.title |
493 |
|
494 |
self.model.data['history'] = self._tabbed_view.get_session() |
495 |
current_tab = self._tabbed_view.get_current_page() |
496 |
self.model.data['current_tab'] = current_tab |
497 |
|
498 |
self.model.data['currents'] = [] |
499 |
for n in range(0, self._tabbed_view.get_n_pages()): |
500 |
n_browser = self._tabbed_view.get_nth_page(n) |
501 |
if n_browser != None: |
502 |
nsiuri = browser.progress.location |
503 |
ui_uri = browser.get_url_from_nsiuri(nsiuri) |
504 |
info = {'title': browser.props.title, 'url': ui_uri} |
505 |
self.model.data['currents'].append(info) |
506 |
|
507 |
f = open(file_path, 'w') |
508 |
try: |
509 |
logging.debug('########## writing %s', self.model.serialize()) |
510 |
f.write(self.model.serialize()) |
511 |
finally: |
512 |
f.close() |
513 |
|
514 |
def _link_add_button_cb(self, button): |
515 |
self._add_link() |
516 |
|
517 |
def _go_home_button_cb(self, button): |
518 |
self._load_homepage() |
519 |
|
520 |
def _key_press_cb(self, widget, event): |
521 |
key_name = gtk.gdk.keyval_name(event.keyval) |
522 |
browser = self._tabbed_view.props.current_browser |
523 |
|
524 |
if event.state & gtk.gdk.CONTROL_MASK: |
525 |
|
526 |
if key_name == 'd': |
527 |
self._add_link() |
528 |
elif key_name == 'f': |
529 |
_logger.debug('keyboard: Find') |
530 |
if NEW_TOOLBARS: |
531 |
self._edit_toolbar_button.set_expanded(True) |
532 |
else: |
533 |
self.toolbox.set_current_toolbar(_TOOLBAR_EDIT) |
534 |
self._edit_toolbar.search_entry.grab_focus() |
535 |
elif key_name == 'l': |
536 |
_logger.debug('keyboard: Focus url entry') |
537 |
if not NEW_TOOLBARS: |
538 |
self.toolbox.set_current_toolbar(_TOOLBAR_BROWSE) |
539 |
self._primary_toolbar.entry.grab_focus() |
540 |
elif key_name == 'minus': |
541 |
_logger.debug('keyboard: Zoom out') |
542 |
browser.zoom_out() |
543 |
elif key_name in ['plus', 'equal']: |
544 |
_logger.debug('keyboard: Zoom in') |
545 |
browser.zoom_in() |
546 |
elif key_name == 'Left': |
547 |
browser.web_navigation.goBack() |
548 |
elif key_name == 'Right': |
549 |
browser.web_navigation.goForward() |
550 |
elif key_name == 'r': |
551 |
flags = components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE |
552 |
browser.web_navigation.reload(flags) |
553 |
elif gtk.gdk.keyval_name(event.keyval) == "t": |
554 |
if not self._disable_multiple_tabs: |
555 |
self._load_homepage(new_tab=True) |
556 |
else: |
557 |
return False |
558 |
|
559 |
return True |
560 |
|
561 |
return False |
562 |
|
563 |
def _add_link(self): |
564 |
''' take screenshot and add link info to the model ''' |
565 |
|
566 |
browser = self._tabbed_view.props.current_browser |
567 |
ui_uri = browser.get_url_from_nsiuri(browser.progress.location) |
568 |
|
569 |
for link in self.model.data['shared_links']: |
570 |
if link['hash'] == sha1(ui_uri).hexdigest(): |
571 |
_logger.debug('_add_link: link exist already a=%s b=%s', |
572 |
link['hash'], sha1(ui_uri).hexdigest()) |
573 |
return |
574 |
buf = self._get_screenshot() |
575 |
timestamp = time.time() |
576 |
self.model.add_link(ui_uri, browser.props.title, buf, |
577 |
profile.get_nick_name(), |
578 |
profile.get_color().to_string(), timestamp) |
579 |
|
580 |
if self.messenger is not None: |
581 |
self.messenger._add_link(ui_uri, browser.props.title, |
582 |
profile.get_color().to_string(), |
583 |
profile.get_nick_name(), |
584 |
base64.b64encode(buf), timestamp) |
585 |
|
586 |
def _add_link_model_cb(self, model, index): |
587 |
''' receive index of new link from the model ''' |
588 |
link = self.model.data['shared_links'][index] |
589 |
self._add_link_totray(link['url'], base64.b64decode(link['thumb']), |
590 |
link['color'], link['title'], |
591 |
link['owner'], index, link['hash']) |
592 |
|
593 |
def _add_link_totray(self, url, buf, color, title, owner, index, hash): |
594 |
''' add a link to the tray ''' |
595 |
item = LinkButton(url, buf, color, title, owner, index, hash) |
596 |
item.connect('clicked', self._link_clicked_cb, url) |
597 |
item.connect('remove_link', self._link_removed_cb) |
598 |
# use index to add to the tray |
599 |
self._tray.add_item(item, index) |
600 |
item.show() |
601 |
if self._tray.props.visible is False: |
602 |
self._tray.show() |
603 |
self._view_toolbar.traybutton.props.sensitive = True |
604 |
|
605 |
def _link_removed_cb(self, button, hash): |
606 |
''' remove a link from tray and delete it in the model ''' |
607 |
self.model.remove_link(hash) |
608 |
self._tray.remove_item(button) |
609 |
if len(self._tray.get_children()) == 0: |
610 |
self._view_toolbar.traybutton.props.sensitive = False |
611 |
|
612 |
def _link_clicked_cb(self, button, url): |
613 |
''' an item of the link tray has been clicked ''' |
614 |
self._tabbed_view.props.current_browser.load_uri(url) |
615 |
|
616 |
def _pixbuf_save_cb(self, buf, data): |
617 |
data[0] += buf |
618 |
return True |
619 |
|
620 |
def get_buffer(self, pixbuf): |
621 |
data = [""] |
622 |
pixbuf.save_to_callback(self._pixbuf_save_cb, "png", {}, data) |
623 |
return str(data[0]) |
624 |
|
625 |
def _get_screenshot(self): |
626 |
window = self._tabbed_view.props.current_browser.window |
627 |
width, height = window.get_size() |
628 |
|
629 |
screenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, has_alpha=False, |
630 |
bits_per_sample=8, width=width, |
631 |
height=height) |
632 |
screenshot.get_from_drawable(window, window.get_colormap(), 0, 0, 0, 0, |
633 |
width, height) |
634 |
|
635 |
screenshot = screenshot.scale_simple(style.zoom(100), |
636 |
style.zoom(80), |
637 |
gtk.gdk.INTERP_BILINEAR) |
638 |
|
639 |
buf = self.get_buffer(screenshot) |
640 |
return buf |
641 |
|
642 |
def can_close(self): |
643 |
if self._force_close: |
644 |
return True |
645 |
elif downloadmanager.can_quit(): |
646 |
return True |
647 |
else: |
648 |
alert = Alert() |
649 |
alert.props.title = _('Download in progress') |
650 |
alert.props.msg = _('Stopping now will cancel your download') |
651 |
cancel_icon = Icon(icon_name='dialog-cancel') |
652 |
alert.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), cancel_icon) |
653 |
stop_icon = Icon(icon_name='dialog-ok') |
654 |
alert.add_button(gtk.RESPONSE_OK, _('Stop'), stop_icon) |
655 |
stop_icon.show() |
656 |
self.add_alert(alert) |
657 |
alert.connect('response', self.__inprogress_response_cb) |
658 |
alert.show() |
659 |
self.present() |
660 |
return False |
661 |
|
662 |
def __inprogress_response_cb(self, alert, response_id): |
663 |
self.remove_alert(alert) |
664 |
if response_id is gtk.RESPONSE_CANCEL: |
665 |
logging.debug('Keep on') |
666 |
elif response_id == gtk.RESPONSE_OK: |
667 |
logging.debug('Stop downloads and quit') |
668 |
self._force_close = True |
669 |
downloadmanager.remove_all_downloads() |
670 |
self.close() |
671 |
|
672 |
def get_document_path(self, async_cb, async_err_cb): |
673 |
browser = self._tabbed_view.props.current_browser |
674 |
browser.get_source(async_cb, async_err_cb) |
675 |
|
676 |
def get_canvas(self): |
677 |
return self._tabbed_view |