Exemple #1
0
class ToolbarInfo(Gtk.Toolbar):

    __gsignals__ = {
        "show-simulation": (GObject.SIGNAL_RUN_FIRST, None, []),
        "show-info": (GObject.SIGNAL_RUN_FIRST, None, []),
        "go-back": (GObject.SIGNAL_RUN_FIRST, None, []),
        "go-forward": (GObject.SIGNAL_RUN_FIRST, None, []),
    }

    def __init__(self):
        Gtk.Toolbar.__init__(self)

        self.button = ToolbarButton(page=self, icon_name="info")

        self.button_simulation = RadioToolButton(icon_name="solar-system",
                                                 group=None)
        self.button_simulation.set_tooltip(_("Show solar system"))
        self.button_simulation.connect("toggled", self._show_simulation_cb)
        self.insert(self.button_simulation, -1)

        self.button_info = RadioToolButton(icon_name="info",
                                           group=self.button_simulation)
        self.button_info.set_tooltip(_("Show information"))
        self.button_info.connect("toggled", self._show_info_cb)
        self.insert(self.button_info, -1)

        self.insert(make_separator(False), -1)

        self.back_button = ToolButton("go-previous-paired")
        self.back_button.set_tooltip(_("Go back"))
        self.back_button.set_sensitive(False)
        self.back_button.connect("clicked", self._go_back_cb)
        self.insert(self.back_button, -1)

        self.forward_button = ToolButton("go-next-paired")
        self.forward_button.set_tooltip(_("Go forward"))
        self.forward_button.set_sensitive(False)
        self.forward_button.connect("clicked", self._go_forward_cb)
        self.insert(self.forward_button, -1)

        self.show_all()

    def _show_simulation_cb(self, widget):
        self.emit("show-simulation")

    def _show_info_cb(self, widget):
        self.emit("show-info")

    def _go_back_cb(self, widget):
        self.emit("go-back")

    def _go_forward_cb(self, widget):
        self.emit("go-forward")

    def select_screen(self, screen):
        if screen == Screen.SOLAR_SYSTEM:
            self.button_simulation.props = True

        elif screen == Screen.INFO:
            self.button_info.props.active = True
Exemple #2
0
class QrReader(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        self.realize()
        self.qr_window = Gtk.DrawingArea()
        self.qr_window.set_double_buffered(False)
        self.qr_window.set_app_paintable(True)

        self.image = Gtk.Image()

        self.box = Gtk.VBox()
        self.box.pack_start(self.qr_window, True, True, 0)
        self.box.pack_end(self.image, True, True, 0)

        self.set_canvas(self.box)

        self.build_toolbar()
        self.show_all()
        self.image.hide()
        GObject.idle_add(self.setup_init)

    def build_toolbar(self):
        toolbox = ToolbarBox()
        toolbar = toolbox.toolbar

        activity_button = ActivityButton(self)
        toolbar.insert(activity_button, -1)
        toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.stop_play = ToogleButton('media-playback-start')
        self.stop_play.set_tooltip(_("Turn on/off the camera"))
        self.stop_play.props.active = True

        self.copylink = ToolButton('text-uri-list')
        self.copylink.set_tooltip(_("Copy link to clipboard"))
        self.copylink.set_sensitive(False)

        toolbar.insert(self.stop_play, -1)
        toolbar.insert(self.copylink, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar.insert(separator, -1)

        stopbtn = StopButton(self)
        toolbar.insert(stopbtn, -1)
        toolbar.show_all()

        self.set_toolbar_box(toolbox)

    def setup_init(self):
        xid = self.qr_window.get_property('window').get_xid()
        visor = QrVisor(xid, self.stop_play, self, self.qr_window,
                self.copylink, self.image)
        visor.play()
Exemple #3
0
class QrReader(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        self.realize()
        self.qr_window = Gtk.DrawingArea()
        self.qr_window.set_double_buffered(False)
        self.qr_window.set_app_paintable(True)

        self.image = Gtk.Image()

        self.box = Gtk.VBox()
        self.box.pack_start(self.qr_window, True, True, 0)
        self.box.pack_end(self.image, True, True, 0)

        self.set_canvas(self.box)

        self.build_toolbar()
        self.show_all()
        self.image.hide()
        GObject.idle_add(self.setup_init)

    def build_toolbar(self):
        toolbox = ToolbarBox()
        toolbar = toolbox.toolbar

        activity_button = ActivityButton(self)
        toolbar.insert(activity_button, -1)
        toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.stop_play = ToogleButton('media-playback-start')
        self.stop_play.set_tooltip(_("Turn on/off the camera"))
        self.stop_play.props.active = True

        self.copylink = ToolButton('text-uri-list')
        self.copylink.set_tooltip(_("Copy link to clipboard"))
        self.copylink.set_sensitive(False)

        toolbar.insert(self.stop_play, -1)
        toolbar.insert(self.copylink, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar.insert(separator, -1)

        stopbtn = StopButton(self)
        toolbar.insert(stopbtn, -1)
        toolbar.show_all()

        self.set_toolbar_box(toolbox)

    def setup_init(self):
        xid = self.qr_window.get_property('window').get_xid()
        visor = QrVisor(xid, self.stop_play, self, self.qr_window,
                        self.copylink, self.image)
        visor.play()
Exemple #4
0
class EditToolbar(SugarEditToolbar):
    def __init__(self, _parent):
        SugarEditToolbar.__init__(self)

        self._parent = _parent

        self.undo.connect('clicked', self.__undo_cb)
        self.redo.connect('clicked', self.__redo_cb)
        self.copy.connect('clicked', self.__copy_cb)
        self.paste.connect('clicked', self.__paste_cb)

        menu_item = MenuItem(_('Cut'))
        menu_item.connect('activate', self.__cut_cb)
        menu_item.show()
        self.copy.get_palette().menu.append(menu_item)

        self.insert(Gtk.SeparatorToolItem(), -1)

        self.erase_button = ToolButton('edit-delete')
        self.erase_button.set_tooltip(_('Erase selected thought(s)'))
        self.erase_button.connect('clicked', self.__delete_cb)
        self.insert(self.erase_button, -1)

        self.show_all()
        self.clipboard = Gtk.Clipboard()

        self.copy.set_sensitive(False)
        self.paste.set_sensitive(False)
        self.erase_button.set_sensitive(False)

    def __undo_cb(self, button):
        self._parent._undo.undo_action(None)

    def __redo_cb(self, button):
        self._parent._undo.redo_action(None)

    def __cut_cb(self, event):
        self._parent._main_area.cut_clipboard(self.clipboard)

    def __copy_cb(self, event):
        self._parent._main_area.copy_clipboard(self.clipboard)

    def __paste_cb(self, event):
        self._parent._main_area.paste_clipboard(self.clipboard)

    def __delete_cb(self, widget):
        self._stop_moving()
        self.stop_dragging()
        self._parent._main_area.delete_selected_elements()

    def stop_dragging(self):
        if self._parent._main_area.is_dragging():
            self._parent._main_area.drag_menu_cb(self._sw, False)

    def _stop_moving(self):
        self._parent._main_area.move_mode = False
class EditToolbar(SugarEditToolbar):
    def __init__(self, _parent):
        SugarEditToolbar.__init__(self)

        self._parent = _parent

        self.undo.connect('clicked', self.__undo_cb)
        self.redo.connect('clicked', self.__redo_cb)
        self.copy.connect('clicked', self.__copy_cb)
        self.paste.connect('clicked', self.__paste_cb)

        menu_item = MenuItem(_('Cut'))
        menu_item.connect('activate', self.__cut_cb)
        menu_item.show()
        self.copy.get_palette().menu.append(menu_item)

        self.insert(Gtk.SeparatorToolItem(), -1)

        self.erase_button = ToolButton('edit-delete')
        self.erase_button.set_tooltip(_('Erase selected thought(s)'))
        self.erase_button.connect('clicked', self.__delete_cb)
        self.insert(self.erase_button, -1)

        self.show_all()
        self.clipboard = Gtk.Clipboard()

        self.copy.set_sensitive(False)
        self.paste.set_sensitive(False)
        self.erase_button.set_sensitive(False)

    def __undo_cb(self, button):
        self._parent._undo.undo_action(None)

    def __redo_cb(self, button):
        self._parent._undo.redo_action(None)

    def __cut_cb(self, event):
        self._parent._main_area.cut_clipboard(self.clipboard)

    def __copy_cb(self, event):
        self._parent._main_area.copy_clipboard(self.clipboard)

    def __paste_cb(self, event):
        self._parent._main_area.paste_clipboard(self.clipboard)

    def __delete_cb(self, widget):
        self._stop_moving()
        self.stop_dragging()
        self._parent._main_area.delete_selected_elements()

    def stop_dragging(self):
        if self._parent._main_area.is_dragging():
            self._parent._main_area.drag_menu_cb(self._sw, False)

    def _stop_moving(self):
        self._parent._main_area.move_mode = False
Exemple #6
0
    def __setup_buttons_cb(self):
        save_button = ToolButton(icon_name='document-save')
        save_button.props.tooltip = _("Save")
        save_button.connect('clicked', self.__save_clicked_cb)
        save_button.show()
        save_button.set_sensitive(True)
        self.page.insert(save_button, -1)

        open_button = ToolButton(icon_name='document-open')
        open_button.props.tooltip = _("Open")
        open_button.connect('clicked', self.__open_clicked_cb)
        open_button.show()
        open_button.set_sensitive(True)
        self.page.insert(open_button, -1)
Exemple #7
0
class Toolbar(Gtk.Toolbar):
    def __init__(self, web_view):
        Gtk.Toolbar.__init__(self)

        self._web_view = web_view

        self._back = ToolButton('go-previous-paired')
        self._back.set_tooltip(_('Back'))
        self._back.props.sensitive = False
        self._back.connect('clicked', self._go_back_cb)
        self.insert(self._back, -1)
        self._back.show()

        self._forward = ToolButton('go-next-paired')
        self._forward.set_tooltip(_('Forward'))
        self._forward.props.sensitive = False
        self._forward.connect('clicked', self._go_forward_cb)
        self.insert(self._forward, -1)
        self._forward.show()

        home = ToolButton('zoom-home')
        home.set_tooltip(_('Home'))
        home.connect('clicked', self._go_home_cb)
        self.insert(home, -1)
        home.show()

        #progress_listener = self._web_view.progress
        #progress_listener.connect('location-changed',
        #                          self._location_changed_cb)
        #progress_listener.connect('loading-stop', self._loading_stop_cb)

    def _location_changed_cb(self, progress_listener, uri):
        self.update_navigation_buttons()

    def _loading_stop_cb(self, progress_listener):
        self.update_navigation_buttons()

    def update_navigation_buttons(self):
        self._back.set_sensitive(self._web_view.can_go_back())
        self._forward.set_sensitive(self._web_view.can_go_forward())

    def _go_back_cb(self, button):
        self._web_view.go_back()

    def _go_forward_cb(self, button):
        self._web_view.go_forward()()

    def _go_home_cb(self, button):
        self._web_view.load_uri(HOME)
Exemple #8
0
class ViewToolbar(Gtk.Toolbar):
    def __init__(self, activity):
        GObject.GObject.__init__(self)

        self._activity = activity

        self._browser = self._activity._web_view

        self.zoomout = ToolButton('zoom-out', accelerator='<ctrl>minus')
        self.zoomout.set_tooltip(_('Zoom out'))
        self.zoomout.connect('clicked', self.__zoomout_clicked_cb)
        self.insert(self.zoomout, -1)
        self.zoomout.show()

        self.zoomin = ToolButton('zoom-in', accelerator='<ctrl>plus')
        self.zoomin.set_tooltip(_('Zoom in'))
        self.zoomin.connect('clicked', self.__zoomin_clicked_cb)
        self.insert(self.zoomin, -1)
        self.zoomin.show()

        self.zoom_original = ToolButton('zoom-original', accelerator='<ctrl>0')
        self.zoom_original.set_tooltip(_('Actual size'))
        self.zoom_original.connect('clicked', self.__zoom_original_clicked_cb)
        self.insert(self.zoom_original, -1)
        self.zoom_original.show()

        self._zoom_update_sensitive()

        self.separator = Gtk.SeparatorToolItem()
        self.separator.set_draw(True)
        self.insert(self.separator, -1)
        self.separator.show()

        self.fullscreen = ToolButton('view-fullscreen')
        self.fullscreen.set_tooltip(_('Fullscreen'))
        self.fullscreen.connect('clicked', self.__fullscreen_clicked_cb)
        self.insert(self.fullscreen, -1)
        self.fullscreen.show()

    def _zoom_update_sensitive(self):
        self.zoomout.set_sensitive(self._activity.can_zoom_out())
        self.zoomin.set_sensitive(self._activity.can_zoom_in())
        self.zoom_original.set_sensitive(self._activity.can_zoom_original())

    def __zoomin_clicked_cb(self, button):
        if button.is_sensitive():
            self._activity.zoom_in()
        self._zoom_update_sensitive()

    def __zoomout_clicked_cb(self, button):
        if button.is_sensitive():
            self._activity.zoom_out()
        self._zoom_update_sensitive()

    def __zoom_original_clicked_cb(self, button):
        self._activity.zoom_original()
        self._zoom_update_sensitive()

    def __fullscreen_clicked_cb(self, button):
        self._activity.fullscreen()
Exemple #9
0
class FileChooserSave(Gtk.Window):

    __gsignals__ = {'save-file': (GObject.SIGNAL_RUN_FIRST, None, [str])}

    def __init__(self, folder=None):
        Gtk.Window.__init__(self)

        self.vbox = Gtk.VBox()
        self.view = Gtk.IconView()
        self.model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)

        self.folder = folder or os.path.expanduser('~/')
        self.files = []
        self.show_hidden_files = False

        self.set_modal(True)
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_size_request(WIDTH, HEIGHT)
        self.set_border_width(style.LINE_WIDTH)
        self.add_events(Gdk.EventMask.KEY_RELEASE_MASK)
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.modify_bg(Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color())
        self.view.set_model(self.model)
        self.view.set_selection_mode(Gtk.SelectionMode.SINGLE)
        self.view.set_text_column(0)
        self.view.set_pixbuf_column(1)

        self.view.connect('selection-changed', self.__selection_changed)
        self.view.connect('button-press-event', self.__button_press_event_cb)

        scrolled = Gtk.ScrolledWindow()
        scrolled.add(self.view)

        self.__make_toolbar()
        self.show_folder()

        self.vbox.pack_end(scrolled, True, True, 0)
        self.add(self.vbox)
        self.show_all()

        GObject.timeout_add(500, self.check_files)

    def __make_toolbar(self):
        self.toolbar = Gtk.Toolbar()
        self.toolbar.modify_bg(Gtk.StateType.NORMAL,
                               style.COLOR_TOOLBAR_GREY.get_gdk_color())

        self.go_up_button = ToolButton(icon_name='go-up')
        self.go_up_button.connect('clicked', self.go_up)
        self.toolbar.insert(self.go_up_button, -1)

        self.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.button_new_folder = ToolButton(icon_name='new-folder')
        self.button_new_folder.set_tooltip(_('Create a folder'))
        self.button_new_folder.connect('clicked', self.create_folder)
        self.toolbar.insert(self.button_new_folder, -1)

        item = Gtk.ToolItem()
        self.entry = Gtk.Entry()
        self.entry.set_size_request(300, -1)
        self.entry.set_text(self.folder)
        self.entry.connect('activate', self.__save_path_from_entry)
        item.add(self.entry)
        self.toolbar.insert(item, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.toolbar.insert(separator, -1)

        item = Gtk.ToolItem()
        self.entry_name = Gtk.Entry()
        self.entry_name.set_size_request(200, -1)
        self.entry_name.set_placeholder_text('Select a name for this file.')
        self.entry_name.connect('activate', self.__name_selected)
        item.add(self.entry_name)
        self.toolbar.insert(item, -1)

        self.button_save = ToolButton(icon_name='save-as')
        self.button_save.connect('clicked', self.__save_path_from_button)
        self.toolbar.insert(self.button_save, -1)

        self.close_button = ToolButton(icon_name='dialog-cancel')
        self.close_button.connect('clicked', self.close)
        self.toolbar.insert(self.close_button, -1)

        self.vbox.pack_start(self.toolbar, False, False, 0)

    def go_up(self, button=None, _return=False):
        path = '/'
        folders = []

        if self.folder == '/':
            return

        for folder in self.folder.split('/'):
            if not folder:
                continue
            folders.append(folder)

        if not folders:
            return

        for folder in folders[:-1]:
            if folder:
                path += folder + '/'

        if _return:
            return path

        self.folder = path
        self.check_files()

    def create_folder(self, widget):
        self.button_new_folder.set_sensitive(False)

        entry = Gtk.Entry()
        entry.set_placeholder_text('Select a name')
        entry.connect('activate', self.__create_new_folder)

        item = Gtk.ToolItem()
        item.add(entry)
        self.toolbar.insert(item, 4)
        self.toolbar.show_all()

    def __create_new_folder(self, entry):
        if entry.get_text():
            try:
                path = os.path.join(self.folder, entry.get_text())
                os.mkdir(path)
                self.folder = path

            except OSError as msg:
                alert = Alert()
                alert.props.title = _('Error creating the folder.')
                alert.props.msg = msg
                image = Gtk.Image.new_from_stock(Gtk.STOCK_OK,
                                                 Gtk.IconSize.MENU)
                alert.add_button(Gtk.ResponseType.NO, _('Ok'), icon=image)

                alert.connect('response', self.__alert_response)

                self.vbox.pack_start(alert, False, False, 0)
                self.vbox.reorder_child(alert, 1)

        item = entry.get_parent()
        self.toolbar.remove(item)
        self.button_new_folder.set_sensitive(True)

    def __save_path_from_button(self, button):
        self.__name_selected()

    def __name_selected(self, entry=None):
        path = os.path.join(self.folder, self.entry_name.get_text())
        self.__save_path_from_entry(path=path)

    def __save_path_from_entry(self, entry=None, path=None):
        if not path:
            path = self.entry.get_text()

        if os.path.exists(path):
            if os.path.isdir(path):
                self.folder = path

            elif os.path.isfile(path):
                self.create_alert(path)

        elif not os.path.exists(path):
            if os.path.isdir(self.go_up(path, _return=True)):
                self.emit('save-file', path)
                self.destroy()

    def create_alert(self, path):
        alert = Alert()
        alert.props.title = _('This file is already exists.')
        alert.props.msg = _('%s already exists, Overwrite it?' % path)
        cancel = Gtk.Image.new_from_icon_name('dialog-cancel',
                                              Gtk.IconSize.MENU)
        save = Gtk.Image.new_from_icon_name('filesave', Gtk.IconSize.MENU)
        alert.add_button(Gtk.ResponseType.NO, _('Cancel'), icon=cancel)
        alert.add_button(Gtk.ResponseType.YES, _('Save'), icon=save)

        alert.connect('response', self.__alert_response, path)

        self.vbox.pack_start(alert, False, False, 0)
        self.vbox.reorder_child(alert, 1)

    def __alert_response(self, alert, response, path):
        if response == Gtk.ResponseType.NO:
            self.vbox.remove(alert)
        elif response == Gtk.ResponseType.YES:
            self.emit('save-file', path)
            self.destroy()

    def check_files(self):
        files = os.listdir(self.folder)
        files.sort()

        if files != self.files:
            self.selected_path = None

            self.entry.set_text(self.folder)
            self.go_up_button.set_sensitive(self.folder != '/')

            self.show_folder()

        return True

    def show_folder(self):
        self.files = os.listdir(self.folder)
        self.files.sort()

        self.model.clear()

        folders = []
        files = []

        for x in self.files:
            path = os.path.join(self.folder, x)
            if os.path.isdir(path):
                folders.append(path)

            elif os.path.isfile(path):
                files.append(path)

        for path in folders + files:
            if not self.show_hidden_files:
                if path.endswith('~') or path.split('/')[-1].startswith('.'):
                    continue

            pixbuf = G.get_pixbuf_from_path(path)
            self.model.append([path.split('/')[-1], pixbuf])

    def __selection_changed(self, view):
        if self.view.get_selected_items():
            path = self.view.get_selected_items()[0]
            iter = self.model.get_iter(path)
            name = self.model.get_value(iter, 0)
            if os.path.isfile(os.path.join(self.folder, name)):
                self.entry_name.set_text(name)

            else:
                self.entry_name.set_text('')

        else:
            self.entry_name.set_text('')

    def __button_press_event_cb(self, view, event):
        if event.button != 1:
            return

        try:
            path = view.get_path_at_pos(int(event.x), int(event.y))
            iter = self.model.get_iter(path)
            directory = os.path.join(self.folder,
                                     self.model.get_value(iter, 0))

            if event.type.value_name == 'GDK_2BUTTON_PRESS':
                if os.path.isdir(directory):
                    self.folder = directory
                    self.selected_path = None

                elif os.path.isfile(directory):
                    self.selected_path = directory
                    self.create_alert(directory)

            elif event.type.value_name == 'GDK_1BUTTON_PRESS':
                self.selected_path = directory

        except TypeError:
            self.selected_path = None

    def close(self, button=None):
        self.destroy()
Exemple #10
0
class SpeechToolbar(Gtk.Toolbar):
    def __init__(self, activity):
        GObject.GObject.__init__(self)
        self._activity = activity
        if not speech.supported:
            return
        self.is_paused = False
        self._cnf_client = GConf.Client.get_default()
        self.load_speech_parameters()

        self.sorted_voices = [i for i in speech.voices()]
        self.sorted_voices.sort(self.compare_voices)
        default = 0
        for voice in self.sorted_voices:
            if voice[0] == speech.voice[0]:
                break
            default = default + 1

        # Play button
        self.play_btn = ToggleToolButton("media-playback-start")
        self.play_btn.show()
        self.play_toggled_handler = self.play_btn.connect("toggled", self.play_cb)
        self.insert(self.play_btn, -1)
        self.play_btn.set_tooltip(_("Play / Pause"))

        # Stop button
        self.stop_btn = ToolButton("media-playback-stop")
        self.stop_btn.show()
        self.stop_btn.connect("clicked", self.stop_cb)
        self.stop_btn.set_sensitive(False)
        self.insert(self.stop_btn, -1)
        self.stop_btn.set_tooltip(_("Stop"))

        self.voice_combo = ComboBox()
        for voice in self.sorted_voices:
            self.voice_combo.append_item(voice, voice[0])
        self.voice_combo.set_active(default)

        self.voice_combo.connect("changed", self.voice_changed_cb)
        combotool = ToolComboBox(self.voice_combo)
        self.insert(combotool, -1)
        combotool.show()
        speech.reset_cb = self.reset_buttons_cb
        speech.end_text_cb = self.reset_buttons_cb

    def compare_voices(self, a, b):
        if a[0].lower() == b[0].lower():
            return 0
        if a[0].lower() < b[0].lower():
            return -1
        if a[0].lower() > b[0].lower():
            return 1

    def voice_changed_cb(self, combo):
        speech.voice = combo.props.value
        speech.say(speech.voice[0])
        self.save_speech_parameters()

    def load_speech_parameters(self):
        speech_parameters = {}
        data_path = os.path.join(self._activity.get_activity_root(), "data")
        data_file_name = os.path.join(data_path, "speech_params.json")
        if os.path.exists(data_file_name):
            f = open(data_file_name, "r")
            try:
                speech_parameters = json.load(f)
                speech.voice = speech_parameters["voice"]
            finally:
                f.close()
        else:
            speech.voice = self.get_default_voice()
            logging.error("Default voice %s", speech.voice)

        self._cnf_client.add_dir("/desktop/sugar/speech", GConf.ClientPreloadType.PRELOAD_NONE)
        speech.pitch = self._cnf_client.get_int("/desktop/sugar/speech/pitch")
        speech.rate = self._cnf_client.get_int("/desktop/sugar/speech/rate")
        self._cnf_client.notify_add("/desktop/sugar/speech/pitch", self.__conf_changed_cb, None)
        self._cnf_client.notify_add("/desktop/sugar/speech/rate", self.__conf_changed_cb, None)

    def get_default_voice(self):
        """Try to figure out the default voice, from the current locale
           ($LANG)
           Fall back to espeak's voice called Default."""
        voices = speech.get_all_voices()

        locale = os.environ.get("LANG", "")
        language_location = locale.split(".", 1)[0].lower()
        language = language_location.split("_")[0]
        variant = ""
        if language_location.find("_") > -1:
            variant = language_location.split("_")[1]
        # if the language is es but not es_es default to es_la (latin voice)
        if language == "es" and language_location != "es_es":
            language_location = "es_la"

        best = voices.get(language_location) or voices.get(language) or "default"
        logging.debug("Best voice for LANG %s seems to be %s", locale, best)
        return [best, language, variant]

    def __conf_changed_cb(self, client, connection_id, entry, args):
        key = entry.get_key()
        value = client.get_int(key)
        if key == "/desktop/sugar/speech/pitch":
            speech.pitch = value
        if key == "/desktop/sugar/speech/rate":
            speech.rate = value

    def save_speech_parameters(self):
        speech_parameters = {}
        speech_parameters["voice"] = speech.voice
        data_path = os.path.join(self._activity.get_activity_root(), "data")
        data_file_name = os.path.join(data_path, "speech_params.json")
        f = open(data_file_name, "w")
        try:
            json.dump(speech_parameters, f)
        finally:
            f.close()

    def reset_buttons_cb(self):
        logging.error("reset buttons")
        self.play_btn.set_icon_name("media-playback-start")
        self.stop_btn.set_sensitive(False)
        self.play_btn.handler_block(self.play_toggled_handler)
        self.play_btn.set_active(False)
        self.play_btn.handler_unblock(self.play_toggled_handler)
        self.is_paused = False

    def play_cb(self, widget):
        self.stop_btn.set_sensitive(True)
        if widget.get_active():
            self.play_btn.set_icon_name("media-playback-pause")
            logging.error("Paused %s", self.is_paused)
            if not self.is_paused:
                # get the text to speech, if there are a selection,
                # play selected text, if not, play all
                abi = self._activity.abiword_canvas
                selection = abi.get_selection("text/plain")
                if not selection or selection[0] is None or selection[1] == 0:
                    # nothing selected
                    abi.select_all()
                    text = abi.get_selection("text/plain")[0]
                    abi.moveto_bod()
                else:
                    text = selection[0]
                speech.play(text)
            else:
                logging.error("Continue play")
                speech.continue_play()
        else:
            self.play_btn.set_icon_name("media-playback-start")
            self.is_paused = True
            speech.pause()

    def stop_cb(self, widget):
        self.stop_btn.set_sensitive(False)
        self.play_btn.set_icon_name("media-playback-start")
        self.play_btn.set_active(False)
        self.is_paused = False
        speech.stop()
Exemple #11
0
class EditToolbar(Gtk.Toolbar):

    def __init__(self, pc, toolbar_box):

        GObject.GObject.__init__(self)

        self._abiword_canvas = pc.abiword_canvas

        copy = CopyButton()
        copy.props.accelerator = '<Ctrl>C'
        copy.connect('clicked', lambda button: pc.abiword_canvas.copy())
        self.insert(copy, -1)
        copy.show()

        paste = PasteButton()
        paste.props.accelerator = '<Ctrl>V'
        paste.connect('clicked', self.__paste_button_cb)
        self.insert(paste, -1)
        paste.show()

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        undo = UndoButton(sensitive=False)
        undo.connect('clicked', lambda button: pc.abiword_canvas.undo())
        pc.abiword_canvas.connect("can-undo", lambda abi, can_undo:
                                  undo.set_sensitive(can_undo))
        self.insert(undo, -1)
        undo.show()

        redo = RedoButton(sensitive=False)
        redo.connect('clicked', lambda button: pc.abiword_canvas.redo())
        pc.abiword_canvas.connect("can-redo", lambda abi, can_redo:
                                  redo.set_sensitive(can_redo))
        self.insert(redo, -1)
        redo.show()

        pc.abiword_canvas.connect('text-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('image-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('selection-cleared', lambda abi, b:
                                  copy.set_sensitive(False))

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        search_label = Gtk.Label(label=_("Search") + ": ")
        search_label.show()
        search_item_page_label = Gtk.ToolItem()
        search_item_page_label.add(search_label)
        self.insert(search_item_page_label, -1)
        search_item_page_label.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()
        self._add_widget(self._search_entry, expand=True)

        self._findprev = ToolButton('go-previous-paired')
        self._findprev.set_tooltip(_('Find previous'))
        self.insert(self._findprev, -1)
        self._findprev.show()
        self._findprev.connect('clicked', self._findprev_cb)

        self._findnext = ToolButton('go-next-paired')
        self._findnext.set_tooltip(_('Find next'))
        self.insert(self._findnext, -1)
        self._findnext.show()
        self._findnext.connect('clicked', self._findnext_cb)

        # set the initial state of the search controls
        # note: we won't simple call self._search_entry_changed_cb
        # here, as that will call into the abiword_canvas, which
        # is not mapped on screen here, causing the set_find_string
        # call to fail
        self._findprev.set_sensitive(False)
        self._findnext.set_sensitive(False)

    def __paste_button_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

        if clipboard.wait_is_image_available():
            pixbuf_sel = clipboard.wait_for_image()
            activity = self._abiword_canvas.get_toplevel()
            temp_path = os.path.join(activity.get_activity_root(), 'instance')
            if not os.path.exists(temp_path):
                os.makedirs(temp_path)
            fd, file_path = tempfile.mkstemp(dir=temp_path, suffix='.png')
            os.close(fd)
            logging.error('tempfile is %s' % file_path)
            success, data = pixbuf_sel.save_to_bufferv('png', [], [])
            if success:
                px_file = open(file_path, 'w')
                px_file.write(data)
                px_file.close()
                self._abiword_canvas.insert_image(file_path, False)

        elif clipboard.wait_is_uris_available():
            selection = clipboard.wait_for_uris()
            if selection is not None:
                for uri in selection:
                    self._abiword_canvas.insert_image(urlparse(uri).path,
                                                      False)
        else:
            self._abiword_canvas.paste()

    def _search_entry_activated_cb(self, entry):
        logger.debug('_search_entry_activated_cb')
        if not self._search_entry.props.text:
            return

        # find the next entry
        self._abiword_canvas.find_next(False)

    def _search_entry_changed_cb(self, entry):
        logger.debug('_search_entry_changed_cb search for \'%s\'',
                     self._search_entry.props.text)

        if not self._search_entry.props.text:
            self._search_entry.activate()
            # set the button contexts
            self._findprev.set_sensitive(False)
            self._findnext.set_sensitive(False)
            return

        self._abiword_canvas.set_find_string(self._search_entry.props.text)

        # set the button contexts
        self._findprev.set_sensitive(True)
        self._findnext.set_sensitive(True)

        # immediately start seaching
        self._abiword_canvas.find_next(True)

    def _findprev_cb(self, button):
        logger.debug('_findprev_cb')
        if self._search_entry.props.text:
            self._abiword_canvas.find_prev()
        else:
            logger.debug('nothing to search for!')

    def _findnext_cb(self, button):
        logger.debug('_findnext_cb')
        if self._search_entry.props.text:
            self._abiword_canvas.find_next(False)
        else:
            logger.debug('nothing to search for!')

    # bad foddex! this function was copied from sugar's activity.py
    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)

        tool_item.add(widget)
        widget.show()

        self.insert(tool_item, -1)
        tool_item.show()
class ImageViewerActivity(activity.Activity):

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self._object_id = handle.object_id
        self._collab = collabwrapper.CollabWrapper(self)
        self._collab.incoming_file.connect(self.__incoming_file_cb)
        self._collab.buddy_joined.connect(self.__buddy_joined_cb)
        self._collab.joined.connect(self.__joined_cb)
        self._needs_file = False  # Set to true when we join

        # Status of temp file used for write_file:
        self._tempfile = None
        self._close_requested = False

        self._zoom_out_button = None
        self._zoom_in_button = None
        self.previous_image_button = None
        self.next_image_button = None

        self.scrolled_window = Gtk.ScrolledWindow()
        self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS,
                                        Gtk.PolicyType.ALWAYS)
        # disable sharing until a file is opened
        self.max_participants = 1

        # Don't use the default kinetic scrolling, let the view do the
        # drag-by-touch and pinch-to-zoom logic.
        self.scrolled_window.set_kinetic_scrolling(False)

        self.view = ImageView.ImageViewer()

        # Connect to the touch signal for performing drag-by-touch.
        self.view.add_events(Gdk.EventMask.TOUCH_MASK)
        self._touch_hid = self.view.connect('touch-event',
                                            self.__touch_event_cb)
        self.scrolled_window.add(self.view)
        self.view.show()

        self.connect('key-press-event', self.__key_press_cb)

        if GESTURES_AVAILABLE:
            # Connect to the zoom signals for performing
            # pinch-to-zoom.
            zoom_controller = SugarGestures.ZoomController()
            zoom_controller.attach(self,
                                   SugarGestures.EventControllerFlags.NONE)

            zoom_controller.connect('began', self.__zoomtouch_began_cb)
            zoom_controller.connect('scale-changed',
                                    self.__zoomtouch_changed_cb)
            zoom_controller.connect('ended', self.__zoomtouch_ended_cb)

        self._progress_alert = None

        toolbar_box = ToolbarBox()
        self._add_toolbar_buttons(toolbar_box)
        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()

        if self._object_id is None or not self._jobject.file_path:
            empty_widgets = Gtk.EventBox()
            empty_widgets.modify_bg(Gtk.StateType.NORMAL,
                                    style.COLOR_WHITE.get_gdk_color())

            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            mvbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            vbox.pack_start(mvbox, True, False, 0)

            image_icon = Icon(pixel_size=style.LARGE_ICON_SIZE,
                              icon_name='imageviewer',
                              stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
                              fill_color=style.COLOR_TRANSPARENT.get_svg())
            mvbox.pack_start(image_icon, False, False, style.DEFAULT_PADDING)

            label = Gtk.Label('<span foreground="%s"><b>%s</b></span>' %
                              (style.COLOR_BUTTON_GREY.get_html(),
                               _('No image')))
            label.set_use_markup(True)
            mvbox.pack_start(label, False, False, style.DEFAULT_PADDING)

            hbox = Gtk.Box()
            open_image_btn = Gtk.Button()
            open_image_btn.connect('clicked', self._show_picker_cb)
            add_image = Gtk.Image.new_from_stock(Gtk.STOCK_ADD,
                                                 Gtk.IconSize.BUTTON)
            buttonbox = Gtk.Box()
            buttonbox.pack_start(add_image, False, True, 0)
            buttonbox.pack_end(Gtk.Label(_('Choose an image')), True, True, 5)
            open_image_btn.add(buttonbox)
            hbox.pack_start(open_image_btn, True, False, 0)
            mvbox.pack_start(hbox, False, False, style.DEFAULT_PADDING)

            empty_widgets.add(vbox)
            empty_widgets.show_all()
            self.set_canvas(empty_widgets)
        else:
            self.set_canvas(self.scrolled_window)
            self.scrolled_window.show()

        Gdk.Screen.get_default().connect('size-changed', self._configure_cb)
        self._collab.setup()

    def __touch_event_cb(self, widget, event):
        coords = event.get_coords()
        if event.type == Gdk.EventType.TOUCH_BEGIN:
            self.view.start_dragtouch(coords)
        elif event.type == Gdk.EventType.TOUCH_UPDATE:
            self.view.update_dragtouch(coords)
        elif event.type == Gdk.EventType.TOUCH_END:
            self.view.finish_dragtouch(coords)

    def __zoomtouch_began_cb(self, controller):
        self.view.start_zoomtouch(controller.get_center())

        # Don't listen to touch signals until pinch-to-zoom ends.
        self.view.disconnect(self._touch_hid)

    def __zoomtouch_changed_cb(self, controller, scale):
        self.view.update_zoomtouch(controller.get_center(), scale)

    def __zoomtouch_ended_cb(self, controller):
        self.view.finish_zoomtouch()
        self._touch_hid = self.view.connect('touch-event',
                                            self.__touch_event_cb)

    def __key_press_cb(self, widget, event):
        key_name = Gdk.keyval_name(event.keyval)
        if key_name == "Left":
            self._change_image(-1)
        elif key_name == "Right":
            self._change_image(1)
        elif event.get_state() & Gdk.ModifierType.CONTROL_MASK:
            if key_name == "q":
                self.close()
        return True

    def _get_image_list(self):
        value = mime.GENERIC_TYPE_IMAGE
        mime_types = mime.get_generic_type(value).mime_types
        (self.image_list, self.image_count) = datastore.find({'mime_type':
                                                             mime_types})

    def _add_toolbar_buttons(self, toolbar_box):
        self._seps = []

        self.activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self._zoom_out_button = ToolButton('zoom-out')
        self._zoom_out_button.set_tooltip(_('Zoom out'))
        self._zoom_out_button.connect('clicked', self.__zoom_out_cb)
        toolbar_box.toolbar.insert(self._zoom_out_button, -1)
        self._zoom_out_button.show()

        self._zoom_in_button = ToolButton('zoom-in')
        self._zoom_in_button.set_tooltip(_('Zoom in'))
        self._zoom_in_button.connect('clicked', self.__zoom_in_cb)
        toolbar_box.toolbar.insert(self._zoom_in_button, -1)
        self._zoom_in_button.show()

        zoom_tofit_button = ToolButton('zoom-best-fit')
        zoom_tofit_button.set_tooltip(_('Fit to window'))
        zoom_tofit_button.connect('clicked', self.__zoom_tofit_cb)
        toolbar_box.toolbar.insert(zoom_tofit_button, -1)
        zoom_tofit_button.show()

        zoom_original_button = ToolButton('zoom-original')
        zoom_original_button.set_tooltip(_('Original size'))
        zoom_original_button.connect('clicked', self.__zoom_original_cb)
        toolbar_box.toolbar.insert(zoom_original_button, -1)
        zoom_original_button.show()

        fullscreen_button = ToolButton('view-fullscreen')
        fullscreen_button.set_tooltip(_('Fullscreen'))
        fullscreen_button.connect('clicked', self.__fullscreen_cb)
        toolbar_box.toolbar.insert(fullscreen_button, -1)
        fullscreen_button.show()

        self._seps.append(Gtk.SeparatorToolItem())
        toolbar_box.toolbar.insert(self._seps[-1], -1)
        self._seps[-1].show()

        rotate_anticlockwise_button = ToolButton('rotate_anticlockwise')
        rotate_anticlockwise_button.set_tooltip(_('Rotate anticlockwise'))
        rotate_anticlockwise_button.connect('clicked',
                                            self.__rotate_anticlockwise_cb)
        toolbar_box.toolbar.insert(rotate_anticlockwise_button, -1)
        rotate_anticlockwise_button.show()

        rotate_clockwise_button = ToolButton('rotate_clockwise')
        rotate_clockwise_button.set_tooltip(_('Rotate clockwise'))
        rotate_clockwise_button.connect('clicked', self.__rotate_clockwise_cb)
        toolbar_box.toolbar.insert(rotate_clockwise_button, -1)
        rotate_clockwise_button.show()

        if self._object_id is None:
            self._seps.append(Gtk.SeparatorToolItem())
            toolbar_box.toolbar.insert(self._seps[-1], -1)
            self._seps[-1].show()

            self.previous_image_button = ToolButton('go-previous-paired')
            self.previous_image_button.set_tooltip(_('Previous Image'))
            self.previous_image_button.props.sensitive = False
            self.previous_image_button.connect('clicked',
                                               self.__previous_image_cb)
            toolbar_box.toolbar.insert(self.previous_image_button, -1)
            self.previous_image_button.show()

            self.next_image_button = ToolButton('go-next-paired')
            self.next_image_button.set_tooltip(_('Next Image'))
            self.next_image_button.props.sensitive = False
            self.next_image_button.connect('clicked', self.__next_image_cb)
            toolbar_box.toolbar.insert(self.next_image_button, -1)
            self.next_image_button.show()

            GObject.idle_add(self._get_image_list)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _configure_cb(self, event=None):
        if Gdk.Screen.width() <= style.GRID_CELL_SIZE * 12:
            for sep in self._seps:
                sep.hide()
        else:
            for sep in self._seps:
                sep.show()

    def _update_zoom_buttons(self):
        self._zoom_in_button.set_sensitive(self.view.can_zoom_in())
        self._zoom_out_button.set_sensitive(self.view.can_zoom_out())

    def _change_image(self, delta):
        # boundary conditions
        if self.current_image_index == 0 and delta == -1:
            return
        if self.current_image_index == self.image_count - 1 and delta == 1:
            return

        self.current_image_index += delta
        self.make_button_sensitive()
        jobject = self.image_list[self.current_image_index]
        self._object_id = jobject.object_id
        if os.path.exists(self._tempfile):
            os.remove(self._tempfile)
        self.read_file(jobject.file_path)

    def __previous_image_cb(self, button):
        if self.current_image_index > 0:
            self._change_image(-1)

    def __next_image_cb(self, button):
        if self.current_image_index < self.image_count:
            self._change_image(1)

    def __zoom_in_cb(self, button):
        self.view.zoom_in()
        self._update_zoom_buttons()

    def __zoom_out_cb(self, button):
        self.view.zoom_out()
        self._update_zoom_buttons()

    def __zoom_tofit_cb(self, button):
        self.view.zoom_to_fit()
        self._update_zoom_buttons()

    def __zoom_original_cb(self, button):
        self.view.zoom_original()
        self._update_zoom_buttons()

    def __rotate_anticlockwise_cb(self, button):
        self.view.rotate_anticlockwise()

    def __rotate_clockwise_cb(self, button):
        self.view.rotate_clockwise()

    def __fullscreen_cb(self, button):
        self.fullscreen()

    def get_current_image_index(self):
        for image in self.image_list:
            if image.object_id == self._object_id:
                jobject = image
                break

        self.current_image_index = self.image_list.index(jobject)

    def make_button_sensitive(self):
        if self.image_count <= 1:
            return

        if self.current_image_index == 0:
            self.next_image_button.props.sensitive = True
            self.previous_image_button.props.sensitive = False
        elif self.current_image_index == self.image_count - 1:
            self.previous_image_button.props.sensitive = True
            self.next_image_button.props.sensitive = False
        else:
            self.next_image_button.props.sensitive = True
            self.previous_image_button.props.sensitive = True

    def _show_picker_cb(self, button):
        if not self._want_document:
            return

        try:
            chooser = ObjectChooser(self, what_filter='Image',
                                    filter_type=FILTER_TYPE_GENERIC_MIME,
                                    show_preview=True)
        except:
            # for compatibility with older versions
            chooser = ObjectChooser(self._activity, what_filter='Image')

        try:
            result = chooser.run()
            if result == Gtk.ResponseType.ACCEPT:
                jobject = chooser.get_selected_object()
                if jobject and jobject.file_path:
                    self._object_id = jobject.object_id
                    self.read_file(jobject.file_path)
                    self.set_canvas(self.scrolled_window)
                    self.scrolled_window.show()
        finally:
            self.get_current_image_index()
            self.make_button_sensitive()
            chooser.destroy()
            del chooser

    def read_file(self, file_path):
        if self._object_id is None or self.shared_activity:
            # read_file is call because the canvas is visible
            # but we need check if is not the case of empty file
            return

        self._want_document = False
        # enable collaboration
        self.activity_button.page.share.props.sensitive = True

        tempfile = os.path.join(self.get_activity_root(), 'instance',
                                'tmp%i' % time.time())

        os.link(file_path, tempfile)
        self._tempfile = tempfile

        self.view.set_file_location(tempfile)

        zoom = self.metadata.get('zoom', None)
        if zoom is not None:
            self.view.set_zoom(float(zoom))

    def write_file(self, file_path):
        if self._tempfile:
            self.metadata['activity'] = self.get_bundle_id()
            self.metadata['zoom'] = str(self.view.get_zoom())
            if self._close_requested:
                os.link(self._tempfile, file_path)
                os.unlink(self._tempfile)
                self._tempfile = None
        else:
            raise NotImplementedError

    def can_close(self):
        self._close_requested = True
        return True

    def __incoming_file_cb(self, collab, file, desc):
        logging.debug('__incoming_file_cb with need %r', self._needs_file)
        if not self._needs_file:
            return

        self._progress_alert = ProgressAlert()
        self._progress_alert.props.title = _('Receiving image...')
        self.add_alert(self._progress_alert)

        self._needs_file = False
        file_path = os.path.join(self.get_activity_root(), 'instance',
                                 '%i' % time.time())
        file.connect('notify::state', self.__file_notify_state_cb)
        file.connect('notify::transfered_bytes',
                     self.__file_transfered_bytes_cb)
        file.accept_to_file(file_path)

    def __file_notify_state_cb(self, file, pspec):
        logging.debug('__file_notify_state %r', file.props.state)
        if file.props.state != collabwrapper.FT_STATE_COMPLETED:
            return

        file_path = file.props.output
        logging.debug("Saving file %s to datastore...", file_path)
        self._jobject.file_path = file_path
        datastore.write(self._jobject, transfer_ownership=True)

        if self._progress_alert is not None:
            self.remove_alert(self._progress_alert)
            self._progress_alert = None

        GObject.idle_add(self.__set_file_idle_cb, self._jobject.object_id)

    def __set_file_idle_cb(self, object_id):
        dsobj = datastore.get(object_id)
        self._tempfile = dsobj.file_path
        """ This method is used when join a collaboration session """
        self.view.set_file_location(self._tempfile)
        try:
            zoom = int(self.metadata.get('zoom', '0'))
            if zoom > 0:
                self.view.set_zoom(zoom)
        except Exception:
            pass
        self.set_canvas(self.scrolled_window)
        self.scrolled_window.show_all()
        return False

    def __file_transfered_bytes_cb(self, file, pspec):
        total = file.file_size
        bytes_downloaded = file.props.transfered_bytes
        fraction = bytes_downloaded / total
        self._progress_alert.set_fraction(fraction)

    def __buddy_joined_cb(self, collab, buddy):
        logging.debug('__buddy_joined_cb %r', buddy)
        self._collab.send_file_file(buddy, self._tempfile, None)

    def __joined_cb(self, collab):
        logging.debug('I joined!')
        # Somebody will send us a file, just wait
        self._needs_file = True

    def _alert(self, title, text=None):
        alert = NotifyAlert(timeout=5)
        alert.props.title = title
        alert.props.msg = text
        self.add_alert(alert)
        alert.connect('response', self._alert_cancel_cb)
        alert.show()

    def _alert_cancel_cb(self, alert, response_id):
        self.remove_alert(alert)
Exemple #13
0
class GlobesManager():

    def __init__(self, toolbar, edit_toolbar, activity):

        self._page = activity.page
        self._activity = activity

        # agregar cuadro
        self.add_photo = ToolButton()
        self.add_photo.props.icon_name = 'insert-picture'
        self.add_photo.connect('clicked', self.__add_photo_clicked_cb)
        self.add_photo.set_tooltip(_('Add Photo'))
        toolbar.insert(self.add_photo, -1)

        self._globes = {
            'globe': _('Globe'), 'think': _('Think'),
            'whisper': _('Whisper'), 'exclamation': _('Exclamation'),
            'box': _('Box')}

        self._globes_menu = ToolButton(icon_name='globe')
        self._globes_menu.props.tooltip = _('Add a globe')
        self._globes_menu.props.hide_tooltip_on_click = False
        self._globes_menu.palette_invoker.props.toggle_palette = True

        menu_box = PaletteMenuBox()
        self._globes_menu.props.palette.set_content(menu_box)
        menu_box.show()

        for globe in self._globes.keys():
            menu_item = PaletteMenuItem(icon_name=globe,
                                        text_label=self._globes[globe])
            menu_item.connect('activate', self.__activate_add_globe_cb, globe)
            menu_box.append_item(menu_item)
            menu_item.show()
        toolbar.insert(self._globes_menu, -1)

        # lineas de movimiento
        # Agregar aqui el nombre de archivo de una linea de moviemiento
        self._lines = {
            'curves': _('Curves'), 'straight': _('Straight'),
            'highlight': _('Highlight'), 'idea': _('Idea')}

        self._lines_menu = ToolButton(icon_name='curves')
        self._lines_menu.props.tooltip = _('Choose a movement line')
        self._lines_menu.props.hide_tooltip_on_click = False
        self._lines_menu.palette_invoker.props.toggle_palette = True

        menu_box = PaletteMenuBox()
        self._lines_menu.props.palette.set_content(menu_box)
        menu_box.show()

        for line in self._lines.keys():
            menu_item = PaletteMenuItem(icon_name=line,
                                        text_label=self._lines[line])
            menu_item.connect('activate', self.__activate_add_line_cb, line)
            menu_box.append_item(menu_item)
            menu_item.show()
        toolbar.insert(self._lines_menu, -1)

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        toolbar.insert(separator, -1)

        # girar
        self.b_girar = ToolButton('object-rotate-right')
        self.b_girar.connect('clicked', self.girar)
        self.b_girar.set_tooltip(_('Turn'))
        self.b_girar.show()
        edit_toolbar.insert(self.b_girar, -1)

        # borrar
        self.b_borrar = ToolButton('gtk-delete')
        self.b_borrar.connect('clicked', self.borrar)
        self.b_borrar.set_tooltip(_('Delete'))
        self.b_borrar.show()
        edit_toolbar.insert(self.b_borrar, -1)

    def set_buttons_sensitive(self, sensitive):
        self._globes_menu.set_sensitive(sensitive)
        self.add_photo.set_sensitive(sensitive)
        self.b_borrar.set_sensitive(sensitive)
        self.b_girar.set_sensitive(sensitive)
        self._lines_menu.set_sensitive(sensitive)

    def __activate_add_line_cb(self, widget, image_name):
        active_box = self._page.get_active_box()
        active_box.add_imagen("icons/" + image_name + ".svg", 60, 60)

    def __activate_add_globe_cb(self, widget, globe):
        selected_font_name = self._activity.page.selected_font_name

        if globe == 'globe':
            self._page.get_active_box().add_globo(
                60, 60, font_name=selected_font_name)

        if globe == 'think':
            self._page.get_active_box().add_nube(
                60, 60, font_name=selected_font_name)

        if globe == 'whisper':
            self._page.get_active_box().add_globo(
                60, 60, gmodo="despacio", font_name=selected_font_name)

        if globe == 'exclamation':
            self._page.get_active_box().add_grito(
                60, 60, font_name=selected_font_name)

        if globe == 'box':
            self._page.get_active_box().add_rectangulo(
                60, 60, font_name=selected_font_name)

    def __add_photo_clicked_cb(self, button):
            self.add_image()

    def girar(self, boton):
        # veo cual es el globo seleccionado y o giro
        box = self._page.get_active_box()
        if box.get_globo_activo() is not None:
            globe = box.get_globo_activo()
            if globe.girar():
                box.redraw()

    def borrar(self, boton):
        box = self._page.get_active_box()
        if box.get_globo_activo() is not None:
            # Do no remove the title globe
            if box.get_globo_activo() == box.title_globe:
                return

            box.globos.remove(box.get_globo_activo())
            box.set_globo_activo(None)
            box.redraw()
        else:
            # Borrar un box es mas complicado
            pos_box = self._page.boxs.index(box)
            if (len(self._page.boxs) > pos_box):
                for i in range(pos_box, len(self._page.boxs) - 1):
                    box1 = self._page.boxs[i]
                    box2 = self._page.boxs[i + 1]
                    box1.image = None
                    box1.image_name = box2.image_name
                    box1.globos = []
                    box1.globos.extend(box2.globos)
                    box1.redraw()
            last_box = self._page.boxs[-1]
            last_box.image = None
            last_box.image_name = ""
            last_box.globos = []
            last_box.redraw()
            self._page.boxs.pop()
            self._page.table.remove(last_box)

    def add_image(self):
        try:
            chooser = ObjectChooser(self._activity, what_filter='Image',
                                    filter_type=FILTER_TYPE_GENERIC_MIME,
                                    show_preview=True)
        except:
            # for compatibility with older versions
            chooser = ObjectChooser(self._activity, what_filter='Image')

        try:
            result = chooser.run()
            if result == Gtk.ResponseType.ACCEPT:
                logging.error('ObjectChooser: %r' %
                              chooser.get_selected_object())
                jobject = chooser.get_selected_object()
                if jobject and jobject.file_path:
                    logging.error("imagen seleccionada: %s",
                                  jobject.file_path)
                    tempfile_name = \
                        os.path.join(self._activity.get_activity_root(),
                                     'instance', 'tmp%i' % time.time())
                    os.link(jobject.file_path, tempfile_name)
                    logging.error("tempfile_name: %s", tempfile_name)
                    self._page.add_box_from_journal_image(tempfile_name)
        finally:
            chooser.destroy()
            del chooser
Exemple #14
0
class IntensitySelector(Gtk.ToolItem):

    __gsignals__ = {
        'changed': (GObject.SignalFlags.RUN_LAST, None, ([])),
    }

    def __init__(self, value_range, default_value, default_image):

        Gtk.ToolItem.__init__(self)
        self._palette_invoker = ToolInvoker()

        self.palette = None
        self._values = value_range
        self._palette_invoker.attach_tool(self)

        # theme the buttons, can be removed if add the style to the sugar css
        # these are the same values used in gtk-widgets.css.em
        if style.zoom(100) == 100:
            subcell_size = 15
            default_padding = 6
        else:
            subcell_size = 11
            default_padding = 4

        hbox = Gtk.HBox()
        vbox = Gtk.VBox()
        self.add(vbox)
        # add a vbox to set the padding up and down
        vbox.pack_start(hbox, True, True, default_padding)

        self._size_down = ToolButton('go-previous-paired')
        self._palette_invoker.attach_tool(self._size_down)

        self._size_down.set_can_focus(False)
        self._size_down.connect('clicked', self.__value_changed_cb, False)
        hbox.pack_start(self._size_down, False, False, 5)

        # TODO: default?
        self._default_value = default_value
        self._value = self._default_value

        self.image_wrapper = Gtk.EventBox()
        self._intensityImage = Gtk.Image()
        self.image_wrapper.add(self._intensityImage)
        self.image_wrapper.show()
        self._intensityImage.set_from_file(default_image)
        self._intensityImage.show()
        self._palette_invoker.attach_widget(self.image_wrapper)

        hbox.pack_start(self.image_wrapper, False, False, 10)

        self._size_up = ToolButton('go-next-paired')

        self._palette_invoker.attach_tool(self._size_up)

        self._size_up.set_can_focus(False)
        self._size_up.connect('clicked', self.__value_changed_cb, True)
        hbox.pack_start(self._size_up, False, False, 5)

        radius = 2 * subcell_size
        theme_up = "GtkButton {border-radius:0px %dpx %dpx 0px;}" % (radius,
                                                                     radius)
        css_provider_up = Gtk.CssProvider()
        css_provider_up.load_from_data(theme_up)

        style_context = self._size_up.get_style_context()
        style_context.add_provider(css_provider_up,
                                   Gtk.STYLE_PROVIDER_PRIORITY_USER)

        theme_down = "GtkButton {border-radius: %dpx 0px 0px %dpx;}" % (radius,
                                                                        radius)
        css_provider_down = Gtk.CssProvider()
        css_provider_down.load_from_data(theme_down)
        style_context = self._size_down.get_style_context()
        style_context.add_provider(css_provider_down,
                                   Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.show_all()

    def __destroy_cb(self, **args):
        if self._palette_invoker is not None:
            self._palette_invoker.detach()

    def __value_changed_cb(self, button, increase):
        if self._value in self._values:
            i = self._values.index(self._value)
            if increase:
                if i < len(self._values) - 1:
                    i += 1
            else:
                if i > 0:
                    i -= 1
        else:
            i = self._values.index(self._default_value)

        self._value = self._values[i]
        self._size_down.set_sensitive(i != 0)
        self._size_up.set_sensitive(i < len(self._values) - 1)
        self.emit('changed')

    def set_value(self, val):
        if val not in self._values:
            # assure the val assigned is in the range
            # if not, assign one close.
            for v in self._values:
                if v > val:
                    val = v
                    break
            if val > self._values[-1]:
                size = self._values[-1]

        self._value = size
        self._size_label.set_text(str(self._value))

        # update the buttons states
        i = self._values.index(self._value)
        self._size_down.set_sensitive(i != 0)
        self._size_up.set_sensitive(i < len(self._value) - 1)
        self.emit('changed')

    def set_tooltip(self, tooltip):
        """ Set a simple palette with just a single label.
        """
        if self.palette is None or self._tooltip is None:
            self.palette = Palette(tooltip)
        elif self.palette is not None:
            self.palette.set_primary_text(tooltip)

        self._tooltip = tooltip

    def get_hide_tooltip_on_click(self):
        return self._hide_tooltip_on_click

    def set_hide_tooltip_on_click(self, hide_tooltip_on_click):
        if self._hide_tooltip_on_click != hide_tooltip_on_click:
            self._hide_tooltip_on_click = hide_tooltip_on_click

    hide_tooltip_on_click = GObject.Property(type=bool,
                                             default=True,
                                             getter=get_hide_tooltip_on_click,
                                             setter=set_hide_tooltip_on_click)

    def get_tooltip(self):
        return self._tooltip

    def create_palette(self):
        return None

    def get_palette(self):
        return self._palette_invoker.palette

    def set_palette(self, palette):
        self._palette_invoker.palette = palette

    palette = GObject.Property(type=object,
                               setter=set_palette,
                               getter=get_palette)

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.Property(type=object,
                                       setter=set_palette_invoker,
                                       getter=get_palette_invoker)

    def do_draw(self, cr):
        Gtk.ToolButton.do_draw(self, cr)

        if self.palette and self.palette.is_up():
            invoker = self.palette.props.invoker
            invoker.draw_rectangle(cr, self.palette)

        return False

    def do_clicked(self):
        if self._hide_tooltip_on_click and self.palette:
            self.palette.popdown(True)

    def set_image(self, image):
        self._intensityImage.set_from_file(image)

    def get_value(self):
        return self._value
class GameToolbar(Gtk.Toolbar):

    __gtype_name__ = 'GameToolbar'

    __gsignals__ = {
        'game-restart': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []),
        'ai-activated': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []),
        'ai-deactivated': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []),
        'game-board-size': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [GObject.TYPE_INT]),
    }
    
    def __init__(self, activity):
        Gtk.Toolbar.__init__(self)
        self.activity = activity

        # Reset Button
        restart_icon = join(dirname(__file__), 'images', 'gtk-refresh.svg')
        restart_image = Gtk.Image()
        restart_image.set_from_file(restart_icon)
        self._restart_button = ToolButton()
        self._restart_button.set_icon_widget(restart_image)
        self._restart_button.connect('clicked', self._game_restart_cb)
        self._restart_button.set_tooltip(_('Restart Game'))
        self.insert(self._restart_button, -1)
        self._restart_button.show()
        
        # Separator
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        self.insert(separator, -1)
        
        self._add_widget(Gtk.Label(_('Board size') + ': '))
        # Change size combobox
        self._size_combo = ToolComboBox()
        self._sizes = ['19 X 19', '13 X 13', '9 X 9']

        for i, f in enumerate(self._sizes):
            self._size_combo.combo.append_item(i, f)

        self._size_combo.combo.connect('changed', self._game_size_cb)
        self._add_widget(self._size_combo)
        self._size_combo.combo.set_active(0)

        # Separator
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        self.insert(separator, -1)

        # Artificial Intelligence Button
        self._ai_button = Gtk.ToggleToolButton()

        if search_for_gnugo():
            self._ai_button.connect('toggled', self._ai_toggled_cb)
            self._ai_button.set_label(_('Play against PlayGo!'))

        else:
            self._ai_button.set_label(_('You need to install gnugo to play against PlayGo'))
            self._ai_button.set_sensitive(False)

        self.insert(self._ai_button, -1)
        self._ai_button.show()
        
    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)
        tool_item.add(widget)
        widget.show()
        self.insert(tool_item, -1)
        tool_item.show()
        
    def _game_restart_cb(self, widget):
        self._size_combo.set_sensitive(True)
        self.emit('game-restart')
    
    def grey_out_restart(self):
        self._restart_button.set_sensitive(False)
    
    def _game_size_cb(self, widget):
        game_size = int(self._sizes[self._size_combo.combo.get_active()][:2])
        self.emit('game-board-size', game_size)
        
    def grey_out_size_change(self):
        self._size_combo.set_sensitive(False)
        
    def update_toolbar(self, widget, data, grid):
        size = data.get('size')
        self._size_combo.combo.handler_block(self.size_handle_id)
        size_index = self._sizes.index(size+' X '+size)
        self._size_combo.combo.set_active(int(size_index))
        self._size_combo.combo.handler_unblock(self.size_handle_id)

    def _ai_toggled_cb(self, widget):
        if widget.get_active():
            self.emit('ai-activated')

        else:
            self.emit('ai-deactivated')
        
    def grey_out_ai(self):
        self._ai_button.set_sensitive(False)
        
    def set_ai_button_state(self, value):
        self._ai_button.set_active(value)
Exemple #16
0
class ReflectActivity(activity.Activity):
    ''' An activity for reflecting on one's work '''

    def __init__(self, handle):
        ''' Initialize the toolbar '''
        try:
            super(ReflectActivity, self).__init__(handle)
        except dbus.exceptions.DBusException as e:
            _logger.error(str(e))

        logging.error('setting reflection data to []')
        self.reflection_data = []

        self.connect('realize', self.__realize_cb)

        self.font_size = 8

        self.max_participants = 4
        self._setup_toolbars()

        color = profile.get_color()
        color_stroke = color.get_stroke_color()
        color_fill = color.get_fill_color()

        lighter = utils.lighter_color([color_stroke, color_fill])
        darker = 1 - lighter

        if lighter == 0:
            self.bg_color = style.Color(color_stroke)
            self.fg_color = style.Color(color_fill)
        else:
            self.bg_color = style.Color(color_fill)
            self.fg_color = style.Color(color_stroke)

        self.modify_bg(Gtk.StateType.NORMAL, self.bg_color.get_gdk_color())

        self.bundle_path = activity.get_bundle_path()
        self.tmp_path = os.path.join(activity.get_activity_root(), 'instance')

        self.sharing = False
        self._copy_entry = None
        self._paste_entry = None
        self._webkit = None
        self._clipboard_text = ''
        self._fixed = None

        self.initiating = True
        if self.shared_activity:
            # We're joining
            if not self.get_shared():
                self.initiating = False

                self.busy_cursor()
                share_icon = Icon(icon_name='zoom-neighborhood')
                self._joined_alert = Alert()
                self._joined_alert.props.icon = share_icon
                self._joined_alert.props.title = _('Please wait')
                self._joined_alert.props.msg = _('Starting connection...')
                self.add_alert(self._joined_alert)

                # Wait for joined signal
                self.connect("joined", self._joined_cb)

        self._open_reflect_windows()

        self._setup_presence_service()

        # Joiners wait to receive data from sharer
        # Otherwise, load reflections from local store
        if not self.shared_activity:
            self.busy_cursor()
            GObject.idle_add(self._load_reflections)

    def read_file(self, file_path):
        fd = open(file_path, 'r')
        data = fd.read()
        fd.close()
        self.reflection_data = json.loads(data)

    def write_file(self, file_path):
        data = json.dumps(self.reflection_data)
        fd = open(file_path, 'w')
        fd.write(data)
        fd.close()

        self.metadata['font_size'] = str(self.font_size)

    def _load_reflections(self):
        self._find_starred()
        self._reflect_window.load(self.reflection_data)
        self.reset_cursor()

    def _found_obj_id(self, obj_id):
        for item in self.reflection_data:
            if 'obj_id' in item and item['obj_id'] == obj_id:
                return True
        return False

    def reload_data(self, data):
        ''' Reload data after sorting or searching '''
        self._reflection_data = data[:]
        self._reflect_window.reload(self._reflection_data)
        self.reset_scrolled_window_adjustments()

    def _find_starred(self):
        ''' Find all the _stars in the Journal. '''
        self.dsobjects, self._nobjects = datastore.find({'keep': '1'})
        for dsobj in self.dsobjects:
            if self._found_obj_id(dsobj.object_id):
                continue  # Already have this object -- TODO: update it
            self._add_new_from_journal(dsobj)

    def _add_new_from_journal(self, dsobj):
        self.reflection_data.append({
            'title': _('Untitled'), 'obj_id': dsobj.object_id})
        if hasattr(dsobj, 'metadata'):
            if 'creation_time' in dsobj.metadata:
                self.reflection_data[-1]['creation_time'] = \
                    dsobj.metadata['creation_time']
            else:
                self.reflection_data[-1]['creation_time'] = \
                    int(time.time())
            if 'timestamp' in dsobj.metadata:
                self.reflection_data[-1]['modification_time'] = \
                    dsobj.metadata['timestamp']
            else:
                self.reflection_data[-1]['modification_time'] = \
                    self.reflection_data[-1]['creation_time']
            if 'activity' in dsobj.metadata:
                self.reflection_data[-1]['activities'] = \
                    [utils.bundle_id_to_icon(dsobj.metadata['activity'])]
            if 'title' in dsobj.metadata:
                self.reflection_data[-1]['title'] = \
                    dsobj.metadata['title']
            if 'description' in dsobj.metadata:
                self.reflection_data[-1]['content'] = \
                    [{'text': dsobj.metadata['description']}]
            else:
                self.reflection_data[-1]['content'] = []
            if 'tags' in dsobj.metadata:
                self.reflection_data[-1]['tags'] = []
                tags = dsobj.metadata['tags'].split()
                for tag in tags:
                    if tag[0] != '#':
                        self.reflection_data[-1]['tags'].append('#' + tag)
                    else:
                        self.reflection_data[-1]['tags'].append(tag)
            if 'comments' in dsobj.metadata:
                try:
                    comments = json.loads(dsobj.metadata['comments'])
                except:
                    comments = []
                self.reflection_data[-1]['comments'] = []
                for comment in comments:
                    try:
                        data = {'nick': comment['from'],
                                'comment': comment['message']}
                        if 'icon-color' in comment:
                            colors = comment['icon-color'].split(',')
                            darker = 1 - utils.lighter_color(colors)
                            data['color'] = colors[darker]
                        else:
                            data['color'] = '#000000'
                        self.reflection_data[-1]['comments'].append(data)
                    except:
                        _logger.debug('could not parse comment %s'
                                      % comment)
            if 'mime_type' in dsobj.metadata and \
               dsobj.metadata['mime_type'][0:5] == 'image':
                new_path = os.path.join(self.tmp_path,
                                        dsobj.object_id)
                try:
                    shutil.copy(dsobj.file_path, new_path)
                except Exception as e:
                    logging.error("Couldn't copy %s to %s: %s" %
                                  (dsobj.file_path, new_path, e))
                self.reflection_data[-1]['content'].append(
                    {'image': new_path})
            elif 'preview' in dsobj.metadata:
                pixbuf = utils.get_pixbuf_from_journal(dsobj, 300, 225)
                if pixbuf is not None:
                    path = os.path.join(self.tmp_path,
                                        dsobj.object_id + '.png')
                    utils.save_pixbuf_to_file(pixbuf, path)
                    self.reflection_data[-1]['content'].append(
                        {'image': path})
            self.reflection_data[-1]['stars'] = 0

    def delete_item(self, obj_id):
        for i, obj in enumerate(self.reflection_data):
            if obj['obj_id'] == obj_id:
                self.reflection_data.remove(self.reflection_data[i])
                return

    def busy_cursor(self):
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def reset_cursor(self):
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.LEFT_PTR))

    def _open_reflect_windows(self):
        # Most things need only be done once
        if self._fixed is None:
            self._fixed = Gtk.Fixed()
            self._fixed.set_size_request(Gdk.Screen.width(),
                                         Gdk.Screen.height())

            # Offsets from the bottom of the screen
            dy1 = 2 * style.GRID_CELL_SIZE
            dy2 = 1 * style.GRID_CELL_SIZE

            self._button_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._button_area.set_size_request(Gdk.Screen.width(),
                                               style.GRID_CELL_SIZE)
            self._fixed.put(self._button_area, 0, 0)
            self._button_area.show()

            self._scrolled_window = Gtk.ScrolledWindow()
            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._set_scroll_policy()
            self._graphics_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._scrolled_window.add_with_viewport(self._graphics_area)
            self._graphics_area.show()
            self._fixed.put(self._scrolled_window, 0, dy2)
            self._scrolled_window.show()

            self._overlay_window = Gtk.ScrolledWindow()
            self._overlay_window.set_size_request(
                style.GRID_CELL_SIZE * 10,
                style.GRID_CELL_SIZE * 6)
            self._overlay_window.modify_bg(
                Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color())
            self._overlay_window.set_policy(
                 Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
            self._overlay_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._overlay_window.add_with_viewport(self._overlay_area)
            self._overlay_area.show()
            x = int((Gdk.Screen.width() - style.GRID_CELL_SIZE * 10) / 2)
            self._fixed.put(self._overlay_window, 0, Gdk.Screen.height())
            self._overlay_window.show()
            self._old_overlay_widget = None

            self._reflect_window = ReflectWindow(self)
            self._reflect_window.show()

            Gdk.Screen.get_default().connect('size-changed',
                                             self._configure_cb)
            self._toolbox.connect('hide', self._resize_hide_cb)
            self._toolbox.connect('show', self._resize_show_cb)

            self._reflect_window.set_events(Gdk.EventMask.KEY_PRESS_MASK)
            self._reflect_window.connect('key_press_event',
                                      self._reflect_window.keypress_cb)
            self._reflect_window.set_can_focus(True)
            self._reflect_window.grab_focus()

        self.set_canvas(self._fixed)
        self._fixed.show()

    def reset_scrolled_window_adjustments(self):
        adj = self._scrolled_window.get_hadjustment()
        if adj is not None:
            adj.set_value(0)
        adj = self._scrolled_window.get_vadjustment()
        if adj is not None:
            adj.set_value(0)

    def load_graphics_area(self, widget):
        self._graphics_area.add(widget)

    def load_button_area(self, widget):
        self._button_area.add(widget)

    def load_overlay_area(self, widget):
        if self._old_overlay_widget is not None:
            self._overlay_area.remove(self._old_overlay_widget)
        self._overlay_area.add(widget)
        self._old_overlay_widget = widget

    def show_overlay_area(self):
        x = int((Gdk.Screen.width() - style.GRID_CELL_SIZE * 10) / 2)
        self._fixed.move(self._overlay_window, x, style.GRID_CELL_SIZE)

    def hide_overlay_area(self):
        self._fixed.move(
            self._overlay_window, 0, Gdk.Screen.height())

    def _resize_hide_cb(self, widget):
        self._resize_canvas(widget, True)

    def _resize_show_cb(self, widget):
        self._resize_canvas(widget, False)

    def _configure_cb(self, event):
        self._fixed.set_size_request(Gdk.Screen.width(), Gdk.Screen.height())
        self._set_scroll_policy()
        self._resize_canvas(None)
        self._reflect_window.reload_graphics()

    def _resize_canvas(self, widget, fullscreen=False):
        # When a toolbar is expanded or collapsed, resize the canvas
        if hasattr(self, '_reflect_window'):
            if self.toolbar_expanded():
                dy1 = 3 * style.GRID_CELL_SIZE
                dy2 = 2 * style.GRID_CELL_SIZE
            else:
                dy1 = 2 * style.GRID_CELL_SIZE
                dy2 = 1 * style.GRID_CELL_SIZE

            if fullscreen:
                dy1 -= 2 * style.GRID_CELL_SIZE
                dy2 -= 2 * style.GRID_CELL_SIZE

            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy2)
            self._fixed.move(self._button_area, 0, 0)

        self._about_panel_visible = False

    def toolbar_expanded(self):
        if self.activity_button.is_expanded():
            return True
        elif self.edit_toolbar_button.is_expanded():
            return True
        elif self.view_toolbar_button.is_expanded():
            return True
        return False

    def get_activity_version(self):
        info_path = os.path.join(self.bundle_path, 'activity', 'activity.info')
        try:
            info_file = open(info_path, 'r')
        except Exception as e:
            _logger.error('Could not open %s: %s' % (info_path, e))
            return 'unknown'

        cp = ConfigParser()
        cp.readfp(info_file)

        section = 'Activity'

        if cp.has_option(section, 'activity_version'):
            activity_version = cp.get(section, 'activity_version')
        else:
            activity_version = 'unknown'
        return activity_version

    def get_uid(self):
        if len(self.volume_data) == 1:
            return self.volume_data[0]['uid']
        else:
            return 'unknown'

    def _setup_toolbars(self):
        ''' Setup the toolbars. '''
        self._toolbox = ToolbarBox()

        self.activity_button = ActivityToolbarButton(self)
        self.activity_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self.set_toolbar_box(self._toolbox)
        self._toolbox.show()
        self.toolbar = self._toolbox.toolbar

        view_toolbar = Gtk.Toolbar()
        self.view_toolbar_button = ToolbarButton(
            page=view_toolbar,
            label=_('View'),
            icon_name='toolbar-view')
        self.view_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.view_toolbar_button, 1)
        view_toolbar.show()
        self.view_toolbar_button.show()

        button = ToolButton('view-fullscreen')
        button.set_tooltip(_('Fullscreen'))
        button.props.accelerator = '<Alt>Return'
        view_toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self._fullscreen_cb)

        edit_toolbar = Gtk.Toolbar()
        self.edit_toolbar_button = ToolbarButton(
            page=edit_toolbar,
            label=_('Edit'),
            icon_name='toolbar-edit')
        self.edit_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.edit_toolbar_button, 1)
        edit_toolbar.show()
        self.edit_toolbar_button.show()

        self._copy_button = ToolButton('edit-copy')
        self._copy_button.set_tooltip(_('Copy'))
        self._copy_button.props.accelerator = '<Ctrl>C'
        edit_toolbar.insert(self._copy_button, -1)
        self._copy_button.show()
        self._copy_button.connect('clicked', self._copy_cb)
        self._copy_button.set_sensitive(False)

        self._paste_button = ToolButton('edit-paste')
        self._paste_button.set_tooltip(_('Paste'))
        self._paste_button.props.accelerator = '<Ctrl>V'
        edit_toolbar.insert(self._paste_button, -1)
        self._paste_button.show()
        self._paste_button.connect('clicked', self._paste_cb)
        self._paste_button.set_sensitive(False)

        button = ToolButton('list-add')
        button.set_tooltip(_('Add Item'))
        button.props.accelerator = '<Ctrl>+'
        self._toolbox.toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self.__add_item_cb)

        self._date_button = RadioToolButton('date-sort', group=None)
        self._date_button.set_tooltip(_('Sort by Date'))
        self._date_button.connect('clicked', self._date_button_cb)
        self._toolbox.toolbar.insert(self._date_button, -1)
        self._date_button.show()

        self._title_button = RadioToolButton('title-sort',
                                             group=self._date_button)
        self._title_button.set_tooltip(_('Sort by Title'))
        self._title_button.connect('clicked', self._title_button_cb)
        self._toolbox.toolbar.insert(self._title_button, -1)
        self._title_button.show()

        self._stars_button = RadioToolButton('stars-sort',
                                             group=self._date_button)
        self._stars_button.set_tooltip(_('Sort by Favourite'))
        self._stars_button.connect('clicked', self._stars_button_cb)
        self._toolbox.toolbar.insert(self._stars_button, -1)
        self._stars_button.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()

        tool_item = Gtk.ToolItem()
        tool_item.set_expand(True)
        tool_item.add(self._search_entry)
        self._search_entry.show()
        self._toolbox.toolbar.insert(tool_item, -1)
        tool_item.show()

        self._search_button = ToolButton('dialog-ok')
        self._search_button.set_tooltip(_('Search by Tags'))
        self._search_button.connect('clicked', self._search_button_cb)
        self._toolbox.toolbar.insert(self._search_button, -1)
        self._search_button.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self._toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        self._toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _search_button_cb(self, button):
        self.busy_cursor()
        self._do_search()

    def _search_entry_activated_cb(self, entry):
        self.busy_cursor()
        self._do_search()

    def _do_search(self):
        logging.debug('_search_entry_activated_cb')
        if self._search_entry.props.text == '':
            logging.debug('clearing search')
            for item in self.reflection_data:
                item['hidden'] = False
        else:
            tags = self._search_entry.props.text.split()
            for i, tag in enumerate(tags):
                if not tag[0] == '#':
                    tags[i] = '#%s' % tag
            logging.error(tags)
            for item in self.reflection_data:
                hidden = True
                if 'tags' in item:
                    for tag in tags:
                        if tag in item['tags']:
                            hidden = False
                item['hidden'] = hidden
        self.reload_data(self.reflection_data)
        self.reset_cursor()

    def _search_entry_changed_cb(self, entry):
        logging.debug('_search_entry_changed_cb search for \'%s\'',
                     self._search_entry.props.text)
        self.busy_cursor()
        self._do_search_changed()

    def _do_search_changed(self):
        if self._search_entry.props.text == '':
            logging.debug('clearing search')
            for item in self.reflection_data:
                item['hidden'] = False
            self.reload_data(self.reflection_data)
        self.reset_cursor()

    def _title_button_cb(self, button):
        ''' sort by title '''
        self.busy_cursor()
        GObject.idle_add(self._title_sort)

    def _title_sort(self):
        sorted_data = sorted(self.reflection_data,
                             key=lambda item: item['title'].lower())
        self.reload_data(sorted_data)
        self.reset_cursor()

    def _date_button_cb(self, button):
        ''' sort by modification date '''
        self.busy_cursor()
        GObject.idle_add(self._date_sort)

    def _date_sort(self):
        sorted_data = sorted(self.reflection_data,
                             key=lambda item: int(item['modification_time']),
                             reverse=True)
        self.reload_data(sorted_data)
        self.reset_cursor()

    def _stars_button_cb(self, button):
        ''' sort by number of stars '''
        self.busy_cursor()
        GObject.idle_add(self._stars_sort)

    def _stars_sort(self):
        sorted_data = sorted(self.reflection_data,
                             key=lambda item: item['stars'], reverse=True)
        self.reload_data(sorted_data)
        self.reset_cursor()

    def __realize_cb(self, window):
        self.window_xid = window.get_window().get_xid()

    def set_copy_widget(self, webkit=None, text_entry=None):
        # Each task is responsible for setting a widget for copy
        if webkit is not None:
            self._webkit = webkit
        else:
            self._webkit = None
        if text_entry is not None:
            self._copy_entry = text_entry
        else:
            self._copy_entry = None

        self._copy_button.set_sensitive(webkit is not None or
                                        text_entry is not None)

    def _copy_cb(self, button):
        if self._copy_entry is not None:
            self._copy_entry.copy_clipboard()
        elif self._webkit is not None:
            self._webkit.copy_clipboard()
        else:
            _logger.debug('No widget set for copy.')

    def set_paste_widget(self, text_entry=None):
        # Each task is responsible for setting a widget for paste
        if text_entry is not None:
            self._paste_entry = text_entry
        self._paste_button.set_sensitive(text_entry is not None)

    def _paste_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.clipboard_text = clipboard.wait_for_text()
        if self._paste_entry is not None:
            self._paste_entry.paste_clipboard()
        else:
            _logger.debug('No widget set for paste (%s).' %
                          self.clipboard_text)

    def _fullscreen_cb(self, button):
        ''' Hide the Sugar toolbars. '''
        self.fullscreen()

    def __add_item_cb(self, button):
        try:
            chooser = ObjectChooser(parent=self, what_filter=None)
        except TypeError:
            chooser = ObjectChooser(
                None, self._reflection.activity,
                Gtk.DialogFlags.MODAL |
                Gtk.DialogFlags.DESTROY_WITH_PARENT)

        try:
            result = chooser.run()
            if result == Gtk.ResponseType.ACCEPT:
                jobject = chooser.get_selected_object()
                if jobject:
                    self._add_new_from_journal(jobject)
                    self.reload_data(self.reflection_data)
        finally:
            chooser.destroy()
            del chooser

    def _set_scroll_policy(self):
        if Gdk.Screen.width() < Gdk.Screen.height():
            self._scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                             Gtk.PolicyType.AUTOMATIC)
        else:
            self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                             Gtk.PolicyType.AUTOMATIC)

    def _remove_alert_cb(self, alert, response_id):
        self.remove_alert(alert)

    def _close_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            self.close()

    def _setup_presence_service(self):
        ''' Setup the Presence Service. '''
        self.pservice = presenceservice.get_instance()

        owner = self.pservice.get_owner()
        self.owner = owner
        self._share = ''
        self.connect('shared', self._shared_cb)
        self.connect('joined', self._joined_cb)

    def _shared_cb(self, activity):
        ''' Either set up initial share...'''
        if self.shared_activity is None:
            _logger.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        self.initiating = True
        self._waiting_for_reflections = False
        _logger.debug('I am sharing...')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        _logger.debug('This is my activity: making a tube...')
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
            SERVICE, {})

        self.sharing = True

    def _joined_cb(self, activity):
        ''' ...or join an exisiting share. '''
        if self.shared_activity is None:
            _logger.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        if self._joined_alert is not None:
            self.remove_alert(self._joined_alert)
            self._joined_alert = None

        self.initiating = False
        self._waiting_for_reflections = True
        _logger.debug('I joined a shared activity.')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        _logger.debug('I am joining an activity: waiting for a tube...')
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
            reply_handler=self._list_tubes_reply_cb,
            error_handler=self._list_tubes_error_cb)

        self.sharing = True

    def _list_tubes_reply_cb(self, tubes):
        ''' Reply to a list request. '''
        for tube_info in tubes:
            self._new_tube_cb(*tube_info)

    def _list_tubes_error_cb(self, e):
        ''' Log errors. '''
        _logger.error('ListTubes() failed: %s', e)

    def _new_tube_cb(self, id, initiator, type, service, params, state):
        ''' Create a new tube. '''
        _logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
                      'params=%r state=%d', id, initiator, type, service,
                      params, state)

        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:
                self.tubes_chan[
                    telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)

            self.collab = CollabWrapper(self)
            self.collab.message.connect(self.event_received_cb)
            self.collab.setup()

            if self._waiting_for_reflections:
                self.send_event(JOIN_CMD, {})
                self._joined_alert = Alert()
                self._joined_alert.props.title = _('Please wait')
                self._joined_alert.props.msg = _('Requesting reflections...')
                self.add_alert(self._joined_alert)

    def event_received_cb(self, collab, buddy, msg):
        ''' Data is passed as tuples: cmd:text '''
        command = msg.get("command")
        payload = msg.get("payload")
        logging.debug(command)

        if command == JOIN_CMD:
            # Sharer needs to send reflections database to joiners.
            if self.initiating:
                # Send pictures first.
                for item in self.reflection_data:
                    if 'content' in item:
                        for content in item['content']:
                            if 'image' in content:
                                pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
                                    content['image'], 120, 90)
                                if pixbuf is not None:
                                    data = utils.pixbuf_to_base64(pixbuf)
                                self.send_event(PICTURE_CMD,
                                    {"image": os.path.basename(content['image']),
                                     "data": data})
                data = json.dumps(self.reflection_data)
                self.send_event(SHARE_CMD, {"data": data})
        elif command == NEW_REFLECTION_CMD:
            self._reflect_window.add_new_reflection(payload)
        elif command == TITLE_CMD:
            obj_id = payload.get("obj_id")
            title = payload.get("title")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.update_title(obj_id, title)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == TAG_CMD:
            obj_id = payload.get("obj_id")
            data = payload.get("data")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.update_tags(obj_id, data)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == ACTIVITY_CMD:
            obj_id = payload.get("obj_id")
            bundle_id = payload.get("bundle_id")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.insert_activity(obj_id, bundle_id)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == STAR_CMD:
            obj_id = payload.get("obj_id")
            stars = payload.get("stars")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.update_stars(obj_id, int(stars))
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == COMMENT_CMD:
            found_the_object = False
            # Receive a comment and associated reflection ID
            obj_id = payload.get("obj_id")
            nick = payload.get("nick")
            color = payload.get("color")
            comment = payload.get("comment")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    if not 'comments' in item:
                        item['comments'] = []
                    data = {'nick': nick, 'comment': comment, 'color': color}
                    item['comments'].append(data)
                    self._reflect_window.insert_comment(obj_id, data)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == REFLECTION_CMD:
            found_the_object = False
            # Receive a reflection and associated reflection ID
            obj_id = payload.get("obj_id")
            reflection = payload.get("reflection")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    if not '' in item:
                        item['content'] = []
                    item['content'].append({'text': reflection})
                    self._reflect_window.insert_reflection(obj_id, reflection)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == IMAGE_REFLECTION_CMD:
            found_the_object = False
            # Receive a picture reflection and associated reflection ID
            obj_id = payload.get("obj_id")
            basename = payload.get("basename")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    if not '' in item:
                        item['content'] = []
                    item['content'].append(
                        {'image': os.path.join(self.tmp_path, basename)})
                    self._reflect_window.insert_picture(
                        obj_id, os.path.join(self.tmp_path, basename))
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == PICTURE_CMD:
            # Receive a picture (MAYBE DISPLAY IT AS IT ARRIVES?)
            basename = payload.get("basename")
            data = payload.get("data")
            utils.base64_to_file(data, os.path.join(self.tmp_path, basename))
        elif command == SHARE_CMD:
            # Joiner needs to load reflection database.
            if not self.initiating:
                # Note that pictures should be received.
                self.reflection_data = payload
                self._reflect_window.load(self.reflection_data)
                self._waiting_for_reflections = False
                self.reset_cursor()
                if self._joined_alert is not None:
                    self.remove_alert(self._joined_alert)
                    self._joined_alert = None

    def send_event(self, command, data):
        ''' Send event through the tube. '''
        if hasattr(self, 'collab') and self.collab is not None:
            data["command"] = command
            self.collab.post(data)
Exemple #17
0
class ClipArtActivity(activity.Activity):

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self._selected_image = None

        self.max_participants = 1

        toolbarbox = ToolbarBox()
        self.set_toolbar_box(toolbarbox)

        activity_button = ActivityToolbarButton(self)
        toolbarbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.save_button = ToolButton('image-save')
        self.save_button.set_tooltip(_('Save to Journal'))
        self.save_button.connect('clicked', self._save_to_journal)
        self.save_button.set_sensitive(False)
        self.save_button.show()
        toolbarbox.toolbar.insert(self.save_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbarbox.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbarbox.toolbar.insert(stop_button, -1)
        stop_button.show()

        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                   Gtk.PolicyType.AUTOMATIC)
        self.set_canvas(scrolled_window)
        scrolled_window.show()

        store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

        icon_view = Gtk.IconView.new_with_model(store)
        icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
        icon_view.connect('selection-changed', self._clipart_selected, store)
        icon_view.set_pixbuf_column(0)
        rgba = Gdk.RGBA()
        rgba.red, rgba.green, rgba.blue, rgba.alpha = 0.67, 0.67, 0.67, 1.0
        icon_view.override_background_color(Gtk.StateFlags.NORMAL, rgba)
        icon_view.grab_focus()
        scrolled_window.add(icon_view)
        icon_view.show()

        toolbarbox.show_all()
        self.show_all()

        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
        self._notify()

        GObject.idle_add(_fill_clipart_list, store)

    def _save_to_journal(self, widget):
        if self._selected_image is None:
            return

        basename = os.path.basename(self._selected_image)
        dsobject = datastore.create()
        dsobject.metadata['title'] = basename[:-4]
        dsobject.metadata['icon-color'] = profile.get_color().to_string()
        dsobject.metadata['mime_type'] = MIME_TYPES[basename.split('.')[-1]]
        dsobject.set_file_path(self._selected_image)
        datastore.write(dsobject)
        dsobject.destroy()

        self.save_button.set_sensitive(False)

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _clipart_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_image = None
            self.save_button.set_sensitive(False)
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_image = image_path
        self.save_button.set_sensitive(True)

    def _notify(self):
        alert = ErrorAlert()
        alert.props.title = _('Scanning for clipart')
        msg = _('Please wait.')
        alert.props.msg = msg

        def remove_alert(alert, response_id):
            self.get_window().set_cursor(None)
            self.remove_alert(alert)

        alert.connect('response', remove_alert)
        self.add_alert(alert)
class SpeechToolbar(Gtk.Toolbar):

    def __init__(self, activity):
        Gtk.Toolbar.__init__(self)
        self._activity = activity
        if not speech.supported:
            return
        self.is_paused = False

        self._cnf_client = GConf.Client.get_default()
        self.load_speech_parameters()

        self.sorted_voices = [i for i in speech.voices()]
        self.sorted_voices.sort(self.compare_voices)
        default = 0
        for voice in self.sorted_voices:
            if voice[0] == speech.voice[0]:
                break
            default = default + 1

        # Play button
        self.play_btn = ToggleToolButton('media-playback-start')
        self.play_btn.show()
        self.play_btn.connect('toggled', self.play_cb)
        self.insert(self.play_btn, -1)
        self.play_btn.set_tooltip(_('Play / Pause'))

        # Stop button
        self.stop_btn = ToolButton('media-playback-stop')
        self.stop_btn.show()
        self.stop_btn.connect('clicked', self.stop_cb)
        self.stop_btn.set_sensitive(False)
        self.insert(self.stop_btn, -1)
        self.stop_btn.set_tooltip(_('Stop'))

        self.voice_combo = ComboBox()
        for voice in self.sorted_voices:
            self.voice_combo.append_item(voice, voice[0])
        self.voice_combo.set_active(default)

        self.voice_combo.connect('changed', self.voice_changed_cb)
        combotool = ToolComboBox(self.voice_combo)
        self.insert(combotool, -1)
        combotool.show()
        speech.reset_buttons_cb = self.reset_buttons_cb

    def compare_voices(self,  a,  b):
        if a[0].lower() == b[0].lower():
            return 0
        if a[0] .lower() < b[0].lower():
            return -1
        if a[0] .lower() > b[0].lower():
            return 1

    def voice_changed_cb(self, combo):
        speech.voice = combo.props.value
        speech.say(speech.voice[0])
        self.save_speech_parameters()

    def load_speech_parameters(self):
        speech_parameters = {}
        data_path = os.path.join(self._activity.get_activity_root(), 'data')
        data_file_name = os.path.join(data_path, 'speech_params.json')
        if os.path.exists(data_file_name):
            f = open(data_file_name, 'r')
            try:
                speech_parameters = json.load(f)
                speech.voice = speech_parameters['voice']
            finally:
                f.close()

        self._cnf_client.add_dir('/desktop/sugar/speech',
                                 GConf.ClientPreloadType.PRELOAD_NONE)
        speech.pitch = self._cnf_client.get_int('/desktop/sugar/speech/pitch')
        speech.rate = self._cnf_client.get_int('/desktop/sugar/speech/rate')
        self._cnf_client.notify_add('/desktop/sugar/speech/pitch',
                                    self.__conf_changed_cb, None)
        self._cnf_client.notify_add('/desktop/sugar/speech/rate',
                                    self.__conf_changed_cb, None)

    def __conf_changed_cb(self, client, connection_id, entry, args):
        key = entry.get_key()
        value = client.get_int(key)
        if key == '/desktop/sugar/speech/pitch':
            speech.pitch = value
        if key == '/desktop/sugar/speech/rate':
            speech.rate = value

    def save_speech_parameters(self):
        speech_parameters = {}
        speech_parameters['voice'] = speech.voice
        data_path = os.path.join(self._activity.get_activity_root(), 'data')
        data_file_name = os.path.join(data_path, 'speech_params.json')
        f = open(data_file_name, 'w')
        try:
            json.dump(speech_parameters, f)
        finally:
            f.close()

    def reset_buttons_cb(self):
        self.play_btn.set_icon_name('media-playback-start')
        self.stop_btn.set_sensitive(False)
        self.is_paused = False

    def play_cb(self, widget):
        self.stop_btn.set_sensitive(True)
        if widget.get_active():
            self.play_btn.set_icon_name('media-playback-pause')
            if not self.is_paused:
                speech.play(self._activity._view.get_marked_words())
            else:
                speech.continue_play()
        else:
            self.play_btn.set_icon_name('media-playback-start')
            self.is_paused = True
            speech.pause()

    def stop_cb(self, widget):
        self.stop_btn.set_sensitive(False)
        self.play_btn.set_icon_name('media-playback-start')
        self.play_btn.set_active(False)
        self.is_paused = False
        speech.stop()
Exemple #19
0
class OneSupportActivity(activity.Activity):
    ''' An activity for sending bug reports '''

    def __init__(self, handle):
        ''' Initialize the toolbar '''
        try:
            super(OneSupportActivity, self).__init__(handle)
        except dbus.exceptions.DBusException as e:
            _logger.error(str(e))

        self.connect('realize', self.__realize_cb)

        if hasattr(self, 'metadata') and 'font_size' in self.metadata:
            self.font_size = int(self.metadata['font_size'])
        else:
            self.font_size = 8
        self.zoom_level = self.font_size / float(len(FONT_SIZES))
        _logger.debug('zoom level is %f' % self.zoom_level)

        # _check_gconf_settings()  # For debugging purposes

        self._setup_toolbars()
        self.modify_bg(Gtk.StateType.NORMAL,
                       style.COLOR_WHITE.get_gdk_color())

        self.bundle_path = activity.get_bundle_path()

        self._copy_entry = None
        self._paste_entry = None
        self._webkit = None
        self._clipboard_text = ''
        self._fixed = None
        self._notify_transfer_status = False

        get_power_manager().inhibit_suspend()
        self._launch_task_master()

    def can_close(self):
        get_power_manager().restore_suspend()
        return True

    def busy_cursor(self):
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def reset_cursor(self):
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.LEFT_PTR))

    def _launch_task_master(self):
        # Most things need only be done once
        if self._fixed is None:
            self._fixed = Gtk.Fixed()
            self._fixed.set_size_request(Gdk.Screen.width(),
                                         Gdk.Screen.height())

            # Offsets from the bottom of the screen
            dy1 = 3 * style.GRID_CELL_SIZE
            dy2 = 2 * style.GRID_CELL_SIZE

            self._progress_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._progress_area.set_size_request(Gdk.Screen.width(), -1)
            self._fixed.put(self._progress_area, 0, Gdk.Screen.height() - dy2)
            self._progress_area.show()

            self._button_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._button_area.set_size_request(Gdk.Screen.width(), -1)
            self._fixed.put(self._button_area, 0, Gdk.Screen.height() - dy1)
            self._button_area.show()

            self._scrolled_window = Gtk.ScrolledWindow()
            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._set_scroll_policy()
            self._graphics_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._scrolled_window.add_with_viewport(self._graphics_area)
            self._graphics_area.show()
            self._fixed.put(self._scrolled_window, 0, 0)
            self._scrolled_window.show()

            self._task_master = TaskMaster(self)
            self._task_master.show()

            Gdk.Screen.get_default().connect('size-changed',
                                             self._configure_cb)
            self._toolbox.connect('hide', self._resize_hide_cb)
            self._toolbox.connect('show', self._resize_show_cb)

            self._task_master.set_events(Gdk.EventMask.KEY_PRESS_MASK)
            self._task_master.connect('key_press_event',
                                      self._task_master.keypress_cb)
            self._task_master.set_can_focus(True)
            self._task_master.grab_focus()

        self.set_canvas(self._fixed)
        self._fixed.show()

        self._task_master.task_master()

    def reset_scrolled_window_adjustments(self):
        adj = self._scrolled_window.get_hadjustment()
        if adj is not None:
            adj.set_value(0)
        adj = self._scrolled_window.get_vadjustment()
        if adj is not None:
            adj.set_value(0)

    def load_graphics_area(self, widget):
        self._graphics_area.add(widget)

    def load_button_area(self, widget):
        self._button_area.add(widget)

    def load_progress_area(self, widget):
        self._progress_area.add(widget)

    def _load_intro_graphics(self, file_name='generic-problem.html',
                             message=None):
        center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0)
        url = os.path.join(self.bundle_path, 'html-content', file_name)
        graphics = Graphics()
        if message is None:
            graphics.add_uri('file://' + url)
        else:
            graphics.add_uri('file://' + url + '?MSG=' +
                             utils.get_safe_text(message))
        graphics.set_zoom_level(0.667)
        center_in_panel.add(graphics)
        graphics.show()
        self.set_canvas(center_in_panel)
        center_in_panel.show()

    def _resize_hide_cb(self, widget):
        self._resize_canvas(widget, True)

    def _resize_show_cb(self, widget):
        self._resize_canvas(widget, False)

    def _configure_cb(self, event):
        self._fixed.set_size_request(Gdk.Screen.width(), Gdk.Screen.height())
        self._set_scroll_policy()
        self._resize_canvas(None)
        self._task_master.reload_graphics()

    def _resize_canvas(self, widget, fullscreen=False):
        # When a toolbar is expanded or collapsed, resize the canvas
        # to ensure that the progress bar is still visible.
        if hasattr(self, '_task_master'):
            if self.toolbar_expanded():
                dy1 = 4 * style.GRID_CELL_SIZE
                dy2 = 3 * style.GRID_CELL_SIZE
            else:
                dy1 = 3 * style.GRID_CELL_SIZE
                dy2 = 2 * style.GRID_CELL_SIZE

            if fullscreen:
                dy1 -= 2 * style.GRID_CELL_SIZE
                dy2 -= 2 * style.GRID_CELL_SIZE

            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._fixed.move(self._progress_area, 0, Gdk.Screen.height() - dy2)
            self._fixed.move(self._button_area, 0, Gdk.Screen.height() - dy1)

    def get_activity_version(self):
        info_path = os.path.join(self.bundle_path, 'activity', 'activity.info')
        try:
            info_file = open(info_path, 'r')
        except Exception as e:
            _logger.error('Could not open %s: %s' % (info_path, e))
            return 'unknown'

        cp = ConfigParser()
        cp.readfp(info_file)

        section = 'Activity'

        if cp.has_option(section, 'activity_version'):
            activity_version = cp.get(section, 'activity_version')
        else:
            activity_version = 'unknown'
        return activity_version

    def get_uid(self):
        if len(self.volume_data) == 1:
            return self.volume_data[0]['uid']
        else:
            return 'unknown'

    def write_file(self, file_path):
        self.metadata['font_size'] = str(self.font_size)

    def _setup_toolbars(self):
        ''' Setup the toolbars. '''
        self.max_participants = 1  # No sharing

        self._toolbox = ToolbarBox()

        self.activity_button = ActivityToolbarButton(self)
        self.activity_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self.set_toolbar_box(self._toolbox)
        self._toolbox.show()
        self.toolbar = self._toolbox.toolbar

        view_toolbar = Gtk.Toolbar()
        self.view_toolbar_button = ToolbarButton(
            page=view_toolbar,
            label=_('View'),
            icon_name='toolbar-view')
        self.view_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.view_toolbar_button, 1)
        view_toolbar.show()
        self.view_toolbar_button.show()

        button = ToolButton('view-fullscreen')
        button.set_tooltip(_('Fullscreen'))
        button.props.accelerator = '<Alt>Return'
        view_toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self._fullscreen_cb)

        self._zoom_in = ToolButton('zoom-in')
        self._zoom_in.set_tooltip(_('Increase size'))
        view_toolbar.insert(self._zoom_in, -1)
        self._zoom_in.show()
        self._zoom_in.connect('clicked', self._zoom_in_cb)

        self._zoom_out = ToolButton('zoom-out')
        self._zoom_out.set_tooltip(_('Decrease size'))
        view_toolbar.insert(self._zoom_out, -1)
        self._zoom_out.show()
        self._zoom_out.connect('clicked', self._zoom_out_cb)

        self._zoom_eq = ToolButton('zoom-original')
        self._zoom_eq.set_tooltip(_('Restore original size'))
        view_toolbar.insert(self._zoom_eq, -1)
        self._zoom_eq.show()
        self._zoom_eq.connect('clicked', self._zoom_eq_cb)

        self._set_zoom_buttons_sensitivity()

        edit_toolbar = Gtk.Toolbar()
        self.edit_toolbar_button = ToolbarButton(
            page=edit_toolbar,
            label=_('Edit'),
            icon_name='toolbar-edit')
        self.edit_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.edit_toolbar_button, 1)
        edit_toolbar.show()
        self.edit_toolbar_button.show()

        self._copy_button = ToolButton('edit-copy')
        self._copy_button.set_tooltip(_('Copy'))
        self._copy_button.props.accelerator = '<Ctrl>C'
        edit_toolbar.insert(self._copy_button, -1)
        self._copy_button.show()
        self._copy_button.connect('clicked', self._copy_cb)
        self._copy_button.set_sensitive(False)

        self._paste_button = ToolButton('edit-paste')
        self._paste_button.set_tooltip(_('Paste'))
        self._paste_button.props.accelerator = '<Ctrl>V'
        edit_toolbar.insert(self._paste_button, -1)
        self._paste_button.show()
        self._paste_button.connect('clicked', self._paste_cb)
        self._paste_button.set_sensitive(False)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self._toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        self._toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def __realize_cb(self, window):
        self.window_xid = window.get_window().get_xid()

    def set_copy_widget(self, webkit=None, text_entry=None):
        # Each task is responsible for setting a widget for copy
        if webkit is not None:
            self._webkit = webkit
        else:
            self._webkit = None
        if text_entry is not None:
            self._copy_entry = text_entry
        else:
            self._copy_entry = None

        self._copy_button.set_sensitive(webkit is not None or
                                        text_entry is not None)

    def _copy_cb(self, button):
        if self._copy_entry is not None:
            self._copy_entry.copy_clipboard()
        elif self._webkit is not None:
            self._webkit.copy_clipboard()
        else:
            _logger.debug('No widget set for copy.')

    def set_paste_widget(self, text_entry=None):
        # Each task is responsible for setting a widget for paste
        if text_entry is not None:
            self._paste_entry = text_entry
        self._paste_button.set_sensitive(text_entry is not None)

    def _paste_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.clipboard_text = clipboard.wait_for_text()
        if self._paste_entry is not None:
            self._paste_entry.paste_clipboard()
        else:
            _logger.debug('No widget set for paste (%s).' %
                          self.clipboard_text)

    def _fullscreen_cb(self, button):
        ''' Hide the Sugar toolbars. '''
        self.fullscreen()

    def _set_zoom_buttons_sensitivity(self):
        if self.font_size < len(FONT_SIZES) - 1:
            self._zoom_in.set_sensitive(True)
        else:
            self._zoom_in.set_sensitive(False)
        if self.font_size > 0:
            self._zoom_out.set_sensitive(True)
        else:
            self._zoom_out.set_sensitive(False)

        if hasattr(self, '_scrolled_window'):
            self._set_scroll_policy()

    def _set_scroll_policy(self):
        if Gdk.Screen.width() < Gdk.Screen.height() or self.zoom_level > 0.667:
            self._scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                             Gtk.PolicyType.AUTOMATIC)
        else:
            self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                             Gtk.PolicyType.AUTOMATIC)

    def _zoom_eq_cb(self, button):
        self.font_size = 8
        self.zoom_level = 0.667
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _zoom_in_cb(self, button):
        if self.font_size < len(FONT_SIZES) - 1:
            self.font_size += 1
            self.zoom_level *= 1.1
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _zoom_out_cb(self, button):
        if self.font_size > 0:
            self.font_size -= 1
            self.zoom_level /= 1.1
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _remove_alert_cb(self, alert, response_id):
        self.remove_alert(alert)

    def _close_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            self.close()

    def _reboot_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            try:
                utils.reboot()
            except Exception as e:
                _logger.error('Cannot reboot: %s' % e)

    def _mount_added_cb(self, volume_monitor, device):
        _logger.error('mount added')
        if self.check_volume_data():
            _logger.debug('launching')
            self._launcher()

    def _mount_removed_cb(self, volume_monitor, device):
        _logger.error('mount removed')
        if self.check_volume_data():
            _logger.debug('launching')
            self._launcher()
class MemorizeToolbarBuilder(GObject.GObject):

    __gtype_name__ = 'MemoryToolbarBuilder'

    standard_game_names = ['addition',
                           'letters',
                           'sounds'
                           ]
    translated_game_names = [_('Addition'),
                             _('Letters'),
                             _('Sounds')
                             ]

    __gsignals__ = {
        'game_changed': (GObject.SignalFlags.RUN_FIRST, None,
                         5 * [GObject.TYPE_PYOBJECT])}

    def __init__(self, activity):
        GObject.GObject.__init__(self)
        self.activity = activity
        self.toolbar = self.activity.get_toolbar_box().toolbar
        self.jobject = None

        # Change demo games button
        self._demo_games = RadioMenuButton(icon_name='memorize-collection')
        self._demo_games.props.tooltip = _('Load demo games')

        for i, game in enumerate(self.translated_game_names):
            menu_item = MenuItem(game, icon_name=self.standard_game_names[i])
            menu_item.connect('activate', self.__activate_game_cb, i)
            self._demo_games.props.palette.menu.append(menu_item)
            menu_item.show()

        self.toolbar.insert(self._demo_games, -1)

        if USE_ART4APPS:
            for language in art4apps_data.get_languages():
                language_description = art4apps_data.get_language_name(
                    language)
                lang_menu_item = MenuItem(language_description,
                                          'activity-start')
                self._demo_games.props.palette.menu.append(lang_menu_item)
                lang_menu_item.show()
                categories_menu = Gtk.Menu()
                # create a submenu with the categories
                categories = {}
                for category in art4apps_data.get_categories():
                    if category in ['letters', 'other']:
                        continue
                    translated_category = art4apps_data.get_translation(
                        category, language)
                    if translated_category is not None:
                        label = translated_category
                    else:
                        label = category
                    categories[label] = category

                for label in sorted(categories):
                    cat_menu_item = MenuItem(label)
                    cat_menu_item.connect('activate',
                                          self.__activate_art4apps_game_cb,
                                          categories[label], language)
                    categories_menu.append(cat_menu_item)
                    cat_menu_item.show()
                lang_menu_item.set_submenu(categories_menu)

        self._selected_game = self.standard_game_names[0]

        # Change size combobox
        self._size_combo = RadioMenuButton(icon_name='change_size')
        self._size_combo.props.tooltip = _('Change size')
        self._sizes = ['4 X 4', '5 X 5', '6 X 6']

        for i, f in enumerate(self._sizes):
            menu_item = MenuItem(f, icon_name=self._sizes[i])
            menu_item.connect('activate', self._game_size_cb, i)
            self._size_combo.props.palette.menu.append(menu_item)
            menu_item.show()
        self.toolbar.insert(self._size_combo, -1)
        self._selected_game_size = self._sizes[0][0]

        # Reset Button
        self._restart_button = ToolButton('game-new')
        self._restart_button.connect('clicked', self._game_reset_cb)
        self._restart_button.set_tooltip(_('Restart Game'))
        self.toolbar.insert(self._restart_button, -1)
        self._restart_button.show()

    def _game_reset_cb(self, widget):
        self.activity.game.model.count = 0
        self.emit('game_changed', None, None, 'reset', None, None)

    def update_controls(self, active):
        self._size_combo.set_sensitive(active)
        self._demo_games.set_sensitive(active)
        self._restart_button.set_sensitive(active)

    def _game_size_cb(self, widget, i):
        self._selected_game_size = int(self._sizes[i][0])
        self._size_combo.props.icon_name = self._sizes[i]
        self.emit('game_changed', None, self._selected_game_size, 'size', None,
                  None)

    def __activate_art4apps_game_cb(self, menu, category, language):
        self._art4apps_data = (category, language)
        if self.activity.game.model.is_demo:
            self._change_art4apps_game(category, language)
        else:
            alert = Alert()
            alert.props.title = _('Discard your modified game?')
            icon = Icon(icon_name='dialog-ok')
            alert.add_button(1, _('Discard'), icon)
            icon = Icon(icon_name='dialog-cancel')
            alert.add_button(0, _('Do not discard'), icon)
            alert.connect('response', self._change_art4apps_game_alert_cb)
            self.activity.add_alert(alert)

    def _change_art4apps_game_alert_cb(self, alert, response_id):
        if alert is not None:
            self.activity.remove_alert(alert)
        category, language = self._art4apps_data
        logging.error('Selected art4apps category %s language %s',
                      category, language)
        self._change_art4apps_game(category, language)

    def _change_art4apps_game(self, category, language):
        game_size = int(self._selected_game_size)
        self.emit('game_changed', category, game_size, 'art4apps', language,
                  None)

    def __activate_game_cb(self, menu, i):
        self._game_selected_index = i
        if self.activity.game.model.is_demo:
            self._change_game()
        else:
            alert = Alert()
            alert.props.title = _('Discard your modified game?')
            icon = Icon(icon_name='dialog-ok')
            alert.add_button(1, _('Discard'), icon)
            icon = Icon(icon_name='dialog-cancel')
            alert.add_button(0, _('Do not discard'), icon)
            alert.connect('response', self._change_game_alert_cb)
            self.activity.add_alert(alert)

    def _change_game_alert_cb(self, alert, response_id):
        if alert is not None:
            self.activity.remove_alert(alert)
        if response_id == 1:
            self._change_game()

    def _change_game(self):
        title = self.translated_game_names[self._game_selected_index]
        game_size = int(self._selected_game_size)

        game_name = self.standard_game_names[self._game_selected_index]
        self._demo_games.props.icon_name = game_name
        game_file = join(dirname(__file__), 'demos', game_name + '.zip')
        self.emit('game_changed', game_file, game_size, 'demo', title, None)

    def reset(self, widget):
        self._demo_games.props.icon_name = 'memorize-collection'

    def update_toolbar(self, widget, data, grid):
        size = data.get('size')
        self._size_combo.props.icon_name = size + ' X ' + size
class InkToolBar(Gtk.Toolbar):

    # Constructor
    def __init__(self, slideviewer, deck):

        Gtk.Toolbar.__init__(self)

        self.__slideviewer = slideviewer
        self.__cur_color = slideviewer.get_color()
        self.__cur_color_str = "blue"
        self.__cur_pen = slideviewer.get_pen()
        self.__deck = deck
        self.__deck.connect('slide-redraw', self.update_buttons)
        self.__slideviewer.connect('undo-redo-changed', self.update_buttons)
        self.__is_instr = False

        # Red Ink
        self.__red = Gtk.RadioToolButton()
        self.__red.set_icon_name('red-button')
        self.insert(self.__red, -1)
        self.__red.show()
        #self.__red.set_tooltip('Red Ink')
        self.__red.connect('clicked', self.set_ink_color, 1.0, 0.0, 0.0, "red")

        # Green Ink
        self.__green = Gtk.RadioToolButton(group=self.__red)
        self.__green.set_icon_name('green-button')
        self.insert(self.__green, -1)
        self.__green.show()
        #self.__green.set_tooltip('Green Ink')
        self.__green.connect(
            'clicked',
            self.set_ink_color,
            0.0,
            1.0,
            0.0,
            "green")

        # Blue Ink
        self.__blue = Gtk.RadioToolButton(group=self.__red)
        self.__blue.set_icon_name('blue-button')
        self.insert(self.__blue, -1)
        self.__blue.show()
        #self.__blue.set_tooltip('Blue Ink')
        self.__blue.connect(
            'clicked',
            self.set_ink_color,
            0.0,
            0.0,
            1.0,
            "blue")

        # Black Ink
        self.__black = Gtk.RadioToolButton(group=self.__red)
        self.__black.set_icon_name('black-button')
        self.insert(self.__black, -1)
        self.__black.show()
        #self.__black.set_tooltip('Black Ink')
        self.__black.connect(
            'clicked',
            self.set_ink_color,
            0.0,
            0.0,
            0.0,
            "black")

        # Separate ink from untensils
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(False)
        self.insert(separator, -1)
        separator.show()

        # Pencil
        self.__pencil = Gtk.RadioToolButton()
        self.__pencil.set_icon_name('tool-pencil')
        self.insert(self.__pencil, -1)
        self.__pencil.show()
        # self.__pencil.set_tooltip('Pencil')
        self.__pencil.connect('clicked', self.set_cur_pen, 4)

        # Brush
        self.__brush = Gtk.RadioToolButton(self.__pencil)
        self.__brush.set_icon_name('tool-brush')
        self.insert(self.__brush, -1)
        self.__brush.show()
        # self.__brush.set_tooltip('Brush')
        self.__brush.connect('clicked', self.set_cur_pen, 8)

        # Erase
        self.__erase = ToolButton('tool-eraser')
        self.insert(self.__erase, -1)
        self.__erase.show()
        self.__erase.set_tooltip('Erase All Ink')
        self.__erase.connect('clicked', self.set_erase)

        """
        # Text
        self.__text = ToolButton('text')
        self.insert(self.__text, -1)
        self.__text.show()
        self.__text.set_tooltip('Text')
        """

        # Separate tools from text
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(False)
        self.insert(separator, -1)
        separator.show()

        # Undo
        self.__undo = ToolButton('edit-undo')
        self.insert(self.__undo, -1)
        self.__undo.show()
        self.__undo.set_tooltip('Undo')
        self.__undo.connect('clicked', self.undo)

        # Redo
        self.__redo = ToolButton('edit-redo')
        self.insert(self.__redo, -1)
        self.__redo.show()
        self.__redo.set_tooltip('Redo')
        self.__redo.connect('clicked', self.redo)

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(False)
        separator.set_expand(True)
        self.insert(separator, -1)
        separator.show()

        # FIXME (though actually not a terrible icon)
        self.__submit = ToolButton('dialog-ok')
        self.insert(self.__submit, -1)
        self.__submit.show()
        self.__submit.set_tooltip('Broadcast Submission')
        self.__submit.connect('clicked', self.submit_ink)

        self.__deck.connect('instr_state_propagate', self.instructor_state_cb)

        self.set_tool_buttons()
        self.show()

    def instructor_state_cb(self, widget, is_instr):
        self.__is_instr = is_instr
        if is_instr:
            self.__submit.set_tooltip('Broadcast Submission')
        else:
            self.__submit.set_tooltip('Submit Ink')

    def set_cur_pen(self, widget, size):
        self.__slideviewer.set_pen(size)

    def set_ink_color(self, widget, r, g, b, color):
        self.__slideviewer.set_color(r, g, b)

    def set_erase(self, widget):
        self.__slideviewer.clear_ink()

    def set_tool_buttons(self):
        if self.__cur_color == (1.0, 0.0, 0.0):
            self.__red.set_active(True)
        elif self.__cur_color == (0.0, 1.0, 0.0):
            self.__green.set_active(True)
        elif self.__cur_color == (0.0, 0.0, 1.0):
            self.__blue.set_active(True)
        else:
            self.__black.set_active(True)

        if self.__cur_pen == 2:
            self.__pencil.set_active(True)
        elif self.__cur_pen == 5:
            self.__brush.set_active(True)

    def submit_ink(self, widget):
        if self.__is_instr:
            self.broadcast_ink()
        else:
            self.__submit.set_sensitive(False)
            self.__timer = threading.Timer(3.0, self.reenable_submissions)
            self.__timer.start()
            self.__slideviewer.submit_ink()

    def broadcast_ink(self):
        self.__slideviewer.broadcast_ink()

    def reenable_submissions(self):
        self.__submit.set_sensitive(True)
        self.__submit.queue_draw()

    def undo(self, widget):
        self.__slideviewer.undo()

    def redo(self, widget):
        self.__slideviewer.redo()

    def update_buttons(self, widget=None):
        can_undo, can_redo = self.__slideviewer.can_undo_redo()
        self.__undo.set_sensitive(can_undo)
        self.__redo.set_sensitive(can_redo)
        if self.__is_instr:
            if self.__deck.getActiveSubmission() == -1:
                self.__submit.set_sensitive(False)
            else:
                self.__submit.set_sensitive(True)
class GameToolbar(Gtk.Toolbar):

    __gtype_name__ = 'GameToolbar'

    __gsignals__ = {
        'game-restart': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []),
        'ai-activated': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []),
        'ai-deactivated': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []),
        'game-board-size': (
            GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [GObject.TYPE_INT]),
    }

    def __init__(self, activity):
        Gtk.Toolbar.__init__(self)
        self.activity = activity

        # Reset Button
        restart_icon = join(dirname(__file__), 'images', 'gtk-refresh.svg')
        restart_image = Gtk.Image()
        restart_image.set_from_file(restart_icon)
        self._restart_button = ToolButton()
        self._restart_button.set_icon_widget(restart_image)
        self._restart_button.connect('clicked', self._game_restart_cb)
        self._restart_button.set_tooltip(_('Restart Game'))
        self.insert(self._restart_button, -1)
        self._restart_button.show()

        # Separator
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        self.insert(separator, -1)

        self._add_widget(Gtk.Label(_('Board size') + ': '))
        # Change size combobox
        self._size_combo = ToolComboBox()
        self._sizes = ['19 X 19', '13 X 13', '9 X 9']

        for i, f in enumerate(self._sizes):
            self._size_combo.combo.append_item(i, f)

        self._size_combo.combo.connect('changed', self._game_size_cb)
        self._add_widget(self._size_combo)
        self._size_combo.combo.set_active(0)

        # Separator
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        self.insert(separator, -1)

        # Artificial Intelligence Button
        self._ai_button = Gtk.ToggleToolButton()

        if search_for_gnugo():
            self._ai_button.connect('toggled', self._ai_toggled_cb)
            self._ai_button.set_label(_('Play against PlayGo!'))

        else:
            self._ai_button.set_label(
                _('You need to install gnugo to play against PlayGo'))
            self._ai_button.set_sensitive(False)

        self.insert(self._ai_button, -1)
        self._ai_button.show()

    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)
        tool_item.add(widget)
        widget.show()
        self.insert(tool_item, -1)
        tool_item.show()

    def _game_restart_cb(self, widget):
        self._size_combo.set_sensitive(True)
        self.emit('game-restart')

    def grey_out_restart(self):
        self._restart_button.set_sensitive(False)

    def _game_size_cb(self, widget):
        game_size = int(self._sizes[self._size_combo.combo.get_active()][:2])
        self.emit('game-board-size', game_size)

    def grey_out_size_change(self):
        self._size_combo.set_sensitive(False)

    def update_toolbar(self, widget, data, grid):
        size = data.get('size')
        self._size_combo.combo.handler_block(self.size_handle_id)
        size_index = self._sizes.index(size + ' X ' + size)
        self._size_combo.combo.set_active(int(size_index))
        self._size_combo.combo.handler_unblock(self.size_handle_id)

    def _ai_toggled_cb(self, widget):
        if widget.get_active():
            self.emit('ai-activated')

        else:
            self.emit('ai-deactivated')

    def grey_out_ai(self):
        self._ai_button.set_sensitive(False)

    def set_ai_button_state(self, value):
        self._ai_button.set_active(value)
class ViewToolbar(Gtk.Toolbar):
    def __init__(self, activity):
        GObject.GObject.__init__(self)

        self._browser = None

        self._activity = activity
        self._activity.tray.connect('unmap', self.__unmap_cb)
        self._activity.tray.connect('map', self.__map_cb)

        self.zoomout = ToolButton('zoom-out')
        self.zoomout.set_tooltip(_('Zoom out'))
        self.zoomout.connect('clicked', self.__zoomout_clicked_cb)
        self.insert(self.zoomout, -1)
        self.zoomout.show()

        self.zoomin = ToolButton('zoom-in')
        self.zoomin.set_tooltip(_('Zoom in'))
        self.zoomin.connect('clicked', self.__zoomin_clicked_cb)
        self.insert(self.zoomin, -1)
        self.zoomin.show()

        self.separator = Gtk.SeparatorToolItem()
        self.separator.set_draw(True)
        self.insert(self.separator, -1)
        self.separator.show()

        self.fullscreen = ToolButton('view-fullscreen')
        self.fullscreen.set_tooltip(_('Fullscreen'))
        self.fullscreen.connect('clicked', self.__fullscreen_clicked_cb)
        self.insert(self.fullscreen, -1)
        self.fullscreen.show()

        self.traybutton = ToolButton('tray-hide')
        self.traybutton.connect('clicked', self.__tray_clicked_cb)
        self.traybutton.props.sensitive = False
        self.insert(self.traybutton, -1)
        self.traybutton.show()

        tabbed_view = self._activity.get_canvas()

        if tabbed_view.get_n_pages():
            self._connect_to_browser(tabbed_view.props.current_browser)

        tabbed_view.connect_after('switch-page', self.__switch_page_cb)

    def __switch_page_cb(self, tabbed_view, page, page_num):
        self._connect_to_browser(tabbed_view.props.current_browser)

    def _connect_to_browser(self, browser):
        self._browser = browser
        self._update_zoom_buttons()

    def _update_zoom_buttons(self):
        is_webkit_browser = isinstance(self._browser, Browser)
        self.zoomin.set_sensitive(is_webkit_browser)
        self.zoomout.set_sensitive(is_webkit_browser)

    def __zoomin_clicked_cb(self, button):
        tabbed_view = self._activity.get_canvas()
        tabbed_view.props.current_browser.zoom_in()

    def __zoomout_clicked_cb(self, button):
        tabbed_view = self._activity.get_canvas()
        tabbed_view.props.current_browser.zoom_out()

    def __fullscreen_clicked_cb(self, button):
        self._activity.fullscreen()

    def __tray_clicked_cb(self, button):
        if self._activity.tray.props.visible is False:
            self._activity.tray.show()
        else:
            self._activity.tray.hide()

    def __map_cb(self, tray):
        if len(self._activity.tray.get_children()) > 0:
            self.tray_set_hide()

    def __unmap_cb(self, tray):
        if len(self._activity.tray.get_children()) > 0:
            self.tray_set_show()

    def tray_set_show(self):
        self.traybutton.set_icon('tray-show')
        self.traybutton.set_tooltip(_('Show Tray'))

    def tray_set_hide(self):
        self.traybutton.set_icon('tray-hide')
        self.traybutton.set_tooltip(_('Hide Tray'))
Exemple #24
0
    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        color = ColorToolButton('color')
        color.connect('notify::color', self.__color_notify_cb)
        toolbar_box.toolbar.insert(color, -1)
        color.show()

        random = ToggleToolButton('colorRandom')
        random.set_tooltip(_('Toggle random color'))
        toolbar_box.toolbar.insert(random, -1)
        random.set_active(True)
        random.connect('toggled', self.__random_toggled_cb)
        random.show()

        color.random = random
        random.color = color

        random.timeout_id = GLib.timeout_add(100, self.__timeout_cb, random)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box
Exemple #25
0
class ImageViewerActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self._object_id = handle.object_id
        self._collab = collabwrapper.CollabWrapper(self)
        self._collab.incoming_file.connect(self.__incoming_file_cb)
        self._collab.buddy_joined.connect(self.__buddy_joined_cb)
        self._collab.joined.connect(self.__joined_cb)
        self._needs_file = False  # Set to true when we join

        # Status of temp file used for write_file:
        self._tempfile = None
        self._close_requested = False
        self._want_document = True

        self._zoom_out_button = None
        self._zoom_in_button = None
        self.previous_image_button = None
        self.next_image_button = None

        self.scrolled_window = Gtk.ScrolledWindow()
        self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS,
                                        Gtk.PolicyType.ALWAYS)
        # disable sharing until a file is opened
        self.max_participants = 1

        # Don't use the default kinetic scrolling, let the view do the
        # drag-by-touch and pinch-to-zoom logic.
        self.scrolled_window.set_kinetic_scrolling(False)

        self.view = ImageView.ImageViewer()

        # Connect to the touch signal for performing drag-by-touch.
        self.view.add_events(Gdk.EventMask.TOUCH_MASK)
        self._touch_hid = self.view.connect('touch-event',
                                            self.__touch_event_cb)
        self.scrolled_window.add(self.view)
        self.view.show()

        self.connect('key-press-event', self.__key_press_cb)

        if GESTURES_AVAILABLE:
            # Connect to the zoom signals for performing
            # pinch-to-zoom.
            zoom_controller = SugarGestures.ZoomController()
            zoom_controller.attach(self,
                                   SugarGestures.EventControllerFlags.NONE)

            zoom_controller.connect('began', self.__zoomtouch_began_cb)
            zoom_controller.connect('scale-changed',
                                    self.__zoomtouch_changed_cb)
            zoom_controller.connect('ended', self.__zoomtouch_ended_cb)

        self._progress_alert = None

        toolbar_box = ToolbarBox()
        self._add_toolbar_buttons(toolbar_box)
        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()

        if self._object_id is None or not self._jobject.file_path:
            empty_widgets = Gtk.EventBox()
            empty_widgets.modify_bg(Gtk.StateType.NORMAL,
                                    style.COLOR_WHITE.get_gdk_color())

            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            mvbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            vbox.pack_start(mvbox, True, False, 0)

            image_icon = Icon(pixel_size=style.LARGE_ICON_SIZE,
                              icon_name='imageviewer',
                              stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
                              fill_color=style.COLOR_TRANSPARENT.get_svg())
            mvbox.pack_start(image_icon, False, False, style.DEFAULT_PADDING)

            label = Gtk.Label(
                '<span foreground="%s"><b>%s</b></span>' %
                (style.COLOR_BUTTON_GREY.get_html(), _('No image')))
            label.set_use_markup(True)
            mvbox.pack_start(label, False, False, style.DEFAULT_PADDING)

            hbox = Gtk.Box()
            open_image_btn = Gtk.Button()
            open_image_btn.connect('clicked', self._show_picker_cb)
            add_image = Gtk.Image.new_from_stock(Gtk.STOCK_ADD,
                                                 Gtk.IconSize.BUTTON)
            buttonbox = Gtk.Box()
            buttonbox.pack_start(add_image, False, True, 0)
            buttonbox.pack_end(Gtk.Label(_('Choose an image')), True, True, 5)
            open_image_btn.add(buttonbox)
            hbox.pack_start(open_image_btn, True, False, 0)
            mvbox.pack_start(hbox, False, False, style.DEFAULT_PADDING)

            empty_widgets.add(vbox)
            empty_widgets.show_all()
            self.set_canvas(empty_widgets)
        else:
            self.set_canvas(self.scrolled_window)
            self.scrolled_window.show()

        Gdk.Screen.get_default().connect('size-changed', self._configure_cb)
        self._collab.setup()

    def __touch_event_cb(self, widget, event):
        coords = event.get_coords()
        if event.type == Gdk.EventType.TOUCH_BEGIN:
            self.view.start_dragtouch(coords)
        elif event.type == Gdk.EventType.TOUCH_UPDATE:
            self.view.update_dragtouch(coords)
        elif event.type == Gdk.EventType.TOUCH_END:
            self.view.finish_dragtouch(coords)

    def __zoomtouch_began_cb(self, controller):
        self.view.start_zoomtouch(controller.get_center())

        # Don't listen to touch signals until pinch-to-zoom ends.
        self.view.disconnect(self._touch_hid)

    def __zoomtouch_changed_cb(self, controller, scale):
        self.view.update_zoomtouch(controller.get_center(), scale)

    def __zoomtouch_ended_cb(self, controller):
        self.view.finish_zoomtouch()
        self._touch_hid = self.view.connect('touch-event',
                                            self.__touch_event_cb)

    def __key_press_cb(self, widget, event):
        key_name = Gdk.keyval_name(event.keyval)
        if key_name == "Left":
            self._change_image(-1)
        elif key_name == "Right":
            self._change_image(1)
        elif event.get_state() & Gdk.ModifierType.CONTROL_MASK:
            if key_name == "q":
                self.close()
        return True

    def _get_image_list(self):
        value = mime.GENERIC_TYPE_IMAGE
        mime_types = mime.get_generic_type(value).mime_types
        (self.image_list,
         self.image_count) = datastore.find({'mime_type': mime_types})

    def _add_toolbar_buttons(self, toolbar_box):
        self._seps = []

        self.activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self._zoom_out_button = ToolButton('zoom-out')
        self._zoom_out_button.set_tooltip(_('Zoom out'))
        self._zoom_out_button.connect('clicked', self.__zoom_out_cb)
        toolbar_box.toolbar.insert(self._zoom_out_button, -1)
        self._zoom_out_button.show()

        self._zoom_in_button = ToolButton('zoom-in')
        self._zoom_in_button.set_tooltip(_('Zoom in'))
        self._zoom_in_button.connect('clicked', self.__zoom_in_cb)
        toolbar_box.toolbar.insert(self._zoom_in_button, -1)
        self._zoom_in_button.show()

        zoom_tofit_button = ToolButton('zoom-best-fit')
        zoom_tofit_button.set_tooltip(_('Fit to window'))
        zoom_tofit_button.connect('clicked', self.__zoom_tofit_cb)
        toolbar_box.toolbar.insert(zoom_tofit_button, -1)
        zoom_tofit_button.show()

        zoom_original_button = ToolButton('zoom-original')
        zoom_original_button.set_tooltip(_('Original size'))
        zoom_original_button.connect('clicked', self.__zoom_original_cb)
        toolbar_box.toolbar.insert(zoom_original_button, -1)
        zoom_original_button.show()

        fullscreen_button = ToolButton('view-fullscreen')
        fullscreen_button.set_tooltip(_('Fullscreen'))
        fullscreen_button.connect('clicked', self.__fullscreen_cb)
        toolbar_box.toolbar.insert(fullscreen_button, -1)
        fullscreen_button.show()

        self._seps.append(Gtk.SeparatorToolItem())
        toolbar_box.toolbar.insert(self._seps[-1], -1)
        self._seps[-1].show()

        rotate_anticlockwise_button = ToolButton('rotate_anticlockwise')
        rotate_anticlockwise_button.set_tooltip(_('Rotate anticlockwise'))
        rotate_anticlockwise_button.connect('clicked',
                                            self.__rotate_anticlockwise_cb)
        toolbar_box.toolbar.insert(rotate_anticlockwise_button, -1)
        rotate_anticlockwise_button.show()

        rotate_clockwise_button = ToolButton('rotate_clockwise')
        rotate_clockwise_button.set_tooltip(_('Rotate clockwise'))
        rotate_clockwise_button.connect('clicked', self.__rotate_clockwise_cb)
        toolbar_box.toolbar.insert(rotate_clockwise_button, -1)
        rotate_clockwise_button.show()

        if self._object_id is None:
            self._seps.append(Gtk.SeparatorToolItem())
            toolbar_box.toolbar.insert(self._seps[-1], -1)
            self._seps[-1].show()

            self.previous_image_button = ToolButton('go-previous-paired')
            self.previous_image_button.set_tooltip(_('Previous Image'))
            self.previous_image_button.props.sensitive = False
            self.previous_image_button.connect('clicked',
                                               self.__previous_image_cb)
            toolbar_box.toolbar.insert(self.previous_image_button, -1)
            self.previous_image_button.show()

            self.next_image_button = ToolButton('go-next-paired')
            self.next_image_button.set_tooltip(_('Next Image'))
            self.next_image_button.props.sensitive = False
            self.next_image_button.connect('clicked', self.__next_image_cb)
            toolbar_box.toolbar.insert(self.next_image_button, -1)
            self.next_image_button.show()

            GObject.idle_add(self._get_image_list)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _configure_cb(self, event=None):
        if Gdk.Screen.width() <= style.GRID_CELL_SIZE * 12:
            for sep in self._seps:
                sep.hide()
        else:
            for sep in self._seps:
                sep.show()

    def _update_zoom_buttons(self):
        self._zoom_in_button.set_sensitive(self.view.can_zoom_in())
        self._zoom_out_button.set_sensitive(self.view.can_zoom_out())

    def _change_image(self, delta):
        # boundary conditions
        if self.current_image_index == 0 and delta == -1:
            return
        if self.current_image_index == self.image_count - 1 and delta == 1:
            return

        self.current_image_index += delta
        self.make_button_sensitive()
        jobject = self.image_list[self.current_image_index]
        self._object_id = jobject.object_id
        if os.path.exists(self._tempfile):
            os.remove(self._tempfile)
        self.read_file(jobject.file_path)

    def __previous_image_cb(self, button):
        if self.current_image_index > 0:
            self._change_image(-1)

    def __next_image_cb(self, button):
        if self.current_image_index < self.image_count:
            self._change_image(1)

    def __zoom_in_cb(self, button):
        self.view.zoom_in()
        self._update_zoom_buttons()

    def __zoom_out_cb(self, button):
        self.view.zoom_out()
        self._update_zoom_buttons()

    def __zoom_tofit_cb(self, button):
        self.view.zoom_to_fit()
        self._update_zoom_buttons()

    def __zoom_original_cb(self, button):
        self.view.zoom_original()
        self._update_zoom_buttons()

    def __rotate_anticlockwise_cb(self, button):
        self.view.rotate_anticlockwise()

    def __rotate_clockwise_cb(self, button):
        self.view.rotate_clockwise()

    def __fullscreen_cb(self, button):
        self.fullscreen()

    def get_current_image_index(self):
        for image in self.image_list:
            if image.object_id == self._object_id:
                jobject = image
                break

        self.current_image_index = self.image_list.index(jobject)

    def make_button_sensitive(self):
        if self.image_count <= 1:
            return

        if self.current_image_index == 0:
            self.next_image_button.props.sensitive = True
            self.previous_image_button.props.sensitive = False
        elif self.current_image_index == self.image_count - 1:
            self.previous_image_button.props.sensitive = True
            self.next_image_button.props.sensitive = False
        else:
            self.next_image_button.props.sensitive = True
            self.previous_image_button.props.sensitive = True

    def _show_picker_cb(self, button):
        if not self._want_document:
            return

        try:
            chooser = ObjectChooser(self,
                                    what_filter='Image',
                                    filter_type=FILTER_TYPE_GENERIC_MIME,
                                    show_preview=True)
        except:
            # for compatibility with older versions
            chooser = ObjectChooser(self._activity, what_filter='Image')

        try:
            result = chooser.run()
            if result == Gtk.ResponseType.ACCEPT:
                jobject = chooser.get_selected_object()
                if jobject and jobject.file_path:
                    self._object_id = jobject.object_id
                    self.read_file(jobject.file_path)
                    self.set_canvas(self.scrolled_window)
                    self.scrolled_window.show()
        finally:
            self.get_current_image_index()
            self.make_button_sensitive()
            chooser.destroy()
            del chooser

    def read_file(self, file_path):
        if self._object_id is None or self.shared_activity:
            # read_file is call because the canvas is visible
            # but we need check if is not the case of empty file
            return

        self._want_document = False
        # enable collaboration
        self.activity_button.page.share.props.sensitive = True

        tempfile = os.path.join(self.get_activity_root(), 'instance',
                                'tmp%i' % time.time())

        os.link(file_path, tempfile)
        self._tempfile = tempfile

        self.view.set_file_location(tempfile)

        zoom = self.metadata.get('zoom', None)
        if zoom is not None:
            self.view.set_zoom(float(zoom))

    def write_file(self, file_path):
        if self._tempfile:
            self.metadata['activity'] = self.get_bundle_id()
            self.metadata['zoom'] = str(self.view.get_zoom())
            if self._close_requested:
                os.link(self._tempfile, file_path)
                os.unlink(self._tempfile)
                self._tempfile = None
        else:
            raise NotImplementedError

    def can_close(self):
        self._close_requested = True
        return True

    def __incoming_file_cb(self, collab, file, desc):
        logging.debug('__incoming_file_cb with need %r', self._needs_file)
        if not self._needs_file:
            return

        self._progress_alert = ProgressAlert()
        self._progress_alert.props.title = _('Receiving image...')
        self.add_alert(self._progress_alert)

        self._needs_file = False
        file_path = os.path.join(self.get_activity_root(), 'instance',
                                 '%i' % time.time())
        file.connect('notify::state', self.__file_notify_state_cb)
        file.connect('notify::transfered_bytes',
                     self.__file_transfered_bytes_cb)
        file.accept_to_file(file_path)

    def __file_notify_state_cb(self, file, pspec):
        logging.debug('__file_notify_state %r', file.props.state)
        if file.props.state != collabwrapper.FT_STATE_COMPLETED:
            return

        file_path = file.props.output
        logging.debug("Saving file %s to datastore...", file_path)
        self._jobject.file_path = file_path
        datastore.write(self._jobject, transfer_ownership=True)

        if self._progress_alert is not None:
            self.remove_alert(self._progress_alert)
            self._progress_alert = None

        GObject.idle_add(self.__set_file_idle_cb, self._jobject.object_id)

    def __set_file_idle_cb(self, object_id):
        dsobj = datastore.get(object_id)
        self._tempfile = dsobj.file_path
        """ This method is used when join a collaboration session """
        self.view.set_file_location(self._tempfile)
        try:
            zoom = int(self.metadata.get('zoom', '0'))
            if zoom > 0:
                self.view.set_zoom(zoom)
        except Exception:
            pass
        self.set_canvas(self.scrolled_window)
        self.scrolled_window.show_all()
        return False

    def __file_transfered_bytes_cb(self, file, pspec):
        total = file.file_size
        bytes_downloaded = file.props.transfered_bytes
        fraction = bytes_downloaded / total
        self._progress_alert.set_fraction(fraction)

    def __buddy_joined_cb(self, collab, buddy):
        logging.debug('__buddy_joined_cb %r', buddy)
        self._collab.send_file_file(buddy, self._tempfile, None)

    def __joined_cb(self, collab):
        logging.debug('I joined!')
        # Somebody will send us a file, just wait
        self._needs_file = True

    def _alert(self, title, text=None):
        alert = NotifyAlert(timeout=5)
        alert.props.title = title
        alert.props.msg = text
        self.add_alert(alert)
        alert.connect('response', self._alert_cancel_cb)
        alert.show()

    def _alert_cancel_cb(self, alert, response_id):
        self.remove_alert(alert)
class EditToolbar(Gtk.Toolbar):

    def __init__(self, pc, toolbar_box):
        Gtk.Toolbar.__init__(self)

        self._abiword_canvas = pc.abiword_canvas

        copy = CopyButton()
        copy.props.accelerator = '<Ctrl>C'
        copy.connect('clicked', lambda button: pc.abiword_canvas.copy())
        self.insert(copy, -1)
        copy.show()

        paste = PasteButton()
        paste.props.accelerator = '<Ctrl>V'
        paste.connect('clicked', lambda button: pc.abiword_canvas.paste())
        self.insert(paste, -1)
        paste.show()

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        undo = UndoButton(sensitive=False)
        undo.connect('clicked', lambda button: pc.abiword_canvas.undo())
        pc.abiword_canvas.connect("can-undo", lambda abi, can_undo:
                undo.set_sensitive(can_undo))
        self.insert(undo, -1)
        undo.show()

        redo = RedoButton(sensitive=False)
        redo.connect('clicked', lambda button: pc.abiword_canvas.redo())
        pc.abiword_canvas.connect("can-redo", lambda abi, can_redo:
                redo.set_sensitive(can_redo))
        self.insert(redo, -1)
        redo.show()

        pc.abiword_canvas.connect('text-selected', lambda abi, b:
                copy.set_sensitive(True))
        pc.abiword_canvas.connect('image-selected', lambda abi, b:
                copy.set_sensitive(True))
        pc.abiword_canvas.connect('selection-cleared', lambda abi, b:
                copy.set_sensitive(False))

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        search_label = Gtk.Label(_("Search") + ": ")
        search_label.show()
        search_item_page_label = Gtk.ToolItem()
        search_item_page_label.add(search_label)
        self.insert(search_item_page_label, -1)
        search_item_page_label.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()
        self._add_widget(self._search_entry, expand=True)

        self._findprev = ToolButton('go-previous-paired')
        self._findprev.set_tooltip(_('Find previous'))
        self.insert(self._findprev, -1)
        self._findprev.show()
        self._findprev.connect('clicked', self._findprev_cb)

        self._findnext = ToolButton('go-next-paired')
        self._findnext.set_tooltip(_('Find next'))
        self.insert(self._findnext, -1)
        self._findnext.show()
        self._findnext.connect('clicked', self._findnext_cb)

        # set the initial state of the search controls
        # note: we won't simple call self._search_entry_changed_cb
        # here, as that will call into the abiword_canvas, which
        # is not mapped on screen here, causing the set_find_string
        # call to fail
        self._findprev.set_sensitive(False)
        self._findnext.set_sensitive(False)

    def _search_entry_activated_cb(self, entry):
        logger.debug('_search_entry_activated_cb')
        if not self._search_entry.props.text:
            return

        # find the next entry
        self._abiword_canvas.find_next(False)

    def _search_entry_changed_cb(self, entry):
        logger.debug('_search_entry_changed_cb search for \'%s\'',
                self._search_entry.props.text)

        if not self._search_entry.props.text:
            self._search_entry.activate()
            # set the button contexts
            self._findprev.set_sensitive(False)
            self._findnext.set_sensitive(False)
            return

        self._abiword_canvas.set_find_string(self._search_entry.props.text)

        # set the button contexts
        self._findprev.set_sensitive(True)
        self._findnext.set_sensitive(True)

        # immediately start seaching
        self._abiword_canvas.find_next(True)

    def _findprev_cb(self, button):
        logger.debug('_findprev_cb')
        if self._search_entry.props.text:
            self._abiword_canvas.find_prev()
        else:
            logger.debug('nothing to search for!')

    def _findnext_cb(self, button):
        logger.debug('_findnext_cb')
        if self._search_entry.props.text:
            self._abiword_canvas.find_next(False)
        else:
            logger.debug('nothing to search for!')

    # bad foddex! this function was copied from sugar's activity.py
    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)

        tool_item.add(widget)
        widget.show()

        self.insert(tool_item, -1)
        tool_item.show()
class CreateCardPanel(Gtk.EventBox):
    __gsignals__ = {
        'add-pair': (GObject.SignalFlags.RUN_FIRST, None,
                     10 * [GObject.TYPE_PYOBJECT]),
        'update-pair': (GObject.SignalFlags.RUN_FIRST, None,
                        8 * [GObject.TYPE_PYOBJECT]),
        'change-font': (GObject.SignalFlags.RUN_FIRST, None,
                        2 * [GObject.TYPE_PYOBJECT]),
        'pair-closed': (GObject.SignalFlags.RUN_FIRST,
                        None, []),
    }

    def __init__(self, game):
        def make_label(icon_name, label):
            label_box = Gtk.VBox()
            icon = Icon(icon_name=icon_name,
                        pixel_size=style.LARGE_ICON_SIZE)
            label_box.pack_start(icon, False, False, 0)
            label = Gtk.Label(label=label)
            label.modify_fg(Gtk.StateType.NORMAL,
                            style.COLOR_TOOLBAR_GREY.get_gdk_color())
            label_box.pack_start(label, True, True, 0)
            label_box.show_all()
            return label_box

        Gtk.EventBox.__init__(self)
        self._game = game
        self.equal_pairs = False
        self._updatebutton_sensitive = False
        self._card1_has_sound = False
        self._card2_has_sound = False

        # save buttons
        self._portrait = Gdk.Screen.width() < Gdk.Screen.height()

        if self._portrait:
            buttons_bar_orientation = Gtk.Orientation.HORIZONTAL
        else:
            buttons_bar_orientation = Gtk.Orientation.VERTICAL

        self._buttons_bar = Gtk.Box(orientation=buttons_bar_orientation)

        self._buttons_bar.props.border_width = 10
        self._buttons_bar.set_valign(Gtk.Align.CENTER)
        self._buttons_bar.set_halign(Gtk.Align.CENTER)

        self._addbutton = ToolButton(tooltip=_('Add as new pair'),
                                     sensitive=False)
        self._addbutton.set_icon_widget(
            make_label('pair-add', ' ' + _('Add')))
        self._addbutton.connect('clicked', self.emit_add_pair)
        self._buttons_bar.pack_start(self._addbutton, False, False, 0)

        self._updatebutton = ToolButton(tooltip=_('Update selected pair'),
                                        sensitive=False)
        self._updatebutton.set_icon_widget(
            make_label('pair-update', ' ' + _('Update')))
        self._updatebutton.connect('clicked', self.emit_update_pair)
        self._buttons_bar.pack_start(self._updatebutton, False, False, 0)

        self._removebutton = ToolButton(tooltip=_('Remove selected pair'),
                                        sensitive=False)
        self._removebutton.set_icon_widget(
            make_label('remove', ' ' + _('Remove')))
        self._removebutton.connect('clicked', self.emit_close)
        self._buttons_bar.pack_start(self._removebutton, False, False, 0)

        # Set card editors

        self.cardeditor1 = CardEditor(self._game, 1)
        self.cardeditor2 = CardEditor(self._game, 2)
        self.clean(None)
        self.cardeditor1.connect('has-text', self.receive_text_signals)
        self.cardeditor2.connect('has-text', self.receive_text_signals)
        self.cardeditor1.connect('has-picture', self.receive_picture_signals)
        self.cardeditor2.connect('has-picture', self.receive_picture_signals)
        self.cardeditor1.connect('has-sound', self.receive_sound_signals)
        self.cardeditor2.connect('has-sound', self.receive_sound_signals)
        self.cardeditor1.connect('change-font', self.receive_font_signals)
        self.cardeditor2.connect('change-font', self.receive_font_signals)

        # edit panel

        self.card_box = Gtk.HBox()
        self.card_box.set_homogeneous(True)
        self.cardeditor1.set_halign(Gtk.Align.CENTER)
        self.cardeditor1.set_valign(Gtk.Align.CENTER)
        self.cardeditor2.set_halign(Gtk.Align.CENTER)
        self.cardeditor2.set_valign(Gtk.Align.CENTER)
        self.card_box.pack_start(self.cardeditor1, True, True, 0)
        self.card_box.pack_start(self.cardeditor2, True, True, 0)

        if self._portrait:
            main_box_orientation = Gtk.Orientation.VERTICAL
        else:
            main_box_orientation = Gtk.Orientation.HORIZONTAL
        self._main_box = Gtk.Box(orientation=main_box_orientation)
        self._main_box.pack_start(self.card_box, True, True, 0)
        self._main_box.pack_start(self._buttons_bar, True, True, 0)
        self.add(self._main_box)
        self.connect('size-allocate', self._allocate_cb)

        self.show_all()

    def _allocate_cb(self, widget, allocation):
        GLib.idle_add(self.update_orientation)

    def update_orientation(self):
        self._portrait = Gdk.Screen.width() < Gdk.Screen.height()
        if self._portrait:
            self._buttons_bar.props.orientation = Gtk.Orientation.HORIZONTAL
            self._main_box.props.orientation = Gtk.Orientation.VERTICAL
        else:
            self._buttons_bar.props.orientation = Gtk.Orientation.VERTICAL
            self._main_box.props.orientation = Gtk.Orientation.HORIZONTAL

    def update_font_combos(self, widget, data, grid):
        if 'font_name1' in data:
            self.cardeditor1.set_font_name(data['font_name1'])
            self.cardeditor1.card.change_font(data['font_name1'])
        if 'font_name2' in data:
            self.cardeditor2.set_font_name(data['font_name2'])
            self.cardeditor2.card.change_font(data['font_name2'])

    def emit_close(self, widget):
        self.emit('pair-closed')

    def emit_add_pair(self, widget):
        self._addbutton.set_sensitive(False)
        if self.equal_pairs:
            self.emit('add-pair', self.cardeditor1.get_text(),
                      self.cardeditor1.get_text(),
                      self.cardeditor1.get_image_path(),
                      self.cardeditor1.get_image_path(),
                      self.cardeditor1.get_snd(),
                      self.cardeditor1.get_snd(),
                      self.cardeditor1.get_speak(),
                      self.cardeditor1.get_speak(),
                      self.cardeditor1.get_font_name(),
                      self.cardeditor1.get_font_name())
        else:
            self.emit('add-pair', self.cardeditor1.get_text(),
                      self.cardeditor2.get_text(),
                      self.cardeditor1.get_image_path(),
                      self.cardeditor2.get_image_path(),
                      self.cardeditor1.get_snd(),
                      self.cardeditor2.get_snd(),
                      self.cardeditor1.get_speak(),
                      self.cardeditor2.get_speak(),
                      self.cardeditor1.get_font_name(),
                      self.cardeditor2.get_font_name())
        self.clean(None)

    def emit_update_pair(self, widget):
        self._addbutton.set_sensitive(False)
        if self.equal_pairs:
            self.emit('update-pair', self.cardeditor1.get_text(),
                      self.cardeditor1.get_text(),
                      self.cardeditor1.get_image_path(),
                      self.cardeditor1.get_image_path(),
                      self.cardeditor1.get_snd(),
                      self.cardeditor1.get_snd(),
                      self.cardeditor1.get_speak(),
                      self.cardeditor1.get_speak())
        else:
            self.emit('update-pair', self.cardeditor1.get_text(),
                      self.cardeditor2.get_text(),
                      self.cardeditor1.get_image_path(),
                      self.cardeditor2.get_image_path(),
                      self.cardeditor1.get_snd(),
                      self.cardeditor2.get_snd(),
                      self.cardeditor1.get_speak(),
                      self.cardeditor2.get_speak())
        self.clean(None)

    def pair_selected(self, widget, selected, newtext1, newtext2,
                      aimage_path, bimage_path, asnd, bsnd, aspeak, bspeak):
        if selected:
            self.cardeditor1.set_text(newtext1)
            self.cardeditor2.set_text(newtext2)
            self.cardeditor1.set_image_path(aimage_path)
            self.cardeditor2.set_image_path(bimage_path)
            self.cardeditor1.set_snd(asnd)
            self.cardeditor2.set_snd(bsnd)
            self.cardeditor1.set_speak(aspeak)
            self.cardeditor2.set_speak(bspeak)
            self._addbutton.set_sensitive(True)
        self._updatebutton.set_sensitive(selected)
        self._updatebutton_sensitive = selected
        self._removebutton.set_sensitive(selected)

    def change_equal_pairs(self, widget, state):
        self.equal_pairs = state
        self.clean(None)

        if self.equal_pairs:
            if self.cardeditor2.get_parent():
                self.card_box.remove(self.cardeditor2)
        else:
            if not self.cardeditor2.get_parent():
                self.card_box.pack_start(self.cardeditor2, True, True, 0)

    def clean(self, widget):
        self.cardeditor1.clean()
        self.cardeditor2.clean()
        self._addbutton.set_sensitive(False)
        self._card1_has_text = False
        self._card2_has_text = False
        self._card1_has_picture = False
        self._card2_has_picture = False

    def receive_text_signals(self, widget, has_text):
        if widget == self.cardeditor1:
            self._card1_has_text = has_text
        if widget == self.cardeditor2:
            self._card2_has_text = has_text
        self._update_buttom_status()

    def receive_picture_signals(self, widget, has_picture):
        if widget == self.cardeditor1:
            self._card1_has_picture = has_picture
        if widget == self.cardeditor2:
            self._card2_has_picture = has_picture
        self._update_buttom_status()

    def receive_sound_signals(self, widget, has_sound):
        if widget == self.cardeditor1:
            self._card1_has_sound = has_sound
        if widget == self.cardeditor2:
            self._card2_has_sound = has_sound
        self._update_buttom_status()

    def receive_font_signals(self, widget, font_name):
        if self.equal_pairs:
            self.emit('change-font', 1, font_name)
            self.emit('change-font', 2, font_name)
        else:
            if widget == self.cardeditor1:
                self.emit('change-font', 1, font_name)
            if widget == self.cardeditor2:
                self.emit('change-font', 2, font_name)

    def _update_buttom_status(self):
        if not self.equal_pairs:
            if (self._card1_has_text or self._card1_has_picture or
                self._card1_has_sound) and (self._card2_has_text or
                                            self._card2_has_picture or
                                            self._card2_has_sound):
                self._addbutton.set_sensitive(True)
                self._updatebutton.set_sensitive(self._updatebutton_sensitive)
            else:
                self._addbutton.set_sensitive(False)
                self._updatebutton.set_sensitive(False)
        else:
            if (self._card1_has_text or self._card1_has_picture or
                    self._card1_has_sound):
                self._addbutton.set_sensitive(True)
                self._updatebutton.set_sensitive(self._updatebutton_sensitive)
            else:
                self._addbutton.set_sensitive(False)
                self._updatebutton.set_sensitive(False)

    def set_temp_folder(self, temp_folder):
        self.cardeditor1.temp_folder = temp_folder
        self.cardeditor2.temp_folder = temp_folder
class Finance(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.set_title(_("Finance"))
        self.max_participants = 1

        # Initialize database.
        # data
        #   next_id
        #   transactions
        #     id, name, type, amount, date, category
        #   budgets
        #     category, period, amount, budget
        self.data = {'next_id': 0, 'transactions': [], 'budgets': {}}

        self.transaction_map = {}
        self.visible_transactions = []

        self.undo_transaction_map = []
        self.undo_id_map = []

        self.redo_id_map = []
        self.redo_transaction_map = []

        self.transaction_names = {}
        self.category_names = {}

        # Initialize view period to the first of the month.
        self.period = MONTH
        self.period_start = self.get_this_period()

        # Create screens.
        self.register = registerscreen.RegisterScreen(self)
        self.chart = chartscreen.ChartScreen(self)
        self.budget = budgetscreen.BudgetScreen(self)

        self.build_toolbox()

        self.screenbox = Gtk.VBox()

        self.headerbox = self.build_header()
        self._active_panel = None

        # Add the summary data.

        self.startlabel = Gtk.Label()
        self.startamountlabel = Gtk.Label()
        self.creditslabel = Gtk.Label()
        self.debitslabel = Gtk.Label()
        self.balancelabel = Gtk.Label()
        self.balancelabel.props.margin_left = style.DEFAULT_SPACING
        self.balancelabel.props.margin_right = style.DEFAULT_SPACING

        font_size = int(style.FONT_SIZE * 1.25)
        font = Pango.FontDescription("Sans %d" % font_size)
        for label in (self.startlabel, self.startamountlabel,
                      self.creditslabel, self.debitslabel):
            label.modify_font(font)
            label.set_hexpand(True)
            label.set_halign(Gtk.Align.START)
            label.props.margin = style.DEFAULT_PADDING

        self.balance_evbox = Gtk.EventBox()
        self.balance_evbox.add(self.balancelabel)
        summarybox = Gtk.Grid()
        summarybox.attach(self.startlabel, 0, 0, 1, 1)
        summarybox.attach(self.startamountlabel, 0, 1, 1, 1)
        summarybox.attach(self.creditslabel, 1, 0, 1, 1)
        summarybox.attach(self.debitslabel, 1, 1, 1, 1)
        summarybox.attach(self.balance_evbox, 2, 0, 1, 2)
        self.balance_evbox.set_halign(Gtk.Align.END)

        summary_evbox = Gtk.EventBox()
        summary_evbox.add(summarybox)
        summary_evbox.modify_bg(Gtk.StateType.NORMAL,
                                style.Color('#666666').get_gdk_color())
        summary_evbox.set_size_request(-1, style.GRID_CELL_SIZE)

        vbox = Gtk.VBox()

        vbox.pack_start(self.headerbox, False, False, 0)
        vbox.pack_start(self.screenbox, True, True, 0)
        vbox.pack_start(summary_evbox, False, False, 0)

        # Start with the empty screen.
        self.empty_panel = emptypanel.create_empty_panel(
            'row-insert-credit', _('Add some credit or debit to get started!'),
            _('Add credit'), self.__empty_panel_btn_cb)

        self._set_internal_panel(self.empty_panel)

        # This has to happen last, because it calls the read_file
        # method when restoring from the Journal.
        self.set_canvas(vbox)

        self.show_all()
        self.show_header_controls()

        if os.getenv('FINANCE_TEST'):
            self.create_test_data()
            self._set_internal_panel(self.register)

    def build_toolbox(self):

        view_tool_group = None
        registerbtn = RadioToolButton()
        registerbtn.props.icon_name = 'view-list'
        registerbtn.props.label = _('Register')
        registerbtn.set_tooltip(_("Register"))
        registerbtn.props.group = view_tool_group
        view_tool_group = registerbtn
        registerbtn.props.accelerator = '<Ctrl>1'
        registerbtn.connect('clicked', self.register_cb)

        budgetbtn = RadioToolButton()
        budgetbtn.props.icon_name = 'budget'
        budgetbtn.props.label = _('Budget')
        budgetbtn.set_tooltip(_("Budget"))
        budgetbtn.props.group = view_tool_group
        budgetbtn.props.accelerator = '<Ctrl>2'
        budgetbtn.connect('clicked', self.budget_cb)

        chartbtn = RadioToolButton()
        chartbtn.props.icon_name = 'chart'
        chartbtn.props.label = _('Chart')
        chartbtn.set_tooltip(_("Chart"))
        chartbtn.props.group = view_tool_group
        chartbtn.props.accelerator = '<Ctrl>3'
        chartbtn.connect('clicked', self.chart_cb)

        helpbutton = self._create_help_button()
        helpbutton.show_all()

        self.toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        self.toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.toolbar_box.toolbar.insert(registerbtn, -1)
        self.toolbar_box.toolbar.insert(budgetbtn, -1)
        self.toolbar_box.toolbar.insert(chartbtn, -1)

        self.toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.toolbar_box.toolbar.insert(helpbutton, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.toolbar_box.toolbar.insert(separator, -1)

        self.toolbar_box.toolbar.insert(StopButton(self), -1)
        self.set_toolbar_box(self.toolbar_box)

        activity_button.page.insert(self._create_export_button(), -1)

        self.toolbar_box.show_all()

    def _create_export_button(self):
        # Add expoprt button
        export_data = ToolButton('save-as-data')

        export_data.props.tooltip = _('Export data')
        export_data.props.hide_tooltip_on_click = False
        export_data.palette_invoker.props.toggle_palette = True
        export_data.show()

        menu_box = PaletteMenuBox()
        export_data.props.palette.set_content(menu_box)

        menu_item = PaletteMenuItem(text_label=_('Export credits by day'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'credit',
                          DAY)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export debits by day'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'debit',
                          DAY)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export credits by month'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'credit',
                          MONTH)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export debits by month'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'debit',
                          MONTH)
        menu_box.append_item(menu_item)

        menu_box.show_all()
        return export_data

    def _create_help_button(self):
        helpitem = HelpButton()
        helpitem.add_section(_('Register'), icon='view-list')
        helpitem.add_paragraph(registerscreen.REGISTER_HELP)
        helpitem.add_section(_('Budget'), icon='budget')
        helpitem.add_paragraph(budgetscreen.BUDGET_HELP)
        helpitem.add_section(_('Chart'), icon='chart')
        helpitem.add_paragraph(chartscreen.CHART_HELP)
        return helpitem

    def build_header(self):
        # Add the header.
        self.periodlabel = Gtk.Label()
        self.periodlabel.set_padding(10, 0)
        self._period_label_item = Gtk.ToolItem()
        self._period_label_item.add(self.periodlabel)
        self.periodlabel.set_halign(Gtk.Align.END)
        self._period_label_item.set_size_request(style.GRID_CELL_SIZE * 5, -1)

        headerbox = Gtk.Toolbar()
        headerbox.modify_bg(Gtk.StateType.NORMAL,
                            style.Color('#424242').get_gdk_color())
        headerbox.set_size_request(-1, style.GRID_CELL_SIZE)

        # register buttons
        self.newcreditbtn = ToolButton('row-insert-credit')
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newcreditbtn.props.accelerator = '<Ctrl>A'
        self.newcreditbtn.connect('clicked', self.__newcredit_cb)

        self.newdebitbtn = ToolButton('row-insert-debit')
        self.newdebitbtn.set_tooltip(_("New Debit"))
        self.newdebitbtn.props.accelerator = '<Ctrl>D'
        self.newdebitbtn.connect('clicked', self.__newdebit_cb)

        self.undoactionbtn = UndoButton()
        self.undoactionbtn.connect('clicked', self.__undoaction_cb)

        self.redoactionbtn = RedoButton()
        self.redoactionbtn.connect('clicked', self.__redoaction_cb)

        self.eraseitembtn = ToolButton('basket')
        self.eraseitembtn.set_tooltip(_("Erase Transaction"))
        self.eraseitembtn.props.accelerator = '<Ctrl>E'
        self.eraseitembtn.connect('clicked', self.__eraseitem_cb)

        headerbox.insert(self.newcreditbtn, -1)
        headerbox.insert(self.newdebitbtn, -1)
        headerbox.insert(self.eraseitembtn, -1)
        headerbox.insert(self.undoactionbtn, -1)
        headerbox.insert(self.redoactionbtn, -1)

        self.header_separator_visible = Gtk.SeparatorToolItem()
        headerbox.insert(self.header_separator_visible, -1)

        self.export_image = ToolButton('save-as-image')
        self.export_image.set_tooltip(_("Save as Image"))
        self.export_image.connect('clicked', self.__save_image_cb)
        headerbox.insert(self.export_image, -1)

        self._header_separator = Gtk.SeparatorToolItem()
        self._header_separator.props.draw = False
        self._header_separator.set_expand(True)
        headerbox.insert(self._header_separator, -1)

        # period controls
        self.thisperiodbtn = ToolButton('go-down')
        self.thisperiodbtn.props.accelerator = '<Ctrl>Down'
        self.thisperiodbtn.connect('clicked', self.thisperiod_cb)

        self.prevperiodbtn = ToolButton('go-previous-paired')
        self.prevperiodbtn.props.accelerator = '<Ctrl>Left'
        self.prevperiodbtn.connect('clicked', self.prevperiod_cb)

        self.nextperiodbtn = ToolButton('go-next-paired')
        self.nextperiodbtn.props.accelerator = '<Ctrl>Right'
        self.nextperiodbtn.connect('clicked', self.nextperiod_cb)

        period_options = {
            DAY: _('Day'),
            WEEK: _('Week'),
            MONTH: _('Month'),
            YEAR: _('Year'),
            FOREVER: _('Forever')
        }
        periodcombo = FilterToolItem('calendar', MONTH, period_options,
                                     _('Select period'))

        periodcombo.connect('changed', self.__period_changed_cb)

        headerbox.insert(periodcombo, -1)
        headerbox.insert(self.prevperiodbtn, -1)
        headerbox.insert(self.nextperiodbtn, -1)
        headerbox.insert(self.thisperiodbtn, -1)

        headerbox.insert(self._period_label_item, -1)
        return headerbox

    def show_header_controls(self):
        for child in self.headerbox.get_children():
            child.show()
            if self._active_panel in (self.register, self.empty_panel):
                if child in (self.header_separator_visible, self.export_image):
                    child.hide()
            elif self._active_panel == self.budget:
                if child in (self.newcreditbtn, self.newdebitbtn,
                             self.eraseitembtn, self.undoactionbtn,
                             self.redoactionbtn, self.header_separator_visible,
                             self.export_image):
                    child.hide()
            elif self._active_panel == self.chart:
                # Use NOT here
                if child not in (self.newcreditbtn, self.newdebitbtn,
                                 self.header_separator_visible,
                                 self.export_image):
                    child.hide()

    def register_cb(self, widget):
        self._set_internal_panel(self.register)
        self.show_header_controls()
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newdebitbtn.set_tooltip(_("New Debit"))

    def budget_cb(self, widget):
        self._set_internal_panel(self.budget)
        self.show_header_controls()

    def chart_cb(self, widget):
        self._set_internal_panel(self.chart)
        self.show_header_controls()
        self.newcreditbtn.set_tooltip(_("Show Credits"))
        self.newdebitbtn.set_tooltip(_("Show Debits"))

    def _set_internal_panel(self, widget):
        if self.screenbox.get_children():
            self.screenbox.remove(self.screenbox.get_children()[0])
        self.screenbox.pack_start(widget, True, True, 0)
        widget.show_all()
        self._active_panel = widget
        self.build_screen()

    def build_screen(self):
        self.build_visible_transactions()

        if hasattr(self._active_panel, 'build'):
            self._active_panel.build()

        self.update_header()
        self.update_summary()
        self.update_toolbar()

    def __empty_panel_btn_cb(self, button):
        self._set_internal_panel(self.register)
        self.register.new_credit()

    def __newcredit_cb(self, widget):
        if self._active_panel == self.chart:
            # in the case of chart, select the graphic
            self.chart.set_mode(self.chart.CHART_CREDIT)
            return

        # this check is used when the emptypanel is visible
        if self._active_panel != self.register:
            self._set_internal_panel(self.register)
        self.register.new_credit()

    def __newdebit_cb(self, widget):
        if self._active_panel == self.chart:
            # in the case of chart, select the graphic
            self.chart.set_mode(self.chart.CHART_DEBIT)
            return

        # this check is used when the emptypanel is visible
        if self._active_panel != self.register:
            self._set_internal_panel(self.register)
        self.register.new_debit()

    def __undoaction_cb(self, widget):
        self.undo_transaction()
        self.build_screen()

    def __redoaction_cb(self, widget):
        self.redo_transaction()
        self.build_screen()

    def __eraseitem_cb(self, widget):
        self.register.erase_item()
        self.build_screen()

    def update_header(self):
        if self.period == DAY:
            # TRANS: representation of the "Day" period
            text = self.period_start.strftime(_("%B %d, %Y"))

        elif self.period == WEEK:
            # TRANS: representation of the "Week of" period
            text = _('Week of') + self.period_start.strftime(_(" %B %d, %Y"))

        elif self.period == MONTH:
            # TRANS: representation of the "Month" period
            text = self.period_start.strftime(_("%B, %Y"))

        elif self.period == YEAR:
            # TRANS: representation of the "Year" period
            text = self.period_start.strftime(_("%Y"))

        elif self.period == FOREVER:
            text = _('Forever')

        self.periodlabel.set_markup("<span size='xx-large' color='white'><b>" +
                                    text + "</b></span>")

    def update_summary(self):
        # Calculate starting balance.
        start = 0.0
        for t in self.data['transactions']:
            d = t['date']
            if d < self.period_start.toordinal():
                if t['type'] == 'credit':
                    start += t['amount']
                else:
                    start -= t['amount']

        # Calculate totals for this period.
        credit_count = 0
        credit_total = 0.0
        debit_count = 0
        debit_total = 0.0
        total = start
        for t in self.visible_transactions:
            if t['type'] == 'credit':
                credit_count += 1
                credit_total += t['amount']
                total += t['amount']
            else:
                debit_count += 1
                debit_total += t['amount']
                total -= t['amount']

        # Update Balance.
        if total >= 0.0:
            balancecolor = colors.CREDIT_COLOR
        else:
            balancecolor = colors.DEBIT_COLOR
        balance = \
            "<span size='xx-large' foreground='white'><b>%s %s</b></span>" % \
            (_('Balance: '), locale.currency(total))
        self.balancelabel.set_markup(balance)

        self.balance_evbox.modify_bg(Gtk.StateType.NORMAL,
                                     style.Color(balancecolor).get_gdk_color())

        self.startlabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            _('Starting Balance:'))
        self.startamountlabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            locale.currency(start))

        self.creditslabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            (_('%(credit_total)s in %(credit_count)d credits') % {
                'credit_total': locale.currency(credit_total),
                'credit_count': credit_count
            }))

        self.debitslabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            (_('%(debit_total)s in %(debit_count)d debits') % {
                'debit_total': locale.currency(debit_total),
                'debit_count': debit_count
            }))

    def update_toolbar(self):
        # Disable the navigation when Forever is selected.
        next_prev = self.period != FOREVER
        self.prevperiodbtn.set_sensitive(next_prev)
        self.thisperiodbtn.set_sensitive(next_prev)
        self.nextperiodbtn.set_sensitive(next_prev)

        # This is a HACK to translate the string properly
        # http://bugs.sugarlabs.org/ticket/3190
        if self.period == MONTH:
            text_previous_period = _('Previous Month')
            text_this_period = _('This Month')
            text_next_period = _('Next Month')
        elif self.period == WEEK:
            text_previous_period = _('Previous Week')
            text_this_period = _('This Week')
            text_next_period = _('Next Week')
        elif self.period == DAY:
            text_previous_period = _('Previous Day')
            text_this_period = _('This Day')
            text_next_period = _('Next Day')
        elif self.period == YEAR:
            text_previous_period = _('Previous Year')
            text_this_period = _('This Year')
            text_next_period = _('Next Year')

        if self.period != FOREVER:
            self.prevperiodbtn.set_tooltip(text_previous_period)
            self.thisperiodbtn.set_tooltip(text_this_period)
            self.nextperiodbtn.set_tooltip(text_next_period)

    # Update the label self.period to reflect the period.
    def get_this_period(self):
        today = datetime.date.today()

        if self.period == DAY:
            return today

        elif self.period == WEEK:
            while today.weekday() != 0:
                today -= datetime.timedelta(days=1)
            return today

        elif self.period == MONTH:
            return datetime.date(today.year, today.month, 1)

        elif self.period == YEAR:
            return datetime.date(today.year, 1, 1)

        elif self.period == FOREVER:
            return datetime.date(1900, 1, 1)

    def get_next_period(self, start):
        if self.period == DAY:
            return start + datetime.timedelta(days=1)

        elif self.period == WEEK:
            return start + datetime.timedelta(days=7)

        elif self.period == MONTH:
            if start.month == 12:
                return datetime.date(start.year + 1, 1, 1)
            else:
                return datetime.date(start.year, start.month + 1, 1)

        elif self.period == YEAR:
            return datetime.date(start.year + 1, 1, 1)

    def get_prev_period(self, start):
        if self.period == DAY:
            return start - datetime.timedelta(days=1)

        elif self.period == WEEK:
            return start - datetime.timedelta(days=7)

        elif self.period == MONTH:
            if start.month == 1:
                return datetime.date(start.year - 1, 12, 1)
            else:
                return datetime.date(start.year, start.month - 1, 1)

        elif self.period == YEAR:
            return datetime.date(start.year - 1, 1, 1)

    def thisperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_this_period()
            self.build_screen()

    def nextperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_next_period(self.period_start)
            self.build_screen()

    def prevperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_prev_period(self.period_start)
            self.build_screen()

    def __period_changed_cb(self, widget, value):
        self.period = int(value)
        self.update_toolbar()

        # Jump to 'this period'.
        self.period_start = self.get_this_period()
        self.build_screen()

    def build_visible_transactions(self):
        self.build_undo_buttons()

        if self.period == FOREVER:
            self.visible_transactions = self.data['transactions']

        else:
            period_start_ord = self.period_start.toordinal()
            period_end_ord = self.get_next_period(
                self.period_start).toordinal()

            self.visible_transactions = []
            for t in self.data['transactions']:
                d = t['date']
                if d >= period_start_ord and d < period_end_ord:
                    self.visible_transactions.append(t)

        self.visible_transactions.sort(key=lambda a: a['date'])

    def build_transaction_map(self):
        self.transaction_map = {}
        for t in self.data['transactions']:
            self.transaction_map[t['id']] = t

    def create_transaction(self,
                           name='',
                           type='debit',
                           amount=0,
                           category='',
                           date=datetime.date.today()):
        id = self.data['next_id']
        self.data['next_id'] += 1

        t = {
            'id': id,
            'name': name,
            'type': type,
            'amount': amount,
            'date': date.toordinal(),
            'category': category
        }
        self.data['transactions'].append(t)
        self.transaction_map[id] = t

        self.undo_id_map.append(id)
        self.undo_transaction_map.append('Erase')
        self.redo_transaction_map = []
        self.redo_id_map = []

        self.build_visible_transactions()

        return id

    def destroy_transaction(self, id):
        t = self.transaction_map[id]
        self.data['transactions'].remove(t)
        del self.transaction_map[id]

    def undo_redo_action(self, id, t, isin=False):
        # if we're updating the transaction
        if t == 'Erase':
            self.destroy_transaction(id)
        elif isin:
            for i in range(len(self.data['transactions'])):
                if id == self.data['transactions'][i]['id']:
                    self.data['transactions'][i] = t
                    break
        else:
            # Have to insert it back into the right position
            flag = 1
            for i in range(len(self.data['transactions'])):
                if id < self.data['transactions'][i]['id']:
                    self.data['transactions'].insert(i, t)
                    flag = 0
                    break
            if flag:
                self.data['transactions'].append(t)

    def undo_transaction(self):
        if len(self.undo_transaction_map) == 0:
            return False

        id = self.undo_id_map.pop()
        t = self.undo_transaction_map.pop()

        self.redo_id_map.append(id)
        isin = False
        if id in self.transaction_map.keys():
            self.redo_transaction_map.append(
                copy.deepcopy(self.transaction_map[id]))
            isin = True
        else:
            self.redo_transaction_map.append('Erase')

        copy_t = copy.deepcopy(t)
        self.undo_redo_action(id, copy_t, isin)

        if t != 'Erase':
            self.transaction_map[id] = copy_t
        return True

    def redo_transaction(self):
        if len(self.redo_transaction_map) == 0:
            return False

        id = self.redo_id_map.pop()
        t = self.redo_transaction_map.pop()

        self.undo_id_map.append(id)
        isin = False
        if id in self.transaction_map.keys():
            self.undo_transaction_map.append(
                copy.deepcopy(self.transaction_map[id]))
            isin = True
        else:
            self.undo_transaction_map.append('Erase')

        self.undo_redo_action(id, t, isin)

        if t != 'Erase':
            self.transaction_map[id] = copy.deepcopy(t)
        return True

    def build_undo_buttons(self):
        if len(self.undo_transaction_map) == 0:
            self.undoactionbtn.set_sensitive(False)
        else:
            self.undoactionbtn.set_sensitive(True)
        if len(self.redo_transaction_map) == 0:
            self.redoactionbtn.set_sensitive(False)
        else:
            self.redoactionbtn.set_sensitive(True)

    def build_names(self):
        self.transaction_names = {}
        self.category_names = {}
        for t in self.data['transactions']:
            self.transaction_names[t['name']] = 1
            self.category_names[t['category']] = 1

    def create_test_data(self):
        cur_date = datetime.date.today()
        cur_date = datetime.date(cur_date.year, cur_date.month, 1)
        self.create_transaction('Initial Balance',
                                type='credit',
                                amount=632,
                                category='Initial Balance',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Fix Car',
                                amount=75.84,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Adopt Cat',
                                amount=100,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('New Coat',
                                amount=25.53,
                                category='Clothing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        self.build_transaction_map()
        self.build_names()

    def read_file(self, file_path):
        if self.metadata['mime_type'] != 'text/plain':
            return

        fd = open(file_path, 'r')
        try:
            text = fd.read()
            self.data = json.loads(text)
        finally:
            fd.close()

        if self.data['transactions']:
            self._set_internal_panel(self.register)
        self.show_header_controls()

        self.build_transaction_map()
        self.build_names()
        self.build_screen()

    def write_file(self, file_path):
        if not self.metadata['mime_type']:
            self.metadata['mime_type'] = 'text/plain'

        fd = open(file_path, 'w')
        try:
            text = json.dumps(self.data)
            fd.write(text)
        finally:
            fd.close()

    def __save_image_cb(self, widget):
        image_file = tempfile.NamedTemporaryFile(mode='w+b', suffix='.png')
        journal_entry = datastore.create()
        journal_entry.metadata['title'] = self.chart.title
        journal_entry.metadata['keep'] = '0'
        journal_entry.metadata['mime_type'] = 'image/png'

        # generate the image
        self.chart.generate_image(image_file.file, 800, 600)
        image_file.file.close()
        journal_entry.file_path = image_file.name

        # generate the preview
        preview_str = io.BytesIO()
        self.chart.generate_image(preview_str, activity.PREVIEW_SIZE[0],
                                  activity.PREVIEW_SIZE[1])
        journal_entry.metadata['preview'] = dbus.ByteArray(
            preview_str.getvalue())

        logging.debug('Create %s image file', image_file.name)
        datastore.write(journal_entry)
        self._show_journal_alert(_('Chart created'), _('Open in the Journal'),
                                 journal_entry.object_id)

    def _show_journal_alert(self, title, msg, object_id):
        open_alert = Alert()
        open_alert.props.title = title
        open_alert.props.msg = msg
        open_icon = Icon(icon_name='zoom-activity')
        open_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'),
                              open_icon)
        open_icon.show()
        ok_icon = Icon(icon_name='dialog-ok')
        open_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon)
        ok_icon.show()
        # Remove other alerts
        for alert in self._alerts:
            self.remove_alert(alert)

        self.add_alert(open_alert)
        open_alert.connect('response', self.__open_response_cb, object_id)
        open_alert.show()

    def __open_response_cb(self, alert, response_id, object_id):
        if response_id is Gtk.ResponseType.APPLY:
            activity.show_object_in_journal(object_id)
        self.remove_alert(alert)

    def __export_data_to_chart_cb(self, widget, type_movement, period):
        """
        type_movement = 'debit' or 'credit'
        period
            DAY = 0
            MONTH = 2
        """
        logging.debug('export data %s %s', type_movement, period)

        chart_params = {}
        axis = {
            'tickFont': 'Sans',
            'labelFont': 'Sans',
            'labelFontSize': 14,
            'labelColor': '#666666',
            'lineColor': '#b3b3b3',
            'tickColor': '#000000',
            'tickFontSize': 12
        }
        chart_params['font_options'] = {
            'titleFont': 'Sans',
            'titleFontSize': 12,
            'titleColor': '#000000',
            'axis': axis
        }

        if type_movement == 'credit':
            what_filter = 'Credits'
        elif type_movement == 'debit':
            what_filter = 'Debits'
        else:
            logging.debug('ERROR type_movement should be credit or debit')

        if period == DAY:
            when = 'day'
        elif period == MONTH:
            when = 'month'
        else:
            logging.debug('ERROR period should be DAY or MONTH')

        title = _('%(what_filter)s by %(when)s') % {
            'what_filter': what_filter,
            'when': when
        }
        chart_params['title'] = title
        chart_params['x_label'] = ''
        chart_params['y_label'] = ''
        chart_params['current_chart.type'] = 1
        xo_color = profile.get_color()
        chart_params['chart_line_color'] = xo_color.get_stroke_color()
        chart_params['chart_color'] = xo_color.get_fill_color()
        """
        'chart_data': [
            ['hello', 200.0],
            ['mrch', 100.0]],
        """
        transactions = self.data['transactions']

        groups = {}
        for transaction in transactions:
            if transaction['type'] == type_movement:
                date = transaction['date']
                if period == DAY:
                    group = date
                elif period == MONTH:
                    d = datetime.date.fromordinal(date)
                    group = datetime.date(d.year, d.month, 1).toordinal()
                if group in list(groups.keys()):
                    groups[group] = groups[group] + transaction['amount']
                else:
                    groups[group] = transaction['amount']

        data = []
        for group in sorted(groups.keys()):
            if period == DAY:
                label = datetime.date.fromordinal(group).isoformat()
            elif period == MONTH:
                d = datetime.date.fromordinal(group)
                label = '%s-%s' % (d.year, d.month)

            data.append([label, groups[group]])

        chart_params['chart_data'] = data

        logging.debug('chart_data %s', chart_params)

        # save to the journal
        data_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json')
        journal_entry = datastore.create()
        journal_entry.metadata['title'] = title
        journal_entry.metadata['keep'] = '0'
        journal_entry.metadata['mime_type'] = 'application/x-chart-activity'
        journal_entry.metadata['activity'] = 'org.sugarlabs.SimpleGraph'

        json.dump(chart_params, data_file.file)
        data_file.file.close()
        journal_entry.file_path = data_file.name

        logging.debug('Create %s data file', data_file.name)
        datastore.write(journal_entry)
        self._show_journal_alert(_('Exported data'), _('Open in the Journal'),
                                 journal_entry.object_id)
class DevelopSearchToolbar(Gtk.Toolbar):

    def __init__(self, _activity):
        GObject.GObject.__init__(self)

        self._activity = _activity

        # setup the search options
        self.s_opts = SearchOptions(
            where=S_WHERE.multifile,
            use_regex=False,
            ignore_caps=True,
            replace_all=False,
            #defaults to avoid creating
            #a new SearchOptions object for normal searches
            #should never be changed, just make a copy like:
            #SearchOptions(self.s_opts, forward=False)
            forward=True,
            stay=False)

        self.safe_to_replace = False

        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(
            iconentry.ICON_ENTRY_PRIMARY,
            SEARCH_ICONS[self.s_opts.use_regex][self.s_opts.where])

        self._search_entry.add_clear_button()
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._add_widget(self._search_entry, expand=True)

        self._findprev = ToolButton('go-previous')
        self._findprev.set_tooltip(_('Find previous'))
        self.insert(self._findprev, -1)
        self._findprev.show()
        self._findprev.connect('clicked', self._findprev_cb)

        self._findnext = ToolButton('go-next')
        self._findnext.set_tooltip(_('Find next'))
        self.insert(self._findnext, -1)
        self._findnext.show()
        self._findnext.connect('clicked', self._findnext_cb)

        """
        self._settings = ToolButton(CAP_ICONS[self.s_opts.ignore_caps])
        self._settings.set_tooltip(_('Search settings'))
        self.insert(self._settings, -1)
        self._settings.show()
        self._settings.connect('clicked', self._settings_cb)

        # Search settings menu
        # This menu should attach to something else beside findnext -
        #location is temporary.
        palette = self._settings.get_palette()
        sswo = self._set_where_options
        ssho = self._set_how_options
        ssco = self._set_cap_options
        #TODO: move data structure to a member and the logic to a function
        for name, function, options, icon in (
                (_('Ignore capitalization'), ssco, True, "ignore-caps"),
                (_('Match capitalization'), ssco, False, "use-caps"),
                (None, None, None, None),
                (_('Search in selection'), sswo, S_WHERE.selection,
                    "search-in-selection"),
                (_('Search in current file'), sswo, S_WHERE.file,
                    "system-search"),
                (_('Search in all open files'), sswo, S_WHERE.multifile,
                    "multi-search"),
                (None, None, None, None),
                (_('Simple search'), ssho, False, "system-search"),
                (_('Advanced search'), ssho, True, "regex"),
                ):
            if not name:
                menuitem = Gtk.SeparatorMenuItem()
            else:
                menuitem = MenuItem(name, icon)
                menuitem.connect('activate', function, options)
            palette.menu.append(menuitem)
            menuitem.show()

        # make expanded non-drawn visible separator to make the replace
        #stuff right-align
        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.insert(separator, -1)
        separator.show()

        # replace entry
        self._replace_entry = iconentry.IconEntry()
        self._replace_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-replace')
        self._replace_entry.connect('changed', self._replace_entry_changed_cb)
        self._replace_entry.add_clear_button()
        self._add_widget(self._replace_entry, expand=True)

        #replace button
        self._replace_button = ToolButton(REPLACE_ICONS[
                                                self.s_opts.replace_all])
        self._replace_button.set_tooltip(_('Replace'))
        self.insert(self._replace_button, -1)
        self._replace_button.show()
        self._replace_button.connect('clicked', self._replace_cb)

        palette = self._replace_button.get_palette()
        ssro = self._set_replace_options
        #TODO: move data structure to a member and the logic to a function
        for name, function, options, icon in (
                (_('Replace one'), ssro, False, "replace-and-find"),
                (_('Replace all'), ssro, True, "multi-replace"),
                ):
            if not name:
                menuitem = Gtk.SeparatorMenuItem()
            else:
                menuitem = MenuItem(name, icon)
                menuitem.connect('activate', function, options)
            palette.menu.append(menuitem)
            menuitem.show()
        """

        self._activity.editor.connect('changed', self._changed_cb)

        self._activity.connect('key_press_event', self._on_key_press_event)

    def _on_key_press_event(self, widget, event):
        keyname = Gdk.keyval_name(event.keyval)
        if "F5" <= keyname and keyname <= "F8":
            if keyname == "F5":
                self._go_to_search_entry_cb()
            elif keyname == "F6":
                self._findprev_cb()
            elif keyname == "F7":
                self._findnext_cb()
            elif keyname == "F8":
                self._replace_or_go_to_replace_entry_cb()
            return True

    def _go_to_search_entry_cb(self):
        entry = self._search_entry
        text = self._activity.editor.get_selected()
        entry.grab_focus()
        if text:
            entry.delete_text(0, -1)
            entry.insert_text(text)
            entry.select_region(0, -1)
        else:
            entry.delete_text(0, 0)
            entry.set_position(-1)
            #for some reason, grab_focus doesn't work otherwise

    def _replace_or_go_to_replace_entry_cb(self):
        if self.safe_to_replace:
            self._replace_cb()
        else:
            self._replace_entry.select_region(0, -1)
            self._replace_entry.grab_focus()

    def _reset_search_icons(self):
        self._search_entry.set_icon_from_name(
            iconentry.ICON_ENTRY_PRIMARY,
            SEARCH_ICONS[self.s_opts.use_regex][self.s_opts.where])
        #self._settings.set_icon(CAP_ICONS[self.s_opts.ignore_caps])
        #self._replace_button.set_icon(REPLACE_ICONS[self.s_opts.replace_all])
        self._reset_replace_sensitivity()

    def _reset_replace_sensitivity(self):
        pass
        """
        self._replace_button.set_sensitive(
            self.s_opts.where == S_WHERE.selection or self.s_opts.replace_all)
        """

    def _set_where_options(self, menu, option):
        self.s_opts.where = option  # IGNORE:W0201
        self._reset_search_icons()

    def _set_how_options(self, menu, option):
        self.s_opts.use_regex = option  # IGNORE:W0201
        self._reset_search_icons()

    def _set_cap_options(self, menu, option):
        self.s_opts.ignore_caps = option  # IGNORE:W0201
        self._reset_search_icons()

    def _set_replace_options(self, menu, option):
        self.s_opts.replace_all = option  # IGNORE:W0201
        if option and self.s_opts.where == S_WHERE.multifile:
            self.s_opts.where = S_WHERE.file  # for safety:
            #do not replace all in multifile except explicitly
        self._reset_search_icons()

    def _changed_cb(self, _buffer):
        self._reset_replace_sensitivity()
        #if self.s_opts.where == S_WHERE.selection:
        #    self._set_where_options(None, S_WHERE.file)

    def _settings_cb(self, button):
        self._set_cap_options(None, not self.s_opts.ignore_caps)

    def _replace_cb(self, button=None):
        pass
        """
        ftext = self._search_entry.props.text
        rtext = self._replace_entry.props.text
        __replaced, found = self._activity.editor.replace(ftext, rtext,
                                                          self.s_opts)
        if found:
            self._replace_button.set_sensitive(True)
        """

    def _search_entry_activated_cb(self, entry):
        text = self._search_entry.props.text
        if text:
            self._findnext_cb(None)

    def _search_entry_changed_cb(self, entry):
        self.safe_to_replace = False
        text = self._search_entry.props.text
        if not text:
            self._findprev.set_sensitive(False)
            self._findnext.set_sensitive(False)
        else:
            self._findprev.set_sensitive(True)
            self._findnext.set_sensitive(True)
            if not self.s_opts.use_regex:
                #do not do partial searches for regex
                if self._activity.editor.find_next(text):
                    #no multifile, or focus gets grabbed
                    pass
                    #self._replace_button.set_sensitive(True)

    def _replace_entry_changed_cb(self, entry):
        if self._replace_entry.props.text:
            self.safe_to_replace = True

    def _findprev_cb(self, button=None):
        ftext = self._search_entry.props.text
        if ftext:
            if self._activity.editor.find_next(ftext, direction='backward'):
                pass
                #self._replace_button.set_sensitive(True)

    def _findnext_cb(self, button=None):
        ftext = self._search_entry.props.text
        if ftext:
            if self._activity.editor.find_next(ftext, direction='forward'):
                pass
                #self._replace_button.set_sensitive(True)

    # bad paul! this function was copied from sugar's activity.py via Write
    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)

        tool_item.add(widget)
        widget.show()

        self.insert(tool_item, -1)
        tool_item.show()
Exemple #30
0
class TextAttributesToolbar(Gtk.Toolbar):
    def __init__(self, main_area):

        Gtk.Toolbar.__init__(self)

        self._main_area = main_area
        self._font_list = ['ABC123', 'Sans', 'Serif', 'Monospace', 'Symbol']
        self._font_sizes = [
            '8', '9', '10', '11', '12', '14', '16', '20', '22', '24', '26',
            '28', '36', '48', '72'
        ]

        self.font_button = ToolButton('font-text')
        self.font_button.set_tooltip(_('Select font'))
        self.font_button.connect('clicked', self.__font_selection_cb)
        self.insert(self.font_button, -1)
        self._setup_font_palette()

        self.insert(Gtk.SeparatorToolItem(), -1)

        self.font_size_up = ToolButton('resize+')
        self.font_size_up.set_tooltip(_('Bigger'))
        self.font_size_up.connect('clicked', self.__font_sizes_cb, True)
        self.insert(self.font_size_up, -1)

        if len(self._main_area.selected) > 0:
            font_size = self._main_area.font_size

        else:
            font_size = utils.default_font_size

        self.size_label = Gtk.Label(str(font_size))
        self.size_label.show()
        toolitem = Gtk.ToolItem()
        toolitem.add(self.size_label)
        toolitem.show()
        self.insert(toolitem, -1)

        self.font_size_down = ToolButton('resize-')
        self.font_size_down.set_tooltip(_('Smaller'))
        self.font_size_down.connect('clicked', self.__font_sizes_cb, False)
        self.insert(self.font_size_down, -1)

        self.insert(Gtk.SeparatorToolItem(), -1)

        self.bold = ToolButton('bold-text')
        self.bold.set_tooltip(_('Bold'))
        self.bold.connect('clicked', self.__bold_cb)
        self.insert(self.bold, -1)

        self.italics = ToolButton('italics-text')
        self.italics.set_tooltip(_('Italics'))
        self.italics.connect('clicked', self.__italics_cb)
        self.insert(self.italics, -1)

        self.underline = ToolButton('underline-text')
        self.underline.set_tooltip(_('Underline'))
        self.underline.connect('clicked', self.__underline_cb)
        self.insert(self.underline, -1)

        foreground_color = ColorToolButton()
        foreground_color.set_title(_('Set font color'))
        foreground_color.connect('color-set', self.__foreground_color_cb)
        self.insert(foreground_color, -1)

        bakground_color = ColorToolButton()
        bakground_color.set_title(_('Set background color'))
        bakground_color.connect('color-set', self.__background_color_cb)
        bakground_color.set_color(Gdk.Color(65535, 65535, 65535))
        self.insert(bakground_color, -1)

        self.show_all()

    def __font_selection_cb(self, widget):
        if self._font_palette:
            if not self._font_palette.is_up():
                self._font_palette.popup(immediate=True,
                                         state=self._font_palette.SECONDARY)
            else:
                self._font_palette.popdown(immediate=True)
            return

    def _init_font_list(self):
        self._font_white_list = []
        self._font_white_list.extend(DEFAULT_FONTS)

        # check if there are a user configuration file
        if not os.path.exists(USER_FONTS_FILE_PATH):
            # verify if exists a file in /etc
            if os.path.exists(GLOBAL_FONTS_FILE_PATH):
                shutil.copy(GLOBAL_FONTS_FILE_PATH, USER_FONTS_FILE_PATH)

        if os.path.exists(USER_FONTS_FILE_PATH):
            # get the font names in the file to the white list
            fonts_file = open(USER_FONTS_FILE_PATH)
            # get the font names in the file to the white list
            for line in fonts_file:
                self._font_white_list.append(line.strip())
            # monitor changes in the file
            gio_fonts_file = Gio.File.new_for_path(USER_FONTS_FILE_PATH)
            self.monitor = gio_fonts_file.monitor_file(0, None)
            self.monitor.set_rate_limit(5000)
            self.monitor.connect('changed', self._reload_fonts)

    def _reload_fonts(self, monitor, gio_file, other_file, event):
        if event != Gio.FileMonitorEvent.CHANGES_DONE_HINT:
            return

        self._font_white_list = []
        self._font_white_list.extend(DEFAULT_FONTS)
        fonts_file = open(USER_FONTS_FILE_PATH)
        for line in fonts_file:
            self._font_white_list.append(line.strip())
        # update the menu
        for child in self._font_palette.menu.get_children():
            self._font_palette.menu.remove(child)
            child = None
        context = self.get_pango_context()
        tmp_list = []
        for family in context.list_families():
            name = family.get_name()
            if name in self._font_white_list:
                tmp_list.append(name)
        for font in sorted(tmp_list):
            menu_item = MyMenuItem(image=FontImage(font.replace(' ', '-')),
                                   text_label=font)
            menu_item.connect('activate', self.__font_selected_cb, font)
            self._font_palette.menu.append(menu_item)
            menu_item.show()

        return False

    def _setup_font_palette(self):
        self._init_font_list()
        context = self._main_area.pango_context
        for family in context.list_families():
            name = Pango.FontDescription(family.get_name()).to_string()
            if name not in self._font_list and \
                    name in self._font_white_list:
                self._font_list.append(name)

        self._font_palette = self.font_button.get_palette()
        for font in sorted(self._font_list):
            menu_item = MyMenuItem(image=FontImage(font.replace(' ', '-')),
                                   text_label=font)
            menu_item.connect('activate', self.__font_selected_cb, font)
            self._font_palette.menu.append(menu_item)
            menu_item.show()

    def __font_selected_cb(self, widget, font_name):
        if not hasattr(self._main_area, 'font_name'):
            return
        if len(self._main_area.selected) > 0:
            font_size = self._main_area.font_size
        else:
            font_size = utils.default_font_size
        self._main_area.set_font(font_name, font_size)
        self._main_area.font_name = font_name
        self._main_area.font_size = font_size

    def __attribute_values(self):
        thought = self._main_area.selected[0]
        return thought.attributes.copy()

    def __font_sizes_cb(self, button, increase):
        if not hasattr(self._main_area, 'font_size'):
            return
        if len(self._main_area.selected) < 1:
            return
        font_size = self._main_area.font_size
        if font_size in self._font_sizes:
            i = self._font_sizes.index(font_size)
            if increase:
                if i < len(self._font_sizes) - 2:
                    i += 1
            else:
                if i > 0:
                    i -= 1
        else:
            i = self._font_sizes.index(utils.default_font_size)

        font_size = self._font_sizes[i]
        self.size_label.set_text(str(font_size))
        self.font_size_down.set_sensitive(i != 0)
        self.font_size_up.set_sensitive(i < len(self._font_sizes) - 2)
        self._main_area.set_font(self._main_area.font_name, font_size)

    def __bold_cb(self, button):
        if len(self._main_area.selected) < 1:
            return
        value = not self.__attribute_values()["bold"]
        self._main_area.set_bold(value)

    def __italics_cb(self, button):
        if len(self._main_area.selected) < 1:
            return

        value = not self.__attribute_values()["italic"]
        self._main_area.set_italics(value)

    def __underline_cb(self, button):
        if len(self._main_area.selected) < 1:
            return
        value = not self.__attribute_values()["underline"]
        self._main_area.set_underline(value)

    def __foreground_color_cb(self, button):
        color = button.get_color()
        self._main_area.set_foreground_color(color)

    def __background_color_cb(self, button):
        color = button.get_color()
        self._parent._main_area.set_background_color(color)

    def change_active_font(self):
        # TODO: update the toolbar
        return
Exemple #31
0
class TrainingActivity(activity.Activity):
    ''' A series of training exercises '''
    transfer_started_signal = GObject.Signal('started', arg_types=([]))
    transfer_progressed_signal = GObject.Signal('progressed', arg_types=([]))
    transfer_completed_signal = GObject.Signal('completed', arg_types=([]))
    transfer_failed_signal = GObject.Signal('failed', arg_types=([]))

    def __init__(self, handle):
        ''' Initialize the toolbars and the game board '''
        try:
            super(TrainingActivity, self).__init__(handle)
        except dbus.exceptions.DBusException as e:
            _logger.error(str(e))

        self.connect('realize', self.__realize_cb)
        self.connect('started', self.__transfer_started_cb)
        self.connect('progressed', self.__transfer_progressed_cb)
        self.connect('completed', self.__transfer_completed_cb)
        self.connect('failed', self.__transfer_failed_cb)

        self.volume_monitor = Gio.VolumeMonitor.get()
        self.volume_monitor.connect('mount-added', self._mount_added_cb)
        self.volume_monitor.connect('mount-removed', self._mount_removed_cb)

        if hasattr(self, 'metadata') and 'font_size' in self.metadata:
            self.font_size = int(self.metadata['font_size'])
        else:
            self.font_size = 8
        self.zoom_level = self.font_size / float(len(FONT_SIZES))
        _logger.debug('zoom level is %f' % self.zoom_level)

        _check_gconf_settings()  # For debugging purposes

        self._setup_toolbars()
        self.modify_bg(Gtk.StateType.NORMAL,
                       style.COLOR_WHITE.get_gdk_color())

        self.bundle_path = activity.get_bundle_path()
        self.volume_data = []

        self.help_palette = None
        self.help_panel_visible = False
        self._copy_entry = None
        self._paste_entry = None
        self._webkit = None
        self._clipboard_text = ''
        self._fixed = None
        self._notify_transfer_status = False

        if self._load_extension() and self.check_volume_data():
            self._launcher()

    def _launcher(self):
        get_power_manager().inhibit_suspend()

        # We are resuming the activity or we are launching a new instance?
        # * Is there a data file to sync on the USB key?
        # * Do we create a new data file on the USB key?
        path = self._check_for_USB_data()
        if path is None:
            self._launch_task_master()
        elif self._sync_data_from_USB(path):
            self._copy_data_from_USB()
            # Flash a welcome back screen.
            self._load_intro_graphics(file_name='welcome-back.html')
            GObject.timeout_add(1500, self._launch_task_master)

    def can_close(self):
        get_power_manager().restore_suspend()
        return True

    def busy_cursor(self):
        self._old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def reset_cursor(self):
        if hasattr(self, '_old_cursor'):
            self.get_window().set_cursor(self._old_cursor)

    def check_volume_data(self):
        # Before we begin (and before each task),
        # we need to find any and all USB keys
        # and any and all training-data files on them.

        _logger.debug(utils.get_volume_paths())
        self.volume_data = []
        for path in utils.get_volume_paths():
            os.path.basename(path)
            self.volume_data.append(
                {'basename': os.path.basename(path),
                 'files': utils.look_for_training_data(path),
                 'sugar_path': os.path.join(self.get_activity_root(), 'data'),
                 'usb_path': path})
            _logger.debug(self.volume_data[-1])

        # (1) We require a USB key
        if len(self.volume_data) == 0:
            _logger.error('NO USB KEY INSERTED')
            alert = ConfirmationAlert()
            alert.props.title = _('USB key required')
            alert.props.msg = _('You must insert a USB key before launching '
                                'this activity.')
            alert.connect('response', self._remove_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(file_name='insert-usb.html')
            return False

        # (2) Only one USB key
        if len(self.volume_data) > 1:
            _logger.error('MULTIPLE USB KEYS INSERTED')
            alert = ConfirmationAlert()
            alert.props.title = _('Multiple USB keys found')
            alert.props.msg = _('Only one USB key must be inserted while '
                                'running this program.\nPlease remove any '
                                'additional USB keys before launching '
                                'this activity.')
            alert.connect('response', self._remove_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        volume = self.volume_data[0]

        # (3) At least 10MB of free space
        if utils.is_full(volume['usb_path'],
                         required=_MINIMUM_SPACE):
            _logger.error('USB IS FULL')
            alert = ConfirmationAlert()
            alert.props.title = _('USB key is full')
            alert.props.msg = _('No room on USB')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        # (4) File is read/write
        if not utils.is_writeable(volume['usb_path']):
            _logger.error('CANNOT WRITE TO USB')
            alert = ConfirmationAlert()
            alert.props.title = _('Cannot write to USB')
            alert.props.msg = _('USB key seems to be read-only.')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        # (5) Only one set of training data per USB key
        # We expect UIDs to formated as XXXX-XXXX
        # We need to make sure we have proper UIDs associated with
        # the USBs and the files on them match the UID.
        # (a) If there are no files, we will assign the UID based on the
        #     volume path;
        # (b) If there is one file with a valid UID, we use that UID;
        if len(volume['files']) == 0:
            volume['uid'] = 'training-data-%s' % \
                            utils.format_volume_name(volume['basename'])
            _logger.debug('No training data found. Using UID %s' %
                          volume['uid'])
            return True
        elif len(volume['files']) == 1:
            volume['uid'] = 'training-data-%s' % volume['files'][0][-9:]
            _logger.debug('Training data found. Using UID %s' %
                          volume['uid'])
            return True
        else:
            _logger.error('MULTIPLE TRAINING-DATA FILES FOUND')
            alert = ConfirmationAlert()
            alert.props.title = _('Multiple training-data files found.')
            alert.props.msg = _('There can only be one set of training '
                                'data per USB key.')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

    def _check_for_USB_data(self):
        usb_path = os.path.join(self.volume_data[0]['usb_path'],
                                self.volume_data[0]['uid'])
        if os.path.exists(usb_path):
            return usb_path
        else:
            return None

    def _sync_data_from_USB(self, usb_data_path=None):
        # We need to sync up file on USB with file on disk,
        # but only if the email addresses match. Otherwise,
        # raise an error.
        if usb_data_path is not None:
            usb_data = {}
            if os.path.exists(usb_data_path):
                fd = open(usb_data_path, 'r')
                json_data = fd.read()
                fd.close()
                if len(json_data) > 0:
                    try:
                        usb_data = json.loads(json_data)
                    except ValueError as e:
                        _logger.error('Cannot load USB data: %s' % e)
            else:
                _logger.error('Cannot find USB data: %s' % usb_data_path)

            sugar_data_path = os.path.join(
                self.volume_data[0]['sugar_path'],
                self.volume_data[0]['uid'])
            sugar_data = {}
            if os.path.exists(sugar_data_path):
                fd = open(sugar_data_path, 'r')
                json_data = fd.read()
                fd.close()
                if len(json_data) > 0:
                    try:
                        sugar_data = json.loads(json_data)
                    except ValueError as e:
                        _logger.error('Cannot load Sugar data: %s' % e)
            else:
                _logger.error('Cannot find Sugar data: %s' % sugar_data_path)

            # First, check to make sure email_address matches
            if EMAIL_UID in usb_data:
                usb_email = usb_data[EMAIL_UID]
            else:
                usb_email = None
            if EMAIL_UID in sugar_data:
                sugar_email = sugar_data[EMAIL_UID]
            else:
                sugar_email = None
            if usb_email != sugar_email:
                if usb_email is None and sugar_email is not None:
                    _logger.warning('Using email address from Sugar: %s' %
                                    sugar_email)
                    usb_data[EMAIL_UID] = sugar_email
                elif usb_email is not None and sugar_email is None:
                    _logger.warning('Using email address from USB: %s' %
                                    usb_email)
                    sugar_data[EMAIL_UID] = usb_email
                elif usb_email is None and sugar_email is None:
                    _logger.warning('No email address found')
                else:
                    # FIX ME: We need to resolve this, but for right now, punt.
                    alert = ConfirmationAlert()
                    alert.props.title = _('Data mismatch')
                    alert.props.msg = _('Are you %(usb)s or %(sugar)s?' %
                                        {'usb': usb_email,
                                         'sugar': sugar_email})
                    alert.connect('response', self._close_alert_cb)
                    self.add_alert(alert)
                    self._load_intro_graphics(message=alert.props.msg)
                    return False

            def count_completed(data):
                count = 0
                for key in data:
                    if isinstance(data[key], dict) and \
                       'completed' in data[key] and \
                       data[key]['completed']:
                        count += 1
                return count

            # The database with the most completed tasks takes precedence.
            if count_completed(usb_data) >= count_completed(sugar_data):
                _logger.debug('data sync: USB data takes precedence')
                data_one = usb_data
                data_two = sugar_data
            else:
                _logger.debug('data sync: Sugar data takes precedence')
                data_one = sugar_data
                data_two = usb_data

            # Copy completed tasks from one to two
            for key in data_one:
                if isinstance(data_one[key], dict) and \
                   'completed' in data_one[key] and \
                   data_one[key]['completed']:
                    data_two[key] = data_one[key]

            # Copy completed tasks from two to one
            for key in data_two:
                if isinstance(data_two[key], dict) and \
                   'completed' in data_two[key] and \
                   data_two[key]['completed']:
                    data_one[key] = data_two[key]

            # Copy incompleted tasks from one to two
            for key in data_one:
                if isinstance(data_one[key], dict) and \
                   (not 'completed' in data_one[key] or
                    not data_one[key]['completed']):
                        data_two[key] = data_one[key]

            # Copy incompleted tasks from two to one
            for key in data_two:
                if isinstance(data_two[key], dict) and \
                   (not 'completed' in data_two[key] or
                    not data_two[key]['completed']):
                        data_one[key] = data_two[key]

            # Copy name, email_address, current_task...
            for key in data_one:
                if not isinstance(data_one[key], dict):
                    data_two[key] = data_one[key]
            for key in data_two:
                if not isinstance(data_two[key], dict):
                    data_one[key] = data_two[key]

            # Finally, write to the USB and ...
            json_data = json.dumps(data_one)
            fd = open(usb_data_path, 'w')
            fd.write(json_data)
            fd.close()

            # ...save a shadow copy in Sugar
            fd = open(sugar_data_path, 'w')
            fd.write(json_data)
            fd.close()
            return True
        else:
            _logger.error('No data to sync on USB')
            return False

    def _copy_data_from_USB(self):
        usb_path = self._check_for_USB_data()
        if usb_path is not None:
            try:
                subprocess.call(['cp', usb_path,
                                 self.volume_data[0]['sugar_path']])
            except OSError as e:
                _logger.error('Could not copy %s to %s: %s' % (
                    usb_path, self.volume_data[0]['sugar_path'], e))
        else:
            _logger.error('No data found on USB')

    def toolbar_expanded(self):
        if self.activity_button.is_expanded():
            return True
        elif self.edit_toolbar_button.is_expanded():
            return True
        elif self.view_toolbar_button.is_expanded():
            return True
        elif hasattr(self, 'progress_toolbar_button') and \
             self.progress_toolbar_button.is_expanded():
            return True

    def _launch_task_master(self):
        # Most things need only be done once
        if self._fixed is None:
            self._fixed = Gtk.Fixed()
            self._fixed.set_size_request(Gdk.Screen.width(),
                                         Gdk.Screen.height())

            # Offsets from the bottom of the screen
            dy1 = 3 * style.GRID_CELL_SIZE
            dy2 = 2 * style.GRID_CELL_SIZE

            self._progress_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._progress_area.set_size_request(Gdk.Screen.width(), -1)
            self._fixed.put(self._progress_area, 0, Gdk.Screen.height() - dy2)
            self._progress_area.show()

            self._button_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._button_area.set_size_request(Gdk.Screen.width(), -1)
            self._fixed.put(self._button_area, 0, Gdk.Screen.height() - dy1)
            self._button_area.show()

            self._scrolled_window = Gtk.ScrolledWindow()
            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._set_scroll_policy()
            self._graphics_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._scrolled_window.add_with_viewport(self._graphics_area)
            self._graphics_area.show()
            self._fixed.put(self._scrolled_window, 0, 0)
            self._scrolled_window.show()

            self._task_master = TaskMaster(self)
            self._task_master.show()

            # Now that we have the tasks, we can build the progress toolbar and
            # help panel.
            self._build_progress_toolbar()

            self._help_panel = HelpPanel(self._task_master)
            self.help_palette = self._help_button.get_palette()
            self.help_palette.set_content(self._help_panel)
            self._help_panel.show()
            self._help_button.set_sensitive(True)

            Gdk.Screen.get_default().connect('size-changed',
                                             self._configure_cb)
            self._toolbox.connect('hide', self._resize_hide_cb)
            self._toolbox.connect('show', self._resize_show_cb)

            self._task_master.set_events(Gdk.EventMask.KEY_PRESS_MASK)
            self._task_master.connect('key_press_event',
                                      self._task_master.keypress_cb)
            self._task_master.set_can_focus(True)
            self._task_master.grab_focus()

        self.set_canvas(self._fixed)
        self._fixed.show()

        self.completed = False
        self._update_completed_sections()
        self._check_connected_task_status()
        self._task_master.task_master()

    def load_graphics_area(self, widget):
        self._graphics_area.add(widget)

    def load_button_area(self, widget):
        self._button_area.add(widget)

    def load_progress_area(self, widget):
        self._progress_area.add(widget)

    def _load_intro_graphics(self, file_name='generic-problem.html',
                             message=None):
        center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0)
        url = os.path.join(self.bundle_path, 'html-content', file_name)
        graphics = Graphics()
        if message is None:
            graphics.add_uri('file://' + url)
        else:
            graphics.add_uri('file://' + url + '?MSG=' +
                             utils.get_safe_text(message))
        graphics.set_zoom_level(0.667)
        center_in_panel.add(graphics)
        graphics.show()
        self.set_canvas(center_in_panel)
        center_in_panel.show()

    def _resize_hide_cb(self, widget):
        self._resize_canvas(widget, True)

    def _resize_show_cb(self, widget):
        self._resize_canvas(widget, False)

    def _configure_cb(self, event):
        self._fixed.set_size_request(Gdk.Screen.width(), Gdk.Screen.height())
        self._set_scroll_policy()
        self._resize_canvas(None)
        self._task_master.reload_graphics()

    def _resize_canvas(self, widget, fullscreen=False):
        # When a toolbar is expanded or collapsed, resize the canvas
        # to ensure that the progress bar is still visible.
        if hasattr(self, '_task_master'):
            if self.toolbar_expanded():
                dy1 = 4 * style.GRID_CELL_SIZE
                dy2 = 3 * style.GRID_CELL_SIZE
            else:
                dy1 = 3 * style.GRID_CELL_SIZE
                dy2 = 2 * style.GRID_CELL_SIZE

            if fullscreen:
                dy1 -= 2 * style.GRID_CELL_SIZE
                dy2 -= 2 * style.GRID_CELL_SIZE

            self._scrolled_window.set_size_request(
                Gdk.Screen.width(), Gdk.Screen.height() - dy1)
            self._fixed.move(self._progress_area, 0, Gdk.Screen.height() - dy2)
            self._fixed.move(self._button_area, 0, Gdk.Screen.height() - dy1)

        self.help_panel_visible = False

    def get_activity_version(self):
        info_path = os.path.join(self.bundle_path, 'activity', 'activity.info')
        try:
            info_file = open(info_path, 'r')
        except Exception as e:
            _logger.error('Could not open %s: %s' % (info_path, e))
            return 'unknown'

        cp = ConfigParser()
        cp.readfp(info_file)

        section = 'Activity'

        if cp.has_option(section, 'activity_version'):
            activity_version = cp.get(section, 'activity_version')
        else:
            activity_version = 'unknown'
        return activity_version

    def get_uid(self):
        if len(self.volume_data) == 1:
            return self.volume_data[0]['uid']
        else:
            return 'unknown'

    def write_file(self, file_path):
        # Only write if we have a valid USB/data file to work with.
        if len(self.volume_data) == 1 and \
           len(self.volume_data[0]['files']) == 1:
            self.metadata[TRAINING_DATA_UID] = self.volume_data[0]['uid']

            # We may have failed before getting to init of taskmaster
            if hasattr(self, '_task_master'):
                self._task_master.write_task_data(
                    'current_task', self._task_master.current_task)
                self.update_activity_title()
                email = self._task_master.read_task_data(EMAIL_UID)
                if email is None:
                    email = ''
                self.metadata[TRAINING_DATA_EMAIL] = email
                name = self._task_master.read_task_data(NAME_UID)
                if name is None:
                    name = ''
                self.metadata[TRAINING_DATA_FULLNAME] = name

        self.metadata['font_size'] = str(self.font_size)

    def update_activity_title(self):
        name = self._task_master.read_task_data(NAME_UID)
        if name is not None:
            bundle_name = activity.get_bundle_name()
            self.metadata['title'] = _('%(name)s %(bundle)s Activity') % \
                {'name': name, 'bundle': bundle_name}

    def _setup_toolbars(self):
        ''' Setup the toolbars. '''
        self.max_participants = 1  # No sharing

        self._toolbox = ToolbarBox()

        self.activity_button = ActivityToolbarButton(self)
        self.activity_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self.set_toolbar_box(self._toolbox)
        self._toolbox.show()
        self.toolbar = self._toolbox.toolbar

        view_toolbar = Gtk.Toolbar()
        self.view_toolbar_button = ToolbarButton(
            page=view_toolbar,
            label=_('View'),
            icon_name='toolbar-view')
        self.view_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.view_toolbar_button, 1)
        view_toolbar.show()
        self.view_toolbar_button.show()

        button = ToolButton('view-fullscreen')
        button.set_tooltip(_('Fullscreen'))
        button.props.accelerator = '<Alt>Return'
        view_toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self._fullscreen_cb)

        self._zoom_in = ToolButton('zoom-in')
        self._zoom_in.set_tooltip(_('Increase size'))
        view_toolbar.insert(self._zoom_in, -1)
        self._zoom_in.show()
        self._zoom_in.connect('clicked', self._zoom_in_cb)

        self._zoom_out = ToolButton('zoom-out')
        self._zoom_out.set_tooltip(_('Decrease size'))
        view_toolbar.insert(self._zoom_out, -1)
        self._zoom_out.show()
        self._zoom_out.connect('clicked', self._zoom_out_cb)

        self._zoom_eq = ToolButton('zoom-original')
        self._zoom_eq.set_tooltip(_('Restore original size'))
        view_toolbar.insert(self._zoom_eq, -1)
        self._zoom_eq.show()
        self._zoom_eq.connect('clicked', self._zoom_eq_cb)

        self._set_zoom_buttons_sensitivity()

        edit_toolbar = Gtk.Toolbar()
        self.edit_toolbar_button = ToolbarButton(
            page=edit_toolbar,
            label=_('Edit'),
            icon_name='toolbar-edit')
        self.edit_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.edit_toolbar_button, 1)
        edit_toolbar.show()
        self.edit_toolbar_button.show()

        self._copy_button = ToolButton('edit-copy')
        self._copy_button.set_tooltip(_('Copy'))
        self._copy_button.props.accelerator = '<Ctrl>C'
        edit_toolbar.insert(self._copy_button, -1)
        self._copy_button.show()
        self._copy_button.connect('clicked', self._copy_cb)
        self._copy_button.set_sensitive(False)

        self._paste_button = ToolButton('edit-paste')
        self._paste_button.set_tooltip(_('Paste'))
        self._paste_button.props.accelerator = '<Ctrl>V'
        edit_toolbar.insert(self._paste_button, -1)
        self._paste_button.show()
        self._paste_button.connect('clicked', self._paste_cb)
        self._paste_button.set_sensitive(False)

        self._progress_toolbar = Gtk.Toolbar()
        self.progress_toolbar_button = ToolbarButton(
            page=self._progress_toolbar,
            label=_('Check progress'),
            icon_name='check-progress')
        self.progress_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.progress_toolbar_button, -1)
        self._progress_toolbar.show()
        self.progress_toolbar_button.show()

        self._help_button = ToolButton('toolbar-help')
        self._help_button.set_tooltip(_('Help'))
        self._help_button.props.accelerator = '<Ctrl>H'
        self._toolbox.toolbar.insert(self._help_button, -1)
        self._help_button.show()
        self._help_button.connect('clicked', self._help_cb)
        self._help_button.set_sensitive(False)
        self._help_button.palette_invoker.props.lock_palette = True

        self.transfer_button = ToolButton('transfer')
        self.transfer_button.set_tooltip(_('Training data upload status'))
        self._toolbox.toolbar.insert(self.transfer_button, -1)
        self.transfer_button.connect('clicked', self._transfer_cb)
        self.transfer_button.hide()

        self.progress_label = Gtk.Label()
        self.progress_label.set_line_wrap(True)
        self.progress_label.set_size_request(300, -1)
        self.progress_label.set_use_markup(True)
        toolitem = Gtk.ToolItem()
        toolitem.add(self.progress_label)
        self.progress_label.show()
        self._toolbox.toolbar.insert(toolitem, -1)
        toolitem.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self._toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        self._toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _build_progress_toolbar(self):
        self._progress_buttons = []
        progress = self._task_master.get_completed_sections()

        for section_index in range(self._task_master.get_number_of_sections()):
            icon = self._task_master.get_section_icon(section_index)
            if section_index in progress:
                icon = icon + '-white'
            else:
                icon = icon + '-grey'

            name = self._task_master.get_section_name(section_index)

            if section_index == 0:
                group = None
            else:
                group = self._progress_buttons[0]

            self._progress_buttons.append(RadioToolButton(group=group))
            self._progress_buttons[-1].set_icon_name(icon)
            self._progress_buttons[-1].set_tooltip(name)
            self._progress_toolbar.insert(self._progress_buttons[-1], -1)
            self._progress_buttons[-1].show()
            self._progress_buttons[-1].connect(
                'clicked', self._jump_to_section_cb, section_index)

        self._radio_buttons_live = False
        section_index, task_index = \
            self._task_master.get_section_and_task_index()
        self._progress_buttons[section_index].set_active(True)
        self._radio_buttons_live = True

    def _check_connected_task_status(self):
        ''' We only want to turn on notifications if we expect connectivity '''
        task = self._task_master.uid_to_task(GET_CONNECTED_TASK)
        self.set_notify_transfer_status(task.is_completed())

    def _update_completed_sections(self):
        progress = self._task_master.get_completed_sections()

        for section in range(self._task_master.get_number_of_sections()):
            icon = self._task_master.get_section_icon(section)
            if section in progress:
                icon = icon + '-white'
            else:
                icon = icon + '-grey'
            self._progress_buttons[section].set_icon_name(icon)

        self._radio_buttons_live = False
        section_index, task_index = \
            self._task_master.get_section_and_task_index()
        self._progress_buttons[section_index].set_active(True)
        self._radio_buttons_live = True

    def mark_section_as_complete(self, section):
        icon = self._task_master.get_section_icon(section) + '-white'
        self._progress_buttons[section].set_icon_name(icon)
        if section < self._task_master.get_number_of_sections() - 1:
            self._radio_buttons_live = False
            self._progress_buttons[section + 1].set_active(True)
            self._radio_buttons_live = True

    def set_notify_transfer_status(self, state):
        _logger.debug('Setting transfer status to %s' % (str(state)))
        self._notify_transfer_status = state

    def _update_transfer_button(self, icon_name, tooltip):
        self.transfer_button.set_icon_name(icon_name)
        self.transfer_button.set_tooltip(tooltip)
        if self._notify_transfer_status:
            self.transfer_button.show()
        else:
            self.transfer_button.hide()

    def _transfer_cb(self, button):
        ''' Hide the button to dismiss notification '''
        self.transfer_button.set_tooltip(_('Training data upload status'))
        self.transfer_button.hide()

    def __transfer_started_cb(self, widget):
        self._update_transfer_button('transfer', _('Data transfer started'))

    def __transfer_progressed_cb(self, widget):
        self._update_transfer_button('transfer',
                                     _('Data transfer progressing'))

    def __transfer_completed_cb(self, widget):
        self._update_transfer_button('transfer-complete',
                                     _('Data transfer completed'))

    def __transfer_failed_cb(self, widget):
        self._update_transfer_button('transfer-failed',
                                     _('Data transfer failed'))

    def __realize_cb(self, window):
        self.window_xid = window.get_window().get_xid()

    def set_copy_widget(self, webkit=None, text_entry=None):
        # Each task is responsible for setting a widget for copy
        if webkit is not None:
            self._webkit = webkit
        else:
            self._webkit = None
        if text_entry is not None:
            self._copy_entry = text_entry
        else:
            self._copy_entry = None

        self._copy_button.set_sensitive(webkit is not None or
                                        text_entry is not None)

    def _copy_cb(self, button):
        if self._copy_entry is not None:
            self._copy_entry.copy_clipboard()
        elif self._webkit is not None:
            self._webkit.copy_clipboard()
        else:
            _logger.debug('No widget set for copy.')

    def set_paste_widget(self, text_entry=None):
        # Each task is responsible for setting a widget for paste
        if text_entry is not None:
            self._paste_entry = text_entry
        self._paste_button.set_sensitive(text_entry is not None)

    def _paste_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.clipboard_text = clipboard.wait_for_text()
        if self._paste_entry is not None:
            self._paste_entry.paste_clipboard()
        else:
            _logger.debug('No widget set for paste (%s).' %
                          self.clipboard_text)

    def _fullscreen_cb(self, button):
        ''' Hide the Sugar toolbars. '''
        self.fullscreen()

    def _set_zoom_buttons_sensitivity(self):
        if self.font_size < len(FONT_SIZES) - 1:
            self._zoom_in.set_sensitive(True)
        else:
            self._zoom_in.set_sensitive(False)
        if self.font_size > 0:
            self._zoom_out.set_sensitive(True)
        else:
            self._zoom_out.set_sensitive(False)

        if hasattr(self, '_scrolled_window'):
            self._set_scroll_policy()

    def _set_scroll_policy(self):
        if Gdk.Screen.width() < Gdk.Screen.height() or self.zoom_level > 0.667:
            self._scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                             Gtk.PolicyType.AUTOMATIC)
        else:
            self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                             Gtk.PolicyType.AUTOMATIC)

    def _zoom_eq_cb(self, button):
        self.font_size = 8
        self.zoom_level = 0.667
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _zoom_in_cb(self, button):
        if self.font_size < len(FONT_SIZES) - 1:
            self.font_size += 1
            self.zoom_level *= 1.1
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _zoom_out_cb(self, button):
        if self.font_size > 0:
            self.font_size -= 1
            self.zoom_level /= 1.1
        self._set_zoom_buttons_sensitivity()
        self._task_master.reload_graphics()

    def _jump_to_section_cb(self, button, section_index):
        if self._radio_buttons_live:
            if self._task_master.requirements_are_met(section_index, 0):
                uid = self._task_master.section_and_task_to_uid(section_index)
                self._task_master.current_task = \
                    self._task_master.uid_to_task_number(uid)
                self._task_master.reload_graphics()
            else:
                section_index, task_index = \
                    self._task_master.get_section_and_task_index()
                self._progress_buttons[section_index].set_active(True)

    def _help_cb(self, button):
        # title, help_file = self._task_master.get_help_info()
        # _logger.debug('%s: %s' % (title, help_file))
        # if not hasattr(self, 'window_xid'):
        #    self.window_xid = self.get_window().get_xid()
        # if title is not None and help_file is not None:
        #     self.viewhelp = ViewHelp(title, help_file, self.window_xid)
        #     self.viewhelp.show()
        try:
            self._help_panel.set_connected(
                utils.nm_status() == 'network-wireless-connected')
        except Exception as e:
            _logger.error('Could not read NM status: %s' % (e))
            self._help_panel.set_connected(False)

        if self.help_palette:
            # FIXME: is_up() is always returning False, so we
            # "debounce" using help_panel_visible.
            if not self.help_palette.is_up() and not self.help_panel_visible:
                self.help_palette.popup(
                    immediate=True, state=self.help_palette.SECONDARY)
                self.help_panel_visible = True
            else:
                self.help_palette.popdown(immediate=True)
                self.help_panel_visible = False
                self._help_button.set_expanded(False)

    def add_badge(self, msg, icon="training-trophy", name="One Academy"):
        sugar_icons = os.path.join(os.path.expanduser('~'), '.icons')
        if not os.path.exists(sugar_icons):
            try:
                subprocess.call(['mkdir', sugar_icons])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (sugar_icons, e))

        badge = {
            'icon': icon,
            'from': name,
            'message': msg
        }

        # Use icons from html-content directory since default colors are
        # intended for white background.
        icon_dir = os.path.join(self.bundle_path, 'html-content', 'images')
        icon_path = os.path.join(icon_dir, icon + '.svg')
        try:
            subprocess.call(['cp', icon_path, sugar_icons])
        except OSError as e:
            _logger.error('Could not copy %s to %s, %s' %
                          (icon_path, sugar_icons, e))

        if 'comments' in self.metadata:
            comments = json.loads(self.metadata['comments'])
            comments.append(badge)
            self.metadata['comments'] = json.dumps(comments)
        else:
            self.metadata['comments'] = json.dumps([badge])

    def _load_extension(self):
        if not WEBSERVICES_AVAILABLE:
            _logger.error('Webservices not available on this version of Sugar')
            self._webservice_alert(_('Sugar upgrade required.'))
            return False

        extensions_path = os.path.join(os.path.expanduser('~'), '.sugar',
                                       'default', 'extensions')
        webservice_path = os.path.join(extensions_path, 'webservice')
        sugarservices_path = os.path.join(self.bundle_path, 'sugarservices')
        init_path = os.path.join(self.bundle_path, 'sugarservices',
                                 '__init__.py')

        if not os.path.exists(extensions_path):
            try:
                subprocess.call(['mkdir', extensions_path])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (extensions_path, e))
                self._webservice_alert(_('System error.'))
                return False

        if not os.path.exists(webservice_path):
            try:
                subprocess.call(['mkdir', webservice_path])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False
            try:
                subprocess.call(['cp', init_path, webservice_path])
            except OSError as e:
                _logger.error('Could not cp %s to %s, %s' %
                              (init_path, webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False

        install = False
        if not os.path.exists(os.path.join(webservice_path, 'sugarservices')):
            _logger.error('SugarServices webservice not found. Installing...')
            install = True
        elif utils.get_sugarservices_version() < \
             _REQUIRED_SUGARSERVICES_VERSION:
            _logger.error('Found old SugarServices version. Installing...')
            install = True

        if install:
            try:
                subprocess.call(['cp', '-r', sugarservices_path,
                                 webservice_path])
            except OSError as e:
                _logger.error('Could not copy %s to %s, %s' %
                              (sugarservices_path, webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False

            alert = ConfirmationAlert()
            alert.props.title = _('Restart required')
            alert.props.msg = _('We needed to install some software on your '
                                'system.\nSugar must be restarted before '
                                'sugarservices can commence.')

            alert.connect('response', self._reboot_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(file_name='restart.html')

        return not install

    def _webservice_alert(self, message):
        alert = ConfirmationAlert()
        alert.props.title = message
        alert.props.msg = _('We are unable to install some software on your '
                            'system.\nSugar must be upgraded before this '
                            'activity can be run.')

        alert.connect('response', self._close_alert_cb)
        self.add_alert(alert)
        self._load_intro_graphics(message=message)

    def _remove_alert_cb(self, alert, response_id):
        self.remove_alert(alert)

    def _close_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            self.close()

    def _reboot_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            try:
                utils.reboot()
            except Exception as e:
                _logger.error('Cannot reboot: %s' % e)

    def _mount_added_cb(self, volume_monitor, device):
        _logger.error('mount added')
        if self.check_volume_data():
            _logger.debug('launching')
            self._launcher()

    def _mount_removed_cb(self, volume_monitor, device):
        _logger.error('mount removed')
        if self.check_volume_data():
            _logger.debug('launching')
            self._launcher()
Exemple #32
0
class InsertToolbar(Gtk.Toolbar):

    def __init__(self, abiword_canvas):
        GObject.GObject.__init__(self)

        self._abiword_canvas = abiword_canvas

        self._table_btn = ToolButton('create-table')
        self._table_btn.set_tooltip(_('Create table'))
        self.insert(self._table_btn, -1)
        self._grid_create = GridCreateWidget()
        self._grid_create.show()
        self._grid_create.connect('create-table', self._create_table_cb)
        palette = self._table_btn.get_palette()
        palette.set_content(self._grid_create)
        self._table_btn.connect('clicked', self._table_btn_clicked_cb)

        self._table_rows_after = ToolButton('row-insert')
        self._table_rows_after.set_tooltip(_('Insert Row'))
        self._table_rows_after_id = self._table_rows_after.connect(
            'clicked', self._table_rows_after_cb)
        self.insert(self._table_rows_after, -1)

        self._table_delete_rows = ToolButton('row-remove')
        self._table_delete_rows.set_tooltip(_('Delete Row'))
        self._table_delete_rows_id = self._table_delete_rows.connect(
            'clicked', self._table_delete_rows_cb)
        self.insert(self._table_delete_rows, -1)

        self._table_cols_after = ToolButton('column-insert')
        self._table_cols_after.set_tooltip(_('Insert Column'))
        self._table_cols_after_id = self._table_cols_after.connect(
            'clicked', self._table_cols_after_cb)
        self.insert(self._table_cols_after, -1)

        self._table_delete_cols = ToolButton('column-remove')
        self._table_delete_cols.set_tooltip(_('Delete Column'))
        self._table_delete_cols_id = self._table_delete_cols.connect(
            'clicked', self._table_delete_cols_cb)
        self.insert(self._table_delete_cols, -1)

        self.show_all()

        self._abiword_canvas.connect('table-state', self._isTable_cb)
        #self._abiword_canvas.connect('image-selected',
        #       self._image_selected_cb)

    def _table_btn_clicked_cb(self, button):
        button.get_palette().popup(True, button.get_palette().SECONDARY)

    def _create_table_cb(self, abi, rows, cols):
        self._abiword_canvas.insert_table(rows, cols)

    def _table_rows_after_cb(self, button):
        self._abiword_canvas.invoke_ex('insertRowsAfter', '', 0, 0)

    def _table_delete_rows_cb(self, button):
        self._abiword_canvas.invoke_ex('deleteRows', '', 0, 0)

    def _table_cols_after_cb(self, button):
        self._abiword_canvas.invoke_ex('insertColsAfter', '', 0, 0)

    def _table_delete_cols_cb(self, button):
        self._abiword_canvas.invoke_ex('deleteColumns', '', 0, 0)

    def _isTable_cb(self, abi, b):
        self._table_rows_after.set_sensitive(b)
        self._table_delete_rows.set_sensitive(b)
        self._table_cols_after.set_sensitive(b)
        self._table_delete_cols.set_sensitive(b)
class TextAttributesToolbar(Gtk.Toolbar):

    def __init__(self, main_area):

        Gtk.Toolbar.__init__(self)

        self._main_area = main_area
        self._font_list = ['ABC123', 'Sans', 'Serif', 'Monospace', 'Symbol']
        self._font_sizes = ['8', '9', '10', '11', '12', '14', '16', '20',
                            '22', '24', '26', '28', '36', '48', '72']

        self.font_button =  ToolButton('font-text')
        self.font_button.set_tooltip(_('Select font'))
        self.font_button.connect('clicked', self.__font_selection_cb)
        self.insert(self.font_button, -1)
        self._setup_font_palette()

        self.insert(Gtk.SeparatorToolItem(), -1)

        self.font_size_up = ToolButton('resize+')
        self.font_size_up.set_tooltip(_('Bigger'))
        self.font_size_up.connect('clicked', self.__font_sizes_cb, True)
        self.insert(self.font_size_up, -1)

        if len(self._main_area.selected) > 0:
            font_size = self._main_area.font_size

        else:
            font_size = utils.default_font_size

        self.size_label = Gtk.Label(str(font_size))
        self.size_label.show()
        toolitem = Gtk.ToolItem()
        toolitem.add(self.size_label)
        toolitem.show()
        self.insert(toolitem, -1)

        self.font_size_down = ToolButton('resize-')
        self.font_size_down.set_tooltip(_('Smaller'))
        self.font_size_down.connect('clicked', self.__font_sizes_cb, False)
        self.insert(self.font_size_down, -1)

        self.insert(Gtk.SeparatorToolItem(), -1)

        self.bold = ToolButton('bold-text')
        self.bold.set_tooltip(_('Bold'))
        self.bold.connect('clicked', self.__bold_cb)
        self.insert(self.bold, -1)

        self.italics = ToolButton('italics-text')
        self.italics.set_tooltip(_('Italics'))
        self.italics.connect('clicked', self.__italics_cb)
        self.insert(self.italics, -1)

        self.underline = ToolButton('underline-text')
        self.underline.set_tooltip(_('Underline'))
        self.underline.connect('clicked', self.__underline_cb)
        self.insert(self.underline, -1)

        foreground_color = ColorToolButton()
        foreground_color.set_title(_('Set font color'))
        foreground_color.connect('color-set', self.__foreground_color_cb)
        self.insert(foreground_color, -1)

        bakground_color = ColorToolButton()
        bakground_color.set_title(_('Set background color'))
        bakground_color.connect('color-set', self.__background_color_cb)
        bakground_color.set_color(Gdk.Color(65535, 65535, 65535))
        self.insert(bakground_color, -1)

        self.show_all()

    def __font_selection_cb(self, widget):
        if self._font_palette:
            if not self._font_palette.is_up():
                self._font_palette.popup(immediate=True,
                                    state=self._font_palette.SECONDARY)
            else:
                self._font_palette.popdown(immediate=True)
            return

    def _init_font_list(self):
        self._font_white_list = []
        self._font_white_list.extend(DEFAULT_FONTS)

        # check if there are a user configuration file
        if not os.path.exists(USER_FONTS_FILE_PATH):
            # verify if exists a file in /etc
            if os.path.exists(GLOBAL_FONTS_FILE_PATH):
                shutil.copy(GLOBAL_FONTS_FILE_PATH, USER_FONTS_FILE_PATH)

        if os.path.exists(USER_FONTS_FILE_PATH):
            # get the font names in the file to the white list
            fonts_file = open(USER_FONTS_FILE_PATH)
            # get the font names in the file to the white list
            for line in fonts_file:
                self._font_white_list.append(line.strip())
            # monitor changes in the file
            gio_fonts_file = Gio.File.new_for_path(USER_FONTS_FILE_PATH)
            self.monitor = gio_fonts_file.monitor_file(0, None)
            self.monitor.set_rate_limit(5000)
            self.monitor.connect('changed', self._reload_fonts)

    def _reload_fonts(self, monitor, gio_file, other_file, event):
        if event != Gio.FileMonitorEvent.CHANGES_DONE_HINT:
            return

        self._font_white_list = []
        self._font_white_list.extend(DEFAULT_FONTS)
        fonts_file = open(USER_FONTS_FILE_PATH)
        for line in fonts_file:
            self._font_white_list.append(line.strip())
        # update the menu
        for child in self._font_palette.menu.get_children():
            self._font_palette.menu.remove(child)
            child = None
        context = self.get_pango_context()
        tmp_list = []
        for family in context.list_families():
            name = family.get_name()
            if name in self._font_white_list:
                tmp_list.append(name)
        for font in sorted(tmp_list):
            menu_item = MyMenuItem(image=FontImage(font.replace(' ', '-')),
                                   text_label=font)
            menu_item.connect('activate', self.__font_selected_cb, font)
            self._font_palette.menu.append(menu_item)
            menu_item.show()

        return False

    def _setup_font_palette(self):
        self._init_font_list()
        context = self._main_area.pango_context
        for family in context.list_families():
            name = Pango.FontDescription(family.get_name()).to_string()
            if name not in self._font_list and \
                    name in self._font_white_list:
                self._font_list.append(name)

        self._font_palette = self.font_button.get_palette()
        for font in sorted(self._font_list):
            menu_item = MyMenuItem(image=FontImage(font.replace(' ', '-')),
                                   text_label=font)
            menu_item.connect('activate', self.__font_selected_cb, font)
            self._font_palette.menu.append(menu_item)
            menu_item.show()

    def __font_selected_cb(self, widget, font_name):
        if not hasattr(self._main_area, 'font_name'):
            return
        if len(self._main_area.selected) > 0:
            font_size = self._main_area.font_size
        else:
            font_size = utils.default_font_size
        self._main_area.set_font(font_name, font_size)
        self._main_area.font_name = font_name
        self._main_area.font_size = font_size

    def __attribute_values(self):
        thought = self._main_area.selected[0]
        return thought.attributes.copy()

    def __font_sizes_cb(self, button, increase):
        if not hasattr(self._main_area, 'font_size'):
            return
        if len(self._main_area.selected) < 1:
            return
        font_size = self._main_area.font_size
        if font_size in self._font_sizes:
            i = self._font_sizes.index(font_size)
            if increase:
                if i < len(self._font_sizes) - 2:
                    i += 1
            else:
                if i > 0:
                    i -= 1
        else:
            i = self._font_sizes.index(utils.default_font_size)

        font_size = self._font_sizes[i]
        self.size_label.set_text(str(font_size))
        self.font_size_down.set_sensitive(i != 0)
        self.font_size_up.set_sensitive(i < len(self._font_sizes) - 2)
        self._main_area.set_font(self._main_area.font_name, font_size)

    def __bold_cb(self, button):
        if len(self._main_area.selected) < 1:
            return
        value = not self.__attribute_values()["bold"]
        self._main_area.set_bold(value)

    def __italics_cb(self, button):
        if len(self._main_area.selected) < 1:
            return

        value = not self.__attribute_values()["italic"]
        self._main_area.set_italics(value)

    def __underline_cb(self, button):
        if len(self._main_area.selected) < 1:
            return
        value = not self.__attribute_values()["underline"]
        self._main_area.set_underline(value)

    def __foreground_color_cb(self, button):
        color = button.get_color()
        self._main_area.set_foreground_color(color)

    def __background_color_cb(self, button):
        color = button.get_color()
        self._parent._main_area.set_background_color(color)

    def change_active_font(self):
        # TODO: update the toolbar
        return
Exemple #34
0
class ClipArtActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self._selected_image = None

        self.max_participants = 1

        toolbarbox = ToolbarBox()
        self.set_toolbar_box(toolbarbox)

        activity_button = ActivityToolbarButton(self)
        toolbarbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.save_button = ToolButton('image-save')
        self.save_button.set_tooltip(_('Save to Journal'))
        self.save_button.connect('clicked', self._save_to_journal)
        self.save_button.set_sensitive(False)
        self.save_button.show()
        toolbarbox.toolbar.insert(self.save_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbarbox.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbarbox.toolbar.insert(stop_button, -1)
        stop_button.show()

        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                   Gtk.PolicyType.AUTOMATIC)
        self.set_canvas(scrolled_window)
        scrolled_window.show()

        store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

        icon_view = Gtk.IconView.new_with_model(store)
        icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
        icon_view.connect('selection-changed', self._clipart_selected, store)
        icon_view.set_pixbuf_column(0)
        rgba = Gdk.RGBA()
        rgba.red, rgba.green, rgba.blue, rgba.alpha = 0.67, 0.67, 0.67, 1.0
        icon_view.override_background_color(Gtk.StateFlags.NORMAL, rgba)
        icon_view.grab_focus()
        scrolled_window.add(icon_view)
        icon_view.show()

        toolbarbox.show_all()
        self.show_all()

        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
        self._notify()

        GObject.idle_add(_fill_clipart_list, store)

    def _save_to_journal(self, widget):
        if self._selected_image is None:
            return

        basename = os.path.basename(self._selected_image)
        dsobject = datastore.create()
        dsobject.metadata['title'] = basename[:-4]
        dsobject.metadata['icon-color'] = profile.get_color().to_string()
        dsobject.metadata['mime_type'] = MIME_TYPES[basename.split('.')[-1]]
        dsobject.set_file_path(self._selected_image)
        datastore.write(dsobject)
        dsobject.destroy()

        self.save_button.set_sensitive(False)

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _clipart_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_image = None
            self.save_button.set_sensitive(False)
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_image = image_path
        self.save_button.set_sensitive(True)

    def _notify(self):
        alert = ErrorAlert()
        alert.props.title = _('Scanning for clipart')
        msg = _('Please wait.')
        alert.props.msg = msg

        def remove_alert(alert, response_id):
            self.get_window().set_cursor(None)
            self.remove_alert(alert)

        alert.connect('response', remove_alert)
        self.add_alert(alert)
Exemple #35
0
    def __init__(self, handle):
        super(PeterActivity, self).__init__(handle)

        # Get user's Sugar colors
        sugarcolors = profile.get_color().to_string().split(',')
        colors = [[int(sugarcolors[0][1:3], 16),
                   int(sugarcolors[0][3:5], 16),
                   int(sugarcolors[0][5:7], 16)],
                  [int(sugarcolors[1][1:3], 16),
                   int(sugarcolors[1][3:5], 16),
                   int(sugarcolors[1][5:7], 16)]]

        # No sharing
        self.max_participants = 1

        # Build the activity toolbar.
        toolbox = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        toolbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        self._add_speed_slider(toolbox.toolbar)

        cyan = ToolButton('cyan')
        toolbox.toolbar.insert(cyan, -1)
        cyan.set_tooltip(_('Next pattern'))
        cyan.connect('clicked', self._button_cb, 'cyan')
        cyan.set_sensitive(False)
        cyan.show()

        green = ToolButton('green')
        toolbox.toolbar.insert(green, -1)
        green.set_tooltip(_('Draw'))
        green.connect('clicked', self._button_cb, 'green')
        green.show()

        red = ToolButton('red')
        toolbox.toolbar.insert(red, -1)
        red.set_tooltip(_('Stop'))
        red.connect('clicked', self._button_cb, 'red')
        red.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        toolbox.toolbar.insert(separator, -1)
        separator.show()

        label = Gtk.Label('')
        label.set_use_markup(True)
        label.show()
        labelitem = Gtk.ToolItem()
        labelitem.add(label)
        toolbox.toolbar.insert(labelitem, -1)
        labelitem.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = _('<Ctrl>Q')
        toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

        toolbox.show()
        self.set_toolbar_box(toolbox)

        # Create the game instance.
        self.game = Spirolaterals.Spirolaterals(colors)

        # Build the Pygame canvas.
        self._pygamecanvas = \
            sugargame.canvas.PygameCanvas(self)
        # Note that set_canvas implicitly calls
        # read_file when resuming from the Journal.
        self.set_canvas(self._pygamecanvas)
        self.game.canvas = self._pygamecanvas

        Gdk.Screen.get_default().connect('size-changed',
                                         self.__configure_cb)

        # Start the game running.
        self.game.set_cyan_button(cyan)
        self.game.set_label(label)
        self._speed_range.set_value(200)
        self._pygamecanvas.run_pygame(self.game.run)
Exemple #36
0
class RecordControl():

    def __init__(self, toolbar):

        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)
        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)
        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)
        toolbar.insert(self._quality_button, -1)
        self._setup_quality_palette()

    def _timer_selection_cb(self, widget):
        if self._timer_palette:

            if not self._timer_palette.is_up():
                self._timer_palette.popup(immediate=True)
            else:
                self._timer_palette.popdown(immediate=True)
            return

    def _setup_timer_palette(self):
        self._timer_palette = self._timer_button.get_palette()
        box = PaletteMenuBox()
        self._timer_palette.set_content(box)
        box.show()

        for seconds in TIMER_VALUES:
            if seconds == 0:
                text = _('Immediate')
            else:
                text = ngettext('%s second', '%s seconds', seconds) % seconds
            menu_item = PaletteMenuItem(text, icon_name='timer-%d' % (seconds))
            menu_item.connect('activate', self._timer_selected_cb, seconds)
            box.append_item(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)
            else:
                self._duration_palette.popdown(immediate=True)
            return

    def _setup_duration_palette(self):
        self._duration_palette = self._duration_button.get_palette()
        box = PaletteMenuBox()
        self._duration_palette.set_content(box)
        box.show()

        for minutes in DURATION_VALUES:
            if minutes == 0:
                text = Gtk.Label(_('Immediate'))
            else:
                text = ngettext('%s minute', '%s minutes', minutes) % minutes
            menu_item = PaletteMenuItem(text,
                                        icon_name='duration-%d' % (minutes))
            menu_item.connect('activate', self._duration_selected_cb, minutes)
            box.append_item(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)
            else:
                self._quality_palette.popdown(immediate=True)
            return

    def _setup_quality_palette(self):
        self._quality_palette = self._quality_button.get_palette()
        box = PaletteMenuBox()
        self._quality_palette.set_content(box)
        box.show()

        for quality in QUALITY_VALUES:
            text = _('%s quality') % (quality)
            menu_item = PaletteMenuItem(text, icon_name=quality + '-quality')
            menu_item.connect('activate', self._quality_selected_cb, quality)
            box.append_item(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_name('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_name(
                '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'):
            name = '%s-quality' % (QUALITY_VALUES[idx])
            self._quality_button.set_icon_name(name)
class SimplePianoActivity(activity.Activity):
    """SimplePianoActivity class as specified in activity.info"""

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.close)
        Gst.init(None)

        self._what_list = []

        self.play_recording_thread = None

        self.playing_recording = False
        self.firstTime = False
        self.playing = False
        self.regularity = 0.7
        self._drums_store = []
        self.recording = False
        self.recorded_keys = []
        self.is_valid_recording = False

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1
        self.csnd = new_csound_client()
        self.rythmInstrument = 'drum1kick'
        # toolbar with the new toolbar redesign
        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        self.play_index = 0

        self.play_recording_button = ToolButton(
            icon_name='media-playback-start')
        self.play_recording_button.set_property('can-default', True)
        self.play_recording_button.show()
        self.record_button = ToggleToolButton(icon_name='media-record')
        self.record_button.set_property('can-default', True)
        self.record_button.show()
        self.play_recording_button.set_sensitive(False)

        self.record_button.connect('clicked', self.__record_button_click_cb)

        self.play_recording_button.connect('clicked',
                                           self.handlePlayRecordingButton)

        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        # TODO: disabe until is implemented with csnd6
        # self.createPercussionToolbar(toolbar_box)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        keybord_labels = RadioToolButton()
        keybord_labels.props.icon_name = 'q_key'
        keybord_labels.props.group = keybord_labels
        keybord_labels.connect('clicked', self.set_keyboard_labels_cb)
        toolbar_box.toolbar.insert(keybord_labels, -1)

        notes_labels = RadioToolButton()
        notes_labels.props.icon_name = 'do_key'
        notes_labels.props.group = keybord_labels
        notes_labels.connect('clicked', self.set_notes_labels_cb)
        toolbar_box.toolbar.insert(notes_labels, -1)

        ti_notes_labels = RadioToolButton()
        ti_notes_labels.props.icon_name = 'ti_key'
        ti_notes_labels.props.group = keybord_labels
        ti_notes_labels.connect('clicked', self.set_ti_notes_labels_cb)
        toolbar_box.toolbar.insert(ti_notes_labels, -1)

        german_labels = RadioToolButton()
        german_labels.props.icon_name = 'c_key'
        german_labels.props.group = keybord_labels
        german_labels.connect('clicked', self.set_german_labels_cb)
        toolbar_box.toolbar.insert(german_labels, -1)

        no_labels = RadioToolButton()
        no_labels.props.icon_name = 'edit-clear'
        no_labels.props.group = keybord_labels
        no_labels.connect('clicked', self.set_keyboard_no_labels_cb)
        toolbar_box.toolbar.insert(no_labels, -1)
        self._what_widget = Gtk.ToolItem()
        self._what_search_button = FilterToolItem(_('Select Instrument'),
                                                  'view-type',
                                                  _('Piano'),
                                                  self._what_widget)
        self._what_widget.show()
        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(self._what_search_button, -1)
        self._what_search_button.show()
        self._what_search_button.set_is_important(True)
        self._what_widget_contents = None
        self._what_drum_widget_contents = None

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)

        toolbar_box.toolbar.insert(self.record_button, -1)
        toolbar_box.toolbar.insert(self.play_recording_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self._save_as_audio_bt = ToolButton(icon_name='save-as-audio')
        self._save_as_audio_bt.props.tooltip = _('Save as audio')
        self._save_as_audio_bt.connect('clicked', self._save_ogg_cb)
        self._save_as_audio_bt.show()
        self._save_as_audio_bt.set_sensitive(False)
        activity_button.page.insert(self._save_as_audio_bt, -1)

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        self.keyboard_letters = ['ZSXDCVGBHNJM', 'Q2W3ER5T6Y7U', 'I']

        notes = ['DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
                 ['FA#', 'SOLb'], 'SOL',
                 ['SOL#', 'LAb'], 'LA', ['LA#', 'SIb'], 'SI']
        self.notes_labels = [notes, notes, ['DO']]

        # some countries use TI instead of SI
        ti_notes = ['DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
                    ['FA#', 'SOLb'], 'SOL',
                    ['SOL#', 'LAb'], 'LA', ['LA#', 'TIb'], 'TI']
        self.ti_notes_labels = [ti_notes, ti_notes, ['DO']]

        german_notes = ['C', ['C#', 'Db'], 'D', ['D#', 'Eb'], 'E', 'F',
                        ['F#', 'Gb'], 'G',
                        ['G#', 'Ab'], 'A', ['A#', 'Bb'], 'B']

        self.german_labels = [german_notes, german_notes, ['C']]

        self.piano = PianoKeyboard(octaves=2, add_c=True,
                                   labels=self.keyboard_letters)

        # init csound
        self.instrumentDB = InstrumentDB.getRef()
        self.timeout_ms = 50
        self.instVolume = 50
        self.drumVolume = 0.5
        self.instrument = 'piano'
        self.beat = 4
        self.reverb = 0.1
        self.tempo = PLAYER_TEMPO
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0

        self.sequencer = MiniSequencer(self.recordStateButton,
                                       self.recordOverSensitivity)
        self.loop = Loop(self.beat, math.sqrt(self.instVolume * 0.01))

        self.drumFillin = Fillin(self.beat,
                                 self.tempo,
                                 self.rythmInstrument,
                                 self.reverb,
                                 self.drumVolume)

        self.muteInst = False
        self.csnd.setTempo(self.tempo)
        self.noteList = []
        for i in range(21):
            self.csnd.setTrackVolume(100, i)

        # TODO commented because apparently are not used in the activity
        # for i in range(10):
        #     self.csnd.load_instrument('guidice' + str(i + 1))

        self.volume = 100
        self.csnd.setMasterVolume(self.volume)

        self.enableKeyboard()
        self.setInstrument(self.instrument)

        self.connect('key-press-event', self.onKeyPress)
        self.connect('key-release-event', self.onKeyRelease)

        self.piano.connect('key_pressed', self.__key_pressed_cb)
        self.piano.connect('key_released', self.__key_released_cb)
        vbox = Gtk.VBox()
        vbox.set_homogeneous(False)
        self.load_instruments()
        self._event_box = Gtk.EventBox()
        self._event_box.modify_bg(
            Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color())
        vbox.pack_start(self._event_box, False, False, 0)
        vbox.pack_end(self.piano, True, True, 0)
        vbox.show_all()
        self.set_canvas(vbox)
        piano_height = Gdk.Screen.width() / 2
        self._event_box.set_size_request(
            -1, Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        self.connect('size-allocate', self.__allocate_cb)

        # TODO: disabe until is implemented with csnd6
        # GLib.idle_add(self.initializePercussion)

    def createPercussionToolbar(self, toolbar_box):

        self.beats_pm_button = IntensitySelector(range(2, 13),
                                                 4,
                                                 imagefile('beat3.svg'))
        self.tempo_button = \
            IntensitySelector(range(PLAYER_TEMPO_LOWER,
                                    PLAYER_TEMPO_UPPER + 1, PLAYER_TEMPO_STEP),
                              PLAYER_TEMPO, imagefile('tempo5.png'))

        self.complexity_button = IntensitySelector(xfrange(0, 1, 0.1),
                                                   self.regularity,
                                                   imagefile('complex6.svg'))

        self._play_percussion_btn = ToolButton(
            icon_name='media-playback-start')
        self._play_percussion_btn.set_property('can-default', True)
        self._play_percussion_btn.show()
        self._play_percussion_btn.connect('clicked', self.handlePlayButton)

        beats_toolbar = ToolbarBox()
        beats_toolbar.toolbar.insert(self._play_percussion_btn, -1)

        self._what_drum_widget = Gtk.ToolItem()
        self._what_drum_search_button = FilterToolItem(
            _('Select Drum'), 'view-type', _('Jazz / Rock Kit'),
            self._what_drum_widget)
        self._what_drum_search_button.set_widget_icon(
            file_name=imagefile("drum1kit.svg"))

        self._what_drum_widget.show()
        beats_toolbar.toolbar.insert(self._what_drum_search_button, -1)
        self._what_drum_search_button.show()
        self._what_drum_search_button.set_is_important(True)

        beats_toolbar.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        beats_toolbar.toolbar.insert(self.complexity_button, -1)
        beats_toolbar.toolbar.insert(self.beats_pm_button, -1)
        beats_toolbar.toolbar.insert(self.tempo_button, -1)

        beats_toolbar_button = ToolbarButton(icon_name='toolbar-drums',
                                             page=beats_toolbar)
        beats_toolbar_button.show()

        toolbar_box.toolbar.insert(beats_toolbar_button, 1)

        self.beats_pm_button.set_tooltip(_("Beats per bar"))
        self.beats_pm_button.show()
        self.beats_pm_button.connect('changed', self.beatSliderChange, True)
        self.tempo_button.connect('changed', self.tempoSliderChange, True)
        self.complexity_button.connect('changed',
                                       self.handleComplexityChange,
                                       True)
        self.complexity_button.set_tooltip(_("Beat complexity"))
        self.tempo_button.show()
        self.tempo_button.set_tooltip(_('Tempo'))
        self.complexity_button.show()

    def initializePercussion(self):
        self.rythmInstrument = 'drum1kit'
        self.csnd.load_drumkit(self.rythmInstrument)
        self.csnd.setTempo(self.tempo)
        self.beatPickup = False

        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval

        noteOnsets = []
        notePitchs = []

        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
            generator(self.rythmInstrument, self.beat,
                      0.8, self.regularity, self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)                    # add as active

        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)

        if self.playing:
            self.csnd.loopStart()

    def __allocate_cb(self, widget, rect):
        GLib.idle_add(self.resize, rect.width, rect.height)
        return False

    def resize(self, width, height):
        logging.debug('activity.py resize......')
        piano_height = width / 2
        self._event_box.set_size_request(
            -1, Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        return False

    def load_instruments(self):
        self._instruments_store = []

        # load the images
        images_path = os.path.join(activity.get_bundle_path(),
                                   'instruments')
        logging.debug('Loading instrument images from %s', images_path)
        for file_name in os.listdir(images_path):
            image_file_name = os.path.join(images_path, file_name)
            pxb = GdkPixbuf.Pixbuf.new_from_file_at_size(
                image_file_name, 75, 75)
            # instrument_name = image_file_name[image_file_name.rfind('/'):]
            instrument_name = image_file_name[image_file_name.rfind('/') + 1:]
            instrument_name = instrument_name[:instrument_name.find('.')]
            instrument_desc = \
                self.instrumentDB.instNamed[instrument_name].nameTooltip

            file_path = os.path.join(images_path, file_name)

            # set the default icon
            if (instrument_name == 'piano'):
                self._what_search_button.set_widget_icon(
                    file_name=file_path)

            self._instruments_store.append(
                {"instrument_name": instrument_name,
                 "pxb": pxb,
                 "instrument_desc": instrument_desc,
                 "file_name": file_path,
                 "callback": self.__instrument_iconview_activated_cb})

        self._what_widget_contents = set_palette_list(self._instruments_store)
        self._what_widget.add(self._what_widget_contents)
        self._what_widget_contents.show()

        # TODO: disabe until is implemented with csnd6
        """
        for drum_number in range(0, DRUMCOUNT):
            drum_name = 'drum%dkit' % (drum_number + 1)
            self._drums_store.append({
                "instrument_name": drum_name,
                "file_name": imagefile(drum_name + '.svg'),
                "instrument_desc":
                    self.instrumentDB.instNamed[drum_name].nameTooltip,
                "callback": self.__drum_iconview_activated_cb
            })

        self._what_drum_widget_contents = set_palette_list(self._drums_store)
        self._what_drum_widget.add(self._what_drum_widget_contents)
        self._what_drum_widget_contents.show()
        """

    def __drum_iconview_activated_cb(self, widget, event, item):
        data = item['instrument_name']
        self.rythmInstrument = data
        self.csnd.load_drumkit(data)
        instrumentId = self.instrumentDB.instNamed[data].instrumentId
        for (o, n) in self.noteList:
            self.csnd.loopUpdate(n, NoteDB.PARAMETER.INSTRUMENT,
                                 instrumentId, -1)
        self.drumFillin.setInstrument(self.rythmInstrument)
        self._what_drum_search_button.set_widget_label(
            label=item['instrument_desc'])
        self._what_drum_search_button.set_widget_icon(
            file_name=item['file_name'])

    def __instrument_iconview_activated_cb(self, widget, event, item):
        self.setInstrument(item['instrument_name'])
        self._what_search_button.set_widget_icon(file_name=item['file_name'])
        self._what_search_button.set_widget_label(
            label=item['instrument_desc'])

    def set_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.notes_labels)

    def set_ti_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.ti_notes_labels)

    def set_keyboard_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.keyboard_letters)

    def set_german_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.german_labels)

    def beatSliderChange(self, widget, event):
        self.beat = int(self.beats_pm_button.get_value())
        self.sequencer.beat = self.beat
        self.loop.beat = self.beat
        self.drumFillin.setBeats(self.beat)
        img = int(self.scale(self.beat, 2, 12, 1, 11))
        self.beats_pm_button.set_image(imagefile('beat' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    def regenerate(self):
        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval
        noteOnsets = []
        notePitchs = []
        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
            generator(self.rythmInstrument,
                      self.beat, 0.8, self.regularity,
                      self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)                    # add as active
        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)
        self.recordOverSensitivity(False)
        if self.playing:
            self.csnd.loopStart()

    def handlePlayRecordingButton(self, val):
        if not self.playing_recording:
            self.playing_recording = True
            self.play_recording_button.props.icon_name = 'media-playback-stop'
            self.play_recording_thread = \
                GLib.timeout_add(100, self._play_recorded_keys)
        else:
            self.playing_recording = False
            self.play_recording_button.props.icon_name = 'media-playback-start'

    def _save_ogg_cb(self, widget):
        self._wav_tempfile = tempfile.NamedTemporaryFile(
            mode='w+b', suffix='.wav', dir='/tmp/')
        self.csnd.inputMessage(Config.CSOUND_RECORD_PERF %
                               self._wav_tempfile.name)

        self.playing_recording = True
        self.play_recording_thread = \
            GLib.timeout_add(100, self._play_recorded_keys,
                             self._save_ogg_end_cb)

    def _save_ogg_end_cb(self):
        self.csnd.inputMessage(Config.CSOUND_STOP_RECORD_PERF %
                               self._wav_tempfile.name)

        self._ogg_tempfile = tempfile.NamedTemporaryFile(
            mode='w+b', suffix='.ogg', dir='/tmp/')

        line = 'filesrc location=%s ! ' \
            'wavparse ! audioconvert ! vorbisenc ! oggmux ! ' \
            'filesink location=%s' % (self._wav_tempfile.name,
                                      self._ogg_tempfile.name)
        pipe = Gst.parse_launch(line)
        pipe.get_bus().add_signal_watch()
        pipe.get_bus().connect('message::eos', self._save_ogg_eos_cb, pipe)
        pipe.set_state(Gst.State.PLAYING)

    def _save_ogg_eos_cb(self, bus, message, pipe):
        bus.remove_signal_watch()
        pipe.set_state(Gst.State.NULL)

        title = '%s saved as audio' % self.metadata['title']

        jobject = datastore.create()
        jobject.metadata['title'] = title
        jobject.metadata['keep'] = '0'
        jobject.metadata['mime_type'] = 'audio/ogg'
        jobject.file_path = self._ogg_tempfile.name
        datastore.write(jobject)

        self._wav_tempfile.close()
        self._ogg_tempfile.close()

        alert = NotifyAlert(10)
        alert.props.title = _('Audio recorded')
        alert.props.msg = _('The audio file was saved in the Journal')
        alert.connect('response', self.__alert_response_cb)
        self.add_alert(alert)

        return False

    def __alert_response_cb(self, alert, result):
        self.remove_alert(alert)

    def __record_button_click_cb(self, button):
        if not self.recording:
            self.play_recording_button.set_sensitive(False)
            self._save_as_audio_bt.set_sensitive(False)
            self.recorded_keys = []
            self.recording = True
            icon = Icon(icon_name='media-record', fill_color='#ff0000')
            icon.show()
            self.record_button.set_icon_widget(icon)
        else:
            self.recording = False
            icon = Icon(icon_name='media-record', fill_color='#ffffff')
            icon.show()
            self.record_button.set_icon_widget(icon)
            if len(self.recorded_keys) != 0:
                self.play_recording_button.set_sensitive(True)
                self._save_as_audio_bt.set_sensitive(True)

    def tempoSliderChange(self, widget, event):
        self._updateTempo(self.tempo_button.get_value())
        img = int(self.scale(self.tempo, PLAYER_TEMPO_LOWER,
                             PLAYER_TEMPO_UPPER, 1, 9))
        self.tempo_button.set_image(imagefile('tempo' + str(img) + '.png'))

    def _updateTempo(self, val):
        self.tempo = val
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0
        self.csnd.setTempo(self.tempo)
        self.sequencer.tempo = self.tempo
        self.drumFillin.setTempo(self.tempo)

    def handlePlayButton(self, val):
        if not self.playing:
            if not self.firstTime:
                self.regenerate()
                self.firstTime = True
            self.drumFillin.play()
            self.csnd.loopSetTick(0)
            self.csnd.loopStart()
            self.playing = True
            self._play_percussion_btn.props.icon_name = 'media-playback-stop'
        else:
            self.drumFillin.stop()
            self.sequencer.stopPlayback()
            self.csnd.loopPause()
            self.playing = False
            self._play_percussion_btn.props.icon_name = 'media-playback-start'

    def scale(self, input, input_min, input_max,
              output_min, output_max):
        range_input = input_max - input_min
        range_output = output_max - output_min
        result = (input - input_min) * range_output / range_input + output_min

        if (input_min > input_max and output_min > output_max) or \
           (output_min > output_max and input_min < input_max):
            if result > output_min:
                return output_min
            elif result < output_max:
                return output_max
            else:
                return result

        if (input_min < input_max and output_min < output_max) or \
           (output_min < output_max and input_min > input_max):
            if result > output_max:
                return output_max
            elif result < output_min:
                return output_min
            else:
                return result

    def handleComplexityChange(self, widget, event):
        self.regularity = self.complexity_button.get_value()
        img = int(self.complexity_button.get_value() * 7) + 1
        self.complexity_button.set_image(
            imagefile('complex' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    """
    def handleBalanceSlider(self, adj):
        self.instVolume = int(adj.get_value())
        self.drumVolume = sqrt( (100-self.instVolume)*0.01 )
        self.adjustDrumVolume()
        self.drumFillin.setVolume(self.drumVolume)
        instrumentVolume = sqrt( self.instVolume*0.01 )
        self.loop.adjustLoopVolume(instrumentVolume)
        self.sequencer.adjustSequencerVolume(instrumentVolume)
        img = int(self.scale(self.instVolume,100,0,0,4.9))
        self._playToolbar.balanceSliderImgLeft.set_from_file(
                imagefile('dru' + str(img) + '.png'))
        img2 = int(self.scale(self.instVolume,0,100,0,4.9))
        self._playToolbar.balanceSliderImgRight.set_from_file(
                imagefile('instr' + str(img2) + '.png'))

    def handleReverbSlider(self, adj):
        self.reverb = adj.get_value()
        self.drumFillin.setReverb( self.reverb )
        img = int(self.scale(self.reverb,0,1,0,4))
        self._playToolbar.reverbSliderImgRight.set_from_file(
                imagefile('reverb' + str(img) + '.png'))
        self.keyboardStandAlone.setReverb(self.reverb)
    """
    def set_keyboard_no_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(None)

    def enableKeyboard(self):
        self.keyboardStandAlone = KeyboardStandAlone(
            self.sequencer.recording, self.sequencer.adjustDuration,
            self.csnd.loopGetTick, self.sequencer.getPlayState, self.loop)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def setInstrument(self, instrument):
        logging.debug("Set Instrument: %s" % instrument)
        self.instrument = instrument
        self.keyboardStandAlone.setInstrument(instrument)
        self.csnd.load_instrument(instrument)

    def recordStateButton(self, button, state):
        pass
#        if button == 1:
#            self._recordToolbar.keyboardRecButton.set_active( state )
#        else:
#            self._recordToolbar.keyboardRecOverButton.set_active( state )

    def recordOverSensitivity(self, state):
        pass
        # self._recordToolbar.keyboardRecOverButton.set_sensitive( state )

    def _play_recorded_keys(self, end_cb=None):
        GLib.source_remove(self.play_recording_thread)
        letter = self.recorded_keys[self.play_index]
        time_difference = 0
        if self.play_index == len(self.recorded_keys) - 1:
            time_difference = \
                self.recorded_keys[self.play_index][0] - \
                self.recorded_keys[self.play_index - 1][0]
        else:
            next_time = self.recorded_keys[self.play_index + 1][0]
            time_difference = next_time - letter[0]

        if not self.playing_recording:
            self.play_recording_button.props.icon_name = 'media-playback-start'
            return

        if letter[-1] == 1:
            self.keyboardStandAlone.do_key_release(
                LETTERS_TO_KEY_CODES[letter[3]])
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], False)
        else:
            self.keyboardStandAlone.do_key_press(
                LETTERS_TO_KEY_CODES[letter[3]], None,
                math.sqrt(self.instVolume * 0.01))
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], True)

        if self.play_index == len(self.recorded_keys) - 1:
            self.play_index = 0
            self.play_recording_button.props.icon_name = 'media-playback-start'
            self.playing_recording = False
            if end_cb is not None:
                end_cb()
        else:
            self.play_index += 1
            self.play_recording_thread = \
                GLib.timeout_add(int((time_difference) * 1000),
                                 self._play_recorded_keys, end_cb)

    def __key_pressed_cb(self, widget, octave_clicked, key_clicked, letter,
                         physicallKey):
        logging.debug(
            'Pressed Octave: %d Key: %d Letter: %s' %
            (octave_clicked, key_clicked, letter))
        if letter in LETTERS_TO_KEY_CODES.keys():
            if self.recording:
                self.recorded_keys.append(
                    [time.time(), octave_clicked, key_clicked, letter])
            if not physicallKey:
                self.keyboardStandAlone.do_key_press(
                    LETTERS_TO_KEY_CODES[letter], None,
                    math.sqrt(self.instVolume * 0.01))

    def __key_released_cb(self, widget, octave_clicked, key_clicked, letter,
                          physicallKey):
        if self.recording:
            self.recorded_keys.append(
                [time.time(), octave_clicked, key_clicked, letter, 1])
        if not physicallKey:
            if letter in LETTERS_TO_KEY_CODES.keys():
                self.keyboardStandAlone.do_key_release(
                    LETTERS_TO_KEY_CODES[letter])

    def onKeyPress(self, widget, event):
        if event.state & Gdk.ModifierType.CONTROL_MASK:
            return
        if event.hardware_keycode == 37:
            if self.muteInst:
                self.muteInst = False
            else:
                self.muteInst = True
        self.piano.physical_key_changed(event.hardware_keycode, True)
        self.keyboardStandAlone.onKeyPress(
            widget, event, math.sqrt(self.instVolume * 0.01))

    def onKeyRelease(self, widget, event):
        self.keyboardStandAlone.onKeyRelease(widget, event)
        self.piano.physical_key_changed(event.hardware_keycode, False)

    def write_file(self, file_path):
        f = open(file_path, 'w')
        # substract the initial time to all the saved values
        if len(self.recorded_keys) > 0:
            initial_time = self.recorded_keys[0][0]
            for key in self.recorded_keys:
                key[0] = key[0] - initial_time

        f.write(json.dumps(self.recorded_keys))
        f.close()

    def read_file(self, file_path):
        f = open(file_path, 'r')
        contents = f.read().strip()

        self.recorded_keys = json.loads(contents)
        if len(self.recorded_keys) != 0:
            self.play_recording_button.set_sensitive(True)
            self._save_as_audio_bt.set_sensitive(True)
        f.close()

    def close(self, skip_save=False):
        self.csnd.stop()  # without which Csound will segfault
        activity.Activity.close(self, skip_save=skip_save)
Exemple #38
0
class FileChooserOpen(Gtk.Window):

    __gsignals__ = {'open-file': (GObject.SIGNAL_RUN_FIRST, None, [str])}

    def __init__(self, activity, folder=None):
        Gtk.Window.__init__(self)

        self._activity = activity
        self.vbox = Gtk.VBox()
        self.view = Gtk.IconView()
        self.model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)

        self.folder = folder or os.path.expanduser('~/')
        self.files = []
        self.show_hidden_files = False

        if os.path.isfile(self.folder):
            self.folder = self.go_up(self.folder, _return=True)

        self.set_modal(True)
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_size_request(WIDTH, HEIGHT)
        self.set_border_width(style.LINE_WIDTH)
        self.add_events(Gdk.EventMask.KEY_RELEASE_MASK)
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.modify_bg(Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color())
        self.view.set_model(self.model)
        self.view.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
        self.view.set_text_column(0)
        self.view.set_pixbuf_column(1)

        self.connect('key-release-event', self.__key_release_event_cb)
        self.view.connect('selection-changed', self.__selection_changed)
        self.view.connect('button-press-event', self.__button_press_event_cb)

        scrolled = Gtk.ScrolledWindow()
        scrolled.add(self.view)

        self.__make_toolbar()
        self.show_folder()

        self.vbox.pack_end(scrolled, True, True, 0)
        self.add(self.vbox)
        self.show_all()

        GObject.timeout_add(500, self.check_files)

    def __make_toolbar(self):
        self.toolbar = Gtk.Toolbar()
        self.toolbar.modify_bg(Gtk.StateType.NORMAL,
                               style.COLOR_TOOLBAR_GREY.get_gdk_color())

        self.go_up_button = ToolButton(icon_name='go-up')
        self.go_up_button.props.accelerator = '<Alt>S'
        self.go_up_button.set_tooltip(_('Go to pather directory'))
        self.go_up_button.connect('clicked', self.go_up)
        self.toolbar.insert(self.go_up_button, -1)

        self.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.hidden_files_button = ToggleToolButton('show-hidden-files')
        self.hidden_files_button.props.accelerator = '<Ctrl>H'
        self.hidden_files_button.set_tooltip(_('Show hidden files'))
        self.hidden_files_button.connect('clicked', self.__show_hidden_files)
        self.toolbar.insert(self.hidden_files_button, -1)

        self.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        item = Gtk.ToolItem()
        self.entry = Gtk.Entry()
        self.entry.set_size_request(300, -1)
        self.entry.set_text(self.folder)
        self.entry.connect('activate', self.__open_path_from_entry)
        item.add(self.entry)
        self.toolbar.insert(item, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.toolbar.insert(separator, -1)

        self.button_open = ToolButton(icon_name='fileopen')
        self.button_open.set_tooltip(_('Open selected file'))
        self.button_open.set_sensitive(False)
        self.button_open.connect('clicked', self.__open_path)
        self.toolbar.insert(self.button_open, -1)

        self.close_button = ToolButton(icon_name='dialog-cancel')
        self.close_button.connect('clicked', self.close)
        self.toolbar.insert(self.close_button, -1)

        self.vbox.pack_start(self.toolbar, False, False, 0)

    def __key_release_event_cb(self, widget, event):
        if event.keyval == 65293:  # Enter
            self.__open_path()

    def go_up(self, button=None, _return=False):
        path = '/'
        folders = []

        if self.folder == '/':
            return

        for folder in self.folder.split('/'):
            if not folder:
                continue
            folders.append(folder)

        if not folders:
            return

        for folder in folders[:-1]:
            if folder:
                path += folder + '/'

        if not _return:
            self.folder = path
            self.check_files()
        else:
            return path

    def __show_hidden_files(self, button):
        self.show_hidden_files = button.get_active()
        self.show_folder()

    def __open_path(self, button=None):
        if len(self.view.get_selected_items()) == 1:
            if os.path.isdir(self.selected_path):
                self.folder = self.selected_path
                self.selected_path = None

            elif os.path.isfile(self.selected_path):
                self.emit('open-file', self.selected_path)

        else:
            files = []
            directory = None

            for item in self.view.get_selected_items():
                iter = self.model.get_iter(item)
                path = os.path.join(self.folder, self.model.get_value(iter, 0))

                if os.path.isfile(path):
                    files.append(path)

                elif os.path.isdir(path) and not directory:
                    directory = path

            if files:
                for path in files:
                    GObject.idle_add(self.emit, 'open-file', path)
                    self.emit('open-file', path)
                    self.selected_path = None

            else:
                if directory:
                    self.folder = directory
                    return  # Evit destroy

            self.destroy()

    def __open_path_from_entry(self, entry):
        path = self.entry.get_text()

        if not os.path.exists(path):
            self.create_alert(path)
            return

        if os.path.isdir(path):
            self.folder = path
            self.check_files()

        elif os.path.isfile(path):
            self.emit('open-file', path)
            self.destroy()

    def create_alert(self, path):
        alert = TimeoutAlert(10)
        alert.props.title = _('This file not exists.')
        alert.props.msg = _(
            "Apparently you've selected a file that does not exist.")
        image = Gtk.Image.new_from_stock(Gtk.STOCK_OK, Gtk.IconSize.MENU)
        alert.add_button(Gtk.ResponseType.NO, _('Ok'), icon=image)

        alert.connect('response', self.__alert_response)

        self.vbox.pack_start(alert, False, False, 0)
        self.vbox.reorder_child(alert, 1)

    def __alert_response(self, alert):
        self.vbox.remove(alert)

    def check_files(self):
        files = os.listdir(self.folder)
        files.sort()

        if files != self.files:
            self.selected_path = None

            self.entry.set_text(self.folder)
            self.go_up_button.set_sensitive(self.folder != '/')

            self.show_folder()

        return True

    def show_folder(self):
        self.files = os.listdir(self.folder)
        self.files.sort()
        self.model.clear()

        folders = []
        files = []

        for x in self.files:
            path = os.path.join(self.folder, x)
            if os.path.isdir(path):
                folders.append(path)

            elif os.path.isfile(path):
                files.append(path)

        for path in folders + files:
            if not self.show_hidden_files:
                if path.endswith('~') or path.split('/')[-1].startswith('.'):
                    continue

            pixbuf = G.get_pixbuf_from_path(path)
            self.model.append([path.split('/')[-1], pixbuf])

    def __selection_changed(self, view):
        if self.view.get_selected_items():
            path = self.view.get_selected_items()[0]
            iter = self.model.get_iter(path)
            self.selected_path = os.path.join(self.folder,
                                              self.model.get_value(iter, 0))
            self.button_open.set_sensitive(True)

        else:
            self.selected_path = None
            self.button_open.set_sensitive(False)

    def __button_press_event_cb(self, view, event):
        if event.button != 1:
            return

        try:
            path = view.get_path_at_pos(int(event.x), int(event.y))
            iter = self.model.get_iter(path)
            directory = os.path.join(self.folder,
                                     self.model.get_value(iter, 0))

            if event.type.value_name == 'GDK_2BUTTON_PRESS':
                if os.path.isdir(directory):
                    self.folder = directory

                elif os.path.isfile(directory):
                    self.emit('open-file', directory)
                    self.destroy()

        except TypeError:
            self.selected_path = None
            self.button_open.set_sensitive(False)

    def close(self, button=None):
        self.destroy()
class IntensitySelector(Gtk.ToolItem):

    __gsignals__ = {
        'changed': (GObject.SignalFlags.RUN_LAST, None, ([])), }

    def __init__(self, value_range, default_value, default_image):

        Gtk.ToolItem.__init__(self)
        self._palette_invoker = ToolInvoker()

        self.palette = None
        self._values = value_range
        self._palette_invoker.attach_tool(self)

        # theme the buttons, can be removed if add the style to the sugar css
        # these are the same values used in gtk-widgets.css.em
        if style.zoom(100) == 100:
            subcell_size = 15
            default_padding = 6
        else:
            subcell_size = 11
            default_padding = 4

        hbox = Gtk.HBox()
        vbox = Gtk.VBox()
        self.add(vbox)
        # add a vbox to set the padding up and down
        vbox.pack_start(hbox, True, True, default_padding)

        self._size_down = ToolButton('go-previous-paired')
        self._palette_invoker.attach_tool(self._size_down)

        self._size_down.set_can_focus(False)
        self._size_down.connect('clicked', self.__value_changed_cb, False)
        hbox.pack_start(self._size_down, False, False, 5)

        # TODO: default?
        self._default_value = default_value
        self._value = self._default_value

        self.image_wrapper = Gtk.EventBox()
        self._intensityImage = Gtk.Image()
        self.image_wrapper.add(self._intensityImage)
        self.image_wrapper.show()
        self._intensityImage.set_from_file(default_image)
        self._intensityImage.show()
        self._palette_invoker.attach_widget(self.image_wrapper)

        hbox.pack_start(self.image_wrapper, False, False, 10)

        self._size_up = ToolButton('go-next-paired')

        self._palette_invoker.attach_tool(self._size_up)

        self._size_up.set_can_focus(False)
        self._size_up.connect('clicked', self.__value_changed_cb, True)
        hbox.pack_start(self._size_up, False, False, 5)

        radius = 2 * subcell_size
        theme_up = "GtkButton {border-radius:0px %dpx %dpx 0px;}" % (radius,
                                                                     radius)
        css_provider_up = Gtk.CssProvider()
        css_provider_up.load_from_data(theme_up)

        style_context = self._size_up.get_style_context()
        style_context.add_provider(css_provider_up,
                                   Gtk.STYLE_PROVIDER_PRIORITY_USER)

        theme_down = "GtkButton {border-radius: %dpx 0px 0px %dpx;}" % (radius,
                                                                        radius)
        css_provider_down = Gtk.CssProvider()
        css_provider_down.load_from_data(theme_down)
        style_context = self._size_down.get_style_context()
        style_context.add_provider(css_provider_down,
                                   Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.show_all()

    def __destroy_cb(self, **args):
        if self._palette_invoker is not None:
            self._palette_invoker.detach()

    def __value_changed_cb(self, button, increase):
        if self._value in self._values:
            i = self._values.index(self._value)
            if increase:
                if i < len(self._values) - 1:
                    i += 1
            else:
                if i > 0:
                    i -= 1
        else:
            i = self._values.index(self._default_value)

        self._value = self._values[i]
        self._size_down.set_sensitive(i != 0)
        self._size_up.set_sensitive(i < len(self._values) - 1)
        self.emit('changed')

    def set_value(self, val):
        if val not in self._values:
            # assure the val assigned is in the range
            # if not, assign one close.
            for v in self._values:
                if v > val:
                    val = v
                    break
            if val > self._values[-1]:
                size = self._values[-1]

        self._value = size
        self._size_label.set_text(str(self._value))

        # update the buttons states
        i = self._values.index(self._value)
        self._size_down.set_sensitive(i != 0)
        self._size_up.set_sensitive(i < len(self._value) - 1)
        self.emit('changed')

    def set_tooltip(self, tooltip):
        """ Set a simple palette with just a single label.
        """
        if self.palette is None or self._tooltip is None:
            self.palette = Palette(tooltip)
        elif self.palette is not None:
            self.palette.set_primary_text(tooltip)

        self._tooltip = tooltip

    def get_hide_tooltip_on_click(self):
        return self._hide_tooltip_on_click

    def set_hide_tooltip_on_click(self, hide_tooltip_on_click):
        if self._hide_tooltip_on_click != hide_tooltip_on_click:
            self._hide_tooltip_on_click = hide_tooltip_on_click

    hide_tooltip_on_click = GObject.Property(
        type=bool, default=True, getter=get_hide_tooltip_on_click,
        setter=set_hide_tooltip_on_click)

    def get_tooltip(self):
        return self._tooltip

    def create_palette(self):
        return None

    def get_palette(self):
        return self._palette_invoker.palette

    def set_palette(self, palette):
        self._palette_invoker.palette = palette

    palette = GObject.Property(
        type=object, setter=set_palette, getter=get_palette)

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.Property(
        type=object, setter=set_palette_invoker, getter=get_palette_invoker)

    def do_draw(self, cr):
        Gtk.ToolButton.do_draw(self, cr)

        if self.palette and self.palette.is_up():
            invoker = self.palette.props.invoker
            invoker.draw_rectangle(cr, self.palette)

        return False

    def do_clicked(self):
        if self._hide_tooltip_on_click and self.palette:
            self.palette.popdown(True)

    def set_image(self, image):
        self._intensityImage.set_from_file(image)

    def get_value(self):
        return self._value
class NavToolBar(Gtk.Toolbar):

    def __init__(self, activity, shared, deck):
        Gtk.Toolbar.__init__(self)

        self.__deck = deck
        self.__activity = activity
        self.__shared = shared
        self.__logger = logging.getLogger('Navigation Toolbar')

        self.__is_initiating = True

        self.__prevbtn = ToolButton('go-previous')
        self.__prevbtn.set_tooltip("Previous slide")
        self.__prevbtn.connect('clicked', self.previous)
        self.insert(self.__prevbtn, -1)
        self.__prevbtn.show()

        self.__nextbtn = ToolButton('go-next')
        self.__nextbtn.set_tooltip("Next slide")
        self.__nextbtn.connect('clicked', self.next)
        self.insert(self.__nextbtn, -1)
        self.__nextbtn.show()

        # page number widget and navigation
        self.__num_page_item = Gtk.ToolItem()
        self.__num_current_page = 1

        self.__num_page_entry = Gtk.Entry()
        self.__num_page_entry.set_text(str(self.__num_current_page))
        self.__num_page_entry.set_alignment(1)
        self.__num_page_entry.connect('activate', self.num_page_activate)

        self.__num_page_entry.set_width_chars(4)

        self.__num_page_item.add(self.__num_page_entry)
        self.__num_page_entry.show()

        self.insert(self.__num_page_item, -1)
        self.__num_page_item.show()

        # total page number widget
        self.__total_page_item = Gtk.ToolItem()
        self.__total_page_label = Gtk.Label(
            ' / ' + str(self.__deck.getSlideCount()))
        self.__total_page_label.modify_font(Pango.FontDescription("14"))
        self.__total_page_label.modify_fg(
            Gtk.StateType.NORMAL, Gdk.Color(
                65535, 65535, 65535))
        self.__total_page_item.add(self.__total_page_label)
        self.__total_page_label.show()

        self.insert(self.__total_page_item, -1)
        self.__total_page_item.show()

        # separator between navigation buttons and lock button
        separator = Gtk.SeparatorToolItem()
        separator.set_draw(False)
        separator.set_expand(True)
        self.insert(separator, -1)
        separator.show()

        # unlocked button
        self.__unlockBtn = ToolButton('unlocked')
        self.__unlockBtn.set_tooltip("Student Navigation Unlocked")

        # navigation is unlocked by default, so insert the unlock button
        self.insert(self.__unlockBtn, 5)
        self.__unlockBtn.show()

        # locked button
        self.__lockBtn = ToolButton('locked')
        self.__lockBtn.set_tooltip("Student Navigation Locked")

        self.__logger.debug(
            "Connecting to navigation locking and activity sharing signals.")
        self.__activity.connect('shared', self.activity_shared_cb)
        self.__activity.connect('joined', self.activity_joined_cb)
        self.__shared.connect('navigation-lock-change', self.set_lock_button)

        # triggers for when slides are changed
        self.__deck.connect("slide-changed", self.slide_changed)
        self.__deck.connect("deck-changed", self.slide_changed)
        self.slide_changed(self.__deck)
        self.show()

    def activity_shared_cb(self, widget):
        # Callback for when the activity is shared
        # bind the lock button click with switching lock mode
        self.__lockBtn.connect('clicked', self.__shared.lock_mode_switch)
        self.__unlockBtn.connect('clicked', self.__shared.lock_mode_switch)

    def activity_joined_cb(self, widget):
        """ Callback for when the activity is joined """
        self.__is_initiating = False

    def set_lock_button(self, widget, is_locked):
        self.__logger.debug("Changing lock button, lock mode %u, init %u",
                            is_locked, self.__is_initiating)
        if is_locked:
            new_button = self.__lockBtn
            if not self.__is_initiating:
                self.__prevbtn.set_sensitive(False)
                self.__nextbtn.set_sensitive(False)
        else:
            new_button = self.__unlockBtn
            if not self.__is_initiating:
                self.__prevbtn.set_sensitive(True)
                self.__nextbtn.set_sensitive(True)

        old = self.get_nth_item(5)
        self.remove(old)
        self.insert(new_button, 5)
        new_button.show()
        self.queue_draw()

    def next(self, widget):
        self.__deck.next()

    def previous(self, widget):
        self.__deck.previous()

    def slide_changed(self, widget):
        self.__logger.debug("Changing slides!")
        if self.__deck.isAtBeginning():
            self.__prevbtn.set_sensitive(False)
        else:
            self.__prevbtn.set_sensitive(True)
        if self.__deck.isAtEnd():
            self.__nextbtn.set_sensitive(False)
        else:
            self.__nextbtn.set_sensitive(True)

        self.__num_current_page = self.__deck.getIndex()
        self.__num_page_entry.set_text(str(self.__num_current_page + 1))
        self.__total_page_label.set_text(
            ' / ' + str(self.__deck.getSlideCount()))

    def num_page_activate(self, entry):
        page_entered = int(entry.get_text())

        if page_entered < 1:
            page_entered = 1
        elif self.__deck.getSlideCount() < page_entered:
            page_entered = self.__deck.getSlideCount()

        self.__deck.goToIndex(page_entered - 1, is_local=True)
Exemple #41
0
class ToolbarBox(SugarToolbarBox):

    __gsignals__ = {
        "show-simulation": (GObject.SIGNAL_RUN_FIRST, None, []),
        "show-info": (GObject.SIGNAL_RUN_FIRST, None, []),
        "go-back": (GObject.SIGNAL_RUN_FIRST, None, []),
        "go-forward": (GObject.SIGNAL_RUN_FIRST, None, []),
        "show-orbits": (GObject.SIGNAL_RUN_FIRST, None, [bool]),
        "show-body": (GObject.SIGNAL_RUN_FIRST, None, [int, bool]),
        "speed-changed": (GObject.SIGNAL_RUN_FIRST, None, [float]),
        "zoom-changed": (GObject.SIGNAL_RUN_FIRST, None, [float]),
    }

    def __init__(self, activity):
        SugarToolbarBox.__init__(self)

        activity_button = ActivityToolbarButton(activity)
        self.toolbar.insert(activity_button, -1)

        self.toolbar.insert(make_separator(False), -1)

        self.toolbar_info = ToolbarInfo()
        self.toolbar_info.connect("show-simulation", self._show_simulation_cb)
        self.toolbar_info.connect("show-info", self._show_info_cb)
        self.toolbar_info.connect("go-back", self._go_back_cb)
        self.toolbar_info.connect("go-forward", self._go_forward_cb)
        self.toolbar.insert(self.toolbar_info.button, -1)

        self.toolbar_view = ToolbarView()
        self.toolbar_view.connect("show-orbits", self._show_orbits_cb)
        self.toolbar_view.connect("show-body", self._show_body_cb)
        self.toolbar.insert(self.toolbar_view.button, -1)

        self.toolbar_speed = ToolbarSpeed()
        self.toolbar_speed.connect("speed-changed", self._speed_changed_cb)
        self.toolbar.insert(self.toolbar_speed.button, -1)

        self.toolbar.insert(make_separator(False), -1)

        adj = Gtk.Adjustment(16.5, 0.8, 75, 0.5, 1)
        self.zoom_scale = Gtk.HScale()
        self.zoom_scale.set_draw_value(False)
        self.zoom_scale.set_adjustment(adj)
        self.zoom_scale.set_size_request(200, 1)
        self.zoom_scale.connect("value-changed", self._zoom_changed_cb)

        self.zoom_out = ToolButton("zoom-out")
        self.zoom_out.set_tooltip(_("Zoom out"))
        self.zoom_out.connect("clicked", self._zoom_out_cb)
        self.toolbar.insert(self.zoom_out, -1)

        item = Gtk.ToolItem()
        item.add(self.zoom_scale)
        self.toolbar.insert(item, -1)

        self.zoom_in = ToolButton("zoom-in")
        self.zoom_in.set_tooltip(_("Zoom in"))
        self.zoom_in.connect("clicked", self._zoom_in_cb)
        self.toolbar.insert(self.zoom_in, -1)

        self.toolbar.insert(make_separator(True), -1)

        help_button = HelpButton()
        self.toolbar.insert(help_button, -1)

        stop_button = StopButton(activity)
        self.toolbar.insert(stop_button, -1)

        self.make_help(help_button)

    def make_help(self, button):
        button.add_section(_("What is the Solar System activity?"))
        button.add_paragraph(_("The Solar System activity is a tool to encourage children\n") +\
                             _("to learn more about the planets and their moons (natural satellites).\n") +\
                             _("It is my hope that this tool serves as a practical and interactve\n") +\
                             _("way to explore astronomy.\n"))

        button.add_section(_("About scales"))
        button.add_paragraph(_("The planets radius are on a scale with each other,\n") +\
                             _("but not with respect to distances (which in turn,\n") +\
                             _("are on scale with each other), and the Sun radius\n") +\
                             _("does not respect either of the two scales.\n"))

        button.add_section(_("Why did I make that decision?"))
        button.add_paragraph(_("Just because if I kept all the distances to the\n") +\
                             _("same scale, you could not see anything (due to the\n") +\
                             _("immense size of the space)."))

    def set_can_back_back(self, can):
        self.toolbar_info.button_back.set_sensitive(can)

    def set_can_go_forward(self, can):
        self.toolbar_info.forward_button.set_sensitive(can)

    def disable_simulation_widgets(self):
        self.zoom_scale.set_sensitive(False)
        self.zoom_out.set_sensitive(False)
        self.zoom_in.set_sensitive(False)
        self.toolbar_view.disable_simulation_widgets()
        self.toolbar_speed.disable()

    def enable_simulation_widgets(self):
        self.zoom_scale.set_sensitive(True)
        self.zoom_out.set_sensitive(True)
        self.zoom_in.set_sensitive(True)
        self.toolbar_view.enable_simulation_widgets()
        self.toolbar_speed.enable()

    def _zoom_out_cb(self, widget):
        new_value = self.zoom_scale.get_value() - 2.5
        lower_value = self.zoom_scale.get_adjustment().get_lower()
        if new_value < lower_value:
            self.zoom_scale.set_value(lower_value)

        else:
            self.zoom_scale.set_value(new_value)

    def _zoom_in_cb(self, widget):
        new_value = self.zoom_scale.get_value() + 2.5
        upper = self.zoom_scale.get_adjustment().get_upper()
        if new_value > upper:
            self.zoom_scale.set_value(upper)

        else:
            self.zoom_scale.set_value(new_value)

    def _show_simulation_cb(self, widget):
        self.emit("show-simulation")

    def _show_info_cb(self, widget):
        self.emit("show-info")

    def _go_back_cb(self, widget):
        self.emit("go-back")

    def _go_forward_cb(self, widget):
        self.emit("go-forward")

    def _speed_changed_cb(self, toolbar, speed):
        self.emit("speed-changed", speed)

    def _show_orbits_cb(self, toolbar, show):
        self.emit("show-orbits", show)

    def _show_body_cb(self, toolbar, body, show):
        self.emit("show-body", body, show)

    def _zoom_changed_cb(self, scale):
        self.emit("zoom-changed", self.zoom_scale.get_value())

    def select_screen(self, screen):
        self.toolbar_info.select_screen(screen)

    def set_can_go_forward(self, can):
        self.toolbar_info.forward_button.set_sensitive(can)

    def set_can_go_back(self, can):
        self.toolbar_info.back_button.set_sensitive(can)
Exemple #42
0
class SimplePianoActivity(activity.Activity):
    """SimplePianoActivity class as specified in activity.info"""
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.close)
        Gst.init(None)

        self._what_list = []

        self.play_recording_thread = None

        self.playing_recording = False
        self.firstTime = False
        self.playing = False
        self.regularity = 0.7
        self._drums_store = []
        self.recording = False
        self.recorded_keys = []
        self.is_valid_recording = False

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1
        self.csnd = new_csound_client()
        self.rythmInstrument = 'drum1kick'
        # toolbar with the new toolbar redesign
        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        self.play_index = 0

        self.play_recording_button = ToolButton(
            icon_name='media-playback-start')
        self.play_recording_button.set_property('can-default', True)
        self.play_recording_button.show()
        self.record_button = ToggleToolButton(icon_name='media-record')
        self.record_button.set_property('can-default', True)
        self.record_button.show()
        self.play_recording_button.set_sensitive(False)

        self.record_button.connect('clicked', self.__record_button_click_cb)

        self.play_recording_button.connect('clicked',
                                           self.handlePlayRecordingButton)

        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        # TODO: disabe until is implemented with csnd6
        # self.createPercussionToolbar(toolbar_box)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        keybord_labels = RadioToolButton()
        keybord_labels.props.icon_name = 'q_key'
        keybord_labels.props.group = keybord_labels
        keybord_labels.connect('clicked', self.set_keyboard_labels_cb)
        toolbar_box.toolbar.insert(keybord_labels, -1)

        notes_labels = RadioToolButton()
        notes_labels.props.icon_name = 'do_key'
        notes_labels.props.group = keybord_labels
        notes_labels.connect('clicked', self.set_notes_labels_cb)
        toolbar_box.toolbar.insert(notes_labels, -1)

        ti_notes_labels = RadioToolButton()
        ti_notes_labels.props.icon_name = 'ti_key'
        ti_notes_labels.props.group = keybord_labels
        ti_notes_labels.connect('clicked', self.set_ti_notes_labels_cb)
        toolbar_box.toolbar.insert(ti_notes_labels, -1)

        german_labels = RadioToolButton()
        german_labels.props.icon_name = 'c_key'
        german_labels.props.group = keybord_labels
        german_labels.connect('clicked', self.set_german_labels_cb)
        toolbar_box.toolbar.insert(german_labels, -1)

        no_labels = RadioToolButton()
        no_labels.props.icon_name = 'edit-clear'
        no_labels.props.group = keybord_labels
        no_labels.connect('clicked', self.set_keyboard_no_labels_cb)
        toolbar_box.toolbar.insert(no_labels, -1)
        self._what_widget = Gtk.ToolItem()
        self._what_search_button = FilterToolItem(_('Select Instrument'),
                                                  'view-type', _('Piano'),
                                                  self._what_widget)
        self._what_widget.show()
        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(self._what_search_button, -1)
        self._what_search_button.show()
        self._what_search_button.set_is_important(True)
        self._what_widget_contents = None
        self._what_drum_widget_contents = None

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)

        toolbar_box.toolbar.insert(self.record_button, -1)
        toolbar_box.toolbar.insert(self.play_recording_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self._save_as_audio_bt = ToolButton(icon_name='save-as-audio')
        self._save_as_audio_bt.props.tooltip = _('Save as audio')
        self._save_as_audio_bt.connect('clicked', self._save_ogg_cb)
        self._save_as_audio_bt.show()
        self._save_as_audio_bt.set_sensitive(False)
        activity_button.page.insert(self._save_as_audio_bt, -1)

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        self.keyboard_letters = ['ZSXDCVGBHNJM', 'Q2W3ER5T6Y7U', 'I']

        notes = [
            'DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
            ['FA#', 'SOLb'], 'SOL', ['SOL#', 'LAb'], 'LA', ['LA#', 'SIb'], 'SI'
        ]
        self.notes_labels = [notes, notes, ['DO']]

        # some countries use TI instead of SI
        ti_notes = [
            'DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
            ['FA#', 'SOLb'], 'SOL', ['SOL#', 'LAb'], 'LA', ['LA#', 'TIb'], 'TI'
        ]
        self.ti_notes_labels = [ti_notes, ti_notes, ['DO']]

        german_notes = [
            'C', ['C#', 'Db'], 'D', ['D#', 'Eb'], 'E', 'F', ['F#', 'Gb'], 'G',
            ['G#', 'Ab'], 'A', ['A#', 'Bb'], 'B'
        ]

        self.german_labels = [german_notes, german_notes, ['C']]

        self.piano = PianoKeyboard(octaves=2,
                                   add_c=True,
                                   labels=self.keyboard_letters)

        # init csound
        self.instrumentDB = InstrumentDB.getRef()
        self.timeout_ms = 50
        self.instVolume = 50
        self.drumVolume = 0.5
        self.instrument = 'piano'
        self.beat = 4
        self.reverb = 0.1
        self.tempo = PLAYER_TEMPO
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0

        self.sequencer = MiniSequencer(self.recordStateButton,
                                       self.recordOverSensitivity)
        self.loop = Loop(self.beat, math.sqrt(self.instVolume * 0.01))

        self.drumFillin = Fillin(self.beat, self.tempo, self.rythmInstrument,
                                 self.reverb, self.drumVolume)

        self.muteInst = False
        self.csnd.setTempo(self.tempo)
        self.noteList = []
        for i in range(21):
            self.csnd.setTrackVolume(100, i)

        # TODO commented because apparently are not used in the activity
        # for i in range(10):
        #     self.csnd.load_instrument('guidice' + str(i + 1))

        self.volume = 100
        self.csnd.setMasterVolume(self.volume)

        self.enableKeyboard()
        self.setInstrument(self.instrument)

        self.connect('key-press-event', self.onKeyPress)
        self.connect('key-release-event', self.onKeyRelease)

        self.piano.connect('key_pressed', self.__key_pressed_cb)
        self.piano.connect('key_released', self.__key_released_cb)
        vbox = Gtk.VBox()
        vbox.set_homogeneous(False)
        self.load_instruments()
        self._event_box = Gtk.EventBox()
        self._event_box.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_WHITE.get_gdk_color())
        vbox.pack_start(self._event_box, False, False, 0)
        vbox.pack_end(self.piano, True, True, 0)
        vbox.show_all()
        self.set_canvas(vbox)
        piano_height = Gdk.Screen.width() / 2
        self._event_box.set_size_request(
            -1,
            Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        self.connect('size-allocate', self.__allocate_cb)

        # TODO: disabe until is implemented with csnd6
        # GLib.idle_add(self.initializePercussion)

    def createPercussionToolbar(self, toolbar_box):

        self.beats_pm_button = IntensitySelector(range(2, 13), 4,
                                                 imagefile('beat3.svg'))
        self.tempo_button = \
            IntensitySelector(range(PLAYER_TEMPO_LOWER,
                                    PLAYER_TEMPO_UPPER + 1, PLAYER_TEMPO_STEP),
                              PLAYER_TEMPO, imagefile('tempo5.png'))

        self.complexity_button = IntensitySelector(xfrange(0, 1, 0.1),
                                                   self.regularity,
                                                   imagefile('complex6.svg'))

        self._play_percussion_btn = ToolButton(
            icon_name='media-playback-start')
        self._play_percussion_btn.set_property('can-default', True)
        self._play_percussion_btn.show()
        self._play_percussion_btn.connect('clicked', self.handlePlayButton)

        beats_toolbar = ToolbarBox()
        beats_toolbar.toolbar.insert(self._play_percussion_btn, -1)

        self._what_drum_widget = Gtk.ToolItem()
        self._what_drum_search_button = FilterToolItem(_('Select Drum'),
                                                       'view-type',
                                                       _('Jazz / Rock Kit'),
                                                       self._what_drum_widget)
        self._what_drum_search_button.set_widget_icon(
            file_name=imagefile("drum1kit.svg"))

        self._what_drum_widget.show()
        beats_toolbar.toolbar.insert(self._what_drum_search_button, -1)
        self._what_drum_search_button.show()
        self._what_drum_search_button.set_is_important(True)

        beats_toolbar.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        beats_toolbar.toolbar.insert(self.complexity_button, -1)
        beats_toolbar.toolbar.insert(self.beats_pm_button, -1)
        beats_toolbar.toolbar.insert(self.tempo_button, -1)

        beats_toolbar_button = ToolbarButton(icon_name='toolbar-drums',
                                             page=beats_toolbar)
        beats_toolbar_button.show()

        toolbar_box.toolbar.insert(beats_toolbar_button, 1)

        self.beats_pm_button.set_tooltip(_("Beats per bar"))
        self.beats_pm_button.show()
        self.beats_pm_button.connect('changed', self.beatSliderChange, True)
        self.tempo_button.connect('changed', self.tempoSliderChange, True)
        self.complexity_button.connect('changed', self.handleComplexityChange,
                                       True)
        self.complexity_button.set_tooltip(_("Beat complexity"))
        self.tempo_button.show()
        self.tempo_button.set_tooltip(_('Tempo'))
        self.complexity_button.show()

    def initializePercussion(self):
        self.rythmInstrument = 'drum1kit'
        self.csnd.load_drumkit(self.rythmInstrument)
        self.csnd.setTempo(self.tempo)
        self.beatPickup = False

        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval

        noteOnsets = []
        notePitchs = []

        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
                generator(self.rythmInstrument, self.beat, 0.8,
                          self.regularity, self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)  # add as active

        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)

        if self.playing:
            self.csnd.loopStart()

    def __allocate_cb(self, widget, rect):
        GLib.idle_add(self.resize, rect.width, rect.height)
        return False

    def resize(self, width, height):
        logging.debug('activity.py resize......')
        piano_height = width / 2
        self._event_box.set_size_request(
            -1,
            Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        return False

    def load_instruments(self):
        self._instruments_store = []

        # load the images
        images_path = os.path.join(activity.get_bundle_path(), 'instruments')
        logging.debug('Loading instrument images from %s', images_path)
        for file_name in os.listdir(images_path):
            image_file_name = os.path.join(images_path, file_name)
            pxb = GdkPixbuf.Pixbuf.new_from_file_at_size(
                image_file_name, 75, 75)
            # instrument_name = image_file_name[image_file_name.rfind('/'):]
            instrument_name = image_file_name[image_file_name.rfind('/') + 1:]
            instrument_name = instrument_name[:instrument_name.find('.')]
            instrument_desc = \
                self.instrumentDB.instNamed[instrument_name].nameTooltip

            file_path = os.path.join(images_path, file_name)

            # set the default icon
            if (instrument_name == 'piano'):
                self._what_search_button.set_widget_icon(file_name=file_path)

            self._instruments_store.append({
                "instrument_name":
                instrument_name,
                "pxb":
                pxb,
                "instrument_desc":
                instrument_desc,
                "file_name":
                file_path,
                "callback":
                self.__instrument_iconview_activated_cb
            })

        self._what_widget_contents = set_palette_list(self._instruments_store)
        self._what_widget.add(self._what_widget_contents)
        self._what_widget_contents.show()

        # TODO: disabe until is implemented with csnd6
        """
        for drum_number in range(0, DRUMCOUNT):
            drum_name = 'drum%dkit' % (drum_number + 1)
            self._drums_store.append({
                "instrument_name": drum_name,
                "file_name": imagefile(drum_name + '.svg'),
                "instrument_desc":
                    self.instrumentDB.instNamed[drum_name].nameTooltip,
                "callback": self.__drum_iconview_activated_cb
            })

        self._what_drum_widget_contents = set_palette_list(self._drums_store)
        self._what_drum_widget.add(self._what_drum_widget_contents)
        self._what_drum_widget_contents.show()
        """

    def __drum_iconview_activated_cb(self, widget, event, item):
        data = item['instrument_name']
        self.rythmInstrument = data
        self.csnd.load_drumkit(data)
        instrumentId = self.instrumentDB.instNamed[data].instrumentId
        for (o, n) in self.noteList:
            self.csnd.loopUpdate(n, NoteDB.PARAMETER.INSTRUMENT, instrumentId,
                                 -1)
        self.drumFillin.setInstrument(self.rythmInstrument)
        self._what_drum_search_button.set_widget_label(
            label=item['instrument_desc'])
        self._what_drum_search_button.set_widget_icon(
            file_name=item['file_name'])

    def __instrument_iconview_activated_cb(self, widget, event, item):
        self.setInstrument(item['instrument_name'])
        self._what_search_button.set_widget_icon(file_name=item['file_name'])
        self._what_search_button.set_widget_label(
            label=item['instrument_desc'])

    def set_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.notes_labels)

    def set_ti_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.ti_notes_labels)

    def set_keyboard_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.keyboard_letters)

    def set_german_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.german_labels)

    def beatSliderChange(self, widget, event):
        self.beat = int(self.beats_pm_button.get_value())
        self.sequencer.beat = self.beat
        self.loop.beat = self.beat
        self.drumFillin.setBeats(self.beat)
        img = int(self.scale(self.beat, 2, 12, 1, 11))
        self.beats_pm_button.set_image(imagefile('beat' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    def regenerate(self):
        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval

        noteOnsets = []
        notePitchs = []
        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
                generator(self.rythmInstrument, self.beat, 0.8,
                          self.regularity, self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)  # add as active
        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)
        self.recordOverSensitivity(False)
        if self.playing:
            self.csnd.loopStart()

    def handlePlayRecordingButton(self, val):
        if not self.playing_recording:
            self.playing_recording = True
            self.play_recording_button.props.icon_name = 'media-playback-stop'
            self.play_recording_thread = \
                GLib.timeout_add(100, self._play_recorded_keys)
        else:
            self.playing_recording = False
            self.play_recording_button.props.icon_name = 'media-playback-start'

    def _save_ogg_cb(self, widget):
        self._wav_tempfile = tempfile.NamedTemporaryFile(mode='w+b',
                                                         suffix='.wav',
                                                         dir='/tmp/')
        self.csnd.inputMessage(Config.CSOUND_RECORD_PERF %
                               self._wav_tempfile.name)

        self.playing_recording = True
        self.play_recording_thread = \
            GLib.timeout_add(100, self._play_recorded_keys,
                             self._save_ogg_end_cb)

    def _save_ogg_end_cb(self):
        self.csnd.inputMessage(Config.CSOUND_STOP_RECORD_PERF %
                               self._wav_tempfile.name)

        self._ogg_tempfile = tempfile.NamedTemporaryFile(mode='w+b',
                                                         suffix='.ogg',
                                                         dir='/tmp/')

        line = 'filesrc location=%s ! ' \
            'wavparse ! audioconvert ! vorbisenc ! oggmux ! ' \
            'filesink location=%s' % (self._wav_tempfile.name,
                                      self._ogg_tempfile.name)
        pipe = Gst.parse_launch(line)
        pipe.get_bus().add_signal_watch()
        pipe.get_bus().connect('message::eos', self._save_ogg_eos_cb, pipe)
        pipe.set_state(Gst.State.PLAYING)

    def _save_ogg_eos_cb(self, bus, message, pipe):
        bus.remove_signal_watch()
        pipe.set_state(Gst.State.NULL)

        title = '%s saved as audio' % self.metadata['title']

        jobject = datastore.create()
        jobject.metadata['title'] = title
        jobject.metadata['keep'] = '0'
        jobject.metadata['mime_type'] = 'audio/ogg'
        jobject.file_path = self._ogg_tempfile.name
        datastore.write(jobject)

        self._wav_tempfile.close()
        self._ogg_tempfile.close()

        alert = NotifyAlert(10)
        alert.props.title = _('Audio recorded')
        alert.props.msg = _('The audio file was saved in the Journal')
        alert.connect('response', self.__alert_response_cb)
        self.add_alert(alert)

        return False

    def __alert_response_cb(self, alert, result):
        self.remove_alert(alert)

    def __record_button_click_cb(self, button):
        if not self.recording:
            self.play_recording_button.set_sensitive(False)
            self._save_as_audio_bt.set_sensitive(False)
            self.recorded_keys = []
            self.recording = True
            icon = Icon(icon_name='media-record', fill_color='#ff0000')
            icon.show()
            self.record_button.set_icon_widget(icon)
        else:
            self.recording = False
            icon = Icon(icon_name='media-record', fill_color='#ffffff')
            icon.show()
            self.record_button.set_icon_widget(icon)
            if len(self.recorded_keys) != 0:
                self.play_recording_button.set_sensitive(True)
                self._save_as_audio_bt.set_sensitive(True)

    def tempoSliderChange(self, widget, event):
        self._updateTempo(self.tempo_button.get_value())
        img = int(
            self.scale(self.tempo, PLAYER_TEMPO_LOWER, PLAYER_TEMPO_UPPER, 1,
                       9))
        self.tempo_button.set_image(imagefile('tempo' + str(img) + '.png'))

    def _updateTempo(self, val):
        self.tempo = val
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0
        self.csnd.setTempo(self.tempo)
        self.sequencer.tempo = self.tempo
        self.drumFillin.setTempo(self.tempo)

    def handlePlayButton(self, val):
        if not self.playing:
            if not self.firstTime:
                self.regenerate()
                self.firstTime = True
            self.drumFillin.play()
            self.csnd.loopSetTick(0)
            self.csnd.loopStart()
            self.playing = True
            self._play_percussion_btn.props.icon_name = 'media-playback-stop'
        else:
            self.drumFillin.stop()
            self.sequencer.stopPlayback()
            self.csnd.loopPause()
            self.playing = False
            self._play_percussion_btn.props.icon_name = 'media-playback-start'

    def scale(self, input, input_min, input_max, output_min, output_max):
        range_input = input_max - input_min
        range_output = output_max - output_min
        result = (input - input_min) * range_output / range_input + output_min

        if (input_min > input_max and output_min > output_max) or \
           (output_min > output_max and input_min < input_max):
            if result > output_min:
                return output_min
            elif result < output_max:
                return output_max
            else:
                return result

        if (input_min < input_max and output_min < output_max) or \
           (output_min < output_max and input_min > input_max):
            if result > output_max:
                return output_max
            elif result < output_min:
                return output_min
            else:
                return result

    def handleComplexityChange(self, widget, event):
        self.regularity = self.complexity_button.get_value()
        img = int(self.complexity_button.get_value() * 7) + 1
        self.complexity_button.set_image(
            imagefile('complex' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    """
    def handleBalanceSlider(self, adj):
        self.instVolume = int(adj.get_value())
        self.drumVolume = sqrt( (100-self.instVolume)*0.01 )
        self.adjustDrumVolume()
        self.drumFillin.setVolume(self.drumVolume)
        instrumentVolume = sqrt( self.instVolume*0.01 )
        self.loop.adjustLoopVolume(instrumentVolume)
        self.sequencer.adjustSequencerVolume(instrumentVolume)
        img = int(self.scale(self.instVolume,100,0,0,4.9))
        self._playToolbar.balanceSliderImgLeft.set_from_file(
                imagefile('dru' + str(img) + '.png'))
        img2 = int(self.scale(self.instVolume,0,100,0,4.9))
        self._playToolbar.balanceSliderImgRight.set_from_file(
                imagefile('instr' + str(img2) + '.png'))

    def handleReverbSlider(self, adj):
        self.reverb = adj.get_value()
        self.drumFillin.setReverb( self.reverb )
        img = int(self.scale(self.reverb,0,1,0,4))
        self._playToolbar.reverbSliderImgRight.set_from_file(
                imagefile('reverb' + str(img) + '.png'))
        self.keyboardStandAlone.setReverb(self.reverb)
    """

    def set_keyboard_no_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(None)

    def enableKeyboard(self):
        self.keyboardStandAlone = KeyboardStandAlone(
            self.sequencer.recording, self.sequencer.adjustDuration,
            self.csnd.loopGetTick, self.sequencer.getPlayState, self.loop)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def setInstrument(self, instrument):
        logging.debug("Set Instrument: %s" % instrument)
        self.instrument = instrument
        self.keyboardStandAlone.setInstrument(instrument)
        self.csnd.load_instrument(instrument)

    def recordStateButton(self, button, state):
        pass
#        if button == 1:
#            self._recordToolbar.keyboardRecButton.set_active( state )
#        else:
#            self._recordToolbar.keyboardRecOverButton.set_active( state )

    def recordOverSensitivity(self, state):
        pass
        # self._recordToolbar.keyboardRecOverButton.set_sensitive( state )

    def _play_recorded_keys(self, end_cb=None):
        GLib.source_remove(self.play_recording_thread)
        letter = self.recorded_keys[self.play_index]
        time_difference = 0
        if self.play_index == len(self.recorded_keys) - 1:
            time_difference = \
                self.recorded_keys[self.play_index][0] - \
                self.recorded_keys[self.play_index - 1][0]
        else:
            next_time = self.recorded_keys[self.play_index + 1][0]
            time_difference = next_time - letter[0]

        if not self.playing_recording:
            self.play_recording_button.props.icon_name = 'media-playback-start'
            return

        if letter[-1] == 1:
            self.keyboardStandAlone.do_key_release(
                LETTERS_TO_KEY_CODES[letter[3]])
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], False)
        else:
            self.keyboardStandAlone.do_key_press(
                LETTERS_TO_KEY_CODES[letter[3]], None,
                math.sqrt(self.instVolume * 0.01))
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], True)

        if self.play_index == len(self.recorded_keys) - 1:
            self.play_index = 0
            self.play_recording_button.props.icon_name = 'media-playback-start'
            self.playing_recording = False
            if end_cb is not None:
                end_cb()
        else:
            self.play_index += 1
            self.play_recording_thread = \
                GLib.timeout_add(int((time_difference) * 1000),
                                 self._play_recorded_keys, end_cb)

    def __key_pressed_cb(self, widget, octave_clicked, key_clicked, letter,
                         physicallKey):
        logging.debug('Pressed Octave: %d Key: %d Letter: %s' %
                      (octave_clicked, key_clicked, letter))
        if letter in LETTERS_TO_KEY_CODES.keys():
            if self.recording:
                self.recorded_keys.append(
                    [time.time(), octave_clicked, key_clicked, letter])
            if not physicallKey:
                self.keyboardStandAlone.do_key_press(
                    LETTERS_TO_KEY_CODES[letter], None,
                    math.sqrt(self.instVolume * 0.01))

    def __key_released_cb(self, widget, octave_clicked, key_clicked, letter,
                          physicallKey):
        if self.recording:
            self.recorded_keys.append(
                [time.time(), octave_clicked, key_clicked, letter, 1])
        if not physicallKey:
            if letter in LETTERS_TO_KEY_CODES.keys():
                self.keyboardStandAlone.do_key_release(
                    LETTERS_TO_KEY_CODES[letter])

    def onKeyPress(self, widget, event):
        if event.state & Gdk.ModifierType.CONTROL_MASK:
            return
        if event.hardware_keycode == 37:
            if self.muteInst:
                self.muteInst = False
            else:
                self.muteInst = True
        self.piano.physical_key_changed(event.hardware_keycode, True)
        self.keyboardStandAlone.onKeyPress(widget, event,
                                           math.sqrt(self.instVolume * 0.01))

    def onKeyRelease(self, widget, event):
        self.keyboardStandAlone.onKeyRelease(widget, event)
        self.piano.physical_key_changed(event.hardware_keycode, False)

    def write_file(self, file_path):
        f = open(file_path, 'w')
        # substract the initial time to all the saved values
        if len(self.recorded_keys) > 0:
            initial_time = self.recorded_keys[0][0]
            for key in self.recorded_keys:
                key[0] = key[0] - initial_time

        f.write(json.dumps(self.recorded_keys))
        f.close()

    def read_file(self, file_path):
        f = open(file_path, 'r')
        contents = f.read().strip()

        self.recorded_keys = json.loads(contents)
        if len(self.recorded_keys) != 0:
            self.play_recording_button.set_sensitive(True)
            self._save_as_audio_bt.set_sensitive(True)
        f.close()

    def close(self, skip_save=False):
        self.csnd.stop()  # without which Csound will segfault
        activity.Activity.close(self, skip_save=skip_save)
Exemple #43
0
    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        export_csv = ToolButton('save-as-csv')
        export_csv.set_tooltip(_('Export tracked objects to journal'))
        export_csv.connect('clicked', self._export_csv_cb)
        activity_button.props.page.insert(export_csv, -1)
        export_csv.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box
class Mastermind(activity.Activity):

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        self.canvas = Canvas()
        self.canvas.connect("data-changed", self._data_changed_cb)
        self.canvas.connect("win", self._win_cb)
        self.canvas.connect("lose", self._lose_cb)
        self.set_canvas(self.canvas)

        self.make_toolbar()
        self.read_file()

        self.show_all()

    def make_toolbar(self):
        def make_separator(expand=True):
            separator = Gtk.SeparatorToolItem()
            separator.props.draw = not expand
            separator.set_expand(expand)
            return separator

        toolbarbox = ToolbarBox()
        self.set_toolbar_box(toolbarbox)

        toolbar = toolbarbox.toolbar
        toolbar.insert(ActivityToolbarButton(self), -1)
        toolbar.insert(make_separator(False), -1)

        self.restart_button = ToolButton(icon_name="system-restart")
        self.restart_button.set_tooltip(_("Restart"))
        self.restart_button.props.accelerator = '<Ctrl>N'
        self.restart_button.connect("clicked", self._restart_cb)
        toolbar.insert(self.restart_button, -1)

        self.ok_button = ToolButton(icon_name="dialog-ok")
        self.ok_button.set_tooltip(_("Ok"))
        self.ok_button.set_sensitive(False)
        self.ok_button.props.accelerator = "Return"
        self.ok_button.connect("clicked", self._ok_cb)
        toolbar.insert(self.ok_button, -1)

        item = Gtk.ToolItem()
        toolbar.insert(item, -1)

        self.label = Gtk.Label()
        self.label.modify_font(Pango.FontDescription("Bold"))
        item.add(self.label)

        toolbar.insert(make_separator(True), -1)

        helpbutton = HelpButton()
        helpbutton.add_section(_("Instructions:"))
        helpbutton.add_paragraph(_("Place colors to the last played row."), image="instructions1.png")
        helpbutton.add_paragraph(_("When you complete a row, click on 'Ok button'."), image="instructions2.png")
        helpbutton.add_paragraph(_("Next to the row will appear black and white circles."), image="instructions3.png")
        helpbutton.add_paragraph(_("A black circle means you matched a peg and you placed it correctly."))
        helpbutton.add_paragraph(_("A white circle means you matched a peg and you wrong placed it."))
        helpbutton.add_paragraph(_("The goal is match all pegs in the correct place."))
        toolbar.insert(helpbutton, -1)

        stop_button = StopButton(self)
        toolbar.insert(stop_button, -1)

        toolbar.show_all()

    def read_file(self):
        if "level" in list(self.metadata.keys()):
            data = {
                "level": int(self.metadata["level"]),
                "correct": [int(x) for x in eval(self.metadata["correct"])],
                "balls": {}
            }

            balls = eval(self.metadata["balls"])
            for key in balls:
                x = int(key)
                data["balls"][x] = []

                for value in balls[key]:
                    y = int(value)
                    data["balls"][x].append(y)

            current = []
            for x in range(0, 4):
                current.append(data["balls"][x][data["level"]])

            self.ok_button.set_sensitive(not BallType.NULL in current)
            self.canvas.set_game_data(data)

    def write_file(self, path):
        data = self.canvas.get_game_data()
        self.metadata["level"] = str(data["level"]).encode('utf-8')
        self.metadata["correct"] = str(data["correct"]).encode('utf-8')
        self.metadata["balls"] = str(data["balls"]).encode('utf-8')

    def _ok_cb(self, button):
        self.canvas.end_turn()

    def _restart_cb(self, button):
        self.canvas.reset()
        self.label.set_text("")

    def _data_changed_cb(self, canvas, data):
        self.ok_button.set_sensitive(not BallType.NULL in data)

    def _win_cb(self, canvas):
        self.ok_button.set_sensitive(False)

        self.label.set_text("You win")

    def _lose_cb(self, canvas):
        self.ok_button.set_sensitive(False)

        self.label.set_text("You lost")
Exemple #45
0
class CreateToolbarBuilder(GObject.GObject):

    __gtype_name__ = 'CreateToolbar'

    __gsignals__ = {
        'create_new_game': (GObject.SignalFlags.RUN_FIRST, None, []),
        'create_equal_pairs': (GObject.SignalFlags.RUN_FIRST,
                               None, [GObject.TYPE_PYOBJECT]),
    }

    def __init__(self, activity):
        GObject.GObject.__init__(self)
        self.activity = activity
        self.toolbar = self.activity.get_toolbar_box().toolbar

        self._equal_pairs = ToggleToolButton('pair-non-equals')
        self._equal_pairs.set_tooltip(_('Match different tiles'))
        self._equal_pairs.connect('toggled', self._emit_equal_pairs)
        self.toolbar.insert(self._equal_pairs, -1)

        self._grouped = ToggleToolButton('grouped_game1')
        self._grouped.set_tooltip(_('Mixed tiles game'))
        self._grouped.connect('toggled', self._grouped_cb)
        self.toolbar.insert(self._grouped, -1)

        self._clear_button = ToolButton('edit-clear')
        self._clear_button.set_tooltip(_('Clear all tiles'))
        self._clear_button.connect('clicked', self._clear_game_bt)
        self.toolbar.insert(self._clear_button, -1)

        self.toolbar.show_all()

    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)
        tool_item.add(widget)
        widget.show()
        self.toolbar.insert(tool_item, -1)
        tool_item.show()

    def _clear_game_bt(self, button):
        if self.activity.game.model.is_demo or \
                len(self.activity.cardlist.pairs) == 0:
            self.clear_game()
        else:
            alert = Alert()
            alert.props.title = _('Clear all the tiles from the game?')
            icon = Icon(icon_name='dialog-ok')
            alert.add_button(1, _('Clear'), icon)
            icon = Icon(icon_name='dialog-cancel')
            alert.add_button(0, _('Do not clear'), icon)
            alert.connect('response', self._clear_game_alert_cb)
            self.activity.add_alert(alert)

    def _clear_game_alert_cb(self, alert, response_id):
        self.activity.remove_alert(alert)
        if response_id == 1:
            self.clear_game()

    def clear_game(self):
            self._equal_pairs.set_active(False)
            self._grouped.set_active(False)
            self.emit('create_new_game')

    def update_controls(self, active):
        self._equal_pairs.set_sensitive(active)
        self._grouped.set_sensitive(active)
        self._clear_button.set_sensitive(active)

    def _emit_equal_pairs(self, widget):
        if self._equal_pairs.get_active():
            self._equal_pairs.set_icon_name('pair-equals')
            self._equal_pairs.set_tooltip(_('Match identical tiles'))
            equal_pairs = '1'
        else:
            self._equal_pairs.set_icon_name('pair-non-equals')
            self._equal_pairs.set_tooltip(_('Match different tiles'))
            equal_pairs = '0'
        self.emit('create_equal_pairs', self._equal_pairs.get_active())
        logging.debug('createtoolbar._emit_equal_pairs')

        if self.activity.game.model.data['equal_pairs'] != equal_pairs:
            self.activity.game.model.data['equal_pairs'] = equal_pairs
            self.activity.game.model.mark_modified()

    def _grouped_cb(self, widget):
        if self._grouped.get_active():
            self._grouped.set_icon_name('grouped_game2')
            self._grouped.set_tooltip(_('Grouped tiles game'))
            divided = '1'
        else:
            self._grouped.set_icon_name('grouped_game1')
            self._grouped.set_tooltip(_('Mixed tiles game'))
            divided = '0'
        logging.debug('createtoolbar._grouped_cb')

        if self.activity.game.model.data['divided'] != divided:
            self.activity.game.model.data['divided'] = divided
            self.activity.game.model.mark_modified()

    def update_create_toolbar(self, widget, game_name, equal_pairs, grouped):
        self._equal_pairs.set_active(equal_pairs == '1')
        self._grouped.set_active(grouped == '1')
Exemple #46
0
class PhysicsActivity(activity.Activity):
    def __init__(self, handle):
        super(PhysicsActivity, self).__init__(handle)
        self._collab = CollabWrapper(self)
        self._collab.message.connect(self.__message_cb)
        self.metadata['mime_type'] = 'application/x-physics-activity'
        self.add_events(Gdk.EventMask.ALL_EVENTS_MASK
                        | Gdk.EventMask.VISIBILITY_NOTIFY_MASK)

        self.connect('visibility-notify-event', self._focus_event)
        self.connect('window-state-event', self._window_event)

        self.game = PhysicsGame(self)
        self.game.canvas = sugargame.canvas.PygameCanvas(
            self, main=self.game.run, modules=[pygame.display, pygame.font])

        self.preview = None
        self._sample_window = None

        self._notebook = Gtk.Notebook(show_tabs=False)
        self._notebook.add(self.game.canvas)

        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE

        self.game.canvas.set_size_request(w, h)

        self._constructors = {}
        self.build_toolbar()

        self.set_canvas(self._notebook)
        Gdk.Screen.get_default().connect('size-changed', self.__configure_cb)

        self.show_all()
        self._collab.setup()

    def get_data(self):
        """ FIXME: not implemented, thus objects created before
        sharing starts are not shared """
        return dict()

    def set_data(self, data):
        pass

    def __configure_cb(self, event):
        ''' Screen size has changed '''
        self.write_file(
            os.path.join(activity.get_activity_root(), 'data', 'data'))
        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE
        pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.read_file(
            os.path.join(activity.get_activity_root(), 'data', 'data'))

    def read_file(self, file_path):
        self.game.read_file(file_path)

    def write_file(self, file_path):
        self.game.write_file(file_path)

    def get_preview(self):
        ''' Custom preview code to get image from pygame. '''
        return self.game.canvas.get_preview()

    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        color = ColorToolButton('color')
        color.connect('notify::color', self.__color_notify_cb)
        toolbar_box.toolbar.insert(color, -1)
        color.show()

        random = ToggleToolButton('colorRandom')
        random.set_tooltip(_('Toggle random color'))
        toolbar_box.toolbar.insert(random, -1)
        random.set_active(True)
        random.connect('toggled', self.__random_toggled_cb)
        random.show()

        color.random = random
        random.color = color

        random.timeout_id = GLib.timeout_add(100, self.__timeout_cb, random)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box

    def __color_notify_cb(self, button, pdesc):
        """ when a color is chosen;
        change world object add color,
        and change color of buttons. """

        color = button.get_color()
        self._set_color(color)
        button.random.set_active(False)
        button.random.get_icon_widget().set_stroke_color(self._rgb8x(color))

    def __random_toggled_cb(self, random):
        if random.props.active:
            self._random_on(random)
        else:
            self._random_off(random)

    def _random_on(self, random):
        """ when random is turned on;
        reset world object add color,
        and begin watching for changed world object add color. """

        self.game.world.add.reset_color()

        if random.timeout_id is None:
            random.timeout_id = GLib.timeout_add(100, self.__timeout_cb,
                                                 random)
            self.__timeout_cb(random)

    def _random_off(self, random):
        """ when random is turned off;
        change world object add color back to chosen color,
        change color of buttons,
        and stop watching for changed world object add color. """

        color = random.color.get_color()
        self._set_color(color)
        random.get_icon_widget().set_stroke_color(self._rgb8x(color))

        if random.timeout_id is not None:
            GLib.source_remove(random.timeout_id)
            random.timeout_id = None

    def __timeout_cb(self, random):
        """ copy the next color to the random button stroke color. """
        if hasattr(self.game, "world"):
            color = self.game.world.add.next_color()
            random.get_icon_widget().set_stroke_color('#%.2X%.2X%.2X' % color)
        return True

    def _set_color(self, color):
        """ set world object add color. """
        self.game.world.add.set_color(self._rgb8(color))

    def _rgb8x(self, color):
        """ convert a Gdk.Color into hex triplet. """
        return '#%.2X%.2X%.2X' % self._rgb8(color)

    def _rgb8(self, color):
        """ convert a Gdk.Color into an 8-bit RGB tuple. """
        return (color.red / 256, color.green / 256, color.blue / 256)

    def can_close(self):
        self.preview = self.get_preview()
        self.game.running = False
        return True

    def _insert_stop_play_button(self, toolbar):

        self.stop_play_toolbar = ToolbarButton()
        st_toolbar = self.stop_play_toolbar
        st_toolbar.props.page = Gtk.Toolbar()
        st_toolbar.props.icon_name = 'media-playback-stop'

        self.stop_play_state = True
        self.stop_play = ToolButton('media-playback-stop')
        self.stop_play.set_tooltip(_('Stop'))
        self.stop_play.set_accelerator(_('<ctrl>space'))
        self.stop_play.connect('clicked', self.stop_play_cb)
        self._insert_item(st_toolbar, self.stop_play)
        self.stop_play.show()

        slowest_button = RadioToolButton(group=None)
        slowest_button.set_icon_name('slow-walk-milton-raposo')
        slowest_button.set_tooltip(_('Run slower'))
        slowest_button.connect('clicked', self._set_fps_cb, SLOWEST_FPS)
        self._insert_item(st_toolbar, slowest_button)
        slowest_button.show()

        slow_button = RadioToolButton(group=slowest_button)
        slow_button.set_icon_name('walking')
        slow_button.set_tooltip(_('Run slow'))
        slow_button.connect('clicked', self._set_fps_cb, SLOW_FPS)
        self._insert_item(st_toolbar, slow_button)
        slow_button.show()

        fast_button = RadioToolButton(group=slowest_button)
        fast_button.set_icon_name('running')
        fast_button.set_tooltip('Run fast')
        fast_button.connect('clicked', self._set_fps_cb, FAST_FPS)
        self._insert_item(st_toolbar, fast_button)
        fast_button.show()
        fast_button.set_active(True)

        toolbar.insert(self.stop_play_toolbar, -1)
        self.stop_play_toolbar.show_all()

    def _set_fps_cb(self, button, value):
        self.game.set_game_fps(value)

    def _insert_clear_all_button(self, toolbar):
        self.clear_all = ToolButton('clear_all')
        self.clear_all.set_tooltip(_('Erase All'))
        self.clear_all.set_accelerator(_('<ctrl>a'))
        self.clear_all.connect('clicked', self.clear_all_cb)
        toolbar.insert(self.clear_all, -1)
        self.clear_all.set_sensitive(False)
        self.clear_all.show()

    def _insert_item(self, toolbar, item, pos=-1):
        if hasattr(toolbar, 'insert'):
            toolbar.insert(item, pos)
        else:
            toolbar.props.page.insert(item, pos)

    def _insert_create_tools(self, create_toolbar):
        # Make + add the component buttons
        self.radioList = {}
        for i, c in enumerate(tools.allTools):
            if i == 0:
                button = RadioToolButton(group=None)
                firstbutton = button
            else:
                button = RadioToolButton(group=firstbutton)
            button.set_icon_name(c.icon)
            button.set_tooltip(c.toolTip)
            button.set_accelerator(c.toolAccelerator)
            button.connect('clicked', self.radioClicked)
            palette = self._build_palette(c)
            if palette is not None:
                palette.show()
                button.get_palette().set_content(palette)
            self._insert_item(create_toolbar, button, -1)
            button.show()
            self.radioList[button] = c.name
            if hasattr(c, 'constructor'):
                self._constructors[c.name] = \
                    self.game.toolList[c.name].constructor

    def __icon_path(self, name):
        activity_path = activity.get_bundle_path()
        icon_path = os.path.join(activity_path, 'icons', name + '.svg')
        return icon_path

    def _build_palette(self, tool):
        if tool.palette_enabled:
            if tool.palette_mode == tools.PALETTE_MODE_ICONS:
                grid = Gtk.Grid()
                for s, settings in enumerate(tool.palette_settings):
                    self.game.toolList[tool.name].buttons.append([])
                    for i, icon_value in enumerate(settings['icon_values']):
                        if i == 0:
                            button = RadioToolButton(group=None)
                            firstbutton = button
                        else:
                            button = RadioToolButton(group=firstbutton)
                        button.set_icon_name(settings['icons'][i])
                        button.connect('clicked', self._palette_icon_clicked,
                                       tool.name, s, settings['name'],
                                       icon_value)
                        grid.attach(button, i, s, 1, 1)
                        self.game.toolList[tool.name].buttons[s].append(button)
                        button.show()
                        if settings['active'] == settings['icons'][i]:
                            button.set_icon_name(settings['icons'][i] +
                                                 '-selected')
                            button.set_active(True)
                return grid
        else:
            return None

    def _palette_icon_clicked(self, widget, toolname, s, value_name, value):
        for tool in tools.allTools:
            if tool.name == toolname:
                # Radio buttons are not highlighting in the palette
                # so adding highlight by hand
                # See http://bugs.sugarlabs.org/ticket/305
                setting = self.game.toolList[tool.name].palette_settings[s]
                for i, button in enumerate(
                        self.game.toolList[tool.name].buttons[s]):
                    icon_name = setting['icons'][i]
                    if button == widget:
                        button.set_icon_name(icon_name + '-selected')
                    else:
                        button.set_icon_name(icon_name)
                if hasattr(tool, 'palette_data_type'):
                    tool.palette_data_type = value
                else:
                    tool.palette_data[value_name] = value

    def clear_trace_alert_cb(self, alert, response):
        self.remove_alert(alert)
        if response is Gtk.ResponseType.OK:
            self.game.full_pos_list = [[] for _ in self.game.full_pos_list]
            self.game.tracked_bodies = 0

    def clear_trace_cb(self, button):
        clear_trace_alert = ConfirmationAlert()
        clear_trace_alert.props.title = _('Are You Sure?')
        clear_trace_alert.props.msg = \
            _('All trace points will be erased. This cannot be undone!')
        clear_trace_alert.connect('response', self.clear_trace_alert_cb)
        self.add_alert(clear_trace_alert)

    def stop_play_cb(self, button):
        pygame.event.post(
            pygame.event.Event(pygame.USEREVENT, action='stop_start_toggle'))
        self.stop_play_state = not self.stop_play_state

        if self.stop_play_state:
            self.stop_play.set_icon_name('media-playback-stop')
            self.stop_play.set_tooltip(_('Stop'))

            self.stop_play_toolbar.set_icon_name('media-playback-stop')
        else:
            self.stop_play.set_icon_name('media-playback-start')
            self.stop_play.set_tooltip(_('Start'))

            self.stop_play_toolbar.set_icon_name('media-playback-start')

    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(
                    pygame.event.Event(pygame.USEREVENT, action='clear_all'))

        if len(self.game.world.world.bodies) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _('Are You Sure?')
            clear_all_alert.props.msg = \
                _('All your work will be discarded. This cannot be undone!')
            clear_all_alert.connect('response', clear_all_alert_cb)
            self.add_alert(clear_all_alert)

    def radioClicked(self, button):
        pygame.event.post(
            pygame.event.Event(pygame.USEREVENT,
                               action=self.radioList[button]))

    def _focus_event(self, event, data=None):
        ''' Send focus events to pygame to allow it to idle when in
        background. '''
        if data.state == Gdk.VisibilityState.FULLY_OBSCURED:
            pygame.event.post(
                pygame.event.Event(pygame.USEREVENT, action='focus_out'))
        else:
            self.game.show_fake_cursor = True
            pygame.event.post(
                pygame.event.Event(pygame.USEREVENT, action='focus_in'))

    def _export_json_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/plain'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        self.game.world.json_save(file_path)

        jobject.set_file_path(file_path)
        datastore.write(jobject)

    def _window_event(self, window, event):
        ''' Send focus out event to pygame when switching to a desktop
        view. '''
        if event.changed_mask & Gdk.WindowState.ICONIFIED:
            pygame.event.post(
                pygame.event.Event(pygame.USEREVENT, action='focus_out'))

    def _restore_cursor(self):
        ''' No longer waiting, so restore standard cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.get_window().set_cursor(self.old_cursor)

    def _waiting_cursor(self):
        ''' Waiting, so set watch cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def __message_cb(self, collab, buddy, msg):
        ''' Data is passed as tuples: cmd:text '''
        action = msg.get('action')
        if action != 'text':
            return

        text = msg['text']
        dispatch_table = {
            'C': self._construct_shared_circle,
            'B': self._construct_shared_box,
            'T': self._construct_shared_triangle,
            'P': self._construct_shared_polygon,
            'M': self._construct_shared_magicpen,
            'j': self._add_shared_joint,
            'p': self._add_shared_pin,
            'm': self._add_shared_motor,
            't': self._add_shared_track,
            'c': self._add_shared_chain,
        }
        logging.debug('<<< %s' % (text[0]))
        dispatch_table[text[0]](text[2:])

    def _construct_shared_circle(self, data):
        circle_data = json.loads(data)
        pos = circle_data[0]
        radius = circle_data[1]
        density = circle_data[2]
        restitution = circle_data[3]
        friction = circle_data[4]
        self._constructors['Circle'](pos,
                                     radius,
                                     density,
                                     restitution,
                                     friction,
                                     share=False)

    def _construct_shared_box(self, data):
        box_data = json.loads(data)
        pos1 = box_data[0]
        pos2 = box_data[1]
        density = box_data[2]
        restitution = box_data[3]
        friction = box_data[4]
        self._constructors['Box'](pos1,
                                  pos2,
                                  density,
                                  restitution,
                                  friction,
                                  share=False)

    def _construct_shared_triangle(self, data):
        triangle_data = json.loads(data)
        pos1 = triangle_data[0]
        pos2 = triangle_data[1]
        density = triangle_data[2]
        restitution = triangle_data[3]
        friction = triangle_data[4]
        self._constructors['Triangle'](pos1,
                                       pos2,
                                       density,
                                       restitution,
                                       friction,
                                       share=False)

    def _construct_shared_polygon(self, data):
        polygon_data = json.loads(data)
        vertices = polygon_data[0]
        density = polygon_data[1]
        restitution = polygon_data[2]
        friction = polygon_data[3]
        self._constructors['Polygon'](vertices,
                                      density,
                                      restitution,
                                      friction,
                                      share=False)

    def _construct_shared_magicpen(self, data):
        magicpen_data = json.loads(data)
        vertices = magicpen_data[0]
        density = magicpen_data[1]
        restitution = magicpen_data[2]
        friction = magicpen_data[3]
        self._constructors['Magicpen'](vertices,
                                       density,
                                       restitution,
                                       friction,
                                       share=False)

    def _add_shared_joint(self, data):
        joint_data = json.loads(data)
        pos1 = joint_data[0]
        pos2 = joint_data[1]
        self._constructors['Joint'](pos1, pos2, share=False)

    def _add_shared_pin(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        self._constructors['Pin'](pos, share=False)

    def _add_shared_motor(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        speed = joint_data[1]
        self._constructors['Motor'](pos, speed, share=False)

    def _add_shared_track(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        color = joint_data[1]
        self._constructors['Track'](pos, color, share=False)

    def _add_shared_chain(self, data):
        joint_data = json.loads(data)
        vertices = joint_data[0]
        link_length = joint_data[1]
        radius = joint_data[2]
        self._constructors['Chain'](vertices, link_length, radius, share=False)

    def send_event(self, text):
        self._collab.post(dict(action='text', text=text))

    def _load_project(self, button):
        chooser = ObjectChooser(parent=self)
        result = chooser.run()
        if result == Gtk.ResponseType.ACCEPT:
            dsobject = chooser.get_selected_object()
            file_path = dsobject.get_file_path()
            self.__load_game(file_path, True)
            chooser.destroy()

    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _('Are You Sure?')
        confirmation_alert.props.msg = \
            _('All your work will be discarded. This cannot be undone!')

        def action(alert, response):
            self.remove_alert(alert)
            if response is not Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, 'r')
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
            except:
                title = _('Load project from journal')
                if not journal:
                    title = _('Load example')
                msg = _('Error: Cannot open Physics project from this file.')
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect('response',
                              lambda alert, response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect('response', action)
        self.add_alert(confirmation_alert)

    def _create_store(self, widget=None):
        if self._sample_window is None:
            self._sample_box = Gtk.EventBox()

            vbox = Gtk.VBox()
            self._sample_window = Gtk.ScrolledWindow()
            self._sample_window.set_policy(Gtk.PolicyType.NEVER,
                                           Gtk.PolicyType.AUTOMATIC)
            self._sample_window.set_border_width(4)

            self._sample_window.set_size_request(Gdk.Screen.width() / 2,
                                                 Gdk.Screen.height() / 2)
            self._sample_window.show()

            store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

            icon_view = Gtk.IconView()
            icon_view.set_model(store)
            icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
            icon_view.connect('selection-changed', self._sample_selected,
                              store)
            icon_view.set_pixbuf_column(0)
            icon_view.grab_focus()
            self._sample_window.add(icon_view)
            icon_view.show()
            self._fill_samples_list(store)

            title = Gtk.HBox()
            title_label = Gtk.Label(_("Select a sample..."))
            separator = Gtk.HSeparator()
            separator.props.expand = True
            separator.props.visible = False

            btn = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
            btn.connect('clicked', self._cancel_clicked_cb)

            title.pack_start(title_label, False, False, 5)
            title.pack_start(separator, False, False, 0)
            title.pack_end(btn, False, False, 0)

            vbox.pack_start(title, False, False, 5)
            vbox.pack_end(self._sample_window, True, True, 0)

            self._sample_box.add(vbox)
            self._sample_box.show_all()
            self._notebook.add(self._sample_box)

        self._notebook.set_current_page(1)

    def _cancel_clicked_cb(self, button=None):
        self._notebook.set_current_page(0)

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _sample_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_sample = None
            self._cancel_clicked_cb()
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_sample = image_path
        self._cancel_clicked_cb()

        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        GLib.idle_add(self._sample_loader)

    def _sample_loader(self):
        # Convert from thumbnail path to sample path
        basename = os.path.basename(self._selected_sample)[:-4]
        file_path = os.path.join(activity.get_bundle_path(), 'samples',
                                 basename + '.json')
        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
        self.__load_game(file_path)

    def _fill_samples_list(self, store):
        '''
        Append images from the artwork_paths to the store.
        '''
        for filepath in self._scan_for_samples():
            pixbuf = None
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filepath, 100, 100)
            store.append([pixbuf, filepath])

    def _scan_for_samples(self):
        samples = sorted(
            glob.glob(
                os.path.join(activity.get_bundle_path(), 'samples',
                             'thumbnails', '*.png')))
        return samples
class InsertToolbar(Gtk.Toolbar):

    def __init__(self, abiword_canvas):
        GObject.GObject.__init__(self)

        self._abiword_canvas = abiword_canvas

        self._table_btn = ToolButton('create-table')
        self._table_btn.set_tooltip(_('Create table'))
        self.insert(self._table_btn, -1)
        self._grid_create = GridCreateWidget()
        self._grid_create.show()
        self._grid_create.connect('create-table', self._create_table_cb)
        palette = self._table_btn.get_palette()
        palette.set_content(self._grid_create)
        self._table_btn.connect('clicked', self._table_btn_clicked_cb)

        self._table_rows_after = ToolButton('row-insert')
        self._table_rows_after.set_tooltip(_('Insert Row'))
        self._table_rows_after_id = self._table_rows_after.connect(
            'clicked', self._table_rows_after_cb)
        self.insert(self._table_rows_after, -1)

        self._table_delete_rows = ToolButton('row-remove')
        self._table_delete_rows.set_tooltip(_('Delete Row'))
        self._table_delete_rows_id = self._table_delete_rows.connect(
            'clicked', self._table_delete_rows_cb)
        self.insert(self._table_delete_rows, -1)

        self._table_cols_after = ToolButton('column-insert')
        self._table_cols_after.set_tooltip(_('Insert Column'))
        self._table_cols_after_id = self._table_cols_after.connect(
            'clicked', self._table_cols_after_cb)
        self.insert(self._table_cols_after, -1)

        self._table_delete_cols = ToolButton('column-remove')
        self._table_delete_cols.set_tooltip(_('Delete Column'))
        self._table_delete_cols_id = self._table_delete_cols.connect(
            'clicked', self._table_delete_cols_cb)
        self.insert(self._table_delete_cols, -1)

        self.show_all()

        self._abiword_canvas.connect('table-state', self._isTable_cb)
        #self._abiword_canvas.connect('image-selected',
        #       self._image_selected_cb)

    def _table_btn_clicked_cb(self, button):
        button.get_palette().popup(True, button.get_palette().SECONDARY)

    def _create_table_cb(self, abi, rows, cols):
        self._abiword_canvas.insert_table(rows, cols)

    def _table_rows_after_cb(self, button):
        self._abiword_canvas.invoke_ex('insertRowsAfter', '', 0, 0)

    def _table_delete_rows_cb(self, button):
        self._abiword_canvas.invoke_ex('deleteRows', '', 0, 0)

    def _table_cols_after_cb(self, button):
        self._abiword_canvas.invoke_ex('insertColsAfter', '', 0, 0)

    def _table_delete_cols_cb(self, button):
        self._abiword_canvas.invoke_ex('deleteColumns', '', 0, 0)

    def _isTable_cb(self, abi, b):
        self._table_rows_after.set_sensitive(b)
        self._table_delete_rows.set_sensitive(b)
        self._table_cols_after.set_sensitive(b)
        self._table_delete_cols.set_sensitive(b)
Exemple #48
0
class FreeFromMalariaActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.activity_state = {}
        self._name = handle
        self.set_title(_("Free From Malaria"))

        restartTitle = _("End User Licence")
        restartText = _(
            "The materials in this game are in no way intended to replace or supersede professional medical care, advice, diagnosis or treatment of a doctor. The game only provides general advice on Malaria. This advice may not apply to everyone in every locality. If you notice medical symptoms or feel ill, you should consult your doctor. Please seek further advice from your local health authority for further information about Malaria.All information provided in this activity has been produced using peer reviewed scientific and health documentation. By clicking OK you agree that this game should be used for general educational and information purposes only and is not intended to replace medical advice or act as a diagnosis tool. For more details please visit http://worldclassproject.org.uk/."
        )
        dialog = self.create_dialog_ok(restartTitle, restartText)
        result = dialog.run()
        dialog.destroy()

        #init the controller
        #game controller

        if handle.object_id == None:
            print "Launched from home."
        else:
            print "Journal resume."

        # Set title for our Activity
        self.set_title('Free From Malaria')

        # Attach sugar toolbox (Share, ...)
        # Use old <= 0.84 toolbar design
        toolbox = ToolbarBox()
        view_tool_bar = toolbox.get_toolbar()

        #for debug only
        #		self.previous_chapter = ToolButton('gtk-media-forward-rtl')
        #		self.previous_chapter.set_tooltip("Previous Chapter")
        #		self.previous_chapter_id = self.previous_chapter.connect('clicked', self.previous_chapter_clicked)
        #		view_tool_bar.insert(self.previous_chapter, 0)
        #		self.previous_chapter.show()
        ########################################
        self.previous_scene_button = ToolButton('previous')
        self.previous_scene_button.set_tooltip("Previous Scene")
        self.previous_scene_id = self.previous_scene_button.connect(
            'clicked', self.previous_scene_clicked)
        view_tool_bar.insert(self.previous_scene_button, 1)
        self.previous_scene_button.show()
        ###########print "CHANGING"
        self.next_scene = ToolButton('next')
        self.next_scene.set_tooltip("Next Scene")
        self.next_scene_id = self.next_scene.connect('clicked',
                                                     self.next_scene_clicked)
        view_tool_bar.insert(self.next_scene, 2)
        self.next_scene.show()
        ###########
        #disable comic buttons in the main menu
        self.set_navigation(False)
        ######################
        self.sound = ToolButton('audio-volume-high')
        homeString = _("Enable/Disable Sound")
        self.sound.set_tooltip(homeString)
        self.sound_id = self.sound.connect('clicked', self.sound_clicked)
        view_tool_bar.insert(self.sound, 3)
        self.sound.show()
        #############################
        ###########
        self.reload = ToolButton('reload')
        restart = _("Restart")
        self.reload.set_tooltip(restart)
        self.reload_id = self.reload.connect('clicked', self.reload_clicked)
        view_tool_bar.insert(self.reload, 4)
        self.reload.show()
        ######################

        ######################
        self.home = ToolButton('go-home')
        homeString = _("Go to main menu")
        self.home.set_tooltip(homeString)
        self.home_id = self.home.connect('clicked', self.home_clicked)
        view_tool_bar.insert(self.home, 5)
        self.home.show()
        #############################

        view_tool_bar.show()
        # toolbox.add_toolbar('Game Control', view_tool_bar)
        self.set_toolbar_box(toolbox)
        toolbox.show()
        activity_toolbar = toolbox.get_toolbar()
        self.max_participants = 1

        #global game state

        ##restore previous session
        self.controller = controller.GameController.GlobalGameController(self)

        self.read_and_parse_prefs(os.environ['SUGAR_ACTIVITY_ROOT'] +
                                  '/data/defaults')

        #welcomeview = WelcomeScreen.GameWelcomeScreen(self.controller);

        #self.create_new_window(welcomeview)
#		self.mywindow = welcomeview.get_window()
#		self.w_child = self.mywindow.get_child()
#		self.widget = self.w_child
#		self.pack_and_show()

    def next_scene_clicked(self, event):

        self.controller.next_scene()

    def previous_scene_clicked(self, event):
        self.controller.previous_scene()

    def reload_clicked(self, event):
        restartTitle = _("Restart The Story?")
        restartText = _("Are you sure you want to restart the story?")
        dialog = self.create_dialog(restartTitle, restartText)
        result = dialog.run()
        dialog.destroy()

        if result == Gtk.ResponseType.YES:
            self.controller.reload()

    def home_clicked(self, event):
        restartTitle = _("Restart The Main Menu?")
        restartText = _("Are you sure you want to return to the main menu?")
        dialog = self.create_dialog(restartTitle, restartText)

        result = dialog.run()
        dialog.destroy()

        if result == Gtk.ResponseType.YES:
            self.controller.play_game("Welcome", None)

    def sound_clicked(self, event):
        if (self.controller.get_sound() == True):
            self.sound.set_icon_name("audio-volume-muted")
            self.controller.set_sound(False)
        else:
            self.controller.set_sound(True)
            self.sound.set_icon_name("audio-volume-high")

    def create_dialog(self, title, message):
        dialog = Gtk.MessageDialog(parent=None,
                                   buttons=Gtk.ButtonsType.YES_NO,
                                   flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                   type=Gtk.MessageType.QUESTION,
                                   message_format=message)
        dialog.set_title(title)
        return dialog

    def create_dialog_ok(self, title, message):
        dialog = Gtk.MessageDialog(parent=None,
                                   buttons=Gtk.ButtonsType.OK,
                                   flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                   type=Gtk.MessageType.QUESTION,
                                   message_format=message)
        dialog.set_title(title)
        return dialog

    ##disables the nav buttons when comic is not being shown
    def set_navigation(self, switch):

        self.previous_scene_button.set_sensitive(switch)
        self.next_scene.set_sensitive(switch)

    def create_new_window(self, view):

        #self.view = view;
        self.mywindow = view.get_window()

        self.w_child = self.mywindow.get_child()

        self.activity_widget = self.w_child

        #		if(self.widget==None or self.w_child==None):
        #			raise RuntimeError("No widget available")
        self.pack_and_show()

    def pack_and_show(self):
        # Create the main container
        self._main_view = Gtk.VBox()

        # Import our class gtktest():

        # Step 1: Load class, which creates gtktest.widget
        #self.gtktest = devtest2(self)

        # Step 2: Remove the widget's parent
        if self.activity_widget.get_parent():
            self.activity_widget.get_parent().remove(self.activity_widget)

        # Step 3: We attach that widget to our window
        self._main_view.pack_start(self.activity_widget, True, True, 0)

        # Display everything
        self.activity_widget.show()
        self._main_view.show()
        self.set_canvas(self._main_view)
        self.show_all()

    def read_and_parse_prefs(self, file_path):
        #Parse and set preference data from a given file.
        #file_path=os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults'
        #file_path=os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults';
        print "Restoring ", file_path
        try:
            read_file = open(file_path, 'r')
            self.activity_state = json.loads(read_file.read())

            if self.activity_state.has_key('comic_index'):

                comic_index = self.activity_state['comic_index']
                #					print "Restoring with index ",comic_index
                self.controller.set_comic_index(comic_index)
                print "Restored COMIC SCENE from ", file_path

            if self.activity_state.has_key('current_language_code'):
                current_language = self.activity_state['current_language_code']
                self.controller.load_language_by_code(current_language)
                print "Restored LANGUAGE from ", file_path
            self.controller.update_scene()
            read_file.close()

        except IOError:  # as (errno, strerror):
            print "Error: Preferences error"
        except AttributeError:  # as (errno):
            print "Warning: controller not initialised and prefs called. {0}"
        except:
            raise

    def read_file(self, file_path):
        #Read state from datastore.

        self.read_and_parse_prefs(file_path)

    def write_file(self, file_path):
        #		#"""Write state to journal datastore and to persistent file system.
        #		#"""
        #file_path=os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults';
        #		print "WRITING FILE TO JOURNAL : "+os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults'
        # BUG [ID: 3334629]
        try:
            print "WRITING FILE TO JOURNAL : " + file_path
            self.activity_state[
                'comic_index'] = self.controller.get_comic_index()
            self.activity_state[
                'current_language_code'] = self.controller.get_current_language_code(
                )

            serialised_data = json.dumps(self.activity_state)

            to_journal = file(file_path, 'w')
            try:

                to_journal.write(serialised_data)
            except:
                raise
            finally:
                to_journal.close()

                to_persistent_fs = file(
                    os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults', 'w')

            try:
                to_persistent_fs.write(serialised_data)
            except:
                raise
            finally:
                to_persistent_fs.close()
        except AttributeError:  # BUG [ID: 3334629]
            print "Warning: controller not initialised and trying to write. {0}"
class ViewToolbar(Gtk.Toolbar):
    def __init__(self, activity):
        GObject.GObject.__init__(self)

        self._browser = None

        self._activity = activity

        self.zoomout = ToolButton('zoom-out')
        self.zoomout.set_tooltip(_('Zoom out'))
        self.zoomout.connect('clicked', self.__zoomout_clicked_cb)
        self.insert(self.zoomout, -1)
        self.zoomout.show()

        self.zoomin = ToolButton('zoom-in')
        self.zoomin.set_tooltip(_('Zoom in'))
        self.zoomin.connect('clicked', self.__zoomin_clicked_cb)
        self.insert(self.zoomin, -1)
        self.zoomin.show()

        self.zoom_original = ToolButton('zoom-original')
        self.zoom_original.set_tooltip(_('Actual size'))
        self.zoom_original.connect('clicked', self.__zoom_original_clicked_cb)
        self.insert(self.zoom_original, -1)
        self.zoom_original.show()

        self.separator = Gtk.SeparatorToolItem()
        self.separator.set_draw(True)
        self.insert(self.separator, -1)
        self.separator.show()

        self.fullscreen = ToolButton('view-fullscreen')
        self.fullscreen.set_tooltip(_('Fullscreen'))
        self.fullscreen.connect('clicked', self.__fullscreen_clicked_cb)
        self.insert(self.fullscreen, -1)
        self.fullscreen.show()

        self.traybutton = ToggleToolButton('tray-show')
        self.traybutton.set_icon_name('tray-favourite')
        self.traybutton.connect('toggled', self.__tray_toggled_cb)
        self.traybutton.props.sensitive = False
        self.traybutton.props.active = False
        self.insert(self.traybutton, -1)
        self.traybutton.show()

        tabbed_view = self._activity.get_canvas()

        if tabbed_view.get_n_pages():
            self._connect_to_browser(tabbed_view.props.current_browser)

        tabbed_view.connect_after('switch-page', self.__switch_page_cb)

    def __switch_page_cb(self, tabbed_view, page, page_num):
        self._connect_to_browser(tabbed_view.props.current_browser)

    def _connect_to_browser(self, browser):
        self._browser = browser
        self._update_zoom_buttons()

    def _update_zoom_buttons(self):
        is_webkit_browser = isinstance(self._browser, Browser)
        self.zoomin.set_sensitive(is_webkit_browser)
        self.zoomout.set_sensitive(is_webkit_browser)
        self.zoom_original.set_sensitive(is_webkit_browser)

    def __zoom_original_clicked_cb(self, button):
        tabbed_view = self._activity.get_canvas()
        tabbed_view.props.current_browser.set_zoom_level(ZOOM_ORIGINAL)

    def __zoomin_clicked_cb(self, button):
        tabbed_view = self._activity.get_canvas()
        tabbed_view.props.current_browser.zoom_in()

    def __zoomout_clicked_cb(self, button):
        tabbed_view = self._activity.get_canvas()
        tabbed_view.props.current_browser.zoom_out()

    def __fullscreen_clicked_cb(self, button):
        self._activity.fullscreen()

    def __tray_toggled_cb(self, button):
        if button.props.active:
            self._activity.tray.show()
        else:
            self._activity.tray.hide()
        self.update_traybutton_tooltip()

    def update_traybutton_tooltip(self):
        if not self.traybutton.props.active:
            self.traybutton.set_tooltip(_('Show Tray'))
        else:
            self.traybutton.set_tooltip(_('Hide Tray'))
Exemple #50
0
class PhysicsActivity(activity.Activity):

    def __init__(self, handle):
        super(PhysicsActivity, self).__init__(handle)
        self.metadata['mime_type'] = 'application/x-physics-activity'
        self.add_events(Gdk.EventMask.ALL_EVENTS_MASK |
                        Gdk.EventMask.VISIBILITY_NOTIFY_MASK)

        self.connect('visibility-notify-event', self._focus_event)
        self.connect('window-state-event', self._window_event)

        self.game_canvas = sugargame.canvas.PygameCanvas(self)
        self.game = physics.main(self)

        self.preview = None
        self._sample_window = None

        self._fixed = Gtk.Fixed()
        self._fixed.put(self.game_canvas, 0, 0)

        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE

        self.game_canvas.set_size_request(w, h)

        self._constructors = {}
        self.build_toolbar()

        self.set_canvas(self._fixed)
        Gdk.Screen.get_default().connect('size-changed',
                                         self.__configure_cb)

        logging.debug(os.path.join(
                      activity.get_activity_root(), 'data', 'data'))
        self.game_canvas.run_pygame(self.game.run)
        GObject.idle_add(self._setup_sharing)
        self.show_all()

    def _setup_sharing(self):
        self.we_are_sharing = False

        if self.shared_activity:
            # We're joining
            if not self.get_shared():
                xocolors = XoColor(profile.get_color().to_string())
                share_icon = Icon(icon_name='zoom-neighborhood',
                                  xo_color=xocolors)
                self._joined_alert = Alert()
                self._joined_alert.props.icon = share_icon
                self._joined_alert.props.title = _('Please wait')
                self._joined_alert.props.msg = _('Starting connection...')
                self.add_alert(self._joined_alert)
                self._waiting_cursor()

        self._setup_presence_service()

    def __configure_cb(self, event):
        ''' Screen size has changed '''
        self.write_file(os.path.join(
                        activity.get_activity_root(), 'data', 'data'))
        w = Gdk.Screen.width()
        h = Gdk.Screen.height() - 2 * GRID_CELL_SIZE
        pygame.display.set_mode((w, h),
                                pygame.RESIZABLE)
        self.read_file(os.path.join(
                       activity.get_activity_root(), 'data', 'data'))
        self.game.run(True)

    def read_file(self, file_path):
        self.game.read_file(file_path)

    def write_file(self, file_path):
        self.game.write_file(file_path)

    def get_preview(self):
        ''' Custom preview code to get image from pygame. '''
        return self.game_canvas.get_preview()

    def build_toolbar(self):
        self.max_participants = 4

        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        create_toolbar = ToolbarButton()
        create_toolbar.props.page = Gtk.Toolbar()
        create_toolbar.props.icon_name = 'magicpen'
        create_toolbar.props.label = _('Create')
        toolbar_box.toolbar.insert(create_toolbar, -1)
        self._insert_create_tools(create_toolbar)

        self._insert_stop_play_button(toolbar_box.toolbar)

        clear_trace = ToolButton('clear-trace')
        clear_trace.set_tooltip(_('Clear Trace Marks'))
        clear_trace.set_accelerator(_('<ctrl>x'))
        clear_trace.connect('clicked', self.clear_trace_cb)
        clear_trace.set_sensitive(False)
        toolbar_box.toolbar.insert(clear_trace, -1)
        clear_trace.show()
        self.clear_trace = clear_trace

        self._insert_clear_all_button(toolbar_box.toolbar)

        load_example = ToolButton('load-sample')
        load_example.set_tooltip(_('Show sample projects'))
        load_example.connect('clicked', self._create_store)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(load_example, -1)
        load_example.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_size_request(0, -1)
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop = StopButton(self)
        toolbar_box.toolbar.insert(stop, -1)
        stop.show()

        separator = Gtk.SeparatorToolItem()
        activity_button.props.page.insert(separator, -1)
        separator.show()

        export_json = ToolButton('save-as-json')
        export_json.set_tooltip(_('Export tracked objects to journal'))
        export_json.connect('clicked', self._export_json_cb)
        activity_button.props.page.insert(export_json, -1)
        export_json.show()

        export_csv = ToolButton('save-as-csv')
        export_csv.set_tooltip(_('Export tracked objects to journal'))
        export_csv.connect('clicked', self._export_csv_cb)
        activity_button.props.page.insert(export_csv, -1)
        export_csv.show()

        load_project = ToolButton('load-project')
        load_project.set_tooltip(_('Load project from journal'))
        load_project.connect('clicked', self._load_project)
        activity_button.props.page.insert(load_project, -1)
        load_project.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()
        create_toolbar.set_expanded(True)
        return toolbar_box

    def can_close(self):
        self.preview = self.get_preview()
        self.game.loop = False
        return True

    def _insert_stop_play_button(self, toolbar):

        self.stop_play_toolbar = ToolbarButton()
        st_toolbar = self.stop_play_toolbar
        st_toolbar.props.page = Gtk.Toolbar()
        st_toolbar.props.icon_name = 'media-playback-stop'

        self.stop_play_state = True
        self.stop_play = ToolButton('media-playback-stop')
        self.stop_play.set_tooltip(_('Stop'))
        self.stop_play.set_accelerator(_('<ctrl>space'))
        self.stop_play.connect('clicked', self.stop_play_cb)
        self._insert_item(st_toolbar, self.stop_play)
        self.stop_play.show()

        slowest_button = RadioToolButton(group=None)
        slowest_button.set_icon_name('slow-walk-milton-raposo')
        slowest_button.set_tooltip(_('Run slower'))
        slowest_button.connect('clicked', self._set_fps_cb, SLOWEST_FPS)
        self._insert_item(st_toolbar, slowest_button)
        slowest_button.show()

        slow_button = RadioToolButton(group=slowest_button)
        slow_button.set_icon_name('walking')
        slow_button.set_tooltip(_('Run slow'))
        slow_button.connect('clicked', self._set_fps_cb, SLOW_FPS)
        self._insert_item(st_toolbar, slow_button)
        slow_button.show()

        fast_button = RadioToolButton(group=slowest_button)
        fast_button.set_icon_name('running')
        fast_button.set_tooltip('Run fast')
        fast_button.connect('clicked', self._set_fps_cb, FAST_FPS)
        self._insert_item(st_toolbar, fast_button)
        fast_button.show()
        fast_button.set_active(True)

        toolbar.insert(self.stop_play_toolbar, -1)
        self.stop_play_toolbar.show_all()

    def _set_fps_cb(self, button, value):
        self.game.set_game_fps(value)

    def _insert_clear_all_button(self, toolbar):
        self.clear_all = ToolButton('clear_all')
        self.clear_all.set_tooltip(_('Erase All'))
        self.clear_all.set_accelerator(_('<ctrl>a'))
        self.clear_all.connect('clicked', self.clear_all_cb)
        toolbar.insert(self.clear_all, -1)
        self.clear_all.set_sensitive(False)
        self.clear_all.show()

    def _insert_item(self, toolbar, item, pos=-1):
        if hasattr(toolbar, 'insert'):
            toolbar.insert(item, pos)
        else:
            toolbar.props.page.insert(item, pos)

    def _insert_create_tools(self, create_toolbar):
        # Make + add the component buttons
        self.radioList = {}
        for i, c in enumerate(tools.allTools):
            if i == 0:
                button = RadioToolButton(group=None)
                firstbutton = button
            else:
                button = RadioToolButton(group=firstbutton)
            button.set_icon_name(c.icon)
            button.set_tooltip(c.toolTip)
            button.set_accelerator(c.toolAccelerator)
            button.connect('clicked', self.radioClicked)
            palette = self._build_palette(c)
            if palette is not None:
                palette.show()
                button.get_palette().set_content(palette)
            self._insert_item(create_toolbar, button, -1)
            button.show()
            self.radioList[button] = c.name
            if hasattr(c, 'constructor'):
                self._constructors[c.name] = \
                    self.game.toolList[c.name].constructor

    def __icon_path(self, name):
        activity_path = activity.get_bundle_path()
        icon_path = os.path.join(activity_path, 'icons',
                                 name + '.svg')
        return icon_path

    def _build_palette(self, tool):
        if tool.palette_enabled:
            if tool.palette_mode == tools.PALETTE_MODE_ICONS:
                grid = Gtk.Grid()
                for s, settings in enumerate(tool.palette_settings):
                    self.game.toolList[tool.name].buttons.append([])
                    for i, icon_value in enumerate(settings['icon_values']):
                        if i == 0:
                            button = RadioToolButton(group=None)
                            firstbutton = button
                        else:
                            button = RadioToolButton(group=firstbutton)
                        button.set_icon_name(settings['icons'][i])
                        button.connect('clicked',
                                       self._palette_icon_clicked,
                                       tool.name,
                                       s,
                                       settings['name'],
                                       icon_value)
                        grid.attach(button, i, s, 1, 1)
                        self.game.toolList[tool.name].buttons[s].append(button)
                        button.show()
                        if settings['active'] == settings['icons'][i]:
                            button.set_icon_name(settings['icons'][i] +
                                                 '-selected')
                            button.set_active(True)
                return grid
        else:
            return None

    def _palette_icon_clicked(self, widget, toolname, s, value_name, value):
        for tool in tools.allTools:
            if tool.name == toolname:
                # Radio buttons are not highlighting in the palette
                # so adding highlight by hand
                # See http://bugs.sugarlabs.org/ticket/305
                setting = self.game.toolList[tool.name].palette_settings[s]
                for i, button in enumerate(
                        self.game.toolList[tool.name].buttons[s]):
                    icon_name = setting['icons'][i]
                    if button == widget:
                        button.set_icon_name(icon_name + '-selected')
                    else:
                        button.set_icon_name(icon_name)
                if hasattr(tool, 'palette_data_type'):
                    tool.palette_data_type = value
                else:
                    tool.palette_data[value_name] = value

    def clear_trace_alert_cb(self, alert, response):
        self.remove_alert(alert)
        if response is Gtk.ResponseType.OK:
            self.game.full_pos_list = [[] for _ in self.game.full_pos_list]
            self.game.tracked_bodies = 0

    def clear_trace_cb(self, button):
        clear_trace_alert = ConfirmationAlert()
        clear_trace_alert.props.title = _('Are You Sure?')
        clear_trace_alert.props.msg = \
            _('All trace points will be erased. This cannot be undone!')
        clear_trace_alert.connect('response', self.clear_trace_alert_cb)
        self.add_alert(clear_trace_alert)

    def stop_play_cb(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             action='stop_start_toggle'))
        self.stop_play_state = not self.stop_play_state

        if self.stop_play_state:
            self.stop_play.set_icon_name('media-playback-stop')
            self.stop_play.set_tooltip(_('Stop'))

            self.stop_play_toolbar.set_icon_name('media-playback-stop')
        else:
            self.stop_play.set_icon_name('media-playback-start')
            self.stop_play.set_tooltip(_('Start'))

            self.stop_play_toolbar.set_icon_name('media-playback-start')

    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                     action='clear_all'))
        if len(self.game.world.world.GetBodyList()) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _('Are You Sure?')
            clear_all_alert.props.msg = \
                _('All your work will be discarded. This cannot be undone!')
            clear_all_alert.connect('response', clear_all_alert_cb)
            self.add_alert(clear_all_alert)

    def radioClicked(self, button):
        pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                             action=self.radioList[button]))

    def _focus_event(self, event, data=None):
        ''' Send focus events to pygame to allow it to idle when in
        background. '''
        if not self.game.pygame_started:
            logging.debug('focus_event: pygame not yet initialized')
            return
        if data.state == Gdk.VisibilityState.FULLY_OBSCURED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_out'))
        else:
            self.game.show_fake_cursor = True
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_in'))

    def _export_json_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/plain'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        data = self.game.full_pos_list
        jsonfile = open(file_path, 'wb')
        jsonfile.write(json.dumps(data))
        jsonfile.close()

        jobject.set_file_path(os.path.abspath(jsonfile.name))
        datastore.write(jobject)

    def _export_csv_cb(self, button):
        jobject = datastore.create()
        jobject.metadata['title'] = _('Physics export')
        jobject.metadata['mime_type'] = 'text/csv'

        tmp_dir = os.path.join(self.get_activity_root(), 'instance')
        fd, file_path = tempfile.mkstemp(dir=tmp_dir)
        os.close(fd)

        data = self.game.full_pos_list
        csvfile = open(file_path, 'wb')
        writer = csv.writer(csvfile)
        writer.writerows(data)
        csvfile.close()

        jobject.set_file_path(os.path.abspath(csvfile.name))
        datastore.write(jobject)

    def _window_event(self, window, event):
        ''' Send focus out event to pygame when switching to a desktop
        view. '''
        if event.changed_mask & Gdk.WindowState.ICONIFIED:
            pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                 action='focus_out'))

    def _setup_presence_service(self):
        ''' Setup the Presence Service. '''
        self.pservice = presenceservice.get_instance()

        owner = self.pservice.get_owner()
        self.owner = owner
        self.buddies = [owner]
        self._share = ''
        self.connect('shared', self._shared_cb)
        self.connect('joined', self._joined_cb)

    def _shared_cb(self, activity):
        ''' Either set up initial share...'''
        if self.get_shared_activity() is None:
            logging.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        self.initiating = True
        self.waiting = False
        self.we_are_sharing = True
        logging.debug('I am sharing...')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        logging.debug('This is my activity: making a tube...')
        id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
            SERVICE, {})

    def _joined_cb(self, activity):
        ''' ...or join an exisiting share. '''
        if self.get_shared_activity() is None:
            logging.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        self.initiating = False
        logging.debug('I joined a shared activity.')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        logging.debug('I am joining an activity: waiting for a tube...')
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
            reply_handler=self._list_tubes_reply_cb,
            error_handler=self._list_tubes_error_cb)

        self.waiting = True

        if self._joined_alert is not None:
            self.remove_alert(self._joined_alert)
            self._joined_alert = None
            self._restore_cursor()
            self.we_are_sharing = True

    def _restore_cursor(self):
        ''' No longer waiting, so restore standard cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.get_window().set_cursor(self.old_cursor)

    def _waiting_cursor(self):
        ''' Waiting, so set watch cursor. '''
        if not hasattr(self, 'get_window'):
            return
        self.old_cursor = self.get_window().get_cursor()
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def _list_tubes_reply_cb(self, tubes):
        ''' Reply to a list request. '''
        for tube_info in tubes:
            self._new_tube_cb(*tube_info)

    def _list_tubes_error_cb(self, e):
        ''' Log errors. '''
        logging.error('ListTubes() failed: %s', e)

    def _new_tube_cb(self, id, initiator, type, service, params, state):
        ''' Create a new tube. '''
        logging.debug('New tube: ID=%d initator=%d type=%d service=%s '
                      'params=%r state=%d', id, initiator, type, service,
                      params, state)

        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:
                self.tubes_chan[
                    telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)

            tube_conn = TubeConnection(
                self.conn,
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id,
                group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])

            self.chattube = ChatTube(tube_conn, self.initiating,
                                     self.event_received_cb)

    def event_received_cb(self, text):
        ''' Data is passed as tuples: cmd:text '''
        dispatch_table = {'C': self._construct_shared_circle,
                          'B': self._construct_shared_box,
                          'T': self._construct_shared_triangle,
                          'P': self._construct_shared_polygon,
                          'M': self._construct_shared_magicpen,
                          'j': self._add_shared_joint,
                          'p': self._add_shared_pin,
                          'm': self._add_shared_motor,
                          't': self._add_shared_track,
                          'c': self._add_shared_chain,
                          }
        logging.debug('<<< %s' % (text[0]))
        dispatch_table[text[0]](text[2:])

    def _construct_shared_circle(self, data):
        circle_data = json.loads(data)
        pos = circle_data[0]
        radius = circle_data[1]
        density = circle_data[2]
        restitution = circle_data[3]
        friction = circle_data[4]
        self._constructors['Circle'](pos, radius, density, restitution,
                                     friction, share=False)

    def _construct_shared_box(self, data):
        box_data = json.loads(data)
        pos1 = box_data[0]
        pos2 = box_data[1]
        density = box_data[2]
        restitution = box_data[3]
        friction = box_data[4]
        self._constructors['Box'](pos1, pos2, density, restitution,
                                  friction, share=False)

    def _construct_shared_triangle(self, data):
        triangle_data = json.loads(data)
        pos1 = triangle_data[0]
        pos2 = triangle_data[1]
        density = triangle_data[2]
        restitution = triangle_data[3]
        friction = triangle_data[4]
        self._constructors['Triangle'](pos1, pos2, density, restitution,
                                       friction, share=False)

    def _construct_shared_polygon(self, data):
        polygon_data = json.loads(data)
        vertices = polygon_data[0]
        density = polygon_data[1]
        restitution = polygon_data[2]
        friction = polygon_data[3]
        self._constructors['Polygon'](vertices, density, restitution,
                                      friction, share=False)

    def _construct_shared_magicpen(self, data):
        magicpen_data = json.loads(data)
        vertices = magicpen_data[0]
        density = magicpen_data[1]
        restitution = magicpen_data[2]
        friction = magicpen_data[3]
        self._constructors['Magicpen'](vertices, density, restitution,
                                       friction, share=False)

    def _add_shared_joint(self, data):
        joint_data = json.loads(data)
        pos1 = joint_data[0]
        pos2 = joint_data[1]
        self._constructors['Joint'](pos1, pos2, share=False)

    def _add_shared_pin(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        self._constructors['Pin'](pos, share=False)

    def _add_shared_motor(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        speed = joint_data[1]
        self._constructors['Motor'](pos, speed, share=False)

    def _add_shared_track(self, data):
        joint_data = json.loads(data)
        pos = joint_data[0]
        color = joint_data[1]
        self._constructors['Track'](pos, color, share=False)

    def _add_shared_chain(self, data):
        joint_data = json.loads(data)
        vertices = joint_data[0]
        link_length = joint_data[1]
        radius = joint_data[2]
        self._constructors['Chain'](vertices, link_length, radius,
                                    share=False)

    def send_event(self, text):
        ''' Send event through the tube. '''
        if hasattr(self, 'chattube') and self.chattube is not None:
            logging.debug('>>> %s' % (text[0]))
            self.chattube.SendText(text)

    def _load_project(self, button):
        chooser = ObjectChooser(parent=self)
        result = chooser.run()
        if result == Gtk.ResponseType.ACCEPT:
            dsobject = chooser.get_selected_object()
            file_path = dsobject.get_file_path()
            self.__load_game(file_path, True)
            chooser.destroy()

    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _('Are You Sure?')
        confirmation_alert.props.msg = \
            _('All your work will be discarded. This cannot be undone!')

        def action(alert, response):
            self.remove_alert(alert)
            if not response is Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, 'r')
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
                self.game.run(True)
            except:
                title = _('Load project from journal')
                if not journal:
                    title = _('Load example')
                msg = _(
                    'Error: Cannot open Physics project from this file.')
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect(
                    'response',
                    lambda alert,
                    response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect('response', action)
        self.add_alert(confirmation_alert)

    def _create_store(self, widget=None):
        width = Gdk.Screen.width() / 4
        height = Gdk.Screen.height() / 4

        if self._sample_window is None:
            self._sample_box = Gtk.EventBox()

            vbox = Gtk.VBox()
            self._sample_window = Gtk.ScrolledWindow()
            self._sample_window.set_policy(Gtk.PolicyType.NEVER,
                                           Gtk.PolicyType.AUTOMATIC)
            self._sample_window.set_border_width(4)
            w = Gdk.Screen.width() / 2
            h = Gdk.Screen.height() / 2
            self._sample_window.set_size_request(w, h)
            self._sample_window.show()

            store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)

            icon_view = Gtk.IconView()
            icon_view.set_model(store)
            icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
            icon_view.connect('selection-changed', self._sample_selected,
                              store)
            icon_view.set_pixbuf_column(0)
            icon_view.grab_focus()
            self._sample_window.add(icon_view)
            icon_view.show()
            self._fill_samples_list(store)

            title = Gtk.HBox()
            title_label = Gtk.Label(_("Select a sample..."))
            separator = Gtk.HSeparator()
            separator.props.expand = True
            separator.props.visible = False

            btn = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
            btn.connect('clicked', lambda button: self._sample_box.hide())

            title.pack_start(title_label, False, False, 5)
            title.pack_start(separator, False, False, 0)
            title.pack_end(btn, False, False, 0)

            vbox.pack_start(title, False, False, 5)
            vbox.pack_end(self._sample_window, True, True, 0)

            self._sample_box.add(vbox)

            self._fixed.put(self._sample_box, width, height)

        if self._sample_window:
            # Remove and add again. Maybe its on portrait mode.
            self._fixed.remove(self._sample_box)
            self._fixed.put(self._sample_box, width, height)

        self._sample_box.show_all()

    def _get_selected_path(self, widget, store):
        try:
            iter_ = store.get_iter(widget.get_selected_items()[0])
            image_path = store.get(iter_, 1)[0]

            return image_path, iter_
        except:
            return None

    def _sample_selected(self, widget, store):
        selected = self._get_selected_path(widget, store)

        if selected is None:
            self._selected_sample = None
            self._sample_box.hide()
            return

        image_path, _iter = selected
        iter_ = store.get_iter(widget.get_selected_items()[0])
        image_path = store.get(iter_, 1)[0]

        self._selected_sample = image_path
        self._sample_box.hide()

        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
        GObject.idle_add(self._sample_loader)

    def _sample_loader(self):
        # Convert from thumbnail path to sample path
        basename = os.path.basename(self._selected_sample)[:-4]
        file_path = os.path.join(activity.get_bundle_path(),
                                 'samples', basename + '.json')
        self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
        self.__load_game(file_path)

    def _fill_samples_list(self, store):
        '''
        Append images from the artwork_paths to the store.
        '''
        for filepath in self._scan_for_samples():
            pixbuf = None
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filepath, 100, 100)
            store.append([pixbuf, filepath])

    def _scan_for_samples(self):
        samples = sorted(
            glob.glob(
                os.path.join(
                    activity.get_bundle_path(),
                    'samples',
                    'thumbnails',
                    '*.png')))
        return samples
Exemple #51
0
    def __init__(self, handle):
        super(PeterActivity, self).__init__(handle)

        # Get user's Sugar colors
        sugarcolors = profile.get_color().to_string().split(',')
        colors = [[
            int(sugarcolors[0][1:3], 16),
            int(sugarcolors[0][3:5], 16),
            int(sugarcolors[0][5:7], 16)
        ],
                  [
                      int(sugarcolors[1][1:3], 16),
                      int(sugarcolors[1][3:5], 16),
                      int(sugarcolors[1][5:7], 16)
                  ]]

        # No sharing
        self.max_participants = 1

        # Build the activity toolbar.
        toolbox = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        toolbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        self._add_speed_slider(toolbox.toolbar)

        cyan = ToolButton('cyan')
        toolbox.toolbar.insert(cyan, -1)
        cyan.set_tooltip(_('Next pattern'))
        cyan.connect('clicked', self._button_cb, 'cyan')
        cyan.set_sensitive(False)
        cyan.show()

        green = ToolButton('green')
        toolbox.toolbar.insert(green, -1)
        green.set_tooltip(_('Draw'))
        green.connect('clicked', self._button_cb, 'green')
        green.show()

        red = ToolButton('red')
        toolbox.toolbar.insert(red, -1)
        red.set_tooltip(_('Stop'))
        red.connect('clicked', self._button_cb, 'red')
        red.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = True
        toolbox.toolbar.insert(separator, -1)
        separator.show()

        label = Gtk.Label('')
        label.set_use_markup(True)
        label.show()
        labelitem = Gtk.ToolItem()
        labelitem.add(label)
        toolbox.toolbar.insert(labelitem, -1)
        labelitem.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = _('<Ctrl>Q')
        toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

        toolbox.show()
        self.set_toolbar_box(toolbox)

        # Create the game instance.
        self.game = Spirolaterals.Spirolaterals(colors)

        # Build the Pygame canvas.
        self._pygamecanvas = \
            sugargame.canvas.PygameCanvas(self)
        # Note that set_canvas implicitly calls
        # read_file when resuming from the Journal.
        self.set_canvas(self._pygamecanvas)
        self.game.canvas = self._pygamecanvas

        Gdk.Screen.get_default().connect('size-changed', self.__configure_cb)

        # Start the game running.
        self.game.set_cyan_button(cyan)
        self.game.set_label(label)
        self._speed_range.set_value(200)
        self._pygamecanvas.run_pygame(self.game.run)
Exemple #52
0
class ReflectActivity(activity.Activity):
    ''' An activity for reflecting on one's work '''
    def __init__(self, handle):
        ''' Initialize the toolbar '''
        try:
            super(ReflectActivity, self).__init__(handle)
        except dbus.exceptions.DBusException as e:
            _logger.error(str(e))

        logging.error('setting reflection data to []')
        self.reflection_data = []

        self.connect('realize', self.__realize_cb)

        self.font_size = 8

        self.max_participants = 4
        self._setup_toolbars()

        color = profile.get_color()
        color_stroke = color.get_stroke_color()
        color_fill = color.get_fill_color()

        lighter = utils.lighter_color([color_stroke, color_fill])
        darker = 1 - lighter

        if lighter == 0:
            self.bg_color = style.Color(color_stroke)
            self.fg_color = style.Color(color_fill)
        else:
            self.bg_color = style.Color(color_fill)
            self.fg_color = style.Color(color_stroke)

        self.modify_bg(Gtk.StateType.NORMAL, self.bg_color.get_gdk_color())

        self.bundle_path = activity.get_bundle_path()
        self.tmp_path = os.path.join(activity.get_activity_root(), 'instance')

        self.sharing = False
        self._copy_entry = None
        self._paste_entry = None
        self._webkit = None
        self._clipboard_text = ''
        self._fixed = None

        self.initiating = True
        if self.shared_activity:
            # We're joining
            if not self.get_shared():
                self.initiating = False

                self.busy_cursor()
                share_icon = Icon(icon_name='zoom-neighborhood')
                self._joined_alert = Alert()
                self._joined_alert.props.icon = share_icon
                self._joined_alert.props.title = _('Please wait')
                self._joined_alert.props.msg = _('Starting connection...')
                self.add_alert(self._joined_alert)

                # Wait for joined signal
                self.connect("joined", self._joined_cb)

        self._open_reflect_windows()

        self._setup_presence_service()

        # Joiners wait to receive data from sharer
        # Otherwise, load reflections from local store
        if not self.shared_activity:
            self.busy_cursor()
            GObject.idle_add(self._load_reflections)

    def read_file(self, file_path):
        fd = open(file_path, 'r')
        data = fd.read()
        fd.close()
        self.reflection_data = json.loads(data)

    def write_file(self, file_path):
        data = json.dumps(self.reflection_data)
        fd = open(file_path, 'w')
        fd.write(data)
        fd.close()

        self.metadata['font_size'] = str(self.font_size)

    def _load_reflections(self):
        self._find_starred()
        self._reflect_window.load(self.reflection_data)
        self.reset_cursor()

    def _found_obj_id(self, obj_id):
        for item in self.reflection_data:
            if 'obj_id' in item and item['obj_id'] == obj_id:
                return True
        return False

    def reload_data(self, data):
        ''' Reload data after sorting or searching '''
        self._reflection_data = data[:]
        self._reflect_window.reload(self._reflection_data)
        self.reset_scrolled_window_adjustments()

    def _find_starred(self):
        ''' Find all the _stars in the Journal. '''
        self.dsobjects, self._nobjects = datastore.find({'keep': '1'})
        for dsobj in self.dsobjects:
            if self._found_obj_id(dsobj.object_id):
                continue  # Already have this object -- TODO: update it
            self._add_new_from_journal(dsobj)

    def _add_new_from_journal(self, dsobj):
        self.reflection_data.append({
            'title': _('Untitled'),
            'obj_id': dsobj.object_id
        })
        if hasattr(dsobj, 'metadata'):
            if 'creation_time' in dsobj.metadata:
                self.reflection_data[-1]['creation_time'] = \
                    dsobj.metadata['creation_time']
            else:
                self.reflection_data[-1]['creation_time'] = \
                    int(time.time())
            if 'timestamp' in dsobj.metadata:
                self.reflection_data[-1]['modification_time'] = \
                    dsobj.metadata['timestamp']
            else:
                self.reflection_data[-1]['modification_time'] = \
                    self.reflection_data[-1]['creation_time']
            if 'activity' in dsobj.metadata:
                self.reflection_data[-1]['activities'] = \
                    [utils.bundle_id_to_icon(dsobj.metadata['activity'])]
            if 'title' in dsobj.metadata:
                self.reflection_data[-1]['title'] = \
                    dsobj.metadata['title']
            if 'description' in dsobj.metadata:
                self.reflection_data[-1]['content'] = \
                    [{'text': dsobj.metadata['description']}]
            else:
                self.reflection_data[-1]['content'] = []
            if 'tags' in dsobj.metadata:
                self.reflection_data[-1]['tags'] = []
                tags = dsobj.metadata['tags'].split()
                for tag in tags:
                    if tag[0] != '#':
                        self.reflection_data[-1]['tags'].append('#' + tag)
                    else:
                        self.reflection_data[-1]['tags'].append(tag)
            if 'comments' in dsobj.metadata:
                try:
                    comments = json.loads(dsobj.metadata['comments'])
                except BaseException:
                    comments = []
                self.reflection_data[-1]['comments'] = []
                for comment in comments:
                    try:
                        data = {
                            'nick': comment['from'],
                            'comment': comment['message']
                        }
                        if 'icon-color' in comment:
                            colors = comment['icon-color'].split(',')
                            darker = 1 - utils.lighter_color(colors)
                            data['color'] = colors[darker]
                        else:
                            data['color'] = '#000000'
                        self.reflection_data[-1]['comments'].append(data)
                    except BaseException:
                        _logger.debug('could not parse comment %s' % comment)
            if 'mime_type' in dsobj.metadata and \
               dsobj.metadata['mime_type'][0:5] == 'image':
                new_path = os.path.join(self.tmp_path, dsobj.object_id)
                try:
                    shutil.copy(dsobj.file_path, new_path)
                except Exception as e:
                    logging.error("Couldn't copy %s to %s: %s" %
                                  (dsobj.file_path, new_path, e))
                self.reflection_data[-1]['content'].append({'image': new_path})
            elif 'preview' in dsobj.metadata:
                pixbuf = utils.get_pixbuf_from_journal(dsobj, 300, 225)
                if pixbuf is not None:
                    path = os.path.join(self.tmp_path,
                                        dsobj.object_id + '.png')
                    utils.save_pixbuf_to_file(pixbuf, path)
                    self.reflection_data[-1]['content'].append({'image': path})
            self.reflection_data[-1]['stars'] = 0

    def delete_item(self, obj_id):
        for i, obj in enumerate(self.reflection_data):
            if obj['obj_id'] == obj_id:
                self.reflection_data.remove(self.reflection_data[i])
                return

    def busy_cursor(self):
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))

    def reset_cursor(self):
        self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.LEFT_PTR))

    def _open_reflect_windows(self):
        # Most things need only be done once
        if self._fixed is None:
            self._fixed = Gtk.Fixed()
            self._fixed.set_size_request(Gdk.Screen.width(),
                                         Gdk.Screen.height())

            # Offsets from the bottom of the screen
            dy1 = 2 * style.GRID_CELL_SIZE
            dy2 = 1 * style.GRID_CELL_SIZE

            self._button_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._button_area.set_size_request(Gdk.Screen.width(),
                                               style.GRID_CELL_SIZE)
            self._fixed.put(self._button_area, 0, 0)
            self._button_area.show()

            self._scrolled_window = Gtk.ScrolledWindow()
            self._scrolled_window.set_size_request(Gdk.Screen.width(),
                                                   Gdk.Screen.height() - dy1)
            self._set_scroll_policy()
            self._graphics_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._scrolled_window.add_with_viewport(self._graphics_area)
            self._graphics_area.show()
            self._fixed.put(self._scrolled_window, 0, dy2)
            self._scrolled_window.show()

            self._overlay_window = Gtk.ScrolledWindow()
            self._overlay_window.set_size_request(style.GRID_CELL_SIZE * 10,
                                                  style.GRID_CELL_SIZE * 6)
            self._overlay_window.modify_bg(Gtk.StateType.NORMAL,
                                           style.COLOR_WHITE.get_gdk_color())
            self._overlay_window.set_policy(Gtk.PolicyType.NEVER,
                                            Gtk.PolicyType.AUTOMATIC)
            self._overlay_area = Gtk.Alignment.new(0.5, 0, 0, 0)
            self._overlay_window.add_with_viewport(self._overlay_area)
            self._overlay_area.show()
            x = int((Gdk.Screen.width() - style.GRID_CELL_SIZE * 10) / 2)
            self._fixed.put(self._overlay_window, 0, Gdk.Screen.height())
            self._overlay_window.show()
            self._old_overlay_widget = None

            self._reflect_window = ReflectWindow(self)
            self._reflect_window.show()

            Gdk.Screen.get_default().connect('size-changed',
                                             self._configure_cb)
            self._toolbox.connect('hide', self._resize_hide_cb)
            self._toolbox.connect('show', self._resize_show_cb)

            self._reflect_window.set_events(Gdk.EventMask.KEY_PRESS_MASK)
            self._reflect_window.connect('key_press_event',
                                         self._reflect_window.keypress_cb)
            self._reflect_window.set_can_focus(True)
            self._reflect_window.grab_focus()

        self.set_canvas(self._fixed)
        self._fixed.show()

    def reset_scrolled_window_adjustments(self):
        adj = self._scrolled_window.get_hadjustment()
        if adj is not None:
            adj.set_value(0)
        adj = self._scrolled_window.get_vadjustment()
        if adj is not None:
            adj.set_value(0)

    def load_graphics_area(self, widget):
        self._graphics_area.add(widget)

    def load_button_area(self, widget):
        self._button_area.add(widget)

    def load_overlay_area(self, widget):
        if self._old_overlay_widget is not None:
            self._overlay_area.remove(self._old_overlay_widget)
        self._overlay_area.add(widget)
        self._old_overlay_widget = widget

    def show_overlay_area(self):
        x = int((Gdk.Screen.width() - style.GRID_CELL_SIZE * 10) / 2)
        self._fixed.move(self._overlay_window, x, style.GRID_CELL_SIZE)

    def hide_overlay_area(self):
        self._fixed.move(self._overlay_window, 0, Gdk.Screen.height())

    def collapse_overlay_area(self, button, event):
        self._fixed.move(self._overlay_window, 0, Gdk.Screen.height())

    def _resize_hide_cb(self, widget):
        self._resize_canvas(widget, True)

    def _resize_show_cb(self, widget):
        self._resize_canvas(widget, False)

    def _configure_cb(self, event):
        self._fixed.set_size_request(Gdk.Screen.width(), Gdk.Screen.height())
        self._set_scroll_policy()
        self._resize_canvas(None)
        self._reflect_window.reload_graphics()

    def _resize_canvas(self, widget, fullscreen=False):
        # When a toolbar is expanded or collapsed, resize the canvas
        if hasattr(self, '_reflect_window'):
            if self.toolbar_expanded():
                dy1 = 3 * style.GRID_CELL_SIZE
                dy2 = 2 * style.GRID_CELL_SIZE
            else:
                dy1 = 2 * style.GRID_CELL_SIZE
                dy2 = 1 * style.GRID_CELL_SIZE

            if fullscreen:
                dy1 -= 2 * style.GRID_CELL_SIZE
                dy2 -= 2 * style.GRID_CELL_SIZE

            self._scrolled_window.set_size_request(Gdk.Screen.width(),
                                                   Gdk.Screen.height() - dy2)
            self._fixed.move(self._button_area, 0, 0)

        self._about_panel_visible = False

    def toolbar_expanded(self):
        if self.activity_button.is_expanded():
            return True
        elif self.edit_toolbar_button.is_expanded():
            return True
        elif self.view_toolbar_button.is_expanded():
            return True
        return False

    def get_activity_version(self):
        info_path = os.path.join(self.bundle_path, 'activity', 'activity.info')
        try:
            info_file = open(info_path, 'r')
        except Exception as e:
            _logger.error('Could not open %s: %s' % (info_path, e))
            return 'unknown'

        cp = ConfigParser()
        cp.readfp(info_file)

        section = 'Activity'

        if cp.has_option(section, 'activity_version'):
            activity_version = cp.get(section, 'activity_version')
        else:
            activity_version = 'unknown'
        return activity_version

    def get_uid(self):
        if len(self.volume_data) == 1:
            return self.volume_data[0]['uid']
        else:
            return 'unknown'

    def _setup_toolbars(self):
        ''' Setup the toolbars. '''
        self._toolbox = ToolbarBox()

        self.activity_button = ActivityToolbarButton(self)
        self.activity_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.activity_button, 0)
        self.activity_button.show()

        self.set_toolbar_box(self._toolbox)
        self._toolbox.show()
        self.toolbar = self._toolbox.toolbar

        view_toolbar = Gtk.Toolbar()
        self.view_toolbar_button = ToolbarButton(page=view_toolbar,
                                                 label=_('View'),
                                                 icon_name='toolbar-view')
        self.view_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.view_toolbar_button, 1)
        view_toolbar.show()
        self.view_toolbar_button.show()

        button = ToolButton('view-fullscreen')
        button.set_tooltip(_('Fullscreen'))
        button.props.accelerator = '<Alt>Return'
        view_toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self._fullscreen_cb)

        edit_toolbar = Gtk.Toolbar()
        self.edit_toolbar_button = ToolbarButton(page=edit_toolbar,
                                                 label=_('Edit'),
                                                 icon_name='toolbar-edit')
        self.edit_toolbar_button.connect('clicked', self._resize_canvas)
        self._toolbox.toolbar.insert(self.edit_toolbar_button, 1)
        edit_toolbar.show()
        self.edit_toolbar_button.show()

        self._copy_button = ToolButton('edit-copy')
        self._copy_button.set_tooltip(_('Copy'))
        self._copy_button.props.accelerator = '<Ctrl>C'
        edit_toolbar.insert(self._copy_button, -1)
        self._copy_button.show()
        self._copy_button.connect('clicked', self._copy_cb)
        self._copy_button.set_sensitive(False)

        self._paste_button = ToolButton('edit-paste')
        self._paste_button.set_tooltip(_('Paste'))
        self._paste_button.props.accelerator = '<Ctrl>V'
        edit_toolbar.insert(self._paste_button, -1)
        self._paste_button.show()
        self._paste_button.connect('clicked', self._paste_cb)
        self._paste_button.set_sensitive(False)

        button = ToolButton('list-add')
        button.set_tooltip(_('Add Item'))
        button.props.accelerator = '<Ctrl>+'
        self._toolbox.toolbar.insert(button, -1)
        button.show()
        button.connect('clicked', self.__add_item_cb)

        self._date_button = RadioToolButton('date-sort', group=None)
        self._date_button.set_tooltip(_('Sort by Date'))
        self._date_button.connect('clicked', self._date_button_cb)
        self._toolbox.toolbar.insert(self._date_button, -1)
        self._date_button.show()

        self._title_button = RadioToolButton('title-sort',
                                             group=self._date_button)
        self._title_button.set_tooltip(_('Sort by Title'))
        self._title_button.connect('clicked', self._title_button_cb)
        self._toolbox.toolbar.insert(self._title_button, -1)
        self._title_button.show()

        self._stars_button = RadioToolButton('stars-sort',
                                             group=self._date_button)
        self._stars_button.set_tooltip(_('Sort by Favourite'))
        self._stars_button.connect('clicked', self._stars_button_cb)
        self._toolbox.toolbar.insert(self._stars_button, -1)
        self._stars_button.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()

        tool_item = Gtk.ToolItem()
        tool_item.set_expand(True)
        tool_item.add(self._search_entry)
        self._search_entry.show()
        self._toolbox.toolbar.insert(tool_item, -1)
        tool_item.show()

        self._search_button = ToolButton('dialog-ok')
        self._search_button.set_tooltip(_('Search by Tags'))
        self._search_button.connect('clicked', self._search_button_cb)
        self._toolbox.toolbar.insert(self._search_button, -1)
        self._search_button.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self._toolbox.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        self._toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _search_button_cb(self, button):
        self.busy_cursor()
        self._do_search()

    def _search_entry_activated_cb(self, entry):
        self.busy_cursor()
        self._do_search()

    def _do_search(self):
        logging.debug('_search_entry_activated_cb')
        if self._search_entry.props.text == '':
            logging.debug('clearing search')
            for item in self.reflection_data:
                item['hidden'] = False
        else:
            tags = self._search_entry.props.text.split()
            for i, tag in enumerate(tags):
                if not tag[0] == '#':
                    tags[i] = '#%s' % tag
            logging.error(tags)
            for item in self.reflection_data:
                hidden = True
                if 'tags' in item:
                    for tag in tags:
                        if tag in item['tags']:
                            hidden = False
                item['hidden'] = hidden
        self.reload_data(self.reflection_data)
        self.reset_cursor()

    def _search_entry_changed_cb(self, entry):
        logging.debug('_search_entry_changed_cb search for \'%s\'',
                      self._search_entry.props.text)
        self.busy_cursor()
        self._do_search_changed()

    def _do_search_changed(self):
        if self._search_entry.props.text == '':
            logging.debug('clearing search')
            for item in self.reflection_data:
                item['hidden'] = False
            self.reload_data(self.reflection_data)
        self.reset_cursor()

    def _title_button_cb(self, button):
        ''' sort by title '''
        self.busy_cursor()
        GObject.idle_add(self._title_sort)

    def _title_sort(self):
        sorted_data = sorted(self.reflection_data,
                             key=lambda item: item['title'].lower())
        self.reload_data(sorted_data)
        self.reset_cursor()

    def _date_button_cb(self, button):
        ''' sort by modification date '''
        self.busy_cursor()
        GObject.idle_add(self._date_sort)

    def _date_sort(self):
        sorted_data = sorted(self.reflection_data,
                             key=lambda item: int(item['modification_time']),
                             reverse=True)
        self.reload_data(sorted_data)
        self.reset_cursor()

    def _stars_button_cb(self, button):
        ''' sort by number of stars '''
        self.busy_cursor()
        GObject.idle_add(self._stars_sort)

    def _stars_sort(self):
        sorted_data = sorted(self.reflection_data,
                             key=lambda item: item['stars'],
                             reverse=True)
        self.reload_data(sorted_data)
        self.reset_cursor()

    def __realize_cb(self, window):
        self.window_xid = window.get_window().get_xid()

    def set_copy_widget(self, webkit=None, text_entry=None):
        # Each task is responsible for setting a widget for copy
        if webkit is not None:
            self._webkit = webkit
        else:
            self._webkit = None
        if text_entry is not None:
            self._copy_entry = text_entry
        else:
            self._copy_entry = None

        self._copy_button.set_sensitive(webkit is not None
                                        or text_entry is not None)

    def _copy_cb(self, button):
        if self._copy_entry is not None:
            self._copy_entry.copy_clipboard()
        elif self._webkit is not None:
            self._webkit.copy_clipboard()
        else:
            _logger.debug('No widget set for copy.')

    def set_paste_widget(self, text_entry=None):
        # Each task is responsible for setting a widget for paste
        if text_entry is not None:
            self._paste_entry = text_entry
        self._paste_button.set_sensitive(text_entry is not None)

    def _paste_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.clipboard_text = clipboard.wait_for_text()
        if self._paste_entry is not None:
            self._paste_entry.paste_clipboard()
        else:
            _logger.debug('No widget set for paste (%s).' %
                          self.clipboard_text)

    def _fullscreen_cb(self, button):
        ''' Hide the Sugar toolbars. '''
        self.fullscreen()

    def __add_item_cb(self, button):
        try:
            chooser = ObjectChooser(parent=self, what_filter=None)
        except TypeError:
            chooser = ObjectChooser(
                None, self._reflection.activity,
                Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT)

        try:
            result = chooser.run()
            if result == Gtk.ResponseType.ACCEPT:
                jobject = chooser.get_selected_object()
                if jobject:
                    self._add_new_from_journal(jobject)
                    self.reload_data(self.reflection_data)
        finally:
            chooser.destroy()
            del chooser

    def _set_scroll_policy(self):
        if Gdk.Screen.width() < Gdk.Screen.height():
            self._scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                             Gtk.PolicyType.AUTOMATIC)
        else:
            self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                             Gtk.PolicyType.AUTOMATIC)

    def _remove_alert_cb(self, alert, response_id):
        self.remove_alert(alert)

    def _close_alert_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            self.close()

    def _setup_presence_service(self):
        ''' Setup the Presence Service. '''
        self.pservice = presenceservice.get_instance()

        owner = self.pservice.get_owner()
        self.owner = owner
        self._share = ''
        self.connect('shared', self._shared_cb)
        self.connect('joined', self._joined_cb)

    def _shared_cb(self, activity):
        ''' Either set up initial share...'''
        if self.shared_activity is None:
            _logger.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        self.initiating = True
        self._waiting_for_reflections = False
        _logger.debug('I am sharing...')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        _logger.debug('This is my activity: making a tube...')
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
            SERVICE, {})

        self.sharing = True

    def _joined_cb(self, activity):
        ''' ...or join an exisiting share. '''
        if self.shared_activity is None:
            _logger.error('Failed to share or join activity ... \
                shared_activity is null in _shared_cb()')
            return

        if self._joined_alert is not None:
            self.remove_alert(self._joined_alert)
            self._joined_alert = None

        self.initiating = False
        self._waiting_for_reflections = True
        _logger.debug('I joined a shared activity.')

        self.conn = self.shared_activity.telepathy_conn
        self.tubes_chan = self.shared_activity.telepathy_tubes_chan
        self.text_chan = self.shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        _logger.debug('I am joining an activity: waiting for a tube...')
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
            reply_handler=self._list_tubes_reply_cb,
            error_handler=self._list_tubes_error_cb)

        self.sharing = True

    def _list_tubes_reply_cb(self, tubes):
        ''' Reply to a list request. '''
        for tube_info in tubes:
            self._new_tube_cb(*tube_info)

    def _list_tubes_error_cb(self, e):
        ''' Log errors. '''
        _logger.error('ListTubes() failed: %s', e)

    def _new_tube_cb(self, id, initiator, type, service, params, state):
        ''' Create a new tube. '''
        _logger.debug(
            'New tube: ID=%d initator=%d type=%d service=%s '
            'params=%r state=%d', id, initiator, type, service, params, state)

        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(
                    id)

            self.collab = CollabWrapper(self)
            self.collab.message.connect(self.event_received_cb)
            self.collab.setup()

            if self._waiting_for_reflections:
                self.send_event(JOIN_CMD, {})
                self._joined_alert = Alert()
                self._joined_alert.props.title = _('Please wait')
                self._joined_alert.props.msg = _('Requesting reflections...')
                self.add_alert(self._joined_alert)

    def event_received_cb(self, collab, buddy, msg):
        ''' Data is passed as tuples: cmd:text '''
        command = msg.get("command")
        payload = msg.get("payload")
        logging.debug(command)

        if command == JOIN_CMD:
            # Sharer needs to send reflections database to joiners.
            if self.initiating:
                # Send pictures first.
                for item in self.reflection_data:
                    if 'content' in item:
                        for content in item['content']:
                            if 'image' in content:
                                pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
                                    content['image'], 120, 90)
                                if pixbuf is not None:
                                    data = utils.pixbuf_to_base64(pixbuf)
                                self.send_event(
                                    PICTURE_CMD, {
                                        "image": os.path.basename(
                                            content['image']),
                                        "data": data
                                    })
                data = json.dumps(self.reflection_data)
                self.send_event(SHARE_CMD, {"data": data})
        elif command == NEW_REFLECTION_CMD:
            self._reflect_window.add_new_reflection(payload)
        elif command == TITLE_CMD:
            obj_id = payload.get("obj_id")
            title = payload.get("title")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.update_title(obj_id, title)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == TAG_CMD:
            obj_id = payload.get("obj_id")
            data = payload.get("data")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.update_tags(obj_id, data)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == ACTIVITY_CMD:
            obj_id = payload.get("obj_id")
            bundle_id = payload.get("bundle_id")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.insert_activity(obj_id, bundle_id)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == STAR_CMD:
            obj_id = payload.get("obj_id")
            stars = payload.get("stars")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    self._reflect_window.update_stars(obj_id, int(stars))
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == COMMENT_CMD:
            found_the_object = False
            # Receive a comment and associated reflection ID
            obj_id = payload.get("obj_id")
            nick = payload.get("nick")
            color = payload.get("color")
            comment = payload.get("comment")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    if 'comments' not in item:
                        item['comments'] = []
                    data = {'nick': nick, 'comment': comment, 'color': color}
                    item['comments'].append(data)
                    self._reflect_window.insert_comment(obj_id, data)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == REFLECTION_CMD:
            found_the_object = False
            # Receive a reflection and associated reflection ID
            obj_id = payload.get("obj_id")
            reflection = payload.get("reflection")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    if '' not in item:
                        item['content'] = []
                    item['content'].append({'text': reflection})
                    self._reflect_window.insert_reflection(obj_id, reflection)
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == IMAGE_REFLECTION_CMD:
            found_the_object = False
            # Receive a picture reflection and associated reflection ID
            obj_id = payload.get("obj_id")
            basename = payload.get("basename")
            for item in self.reflection_data:
                if item['obj_id'] == obj_id:
                    found_the_object = True
                    if '' not in item:
                        item['content'] = []
                    item['content'].append(
                        {'image': os.path.join(self.tmp_path, basename)})
                    self._reflect_window.insert_picture(
                        obj_id, os.path.join(self.tmp_path, basename))
                    break
            if not found_the_object:
                logging.error('Could not find obj_id %s' % obj_id)
        elif command == PICTURE_CMD:
            # Receive a picture (MAYBE DISPLAY IT AS IT ARRIVES?)
            basename = payload.get("basename")
            data = payload.get("data")
            utils.base64_to_file(data, os.path.join(self.tmp_path, basename))
        elif command == SHARE_CMD:
            # Joiner needs to load reflection database.
            if not self.initiating:
                # Note that pictures should be received.
                self.reflection_data = payload
                self._reflect_window.load(self.reflection_data)
                self._waiting_for_reflections = False
                self.reset_cursor()
                if self._joined_alert is not None:
                    self.remove_alert(self._joined_alert)
                    self._joined_alert = None

    def send_event(self, command, data):
        ''' Send event through the tube. '''
        if hasattr(self, 'collab') and self.collab is not None:
            data["command"] = command
            self.collab.post(data)
Exemple #53
0
class Finance(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.set_title(_("Finance"))
        self.max_participants = 1

        # Initialize database.
        # data
        #   next_id
        #   transactions
        #     id, name, type, amount, date, category
        #   budgets
        #     category, period, amount, budget
        self.data = {
            'next_id': 0,
            'transactions': [],
            'budgets': {}
        }

        self.transaction_map = {}
        self.visible_transactions = []

        self.transaction_names = {}
        self.category_names = {}

        # self.create_test_data()

        # Initialize view period to the first of the month.
        self.period = MONTH
        self.period_start = self.get_this_period()

        # Create screens.
        self.register = registerscreen.RegisterScreen(self)
        self.chart = chartscreen.ChartScreen(self)
        self.budget = budgetscreen.BudgetScreen(self)

        self.build_toolbox()

        self.screenbox = Gtk.VBox()

        self.headerbox = self.build_header()
        self._active_panel = None

        # Add the summary data.

        self.startlabel = Gtk.Label()
        self.startamountlabel = Gtk.Label()
        self.creditslabel = Gtk.Label()
        self.debitslabel = Gtk.Label()
        self.balancelabel = Gtk.Label()
        self.balancelabel.props.margin_left = style.DEFAULT_SPACING
        self.balancelabel.props.margin_right = style.DEFAULT_SPACING

        font_size = int(style.FONT_SIZE * 1.25)
        font = Pango.FontDescription("Sans %d" % font_size)
        for label in (self.startlabel, self.startamountlabel,
                      self.creditslabel, self.debitslabel):
            label.modify_font(font)
            label.set_hexpand(True)
            label.set_halign(Gtk.Align.START)
            label.props.margin = style.DEFAULT_PADDING

        self.balance_evbox = Gtk.EventBox()
        self.balance_evbox.add(self.balancelabel)
        summarybox = Gtk.Grid()
        summarybox.attach(self.startlabel, 0, 0, 1, 1)
        summarybox.attach(self.startamountlabel, 0, 1, 1, 1)
        summarybox.attach(self.creditslabel, 1, 0, 1, 1)
        summarybox.attach(self.debitslabel, 1, 1, 1, 1)
        summarybox.attach(self.balance_evbox, 2, 0, 1, 2)
        self.balance_evbox.set_halign(Gtk.Align.END)

        summary_evbox = Gtk.EventBox()
        summary_evbox.add(summarybox)
        summary_evbox.modify_bg(Gtk.StateType.NORMAL,
                                style.Color('#666666').get_gdk_color())
        summary_evbox.set_size_request(-1, style.GRID_CELL_SIZE)

        vbox = Gtk.VBox()

        vbox.pack_start(self.headerbox, False, False, 0)
        vbox.pack_start(self.screenbox, True, True, 0)
        vbox.pack_start(summary_evbox, False, False, 0)

        # Start with the empty screen.
        self.empty_panel = emptypanel.create_empty_panel(
            'row-insert-credit',
            _('Add some credit or debit to get started!'),
            _('Add credit'), self.__empty_panel_btn_cb)

        self._set_internal_panel(self.empty_panel)

        # This has to happen last, because it calls the read_file
        # method when restoring from the Journal.
        self.set_canvas(vbox)

        self.show_all()
        self.show_header_controls()

    def build_toolbox(self):

        view_tool_group = None
        registerbtn = RadioToolButton()
        registerbtn.props.icon_name = 'view-list'
        registerbtn.props.label = _('Register')
        registerbtn.set_tooltip(_("Register"))
        registerbtn.props.group = view_tool_group
        view_tool_group = registerbtn
        registerbtn.props.accelerator = '<Ctrl>1'
        registerbtn.connect('clicked', self.register_cb)

        budgetbtn = RadioToolButton()
        budgetbtn.props.icon_name = 'budget'
        budgetbtn.props.label = _('Budget')
        budgetbtn.set_tooltip(_("Budget"))
        budgetbtn.props.group = view_tool_group
        budgetbtn.props.accelerator = '<Ctrl>2'
        budgetbtn.connect('clicked', self.budget_cb)

        chartbtn = RadioToolButton()
        chartbtn.props.icon_name = 'chart'
        chartbtn.props.label = _('Chart')
        chartbtn.set_tooltip(_("Chart"))
        chartbtn.props.group = view_tool_group
        chartbtn.props.accelerator = '<Ctrl>3'
        chartbtn.connect('clicked', self.chart_cb)

        helpbutton = self._create_help_button()
        helpbutton.show_all()

        self.toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        self.toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.toolbar_box.toolbar.insert(registerbtn, -1)
        self.toolbar_box.toolbar.insert(budgetbtn, -1)
        self.toolbar_box.toolbar.insert(chartbtn, -1)

        self.toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.toolbar_box.toolbar.insert(helpbutton, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.toolbar_box.toolbar.insert(separator, -1)

        self.toolbar_box.toolbar.insert(StopButton(self), -1)
        self.set_toolbar_box(self.toolbar_box)

        activity_button.page.insert(self._create_export_button(), -1)

        self.toolbar_box.show_all()

    def _create_export_button(self):
        # Add expoprt button
        export_data = ToolButton('save-as-data')

        export_data.props.tooltip = _('Export data')
        export_data.props.hide_tooltip_on_click = False
        export_data.palette_invoker.props.toggle_palette = True
        export_data.show()

        menu_box = PaletteMenuBox()
        export_data.props.palette.set_content(menu_box)

        menu_item = PaletteMenuItem(text_label=_('Export credits by day'))
        menu_item.connect('activate', self.__export_data_to_chart_cb,
                          'credit', DAY)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export debits by day'))
        menu_item.connect('activate', self.__export_data_to_chart_cb,
                          'debit', DAY)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export credits by month'))
        menu_item.connect('activate', self.__export_data_to_chart_cb,
                          'credit', MONTH)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export debits by month'))
        menu_item.connect('activate', self.__export_data_to_chart_cb,
                          'debit', MONTH)
        menu_box.append_item(menu_item)

        menu_box.show_all()
        return export_data

    def _create_help_button(self):
        helpitem = HelpButton()
        helpitem.add_section(_('Register'), icon='view-list')
        helpitem.add_paragraph(registerscreen.REGISTER_HELP)
        helpitem.add_section(_('Budget'), icon='budget')
        helpitem.add_paragraph(budgetscreen.BUDGET_HELP)
        helpitem.add_section(_('Chart'), icon='chart')
        helpitem.add_paragraph(chartscreen.CHART_HELP)
        return helpitem

    def build_header(self):
        # Add the header.
        self.periodlabel = Gtk.Label()
        self.periodlabel.set_padding(10, 0)
        self._period_label_item = Gtk.ToolItem()
        self._period_label_item.add(self.periodlabel)
        self.periodlabel.set_halign(Gtk.Align.END)
        self._period_label_item.set_size_request(style.GRID_CELL_SIZE * 5, -1)

        headerbox = Gtk.Toolbar()
        headerbox.modify_bg(Gtk.StateType.NORMAL,
                            style.Color('#424242').get_gdk_color())
        headerbox.set_size_request(-1, style.GRID_CELL_SIZE)

        # register buttons
        self.newcreditbtn = ToolButton('row-insert-credit')
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newcreditbtn.props.accelerator = '<Ctrl>A'
        self.newcreditbtn.connect('clicked', self.__newcredit_cb)

        self.newdebitbtn = ToolButton('row-insert-debit')
        self.newdebitbtn.set_tooltip(_("New Debit"))
        self.newdebitbtn.props.accelerator = '<Ctrl>D'
        self.newdebitbtn.connect('clicked', self.__newdebit_cb)

        self.eraseitembtn = ToolButton('basket')
        self.eraseitembtn.set_tooltip(_("Erase Transaction"))
        self.eraseitembtn.props.accelerator = '<Ctrl>E'
        self.eraseitembtn.connect('clicked', self.__eraseitem_cb)

        headerbox.insert(self.newcreditbtn, -1)
        headerbox.insert(self.newdebitbtn, -1)
        headerbox.insert(self.eraseitembtn, -1)

        self.header_separator_visible = Gtk.SeparatorToolItem()
        headerbox.insert(self.header_separator_visible, -1)

        self.export_image = ToolButton('save-as-image')
        self.export_image.set_tooltip(_("Save as Image"))
        self.export_image.connect('clicked', self.__save_image_cb)
        headerbox.insert(self.export_image, -1)

        self._header_separator = Gtk.SeparatorToolItem()
        self._header_separator.props.draw = False
        self._header_separator.set_expand(True)
        headerbox.insert(self._header_separator, -1)

        # period controls
        self.thisperiodbtn = ToolButton('go-down')
        self.thisperiodbtn.props.accelerator = '<Ctrl>Down'
        self.thisperiodbtn.connect('clicked', self.thisperiod_cb)

        self.prevperiodbtn = ToolButton('go-previous-paired')
        self.prevperiodbtn.props.accelerator = '<Ctrl>Left'
        self.prevperiodbtn.connect('clicked', self.prevperiod_cb)

        self.nextperiodbtn = ToolButton('go-next-paired')
        self.nextperiodbtn.props.accelerator = '<Ctrl>Right'
        self.nextperiodbtn.connect('clicked', self.nextperiod_cb)

        period_options = {DAY: _('Day'), WEEK: _('Week'), MONTH: _('Month'),
                          YEAR: _('Year'), FOREVER: _('Forever')}
        periodcombo = FilterToolItem('calendar', MONTH, period_options,
                                     _('Select period'))

        periodcombo.connect('changed', self.__period_changed_cb)

        headerbox.insert(periodcombo, -1)
        headerbox.insert(self.prevperiodbtn, -1)
        headerbox.insert(self.nextperiodbtn, -1)
        headerbox.insert(self.thisperiodbtn, -1)

        headerbox.insert(self._period_label_item, -1)
        return headerbox

    def show_header_controls(self):
        for child in self.headerbox.get_children():
            child.show()
            if self._active_panel in (self.register, self.empty_panel):
                if child in (self.header_separator_visible,
                             self.export_image):
                    child.hide()
            elif self._active_panel == self.budget:
                if child in (self.newcreditbtn, self.newdebitbtn,
                             self.eraseitembtn, self.header_separator_visible,
                             self.export_image):
                    child.hide()
            elif self._active_panel == self.chart:
                # Use NOT here
                if child not in (self.newcreditbtn, self.newdebitbtn,
                                 self.header_separator_visible,
                                 self.export_image):
                    child.hide()

    def register_cb(self, widget):
        self._set_internal_panel(self.register)
        self.show_header_controls()
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newdebitbtn.set_tooltip(_("New Debit"))

    def budget_cb(self, widget):
        self._set_internal_panel(self.budget)
        self.show_header_controls()

    def chart_cb(self, widget):
        self._set_internal_panel(self.chart)
        self.show_header_controls()
        self.newcreditbtn.set_tooltip(_("Show Credits"))
        self.newdebitbtn.set_tooltip(_("Show Debits"))

    def _set_internal_panel(self, widget):
        if self.screenbox.get_children():
            self.screenbox.remove(self.screenbox.get_children()[0])
        self.screenbox.pack_start(widget, True, True, 0)
        widget.show_all()
        self._active_panel = widget
        self.build_screen()

    def build_screen(self):
        self.build_visible_transactions()

        if hasattr(self._active_panel, 'build'):
            self._active_panel.build()

        self.update_header()
        self.update_summary()
        self.update_toolbar()

    def __empty_panel_btn_cb(self, button):
        self._set_internal_panel(self.register)
        self.register.new_credit()

    def __newcredit_cb(self, widget):
        if self._active_panel == self.chart:
            # in the case of chart, select the graphic
            self.chart.set_mode(self.chart.CHART_CREDIT)
            return

        # this check is used when the emptypanle is visible
        if self._active_panel != self.register:
            self._set_internal_panel(self.register)
        self.register.new_credit()

    def __newdebit_cb(self, widget):
        if self._active_panel == self.chart:
            # in the case of chart, select the graphic
            self.chart.set_mode(self.chart.CHART_DEBIT)
            return

        # this check is used when the emptypanle is visible
        if self._active_panel != self.register:
            self._set_internal_panel(self.register)
        self.register.new_debit()

    def __eraseitem_cb(self, widget):
        self.register.erase_item()
        self.build_screen()

    def update_header(self):
        if self.period == DAY:
            # TRANS: representation of the "Day" period
            text = self.period_start.strftime(_("%B %d, %Y"))

        elif self.period == WEEK:
            # TRANS: representation of the "Week of" period
            text = _('Week of') + self.period_start.strftime(_(" %B %d, %Y"))

        elif self.period == MONTH:
            # TRANS: representation of the "Month" period
            text = self.period_start.strftime(_("%B, %Y"))

        elif self.period == YEAR:
            # TRANS: representation of the "Year" period
            text = self.period_start.strftime(_("%Y"))

        elif self.period == FOREVER:
            text = _('Forever')

        self.periodlabel.set_markup(
            "<span size='xx-large' color='white'><b>" + text + "</b></span>")

    def update_summary(self):
        # Calculate starting balance.
        start = 0.0
        for t in self.data['transactions']:
            d = t['date']
            if d < self.period_start.toordinal():
                if t['type'] == 'credit':
                    start += t['amount']
                else:
                    start -= t['amount']

        # Calculate totals for this period.
        credit_count = 0
        credit_total = 0.0
        debit_count = 0
        debit_total = 0.0
        total = start
        for t in self.visible_transactions:
            if t['type'] == 'credit':
                credit_count += 1
                credit_total += t['amount']
                total += t['amount']
            else:
                debit_count += 1
                debit_total += t['amount']
                total -= t['amount']

        # Update Balance.
        if total >= 0.0:
            balancecolor = colors.CREDIT_COLOR
        else:
            balancecolor = colors.DEBIT_COLOR
        balance = \
            "<span size='xx-large' foreground='white'><b>%s %s</b></span>" % \
            (_('Balance: '), locale.currency(total))
        self.balancelabel.set_markup(balance)

        self.balance_evbox.modify_bg(
            Gtk.StateType.NORMAL, style.Color(balancecolor).get_gdk_color())

        self.startlabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            _('Starting Balance:'))
        self.startamountlabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            locale.currency(start))

        self.creditslabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            (_('%s in %d credits') % (locale.currency(credit_total),
                                      credit_count)))
        self.debitslabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            (_('%s in %d debits') % (locale.currency(debit_total),
                                     debit_count)))

    def update_toolbar(self):
        # Disable the navigation when Forever is selected.
        next_prev = self.period != FOREVER
        self.prevperiodbtn.set_sensitive(next_prev)
        self.thisperiodbtn.set_sensitive(next_prev)
        self.nextperiodbtn.set_sensitive(next_prev)

        # This is a HACK to translate the string properly
        # http://bugs.sugarlabs.org/ticket/3190
        if self.period == MONTH:
            text_previous_period = _('Previous Month')
            text_this_period = _('This Month')
            text_next_period = _('Next Month')
        elif self.period == WEEK:
            text_previous_period = _('Previous Week')
            text_this_period = _('This Week')
            text_next_period = _('Next Week')
        elif self.period == DAY:
            text_previous_period = _('Previous Day')
            text_this_period = _('This Day')
            text_next_period = _('Next Day')
        elif self.period == YEAR:
            text_previous_period = _('Previous Year')
            text_this_period = _('This Year')
            text_next_period = _('Next Year')

        # Update the label self.period to reflect the period.
        self.prevperiodbtn.set_tooltip(text_previous_period)
        self.thisperiodbtn.set_tooltip(text_this_period)
        self.nextperiodbtn.set_tooltip(text_next_period)

    def get_this_period(self):
        today = datetime.date.today()

        if self.period == DAY:
            return today

        elif self.period == WEEK:
            while today.weekday() != 0:
                today -= datetime.timedelta(days=1)
            return today

        elif self.period == MONTH:
            return datetime.date(today.year, today.month, 1)

        elif self.period == YEAR:
            return datetime.date(today.year, 1, 1)

        elif self.period == FOREVER:
            return datetime.date(1900, 1, 1)

    def get_next_period(self, start):
        if self.period == DAY:
            return start + datetime.timedelta(days=1)

        elif self.period == WEEK:
            return start + datetime.timedelta(days=7)

        elif self.period == MONTH:
            if start.month == 12:
                return datetime.date(start.year + 1, 1, 1)
            else:
                return datetime.date(start.year, start.month + 1, 1)

        elif self.period == YEAR:
            return datetime.date(start.year + 1, 1, 1)

    def get_prev_period(self, start):
        if self.period == DAY:
            return start - datetime.timedelta(days=1)

        elif self.period == WEEK:
            return start - datetime.timedelta(days=7)

        elif self.period == MONTH:
            if start.month == 1:
                return datetime.date(start.year - 1, 12, 1)
            else:
                return datetime.date(start.year, start.month - 1, 1)

        elif self.period == YEAR:
            return datetime.date(start.year - 1, 1, 1)

    def thisperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_this_period()
            self.build_screen()

    def nextperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_next_period(self.period_start)
            self.build_screen()

    def prevperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_prev_period(self.period_start)
            self.build_screen()

    def __period_changed_cb(self, widget, value):
        self.period = int(value)
        self.update_toolbar()

        # Jump to 'this period'.
        self.period_start = self.get_this_period()
        self.build_screen()

    def build_visible_transactions(self):
        if self.period == FOREVER:
            self.visible_transactions = self.data['transactions']

        else:
            period_start_ord = self.period_start.toordinal()
            period_end_ord = self.get_next_period(
                self.period_start).toordinal()

            self.visible_transactions = []
            for t in self.data['transactions']:
                d = t['date']
                if d >= period_start_ord and d < period_end_ord:
                    self.visible_transactions.append(t)

        self.visible_transactions.sort(lambda a, b: a['date'] - b['date'])

    def build_transaction_map(self):
        self.transaction_map = {}
        for t in self.data['transactions']:
            self.transaction_map[t['id']] = t

    def create_transaction(self, name='', type='debit', amount=0,
                           category='', date=datetime.date.today()):
        id = self.data['next_id']
        self.data['next_id'] += 1

        t = {
            'id': id,
            'name': name,
            'type': type,
            'amount': amount,
            'date': date.toordinal(),
            'category': category
        }
        self.data['transactions'].append(t)
        self.transaction_map[id] = t

        self.build_visible_transactions()

        return id

    def destroy_transaction(self, id):
        t = self.transaction_map[id]
        self.data['transactions'].remove(t)
        del self.transaction_map[id]

    def build_names(self):
        self.transaction_names = {}
        self.category_names = {}
        for t in self.data['transactions']:
            self.transaction_names[t['name']] = 1
            self.category_names[t['category']] = 1

    def create_test_data(self):
        cur_date = datetime.date.today()
        cur_date = datetime.date(cur_date.year, cur_date.month, 1)
        self.create_transaction('Initial Balance', type='credit', amount=632,
                                category='Initial Balance', date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Fix Car', amount=75.84,
                                category='Transportation', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Adopt Cat', amount=100, category='Pets',
                                date=cur_date)
        self.create_transaction('New Coat', amount=25.53, category='Clothing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent', amount=500, category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe', amount=5.20, category='Food',
                                date=cur_date)
        self.create_transaction('Groceries', amount=50.92, category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food', amount=5.40, category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck', type='credit', amount=700,
                                category='Paycheck', date=cur_date)
        self.create_transaction('Gas', amount=21.20, category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys', amount=10.95, category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister', amount=23.20,
                                category='Gifts', date=cur_date)

        self.build_transaction_map()
        self.build_names()

    def read_file(self, file_path):
        if self.metadata['mime_type'] != 'text/plain':
            return

        fd = open(file_path, 'r')
        try:
            text = fd.read()
            self.data = json.loads(text)
        finally:
            fd.close()

        if self.data['transactions']:
            self._set_internal_panel(self.register)
        self.show_header_controls()

        self.build_transaction_map()
        self.build_names()
        self.build_screen()

    def write_file(self, file_path):
        if not self.metadata['mime_type']:
            self.metadata['mime_type'] = 'text/plain'

        fd = open(file_path, 'w')
        try:
            text = json.dumps(self.data)
            fd.write(text)
        finally:
            fd.close()

    def __save_image_cb(self, widget):
        image_file = tempfile.NamedTemporaryFile(mode='w+b', suffix='.png')
        journal_entry = datastore.create()
        journal_entry.metadata['title'] = self.chart.title
        journal_entry.metadata['keep'] = '0'
        journal_entry.metadata['mime_type'] = 'image/png'

        # generate the image
        self.chart.generate_image(image_file.file, 800, 600)
        image_file.file.close()
        journal_entry.file_path = image_file.name

        # generate the preview
        preview_str = StringIO.StringIO()
        self.chart.generate_image(preview_str, activity.PREVIEW_SIZE[0],
                                  activity.PREVIEW_SIZE[1])
        journal_entry.metadata['preview'] = dbus.ByteArray(
            preview_str.getvalue())

        logging.error('Create %s image file', image_file.name)
        datastore.write(journal_entry)
        self._show_journal_alert(
            _('Chart created'), _('Open in the Journal'),
            journal_entry.object_id)

    def _show_journal_alert(self, title, msg, object_id):
        open_alert = Alert()
        open_alert.props.title = title
        open_alert.props.msg = msg
        open_icon = Icon(icon_name='zoom-activity')
        open_alert.add_button(Gtk.ResponseType.APPLY,
                              _('Show in Journal'), open_icon)
        open_icon.show()
        ok_icon = Icon(icon_name='dialog-ok')
        open_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon)
        ok_icon.show()
        # Remove other alerts
        for alert in self._alerts:
            self.remove_alert(alert)

        self.add_alert(open_alert)
        open_alert.connect('response', self.__open_response_cb, object_id)
        open_alert.show()

    def __open_response_cb(self, alert, response_id, object_id):
        if response_id is Gtk.ResponseType.APPLY:
            activity.show_object_in_journal(object_id)
        self.remove_alert(alert)

    def __export_data_to_chart_cb(self, widget, type_movement, period):
        """
        type_movement = 'debit' or 'credit'
        period
            DAY = 0
            MONTH = 2
        """
        logging.debug('export data %s %s', type_movement, period)

        chart_params = {}
        axis = {'tickFont': 'Sans', 'labelFont': 'Sans', 'labelFontSize': 14,
                'labelColor': '#666666', 'lineColor': '#b3b3b3',
                'tickColor': '#000000', 'tickFontSize': 12}
        chart_params['font_options'] = {
            'titleFont': 'Sans', 'titleFontSize': 12, 'titleColor': '#000000',
            'axis': axis}

        if type_movement == 'credit':
            what_filter = 'Credits'
        elif type_movement == 'debit':
            what_filter = 'Debits'
        else:
            logging.error('ERROR type_movement should be credit or debit')

        if period == DAY:
            when = 'day'
        elif period == MONTH:
            when = 'month'
        else:
            logging.error('ERROR period should be DAY or MONTH')

        title = _('%s by %s') % (what_filter, when)
        chart_params['title'] = title
        chart_params['x_label'] = ''
        chart_params['y_label'] = ''
        chart_params['current_chart.type'] = 1
        xo_color = profile.get_color()
        chart_params['chart_line_color'] = xo_color.get_stroke_color()
        chart_params['chart_color'] = xo_color.get_fill_color()

        """
        'chart_data': [
            ['hello', 200.0],
            ['mrch', 100.0]],
        """
        transactions = self.data['transactions']

        groups = {}
        for transaction in transactions:
            if transaction['type'] == type_movement:
                date = transaction['date']
                if period == DAY:
                    group = date
                elif period == MONTH:
                    d = datetime.date.fromordinal(date)
                    group = datetime.date(d.year, d.month, 1).toordinal()
                if group in groups.keys():
                    groups[group] = groups[group] + transaction['amount']
                else:
                    groups[group] = transaction['amount']

        data = []
        for group in sorted(groups.keys()):
            if period == DAY:
                label = datetime.date.fromordinal(group).isoformat()
            elif period == MONTH:
                d = datetime.date.fromordinal(group)
                label = '%s-%s' % (d.year, d.month)

            data.append([label, groups[group]])

        chart_params['chart_data'] = data

        logging.debug('chart_data %s', chart_params)

        # save to the journal
        data_file = tempfile.NamedTemporaryFile(mode='w+b', suffix='.json')
        journal_entry = datastore.create()
        journal_entry.metadata['title'] = title
        journal_entry.metadata['keep'] = '0'
        journal_entry.metadata['mime_type'] = 'application/x-chart-activity'
        journal_entry.metadata['activity'] = 'org.sugarlabs.SimpleGraph'

        json.dump(chart_params, data_file.file)
        data_file.file.close()
        journal_entry.file_path = data_file.name

        logging.debug('Create %s data file', data_file.name)
        datastore.write(journal_entry)
        self._show_journal_alert(
            _('Exported data'), _('Open in the Journal'),
            journal_entry.object_id)
class EditToolbar(Gtk.Toolbar):

    def __init__(self, pc, toolbar_box):

        GObject.GObject.__init__(self)

        self._abiword_canvas = pc.abiword_canvas

        copy = CopyButton()
        copy.props.accelerator = '<Ctrl>C'
        copy.connect('clicked', lambda button: pc.abiword_canvas.copy())
        self.insert(copy, -1)
        copy.show()

        paste = PasteButton()
        paste.props.accelerator = '<Ctrl>V'
        paste.connect('clicked', self.__paste_button_cb)
        self.insert(paste, -1)
        paste.show()

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        undo = UndoButton(sensitive=False)
        undo.connect('clicked', lambda button: pc.abiword_canvas.undo())
        pc.abiword_canvas.connect("can-undo", lambda abi, can_undo:
                                  undo.set_sensitive(can_undo))
        self.insert(undo, -1)
        undo.show()

        redo = RedoButton(sensitive=False)
        redo.connect('clicked', lambda button: pc.abiword_canvas.redo())
        pc.abiword_canvas.connect("can-redo", lambda abi, can_redo:
                                  redo.set_sensitive(can_redo))
        self.insert(redo, -1)
        redo.show()

        pc.abiword_canvas.connect('text-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('image-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('selection-cleared', lambda abi, b:
                                  copy.set_sensitive(False))

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        search_label = Gtk.Label(label=_("Search") + ": ")
        search_label.show()
        search_item_page_label = Gtk.ToolItem()
        search_item_page_label.add(search_label)
        self.insert(search_item_page_label, -1)
        search_item_page_label.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()
        self._add_widget(self._search_entry, expand=True)

        self._findprev = ToolButton('go-previous-paired')
        self._findprev.set_tooltip(_('Find previous'))
        self.insert(self._findprev, -1)
        self._findprev.show()
        self._findprev.connect('clicked', self._findprev_cb)

        self._findnext = ToolButton('go-next-paired')
        self._findnext.set_tooltip(_('Find next'))
        self.insert(self._findnext, -1)
        self._findnext.show()
        self._findnext.connect('clicked', self._findnext_cb)

        # set the initial state of the search controls
        # note: we won't simple call self._search_entry_changed_cb
        # here, as that will call into the abiword_canvas, which
        # is not mapped on screen here, causing the set_find_string
        # call to fail
        self._findprev.set_sensitive(False)
        self._findnext.set_sensitive(False)

    def __paste_button_cb(self, button):
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

        if clipboard.wait_is_image_available():
            pixbuf_sel = clipboard.wait_for_image()
            activity = self._abiword_canvas.get_toplevel()
            temp_path = os.path.join(activity.get_activity_root(), 'instance')
            if not os.path.exists(temp_path):
                os.makedirs(temp_path)
            fd, file_path = tempfile.mkstemp(dir=temp_path, suffix='.png')
            os.close(fd)
            logging.error('tempfile is %s' % file_path)
            success, data = pixbuf_sel.save_to_bufferv('png', [], [])
            if success:
                px_file = open(file_path, 'w')
                px_file.write(data)
                px_file.close()
                self._abiword_canvas.insert_image(file_path, False)

        elif clipboard.wait_is_uris_available():
            selection = clipboard.wait_for_uris()
            if selection is not None:
                for uri in selection:
                    self._abiword_canvas.insert_image(urlparse(uri).path,
                                                      False)
        else:
            self._abiword_canvas.paste()

    def _search_entry_activated_cb(self, entry):
        logger.debug('_search_entry_activated_cb')
        if not self._search_entry.props.text:
            return

        # find the next entry
        self._abiword_canvas.find_next(False)

    def _search_entry_changed_cb(self, entry):
        logger.debug('_search_entry_changed_cb search for \'%s\'',
                     self._search_entry.props.text)

        if not self._search_entry.props.text:
            self._search_entry.activate()
            # set the button contexts
            self._findprev.set_sensitive(False)
            self._findnext.set_sensitive(False)
            return

        self._abiword_canvas.set_find_string(self._search_entry.props.text)

        # set the button contexts
        self._findprev.set_sensitive(True)
        self._findnext.set_sensitive(True)

        # immediately start seaching
        self._abiword_canvas.find_next(True)

    def _findprev_cb(self, button):
        logger.debug('_findprev_cb')
        if self._search_entry.props.text:
            self._abiword_canvas.find_prev()
        else:
            logger.debug('nothing to search for!')

    def _findnext_cb(self, button):
        logger.debug('_findnext_cb')
        if self._search_entry.props.text:
            self._abiword_canvas.find_next(False)
        else:
            logger.debug('nothing to search for!')

    # bad foddex! this function was copied from sugar's activity.py
    def _add_widget(self, widget, expand=False):
        tool_item = Gtk.ToolItem()
        tool_item.set_expand(expand)

        tool_item.add(widget)
        widget.show()

        self.insert(tool_item, -1)
        tool_item.show()
class WriteBooksActivity(activity.Activity):

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        self._book_model = BookModel()
        self._actual_page = 1

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1

        # get the language configured by the user
        # will be used to translate the names of the media files
        locale = os.environ.get('LANG', '')
        language_location = locale.split('.', 1)[0].lower()
        self._language = language_location.split('_')[0]
        if self._language == 'en':
            # we don't need translate the file names if langauage is 'en'
            self._language = None
        self._translations = None
        if self._language is not None:
            # read the translations file if available
            dict_path = os.path.join(activity.get_bundle_path(), 'data',
                                     "%s_dict.csv" % self._language)
            logging.debug('Looking for media translation dictionary %s',
                          dict_path)
            if os.path.exists(dict_path):
                logging.debug('Loading translations')
                self._translations = {}
                with open(dict_path) as dict_file:
                    for line in dict_file:
                        words = line.split(',')
                        self._translations[words[0]] = words[1].strip()

        toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)

        self._edit_toolbar = EditToolbar()
        edit_toolbar_button = ToolbarButton(
            page=self._edit_toolbar, icon_name='toolbar-edit')
        toolbar_box.toolbar.insert(edit_toolbar_button, 1)

        set_background_button = ToolButton('set-background')
        set_background_button.set_tooltip(_('Set the background'))
        set_background_button.connect('clicked',
                                      self.__set_background_clicked_cb)
        toolbar_box.toolbar.insert(set_background_button, -1)

        insert_picture_button = ToolButton('insert-picture')
        insert_picture_button.set_tooltip(_('Add a picture'))
        insert_picture_button.connect('clicked',
                                      self.__add_image_clicked_cb)
        toolbar_box.toolbar.insert(insert_picture_button, -1)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self._duplicate_page_button = ToolButton()
        icon = Icon(icon_name='edit-duplicate', xo_color=profile.get_color())
        self._duplicate_page_button.set_icon_widget(icon)

        self._duplicate_page_button.set_tooltip(_('Duplicate page'))
        self._duplicate_page_button.connect(
            'clicked', self.__duplicate_page_clicked_cb)
        toolbar_box.toolbar.insert(self._duplicate_page_button, -1)

        self._add_page_button = ToolButton('list-add')
        self._add_page_button.set_tooltip(_('Add a page'))
        self._add_page_button.connect('clicked', self.__add_page_clicked_cb)
        toolbar_box.toolbar.insert(self._add_page_button, -1)

        self._remove_button = ToolButton('edit-delete')
        self._remove_button.set_tooltip(_('Remove an image or page'))
        self._remove_button.connect('clicked', self.__remove_clicked_cb)
        toolbar_box.toolbar.insert(self._remove_button, -1)

        self._prev_page_button = ToolButton('go-previous-paired')
        self._prev_page_button.set_tooltip(_('Previous page'))
        self._prev_page_button.connect('clicked', self.__prev_page_clicked_cb)
        toolbar_box.toolbar.insert(self._prev_page_button, -1)

        self._next_page_button = ToolButton('go-next-paired')
        self._next_page_button.set_tooltip(_('Next page'))
        self._next_page_button.connect('clicked', self.__next_page_clicked_cb)
        toolbar_box.toolbar.insert(self._next_page_button, -1)

        self._view_list_button = ToggleToolButton('view-list')
        self._view_list_button.set_tooltip(_('View pages'))
        self._view_list_button.connect('toggled', self.__view_list_toggled_cb)
        toolbar_box.toolbar.insert(self._view_list_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)

        # add export buttons
        activity_toolbar = activity_button.props.page
        epub_button = ToolButton('save-as-epub')
        epub_button.set_tooltip(_('Save as EPUB book'))
        epub_button.connect('clicked', self.__save_ebook_clicked_cb)
        activity_toolbar.insert(epub_button, -1)
        epub_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        edition_canvas = self.create_edition_canvas()

        hbox = Gtk.HBox()
        self._preview_panel = PreviewPanel(self._book_model.get_pages())
        self._preview_panel.connect('page-activated', self.__page_activated_cb)
        self._preview_panel.connect('page-moved', self.__page_moved_cb)
        hbox.pack_start(self._preview_panel, False, False, 0)
        hbox.pack_start(edition_canvas, True, True, 0)

        self.set_canvas(hbox)
        self.prepare_edit_toolbar()
        self._update_page_buttons()

        self.show_all()
        self._preview_panel.hide()

    def create_edition_canvas(self):
        self._image_canvas = ImageCanvas()
        self._image_canvas.connect('images-modified',
                                   self.__images_modified_cb)
        self._image_canvas.set_halign(Gtk.Align.CENTER)
        self._image_canvas.set_valign(Gtk.Align.CENTER)
        self._image_canvas.set_vexpand(True)

        self._text_editor = TextEditor()
        self._text_changed_signal_id = self._text_editor.connect(
            'changed', self.__text_changed_cb)

        self._page_counter_label = Gtk.Label('1 / 1')
        font_desc = Pango.font_description_from_string('12')
        self._page_counter_label.modify_font(font_desc)
        self._page_counter_label.set_halign(Gtk.Align.END)
        self._page_counter_label.set_valign(Gtk.Align.END)
        self._page_counter_label.set_margin_right(style.DEFAULT_PADDING)
        self._page_counter_label.set_margin_top(style.DEFAULT_PADDING)

        background = Gtk.EventBox()
        rgba = Gdk.RGBA()
        rgba.red, rgba.green, rgba.blue, rgba.alpha = 1., 1., 1., 1.
        background.override_background_color(Gtk.StateFlags.NORMAL, rgba)

        self._scrolled_window = Gtk.ScrolledWindow()
        self._scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        self._scrolled_window.set_size_request(
            Gdk.Screen.width() - style.GRID_CELL_SIZE * 2,
            style.GRID_CELL_SIZE * 2)
        self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                         Gtk.PolicyType.AUTOMATIC)
        self._scrolled_window.add(self._text_editor)
        self._scrolled_window.set_margin_left(style.GRID_CELL_SIZE)
        self._scrolled_window.set_margin_right(style.GRID_CELL_SIZE)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        box.pack_start(self._page_counter_label, False, False, 0)
        box.pack_start(self._image_canvas, True, True, 0)
        box.pack_start(self._scrolled_window, False, False,
                       style.DEFAULT_PADDING)
        background.add(box)
        background.show_all()
        background.connect('size_allocate', self.__background_size_allocate_cb)
        return background

    def __background_size_allocate_cb(self, widget, allocation):
        height = allocation.height / 4 * 3
        width = height / 3 * 4
        logging.debug('size allocate %s x %s', width, height)
        self._image_canvas.set_size_request(width, height)
        widget.check_resize()

    def __view_list_toggled_cb(self, button):
        if button.get_active():
            self._preview_panel.update_model(self._book_model.get_pages())
            self._preview_panel.show()
            self._image_canvas.set_editable(False)
            self._text_editor.set_editable(False)
            self._scrolled_window.set_size_request(
                (Gdk.Screen.width() * 3 / 4) - style.GRID_CELL_SIZE * 2,
                style.GRID_CELL_SIZE * 2)
        else:
            self._preview_panel.hide()
            self._image_canvas.set_editable(True)
            self._text_editor.set_editable(True)
            self._scrolled_window.set_size_request(
                Gdk.Screen.width() - style.GRID_CELL_SIZE * 2,
                style.GRID_CELL_SIZE * 2)

    def write_file(self, file_path):
        self._book_model.write(file_path)
        self.metadata['mime_type'] = 'application/x-writebooks-activity'

    def read_file(self, file_path):
        self._book_model.read(file_path)
        self._update_page_buttons()

    def prepare_edit_toolbar(self):
        self._edit_toolbar.copy.connect('clicked', self.__copy_clicked_cb)
        self._edit_toolbar.paste.connect('clicked', self.__paste_clicked_cb)
        self._edit_toolbar.undo.connect('clicked', self.__undo_clicked_cb)
        self._edit_toolbar.redo.connect('clicked', self.__redo_clicked_cb)

    def __copy_clicked_cb(self, button):
        if self._text_editor.get_buffer().get_has_selection():
            self._text_editor.get_buffer().copy_clipboard(
                Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD))
        elif self._image_canvas.is_image_active():
            # if not text is selected
            # and a image is selected, copy as pixbuf
            clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
            pxb = self._image_canvas.create_pixbuf_with_active_image()
            clipboard.set_image(pxb)

    def __paste_clicked_cb(self, button):
        self._text_editor.get_buffer().paste_clipboard(
            Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD), None, True)

    def __undo_clicked_cb(self, button):
        self._text_editor.get_buffer().undo()

    def __redo_clicked_cb(self, button):
        self._text_editor.get_buffer().redo()

    def __set_background_clicked_cb(self, button):
        categories = {
            _('Indoors'): [os.path.join(SCRATCH_BACKGROUNDS_PATH, 'Indoors')],
            _('Nature'): [os.path.join(SCRATCH_BACKGROUNDS_PATH, 'Nature')],
            _('Outdoors'):
                [os.path.join(SCRATCH_BACKGROUNDS_PATH, 'Outdoors')],
            _('Sports'): [os.path.join(SCRATCH_BACKGROUNDS_PATH, 'Sports')]}

        chooser = ImageFileChooser(image_type='backgrounds',
                                   title=_('Select a background'),
                                   categories=categories,
                                   language=self._language,
                                   translations=self._translations,
                                   parent=self.get_window())
        chooser.connect('response', self.__chooser_response_cb,
                        self._change_background)
        self.set_sensitive(False)
        chooser.show()

    def __chooser_response_cb(self, chooser, response_id, operation_function):
        self.set_sensitive(True)
        if response_id == Gtk.ResponseType.ACCEPT:
            logging.error('selected %s', chooser.get_selected_object_id())
            file_path = chooser.get_selected_object_id()
            tempfile_name = \
                os.path.join(self.get_activity_root(),
                             'instance', 'tmp%i' % time.time())
            os.link(file_path, tempfile_name)
            operation_function(tempfile_name)
        chooser.destroy()
        del chooser
        if response_id == Gtk.ResponseType.REJECT:
            try:
                chooser = ObjectChooser(self, what_filter='Image',
                                        filter_type=FILTER_TYPE_GENERIC_MIME,
                                        show_preview=True)
            except:
                # for compatibility with older versions
                chooser = ObjectChooser(self, what_filter='Image')

            try:
                result = chooser.run()
                if result == Gtk.ResponseType.ACCEPT:
                    logging.error('ObjectChooser: %r' %
                                  chooser.get_selected_object())
                    jobject = chooser.get_selected_object()
                    if jobject and jobject.file_path:
                        logging.error("imagen seleccionada: %s",
                                      jobject.file_path)
                        tempfile_name = \
                            os.path.join(self.get_activity_root(),
                                         'instance', 'tmp%i' % time.time())
                        os.link(jobject.file_path, tempfile_name)
                        operation_function(tempfile_name)
            finally:
                chooser.destroy()
                del chooser

    def _change_background(self, file_name):
        self._book_model.set_page_background(self._actual_page, file_name)
        self._update_page_view()

    def __add_image_clicked_cb(self, button):
        categories = {
            _('Animals'): [os.path.join(SCRATCH_COSTUMES_PATH, 'Animals'),
                           os.path.join(TUXPAINT_STAMPS_PATH, 'animals')],
            _('Fantasy'): [os.path.join(SCRATCH_COSTUMES_PATH, 'Fantasy'),
                           os.path.join(TUXPAINT_STAMPS_PATH, 'cartoon')],
            _('Letters'): [os.path.join(SCRATCH_COSTUMES_PATH, 'Letters')],
            _('People'): [os.path.join(SCRATCH_COSTUMES_PATH, 'People'),
                          os.path.join(TUXPAINT_STAMPS_PATH, 'people')],
            _('Things'): [os.path.join(SCRATCH_COSTUMES_PATH, 'Things'),
                          os.path.join(TUXPAINT_STAMPS_PATH, 'clothes'),
                          os.path.join(TUXPAINT_STAMPS_PATH, 'hobbies'),
                          os.path.join(TUXPAINT_STAMPS_PATH, 'medical'),
                          os.path.join(TUXPAINT_STAMPS_PATH, 'household'),
                          os.path.join(TUXPAINT_STAMPS_PATH, 'food')],
            _('Transportation'): [
                os.path.join(SCRATCH_COSTUMES_PATH, 'Transportation'),
                os.path.join(TUXPAINT_STAMPS_PATH, 'vehicles')]}

        chooser = ImageFileChooser(image_type='actors',
                                   title=_('Select an image to add'),
                                   categories=categories,
                                   language=self._language,
                                   translations=self._translations,
                                   parent=self.get_window())
        chooser.connect('response', self.__chooser_response_cb,
                        self._add_image)
        self.set_sensitive(False)
        chooser.show()

    def _add_image(self, file_name):
        logging.error('Add image %s', file_name)
        self._book_model.add_image(self._actual_page, file_name)
        self._update_page_view()

    def __remove_clicked_cb(self, file_name):
        if self._image_canvas.is_image_active():
            alert = ConfirmationAlert()
            alert.props.title = _('Do you want remove the selected image?')
            # alert.props.msg = _('')
            alert.connect('response', self.__confirm_remove_image_cb)
            self.add_alert(alert)
        else:
            if len(self._book_model.get_pages()) > 1:
                alert = ConfirmationAlert()
                alert.props.title = _('Do you want remove the page?')
                # alert.props.msg = _('')
                alert.connect('response', self.__confirm_remove_page_cb)
                self.add_alert(alert)

    def __confirm_remove_image_cb(self, alert, response_id):
        # Callback for conf alert
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            self._image_canvas.remove_active_image()

    def __confirm_remove_page_cb(self, alert, response_id):
        # Callback for conf alert
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.OK:
            if self._book_model.remove_page(self._actual_page):
                if self._actual_page > len(self._book_model.get_pages()):
                    self._actual_page -= 1
                self._update_page_buttons()
                self._preview_panel.update_model(self._book_model.get_pages())

    def __images_modified_cb(self, canvas, images_views):
        self._book_model.update_images(self._actual_page, images_views)

    def _update_page_buttons(self):
        cant_pages = len(self._book_model.get_pages())
        self._page_counter_label.set_text('%d / %d' %
                                          (self._actual_page, cant_pages))
        self._prev_page_button.set_sensitive(self._actual_page > 1)
        self._next_page_button.set_sensitive(self._actual_page < cant_pages)
        self._update_page_view()

    def _update_page_view(self):
        page_model = self._book_model.get_page_model(self._actual_page)
        self._image_canvas.set_background(page_model.background_path)
        self._image_canvas.set_images(page_model.images)
        self._text_editor.disconnect(self._text_changed_signal_id)
        self._text_editor.set_text(page_model.text)
        self._text_changed_signal_id = self._text_editor.connect(
            'changed', self.__text_changed_cb)

    def __add_page_clicked_cb(self, button):
        self._book_model.add_page()
        self._actual_page = len(self._book_model.get_pages())
        self._update_page_buttons()
        self._preview_panel.update_model(self._book_model.get_pages())

    def __duplicate_page_clicked_cb(self, button):
        actual_page_model = self._book_model.get_page_model(self._actual_page)
        self._book_model.add_page(actual_page_model)
        self._actual_page = len(self._book_model.get_pages())
        self._update_page_buttons()
        self._preview_panel.update_model(self._book_model.get_pages())

    def __next_page_clicked_cb(self, button):
        self._actual_page += 1
        self._update_page_buttons()
        self._preview_panel.update_position(1)

    def __prev_page_clicked_cb(self, button):
        self._actual_page -= 1
        self._update_page_buttons()
        self._preview_panel.update_position(-1)

    def __page_activated_cb(self, preview_panel, order):
        self._actual_page = order
        self._update_page_buttons()

    def __page_moved_cb(self, preview_panel, pages_order_array):
        new_pages = []
        for n in pages_order_array:
            new_pages.append(self._book_model.get_pages()[n])
        # actual_page is 1 based and order is 0 based
        actual_page_order = self._actual_page - 1
        new_order_actual_page = pages_order_array.index(actual_page_order)
        self._actual_page = new_order_actual_page + 1
        self._book_model.set_pages(new_pages)
        self._update_page_buttons()
        preview_panel.update_model(self._book_model.get_pages())

    def __text_changed_cb(self, texteditor):
        self._book_model.set_page_text(self._actual_page,
                                       texteditor.get_text())

    def __save_ebook_clicked_cb(self, button):
        alert = Alert()
        alert.props.title = _('Book creation')
        alert.props.msg = _('Do you want to add an image for the cover?')
        icon = Icon(icon_name='dialog-ok')
        alert.add_button(Gtk.ResponseType.YES, _('Yes'), icon)
        icon.show()
        icon = Icon(icon_name='dialog-cancel')
        alert.add_button(Gtk.ResponseType.NO, _('No'), icon)
        icon.show()
        alert.connect('response', self.__add_cover_response_cb,
                      self._set_cover_and_create_book)
        self.add_alert(alert)

    def __add_cover_response_cb(self, alert, response_id, operation_function):
        if response_id == Gtk.ResponseType.YES:
            try:
                chooser = ObjectChooser(self, what_filter='Image',
                                        filter_type=FILTER_TYPE_GENERIC_MIME,
                                        show_preview=True)
            except:
                # for compatibility with older versions
                chooser = ObjectChooser(self, what_filter='Image')

            try:
                result = chooser.run()
                if result == Gtk.ResponseType.ACCEPT:
                    logging.error('ObjectChooser: %r' %
                                  chooser.get_selected_object())
                    jobject = chooser.get_selected_object()
                    if jobject and jobject.file_path:
                        logging.error("imagen seleccionada: %s",
                                      jobject.file_path)
                        mime_type = mime.get_for_file(jobject.file_path)
                        extension = mime.get_primary_extension(mime_type)
                        tempfile_name = \
                            os.path.join(
                                self.get_activity_root(), 'instance',
                                'tmp%i.%s' % (time.time(), extension))
                        os.link(jobject.file_path, tempfile_name)
                        operation_function(tempfile_name)
            finally:
                chooser.destroy()
                del chooser

        elif response_id == Gtk.ResponseType.NO:
            self._save_epub()
        self.remove_alert(alert)

    def _set_cover_and_create_book(self, cover_file_name):
        self._book_model.cover_path = cover_file_name
        self._save_epub()

    def _save_epub(self):
        epub_file_name = create_ebub_from_book_model(
            self.metadata['title'], self._book_model)

        # create a new journal item
        fileObject = datastore.create()
        fileObject.metadata['title'] = \
            _('"%s" as book') % self.metadata['title']
        fileObject.metadata['mime_type'] = 'application/epub+zip'

        full_text = ''
        for page in self._book_model.get_pages():
            full_text += page.text + '\n'
        fileObject.metadata['fulltext'] = full_text
        fileObject.metadata['icon-color'] = self.metadata['icon-color']
        fileObject.metadata['keep'] = self.metadata.get('keep', '0')

        fileObject.metadata['preview'] = self.metadata['preview']
        fileObject.file_path = epub_file_name

        # store the journal item
        datastore.write(fileObject, transfer_ownership=True)
        book_object_id = fileObject.object_id

        fileObject.destroy()
        del fileObject
        shutil.rmtree(os.path.dirname(epub_file_name))

        finish_alert = Alert()
        finish_alert.props.title = _('Book created')
        finish_alert.props.msg = _('You can read the book in your Journal')
        open_icon = Icon(icon_name='zoom-activity')
        finish_alert.add_button(Gtk.ResponseType.APPLY,
                                _('Show in Journal'), open_icon)
        open_icon.show()
        ok_icon = Icon(icon_name='dialog-ok')
        finish_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon)
        ok_icon.show()
        # Remove other alerts
        for alert in self._alerts:
            self.remove_alert(alert)

        self.add_alert(finish_alert)
        finish_alert.connect('response', self.__book_saved_alert_response_cb,
                             book_object_id)
        finish_alert.show()

    def __book_saved_alert_response_cb(self, alert, response_id,
                                       book_object_id):
        if response_id is Gtk.ResponseType.APPLY:
            activity.show_object_in_journal(book_object_id)
        self.remove_alert(alert)
Exemple #56
0
class ChartActivity(activity.Activity):

    def __init__(self, handle):

        activity.Activity.__init__(self, handle, True)

        self.max_participants = 1

        # CHART_OPTIONS

        self._font_option = TITLE_FONT
        self.x_label = ''
        self.y_label = ''
        self.chart_color = utils.get_user_fill_color('str')
        self.chart_line_color = utils.get_user_stroke_color('str')
        self.current_chart = None
        self.charts_area = None
        self.chart_data = []
        self.chart_type_buttons = []
        self._font_options = {
            'titleColor': '#000000',
            'titleFont': 'Sans',
            'titleFontSize': 12,
            'axis': {
                'tickFont': 'Sans',
                'tickFontSize': 12,
                'tickColor': '#000000',
                'labelFontSize': 14,
                'labelColor': '#666666',
                'labelFont': 'Sans',
                'lineColor': '#b3b3b3'}}

        # TOOLBARS
        self._labels_font = RadioToolButton()
        self._title_font = RadioToolButton()

        toolbarbox = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        activity_btn_toolbar = activity_button.page

        activity_btn_toolbar.title.connect('changed', self._set_chart_title)

        save_as_image = ToolButton('save-as-image')
        save_as_image.connect('clicked', self._save_as_image)
        save_as_image.set_tooltip(_('Save as image'))
        activity_btn_toolbar.insert(save_as_image, -1)

        save_as_image.show()

        import_stopwatch = ToolButton('import-stopwatch')
        import_stopwatch.connect('clicked', self.__import_stopwatch_cb)
        import_stopwatch.set_tooltip(_('Read StopWatch data'))
        activity_btn_toolbar.insert(import_stopwatch, -1)

        import_stopwatch.show()

        import_measure = ToolButton('import-measure')
        import_measure.set_tooltip(_('Read Measure data'))

        if utils.get_channels() == 1:
            import_measure.connect('clicked', self.__import_measure_cb, 1)

        else:
            import_measure.connect('clicked', self._measure_btn_clicked)
            self._create_measure_palette(import_measure)

        activity_btn_toolbar.insert(import_measure, -1)
        import_measure.show()

        toolbarbox.toolbar.insert(activity_button, 0)

        add_v = ToolButton('gtk-add')
        add_v.connect('clicked', self._add_value)
        add_v.set_tooltip(_('Add a value'))

        toolbarbox.toolbar.insert(add_v, -1)

        remove_v = ToolButton('gtk-remove')
        remove_v.connect('clicked', self._remove_value)
        remove_v.set_tooltip(_('Remove the selected value'))

        toolbarbox.toolbar.insert(remove_v, -1)

        self._remove_v = remove_v

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        separator.set_expand(False)
        toolbarbox.toolbar.insert(separator, -1)

        # We create two sets: one for the main toolbar and one for the
        # chart toolbar. We choose which set to use based on the
        # screen width.
        self._create_chart_buttons(toolbarbox.toolbar)

        self._chart_button = ToolbarButton(icon_name='vbar')
        chart_toolbar = Gtk.Toolbar()
        self._create_chart_buttons(chart_toolbar)
        self._chart_button.props.page = chart_toolbar
        chart_toolbar.show_all()
        toolbarbox.toolbar.insert(self._chart_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        separator.set_expand(False)
        toolbarbox.toolbar.insert(separator, -1)

        self._options_button = ToolbarButton(icon_name='preferences-system')
        options_toolbar = Gtk.Toolbar()

        self.chart_color_btn = ColorToolButton()
        self.chart_color_btn.set_color(_COLOR1)
        self.chart_color_btn.set_title(_('Chart Color'))
        options_toolbar.insert(self.chart_color_btn, -1)
        GObject.timeout_add(1000,
                            self._connect_color_btn,
                            self.chart_color_btn,
                            self._set_chart_color)

        self.line_color_btn = ColorToolButton()
        self.line_color_btn.set_color(_COLOR2)
        self.line_color_btn.set_title(_('Line Color'))
        options_toolbar.insert(self.line_color_btn, -1)
        GObject.timeout_add(1000,
                            self._connect_color_btn,
                            self.line_color_btn,
                            self._set_chart_line_color)

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        separator.set_expand(False)
        options_toolbar.insert(separator, -1)

        h_label_icon = Icon(icon_name='hlabel')
        h_label_tool_item = Gtk.ToolItem()
        h_label_tool_item.add(h_label_icon)
        options_toolbar.insert(h_label_tool_item, -1)

        self.h_label = Entry(_('Horizontal label...'))
        self.h_label.entry.connect('changed', self._set_h_label)
        options_toolbar.insert(self.h_label, -1)

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(False)
        separator.set_expand(False)
        options_toolbar.insert(separator, -1)

        v_label_icon = Icon(icon_name='vlabel')
        v_label_tool_item = Gtk.ToolItem()
        v_label_tool_item.add(v_label_icon)
        options_toolbar.insert(v_label_tool_item, -1)

        self.v_label = Entry(_('Vertical label...'))
        self.v_label.entry.connect('changed', self._set_v_label)
        options_toolbar.insert(self.v_label, -1)

        self._options_button.props.page = options_toolbar
        options_toolbar.show_all()

        toolbarbox.toolbar.insert(self._options_button, -1)

        text_toolbar_btn = ToolbarButton()
        text_toolbar_btn.props.icon_name = 'format-text'
        text_toolbar_btn.props.label = _('Text')
        toolbarbox.toolbar.insert(text_toolbar_btn, -1)
        self._text_options_btn = text_toolbar_btn

        texttoolbar = Gtk.Toolbar()

        self.font_name_combo = FontComboBox()
        self.font_name_combo.set_font_name('Sans')

        def set_font_name(w):
            self._set_chart_font_options(font=w.get_font_name())

        self.font_name_combo.connect("changed", set_font_name)
        texttoolbar.insert(ToolComboBox(self.font_name_combo), -1)

        self.font_size = FontSize()

        def set_font_size(w):
            self._set_chart_font_options(size=w.get_font_size())

        self.font_size.connect("changed", set_font_size)
        texttoolbar.insert(self.font_size, -1)

        self.text_color_btn = ColorToolButton()
        self.text_color_btn.set_color(style.COLOR_BLACK.get_gdk_color())
        self.text_color_btn.set_title(_('Font Color'))
        texttoolbar.insert(self.text_color_btn, -1)
        GObject.timeout_add(1000, self._connect_color_btn,
                            self.text_color_btn,
                            self._set_text_color)

        # self._title_font created in the top of the file
        self._title_font.connect('clicked', self._set_font_option,
                                 TITLE_FONT)
        self._title_font.set_tooltip(_('Title font'))
        self._title_font.props.icon_name = 'title-font'
        op_group = self._title_font

        texttoolbar.insert(self._title_font, 0)

        # self._labels_font created in the top of the file
        self._labels_font.connect('clicked', self._set_font_option,
                                  LABELS_FONT)
        self._labels_font.set_tooltip(_('Labels font'))
        self._labels_font.props.icon_name = 'labels-font'
        self._labels_font.props.group = op_group
        texttoolbar.insert(self._labels_font, 1)

        tick_font = RadioToolButton()
        tick_font.connect('clicked', self._set_font_option, TICK_FONT)
        tick_font.set_tooltip(_('Tick font'))
        tick_font.props.icon_name = 'tick-font'
        tick_font.props.group = op_group
        texttoolbar.insert(tick_font, 2)

        separator = Gtk.SeparatorToolItem()
        texttoolbar.insert(separator, 3)

        text_toolbar_btn.props.page = texttoolbar
        texttoolbar.show_all()

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(True)
        separator.set_expand(False)
        toolbarbox.toolbar.insert(separator, -1)

        self._fullscreen_button = ToolButton('view-fullscreen')
        self._fullscreen_button.set_tooltip(_("Fullscreen"))
        self._fullscreen_button.props.accelerator = '<Alt>Return'
        self._fullscreen_button.connect('clicked', self.__fullscreen_cb)
        toolbarbox.toolbar.insert(self._fullscreen_button, -1)

        charthelp.create_help(toolbarbox.toolbar)

        separator = Gtk.SeparatorToolItem()
        separator.set_draw(False)
        separator.set_expand(True)
        toolbarbox.toolbar.insert(separator, -1)

        stopbtn = StopButton(self)
        toolbarbox.toolbar.insert(stopbtn, -1)

        self.set_toolbar_box(toolbarbox)

        # CANVAS
        paned = Gtk.HPaned()
        box = Gtk.VBox()
        self.box = box

        # Set the info box width to 1/3 of the screen:
        def size_allocate_cb(widget, allocation):
            paned.disconnect(self._setup_handle)
            box_width = allocation.width / 3
            box.set_size_request(box_width, -1)

        self._setup_handle = paned.connect('size_allocate',
                                           size_allocate_cb)

        scroll = Gtk.ScrolledWindow()
        scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.labels_and_values = ChartData(self)
        scroll.add(self.labels_and_values)

        self.labels_and_values.connect('label-changed', self._label_changed)
        self.labels_and_values.connect('value-changed', self._value_changed)

        box.pack_start(scroll, True, True, 0)

        liststore_toolbar = Gtk.Toolbar()

        move_up = ToolButton('go-up')
        move_up.set_tooltip(_('Move up'))
        move_up.connect('clicked', self._move_up)

        move_down = ToolButton('go-down')
        move_down.set_tooltip(_('Move down'))
        move_down.connect('clicked', self._move_down)

        liststore_toolbar.insert(move_up, 0)
        liststore_toolbar.insert(move_down, 1)

        box.pack_end(liststore_toolbar, False, False, 0)

        paned.add1(box)

        # CHARTS AREA
        eventbox = Gtk.EventBox()
        self.charts_area = ChartArea(self)

        eventbox.modify_bg(Gtk.StateType.NORMAL, _WHITE)
        eventbox.add(self.charts_area)

        self._notebook = Gtk.Notebook()
        self._notebook.set_property('show-tabs', False)
        self._notebook.append_page(eventbox, Gtk.Label())

        # EMPTY WIDGETS
        empty_widgets = Gtk.EventBox()
        empty_widgets.modify_bg(Gtk.StateType.NORMAL,
                                style.COLOR_WHITE.get_gdk_color())

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        mvbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(mvbox, True, False, 0)

        image_icon = Icon(pixel_size=style.LARGE_ICON_SIZE,
                          icon_name='chart',
                          stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
                          fill_color=style.COLOR_TRANSPARENT.get_svg())
        mvbox.pack_start(image_icon, False, False, style.DEFAULT_PADDING)

        label = Gtk.Label('<span foreground="%s"><b>%s</b></span>' %
                          (style.COLOR_BUTTON_GREY.get_html(),
                           _('No data')))
        label.set_use_markup(True)
        mvbox.pack_start(label, False, False, style.DEFAULT_PADDING)

        hbox = Gtk.Box()
        open_image_btn = Gtk.Button()
        open_image_btn.connect('clicked', self._add_value)
        add_image = Gtk.Image.new_from_stock(Gtk.STOCK_ADD,
                                             Gtk.IconSize.BUTTON)
        buttonbox = Gtk.Box()
        buttonbox.pack_start(add_image, False, True, 0)
        buttonbox.pack_end(Gtk.Label(_('Add a value')), True, True, 5)
        open_image_btn.add(buttonbox)
        hbox.pack_start(open_image_btn, True, False, 0)
        mvbox.pack_start(hbox, False, False, style.DEFAULT_PADDING)

        empty_widgets.add(vbox)
        empty_widgets.show_all()
        self._notebook.append_page(empty_widgets, Gtk.Label())

        paned.add2(self._notebook)

        self.set_canvas(paned)
        self.charts_area.connect('size_allocate', self._chart_size_allocate)

        self.show_all()

        Gdk.Screen.get_default().connect('size-changed', self._configure_cb)
        self._configure_cb()

    def _set_text_color(self, *args):
        color = utils.rgb2html(args[-1].get_color())
        self._set_chart_font_options(color=color)

    def _set_chart_font_options(self, font=None, size=None, color=None):
        op = self._font_options
        if self._font_option == TITLE_FONT:
            op['titleFont'] = font or op['titleFont']
            op['titleFontSize'] = size or op['titleFontSize']
            op['titleColor'] = color or op['titleColor']

        elif self._font_option == LABELS_FONT:
            op['axis']['labelFont'] = font or op['axis']['labelFont']
            op['axis']['labelFontSize'] = size or op['axis']['labelFontSize']
            op['axis']['labelColor'] = color or op['axis']['labelColor']

        elif self._font_option == TICK_FONT:
            op['axis']['tickFont'] = font or op['axis']['tickFont']
            op['axis']['tickFontSize'] = size or op['axis']['tickFontSize']
            op['axis']['tickColor'] = color or op['axis']['tickColor']

        self._font_options = op
        self._render_chart()

    def _get_chart_font_options(self, option):
        chart_options = self._font_options
        if option == TITLE_FONT:
            font = chart_options['titleFont']
            size = chart_options['titleFontSize']
            color = chart_options['titleColor']

        elif option == LABELS_FONT:
            font = chart_options['axis']['labelFont']
            size = chart_options['axis']['labelFontSize']
            color = chart_options['axis']['labelColor']

        elif option == TICK_FONT:
            font = chart_options['axis']['tickFont']
            size = chart_options['axis']['tickFontSize']
            color = chart_options['axis']['tickColor']

        else:
            return None, None, None
        return font, size, color

    def _set_font_option(self, *args):
        if not hasattr(self, 'font_name_combo'):
            return

        self._font_option = args[-1]

        font, size, color = self._get_chart_font_options(self._font_option)

        self.font_name_combo.set_font_name(font)
        self.font_size.set_font_size(size)
        self.text_color_btn.set_color(Color(color).get_gdk_color())

    def _create_chart_buttons(self, toolbar):
        add_vbar_chart = RadioToolButton()
        add_vbar_chart.connect('clicked', self._add_chart_cb,
                               charts.VERTICAL_BAR)
        add_vbar_chart.set_tooltip(_('Vertical Bar Chart'))
        add_vbar_chart.props.icon_name = 'vbar'
        charts_group = add_vbar_chart

        toolbar.insert(add_vbar_chart, -1)

        add_hbar_chart = RadioToolButton()
        add_hbar_chart.connect('clicked', self._add_chart_cb,
                               charts.HORIZONTAL_BAR)
        add_hbar_chart.set_tooltip(_('Horizontal Bar Chart'))
        add_hbar_chart.props.icon_name = 'hbar'
        add_hbar_chart.props.group = charts_group
        toolbar.insert(add_hbar_chart, -1)

        add_line_chart = RadioToolButton()
        add_line_chart.connect('clicked', self._add_chart_cb, charts.LINE)
        add_line_chart.set_tooltip(_('Line Chart'))
        add_line_chart.props.icon_name = 'line'
        add_line_chart.props.group = charts_group
        toolbar.insert(add_line_chart, -1)

        add_pie_chart = RadioToolButton()
        add_pie_chart.set_active(True)
        add_pie_chart.connect('clicked', self._add_chart_cb, charts.PIE)
        add_pie_chart.set_tooltip(_('Pie Chart'))
        add_pie_chart.props.icon_name = 'pie'
        add_pie_chart.props.group = charts_group
        toolbar.insert(add_pie_chart, -1)

        self.chart_type_buttons.append(add_vbar_chart)
        self.chart_type_buttons.append(add_hbar_chart)
        self.chart_type_buttons.append(add_line_chart)
        self.chart_type_buttons.append(add_pie_chart)

    def _show_empty_widgets(self):
        if hasattr(self, '_notebook'):
            self._notebook.set_current_page(1)
            self._remove_v.set_sensitive(False)

            for btn in self.chart_type_buttons:
                btn.set_sensitive(False)

            self._options_button.set_sensitive(False)
            self._text_options_btn.set_sensitive(False)
            self._fullscreen_button.set_sensitive(False)

    def _show_chart_area(self):
        if self._notebook.get_current_page() == 1:
            self._notebook.set_current_page(0)
            self._remove_v.set_sensitive(True)

            for btn in self.chart_type_buttons:
                btn.set_sensitive(True)

            self._options_button.set_sensitive(True)
            self._text_options_btn.set_sensitive(True)
            self._fullscreen_button.set_sensitive(True)

    def _create_measure_palette(self, button):
        palette = button.get_palette()
        hbox = Gtk.HBox()

        channel1 = ToolButton('measure-channel-1')
        channel1.connect('clicked', self.__import_measure_cb, 1)

        channel2 = ToolButton('measure-channel-2')
        channel2.connect('clicked', self.__import_measure_cb, 2)

        hbox.pack_start(channel1, False, True, 0)
        hbox.pack_end(channel2, False, True, 0)

        hbox.show_all()

        palette.set_content(hbox)

    def _measure_btn_clicked(self, button):
        palette = button.get_palette()
        palette.popup(immediate=True, state=1)

    def _add_value(self, widget, label='', value='0.0'):
        if label == '':
            label = str(len(self.chart_data) + 1)

        is_number = True
        try:
            float(value)
        except ValueError:
            _logger.debug('data (%s) not a number' % (str(value)))
            is_number = False

        if is_number:
            data = (label, float(value))
            if data not in self.chart_data:
                pos = self.labels_and_values.add_value(label, value)
                self.chart_data.insert(pos, data)
                self._update_chart_data()

        elif not is_number:
            _invalid_number_alert(activity)

    def _remove_value(self, widget):
        value = self.labels_and_values.remove_selected_value()
        self.chart_data.remove(value)
        self._update_chart_data()

    def _add_chart_cb(self, widget, type=charts.VERTICAL_BAR):
        self.current_chart = charts.Chart(type)

        def update_btn():
            if (type == charts.PIE and
                    not self.chart_type_buttons[3].get_active() and
                    not self.chart_type_buttons[7].get_active()):
                self.chart_type_buttons[3].set_active(True)
                self.chart_type_buttons[7].set_active(True)

        GObject.idle_add(update_btn)

        self.update_chart()

    def _configure_cb(self, event=None):
        # If we have room, put buttons on the main toolbar
        if Gdk.Screen.width() / 14 > style.GRID_CELL_SIZE:
            self._chart_button.set_expanded(False)
            self._chart_button.hide()
            for i in range(4):
                self.chart_type_buttons[i].show()
                self.chart_type_buttons[i + 4].hide()
        else:
            self._chart_button.show()
            self._chart_button.set_expanded(True)
            for i in range(4):
                self.chart_type_buttons[i].hide()
                self.chart_type_buttons[i + 4].show()

    def _chart_size_allocate(self, widget, allocation):
            self._render_chart()

    def unfullscreen(self):
        self.box.show()
        activity.Activity.unfullscreen(self)
        GObject.idle_add(self._render_chart)

    def __fullscreen_cb(self, button):
        self.box.hide()
        self._render_chart(fullscreen=True)
        activity.Activity.fullscreen(self)

    def _render_chart(self, fullscreen=False):
        if not self.chart_data:
            self._show_empty_widgets()
            return

        if self.current_chart is None or self.charts_area is None:
            return

        try:
            # Resize the chart for all the screen sizes
            alloc = self.get_allocation()

            if fullscreen:
                new_width = alloc.width
                new_height = alloc.height
                self.current_chart.width = alloc.width
                self.current_chart.height = alloc.height
            if not fullscreen:
                alloc = self.charts_area.get_allocation()
                new_width = alloc.width - 40
                new_height = alloc.height - 40
            self.current_chart.width = new_width
            self.current_chart.height = new_height

            # Set options
            self.current_chart.set_color_scheme(color=self.chart_color)
            self.current_chart.set_line_color(self.chart_line_color)
            self.current_chart.set_font_options(self._font_options)

            if self.current_chart.type == charts.PIE:
                self.current_chart.render(self)
            else:
                self.current_chart.render()
            self.charts_area.queue_draw()

        except (ZeroDivisionError, ValueError):
            pass

        self._show_chart_area()
        return False

    def _update_chart_active_button(self, type=None):
        if self.current_chart is None and type is None:
            return

        _type = type or self.current_chart.type

        if _type == charts.VERTICAL_BAR:
            self.chart_type_buttons[0].set_active(True)
            self.chart_type_buttons[4].set_active(True)

        elif _type == charts.HORIZONTAL_BAR:
            self.chart_type_buttons[1].set_active(True)
            self.chart_type_buttons[5].set_active(True)

        elif _type == charts.LINE:
            self.chart_type_buttons[2].set_active(True)
            self.chart_type_buttons[6].set_active(True)

        elif _type == charts.PIE:
            self.chart_type_buttons[3].set_active(True)
            self.chart_type_buttons[7].set_active(True)
            self._labels_font.set_sensitive(False)

    def _update_chart_data(self):
        if self.current_chart is None:
            return
        self.current_chart.data_set(self.chart_data)
        self._update_chart_labels()

    def _set_chart_title(self, widget):
        self._update_chart_labels(title=widget.get_text())

    def _update_chart_labels(self, title=''):
        if self.current_chart is None:
            return

        if not title and self.metadata['title']:
            title = self.metadata['title']

        self.current_chart.set_title(title)
        self.current_chart.set_x_label(self.x_label)
        self.current_chart.set_y_label(self.y_label)
        self._render_chart()

    def update_chart(self):
        if self.current_chart:
            self.current_chart.data_set(self.chart_data)
            self.current_chart.set_title(self.metadata['title'])
            self.current_chart.set_x_label(self.x_label)
            self.current_chart.set_y_label(self.y_label)
            self._set_font_option(self._font_option)
            self._render_chart()

    def _label_changed(self, treeview, path, new_label):
        path = int(path)
        self.chart_data[path] = (new_label, self.chart_data[path][1])
        self._update_chart_data()

    def _value_changed(self, treeview, path, new_value):
        path = int(path)
        self.chart_data[path] = (self.chart_data[path][0], float(new_value))
        self._update_chart_data()

    def _move_up(self, widget):
        old, new = self.labels_and_values.move_up()
        _object = self.chart_data[old]
        self.chart_data.remove(_object)
        self.chart_data.insert(new, _object)
        self._update_chart_data()

    def _move_down(self, widget):
        old, new = self.labels_and_values.move_down()
        if old is not None:
            _object = self.chart_data[old]
            self.chart_data.remove(_object)
            self.chart_data.insert(new, _object)
            self._update_chart_data()

    def _set_h_label(self, widget):
        self.x_label = widget.get_text()
        self._update_chart_labels()

    def _set_v_label(self, widget):
        self.y_label = widget.get_text()
        self._update_chart_labels()

    def _set_chart_color(self, *args):
        self.chart_color = utils.rgb2html(args[-1].get_color())
        self._render_chart()

    def _set_chart_line_color(self, *args):
        self.chart_line_color = utils.rgb2html(args[-1].get_color())
        self._render_chart()

    def _connect_color_btn(self, colorbtn, function):
        if colorbtn._palette is None:
            return True

        for scale in colorbtn._palette._scales:
            scale.connect('button-release-event', function, colorbtn)

        for button in colorbtn._palette._swatch_tray.get_children():
            button.connect('clicked', function, colorbtn)

        return False

    def _object_chooser(self, mime_type, type_name):
        chooser = ObjectChooser()
        matches_mime_type = False

        response = chooser.run()
        if response == Gtk.ResponseType.ACCEPT:
            jobject = chooser.get_selected_object()
            metadata = jobject.metadata
            file_path = jobject.file_path

            if metadata['mime_type'] == mime_type:
                matches_mime_type = True

            else:
                alert = Alert()

                alert.props.title = _('Invalid object')
                alert.props.msg = \
                    _('The selected object must be a %s file' % (type_name))

                ok_icon = Icon(icon_name='dialog-ok')
                alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon)
                ok_icon.show()

                alert.connect('response', lambda a, r: self.remove_alert(a))

                self.add_alert(alert)

                alert.show()

        return matches_mime_type, file_path, metadata['title']

    def _graph_from_reader(self, reader):
        self.labels_and_values.model.clear()
        self.chart_data = []

        chart_data = reader.get_chart_data()

        horizontal, vertical = reader.get_labels_name()

        self.v_label.entry.set_text(horizontal)
        self.h_label.entry.set_text(vertical)

        # Load the data
        for row in chart_data:
            self._add_value(None,
                            label=row[0], value=float(row[1]))

            self.update_chart()

    def __import_stopwatch_cb(self, widget):
        matches_mime_type, file_path, title = \
            self._object_chooser(_STOPWATCH_MIME_TYPE, _('StopWatch'))

        if matches_mime_type:
            f = open(file_path)
            reader = StopWatchReader(f)
            self._graph_from_reader(reader)

            f.close()

    def __import_measure_cb(self, widget, channel=1):
        matches_mime_type, file_path, title = \
            self._object_chooser(_CSV_MIME_TYPE, _('Measure'))

        if matches_mime_type:
            f = open(file_path)
            reader = MeasureReader(f, channel)
            self._graph_from_reader(reader)

            f.close()

    def _save_as_image(self, widget):
        if self.current_chart:
            jobject = datastore.create()

            jobject.metadata['title'] = self.metadata['title']
            jobject.metadata['mime_type'] = 'image/png'

            self.current_chart.as_png(_CHART_FILE)
            jobject.set_file_path(_CHART_FILE)

            datastore.write(jobject)

    def load_from_file(self, f):
        try:
            data = json.load(f)
        finally:
            f.close()

        self.metadata['title'] = data['title']
        self.x_label = data['x_label']
        self.y_label = data['y_label']
        self.chart_color = data['chart_color']
        self.chart_line_color = data['chart_line_color']
        self.current_chart.type = data['current_chart.type']

        # Make it compatible with old Chart instances
        if 'font_options' in data:
            self._font_options = data['font_options']

        chart_data = data['chart_data']

        # Update charts buttons
        self._update_chart_active_button()

        # Update the controls in the config subtoolbar
        self.chart_color_btn.set_color(Color(self.chart_color).get_gdk_color())
        self.line_color_btn.set_color(Color(self.chart_line_color).
                                      get_gdk_color())

        # If the saved label is not '', set the text entry with the saved label
        if self.x_label != '':
            self.h_label.entry.set_text(self.x_label)

        if self.y_label != '':
            self.v_label.entry.set_text(self.y_label)

        # load the data
        for row in chart_data:
            self._add_value(None, label=row[0], value=float(row[1]))

        self.update_chart()

    def write_file(self, file_path):
        self.metadata['mime_type'] = 'application/x-chart-activity'
        if self.current_chart:

            data = {}
            data['title'] = self.metadata['title']
            data['x_label'] = self.x_label
            data['y_label'] = self.y_label
            data['chart_color'] = self.chart_color
            data['chart_line_color'] = self.chart_line_color
            data['current_chart.type'] = self.current_chart.type
            data['chart_data'] = self.chart_data
            data['font_options'] = self._font_options

            f = open(file_path, 'w')
            try:
                json.dump(data, f)
            finally:
                f.close()

    def read_file(self, file_path):
        f = open(file_path, 'r')
        GObject.idle_add(self.load_from_file, f)
class SpeechToolbar(Gtk.Toolbar):

    def __init__(self, activity):
        Gtk.Toolbar.__init__(self)
        self._activity = activity
        if not speech.supported:
            return

        self._cnf_client = GConf.Client.get_default()
        self.load_speech_parameters()

        self.sorted_voices = [i for i in speech.voices()]
        self.sorted_voices.sort(self.compare_voices)
        default = 0
        for voice in self.sorted_voices:
            if voice[0] == speech.voice[0]:
                break
            default = default + 1

        # Play button
        self.play_btn = ToggleToolButton('media-playback-start')
        self.play_btn.show()
        self.play_btn.connect('toggled', self.play_cb)
        self.insert(self.play_btn, -1)
        self.play_btn.set_tooltip(_('Play / Pause'))

        # Stop button
        self.stop_btn = ToolButton('media-playback-stop')
        self.stop_btn.show()
        self.stop_btn.connect('clicked', self.stop_cb)
        self.stop_btn.set_sensitive(False)
        self.insert(self.stop_btn, -1)
        self.stop_btn.set_tooltip(_('Stop'))

        self.voice_combo = ComboBox()
        for voice in self.sorted_voices:
            self.voice_combo.append_item(voice, voice[0])
        self.voice_combo.set_active(default)

        self.voice_combo.connect('changed', self.voice_changed_cb)
        combotool = ToolComboBox(self.voice_combo)
        self.insert(combotool, -1)
        combotool.show()
        speech.reset_buttons_cb = self.reset_buttons_cb

    def compare_voices(self,  a,  b):
        if a[0].lower() == b[0].lower():
            return 0
        if a[0] .lower() < b[0].lower():
            return -1
        if a[0] .lower() > b[0].lower():
            return 1

    def voice_changed_cb(self, combo):
        speech.voice = combo.props.value
        speech.say(speech.voice[0])
        self.save_speech_parameters()

    def load_speech_parameters(self):
        speech_parameters = {}
        data_path = os.path.join(self._activity.get_activity_root(), 'data')
        data_file_name = os.path.join(data_path, 'speech_params.json')
        if os.path.exists(data_file_name):
            f = open(data_file_name, 'r')
            try:
                speech_parameters = simplejson.load(f)
                speech.voice = speech_parameters['voice']
            finally:
                f.close()

        self._cnf_client.add_dir('/desktop/sugar/speech',
                GConf.ClientPreloadType.PRELOAD_NONE)
        speech.pitch = self._cnf_client.get_int('/desktop/sugar/speech/pitch')
        speech.rate = self._cnf_client.get_int('/desktop/sugar/speech/rate')
        self._cnf_client.notify_add('/desktop/sugar/speech/pitch', \
                self.__conf_changed_cb, None)
        self._cnf_client.notify_add('/desktop/sugar/speech/rate', \
                self.__conf_changed_cb, None)

    def __conf_changed_cb(self, client, connection_id, entry, args):
        key = entry.get_key()
        value = client.get_int(key)
        if key == '/desktop/sugar/speech/pitch':
            speech.pitch = value
        if key == '/desktop/sugar/speech/rate':
            speech.rate = value

    def save_speech_parameters(self):
        speech_parameters = {}
        speech_parameters['voice'] = speech.voice
        data_path = os.path.join(self._activity.get_activity_root(), 'data')
        data_file_name = os.path.join(data_path, 'speech_params.json')
        f = open(data_file_name, 'w')
        try:
            simplejson.dump(speech_parameters, f)
        finally:
            f.close()

    def reset_buttons_cb(self):
        logging.error('reset buttons')
        self.play_btn.set_named_icon('media-playback-start')
        self.stop_btn.set_sensitive(False)

    def play_cb(self, widget):
        self.stop_btn.set_sensitive(True)
        if widget.get_active():
            self.play_btn.set_named_icon('media-playback-pause')
            if speech.is_stopped():
                speech.play(self._activity._view.get_marked_words())
        else:
            self.play_btn.set_named_icon('media-playback-start')
            speech.pause()

    def stop_cb(self, widget):
        self.stop_btn.set_sensitive(False)
        self.play_btn.set_named_icon('media-playback-start')
        self.play_btn.set_active(False)
        speech.stop()
class Controls(GObject.GObject):
    """Class to create the Control (play, back, forward,
    add, remove, etc) toolbar"""

    SCALE_UPDATE_INTERVAL = 1000
    SCALE_DURATION_TEXT = 100
    RESEEK_TIMEOUT = 250  # ms

    def __init__(self, activity, main_toolbar, secondary_toolbar):
        GObject.GObject.__init__(self)

        self.activity = activity
        self.toolbar = main_toolbar
        self.secondary_toolbar = secondary_toolbar

        self._scale_update_id = -1
        self._scale_value_changed_id = -1
        self._scale_reseek_timeout_id = -1

        self.open_button = ToolButton('list-add')
        self.open_button.set_tooltip(_('Add track'))
        self.open_button.show()
        self.open_button.connect('clicked', self.__open_button_clicked_cb)
        self.toolbar.insert(self.open_button, -1)

        erase_playlist_entry_btn = ToolButton(icon_name='list-remove')
        erase_playlist_entry_btn.set_tooltip(_('Remove track'))
        erase_playlist_entry_btn.connect(
            'clicked', self.__erase_playlist_entry_clicked_cb)
        self.toolbar.insert(erase_playlist_entry_btn, -1)

        self._spacer = Gtk.SeparatorToolItem()
        self._spacer.props.draw = True
        self._spacer.set_expand(False)
        self.toolbar.insert(self._spacer, -1)
        self._spacer.show()

        self.prev_button = ToolButton('player_rew')
        self.prev_button.set_tooltip(_('Previous'))
        self.prev_button.props.accelerator = 'Up'
        self.prev_button.show()
        self.prev_button.connect('clicked', self.__prev_button_clicked_cb)
        self.toolbar.insert(self.prev_button, -1)

        self.pause_image = Gtk.Image.new_from_stock(Gtk.STOCK_MEDIA_PAUSE,
                                                    Gtk.IconSize.BUTTON)
        self.pause_image.show()
        self.play_image = Gtk.Image.new_from_stock(Gtk.STOCK_MEDIA_PLAY,
                                                   Gtk.IconSize.BUTTON)
        self.play_image.show()

        self.button = ToolButton('media-playback-start')
        self.button.set_tooltip(_('Play or Pause'))
        self.button.set_icon_widget(self.play_image)
        self.button.props.accelerator = 'space'
        self.button.set_property('can-default', True)
        self.button.show()
        self.button.connect('clicked', self._button_clicked_cb)

        self.toolbar.insert(self.button, -1)

        self.next_button = ToolButton('player_fwd')
        self.next_button.set_tooltip(_('Next'))
        self.next_button.props.accelerator = 'Down'
        self.next_button.show()
        self.next_button.connect('clicked', self.__next_button_clicked_cb)
        self.toolbar.insert(self.next_button, -1)

        self._current_time = Gtk.ToolItem()
        self.current_time_label = Gtk.Label(label='')
        self._current_time.add(self.current_time_label)
        self._current_time.show()
        self.toolbar.insert(self._current_time, -1)

        self.adjustment = Gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
        self.hscale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL,
                                adjustment=self.adjustment)
        self.hscale.set_draw_value(False)
        # FIXME: this seems to be deprecated
        # self.hscale.set_update_policy(Gtk.UPDATE_CONTINUOUS)
        logging.debug("FIXME: AttributeError: 'Scale' object has no "
                      "attribute 'set_update_policy'")
        self.hscale.connect('button-press-event',
                            self.__scale_button_press_cb)
        self.hscale.connect('button-release-event',
                            self.__scale_button_release_cb)

        self.scale_item = Gtk.ToolItem()
        self.scale_item.set_expand(True)
        self.scale_item.add(self.hscale)
        self.toolbar.insert(self.scale_item, -1)

        self._total_time = Gtk.ToolItem()
        self.total_time_label = Gtk.Label(label='')
        self._total_time.add(self.total_time_label)
        self._total_time.show()
        self.toolbar.insert(self._total_time, -1)

        self.activity.connect('playlist-finished', self.__playlist_finished_cb)
        self.activity.player.connect('play', self.__player_play)

    def update_layout(self, landscape=True):
        if landscape:
            self._remove_controls(self.secondary_toolbar)
            self._add_controls(self.toolbar)
        else:
            self._remove_controls(self.toolbar)
            self._add_controls(self.secondary_toolbar)
            self._spacer.hide()

    def _remove_controls(self, toolbar):
        for control in [self._spacer, self.prev_button,
                        self.button, self.next_button,
                        self._current_time, self.scale_item,
                        self._total_time]:
            if control in toolbar:
                toolbar.remove(control)

    def _add_controls(self, toolbar):
        for control in [self._spacer, self.prev_button,
                        self.button, self.next_button,
                        self._current_time, self.scale_item,
                        self._total_time]:
            if control not in toolbar:
                toolbar.insert(control, -1)
                control.show()

    def __player_play(self, widget):
        if self._scale_update_id == -1:
            self._scale_update_id = GObject.timeout_add(
                self.SCALE_UPDATE_INTERVAL, self.__update_scale_cb)

        # We need to wait for GstPlayer to load the stream's duration
        GObject.timeout_add(self.SCALE_DURATION_TEXT,
                            self.__set_scale_duration)

        self.set_enabled()
        self.set_button_pause()

    def __set_scale_duration(self):
        success, self.p_position, self.p_duration = \
            self.activity.player.query_position()

        if success and self.p_duration != Gst.CLOCK_TIME_NONE:
            seconds = self.p_duration * 10 ** -9
            time = '%2d:%02d' % (int(seconds / 60), int(seconds % 60))
            self.total_time_label.set_text(time)
            # Once we set the total_time we don't need to change it
            # until a new stream is played
            return False
        else:
            # We don't have the stream's duration yet, we need to call
            # this method again
            return True

    def __open_button_clicked_cb(self, widget):
        self.show_picker_cb()

    def __erase_playlist_entry_clicked_cb(self, widget):
        self.activity.playlist_widget.delete_selected_items()
        self.check_if_next_prev()

    def show_picker_cb(self, button=None):
        # optional parameter button is used when called from activity.py
        # emptypanel big button
        jobject = None
        chooser = ObjectChooser(self.activity,
                                what_filter=mime.GENERIC_TYPE_AUDIO)

        try:
            result = chooser.run()
            if result == Gtk.ResponseType.ACCEPT:
                jobject = chooser.get_selected_object()
                if jobject and jobject.file_path:
                    logging.info('Adding %s', jobject.file_path)
                    self.activity.playlist_widget.load_file(jobject)
                    self.check_if_next_prev()

                    self.activity._switch_canvas(False)
                    self.activity._view_toolbar._show_playlist.set_active(
                        True)
        finally:
            if jobject is not None:
                jobject.destroy()

    def __prev_button_clicked_cb(self, widget):
        self.activity.songchange('prev')

    def __next_button_clicked_cb(self, widget):
        self.activity.songchange('next')

    def check_if_next_prev(self):
        current_playing = self.activity.playlist_widget.get_current_playing()
        if len(self.activity.playlist_widget._items) == 0:
            # There is no media in the playlist
            self.prev_button.set_sensitive(False)
            self.button.set_sensitive(False)
            self.next_button.set_sensitive(False)
            self.hscale.set_sensitive(False)
            self.activity._view_toolbar._fullscreen.set_sensitive(False)
        else:
            self.button.set_sensitive(True)
            self.hscale.set_sensitive(True)
            self.activity._view_toolbar._fullscreen.set_sensitive(True)

            if current_playing == 0:
                self.prev_button.set_sensitive(False)
            else:
                self.prev_button.set_sensitive(True)

            items = len(self.activity.playlist_widget._items)
            if current_playing == items - 1:
                self.next_button.set_sensitive(False)
            else:
                self.next_button.set_sensitive(True)

    def _button_clicked_cb(self, widget):
        self.set_enabled()

        if self.activity.player.is_playing():
            self.activity.player.pause()
            self.set_button_play()
            GObject.source_remove(self._scale_update_id)
            self._scale_update_id = -1
        else:
            if self.activity.player.error:
                self.set_disabled()
            else:
                if self.activity.player.player.props.current_uri is None:
                    # There is no stream selected to be played
                    # yet. Select the first one
                    available = self.activity.playlist_widget.\
                        _items[0]['available']
                    if available:
                        path = self.activity.playlist_widget._items[0]['path']
                        self.activity.playlist_widget.emit(
                            'play-index', 0, path)
                        self.activity.playlist_widget.set_current_playing(0)
                else:
                    self.activity.player.play()
                    self.activity._switch_canvas(True)
                    self._scale_update_id = GObject.timeout_add(
                        self.SCALE_UPDATE_INTERVAL, self.__update_scale_cb)

    def set_button_play(self):
        self.button.set_icon_widget(self.play_image)

    def set_button_pause(self):
        self.button.set_icon_widget(self.pause_image)

    def set_disabled(self):
        self.button.set_sensitive(False)
        self.scale_item.set_sensitive(False)
        self.hscale.set_sensitive(False)

    def set_enabled(self):
        self.button.set_sensitive(True)
        self.scale_item.set_sensitive(True)
        self.hscale.set_sensitive(True)

    def __scale_button_press_cb(self, widget, event):
        self.button.set_sensitive(False)
        self._was_playing = self.activity.player.is_playing()
        if self._was_playing:
            self.activity.player.pause()

        # don't timeout-update position during seek
        if self._scale_update_id != -1:
            GObject.source_remove(self._scale_update_id)
            self._scale_update_id = -1

        # make sure we get changed notifies
        if self._scale_value_changed_id == -1:
            self._scale_value_changed_id = self.hscale.connect(
                'value-changed', self.__scale_value_changed_cb)

    def __scale_value_changed_cb(self, scale):
        if self._scale_reseek_timeout_id != -1:
            GObject.source_remove(self._scale_reseek_timeout_id)

        self._scale_reseek_timeout_id = GObject.timeout_add(
            self.RESEEK_TIMEOUT, self._reseek)

    def _reseek(self):
        self._scale_reseek_timeout_id = -1
        location = long(self.activity.control.hscale.get_value() *
                        self.p_duration / 100)  # in ns
        self.activity.player.seek(location)
        # Allow for a preroll
        self.activity.player.get_state(timeout=50 * Gst.MSECOND)  # 50 ms
        return False

    def __scale_button_release_cb(self, widget, event):
        if self._scale_reseek_timeout_id != -1:
            GObject.source_remove(self._scale_reseek_timeout_id)
            self._scale_reseek_timeout_id = -1
        self._reseek()

        widget.disconnect(self._scale_value_changed_id)
        self._scale_value_changed_id = -1

        self.button.set_sensitive(True)

        if self._was_playing:
            self.activity.player.play()

        if self._scale_update_id == -1:
            self._scale_update_id = GObject.timeout_add(
                self.SCALE_UPDATE_INTERVAL, self.__update_scale_cb)

    def __update_scale_cb(self):
        success, self.p_position, self.p_duration = \
            self.activity.player.query_position()

        if success and self.p_position != Gst.CLOCK_TIME_NONE:
            value = self.p_position * 100.0 / self.p_duration
            self.adjustment.set_value(value)

            # Update the current time
            seconds = self.p_position * 10 ** -9
            time = '%2d:%02d' % (int(seconds / 60), int(seconds % 60))
            self.current_time_label.set_text(time)

        return True

    def __playlist_finished_cb(self, widget):
        self.activity.player.stop()
        self.set_button_play()
        self.check_if_next_prev()

        self.adjustment.set_value(0)
        self.current_time_label.set_text('')
        self.total_time_label.set_text('')
class InsertToolbar(Gtk.Toolbar):

    def __init__(self, abiword_canvas):
        Gtk.Toolbar.__init__(self)

        self._abiword_canvas = abiword_canvas

        self._table = abiword.TableCreator()
        self._table.set_labels(_('Table'), _('Cancel'))
        self._table_id = self._table.connect('selected', self._table_cb)
        image = Gtk.Image()
        image.set_from_icon_name('insert-table', -1)
        self._table.set_image(image)
        self._table.set_relief(Gtk.ReliefStyle.NONE)
        tool_item = Gtk.ToolItem()
        tool_item.add(self._table)
        self.insert(tool_item, -1)
        tool_item.show_all()

        self._table_rows_after = ToolButton('row-insert')
        self._table_rows_after.set_tooltip(_('Insert Row'))
        self._table_rows_after_id = self._table_rows_after.connect( \
                'clicked', self._table_rows_after_cb)
        self.insert(self._table_rows_after, -1)

        self._table_delete_rows = ToolButton('row-remove')
        self._table_delete_rows.set_tooltip(_('Delete Row'))
        self._table_delete_rows_id = self._table_delete_rows.connect( \
                'clicked', self._table_delete_rows_cb)
        self.insert(self._table_delete_rows, -1)

        self._table_cols_after = ToolButton('column-insert')
        self._table_cols_after.set_tooltip(_('Insert Column'))
        self._table_cols_after_id = self._table_cols_after.connect( \
                'clicked', self._table_cols_after_cb)
        self.insert(self._table_cols_after, -1)

        self._table_delete_cols = ToolButton('column-remove')
        self._table_delete_cols.set_tooltip(_('Delete Column'))
        self._table_delete_cols_id = self._table_delete_cols.connect( \
                'clicked', self._table_delete_cols_cb)
        self.insert(self._table_delete_cols, -1)

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)

        image = ToolButton('insert-picture')
        image.set_tooltip(_('Insert Image'))
        self._image_id = image.connect('clicked', self._image_cb)
        self.insert(image, -1)

        self.show_all()

        self._abiword_canvas.connect('table-state', self._isTable_cb)

    def _image_cb(self, button):
        def cb(object):
            logging.debug('ObjectChooser: %r' % object)
            self._abiword_canvas.insert_image(object.file_path, True)

        chooser.pick(what=chooser.IMAGE, cb=cb)

    def _table_cb(self, abi, rows, cols):
        self._abiword_canvas.insert_table(rows, cols)

    def _table_rows_after_cb(self, button):
        self._abiword_canvas.invoke_ex('insertRowsAfter', '', 0, 0)

    def _table_delete_rows_cb(self, button):
        self._abiword_canvas.invoke_ex('deleteRows', '', 0, 0)

    def _table_cols_after_cb(self, button):
        self._abiword_canvas.invoke_ex('insertColsAfter', '', 0, 0)

    def _table_delete_cols_cb(self, button):
        self._abiword_canvas.invoke_ex('deleteColumns', '', 0, 0)

    def _isTable_cb(self, abi, b):
        self._table_rows_after.set_sensitive(b)
        self._table_delete_rows.set_sensitive(b)
        self._table_cols_after.set_sensitive(b)
        self._table_delete_cols.set_sensitive(b)