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
18 from gettext
import gettext
as _
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
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
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
)
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
))
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
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
)
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
)
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
:
139 self
._preview
= self
._create
_preview
()
140 first_column
.append(self
._preview
)
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
)
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()
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
))
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
)
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
))
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']
203 preview_data
= base64
.b64decode(self
._jobject
.metadata
['preview'])
206 png_file
= StringIO
.StringIO(preview_data
)
208 surface
= cairo
.ImageSurface
.create_from_png(png_file
)
211 logging
.error('Error while loading the preview: %r' % e
)
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
,
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(),
234 preview_box
.connect_after('button-release-event',
235 self
._preview
_box
_button
_release
_event
_cb
)
236 box
.append(preview_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
251 text
.props
.xalign
= hippo
.ALIGNMENT_START
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
))
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
276 text
.props
.xalign
= hippo
.ALIGNMENT_START
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
301 text
.props
.xalign
= hippo
.ALIGNMENT_START
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},
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'])
333 date = hippo.CanvasText(text=misc.get_date(jobject),
334 xalign=hippo.ALIGNMENT_START,
335 font_desc=style.FONT_NORMAL.get_pango_desc())
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
):
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
):
355 def _description_focus_out_event_cb(self
, text_view
, event
):
358 def _tags_focus_out_event_cb(self
, text_view
, event
):
361 def _update_entry(self
):
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'
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
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
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
391 return self
._jobject
.metadata
.has_key('keep') and \
392 self
._jobject
.metadata
['keep'] == 1
394 def _keep_icon_activated_cb(self
, keep_icon
):
396 self
._jobject
.metadata
['keep'] = 0
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()
408 def _preview_box_button_release_event_cb(self
, button
, event
):
409 logging
.debug('_preview_box_button_release_event_cb')
410 self
._jobject
.resume()