Logo Search packages:      
Sourcecode: magicicada version File versions  Download package

__init__.py

# __init__.py
#
# Author: Natalia Bidart <natalia.bidart@gmail.com>
#
# Copyright 2010 Chicharreros
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Magicicada."""

import gtk
import sys

import gettext
from gettext import gettext as _
gettext.textdomain('magicicada')

from twisted.internet import gtk2reactor # for gtk-2.0
gtk2reactor.install()

from magicicada import syncdaemon, logger
from magicicada.helpers import humanize_bytes, get_data_file, get_builder, NO_OP

CONTENT_QUEUE = 'content'
META_QUEUE = 'meta'

# set up the logging for all the project
logger.set_up()

class MagicicadaUI(object):

    STATUS_JOINER = " - "
    STATUS = {
        'initial': _('Service is not started, click Start to continue.'),
    }

    def __init__(self, launchpad_available=False, on_destroy=NO_OP,
                 syncdaemon_class=syncdaemon.SyncDaemon):
        """Init."""
        self.builder = get_builder('gui.glade')
        self.builder.connect_signals(self)

        if launchpad_available:
            # for more information about LaunchpadIntegration:
            # wiki.ubuntu.com/UbuntuDevelopment/Internationalisation/Coding
            helpmenu = self.builder.get_object('helpMenu')
            if helpmenu:
                LaunchpadIntegration.set_sourcepackagename('magicicada')
                LaunchpadIntegration.add_items(helpmenu, 0, False, True)

        self.on_destroy = on_destroy

        animation_filename = get_data_file('media', 'loader-ball.gif')
        self.loading_animation = gtk.gdk.PixbufAnimation(animation_filename)
        active_filename = get_data_file('media', 'active-016.png')
        self.active_indicator = gtk.gdk.pixbuf_new_from_file(active_filename)

        widgets = (
            'start', 'stop', 'connect', 'disconnect', # toolbar buttons
            'folders', 'folders_dialog', # folders
            'folders_store', 'folders_close',
            'shares_to_me', 'shares_to_me_dialog', # shares_to_me
            'shares_to_me_store', 'shares_to_me_close',
            'shares_to_others', 'shares_to_others_dialog', # shares_to_others
            'shares_to_others_store', 'shares_to_others_close',
            'is_started', 'is_connected', 'is_online', # status bar images
            'status_label', 'status_icon', # status label and systray icon
            'metaq_view', 'contentq_view', # queues tree views
            'metaq_store', 'contentq_store', # queues list stores
            'metaq_label', 'contentq_label', # queues labels
            'raw_metadata', # raw metadata
            'about_dialog', # dialogs
            'main_window'
        )
        for widget in widgets:
            obj = self.builder.get_object(widget)
            setattr(self, widget, obj)
            assert obj is not None, '%s must not be None' % widget

        self.volumes = (self.folders, self.shares_to_me, self.shares_to_others)
        self.windows = (self.main_window, self.about_dialog,
                         self.folders_dialog)

        icon_filename = get_data_file('media', 'logo-016.png')
        self._icon = gtk.gdk.pixbuf_new_from_file(icon_filename)
        self.status_icon.set_from_file(icon_filename)
        for w in self.windows:
            w.set_icon(self._icon)

        about_filename = get_data_file('media', 'logo-128.png')
        self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_filename))

        self.sd = syncdaemon_class()
        self.sd.on_started_callback = self.on_started
        self.sd.on_stopped_callback = self.on_stopped
        self.sd.on_connected_callback = self.on_connected
        self.sd.on_disconnected_callback = self.on_disconnected
        self.sd.on_online_callback = self.on_online
        self.sd.on_offline_callback = self.on_offline
        self.sd.status_changed_callback = self.on_status_changed
        self.sd.content_queue_changed_callback = self.on_content_queue_changed
        self.sd.meta_queue_changed_callback = self.on_meta_queue_changed

        self.widget_is_visible = lambda w: w.get_property('visible')
        self.widget_enabled = lambda w: self.widget_is_visible(w) and \
                                        w.is_sensitive()

        self.update()

    # GTK callbacks

    def on_main_window_destroy(self, widget, data=None):
        """Called when the MagicicadaWindow is closed."""
        # Clean up code for saving application state should be added here.
        self.sd.shutdown()
        self.on_destroy()

    def on_quit_activate(self, widget, data=None):
        """Signal handler for closing the program."""
        self.on_main_window_destroy(self.main_window)

    def on_about_activate(self, widget, data=None):
        """Display the about box."""
        response = self.about_dialog.run()
        self.about_dialog.hide()

    def on_start_clicked(self, widget, data=None):
        """Start syncdaemon."""
        self.sd.start()
        self.start.set_sensitive(False)
        self._start_loading(self.is_started)

    def on_stop_clicked(self, widget, data=None):
        """Stop syncdaemon."""
        for v in self.volumes:
            v.set_sensitive(False)

        if self.widget_enabled(self.disconnect):
            self.on_disconnect_clicked(self.disconnect)
        self.connect.set_sensitive(False)

        self.stop.set_sensitive(False)
        self.sd.quit()

    def on_connect_clicked(self, widget, data=None):
        """Connect syncdaemon."""
        self.sd.connect()
        self.connect.set_sensitive(False)
        self._start_loading(self.is_connected)

    def on_disconnect_clicked(self, widget, data=None):
        """Disconnect syncdaemon."""
        self.disconnect.set_sensitive(False)
        self.sd.disconnect()

    def on_folders_close_clicked(self, widget, data=None):
        """Close the folders dialog."""
        self.folders_dialog.response(gtk.RESPONSE_CLOSE)

    def on_folders_clicked(self, widget, data=None):
        """List user folders."""
        items = self.sd.folders
        if items is None:
            items = []

        self.folders_store.clear()
        for item in items:
            row = (item.node, item.path, item.suggested_path,
                   item.subscribed, item.volume)
            self.folders_store.append(row)

        res = self.folders_dialog.run()
        self.folders_dialog.hide()

    def on_shares_to_me_close_clicked(self, widget, data=None):
        """Close the shares_to_me dialog."""
        self.shares_to_me_dialog.response(gtk.RESPONSE_CLOSE)

    def _on_shares_clicked(self, items, store, dialog):
        """List shares to the user or to others."""
        if items is None:
            items = []

        store.clear()
        for item in items:
            free_bytes = item.free_bytes
            if isinstance(free_bytes, int):
                free_bytes = humanize_bytes(free_bytes, precision=2)
            row = (item.accepted, item.access_level, free_bytes, item.name,
                   item.node_id, item.other_username, item.other_visible_name,
                   item.path, item.volume_id)
            store.append(row)

        res = dialog.run()
        dialog.hide()

    def on_shares_to_me_clicked(self, widget, data=None):
        """List shares to the user."""
        self._on_shares_clicked(self.sd.shares_to_me,
                                self.shares_to_me_store,
                                self.shares_to_me_dialog)

    def on_shares_to_others_close_clicked(self, widget, data=None):
        """Close the shares_to_others dialog."""
        self.shares_to_others_dialog.response(gtk.RESPONSE_CLOSE)

    def on_shares_to_others_clicked(self, widget, data=None):
        """List user shares to others."""
        self._on_shares_clicked(self.sd.shares_to_others,
                                self.shares_to_others_store,
                                self.shares_to_others_dialog)

    def on_raw_metadata_clicked(self, widget, data=None):
        """Show raw metadata for a path choosen by the user."""

    def on_status_icon_activate(self, widget, data=None):
        """Systray icon was clicked."""
        if self.widget_is_visible(self.main_window):
            self.main_window.hide()
        else:
            self.main_window.show()

    # SyncDaemon callbacks

    def on_started(self, *args, **kwargs):
        """Callback'ed when syncadaemon is started."""
        self.start.hide()
        self.stop.show()
        self.stop.set_sensitive(True)
        self._activate_indicator(self.is_started)
        self.connect.set_sensitive(True)

        for v in self.volumes:
            v.set_sensitive(True)

        self._update_queues_and_status(self.sd.current_state)

    def on_stopped(self, *args, **kwargs):
        """Callback'ed when syncadaemon is stopped."""
        self.stop.hide()
        self.start.show()
        self.start.set_sensitive(True)
        self.connect.set_sensitive(False)

        self._activate_indicator(self.is_started, sensitive=False)
        self._activate_indicator(self.is_connected, sensitive=False)
        self._activate_indicator(self.is_online, sensitive=False)

        self._update_queues_and_status(self.sd.current_state)

    def on_connected(self, *args, **kwargs):
        """Callback'ed when syncadaemon is connected."""
        self.connect.hide()
        self.disconnect.show()
        self.disconnect.set_sensitive(True)
        self._activate_indicator(self.is_connected)
        self._start_loading(self.is_online)
        self._start_loading(self.is_online)

    def on_disconnected(self, *args, **kwargs):
        """Callback'ed when syncadaemon is disconnected."""
        self.disconnect.hide()
        self.connect.show()
        self.connect.set_sensitive(True)

        self._activate_indicator(self.is_connected, sensitive=False)
        self._activate_indicator(self.is_online, sensitive=False)

    def on_online(self, *args, **kwargs):
        """Callback'ed when syncadaemon is online."""
        self.is_online.set_sensitive(True)
        self._activate_indicator(self.is_online)

    def on_offline(self, *args, **kwargs):
        """Callback'ed when syncadaemon is offline."""
        self._activate_indicator(self.is_online, sensitive=False)

    def on_status_changed(self, name=None, description=None,
                          is_error=False, is_connected=True, is_online=True,
                          queues=None, connection=None):
        """Callback'ed when the SD status changed."""
        values = (v for v in (name, description, queues, connection) if v)
        text = self.STATUS_JOINER.join(values)
        if not (text or self.sd.current_state.is_started):
            text = self.STATUS['initial']
        self.status_label.set_text(text)

    def _on_queue_changed(self, queue_name, items, *args, **kwargs):
        """Callback'ed when a queue changed."""
        if items is None:
            items = []

        queue_label = getattr(self, '%sq_label' % queue_name)
        queue_view = getattr(self, '%sq_view' % queue_name)
        queue_store = getattr(self, '%sq_store' % queue_name)
        queue_store.clear()
        for item in items:
            row = (item.operation, item.path, item.share, item.node)
            queue_store.append(row)

        items_len = len(items)
        label = '%s Queue (%i)' % (queue_name.capitalize(), items_len)
        queue_label.set_text(label)

        if not queue_view.is_sensitive() and len(items) > 0:
            queue_view.set_sensitive(True)

    def on_content_queue_changed(self, items, *args, **kwargs):
        """Callback'ed when syncadaemon's content queue changed."""
        self._on_queue_changed(CONTENT_QUEUE, items, args, kwargs)

    def on_meta_queue_changed(self, items, *args, **kwargs):
        """Callback'ed when syncadaemon's meta queue changed."""
        self._on_queue_changed(META_QUEUE, items, args, kwargs)

    # custom

    def _start_loading(self, what):
        """Set a loader animation on 'what'."""
        what.set_sensitive(True)
        what.set_from_animation(self.loading_animation)

    def _activate_indicator(self, what, sensitive=True):
        """Set ready pixbuf on 'what' and make it 'sensitive'."""
        what.set_sensitive(sensitive)
        what.set_from_pixbuf(self.active_indicator)

    def _update_queues_and_status(self, state):
        """Update UI based on SD current state."""
        self.on_meta_queue_changed(self.sd.meta_queue)
        self.on_content_queue_changed(self.sd.content_queue)
        self.on_status_changed(name=state.name, description=state.description,
                               queues=state.queues, connection=state.connection)

    def update(self):
        """Update UI based on SD current state."""
        current_state = self.sd.current_state

        self._activate_indicator(self.is_online,
                                 sensitive=current_state.is_online)

        if current_state.is_started:
            self.on_started()
            if current_state.is_connected:
                self.on_connected()
            else:
                self.on_disconnected()
        else:
            self.on_disconnected()
            self.on_stopped()

        self._update_queues_and_status(current_state)

Generated by  Doxygen 1.6.0   Back to index