#Copyright (c) 2008, Media Modifications Ltd. #Copyright (c) 2013, Sugar Labs #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal #in the Software without restriction, including without limitation the rights #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: #The above copyright notice and this permission notice shall be included in #all copies or substantial portions of the Software. #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. import os import logging import shutil from gettext import gettext as _ from gettext import ngettext import gtk from gtk import gdk import gio import cairo import pango import pangocairo import pygst pygst.require('0.10') import gst from sugar.activity import activity from sugar.graphics.toolcombobox import ToolComboBox from sugar.graphics.toolbarbox import ToolbarBox from sugar.graphics.toolbarbox import ToolbarButton from sugar.graphics.toolbutton import ToolButton from sugar.graphics.radiotoolbutton import RadioToolButton from sugar.activity.widgets import StopButton from sugar.activity.widgets import ActivityToolbarButton from sugar.graphics.menuitem import MenuItem from sugar.graphics import style from model import Model from button import RecdButton import constants from instance import Instance import utils from tray import HTray from mediaview import MediaView import hw from iconcombobox import IconComboBox logger = logging.getLogger('record.py') COLOR_BLACK = gdk.color_parse('#000000') COLOR_WHITE = gdk.color_parse('#ffffff') TIMER_VALUES = [0, 5, 10] DURATION_VALUES = [2, 4, 6] QUALITY_VALUES = ['low', 'high'] gst.debug_set_active(True) gst.debug_set_colored(False) if logging.getLogger().level <= logging.DEBUG: gst.debug_set_default_threshold(gst.LEVEL_WARNING) else: gst.debug_set_default_threshold(gst.LEVEL_ERROR) class Record(activity.Activity): def __init__(self, handle): super(Record, self).__init__(handle) self.props.enable_fullscreen_mode = False self.Instance = Instance(self) self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) self.connect("visibility-notify-event", self._visibility_changed) #the main classes self.model = Model(self) self.ui_init() #CSCL self.connect("shared", self._shared_cb) if self.get_shared_activity(): #have you joined or shared this activity yourself? if self.get_shared(): self._joined_cb(self) else: self.connect("joined", self._joined_cb) # Realize the video view widget so that it knows its own window XID self._media_view.realize_video() # Changing to the first toolbar kicks off the rest of the setup if self.model.get_has_camera(): self.model.change_mode(constants.MODE_PHOTO) else: self.model.change_mode(constants.MODE_AUDIO) def read_file(self, path): self.model.read_file(path) def write_file(self, path): self.model.write_file(path) def close(self): self.model.gplay.stop() self.model.glive.stop() super(Record, self).close() def _visibility_changed(self, widget, event): self.model.set_visible(event.state != gtk.gdk.VISIBILITY_FULLY_OBSCURED) def _shared_cb(self, activity): self.model.collab.set_activity_shared() def _joined_cb(self, activity): self.model.collab.joined() def ui_init(self): self._fullscreen = False self._showing_info = False # FIXME: if _thumb_tray becomes some kind of button group, we wouldn't # have to track which recd is active self._active_recd = None self.connect_after('key-press-event', self._key_pressed) self._active_toolbar_idx = 0 self._toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) self._toolbar_box.toolbar.insert(activity_button, 0) self.set_toolbar_box(self._toolbar_box) self._toolbar = self.get_toolbar_box().toolbar tool_group = None if self.model.get_has_camera(): self._photo_button = RadioToolButton() self._photo_button.props.group = tool_group tool_group = self._photo_button self._photo_button.props.icon_name = 'camera-external' self._photo_button.props.label = _('Photo') self._photo_button.mode = constants.MODE_PHOTO self._photo_button.connect('clicked', self._mode_button_clicked) self._toolbar.insert(self._photo_button, -1) self._video_button = RadioToolButton() self._video_button.props.group = tool_group self._video_button.props.icon_name = 'media-video' self._video_button.props.label = _('Video') self._video_button.mode = constants.MODE_VIDEO self._video_button.connect('clicked', self._mode_button_clicked) self._toolbar.insert(self._video_button, -1) else: self._photo_button = None self._video_button = None self._audio_button = RadioToolButton() self._audio_button.props.group = tool_group self._audio_button.props.icon_name = 'media-audio' self._audio_button.props.label = _('Audio') self._audio_button.mode = constants.MODE_AUDIO self._audio_button.connect('clicked', self._mode_button_clicked) self._toolbar.insert(self._audio_button, -1) self._toolbar.insert(gtk.SeparatorToolItem(), -1) self._toolbar_controls = RecordControl(self._toolbar, self, 8 if self._video_button else 6) separator = gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) self._toolbar.insert(separator, -1) self._toolbar.insert(StopButton(self), -1) self.get_toolbar_box().show_all() main_box = gtk.VBox() self.set_canvas(main_box) main_box.get_parent().modify_bg(gtk.STATE_NORMAL, COLOR_BLACK) main_box.show() self._media_view = MediaView() self._media_view.connect('media-clicked', self._media_view_media_clicked) self._media_view.connect('pip-clicked', self._media_view_pip_clicked) self._media_view.connect('info-clicked', self._media_view_info_clicked) self._media_view.connect('full-clicked', self._media_view_full_clicked) self._media_view.connect('tags-changed', self._media_view_tags_changed) self._media_view.show() self._controls_hbox = gtk.HBox() self._controls_hbox.show() self._shutter_button = ShutterButton() self._shutter_button.connect("clicked", self._shutter_clicked) self._controls_hbox.pack_start(self._shutter_button, expand=True, fill=False) self._countdown_image = CountdownImage() self._controls_hbox.pack_start(self._countdown_image, expand=True, fill=False) self._play_button = PlayButton() self._play_button.connect('clicked', self._play_pause_clicked) self._controls_hbox.pack_start(self._play_button, expand=False) self._playback_scale = PlaybackScale(self.model) self._controls_hbox.pack_start(self._playback_scale, expand=True, fill=True) self._progress = ProgressInfo() self._controls_hbox.pack_start(self._progress, expand=True, fill=True) self._title_label = gtk.Label() self._title_label.set_markup(""+_('Title:')+'') self._controls_hbox.pack_start(self._title_label, expand=False) self._title_entry = gtk.Entry() self._title_entry.modify_bg(gtk.STATE_INSENSITIVE, COLOR_BLACK) self._title_entry.connect('changed', self._title_changed) self._controls_hbox.pack_start(self._title_entry, expand=True, fill=True, padding=10) self._record_container = RecordContainer(self._media_view, self._controls_hbox) main_box.pack_start(self._record_container, expand=True, fill=True, padding=6) self._record_container.show() self._thumb_tray = HTray() self._thumb_tray.set_size_request(-1, 150) main_box.pack_end(self._thumb_tray, expand=False) self._thumb_tray.show_all() def serialize(self): data = {} data['timer'] = self._toolbar_controls.get_timer_idx() data['duration'] = self._toolbar_controls.get_duration_idx() data['quality'] = self._toolbar_controls.get_quality() return data def deserialize(self, data): self._toolbar_controls.set_timer_idx(data.get('timer', 0)) self._toolbar_controls.set_duration_idx(data.get('duration', 0)) self._toolbar_controls.set_quality(data.get('quality', 0)) def _key_pressed(self, widget, event): key = event.keyval if key == gtk.keysyms.KP_Page_Up: # game key O if self._shutter_button.props.visible: if self._shutter_button.props.sensitive: self._shutter_button.clicked() else: # return to live mode self.model.set_state(constants.STATE_READY) if self.model.ui_frozen(): return False if key == gtk.keysyms.c and event.state == gdk.CONTROL_MASK: self._copy_to_clipboard(self._active_recd) elif key == gtk.keysyms.i: self._toggle_info() elif key == gtk.keysyms.Escape: if self._fullscreen: self._toggle_fullscreen() return False def _play_pause_clicked(self, widget): self.model.play_pause() def set_mode(self, mode): self._toolbar_controls.set_mode(mode) # can be called from gstreamer thread, so must not do any GTK+ stuff def set_glive_sink(self, sink): return self._media_view.set_video_sink(sink) # can be called from gstreamer thread, so must not do any GTK+ stuff def set_gplay_sink(self, sink): return self._media_view.set_video2_sink(sink) def get_selected_quality(self): return self._toolbar_controls.get_quality() def get_selected_timer(self): return self._toolbar_controls.get_timer() def get_selected_duration(self): return self._toolbar_controls.get_duration() * 60 # convert to secs def set_progress(self, value, text): self._progress.set_progress(value) self._progress.set_text(text) def set_countdown(self, value): if value == 0: self._shutter_button.show() self._countdown_image.hide() self._countdown_image.clear() return self._shutter_button.hide() self._countdown_image.show() self._countdown_image.set_value(value) def _title_changed(self, widget): self._active_recd.setTitle(self._title_entry.get_text()) def _media_view_media_clicked(self, widget): if self._play_button.props.visible and self._play_button.props.sensitive: self._play_button.clicked() def _media_view_pip_clicked(self, widget): # clicking on the PIP always returns to live mode self.model.set_state(constants.STATE_READY) def _media_view_info_clicked(self, widget): self._toggle_info() def _toggle_info(self): recd = self._active_recd if not recd: return if self._showing_info: self._show_recd(recd, play=False) return self._showing_info = True if self.model.get_mode() in (constants.MODE_PHOTO, constants.MODE_AUDIO): func = self._media_view.show_info_photo else: func = self._media_view.show_info_video self._play_button.hide() self._progress.hide() self._playback_scale.hide() self._title_entry.set_text(recd.title) self._title_entry.show() self._title_label.show() self._record_container.set_title_visible(True) func(recd.recorderName, recd.colorStroke, recd.colorFill, utils.getDateString(recd.time), recd.tags) def _media_view_full_clicked(self, widget): self._toggle_fullscreen() def _media_view_tags_changed(self, widget, tbuffer): text = tbuffer.get_text(tbuffer.get_start_iter(), tbuffer.get_end_iter()) self._active_recd.setTags(text) def _toggle_fullscreen(self): if not self._fullscreen: self._toolbar_box.hide() self._thumb_tray.hide() else: self._toolbar_box.show() self._thumb_tray.show() self._fullscreen = not self._fullscreen self._media_view.set_fullscreen(self._fullscreen) def _mode_button_clicked(self, button): self.model.change_mode(button.mode) def _shutter_clicked(self, arg): self.model.do_shutter() def set_shutter_sensitive(self, value): self._shutter_button.set_sensitive(value) def set_state(self, state): radio_state = (state == constants.STATE_READY) for item in (self._photo_button, self._audio_button, self._video_button): if item: item.set_sensitive(radio_state) self._showing_info = False if state == constants.STATE_READY: self._set_cursor_default() self._active_recd = None self._title_entry.hide() self._title_label.hide() self._record_container.set_title_visible(False) self._play_button.hide() self._playback_scale.hide() self._progress.hide() self._controls_hbox.set_child_packing(self._shutter_button, expand=True, fill=False, padding=0, pack_type=gtk.PACK_START) self._shutter_button.set_normal() self._shutter_button.set_sensitive(True) self._shutter_button.show() self._media_view.show_live() elif state == constants.STATE_RECORDING: self._shutter_button.set_recording() self._controls_hbox.set_child_packing(self._shutter_button, expand=False, fill=False, padding=0, pack_type=gtk.PACK_START) self._progress.show() elif state == constants.STATE_PROCESSING: self._set_cursor_busy() self._shutter_button.hide() self._progress.show() elif state == constants.STATE_DOWNLOADING: self._shutter_button.hide() self._progress.show() def set_paused(self, value): if value: self._play_button.set_play() else: self._play_button.set_pause() def _thumbnail_clicked(self, button, recd): if self.model.ui_frozen(): return self._active_recd = recd self._show_recd(recd) def add_thumbnail(self, recd, scroll_to_end): button = RecdButton(recd) clicked_handler = button.connect("clicked", self._thumbnail_clicked, recd) remove_handler = button.connect("remove-requested", self._remove_recd) clipboard_handler = button.connect("copy-clipboard-requested", self._thumbnail_copy_clipboard) button.set_data('handler-ids', (clicked_handler, remove_handler, clipboard_handler)) self._thumb_tray.add_item(button) button.show() if scroll_to_end: self._thumb_tray.scroll_to_end() def _copy_to_clipboard(self, recd): if recd == None: return if not recd.isClipboardCopyable(): return media_path = recd.getMediaFilepath() tmp_path = utils.getUniqueFilepath(media_path, 0) shutil.copyfile(media_path, tmp_path) gtk.Clipboard().set_with_data([('text/uri-list', 0, 0)], self._clipboard_get, self._clipboard_clear, tmp_path) def _clipboard_get(self, clipboard, selection_data, info, path): selection_data.set("text/uri-list", 8, "file://" + path) def _clipboard_clear(self, clipboard, path): if os.path.exists(path): os.unlink(path) def _thumbnail_copy_clipboard(self, recdbutton): self._copy_to_clipboard(recdbutton.get_recd()) def _remove_recd(self, recdbutton): recd = recdbutton.get_recd() self.model.delete_recd(recd) if self._active_recd == recd: self.model.set_state(constants.STATE_READY) self._remove_thumbnail(recdbutton) def _remove_thumbnail(self, recdbutton): handlers = recdbutton.get_data('handler-ids') for handler in handlers: recdbutton.disconnect(handler) self._thumb_tray.remove_item(recdbutton) recdbutton.cleanup() def remove_all_thumbnails(self): for child in self._thumb_tray.get_children(): self._remove_thumbnail(child) def show_still(self, pixbuf): self._media_view.show_still(pixbuf) def _show_photo(self, recd): path = self._get_photo_path(recd) self._media_view.show_photo(path) self._title_entry.set_text(recd.title) self._title_entry.show() self._title_label.show() self._record_container.set_title_visible(True) self._shutter_button.hide() self._progress.hide() def _show_audio(self, recd, play): self._progress.hide() self._shutter_button.hide() self._title_entry.hide() self._title_label.hide() self._record_container.set_title_visible(False) self._play_button.show() self._playback_scale.show() path = recd.getAudioImageFilepath() self._media_view.show_photo(path) if play: self.model.play_audio(recd) def _show_video(self, recd, play): self._progress.hide() self._shutter_button.hide() self._title_entry.hide() self._title_label.hide() self._record_container.set_title_visible(False) self._play_button.show() self._playback_scale.show() self._media_view.show_video() if play: self.model.play_video(recd) def set_playback_scale(self, value): self._playback_scale.set_value(value) def _get_photo_path(self, recd): # FIXME should live (partially) in recd? #downloading = self.ca.requestMeshDownload(recd) #self.MESHING = downloading if True: #not downloading: #self.progressWindow.updateProgress(0, "") return recd.getMediaFilepath() #maybe it is not downloaded from the mesh yet... #but we can show the low res thumb in the interim return recd.getThumbFilepath() def _show_recd(self, recd, play=True): self._showing_info = False if recd.buddy and not recd.downloadedFromBuddy: self.model.request_download(recd) elif recd.type == constants.TYPE_PHOTO: self._show_photo(recd) elif recd.type == constants.TYPE_AUDIO: self._show_audio(recd, play) elif recd.type == constants.TYPE_VIDEO: self._show_video(recd, play) def remote_recd_available(self, recd): if recd == self._active_recd: self._show_recd(recd) def update_download_progress(self, recd): if recd != self._active_recd: return if not recd.meshDownloading: msg = _('Download failed.') elif recd.meshDownloadingProgress: msg = _('Downloading...') else: msg = _('Requesting...') self.set_progress(recd.meshDownlodingPercent, msg) def _set_cursor_busy(self): self.window.set_cursor(gdk.Cursor(gdk.WATCH)) def _set_cursor_default(self): self.window.set_cursor(None) class RecordContainer(gtk.Container): """ A custom Container that contains a media view area, and a controls hbox. The controls hbox is given the first height that it requests, locked in for the duration of the widget. The media view is given the remainder of the space, but is constrained to a strict 4:3 ratio, therefore deducing its width. The controls hbox is given the same width, and both elements are centered horizontall.y """ __gtype_name__ = 'RecordContainer' def __init__(self, media_view, controls_hbox): self._media_view = media_view self._controls_hbox = controls_hbox self._show_title = False self._controls_hbox_height = 0 super(RecordContainer, self).__init__() for widget in (self._media_view, self._controls_hbox): if widget.flags() & gtk.REALIZED: widget.set_parent_window(self.window) widget.set_parent(self) def do_realize(self): self.set_flags(gtk.REALIZED) self.window = gdk.Window( self.get_parent_window(), window_type=gdk.WINDOW_CHILD, x=self.allocation.x, y=self.allocation.y, width=self.allocation.width, height=self.allocation.height, wclass=gdk.INPUT_OUTPUT, colormap=self.get_colormap(), event_mask=self.get_events() | gdk.VISIBILITY_NOTIFY_MASK | gdk.EXPOSURE_MASK) self.window.set_user_data(self) self.set_style(self.style.attach(self.window)) for widget in (self._media_view, self._controls_hbox): widget.set_parent_window(self.window) self.queue_resize() # GTK+ contains on exit if remove is not implemented def do_remove(self, widget): pass def do_size_request(self, req): # always request 320x240 (as a minimum for video) req.width = 320 req.height = 240 self._media_view.size_request() w, h = self._controls_hbox.size_request() # add on height requested by controls hbox if self._controls_hbox_height == 0: self._controls_hbox_height = h req.height += self._controls_hbox_height @staticmethod def _constrain_4_3(width, height): if (width % 4 == 0) and (height % 3 == 0) and ((width / 4) * 3) == height: return width, height # nothing to do ratio = 4.0 / 3.0 if ratio * height > width: width = (width / 4) * 4 height = int(width / ratio) else: height = (height / 3) * 3 width = int(ratio * height) return width, height @staticmethod def _center_in_plane(plane_size, size): return (plane_size - size) / 2 def do_size_allocate(self, allocation): self.allocation = allocation # give the controls hbox the height that it requested remaining_height = self.allocation.height - self._controls_hbox_height # give the mediaview the rest, constrained to 4/3 and centered media_view_width, media_view_height = self._constrain_4_3(self.allocation.width, remaining_height) media_view_x = self._center_in_plane(self.allocation.width, media_view_width) media_view_y = self._center_in_plane(remaining_height, media_view_height) if self._show_title: # position the controls hbox at the top of the window # and the same width as the media view controls_box_y = 0 # put the mediaview after the title media_view_y = media_view_y + self._controls_hbox_height else: # position hbox at the bottom of the window, # with the requested height, # and the same width as the media view controls_box_y = self.allocation.height - self._controls_hbox_height # send allocation to mediaview alloc = gdk.Rectangle() alloc.width = media_view_width alloc.height = media_view_height alloc.x = media_view_x alloc.y = media_view_y self._media_view.size_allocate(alloc) alloc = gdk.Rectangle() alloc.x = media_view_x alloc.y = controls_box_y alloc.width = media_view_width alloc.height = self._controls_hbox_height self._controls_hbox.size_allocate(alloc) if self.flags() & gtk.REALIZED: self.window.move_resize(*allocation) def do_forall(self, include_internals, callback, data): for widget in (self._media_view, self._controls_hbox): callback(widget, data) def set_title_visible(self, visible): self._show_title = visible self.queue_draw() class PlaybackScale(gtk.HScale): def __init__(self, model): self.model = model self._change_handler = None self._playback_adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0) super(PlaybackScale, self).__init__(self._playback_adjustment) self.set_draw_value(False) self.set_update_policy(gtk.UPDATE_CONTINUOUS) self.connect('button-press-event', self._button_press) self.connect('button-release-event', self._button_release) def set_value(self, value): self._playback_adjustment.set_value(value) def _value_changed(self, scale): self.model.do_seek(scale.get_value()) def _button_press(self, widget, event): self.model.start_seek() self._change_handler = self.connect('value-changed', self._value_changed) def _button_release(self, widget, event): self.disconnect(self._change_handler) self._change_handler = None self.model.end_seek() class ProgressInfo(gtk.VBox): def __init__(self): super(ProgressInfo, self).__init__() self._progress_bar = gtk.ProgressBar() self._progress_bar.modify_bg(gtk.STATE_NORMAL, COLOR_BLACK) self._progress_bar.modify_bg(gtk.STATE_INSENSITIVE, COLOR_BLACK) self.pack_start(self._progress_bar, expand=True, fill=True, padding=5) self._label = gtk.Label() self._label.modify_fg(gtk.STATE_NORMAL, COLOR_WHITE) self.pack_start(self._label, expand=True, fill=True) def show(self): self._progress_bar.show() self._label.show() super(ProgressInfo, self).show() def hide(self): self._progress_bar.hide() self._label.hide() super(ProgressInfo, self).hide() def set_progress(self, value): self._progress_bar.set_fraction(value) def set_text(self, text): self._label.set_text(text) class CountdownImage(gtk.Image): def __init__(self): super(CountdownImage, self).__init__() self._countdown_images = {} def _generate_image(self, num): w = 55 h = w pixmap = gdk.Pixmap(self.get_window(), w, h, -1) ctx = pixmap.cairo_create() ctx.rectangle(0, 0, w, h) ctx.set_source_rgb(0, 0, 0) ctx.fill() x = 0 y = 4 ctx.translate(x, y) circle_path = os.path.join(constants.GFX_PATH, 'media-circle.png') surface = cairo.ImageSurface.create_from_png(circle_path) ctx.set_source_surface(surface, 0, 0) ctx.paint() ctx.translate(-x, -y) ctx.set_source_rgb(255, 255, 255) pctx = pangocairo.CairoContext(ctx) play = pctx.create_layout() font = pango.FontDescription("sans 30") play.set_font_description(font) play.set_text(str(num)) dim = play.get_pixel_extents() ctx.translate(-dim[0][0], -dim[0][1]) xoff = (w - dim[0][2]) / 2 yoff = (h - dim[0][3]) / 2 ctx.translate(xoff, yoff) ctx.translate(-3, 0) pctx.show_layout(play) return pixmap def set_value(self, num): if num not in self._countdown_images: self._countdown_images[num] = self._generate_image(num) self.set_from_pixmap(self._countdown_images[num], None) class ShutterButton(gtk.Button): def __init__(self): gtk.Button.__init__(self) self.set_relief(gtk.RELIEF_NONE) self.set_focus_on_click(False) self.modify_bg(gtk.STATE_ACTIVE, COLOR_BLACK) path = os.path.join(constants.GFX_PATH, 'media-record.png') self._rec_image = gtk.image_new_from_file(path) path = os.path.join(constants.GFX_PATH, 'media-record-red.png') self._rec_red_image = gtk.image_new_from_file(path) path = os.path.join(constants.GFX_PATH, 'media-insensitive.png') self._insensitive_image = gtk.image_new_from_file(path) self.set_normal() def set_sensitive(self, sensitive): if sensitive: self.set_image(self._rec_image) else: self.set_image(self._insensitive_image) super(ShutterButton, self).set_sensitive(sensitive) def set_normal(self): self.set_image(self._rec_image) def set_recording(self): self.set_image(self._rec_red_image) class PlayButton(gtk.Button): def __init__(self): super(PlayButton, self).__init__() self.set_relief(gtk.RELIEF_NONE) self.set_focus_on_click(False) self.modify_bg(gtk.STATE_ACTIVE, COLOR_BLACK) path = os.path.join(constants.GFX_PATH, 'media-play.png') self._play_image = gtk.image_new_from_file(path) path = os.path.join(constants.GFX_PATH, 'media-pause.png') self._pause_image = gtk.image_new_from_file(path) self.set_play() def set_play(self): self.set_image(self._play_image) def set_pause(self): self.set_image(self._pause_image) class RecordControl(): def __init__(self, toolbar, activity, output_pos): self.activity = activity self.toolbar = toolbar self.output_pos = output_pos self._timer_value = TIMER_VALUES[0] self._timer_button = ToolButton('timer-0') self._timer_button.set_tooltip(_('Select timer')) self._timer_button.connect('clicked', self._timer_selection_cb) self.toolbar.insert(self._timer_button, -1) self._setup_timer_palette() self._duration_value = DURATION_VALUES[0] self._duration_button = ToolButton('duration-2') self._duration_button.set_tooltip(_('Select duration')) self._duration_button.connect('clicked', self._duration_selection_cb) self.toolbar.insert(self._duration_button, -1) self._setup_duration_palette() self._quality_value = 0 self._quality_button = ToolButton('low-quality') self._quality_button.set_tooltip(_('Select quality')) self._quality_button.connect('clicked', self._quality_selection_cb) self.toolbar.insert(self._quality_button, -1) self._setup_quality_palette() self._setup_output_palette(False) return def _output_selection_cb(self, widget): if self._output_palette: if not self._output_palette.is_up(): self._output_palette.popup(immediate=True, state=self._timer_palette.SECONDARY) else: self._output_palette.popdown(immediate=True) return def _setup_output_palette(self, notFirst): if notFirst: self.toolbar.remove(self._output_button) self._output_button = ToolButton() self._output_button.set_icon_name('user-documents') self._output_button.set_tooltip(_('Select output device')) self._output_button.connect('clicked', self._output_selection_cb) self._output_button.show() self.toolbar.insert(self._output_button, self.output_pos) self._output_palette = self._output_button.get_palette() volume_monitor = gio.volume_monitor_get() self._mount_added_hid = volume_monitor.connect('mount-added', self.__mount_change_cb) self._mount_removed_hid = volume_monitor.connect('mount-removed', self.__mount_change_cb) button = MenuItem(icon_name='activity-journal', text_label='Journal') self._output_palette.menu.append(button) button.connect('activate', self._output_selected_cb, None) button.show() self._output_palette.menu.append(button) for mount in volume_monitor.get_mounts(): self._add_button(mount) def __mount_change_cb(self, volume_monitor, mount): self._setup_output_palette(True) def _get_mount_icon_name(self, mount, size): icon = mount.get_icon() if isinstance(icon, gio.ThemedIcon): icon_theme = gtk.icon_theme_get_default() for icon_name in icon.props.names: if icon_theme.lookup_icon(icon_name, size, 0) is not None: return icon_name logging.error('Cannot find icon name for %s, %s', icon, mount) return 'drive' def _add_button(self, mount): logging.debug('VolumeToolbar._add_button: %r', mount.get_name()) button = MenuItem(icon_name= self._get_mount_icon_name(mount, gtk.ICON_SIZE_LARGE_TOOLBAR), text_label=mount.get_name()) self._output_palette.menu.append(button) button.connect('activate', self._output_selected_cb, mount) button.show() self._output_palette.menu.append(button) def _output_selected_cb(self, button, mount): if mount: self.activity.Instance.savePath = mount.get_root().get_path() self._output_button.set_icon_name(self._get_mount_icon_name(mount, gtk.ICON_SIZE_LARGE_TOOLBAR)) else: self.activity.Instance.savePath = None self._output_button.set_icon_name('activity-journal') def _timer_selection_cb(self, widget): if self._timer_palette: if not self._timer_palette.is_up(): self._timer_palette.popup(immediate=True, state=self._timer_palette.SECONDARY) else: self._timer_palette.popdown(immediate=True) return def _setup_timer_palette(self): self._timer_palette = self._timer_button.get_palette() for seconds in TIMER_VALUES: if seconds == 0: text = _('Immediate') else: text = ngettext('%s second', '%s seconds', seconds) % seconds menu_item = MenuItem(icon_name='timer-%d' % (seconds), text_label=text) menu_item.connect('activate', self._timer_selected_cb, seconds) self._timer_palette.menu.append(menu_item) menu_item.show() def _timer_selected_cb(self, button, seconds): self.set_timer_idx(TIMER_VALUES.index(seconds)) def _duration_selection_cb(self, widget): if self._duration_palette: if not self._duration_palette.is_up(): self._duration_palette.popup(immediate=True, state=self._duration_palette.SECONDARY) else: self._duration_palette.popdown(immediate=True) return def _setup_duration_palette(self): self._duration_palette = self._duration_button.get_palette() for minutes in DURATION_VALUES: if minutes == 0: text = gtk.Label(_('Immediate')) else: text = ngettext('%s minute', '%s minutes', minutes) % minutes menu_item = MenuItem(icon_name='duration-%d' % (minutes), text_label=text) menu_item.connect('activate', self._duration_selected_cb, minutes) self._duration_palette.menu.append(menu_item) menu_item.show() def _duration_selected_cb(self, button, minutes): self.set_duration_idx(DURATION_VALUES.index(minutes)) def _quality_selection_cb(self, widget): if self._quality_palette: if not self._quality_palette.is_up(): self._quality_palette.popup(immediate=True, state=self._quality_palette.SECONDARY) else: self._quality_palette.popdown(immediate=True) return def _setup_quality_palette(self): self._quality_palette = self._quality_button.get_palette() for quality in QUALITY_VALUES: text = _('%s quality') % (quality) menu_item = MenuItem(icon_name=quality + '-quality', text_label=text) menu_item.connect('activate', self._quality_selected_cb, quality) self._quality_palette.menu.append(menu_item) menu_item.show() def _quality_selected_cb(self, button, quality): self.set_quality(QUALITY_VALUES.index(quality)) def set_mode(self, mode): if mode == constants.MODE_PHOTO: self._quality_button.set_sensitive(False) self._timer_button.set_sensitive(True) self._duration_button.set_sensitive(False) if mode == constants.MODE_VIDEO: self._quality_button.set_sensitive(True) self._timer_button.set_sensitive(True) self._duration_button.set_sensitive(True) if mode == constants.MODE_AUDIO: self._quality_button.set_sensitive(False) self._timer_button.set_sensitive(True) self._duration_button.set_sensitive(True) def get_timer(self): return self._timer_value def get_timer_idx(self): if self._timer_value in TIMER_VALUES: return TIMER_VALUES.index(self._timer_value) else: return TIMER_VALUES[0] def set_timer_idx(self, idx): self._timer_value = TIMER_VALUES[idx] if hasattr(self, '_timer_button'): self._timer_button.set_icon('timer-%d' % (self._timer_value)) def get_duration(self): return self._duration_value def get_duration_idx(self): if self._duration_value in DURATION_VALUES: return DURATION_VALUES.index(self._duration_value) else: return DURATION_VALUES[0] def set_duration_idx(self, idx): self._duration_value = DURATION_VALUES[idx] if hasattr(self, '_duration_button'): self._duration_button.set_icon( 'duration-%d' % (self._duration_value)) def get_quality(self): return self._quality_value def set_quality(self, idx): self._quality_value = idx if hasattr(self, '_quality_button'): self._quality_button.set_icon('%s-quality' % (QUALITY_VALUES[idx]))