Commit from One Laptop Per Child: Translation System by user HoboPrimate. 31 of 31...
[journal-activity.git] / expandedentry.py
blobb84492938f6872ee0bfab0571b146183cc901016
1 # Copyright (C) 2007, One Laptop Per Child
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 import logging
18 from gettext import gettext as _
19 import StringIO
21 import hippo
22 import cairo
23 import gobject
24 import gtk
25 import json
27 from sugar.graphics import style
28 from sugar.graphics.icon import CanvasIcon
29 from sugar.graphics.xocolor import XoColor
30 from sugar.graphics.entry import CanvasEntry
31 from sugar.graphics.combobox import ComboBox
32 from sugar import activity
33 from sugar.datastore import datastore
35 from tagbox import TagBox
36 from keepicon import KeepIcon
37 from palettes import ObjectPalette, BuddyPalette
39 import misc
41 class Separator(hippo.CanvasBox, hippo.CanvasItem):
42 def __init__(self, orientation):
43 hippo.CanvasBox.__init__(self,
44 background_color=style.COLOR_PANEL_GREY.get_int())
46 if orientation == hippo.ORIENTATION_VERTICAL:
47 self.props.box_width = style.LINE_WIDTH
48 else:
49 self.props.box_height = style.LINE_WIDTH
51 class CanvasTextView(hippo.CanvasWidget):
52 def __init__(self, text, **kwargs):
53 hippo.CanvasWidget.__init__(self, **kwargs)
54 self.text_view_widget = gtk.TextView()
55 self.text_view_widget.props.buffer.props.text = text
56 self.text_view_widget.props.left_margin = style.DEFAULT_SPACING
57 self.text_view_widget.props.right_margin = style.DEFAULT_SPACING
58 self.text_view_widget.props.wrap_mode = gtk.WRAP_WORD
59 self.text_view_widget.show()
61 # TODO: These fields should expand vertically instead of scrolling
62 scrolled_window = gtk.ScrolledWindow()
63 scrolled_window.set_shadow_type(gtk.SHADOW_OUT)
64 scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
65 scrolled_window.add(self.text_view_widget)
67 self.props.widget = scrolled_window
69 class BuddyList(hippo.CanvasBox):
70 def __init__(self, model):
71 hippo.CanvasBox.__init__(self, xalign=hippo.ALIGNMENT_START,
72 orientation=hippo.ORIENTATION_HORIZONTAL)
74 for buddy in model:
75 nick, color = buddy
76 hbox = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL)
77 icon = CanvasIcon(icon_name='computer-xo',
78 xo_color=XoColor(color),
79 size=style.STANDARD_ICON_SIZE)
80 icon.set_palette(BuddyPalette(buddy))
81 hbox.append(icon)
82 self.append(hbox)
84 class ExpandedEntry(hippo.CanvasBox):
85 def __init__(self, object_id):
86 hippo.CanvasBox.__init__(self)
87 self.props.orientation = hippo.ORIENTATION_VERTICAL
88 self.props.background_color = style.COLOR_WHITE.get_int()
89 self.props.padding_top = style.DEFAULT_SPACING * 3
91 self._jobject = datastore.get(object_id)
92 self._update_title_sid = None
94 # Create header
95 header = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL,
96 padding=style.DEFAULT_PADDING,
97 padding_right=style.GRID_CELL_SIZE,
98 spacing=style.DEFAULT_SPACING)
99 self.append(header)
101 # Create two column body
103 body = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL,
104 spacing=style.DEFAULT_SPACING * 3,
105 padding_left=style.GRID_CELL_SIZE,
106 padding_right=style.GRID_CELL_SIZE,
107 padding_top=style.DEFAULT_SPACING * 3)
109 self.append(body, hippo.PACK_EXPAND)
111 first_column = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL,
112 spacing=style.DEFAULT_SPACING)
113 body.append(first_column)
115 second_column = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL,
116 spacing=style.DEFAULT_SPACING)
117 body.append(second_column, hippo.PACK_EXPAND)
119 # Header
121 self._keep_icon = self._create_keep_icon()
122 header.append(self._keep_icon)
124 self._icon = self._create_icon()
125 header.append(self._icon)
127 self._title = self._create_title()
128 header.append(self._title, hippo.PACK_EXPAND)
130 # TODO: create a version list popup instead of a date label
131 self._date = self._create_date()
132 header.append(self._date)
134 if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
135 header.reverse()
137 # First column
139 self._preview = self._create_preview()
140 first_column.append(self._preview)
142 # Second column
144 description_box, self._description = self._create_description()
145 second_column.append(description_box)
147 tags_box, self._tags = self._create_tags()
148 second_column.append(tags_box)
150 self._buddy_list = self._create_buddy_list()
151 second_column.append(self._buddy_list)
153 def _create_keep_icon(self):
154 keep = self._jobject.metadata.has_key('keep') and \
155 self._jobject.metadata['keep'] == 1
156 keep_icon = KeepIcon(keep)
157 keep_icon.connect('activated', self._keep_icon_activated_cb)
158 return keep_icon
160 def _create_icon(self):
161 icon = CanvasIcon(file_name=misc.get_icon_name(self._jobject))
162 icon.connect_after('button-release-event', self._icon_button_release_event_cb)
164 if self._jobject.is_activity_bundle():
165 icon.props.fill_color=style.COLOR_TRANSPARENT.get_svg()
166 icon.props.stroke_color=style.COLOR_BUTTON_GREY.get_svg()
167 else:
168 if self._jobject.metadata.has_key('icon-color') and \
169 self._jobject.metadata['icon-color']:
170 icon.props.xo_color = XoColor( \
171 self._jobject.metadata['icon-color'])
173 icon.set_palette(ObjectPalette(self._jobject))
175 return icon
177 def _create_title(self):
178 title = CanvasEntry()
179 title.set_background(style.COLOR_WHITE.get_html())
180 title.props.text = self._jobject.metadata.get('title', _('Untitled'))
181 title.props.widget.connect('focus-out-event',
182 self._title_focus_out_event_cb)
183 return title
185 def _create_date(self):
186 date = hippo.CanvasText(xalign=hippo.ALIGNMENT_START,
187 font_desc=style.FONT_NORMAL.get_pango_desc(),
188 text = misc.get_date(self._jobject))
189 return date
191 def _create_preview(self):
192 width = style.zoom(320)
193 height = style.zoom(240)
194 box = hippo.CanvasBox()
196 if self._jobject.metadata.has_key('preview') and \
197 len(self._jobject.metadata['preview']) > 4:
199 if self._jobject.metadata['preview'][1:4] == 'PNG':
200 preview_data = self._jobject.metadata['preview']
201 else:
202 import base64
203 preview_data = base64.b64decode(self._jobject.metadata['preview'])
206 png_file = StringIO.StringIO(preview_data)
207 try:
208 surface = cairo.ImageSurface.create_from_png(png_file)
209 has_preview = True
210 except Exception, e:
211 logging.error('Error while loading the preview: %r' % e)
212 has_preview = False
213 else:
214 has_preview = False
216 if has_preview:
217 preview_box = hippo.CanvasImage(image=surface,
218 border=style.LINE_WIDTH,
219 border_color=style.COLOR_BUTTON_GREY.get_int(),
220 xalign=hippo.ALIGNMENT_CENTER,
221 yalign=hippo.ALIGNMENT_CENTER,
222 scale_width=width,
223 scale_height=height)
224 else:
225 preview_box = hippo.CanvasText(text=_('No preview'),
226 font_desc=style.FONT_NORMAL.get_pango_desc(),
227 xalign=hippo.ALIGNMENT_CENTER,
228 yalign=hippo.ALIGNMENT_CENTER,
229 border=style.LINE_WIDTH,
230 border_color=style.COLOR_BUTTON_GREY.get_int(),
231 color=style.COLOR_BUTTON_GREY.get_int(),
232 box_width=width,
233 box_height=height)
234 preview_box.connect_after('button-release-event',
235 self._preview_box_button_release_event_cb)
236 box.append(preview_box)
237 return box
239 def _create_buddy_list(self):
241 vbox = hippo.CanvasBox()
242 vbox.props.spacing = style.DEFAULT_SPACING
244 text = hippo.CanvasText(text=_('Participants:'),
245 font_desc=style.FONT_NORMAL.get_pango_desc())
246 text.props.color=style.COLOR_BUTTON_GREY.get_int()
248 if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
249 text.props.xalign = hippo.ALIGNMENT_END
250 else:
251 text.props.xalign = hippo.ALIGNMENT_START
253 vbox.append(text)
255 if self._jobject.metadata.has_key('buddies') and \
256 self._jobject.metadata['buddies']:
257 # json cannot read unicode strings
258 buddies_str = self._jobject.metadata['buddies'].encode('utf8')
259 buddies = json.read(buddies_str).values()
260 vbox.append(BuddyList(buddies))
261 return vbox
262 else:
263 return vbox
265 def _create_description(self):
266 vbox = hippo.CanvasBox()
267 vbox.props.spacing = style.DEFAULT_SPACING
269 text = hippo.CanvasText(text=_('Description:'),
270 font_desc=style.FONT_NORMAL.get_pango_desc())
271 text.props.color=style.COLOR_BUTTON_GREY.get_int()
273 if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
274 text.props.xalign = hippo.ALIGNMENT_END
275 else:
276 text.props.xalign = hippo.ALIGNMENT_START
278 vbox.append(text)
280 description = self._jobject.metadata.get('description', '')
281 text_view = CanvasTextView(description, box_height=style.GRID_CELL_SIZE * 2)
282 vbox.append(text_view, hippo.PACK_EXPAND)
284 text_view.text_view_widget.props.accepts_tab = False
285 text_view.text_view_widget.connect('focus-out-event',
286 self._description_focus_out_event_cb)
288 return vbox, text_view
290 def _create_tags(self):
291 vbox = hippo.CanvasBox()
292 vbox.props.spacing = style.DEFAULT_SPACING
294 text = hippo.CanvasText(text=_('Tags:'),
295 font_desc=style.FONT_NORMAL.get_pango_desc())
296 text.props.color=style.COLOR_BUTTON_GREY.get_int()
298 if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
299 text.props.xalign = hippo.ALIGNMENT_END
300 else:
301 text.props.xalign = hippo.ALIGNMENT_START
303 vbox.append(text)
305 tags = self._jobject.metadata.get('tags', '')
306 text_view = CanvasTextView(tags, box_height=style.GRID_CELL_SIZE * 2)
307 vbox.append(text_view, hippo.PACK_EXPAND)
309 text_view.text_view_widget.props.accepts_tab = False
310 text_view.text_view_widget.connect('focus-out-event',
311 self._tags_focus_out_event_cb)
313 return vbox, text_view
315 def _create_version_list(self):
316 vbox = hippo.CanvasBox()
317 vbox.props.spacing = style.DEFAULT_SPACING
318 # TODO: Enable again when we have versions in the DS
320 jobjects, count = datastore.find({'uid': self._jobject.object_id},
321 sorting=['-mtime'])
322 for jobject in jobjects:
323 hbox = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL)
324 hbox.props.spacing = style.DEFAULT_SPACING
326 icon = CanvasIcon(file_name=misc.get_icon_name(jobject),
327 size=style.SMALL_ICON_SIZE)
328 if jobject.metadata.has_key('icon-color') and \
329 jobject.metadata['icon-color']:
330 icon.props.xo_color = XoColor(jobject.metadata['icon-color'])
331 hbox.append(icon)
333 date = hippo.CanvasText(text=misc.get_date(jobject),
334 xalign=hippo.ALIGNMENT_START,
335 font_desc=style.FONT_NORMAL.get_pango_desc())
336 hbox.append(date)
338 vbox.append(hbox)
340 return vbox
342 def _title_notify_text_cb(self, entry, pspec):
343 if not self._update_title_sid:
344 self._update_title_sid = gobject.timeout_add(1000, self._update_title_cb)
346 def _datastore_write_cb(self):
347 pass
349 def _datastore_write_error_cb(self, error):
350 logging.error('ExpandedEntry._datastore_write_error_cb: %r' % error)
352 def _title_focus_out_event_cb(self, entry, event):
353 self._update_entry()
355 def _description_focus_out_event_cb(self, text_view, event):
356 self._update_entry()
358 def _tags_focus_out_event_cb(self, text_view, event):
359 self._update_entry()
361 def _update_entry(self):
362 needs_update = False
364 old_title = self._jobject.metadata.get('title', None)
365 if old_title != self._title.props.text:
366 self._icon.palette.props.primary_text = self._title.props.text
367 self._jobject.metadata['title'] = self._title.props.text
368 self._jobject.metadata['title_set_by_user'] = '1'
369 needs_update = True
371 old_tags = self._jobject.metadata.get('tags', None)
372 new_tags = self._tags.text_view_widget.props.buffer.props.text
373 if old_tags != new_tags:
374 self._jobject.metadata['tags'] = new_tags
375 needs_update = True
377 old_description = self._jobject.metadata.get('description', None)
378 new_description = self._description.text_view_widget.props.buffer.props.text
379 if old_description != new_description:
380 self._jobject.metadata['description'] = new_description
381 needs_update = True
383 if needs_update:
384 datastore.write(self._jobject, update_mtime=False,
385 reply_handler=self._datastore_write_cb,
386 error_handler=self._datastore_write_error_cb)
388 self._update_title_sid = None
390 def get_keep(self):
391 return self._jobject.metadata.has_key('keep') and \
392 self._jobject.metadata['keep'] == 1
394 def _keep_icon_activated_cb(self, keep_icon):
395 if self.get_keep():
396 self._jobject.metadata['keep'] = 0
397 else:
398 self._jobject.metadata['keep'] = 1
399 datastore.write(self._jobject, update_mtime=False)
401 keep_icon.props.keep = self.get_keep()
403 def _icon_button_release_event_cb(self, button, event):
404 logging.debug('_icon_button_release_event_cb')
405 self._jobject.resume()
406 return True
408 def _preview_box_button_release_event_cb(self, button, event):
409 logging.debug('_preview_box_button_release_event_cb')
410 self._jobject.resume()
411 return True