Example #1
0
    def testRange(self):
        maxint64 = 2 ** 62 - 1
        minint64 = -2 ** 62 - 1
        maxuint64 = 2 ** 63 - 1

        types = [
            (TYPE_INT, G_MININT, G_MAXINT),
            (TYPE_UINT, 0, G_MAXUINT),
            (TYPE_LONG, G_MINLONG, G_MAXLONG),
            (TYPE_ULONG, 0, G_MAXULONG),
            (TYPE_INT64, minint64, maxint64),
            (TYPE_UINT64, 0, maxuint64),
            ]

        for gtype, min, max in types:
            # Normal, everything is alright
            prop = GObject.property(type=gtype, minimum=min, maximum=max)
            subtype = type('', (GObject.GObject,),
                         dict(prop=prop))
            self.assertEqual(subtype.props.prop.minimum, min)
            self.assertEqual(subtype.props.prop.maximum, max)

            # Lower than minimum
            self.assertRaises(TypeError,
                              GObject.property, type=gtype, minimum=min-1,
                              maximum=max)

            # Higher than maximum
            self.assertRaises(TypeError,
                              GObject.property, type=gtype, minimum=min,
                              maximum=max+1)
Example #2
0
def create_activatables(*specs):
    """
        Convenience for creating activatables
        Each spec is a (kls, extending) tuple:
            * kls: ordinary class that impliments desired methods for activatable
            * extending: Either "App", "Window" or "View"
    """    
    def init(self):
        """
            __init__ given to the created activatables
            Unless overidden by kls.__init__
        """
        GObject.Object.__init__(self)
    
    # Dictionary of {name : activable}
    # Where name is "ViGedit<extending>Activatable"
    created = {}
    
    for kls, extending in specs:
        activatable = "%sActivatable" % extending
        name = "ViGedit%s" % activatable
        
        attrs = {
              '__init__' : init
            , '__gtype_name__' : name
            , extending.lower() : GObject.property(type=getattr(Gedit, extending))
            }
        
        # It would seem it doesn't work if I give kls as another class to inherit from...
        attrs.update(kls.__dict__)
        
        created[name] = type(name, (GObject.Object, getattr(Gedit, activatable)), attrs)
    
    return created
Example #3
0
 def get_translation_unit(self, tab, msg):
     po_file = GObject.property(type=Gtranslator.Po)
     po_file = self.tab.get_po()
     print msg.get_msgid()
     msg = po_file.get_current_message()
     c = msg[0].get_msgid()
     self.panel.set_translation_unit(c)
     self.panel.set_project(self.project)
     self.panel.set_version(self.version)
     self.panel.set_host(self.host)
     print "hola: " + self.panel.get_host()
     # Updating the results
     self.panel.update_data()
    def __init__(self,**kwargs):
        self.element=None
        self.controler=None
        self.active=False
        self.windows=[]
        Gtk.StatusIcon.__init__(self)
        self.start_controler(**kwargs)
        __gtype_name__ = 'TrayiconPlugin'
        object = GObject.property (type=GObject.Object)

        self.set_from_stock (Gtk.STOCK_ABOUT)
        self.connect ("activate", self.trayicon_activate)
        self.connect ("popup_menu", self.trayicon_popup)
        self.set_visible (True)
Example #5
0
class GoPlugin(GObject.Object, Gedit.WindowActivatable):
    __gtype_name__ = "GoPlugin"
    window = GObject.property(type=Gedit.Window)

    def __init__(self):
        GObject.Object.__init__(self)
        self._instances = {}
        self.views = {}
        self.icons = {}
        self.gobin_path = os.getenv("GOBIN", "")
        self.go_path = os.getenv("GOPATH", "")
        self._icons_path = os.path.dirname(
            __file__) + os.sep + "icons" + os.sep
        self._provider = GoProvider(self)
        # load completion icons
        self._load_completion_icons()
        #update path to find gocode
        self.update_path()

    def do_activate(self):
        self.do_update_state()

    def do_deactivate(self):
        for view in self.window.get_views():
            completion = view.get_completion()
            if self._provider in completion.get_providers():
                completion.remove_provider(self._provider)

    def do_update_state(self):
        self.update_ui()

    def update_path(self):
        # make sure $GOBIN is in $PATH
        path = os.environ["PATH"]
        paths = os.getenv("PATH", "").split(":")
        #The person doesn't have a GOBIN setup
        if self.gobin_path == "":
            if self.go_path not in paths:
                path += ":" + self.go_path + os.sep + "bin"
        #The person doesn't have a GOPATH set up
        elif self.go_path == "":
            if self.gobin_path not in paths:
                path += ":" + self.gobin_path
        os.environ["PATH"] = path

    def update_ui(self):
        for view in self.window.get_views():
            completion = view.get_completion()
            if self._provider not in completion.get_providers():
                completion.add_provider(self._provider)

    def _load_completion_icons(self):
        self.icons['var'] = GdkPixbuf.Pixbuf.new_from_file(self._icons_path +
                                                           "var16.png")
        self.icons['const'] = GdkPixbuf.Pixbuf.new_from_file(self._icons_path +
                                                             "const16.png")
        self.icons['func'] = GdkPixbuf.Pixbuf.new_from_file(self._icons_path +
                                                            "func16.png")
        self.icons['interface'] = GdkPixbuf.Pixbuf.new_from_file(
            self._icons_path + "interface16.png")
        self.icons['package'] = GdkPixbuf.Pixbuf.new_from_file(
            self._icons_path + "package16.png")
        self.icons['struct'] = GdkPixbuf.Pixbuf.new_from_file(
            self._icons_path + "struct16.png")
        self.icons['gopher'] = GdkPixbuf.Pixbuf.new_from_file(
            self._icons_path + "gopher16.png")
Example #6
0
class DashboardWindowActivatable(GObject.Object, Gedit.WindowActivatable):

    window = GObject.property(type=Gedit.Window)

    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        self.status_bar = self.window.get_statusbar()
        self._handlers = [
            self.window.connect_after("tab-added", self._add_tab),
            self.window.connect_after("active-tab-changed",
                                      self._toggle_status_bar),
            self.window.connect_after("destroy",
                                      lambda x: self.status_bar.show())
        ]

    def _toggle_status_bar(self, window, tab):
        self.status_bar.show()
        for w in tab.get_children():
            if type(w) == Dashboard:
                self.status_bar.hide()
                break

    def _add_tab(self, window, tab):

        notebook = tab.get_parent()

        doc = tab.get_document()
        uri = doc.get_uri_for_display()

        if not uri.startswith("/"):
            notebook.get_tab_label(tab).get_children()\
                    [0].get_children()[0].get_children()[1].hide()
            self.status_bar.hide()
            d = None
            if str(type(notebook.get_tab_label(tab))) == \
                "<class 'gi.types.__main__.GeditTabLabel'>":
                label = notebook.get_tab_label(tab).get_children()\
                    [0].get_children()[0].get_children()[2]
                text = label.get_text()
                label.set_text("Getting Started")

                def show_doc():
                    notebook.get_tab_label(tab).get_children()\
                        [0].get_children()[0].get_children()[1].show()
                    tab.remove(d)
                    tab.get_children()[0].show()
                    tab.grab_focus()
                    if label.get_text() == "Getting Started":
                        label.set_text(text)
                    self.status_bar.show()

                tab.get_children()[0].hide()
                d = Dashboard(self.window, show_doc)
                tab.pack_start(d, True, True, 0)
                doc.connect("loaded", lambda x, y: show_doc())
                d.search.set_receives_default(True)
                self.window.set_default(d.search)
                d.search.grab_focus()

    def do_deactivate(self):
        tab = self.window.get_active_tab()
        if tab:
            notebook = tab.get_parent()
            for tab in notebook.get_children():
                for w in tab.get_children():
                    if type(w) == Dashboard:
                        tab.remove(w)
                    else:
                        w.show()
                doc = tab.get_document()
                label = notebook.get_tab_label(tab).get_children()\
                    [0].get_children()[0].get_children()[2]
                label.set_text(doc.get_short_name_for_display())

        for handler in self._handlers:
            self.window.disconnect(handler)
        self.window = None
Example #7
0
class CS50Bot(GObject.Object, Gedit.AppActivatable):
    __gtype_name__ = "CS50BotPlugin"

    app = GObject.property(type=Gedit.App)

    # TODO is untouched

    def __init__(self):
        GObject.Object.__init__(self)
        RedisListener(self).start()

    def do_activate(self):
        #views = self.app.get_views()
        #doc = self.window.get_active_document()
        #if not doc:
        #    return
        #doc.goto_line(3)
        pass

    def do_deactivate(self):
        pass

    def get_unsaved(self):
        unsaved = []
        # TODO: file might be unsaved across multiple windows
        for window in self.app.get_windows():
            for doc in window.get_unsaved_documents():
                unsaved.append(doc.get_short_name_for_display())
        return unsaved

    def goto_line(self, filename, line, char):
        # XXX get_windows vs get_main_windows?
        for window in self.app.get_windows():
            for doc in window.get_documents():
                # XXX is this the right comparison to make?
                # add in offset?
                if filename == doc.get_short_name_for_display():
                    doc.goto_line_offset(line, char)

                    tab = Gedit.Tab.get_from_document(doc)
                    window.set_active_tab(tab)

                    # scroll to the targeted line in the document
                    view = tab.get_view()
                    view.scroll_to_cursor()

                    tag_table = doc.get_tag_table()

                    if tag_table.lookup("highlight-error") is None:
                        doc.create_tag("highlight-error",
                                       background="#C9FFFC",
                                       foreground="#000000")
                    else:
                        # delete any previously highlighted lines
                        start = doc.get_start_iter()
                        end = doc.get_end_iter()
                        doc.remove_tag_by_name("highlight-error", start, end)

                    # highlight the line with the error
                    iter1 = doc.get_iter_at_line(line)
                    iter2 = doc.get_iter_at_line(line + 1)
                    doc.apply_tag_by_name("highlight-error", iter1, iter2)

                    break
Example #8
0
class NaiveService (ScheduleManager, GPropSync):
    openaps = None
    OWN_IFACE = IFACE
    PROP_SIGS = {
      'fuel': 'i'
    , 'blah': 's'
    , 'name': 's'
    , 'Name': 's'
    , 'mode': 's'
    , 'status': 'u'
    , 'ini_home': 's'
    , 'homedir': 's'
    }
    blah = gobject.property(type=str)
    homedir = gobject.property(type=str, default=".")
    mode = gobject.property(type=str, flags=gobject.PARAM_READABLE, default='foo')
    heartbeat = None

    @gobject.property(type=int, default=0)
    def status (self):
      """ Status """

      return 0

    # describe_property(dbus_interface=IFACE, type_signature='s') ()
    name = gobject.property(type=str)
    @gobject.property(type=str)
    def Name (self):
      return self.name
    @gobject.property(type=str)
    def ini_home (self):
      return self.homedir
    @ini_home.setter
    def set_ini_home (self, value):
      directory = os.path.realpath(os.path.expanduser(value))
      print "NEW HOME", value, directory, os.path.isdir(directory)
      if not os.path.exists(directory) or not os.path.isdir(directory):
        # raise Exception("Not a directory: %s" % directory)
        raise dbus.exceptions.DBusException(
            self.OWN_IFACE,
            'Directory does not exist, failed to set ini_home to: %s.'
                % directory)
      os.chdir(directory)
      if self.openaps:
        self.InterfacesRemoved(self.openaps.path, self.openaps.GetAll(self.openaps.OWN_IFACE))
        self.openaps.remove_from_connection( )
      self.openaps = managed.Instance(self.bus, self)
      self.InterfacesAdded(self.openaps.path, self.openaps.GetAll(self.openaps.OWN_IFACE))
      self.homedir = directory
    def __init__ (self, loop, bus=None, path=PATH):
        self.loop = loop
        self.bus = bus or dbus.SystemBus( )
        self.path = path
        request = self.bus.request_name(BUS, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
        self.running = False
        ScheduleManager.__init__(self, self.bus, PATH)
        self.sync_all_props( )
        self.init_managed( )
        self.ResetHeartbeat( )
        self.scheduler = scheduler.Scheduler(self.bus, self)
        self.background = doable.Doable(self)
        self.eventsink = triggers.EventSink(self.bus, self)
        # self.connect("notify::ini-home", self.on_change_home)

    def get_all_managed (self):
      things = filter(lambda x: x, [self.background, self.scheduler, self.heartbeat, self.openaps])
      paths = dict( )
      # for thing in self.schedules:
      for thing in things:
        print thing
        spec = { thing.OWN_IFACE:  dict(**thing.GetAll(thing.OWN_IFACE))
               , dbus.INTROSPECTABLE_IFACE:  dict( )
               , dbus.PROPERTIES_IFACE:  dict( ) }
        #if hasattr(thing, 'GetManagedObjects'): spec['org.freedesktop.DBus.ObjectManager']
        paths[thing.path] = spec
      return paths

    @dbus.service.method(dbus_interface=IFACE,
                         in_signature='i',
                         out_signature='i',
                         async_callbacks=('reply_handler',
                                          'error_handler'))
    def Delay (self, seconds, reply_handler, error_handler):
        print "Sleeping for %ds" % seconds
        gobject.timeout_add_seconds (seconds,
                                     lambda: reply_handler (seconds))

    @dbus.service.method(dbus_interface=IFACE,
                         in_signature='', out_signature='')
    def ResetHeartbeat (self):
      if self.heartbeat:
        self.heartbeat.Stop( )
        self.heartbeat.remove_from_connection( )
      self.heartbeat = heartbeat.Heartbeat(self.bus, self)
      print "Set up HEARTBEAT!", self.heartbeat

    @dbus.service.method(dbus_interface=IFACE,
                         in_signature='', out_signature='')
    def Howdy(self):
      print "Howdy!"

    @dbus.service.method(dbus_interface=IFACE,
                         in_signature='', out_signature='s')
    def Start (self):
      print "Howdy!"
      return "OK"
    @dbus.service.signal(dbus_interface=OWN_IFACE,
                         signature='')
    def Quit (self):
      gobject.timeout_add(2, self.loop.quit)
Example #9
0
class View(Gtk.EventBox):
    __gsignals__ = {
        'frame-changed': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE,
                          2 * [GObject.TYPE_PYOBJECT]),
        'ground-changed':
        (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [GObject.TYPE_PYOBJECT]),
        'sound-changed':
        (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [GObject.TYPE_PYOBJECT])
    }

    def set_frame(self, value):
        tape_num, frame = value

        if frame == None:
            clean(tape_num)
            self._tape[tape_num].get_child().set_from_pixbuf(theme.EMPTY_THUMB)

            if self._emission:
                self.emit('frame-changed', tape_num, None)
        else:
            if not frame.select():
                return False

            Document.tape[tape_num] = frame
            self._tape[tape_num].get_child().set_from_pixbuf(frame.thumb())

            if frame.custom():
                index = [
                    i for i, f in enumerate(char.THEMES[-1].frames)
                    if f == frame
                ][0]
                if index >= len(self._frames):
                    first = index / theme.FRAME_COLS * theme.FRAME_COLS
                    for i in range(first, first + theme.FRAME_COLS):
                        self._add_frame(i)

            if self._char.custom():
                self._frames[index].set_from_pixbuf(frame.thumb())

            if self._emission:
                self.emit('frame-changed', tape_num, frame)

        if self._tape_selected == tape_num:
            self._tape_cb(None, None, tape_num)

        return True

    def set_ground(self, value):
        self._set_combo(self._ground_combo, value)

    def set_sound(self, value):
        self._set_combo(self._sound_combo, value)

    def get_emittion(self):
        return self._emission

    def set_emittion(self, value):
        self._emission = value

    frame = GObject.property(type=object, getter=None, setter=set_frame)
    ground = GObject.property(type=object, getter=None, setter=set_ground)
    sound = GObject.property(type=object, getter=None, setter=set_sound)
    emittion = GObject.property(type=bool,
                                default=True,
                                getter=get_emittion,
                                setter=set_emittion)

    def restore(self):
        def new_combo(themes, cb, object=None, closure=None):
            combo = ComboBox()
            sel = 0

            for i, o in enumerate(themes):
                if o:
                    combo.append_item(o,
                                      text=o.name,
                                      size=(theme.THUMB_SIZE,
                                            theme.THUMB_SIZE),
                                      pixbuf=o.thumb())
                    if object and o.name == object.name:
                        sel = i
                else:
                    combo.append_separator()

            combo.connect('changed', cb, closure)
            combo.set_active(sel)
            combo.show()

            return combo

        self.controlbox.pack_start(new_combo(char.THEMES, self._char_cb),
                                   False, False, 0)
        self._ground_combo = new_combo(ground.THEMES, self._combo_cb,
                                       Document.ground, self._ground_cb)
        self.controlbox.pack_start(self._ground_combo, False, False, 0)
        self._sound_combo = new_combo(sound.THEMES, self._combo_cb,
                                      Document.sound, self._sound_cb)
        self.controlbox.pack_start(self._sound_combo, False, False, 0)

        for i in range(theme.TAPE_COUNT):
            self._tape[i].get_child().set_from_pixbuf(Document.tape[i].thumb())
        self._tape_cb(None, None, 0)

    def play(self):
        self._play_tape_num = 0
        self._playing = GObject.timeout_add(self._delay, self._play_tape)

    def stop(self):
        self._playing = None
        self._screen.fgpixbuf = Document.tape[self._tape_selected].orig()
        self._screen.draw()

    def set_tempo(self, tempo):
        logger.debug('carto')
        logger.debug(tempo)
        self._delay = 10 + (10 - int(tempo)) * 100
        if self._playing:
            GObject.source_remove(self._playing)
            self._playing = GObject.timeout_add(self._delay, self._play_tape)

    def __init__(self):
        GObject.GObject.__init__(self)

        self._screen = Screen()
        self._play_tape_num = 0
        self._playing = None
        self._delay = 3 * 150
        self._tape_selected = -1
        self._tape = []
        self._char = None
        self._frames = []
        self._prev_combo_selected = {}
        self._emission = True
        self._screen_size_id = None

        # frames table

        self.table = Gtk.Table(  # theme.FRAME_ROWS, columns=theme.FRAME_COLS,
            homogeneous=False)

        for i in range(theme.FRAME_ROWS * theme.FRAME_COLS):
            self._add_frame(i)

        # frames box

        table_scroll = VScrolledBox()
        table_scroll.set_viewport(self.table)
        table_scroll.modify_bg(Gtk.StateType.NORMAL,
                               Gdk.color_parse(BUTTON_BACKGROUND))

        yellow_frames = Gtk.EventBox()
        yellow_frames.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(YELLOW))
        table_frames = Gtk.EventBox()
        table_frames.modify_bg(Gtk.StateType.NORMAL,
                               Gdk.color_parse(BACKGROUND))
        table_frames.set_border_width(5)
        table_frames.add(table_scroll)
        yellow_frames.add(table_frames)

        yelow_arrow = Gtk.Image()
        yelow_arrow.set_from_file(theme.path('icons', 'yellow_arrow.png'))

        frames_box = Gtk.VBox()
        frames_box.pack_start(yellow_frames, True, True, 0)
        frames_box.pack_start(yelow_arrow, False, False, 0)
        frames_box.props.border_width = theme.BORDER_WIDTH

        # screen

        screen_pink = Gtk.EventBox()
        screen_pink.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(PINK))
        screen_box = Gtk.EventBox()
        screen_box.set_border_width(5)
        screen_box.add(self._screen)
        screen_pink.add(screen_box)
        screen_pink.props.border_width = theme.BORDER_WIDTH

        # tape

        tape = Gtk.HBox()

        for i in range(TAPE_COUNT):
            frame_box = Gtk.VBox()

            filmstrip_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
                theme.path('icons', 'filmstrip.png'), THUMB_SIZE, -1, False)

            filmstrip = Gtk.Image()
            filmstrip.set_from_pixbuf(filmstrip_pixbuf)
            frame_box.pack_start(filmstrip, False, False, 0)

            frame = Gtk.EventBox()
            frame.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
            frame.connect('button_press_event', self._tape_cb, i)
            frame.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(BLACK))
            frame.modify_bg(Gtk.StateType.PRELIGHT, Gdk.color_parse(BLACK))
            frame.props.border_width = 2
            frame.set_size_request(theme.THUMB_SIZE, theme.THUMB_SIZE)
            frame_box.pack_start(frame, True, True, 0)
            self._tape.append(frame)

            frame_image = Gtk.Image()
            frame_image.set_from_pixbuf(theme.EMPTY_THUMB)
            frame.add(frame_image)

            filmstrip = Gtk.Image()
            filmstrip.set_from_pixbuf(filmstrip_pixbuf)
            frame_box.pack_start(filmstrip, False, False, 0)

            tape.pack_start(frame_box, False, False, 0)

        # left control box

        self.controlbox = Gtk.VBox()
        self.controlbox.props.border_width = theme.BORDER_WIDTH
        self.controlbox.props.spacing = theme.BORDER_WIDTH

        leftbox = Gtk.VBox()
        logo = Gtk.Image()
        logo.set_from_file(theme.path('icons', 'logo.png'))
        leftbox.set_size_request(logo.props.pixbuf.get_width(), -1)
        leftbox.pack_start(logo, False, False, 0)
        leftbox.pack_start(self.controlbox, True, True, 0)

        # screen box

        screen_alignment = Gtk.Alignment.new(0.5, 0.5, 0, 0)
        screen_alignment.add(screen_pink)

        box = Gtk.EventBox()
        box.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(BACKGROUND))
        box.connect('size-allocate', self._screen_size_cb, screen_pink)
        box.add(screen_alignment)

        cetralbox = Gtk.HBox()
        cetralbox.pack_start(box, expand=True, fill=True, padding=0)
        cetralbox.pack_start(frames_box, expand=False, fill=True, padding=0)

        hdesktop = Gtk.HBox()
        hdesktop.pack_start(leftbox, False, True, 0)
        hdesktop.pack_start(cetralbox, True, True, 0)

        # tape box

        arrow = Gtk.Image()
        arrow.set_from_file(theme.path('icons', 'pink_arrow.png'))
        tape_pink = Gtk.EventBox()
        tape_pink.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(PINK))
        tape_bg = Gtk.EventBox()
        tape_bg.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(BACKGROUND))
        tape_bg.set_border_width(5)
        tape_bg.add(tape)
        tape_pink.add(tape_bg)

        tape_hbox = Gtk.HBox()
        tape_hbox.pack_start(tape_pink, True, False, 0)

        tape_box = Gtk.VBox()
        tape_box.props.border_width = theme.BORDER_WIDTH
        tape_box.pack_start(arrow, False, False, 0)
        tape_box.pack_start(tape_hbox, True, True, 0)

        desktop = Gtk.VBox()
        desktop.pack_start(hdesktop, True, True, 0)
        desktop.pack_start(tape_box, False, False, 0)

        greenbox = Gtk.EventBox()
        greenbox.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(BACKGROUND))
        greenbox.set_border_width(5)
        greenbox.add(desktop)

        self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(YELLOW))
        self.add(greenbox)
        self.show_all()

    def _set_combo(self, combo, value):
        pos = -1

        for i, item in enumerate(combo.get_model()):
            if item[0] == value:
                pos = i
                break

        if pos == -1:
            combo.append_item(value,
                              text=value.name,
                              size=(theme.THUMB_SIZE, theme.THUMB_SIZE),
                              pixbuf=value.thumb())
            pos = len(combo.get_model()) - 1

        combo.set_active(pos)

    def _play_tape(self):
        if not self._playing:
            return False

        self._screen.fgpixbuf = Document.tape[self._play_tape_num].orig()
        self._screen.draw()

        for i in range(theme.TAPE_COUNT):
            self._play_tape_num += 1
            if self._play_tape_num == TAPE_COUNT:
                self._play_tape_num = 0
            if Document.tape[self._play_tape_num].empty():
                continue
            return True

        return True

    def _add_frame(self, index):
        y = index / theme.FRAME_COLS
        x = index - y * theme.FRAME_COLS
        logger.debug('add new frame x=%d y=%d index=%d' % (x, y, index))

        image = Gtk.Image()
        image.show()
        image.set_from_pixbuf(theme.EMPTY_THUMB)
        self._frames.append(image)

        image_box = Gtk.EventBox()
        image_box.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        image_box.connect('button_press_event', self._frame_cb, index)
        image_box.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(BLACK))
        image_box.modify_bg(Gtk.StateType.PRELIGHT, Gdk.color_parse(BLACK))
        image_box.props.border_width = 2
        image_box.set_size_request(theme.THUMB_SIZE, theme.THUMB_SIZE)
        image_box.add(image)

        if self._char and self._char.custom():
            image_box.show()

        self.table.attach(image_box, x, x + 1, y, y + 1)

        return image

    def _tape_cb(self, widget, event, index):
        if event and event.button == 3:
            self.set_frame((index, None))
            return

        tape = self._tape[index]
        tape.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(YELLOW))
        tape.modify_bg(Gtk.StateType.PRELIGHT, Gdk.color_parse(YELLOW))

        if self._tape_selected != index:
            if self._tape_selected != -1:
                old_tape = self._tape[self._tape_selected]
                old_tape.modify_bg(Gtk.StateType.NORMAL,
                                   Gdk.color_parse(BLACK))
                old_tape.modify_bg(Gtk.StateType.PRELIGHT,
                                   Gdk.color_parse(BLACK))

        self._tape_selected = index
        self._screen.fgpixbuf = Document.tape[index].orig()
        self._screen.draw()

    def _frame_cb(self, widget, event, i):
        if event.button == 3:
            self._char.clean(i)
            self._frames[i].set_from_pixbuf(self._char.frames[i].thumb())
        else:
            if i < len(self._char.frames):
                frame = self._char.frames[i]
                if not self.set_frame((self._tape_selected, frame)):
                    return
            else:
                frame = None
                self.set_frame((self._tape_selected, None))

    def _char_cb(self, widget, closure):
        self._char = widget.props.value
        for i in range(len(self._frames)):
            if i < len(self._char.frames):
                self._frames[i].set_from_pixbuf(self._char.frames[i].thumb())
                self._frames[i].get_parent().show()
            else:
                self._frames[i].get_parent().hide()

    def _combo_cb(self, widget, cb):
        choice = widget.props.value.select()

        if not choice:
            widget.set_active(self._prev_combo_selected[widget])
            return

        if id(choice) != id(widget.props.value):
            widget.append_item(choice,
                               text=choice.name,
                               size=(theme.THUMB_SIZE, theme.THUMB_SIZE),
                               pixbuf=choice.thumb())
            widget.set_active(len(widget.get_model()) - 1)

        self._prev_combo_selected[widget] = widget.get_active()
        cb(choice)

    def _ground_cb(self, choice):
        self._screen.bgpixbuf = choice.orig()
        self._screen.draw()
        Document.ground = choice
        if self._emission:
            self.emit('ground-changed', choice)

    def _sound_cb(self, choice):
        Document.sound = choice
        if self._emission:
            self.emit('sound-changed', choice)

    def _screen_size_cb(self, sender, aloc, widget):
        def set_size():
            size = min(aloc.width, aloc.height)
            widget.set_size_request(size, size)
            self._screen_size_id = None
            return False

        if self._screen_size_id is not None:
            GObject.source_remove(self._screen_size_id)
        self._screen_size_id = GObject.timeout_add(500, set_size)
Example #10
0
class PlaylistIdentifierPlugin(GObject.Object, Peas.Activatable):
    object = GObject.property(type=GObject.Object)

    def __init__(self):
        super(PlaylistIdentifierPlugin, self).__init__()
        GObject.Object.__init__(self)

    def do_activate(self):
        data = dict()
        shell = self.object
        manager = shell.props.ui_manager

        data['action_group'] = Gtk.ActionGroup(
            name='ShowPlaylistsForTrackActions')

        action = Gtk.Action(
            name='ShowPlaylistsForTrack',
            label=_("_Show Playlists For Track"),
            tooltip=_("Show the containing playlists for the selected track"),
            stock_id='gnome-mime-text-x-python')
        action.connect('activate', self._show_playlists_for_track, shell)
        data['action_group'].add_action(action)

        manager.insert_action_group(data['action_group'], 0)
        data['ui_id'] = manager.add_ui_from_string(ui_str)
        manager.ensure_update()

        shell.set_data('ShowPlaylistsForTrackInfo', data)

    def do_deactivate(self):
        shell = self.object
        data = shell.get_data('ShowPlaylistsForTrackInfo')

        manager = shell.props.ui_manager
        manager.remove_ui(data['ui_id'])
        manager.remove_action_group(data['action_group'])
        manager.ensure_update()

        shell.set_data('ShowPlaylistsForTrackInfo', None)

    def _show_playlists_for_track(self, action, shell):
        playlists = self._get_playlists_for_selected_track(shell)

        dialog = Gtk.Dialog("Playlists", None, 0,
                            (Gtk.STOCK_OK, Gtk.ResponseType.YES,
                             Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))

        # add labels for all playlists
        for playlist in playlists:
            playlist_label = Gtk.Label(playlist)
            dialog.vbox.pack_start(playlist_label, True, True, 0)
            playlist_label.show()

        # build and show dialog
        box1 = Gtk.HBox(False, 0)
        dialog.vbox.pack_start(box1, True, True, 0)
        box1.show()
        response = dialog.run()

        # cleanup
        dialog.destroy()
        while Gtk.events_pending():
            Gtk.main_iteration()

    def _get_playlists_for_selected_track(self, shell):
        page = shell.props.selected_page
        if not hasattr(page, "get_entry_view"):
            return
        selected = page.get_entry_view().get_selected_entries()
        if selected != []:
            uri = selected[0].get_playback_uri()
            return self._get_playlists_for_uri(shell, uri)

    def _get_playlists_for_uri(self, shell, uri):
        playlist_model_entries = shell.props.playlist_manager.get_playlists()
        if playlist_model_entries:
            playlists = []
            for playlist in playlist_model_entries:
                playlist_rows = playlist.get_query_model()
                for row in playlist_rows:
                    if row[0].get_string(RB.RhythmDBPropType.LOCATION) == uri:
                        playlists.append(playlist.props.name)
                        break
            return playlists
        else:
            return []
Example #11
0
class ShoebotPlugin(GObject.Object, Editor.WindowActivatable,
                    PeasGtk.Configurable, GioActionHelperMixin):
    __gtype_name__ = "ShoebotPlugin"
    window = GObject.property(type=Editor.Window)

    def __init__(self):
        GObject.Object.__init__(self)

        self.changed_handler_id = None
        self.idle_handler_id = None

        self.text = None
        self.live_text = None

        self.id_name = 'ShoebotPluginID'
        self.bot = None

    def do_activate(self):
        self.add_output_widgets()
        self.add_window_actions()

    def create_scrollable_textview(self, name):
        """
        Create a Gtk.TextView inside a Gtk.ScrolledWindow

        :return: container, text_view
        """
        text_view = Gtk.TextView()
        text_view.set_editable(False)

        font_desc = Pango.FontDescription("Monospace")
        text_view.modify_font(font_desc)
        text_view.set_name(name)

        buff = text_view.get_buffer()
        buff.create_tag('error', foreground='red')

        container = Gtk.ScrolledWindow()
        container.add(text_view)
        container.show_all()
        return container, text_view

    def add_output_widgets(self):
        self.output_container, self.text = self.create_scrollable_textview(
            "shoebot-output")
        self.live_container, self.live_text = self.create_scrollable_textview(
            "shoebot-live")
        self.panel = self.window.get_bottom_panel()

        self.panel.add_titled(self.output_container, _('Shoebot'),
                              _('Shoebot'))
        self.panel.add_titled(self.live_container, _('Shoebot Live'),
                              _('Shoebot Live'))

    def create_example_actions(self, examples):
        for rel_path in examples:
            action = Gio.SimpleAction.new(
                "open_example__%s" % encode_relpath(rel_path), None)

            action.connect("activate", self.on_open_example, rel_path)
            self.window.add_action(action)

    def add_window_actions(self):
        self.create_example_actions(EXAMPLES)
        self.create_actions(MENU_ACTIONS)

    def on_run(self, action, user_data):
        self.start_shoebot()

    def on_open_example(self, action, user_data, rel_path):
        example_dir = preferences.example_dir
        path = os.path.join(example_dir, rel_path)

        drive, directory = os.path.splitdrive(
            os.path.abspath(os.path.normpath(path)))
        uri = "file://%s%s" % (drive, directory)
        gio_file = Gio.file_new_for_uri(uri)
        self.window.create_tab_from_location(
            gio_file,
            None,  # encoding
            0,
            0,  # column
            False,  # Do not create an empty file
            True)  # Switch to the tab

    def start_shoebot(self):
        sbot_bin = preferences.shoebot_binary
        if not sbot_bin:
            textbuffer = self.text.get_buffer()
            textbuffer.set_text('Cannot find sbot in path.')
            while Gtk.events_pending():
                Gtk.main_iteration()
            return False

        if self.bot and self.bot.process.poll() is not None:
            self.bot.send_command("quit")

        # get the text buffer
        doc = self.window.get_active_document()
        if not doc:
            return

        title = '%s - Shoebot on %s' % (doc.get_short_name_for_display(),
                                        EDITOR_NAME)
        cwd = os.path.dirname(doc.get_uri_for_display()) or None

        start, end = doc.get_bounds()
        source = doc.get_text(start, end, False)
        if not source:
            return False

        textbuffer = self.text.get_buffer()
        textbuffer.set_text('running shoebot at %s\n' % sbot_bin)

        while Gtk.events_pending():
            Gtk.main_iteration()

        self.disconnect_change_handler(doc)
        self.changed_handler_id = doc.connect("changed", self.doc_changed)

        self.bot = ShoebotProcess(source,
                                  self.socket_server_enabled,
                                  self.var_window_enabled,
                                  self.full_screen_enabled,
                                  self.verbose_output_enabled,
                                  title,
                                  cwd=cwd,
                                  sbot=sbot_bin)
        self.idle_handler_id = GObject.idle_add(self.update_shoebot)

    def disconnect_change_handler(self, doc):
        if self.changed_handler_id is not None:
            doc.disconnect(self.changed_handler_id)
            self.changed_handler_id = None

    def get_source(self, doc):
        """
        Grab contents of 'doc' and return it

        :param doc: The active document
        :return:
        """
        start_iter = doc.get_start_iter()
        end_iter = doc.get_end_iter()
        source = doc.get_text(start_iter, end_iter, False)
        return source

    def doc_changed(self, *args):
        if self.live_coding_enabled and self.bot:
            doc = self.window.get_active_document()
            source = self.get_source(doc)

            try:
                self.bot.live_source_load(source)
            except Exception:
                self.bot = None
                self.disconnect_change_handler(doc)
                raise
            except IOError as e:
                self.bot = None
                self.disconnect_change_handler()
                if e.errno == errno.EPIPE:
                    # EPIPE error
                    sys.write('FIXME: %s\n' % str(e))
                else:
                    # Something else bad happened
                    raise

    def update_shoebot(self):
        if self.bot:
            textbuffer = self.text.get_buffer()
            for stdout_line, stderr_line in self.bot.get_output():
                if stdout_line is not None:
                    textbuffer.insert(textbuffer.get_end_iter(), stdout_line)
                if stderr_line is not None:
                    # Use the 'error' tag so text is red
                    textbuffer.insert(textbuffer.get_end_iter(), stderr_line)
                    offset = textbuffer.get_char_count() - len(stderr_line)
                    start_iter = textbuffer.get_iter_at_offset(offset)
                    end_iter = textbuffer.get_end_iter()
                    textbuffer.apply_tag_by_name("error", start_iter, end_iter)
            self.text.scroll_to_iter(textbuffer.get_end_iter(), 0.0, True, 0.0,
                                     0.0)

            textbuffer = self.live_text.get_buffer()
            for response in self.bot.get_command_responses():
                if response is None:
                    # sentinel value - clear the buffer
                    textbuffer.delete(textbuffer.get_start_iter(),
                                      textbuffer.get_end_iter())
                else:
                    cmd, status, info = response.cmd, response.status, response.info
                    if cmd == CMD_LOAD_BASE64:
                        if status == RESPONSE_CODE_OK:
                            textbuffer.delete(textbuffer.get_start_iter(),
                                              textbuffer.get_end_iter())
                            # TODO switch panels to 'Shoebot' if on 'Shoebot Live'
                        elif status == RESPONSE_REVERTED:
                            textbuffer.insert(
                                textbuffer.get_end_iter(),
                                '\n'.join(info).replace('\\n', '\n'))

            while Gtk.events_pending():
                Gtk.main_iteration()

        if self.bot:
            return self.bot.running
        else:
            return False

    def on_toggle_live_coding(self, action, user_data):
        panel = self.window.get_bottom_panel()
        if self.live_coding_enabled and self.bot:
            doc = self.window.get_active_document()
            source = self.get_source(doc)
            self.bot.live_source_load(source)

            panel.add_titled(self.live_container, 'Shoebot Live',
                             'Shoebot Live')
        else:
            panel.remove(self.live_container)

    def do_deactivate(self):
        self.panel.remove(self.live_container)
        self.panel.remove(self.output_container)

    def do_create_configure_widget(self):
        widget = ShoebotPreferences()
        return widget
class MobileModePlugin (GObject.Object,
        Liferea.ShellActivatable, PeasGtk.Configurable):
    __gtype_name__ = "MobileModePlugin"

    shell = GObject.property(type=Liferea.Shell)

    _shell = None

    def __init__(self):
        GObject.Object.__init__(self)
        self.gesture_valid_region = 0.3  # valid region for drag start
        self.gesture_threshold = 10

    def do_activate(self):
        """Override Peas Plugin entry point"""
        if self._shell is None:
            MobileModePlugin._shell = self.props.shell
        left_pane = get_widget_by_name(self.main_win, "leftpane")
        normal_view_pane = get_widget_by_name(self.main_win, "normalViewPane")

        self.left_pane = left_pane
        self.normal_view_pane = normal_view_pane

        self.drag_gesture = Gtk.GestureDrag.new(self.main_win)
        self.drag_gesture.props.propagation_phase = Gtk.PropagationPhase.CAPTURE
        self.drag_end_cid = self.drag_gesture.connect("drag-end", self.on_main_win_drag_end)

        self.build_compact_menu()
        self.load_actions()

    def do_deactivate(self):
        """Peas Plugin exit point"""
        self.drag_gesture.disconnect(self.drag_end_cid)
        self.drag_end_cid = -1
        self.reset_panes()

        builder = self.shell.get_property("builder")
        status_bar = builder.get_object("statusbar")
        Gtk.Container.remove(status_bar, self.menu_dropdown)
        self.main_win.set_show_menubar(True)

    def build_compact_menu(self):
        """Hide main menubar while create a hamburg button on status bar
        Borrowed from the Header Bar plugin

        """
        # hamburg button
        button = Gtk.MenuButton()
        icon = Gio.ThemedIcon(name="open-menu-symbolic")
        image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
        builder = self.shell.get_property("builder")
        button.set_menu_model(builder.get_object("menubar"))
        button.add(image)
        button.show_all()

        self.main_win.set_show_menubar(False)
        status_bar = builder.get_object("statusbar")
        status_bar.pack_end(button, False, False, 0)

        self.menu_dropdown = button

    def show_feed_list(self):
        """Show the feedlist widget"""
        pane_width = self.left_pane.get_allocated_width()
        if self.left_pane.props.position != pane_width:
            self.left_pane.props.position = pane_width

    def show_item_list(self):
        """Show the itemlist widget"""
        if self.left_pane.props.position != 0:
            self.left_pane.props.position = 0

        pane_height = self.normal_view_pane.get_allocated_height()
        if self.normal_view_pane.props.position != pane_height:
            self.normal_view_pane.props.position = pane_height

    def show_item(self):
        """Show the item view widget"""
        if self.left_pane.props.position != 0:
            self.left_pane.props.position = 0

        if self.normal_view_pane.props.position != 0:
            self.normal_view_pane.props.position = 0

    def show_left(self):
        """Show the view to the left of current view"""
        if self.view_mode == ViewMode.ITEM:
            self.show_item_list()
        elif self.view_mode == ViewMode.ITEM_LIST:
            self.show_feed_list()
        else:
            self.show_feed_list()

    def show_right(self):
        """Show the view to the right of current view"""
        if self.view_mode == ViewMode.FEED_LIST:
            self.show_item_list()
        elif self.view_mode == ViewMode.ITEM_LIST:
            self.show_item()
        else:
            self.show_item_list()

    def reset_panes(self):
        """Reset the panes to a proper default position"""
        pane_width = self.left_pane.get_allocated_width()
        self.left_pane.props.position = pane_width // 3
        pane_height = self.normal_view_pane.get_allocated_height()
        self.normal_view_pane.props.position = pane_height * 2 // 5

    def on_main_win_drag_end(self, gesture, offset_x, offset_y, *udata):
        """Handler for drag-end event."""

        # too close for a drag
        if (abs(offset_x) < self.gesture_threshold and
                abs(offset_y) < self.gesture_threshold):
            return

        res, start_x, start_y = gesture.get_start_point()
        main_height = self.main_win.get_allocated_height()
        # print(main_height, start_y, main_height - start_y, main_height * self.valid_region)
        if (main_height - start_y) > int(main_height * self.gesture_valid_region):
            return

        if offset_x != 0:
            tan = abs(offset_y / offset_x)
        else:
            tan = 57  # 89deg
        if tan > TAN_VERT:  # vertical
            if offset_y < 0:
                # vertical up drag
                if self.view_mode == ViewMode.ITEM:
                    self.do_action("next-unread-item")
                else:
                    pass
        elif tan < TAN_HORI:  # horizontal
            if offset_x > 0:
                self.show_left()
            else:
                self.show_right()
        elif offset_y < 0:  # angle up
            if offset_x < 0:
                self._step_item("up")
            else:
                self._step_item("down")

    @property
    def view_mode(self):
        if self.left_pane.props.position == self.normal_view_pane.props.position == 0:
            mode = ViewMode.ITEM
        elif self.left_pane.props.position == 0:
            mode = ViewMode.ITEM_LIST
        elif self.normal_view_pane == 0:
            mode = ViewMode.FEED_LIST
        else:
            mode = ViewMode.UNKNOWN
        return mode

    @property
    def main_win(self):
        window = self._shell.get_window()
        return window

    @property
    def gapp(self):
        app = self.main_win.get_application()
        return app

    @property
    def itemlist_treeview(self):
        itemlist_view = self._shell.props.item_view.props.item_list_view
        tview = itemlist_view.get_widget().get_child()
        return tview

    @property
    def main_webkit_view(self):
        """Return the webkit webview in the item_view"""
        shell = self._shell
        item_view = shell.props.item_view
        if not item_view:
            print("Item view not found!")
            return None

        htmlv = item_view.props.html_view
        if not htmlv:
            print("HTML view not found!")
            return None

        return htmlv.props.renderwidget

    @property
    def current_webviews(self):
        """Get all the available webviews """
        views = []
        webkit_view = self.main_webkit_view
        if webkit_view is None:
            return views
        views.append(webkit_view)

        browser_tabs = self._shell.props.browser_tabs
        tab_infos = browser_tabs.props.tab_info_list
        if tab_infos:
            box_in_tabs = [x.htmlview for x in tab_infos]
            html_in_tabs = [x.get_widget() for x in box_in_tabs]
            views.extend(html_in_tabs)
        return views

    def do_action(self, action_name, values=None):
        """
        values: action parameters as a single value: 1, "some text", (1, True)
        """
        win = self._shell.get_window()
        gapp = win.props.application
        for action_map_group in [win, gapp]:
            action = action_map_group.lookup_action(action_name)
            if action is None:
                action = action_map_group.lookup_action(action_name.replace("_", "-"))

            if action is not None:
                break
        if action is None:
            log_error(f"Unknown action {action_name}")
            return
        # log_error("doing", action_name)
        vtype = action.get_parameter_type()
        if (vtype is None) != (values is None):
            log_error(f"Action {action_name} param type don't confirm values")
            return

        param = None
        if vtype:
            param = GLib.Variant(vtype.dup_string(), values)
        action.activate(param)

    def load_actions(self):
        """Setup actions and add default shortcuts"""
        window = self.main_win
        simple_action_names = [
                "mobile_mode_show_feed_list",
                "mobile_mode_show_item_list",
                "mobile_mode_show_item",
                "mobile_mode_show_left",
                "mobile_mode_show_right",
        ]

        accel_maps = [
                # ["win.webkit_page_up", ["BackSpace"]],
        ]

        # action name cannot have "-" in it.
        # action callbacks prefixed with "action_"
        action_entries = [[x.replace("_", "-"),
            getattr(self, "action_" + x, None)] for x in simple_action_names]

        add_action_entries(window, action_entries)
        for act, accels in accel_maps:
            try:
                act = act.replace("_", "-")
                self.gapp.set_accels_for_action(act, accels)
            except Exception as e:
                log_error(e)

    def action_mobile_mode_show_feed_list(self, action, param):
        """action to show feed list in mobile mode"""
        self.show_feed_list()

    def action_mobile_mode_show_item_list(self, action, param):
        """action to show item list in mobile mode"""
        self.show_feed_list()

    def action_mobile_mode_show_item(self, action, param):
        """action to show item in mobile mode"""
        self.show_feed_list()

    def action_mobile_mode_show_left(self, action, param):
        """action to show left view in mobile mode"""
        self.show_left()

    def action_mobile_mode_show_right(self, action, param):
        """action to show right view in mobile mode"""
        self.show_right()

    def _get_next_iter(self, direct="down"):
        tree = self.itemlist_treeview
        model = tree.props.model
        path, col = tree.get_cursor()
        if path is None:
            if direct == "down":
                miter = model.get_iter_first()
            else:
                num_kids = model.iter_n_children(None)
                miter = model.iter_nth_child(None, num_kids - 1)
        else:
            miter = model.get_iter(path)
            if not miter: return
            if direct == "down":
                miter = model.iter_next(miter)
            else:
                miter = model.iter_previous(miter)
        return miter

    def _step_item(self, direct="down"):
        miter = self._get_next_iter(direct)
        if miter:
            tree = self.itemlist_treeview
            model = tree.props.model
            tree.grab_focus()
            path = model.get_path(miter)
            tree.set_cursor(path, None, False)
Example #13
0
class ChangeIndentPlugin(GObject.Object, Gedit.WindowActivatable,
                         PeasGtk.Configurable):
    __gtype_name__ = 'ChangeIndentPlugin'
    window = GObject.property(type=Gedit.Window)

    # config
    config = ConfigParser.ConfigParser()
    config_file = 'indent.cfg'
    spaces = 2
    tab = False

    def __init__(self):
        GObject.Object.__init__(self)
        self._get_config()

    def _add_ui(self):
        manager = self.window.get_ui_manager()
        self._actions = Gtk.ActionGroup('ChangeIndentActions')
        self._actions.add_actions([
            ('ChangeIndentPlugin', Gtk.STOCK_INFO, 'Change Indent',
             '<control><alt>i', 'Change indent in current document',
             self.on_change_indent),
        ])
        manager.insert_action_group(self._actions)
        self._ui_merge_id = manager.add_ui_from_string(UI_XML)
        manager.ensure_update()

    def do_activate(self):
        self._add_ui()

    def do_deactivate(self):
        self._remove_ui()

    def do_update_state(self):
        pass

    def do_create_configure_widget(self):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        box.set_border_width(20)

        label = Gtk.Label('Change Indent Configuration (Tab to spaces).')
        box.pack_start(label, False, False, 0)

        section = Gtk.Box(spacing=10)
        label = Gtk.Label('Spaces')
        section.pack_start(label, False, False, 0)

        adjustment = Gtk.Adjustment(self.spaces, 2, 10, 1, 10, 0)
        spinbutton = Gtk.SpinButton()
        spinbutton.set_adjustment(adjustment)
        spinbutton.connect("value-changed", self.on_spaces_value_changed)
        section.pack_start(spinbutton, False, False, 0)

        change_tab = Gtk.CheckButton("Spaces to Tab")
        change_tab.connect("toggled", self.on_change_tab_toggled)
        change_tab.set_active(True if self.tab == 1 else False)
        section.pack_start(change_tab, False, False, 0)

        box.pack_start(section, False, False, 0)
        return box

    def on_spaces_value_changed(self, button):
        self.spaces = int(button.get_value())
        self._save_config()

    def on_change_tab_toggled(self, button):
        self.tab = button.get_active()
        self._save_config()

    def on_change_indent(self, action, data=None):
        self._get_config()

        doc = self.window.get_active_document()
        text = ''

        builded_spaces = ''
        for i in range(self.spaces):
            builded_spaces += ' '

        if doc:
            start, end = doc.get_bounds()
            text = doc.get_text(start, end, False)

            stripped_text = []
            for line in text.split('\n'):
                if self.tab:
                    stripped_text.append(line.replace(builded_spaces, '\t'))
                else:
                    stripped_text.append(line.replace('\t', builded_spaces))

            doc.set_text('\n'.join(stripped_text))

    def _get_config(self):
        self.config.read(self.config_file)
        if self.config.has_option('settings', 'tab'):
            self.tab = self.config.getint('settings', 'tab')

        if self.config.has_option('settings', 'spaces'):
            self.spaces = self.config.getint('settings', 'spaces')

    def _save_config(self):
        f = open(self.config_file, 'w')
        if not self.config.has_section('settings'):
            self.config.add_section('settings')

        self.config.set('settings', 'tab', 1 if self.tab else 0)
        self.config.set('settings', 'spaces', self.spaces)
        self.config.write(f)
        f.close()

    def _remove_ui(self):
        manager = self.window.get_ui_manager()
        manager.remove_ui(self._ui_merge_id)
        manager.remove_action_group(self._actions)
        manager.ensure_update()
Example #14
0
class Magnatune(GObject.GObject, Peas.Activatable):
    __gtype_name__ = 'Magnatune'
    object = GObject.property(type=GObject.GObject)

    def __init__(self):
        GObject.GObject.__init__(self)

    def download_album_action_cb(self, action, parameter):
        shell = self.object
        shell.props.selected_page.download_album()

    def artist_info_action_cb(self, action, parameter):
        shell = self.object
        shell.props.selected_page.display_artist_info()

    def do_activate(self):
        shell = self.object
        self.db = shell.props.db

        rb.append_plugin_source_path(self, "icons")

        self.entry_type = MagnatuneEntryType()
        self.db.register_entry_type(self.entry_type)

        self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")

        app = Gio.Application.get_default()
        action = Gio.SimpleAction(name="magnatune-album-download")
        action.connect("activate", self.download_album_action_cb)
        app.add_action(action)

        action = Gio.SimpleAction(name="magnatune-artist-info")
        action.connect("activate", self.artist_info_action_cb)
        app.add_action(action)

        builder = Gtk.Builder()
        builder.add_from_file(rb.find_plugin_file(self,
                                                  "magnatune-toolbar.ui"))
        toolbar = builder.get_object("magnatune-toolbar")
        app.link_shared_menus(toolbar)

        group = RB.DisplayPageGroup.get_by_id("stores")
        settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")
        self.source = GObject.new(
            MagnatuneSource,
            shell=shell,
            entry_type=self.entry_type,
            icon=Gio.ThemedIcon.new("magnatune-symbolic"),
            plugin=self,
            settings=settings.get_child("source"),
            name=_("Magnatune"),
            toolbar_menu=toolbar)

        shell.register_entry_type_for_source(self.source, self.entry_type)
        shell.append_display_page(self.source, group)

        self.pec_id = shell.props.shell_player.connect(
            'playing-song-changed', self.playing_entry_changed)

    def do_deactivate(self):
        shell = self.object

        shell.props.shell_player.disconnect(self.pec_id)

        self.db.entry_delete_by_type(self.entry_type)
        self.db.commit()
        self.db = None
        self.entry_type = None
        self.source.delete_thyself()
        self.source = None

    def playing_entry_changed(self, sp, entry):
        self.source.playing_entry_changed(entry)
Example #15
0
class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
    __gtype_name__ = 'MagnatuneConfig'
    object = GObject.property(type=GObject.GObject)

    format_list = ['ogg', 'flac', 'wav', 'mp3-vbr', 'mp3-cbr']

    def __init__(self):
        GObject.GObject.__init__(self)
        self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")
        self.account = MagnatuneAccount.instance()

    def do_create_configure_widget(self):
        # We use a dictionary so we can modify these values from within inner functions
        keyring_data = {'id': 0, 'item': None}

        def update_sensitivity(account_type):
            has_account = account_type != "none"
            builder.get_object("username_entry").set_sensitive(has_account)
            builder.get_object("password_entry").set_sensitive(has_account)
            builder.get_object("username_label").set_sensitive(has_account)
            builder.get_object("password_label").set_sensitive(has_account)

        def fill_account_details():
            (account_type, username, password) = self.account.get()
            builder.get_object("no_account_radio").set_active(
                account_type == "none")
            builder.get_object("stream_account_radio").set_active(
                account_type == "stream")
            builder.get_object("download_account_radio").set_active(
                account_type == "download")

            builder.get_object("username_entry").set_text(username or "")
            builder.get_object("password_entry").set_text(password or "")

            update_sensitivity(account_type)

        def account_type_toggled(button):
            print("account type radiobutton toggled: " + button.get_name())
            account_type = {
                "no_account_radio": 'none',
                "stream_account_radio": 'stream',
                "download_account_radio": 'download'
            }
            if button.get_active():
                self.settings['account-type'] = account_type[button.get_name()]
                update_sensitivity(account_type[button.get_name()])

        def account_details_changed(entry, event):
            username = builder.get_object("username_entry").get_text()
            password = builder.get_object("password_entry").get_text()

            if username == "" or password == "":
                print("missing something")
                return

            # should actually try a request to http://username:[email protected]/
            # to check the password is correct..

            MagnatuneAccount.instance().update(username, password)

        def format_selection_changed(button):
            self.settings['format'] = self.format_list[button.get_active()]

        self.configure_callback_dic = {
            "rb_magnatune_audio_combobox_changed_cb": format_selection_changed,
            "rb_magnatune_radio_account_toggled_cb": account_type_toggled
        }

        builder = Gtk.Builder()
        builder.add_from_file(rb.find_plugin_file(self, "magnatune-prefs.ui"))

        dialog = builder.get_object('magnatune_vbox')

        # Set the names of the radio buttons so we can tell which one has been clicked
        for name in ("no_account_radio", "stream_account_radio",
                     "download_account_radio"):
            builder.get_object(name).set_name(name)

        builder.get_object("audio_combobox").set_active(
            self.format_list.index(self.settings['format']))

        builder.connect_signals(self.configure_callback_dic)
        builder.get_object("username_entry").connect("focus-out-event",
                                                     account_details_changed)
        builder.get_object("password_entry").connect("focus-out-event",
                                                     account_details_changed)

        fill_account_details()
        return dialog
Example #16
0
class CddbPlugin(GObject.Object, Peas.Activatable):
    object = GObject.property (type = GObject.Object)

    def __init__(self):
        GObject.Object.__init__(self)

        for path in sys.path:
            try:
                self.gladefile = path + "/cddb.glade"
            except:
                pass
            else:
                break
        self.glade = Gtk.Builder()
        self.glade.add_from_file(self.gladefile)
        dic = { "on_album_view_cursor_changed" : self.album_cursor_changed,
                "apply_dialog" : self.apply_dialog,
                "hide_dialog" : self.hide_dialog }
        self.glade.connect_signals(dic)
        self.dialog = self.glade.get_object("dialog")

    def do_activate(self):
        data = dict()
        shell = self.object
        app = Gio.Application.get_default()

        data['action_group'] = Gio.SimpleAction.new("cddb-plugin-actions", None)
        data['action_group'].connect('activate', self.cddb)
        app.add_action(data['action_group'])

        data['ui_id'] = app.add_plugin_menu_item("tools",
                                 "cddb-plugin-actions",
                                 Gio.MenuItem.new(label=_("Cddb"),
                                                  detailed_action="app.cddb-plugin-actions"))


        self.cddb_name = "freedb.freedb.org"
        self.cddb_port = 80
        self.appname = "Rhythmbox"
        self.version = "0.10.1"

        self.albums = None
        self.disc = DiscInfos()

        # Configure search in the array on the first column
        treeview = self.glade.get_object('album-view')
        renderer = Gtk.CellRendererText()
        treeview.insert_column_with_attributes(-1, 'Artist / Album', renderer, text = 0)
        renderer = Gtk.CellRendererText()
        treeview.insert_column_with_attributes(-1, 'Category', renderer, text = 1)

        treeview = self.glade.get_object('tracks-view')
        renderer = Gtk.CellRendererText()
        treeview.insert_column_with_attributes(-1, '#', renderer, text = 0)
        treeview.insert_column_with_attributes(-1, 'Track Name', renderer, text = 1)
        treeview.insert_column_with_attributes(-1, 'Time', renderer, text = 2)

    def do_deactivate(self):
        shell = self.object

        app = shell.props.application
        app.remove_plugin_menu_item("tools", "cddb-plugin-actions")
        app.remove_action("cddb-plugin-actions")

        treeview = self.glade.get_object('album-view')
        model = Gtk.ListStore(str, str)
        treeview.set_model(model)
        treeview = self.glade.get_object('tracks-view')
        model = Gtk.ListStore(str, str, str)
        treeview.set_model(model)
        statusbar = self.glade.get_object('album-statusbar')
        statusbar.pop(0)

        self.albums = None
        self.disc = None

    def apply_dialog(self, *args):
        shell = self.object
        db = shell.props.db
        source = shell.props.library_source
        entryView = source.get_entry_view()
        entries = entryView.get_selected_entries()

        i = 0
        for entry in entries:
            db.entry_set(entry, RB.RhythmDBPropType.ARTIST, self.disc.disc[0])
            db.entry_set(entry, RB.RhythmDBPropType.ALBUM, self.disc.disc[1])
            db.entry_set(entry, RB.RhythmDBPropType.GENRE, self.disc.genre)
            db.entry_set(entry, RB.RhythmDBPropType.YEAR, int(self.disc.year))
            db.entry_set(entry, RB.RhythmDBPropType.TRACK_NUMBER, (i + 1))
            db.entry_set(entry, RB.RhythmDBPropType.TITLE, self.disc.trackname[i])
            i += 1
        db.commit()
        self.hide_dialog(args)
        return True

    def hide_dialog(self, *args):
        treeview = self.glade.get_object('album-view')
        model = Gtk.ListStore(str, str)
        treeview.set_model(model)
        treeview = self.glade.get_object('tracks-view')
        model = Gtk.ListStore(str, str, str)
        treeview.set_model(model)
        statusbar = self.glade.get_object('album-statusbar')
        statusbar.pop(0)

        self.albums = None
        self.disc = DiscInfos()
        self.dialog.hide()
        return True

    def cddb(self, action, parameter):
        shell = self.object
        source = shell.props.library_source
        entryView = source.get_entry_view()
        entries = entryView.get_selected_entries()
        total_frames = 150
        disc_length  = 2
        total_id = 0
        num_tracks = len(entries)
        query_string = ""

        self.disc.trackslength = []
        for entry in entries:
            secs = entry.get_ulong(RB.RhythmDBPropType.DURATION)
            self.disc.trackslength.append(time.strftime("%M:%S", time.gmtime(secs)))
            query_string = "%s+%d" % (
                    query_string,
                    total_frames )
            total_id += secs % 10
            total_frames += secs * 75
            disc_length += secs

        cddb_discid = "%08x" % ( ((total_id % 0xFF) << 24) | (disc_length << 8) | num_tracks )

        loader = rb.Loader()
        url = "http://%s:%d/~cddb/cddb.cgi?cmd=cddb+query+%s+%d+%s+%d&hello=noname+localhost+%s+%s&proto=6" % (
                urllib.parse.quote(self.cddb_name.encode('utf-8')),
                self.cddb_port,
                cddb_discid,
                num_tracks,
                query_string,
                disc_length,
                urllib.parse.quote(self.appname.encode('utf-8')),
                urllib.parse.quote(self.version.encode('utf-8')))

        self.dialog.show_all()
        self.dialog.grab_focus()

        print(url)
        loader.get_url(url, self.handle_albums_result)

    def handle_albums_result(self, data):
        treeview = self.glade.get_object('tracks-view')
        model = Gtk.ListStore(str, str, str)
        treeview.set_model(model)
        treeview = self.glade.get_object('album-view')
        model = Gtk.ListStore(str, str)
        treeview.set_model(model)

        if data is None:
            model.append(["Server did not respond.", ""])
            return

        lines = data.splitlines()
        if len(lines) < 3:
            model.append(["No album found.", ""])
            return

        lines.pop(0)
        lines.pop()

        self.albums = []
        for line in lines:
            tmp = str(line, encoding='utf8').split(" ", 2)
            self.albums.append(tmp[1])
            model.append([tmp[2], tmp[0]])

    def album_cursor_changed(self, treeview):
        treeview = self.glade.get_object('album-view')
        treeselection = treeview.get_selection()
        model, iter = treeselection.get_selected()

        if iter is None:
            return

        row = int(model.get_string_from_iter(iter))
        #row to color in red
        discid = self.albums[row]

        loader = rb.Loader()
        url = "http://%s:%d/~cddb/cddb.cgi?cmd=cddb+read+%s+%s&hello=noname+localhost+%s+%s&proto=6" % (
                urllib.parse.quote(self.cddb_name.encode('utf-8')),
                self.cddb_port,
                model.get_value(iter, 1),
                discid,
                urllib.parse.quote(self.appname.encode('utf-8')),
                urllib.parse.quote(self.version.encode('utf-8')))

        print(url)
        loader.get_url(url, self.handle_album_result)

    def handle_album_result(self, data):
        treeview = self.glade.get_object('tracks-view')
        model = Gtk.ListStore(str, str, str)
        treeview.set_model(model)
        statusbar = self.glade.get_object('album-statusbar')
        statusbar.pop(0)

        if data is None:
            model.append(["Server did not respond.", "", ""])
            return

        lines = str(data, encoding='utf8').splitlines()
        lines.pop(0)
        lines.pop()

        album = []
        i = 1
        for line in lines:
            if line[0] != "#":
                if line.startswith("DTITLE"):
                    tmp = line.replace("DTITLE=", "", 1)
                    self.disc.disc = tmp.split(" / ", 1)
                if line.startswith("TTITLE"):
                    tmp = line.split("=", 1)
                    model.append([str(i), str(tmp[1]), str(self.disc.trackslength[i - 1])])
                    self.disc.trackname.append(tmp[1])
                    i += 1
                if line.startswith("DYEAR="):
                    self.disc.year = line.replace("DYEAR=", "", 1)
                if line.startswith("DGENRE="):
                    self.disc.genre = line.replace("DGENRE=", "", 1)

        statusbar.push(0, "Title (" + self.disc.disc[1] + "), Artist (" + self.disc.disc[0] + "), Year (" + self.disc.year + "), genre (" + self.disc.genre + ")")
Example #17
0
class SyncObject(GObject.Object):
    __gsignals__ = {
        'sync-done': (GObject.SignalFlags.RUN_LAST, None, []),
    }
    sync_status = GObject.property(type=object)
    auth = GObject.property(type=GObject.Object)
Example #18
0
class TextView(Gtk.DrawingArea, Gtk.Scrollable):

    __gsignals__ = {
        'cut-clipboard': (GObject.SIGNAL_RUN_LAST, None, ()),
        'copy-clipboard': (GObject.SIGNAL_RUN_LAST, None, ()),
        'paste-clipboard': (GObject.SIGNAL_RUN_FIRST, None, ()),
        'select-all': (GObject.SIGNAL_RUN_FIRST, None, (bool,))
    }

    def __init__(self):
        Gtk.DrawingArea.__init__(self)
        self._init_scrollable()
        self._init_immultiontext()

        self.buffer = TextBuffer()
        self.spacing = 12
        self.width = 0
        self.height = 0
        self.allocated_height = 0
        self.caret = Gdk.Rectangle()
        self.heights = list()
        self.highlight_sentences = True
        self.reflow_line = -1   # line number to reflow after "delete_range"; -1 to reflow every line

        self.buffer.connect_after("insert_text", self.on_inserted)
        self.buffer.connect("delete_range", self.on_delete)
        self.buffer.connect_after("delete_range", self.on_deleted)

        self.connect("draw", self.on_draw)
        self.connect("key-press-event", self.on_key_press)
        self.connect("key-release-event", self.on_key_release)
        self.connect("focus-in-event", self.on_focus_in)
        self.connect("focus-out-event", self.on_focus_out)

        self.connect('motion-notify-event', self.on_mouse_move)
        self.connect('button-press-event', self.on_mouse_press)
        self.connect('button-release-event', self.on_mouse_release)

        self.set_can_focus(True)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
                        Gdk.EventMask.BUTTON_MOTION_MASK |
                        Gdk.EventMask.BUTTON_RELEASE_MASK |
                        Gdk.EventMask.SCROLL_MASK)

        desc = Pango.font_description_from_string(DEFAULT_FONT)
        # desc = self.get_style().font_desc
        self.set_font(desc)

        self.tr = str.maketrans({
            '<': '&lt;',
            '>': '&gt;',
            '&': '&amp;'
        })

    def _init_scrollable(self):
        self._hadjustment = self._vadjustment = None
        self._hadjust_signal = self._vadjust_signal = None

    def _init_immultiontext(self):
        self.im = Gtk.IMMulticontext()
        self.im.connect("commit", self.on_commit)
        self.im.connect("delete-surrounding", self.on_delete_surrounding)
        self.im.connect("retrieve-surrounding", self.on_retrieve_surrounding)
        self.im.connect("preedit-changed", self.on_preedit_changed)
        self.im.connect("preedit-end", self.on_preedit_end)
        self.im.connect("preedit-start", self.on_preedit_start)
        self.preedit = ('', None, 0)

    def _escape(self, str):
        return str.translate(self.tr)

    def _get_offset(self):
        offset = 0
        if self._vadjustment:
            offset = self._vadjustment.get_value()
        return offset

    def _has_preedit(self):
        return self.preedit[0]

    def get_buffer(self):
        return self.buffer

    def get_paragraph(self, line):
        if 0 <= line and line < self.get_buffer().get_line_count():
            return self.get_buffer().paragraphs[line]
        return None

    def get_editable(self):
        return True

    def get_iter_at_location(self, x, y):
        cursor = self.buffer.get_cursor()
        width = self.get_allocated_width()
        height = 0
        i = 0
        for h in self.heights:
            if y < h + height:
                context = self.create_pango_context()
                layout = Pango.Layout(context)
                desc = self.get_font()
                layout.set_font_description(desc)
                layout.set_width(width * Pango.SCALE)
                layout.set_spacing(self.spacing * Pango.SCALE)
                paragraph = self.get_paragraph(i)
                text = paragraph.get_plain_text()
                cursor_offset = len(text)
                if i == cursor.get_line() and self._has_preedit():
                    cursor_offset = cursor.get_plain_line_offset()
                    text = text[:cursor_offset] + self.preedit[0] + text[cursor_offset:]
                layout.set_text(text, -1)
                inside, index, trailing = layout.xy_to_index(x * Pango.SCALE, (y - height) * Pango.SCALE)
                offset = len(text.encode()[:index].decode())
                if cursor_offset <= offset:
                    offset -= len(self.preedit[0])
                offset = paragraph._expand_plain_offset(offset)
                if trailing:
                    offset = paragraph._forward_cursor_position(offset)
                iter = self.buffer.get_iter_at_line_offset(i, offset)
                return inside, iter
            height += h
            i += 1
        return False, self.buffer.get_end_iter()

    def get_font(self):
        return self.font_desc

    def set_font(self, font_desc):
        self.font_desc = font_desc
        if font_desc.get_size_is_absolute():
            self.spacing = font_desc.get_size() / Pango.SCALE * 7 / 8
        else:
            context = self.create_pango_context()
            dpi = PangoCairo.context_get_resolution(context)
            self.spacing = font_desc.get_size() / Pango.SCALE * dpi / 72 * 7 / 8
        self.reflow()

    def get_check_sentences(self):
        return self.highlight_sentences

    def set_check_sentences(self, value):
        if value != self.highlight_sentences:
            self.highlight_sentences = value
            self.queue_draw()

    def place_cursor_onscreen(self):
        self.scroll_mark_onscreen(self.buffer.get_insert())

    def scroll_mark_onscreen(self, mark):
        if not self._vadjustment:
            return

        width = self.get_allocated_width()
        height = self.get_allocated_height()
        offset = self._get_offset()

        y = 0
        line = mark.iter.get_line()
        for i in range(line):
            y += self.heights[i]

        context = self.create_pango_context()
        layout = Pango.Layout(context)
        desc = self.get_font()
        layout.set_font_description(desc)
        layout.set_width(width * Pango.SCALE)
        layout.set_spacing(self.spacing * Pango.SCALE)
        text = self.buffer.paragraphs[line].get_plain_text()
        layout.set_text(text, -1)

        current = text[:mark.iter.get_plain_line_offset()]
        st, we = layout.get_cursor_pos(len(current.encode()))
        st.x, st.y, st.width, st.height = \
            st.x / Pango.SCALE - 1, st.y / Pango.SCALE, st.width / Pango.SCALE + 2, st.height / Pango.SCALE
        y += st.y
        h = self.spacing + self.caret.height

        if y < offset:
            self._vadjustment.set_value(y)
        elif offset + height <= y + h:
            y += h
            lines = height // h
            y = (y + h - 1) // h
            self._vadjustment.set_value(h * (y - lines))

        self.queue_draw()

    def _draw_rubies(self, cr, layout, paragraph, plain, height, cursor_offset):
        lt = PangoCairo.create_layout(cr)
        desc = self.get_font().copy_static()
        size = desc.get_size()
        desc.set_size(size // 2)
        lt.set_font_description(desc)
        for pos, length, ruby in paragraph.rubies:
            if self._has_preedit() and cursor_offset <= pos:
                pos += len(self.preedit[0])
            text = plain[:pos]
            left = layout.index_to_pos(len(text.encode()))
            text = plain[:pos + length - 1]
            right = layout.index_to_pos(len(text.encode()))
            left.x /= Pango.SCALE
            left.y /= Pango.SCALE
            right.x += right.width
            right.x /= Pango.SCALE
            right.y /= Pango.SCALE
            if left.y == right.y:
                cr.save()
                lt.set_text(ruby, -1)
                PangoCairo.update_layout(cr, lt)
                w, h = lt.get_pixel_size()
                x = (left.x + right.x - w) / 2
                if x < 0:
                    x = 0
                elif self.width < x + w:
                    x = self.width - w
                y = height + left.y - h
                cr.move_to(x, y)
                PangoCairo.show_layout(cr, lt)
                cr.restore()
            else:
                ruby_width = right.x + self.width - left.x
                left_length = round(len(ruby) * (self.width - left.x) / ruby_width)
                if 0 < left_length:
                    text = ruby[:left_length]
                    cr.save()
                    lt.set_text(text, -1)
                    PangoCairo.update_layout(cr, lt)
                    w, h = lt.get_pixel_size()
                    x = self.width - w
                    y = height + left.y - h
                    cr.move_to(x, y)
                    PangoCairo.show_layout(cr, lt)
                    cr.restore()
                if left_length < len(ruby):
                    text = ruby[left_length:]
                    cr.save()
                    lt.set_text(text, -1)
                    PangoCairo.update_layout(cr, lt)
                    w, h = lt.get_pixel_size()
                    x = 0
                    y = height + right.y - h
                    cr.move_to(x, y)
                    PangoCairo.show_layout(cr, lt)
                    cr.restore()

    def _draw_caret(self, cr, layout, current, y, offset):
        cr.save()
        st, we = layout.get_cursor_pos(len(current[:offset].encode()))
        self.caret.x, self.caret.y, self.caret.width, self.caret.height = \
            st.x / Pango.SCALE - 1, y + st.y / Pango.SCALE, st.width / Pango.SCALE + 2, st.height / Pango.SCALE
        cr.set_operator(cairo.Operator.DIFFERENCE)
        cr.set_source_rgb(1, 1, 1)
        cr.rectangle(self.caret.x, self.caret.y, self.caret.width, self.caret.height)
        cr.fill()
        self.im.set_cursor_location(self.caret)
        cr.restore()

    def reflow(self, line=-1, redraw=True):
        self.width = self.get_allocated_width()

        cursor = self.buffer.get_cursor()

        context = self.create_pango_context()
        layout = Pango.Layout(context)
        desc = self.get_font()
        layout.set_font_description(desc)
        layout.set_width(self.width * Pango.SCALE)
        layout.set_spacing(self.spacing * Pango.SCALE)

        paragraph = self.get_paragraph(line)
        if paragraph and self.heights:
            self.height -= self.heights[line]
            text = paragraph.get_plain_text()
            if line == cursor.get_line() and self._has_preedit():
                cursor_offset = cursor.get_plain_line_offset()
                text = text[:cursor_offset] + self.preedit[0] + text[cursor_offset:]
            layout.set_text(text, -1)
            w, h = layout.get_pixel_size()
            h += self.spacing
            self.heights[line] = h
            self.height += h
        else:
            self.heights.clear()
            self.height = self.spacing
            for paragraph in self.get_buffer().paragraphs:
                text = paragraph.get_plain_text()
                if line == cursor.get_line() and self._has_preedit():
                    cursor_offset = cursor.get_plain_line_offset()
                    text = text[:cursor_offset] + self.preedit[0] + text[cursor_offset:]
                layout.set_text(text, -1)
                w, h = layout.get_pixel_size()
                h += self.spacing
                self.heights.append(h)
                self.height += h

        if self._vadjustment:
            # TODO: Adjust _vadjustment value as well
            self._vadjustment.set_properties(
                lower=0,
                upper=self.height,
                page_size=self.get_allocated_height()
            )

        if redraw:
            self.queue_draw()

    def _check_sentences(self, text):
        if not self.highlight_sentences:
            return text
        markup = ''
        sentence = ''
        start = end = 0
        text_length = len(text)
        for i in range(text_length):
            c = text[i]
            if start == end:
                if c in "\t  ":
                    markup += c
                    start += 1
                    end = start
                    continue
            end = i + 1
            if c in "  。.?!" or text_length <= end:
                if c in "  ":
                    end -= 1
                else:
                    sentence += c
                count = end - start
                if SENTENCE_LONG < count:
                    markup += '<span background="light pink">' + sentence + '</span>'
                elif SENTENCE_SHORT < count:
                    markup += '<span background="light yellow">' + sentence + '</span>'
                else:
                    markup += sentence
                if c in "  ":
                    markup += c
                start = end = i + 1
                sentence = ''
            else:
                sentence += self._escape(c)
        return markup

    def on_draw(self, wid, cr):
        if wid.get_allocated_width() != self.width:
            self.reflow(redraw=False)
        height = wid.get_allocated_height()

        cr.set_source_rgb(255, 255, 255)
        cr.rectangle(0, 0, self.width, height)
        cr.fill()
        cr.move_to(0, 0)
        cr.set_source_rgb(0, 0, 0)

        cursor = self.buffer.get_cursor()
        start, end = self.buffer.get_selection_bounds()

        layout = PangoCairo.create_layout(cr)
        desc = self.get_font()
        layout.set_font_description(desc)
        layout.set_width(self.width * Pango.SCALE)
        layout.set_spacing(self.spacing * Pango.SCALE)

        lineno = 0
        offset = self._get_offset()
        y = self.spacing - offset
        for paragraph in self.buffer.paragraphs:
            h = self.heights[lineno]
            if y < height and 0 <= y + h:
                text = paragraph.get_plain_text()
                cursor_offset = len(text)
                if lineno == cursor.get_line():
                    cursor_offset = cursor.get_plain_line_offset()
                    if self._has_preedit():
                        text = text[:cursor_offset] + self.preedit[0] + text[cursor_offset:]
                if start == end or lineno < start.get_line() or end.get_line() < lineno:
                    # Note set_text() does not reset the existing markups.
                    markup = self._check_sentences(text)
                    layout.set_markup(markup, -1)
                elif start.get_line() < lineno and lineno < end.get_line():
                    markup = self._escape(text)
                    markup = '<span background="#ACCEF7">' + markup + '</span>'
                    layout.set_markup(markup, -1)
                elif start.get_line() == end.get_line():
                    assert lineno == end.get_line()
                    so = start.get_plain_line_offset()
                    eo = end.get_plain_line_offset()
                    markup = self._escape(text[:so]) + \
                        '<span background="#ACCEF7">' + self._escape(text[so:eo]) + '</span>' + \
                        self._escape(text[eo:])
                    layout.set_markup(markup, -1)
                elif start.get_line() == lineno:
                    o = start.get_plain_line_offset()
                    markup = self._escape(text[:o]) + '<span background="#ACCEF7">' + self._escape(text[o:]) + '</span>'
                    layout.set_markup(markup, -1)
                elif lineno == end.get_line():
                    o = end.get_plain_line_offset()
                    markup = '<span background="#ACCEF7">' + self._escape(text[:o]) + '</span>' + self._escape(text[o:])
                    layout.set_markup(markup, -1)
                PangoCairo.update_layout(cr, layout)
                cr.move_to(0, y)
                PangoCairo.show_layout(cr, layout)
                self._draw_rubies(cr, layout, paragraph, text, y, cursor_offset)
                if lineno == cursor.get_line():
                    self._draw_caret(cr, layout, text, y, cursor_offset + self.preedit[2])
            y += h
            lineno += 1

        self.caret.y += offset

        if height != self.allocated_height:
            self.allocated_height = height
            if self._vadjustment:
                # TODO: Adjust _vadjustment value as well
                self._vadjustment.set_properties(
                    lower=0,
                    upper=self.height,
                    page_size=height
                )

    def on_focus_in(self, wid, event):
        print("on_focus_in")
        self.im.set_client_window(wid.get_window())
        self.im.focus_in()
        return True

    def on_focus_out(self, wid, event):
        print("on_focus_out")
        self.im.focus_out()
        return True

    def on_key_press(self, wid, event):
        is_selection = (event.state & Gdk.ModifierType.SHIFT_MASK)
        is_control = (event.state & Gdk.ModifierType.CONTROL_MASK)
        print("on_key_press:", Gdk.keyval_name(event.keyval), event.state)
        if self.im.filter_keypress(event):
            return True
        # Process shortcut keys firstly
        if is_control:
            if event.keyval == Gdk.KEY_A or event.keyval == Gdk.KEY_a:
                self.emit('select-all', True)
                return True
            elif event.keyval == Gdk.KEY_X or event.keyval == Gdk.KEY_x:
                self.emit('cut-clipboard')
                return True
            elif event.keyval == Gdk.KEY_C or event.keyval == Gdk.KEY_c:
                self.emit('copy-clipboard')
                return True
            elif event.keyval == Gdk.KEY_V or event.keyval == Gdk.KEY_v:
                self.emit('paste-clipboard')
                return True
        if event.keyval == Gdk.KEY_BackSpace:
            if self.buffer.delete_selection(True, True):
                return True
            end = self.buffer.get_cursor()
            start = end.copy()
            if start.backward_cursor_position():
                self.buffer.begin_user_action()
                self.buffer.delete(start, end)
                self.buffer.end_user_action()
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Delete:
            if self.buffer.delete_selection(True, True):
                return True
            start = self.buffer.get_cursor()
            end = start.copy()
            if start.forward_cursor_position():
                self.buffer.begin_user_action()
                self.buffer.delete(start, end)
                self.buffer.end_user_action()
                self.buffer.place_cursor(start)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Return:
            self.buffer.begin_user_action()
            self.buffer.insert_at_cursor('\n')
            self.buffer.end_user_action()
            self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Escape:
            cursor = self.buffer.get_cursor()
            self.buffer.place_cursor(cursor)
            self.queue_draw()
            return True
        elif event.keyval == Gdk.KEY_Left:
            cursor = self.buffer.get_cursor()
            if cursor.backward_cursor_position():
                self.buffer.move_cursor(cursor, is_selection)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Right:
            cursor = self.buffer.get_cursor()
            if cursor.forward_cursor_position():
                self.buffer.move_cursor(cursor, is_selection)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Up:
            y = self.caret.y - self.caret.height - self.spacing
            if 0 <= y:
                inside, cursor = self.get_iter_at_location(self.caret.x + 1, y)
                self.buffer.move_cursor(cursor, is_selection)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Down:
            y = self.caret.y + self.caret.height + self.spacing
            if y < self.height:
                inside, cursor = self.get_iter_at_location(self.caret.x + 1, y)
                self.buffer.move_cursor(cursor, is_selection)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Home:
            if is_control:
                cursor = self.buffer.get_start_iter()
            else:
                inside, cursor = self.get_iter_at_location(0, self.caret.y)
            if cursor != self.buffer.get_cursor():
                self.buffer.move_cursor(cursor, is_selection)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_End:
            if is_control:
                cursor = self.buffer.get_end_iter()
            else:
                width = wid.get_allocated_width()
                inside, cursor = self.get_iter_at_location(width, self.caret.y)
            if cursor != self.buffer.get_cursor():
                self.buffer.move_cursor(cursor, is_selection)
                self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Page_Up:
            y = self.caret.y - wid.get_allocated_height()
            inside, cursor = self.get_iter_at_location(self.caret.x + 1, y)
            self.buffer.move_cursor(cursor, is_selection)
            self.place_cursor_onscreen()
            return True
        elif event.keyval == Gdk.KEY_Page_Down:
            y = self.caret.y + wid.get_allocated_height()
            inside, cursor = self.get_iter_at_location(self.caret.x + 1, y)
            self.buffer.move_cursor(cursor, is_selection)
            self.place_cursor_onscreen()
            return True
        return False

    def on_key_release(self, wid, event):
        # print("on_key_release: '", Gdk.keyval_name(event.keyval), "', ", event.state, sep='')
        if self.im.filter_keypress(event):
            return True
        return False

    def on_commit(self, wid, str):
        self.buffer.begin_user_action()
        self.buffer.insert_at_cursor(str)
        self.buffer.end_user_action()
        self.place_cursor_onscreen()
        return True

    def on_retrieve_surrounding(self, wid):
        text, offset = self.buffer.get_surrounding()
        self.im.set_surrounding(text, len(text.encode()), len(text[:offset].encode()))
        return True

    def on_delete_surrounding(self, wid, offset, n_chars):
        self.buffer.begin_user_action()
        self.buffer.delete_surrounding(offset, n_chars)
        self.buffer.end_user_action()
        self.place_cursor_onscreen()
        return True

    def on_preedit_changed(self, wid):
        self.preedit = self.im.get_preedit_string()
        cursor = self.buffer.get_cursor()
        self.buffer.delete_selection(True, True)
        self.reflow(cursor.get_line())
        print('on_preedit_changed', self.preedit)

    def on_preedit_end(self, wid):
        self.preedit = self.im.get_preedit_string()
        self.buffer.delete_selection(True, True)
        print('on_preedit_end', self.preedit)

    def on_preedit_start(self, wid):
        self.preedit = self.im.get_preedit_string()
        self.buffer.delete_selection(True, True)
        print('on_preedit_start', self.preedit)

    def on_value_changed(self, *whatever):
        self.queue_draw()

    def on_inserted(self, textbuffer, iter, text):
        if has_newline(text):
            self.reflow()
        else:
            self.reflow(iter.get_line())
        self.queue_draw()

    def on_delete(self, textbuffer, start, end):
        if start.get_line() == end.get_line():
            self.reflow_line = start.get_line()
        else:
            self.reflow_line = -1

    def on_deleted(self, textbuffer, start, end):
        self.reflow(self.reflow_line)
        self.queue_draw()

    def on_mouse_move(self, wid, event):
        if (event.state & Gdk.ModifierType.BUTTON1_MASK):
            inside, cursor = self.get_iter_at_location(event.x, self._get_offset() + event.y)
            self.buffer.move_cursor(cursor, True)
            self.place_cursor_onscreen()
        return True

    def on_mouse_press(self, wid, event):
        if event.button == Gdk.BUTTON_PRIMARY:
            is_selection = (event.state & Gdk.ModifierType.SHIFT_MASK)
            inside, cursor = self.get_iter_at_location(event.x, self._get_offset() + event.y)
            self.buffer.move_cursor(cursor, is_selection)
            self.place_cursor_onscreen()
        return True

    def on_mouse_release(self, wid, event):
        return True

    def get_hadjustment(self):
        return self._hadjustment

    def get_vadjustment(self):
        return self._vadjustment

    def set_hadjustment(self, adjustment):
        if self._hadjustment:
            self._hadjustment.disconnect(self._hadjust_signal)
            self._hadjust_signal = None

        self._hadjustment = adjustment
        if adjustment:
            adjustment.set_properties(
                lower=0,
                upper=self.get_allocated_width(),
                page_size=self.get_allocated_width()
            )
            self._hadjust_signal = adjustment.connect("value-changed", self.on_value_changed)

    def set_vadjustment(self, adjustment):
        if self._vadjustment:
            self._vadjustment.disconnect(self._vadjust_signal)
            self._vadjust_signal = None

        self._vadjustment = adjustment
        if adjustment:
            adjustment.set_properties(
                lower=0,
                upper=self.height,
                page_size=self.get_allocated_height()
            )
            self._vadjust_signal = adjustment.connect("value-changed", self.on_value_changed)

    def do_cut_clipboard(self):
        clipboard = self.get_clipboard(Gdk.SELECTION_CLIPBOARD)
        self.buffer.cut_clipboard(clipboard, self.get_editable())
        self.place_cursor_onscreen()

    def do_copy_clipboard(self):
        clipboard = self.get_clipboard(Gdk.SELECTION_CLIPBOARD)
        self.buffer.copy_clipboard(clipboard)

    def do_paste_clipboard(self):
        clipboard = self.get_clipboard(Gdk.SELECTION_CLIPBOARD)
        text = clipboard.wait_for_text()
        if text is not None:
            self.buffer.begin_user_action()
            self.buffer.insert_at_cursor(text)
            self.buffer.end_user_action()

    def do_select_all(self, select):
        self.buffer.select_all()
        self.queue_draw()

    hadjustment = GObject.property(
        get_hadjustment, set_hadjustment, type=Gtk.Adjustment
    )
    vadjustment = GObject.property(
        get_vadjustment, set_vadjustment, type=Gtk.Adjustment
    )
    hscroll_policy = GObject.property(
        default=Gtk.ScrollablePolicy.NATURAL, type=Gtk.ScrollablePolicy
    )
    vscroll_policy = GObject.property(
        default=Gtk.ScrollablePolicy.NATURAL, type=Gtk.ScrollablePolicy
    )
class ProgressToolButton(Gtk.ToolButton):
    """Display the progress filling the ToolButton icon.

    Call update(progress) with the new progress to update the ToolButton icon.

    The direction defaults to 'vertical', in which case the icon is
    filled from bottom to top.  If direction is set to 'horizontal',
    it will be filled from right to left or from left to right,
    depending on the system's language RTL setting.

    """
    __gtype_name__ = 'SugarProgressToolButton'

    def __init__(self,
                 icon_name=None,
                 pixel_size=None,
                 direction='vertical',
                 **kwargs):
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._progress = 0.0
        self._icon_name = icon_name
        self._pixel_size = pixel_size
        self._direction = direction

        GObject.GObject.__init__(self, **kwargs)

        self._hide_tooltip_on_click = True
        self._palette_invoker.attach_tool(self)

        self._stroke = get_surface(
            icon_name=self._icon_name,
            width=self._pixel_size,
            height=self._pixel_size,
            stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
            #stroke_color=style.COLOR_WHITE.get_svg(),
            fill_color=style.COLOR_TRANSPARENT.get_svg())

        self._fill = get_surface(
            icon_name=self._icon_name,
            width=self._pixel_size,
            height=self._pixel_size,
            stroke_color=style.COLOR_TRANSPARENT.get_svg(),
            fill_color=style.COLOR_WHITE.get_svg())

        self.get_child().connect('can-activate-accel',
                                 self.__button_can_activate_accel_cb)

        self.connect('destroy', self.__destroy_cb)

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

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    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

        # Set label, shows up when toolbar overflows
        Gtk.ToolButton.set_label(self, tooltip)

    def get_tooltip(self):
        return self._tooltip

    tooltip = GObject.property(type=str,
                               setter=set_tooltip,
                               getter=get_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 set_accelerator(self, accelerator):
        self._accelerator = accelerator
        setup_accelerator(self)

    def get_accelerator(self):
        return self._accelerator

    accelerator = GObject.property(type=str,
                                   setter=set_accelerator,
                                   getter=get_accelerator)

    def set_icon_name(self, icon_name):
        icon = Icon(icon_name=icon_name)
        self.set_icon_widget(icon)
        icon.show()

    def get_icon_name(self):
        if self.props.icon_widget is not None:
            return self.props.icon_widget.props.icon_name
        else:
            return None

    icon_name = GObject.property(type=str,
                                 setter=set_icon_name,
                                 getter=get_icon_name)

    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):
        if self._progress > 0:
            self._stroke = get_surface(
                icon_name=self._icon_name,
                width=self._pixel_size,
                height=self._pixel_size,
                stroke_color=style.COLOR_WHITE.get_svg(),
                fill_color=style.COLOR_TRANSPARENT.get_svg())
        else:
            self._stroke = get_surface(
                icon_name=self._icon_name,
                width=self._pixel_size,
                height=self._pixel_size,
                stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
                fill_color=style.COLOR_TRANSPARENT.get_svg())

        allocation = self.get_allocation()

        # Center the graphic in the allocated space.
        margin_x = (allocation.width - self._stroke.get_width()) / 2
        margin_y = (allocation.height - self._stroke.get_height()) / 2
        cr.translate(margin_x, margin_y)

        # Paint the fill, clipping it by the progress.
        x_, y_ = 0, 0
        width, height = self._stroke.get_width(), self._stroke.get_height()
        if self._direction == 'vertical':  # vertical direction, bottom to top
            y_ = self._stroke.get_height()
            height *= self._progress * -1
        else:
            rtl_direction = \
                Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL
            if rtl_direction:  # horizontal direction, right to left
                x_ = self._stroke.get_width()
                width *= self._progress * -1
            else:  # horizontal direction, left to right
                width *= self._progress

        cr.rectangle(x_, y_, width, height)
        cr.clip()
        cr.set_source_surface(self._fill, 0, 0)
        cr.paint()

        # Paint the stroke over the fill.
        cr.reset_clip()
        cr.set_source_surface(self._stroke, 0, 0)
        cr.paint()
        return False

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

    def update(self, progress):
        self._progress = progress
        self.queue_draw()
Example #20
0
class PythonConsolePlugin(GObject.Object, Peas.Activatable):
	__gtype_name__ = 'PythonConsolePlugin'

	object = GObject.property (type = GObject.Object)

	def __init__(self):
		GObject.Object.__init__(self)
		self.window = None
		
	def do_activate(self):
		shell = self.object
		app = shell.props.application

		action = Gio.SimpleAction.new("python-console", None)
		action.connect('activate', self.show_console, shell)
		app.add_action(action)

		app.add_plugin_menu_item("tools",
					 "python-console",
					 Gio.MenuItem.new(label=_("Python Console"),
						 	  detailed_action="app.python-console"))

		if have_rpdb2:
			action = Gio.SimpleAction.new("python-debugger", None)
			action.connect('activate', self.enable_debugging, shell)
			app.add_action(action)

			app.add_plugin_menu_item("tools",
						 "python-debugger",
						 Gio.MenuItem.new(label=_("Python Debugger"),
								  detailed_action="app.python-debugger"))

	def do_deactivate(self):
		shell = self.object
		app = shell.props.application

		app.remove_plugin_menu_item("tools", "python-console")
		app.remove_plugin_menu_item("tools", "python-debugger")
		app.remove_action("python-console")
		app.remove_action("python-debugger")
		
		if self.window is not None:
			self.window.destroy()


	def show_console(self, action, parameter, shell):
		if not self.window:
			ns = {'__builtins__' : __builtins__, 
			      'RB' : RB,
			      'shell' : shell}
			console = PythonConsole(namespace = ns, 
			                        destroy_cb = self.destroy_console)
			console.set_size_request(600, 400)
			console.eval('print("' + \
			             _("You can access the main window " \
			             "through the \'shell\' variable :") +
			             '\\n%s" % shell)', False)

			self.window = Gtk.Window()
			self.window.set_title('Rhythmbox Python Console')
			self.window.add(console)
			self.window.connect('destroy', self.destroy_console)
			self.window.show_all()
		else:
			self.window.show_all()
		self.window.grab_focus()

	def enable_debugging(self, action, parameter, shell):
		pwd_path = os.path.join(rb.user_data_dir(), "rpdb2_password")
		msg = _("After you press OK, Rhythmbox will wait until you connect to it with winpdb or rpdb2. If you have not set a debugger password in the file %s, it will use the default password ('rhythmbox').") % pwd_path
		dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK_CANCEL, msg)
		if dialog.run() == Gtk.RESPONSE_OK:
			password = "******"
			if os.path.exists(pwd_path):
				pwd_file = open(pwd_path)
				password = pwd_file.read().rstrip()
				pwd_file.close()

			def start_debugger(password):
				rpdb2.start_embedded_debugger(password)
				return False

			GLib.idle_add(start_debugger, password)
		dialog.destroy()
	
	def destroy_console(self, *args):
		self.window.destroy()
		self.window = None
class CompletionPlugin(GObject.Object, Gedit.WindowActivatable,
                       PeasGtk.Configurable):

    __gtype_name__ = "HaxeCompletionPlugin"
    window = GObject.property(type=Gedit.Window)
    re_alpha = re.compile(r"\w+", re.UNICODE | re.MULTILINE)
    re_non_alpha = re.compile(r"\W+", re.UNICODE | re.MULTILINE)
    re_multiline_comments = re.compile(r"/\*(?:.|[\r\n])*?\*/",
                                       re.UNICODE | re.MULTILINE)
    re_singleline_comments = re.compile(r"//+[^\n]*\n",
                                        re.UNICODE | re.MULTILINE)

    def __init__(self):
        GObject.Object.__init__(self)
        self.completions = None
        self.name = "HaxeCompletionPlugin"
        self.window = None
        self.islaunching = False
        self.tempstring = ""
        self.w = None
        self.t = None

    def do_activate(self):
        """Activate plugin."""

        handler_ids = []
        callback = self.on_window_tab_added
        handler_id = self.window.connect("tab-added", callback)
        handler_ids.append(handler_id)
        self.apiInfoPanel = ApiInfoPanel(self)
        self.window.set_data(self.name, handler_ids)
        for view in self.window.get_views():
            self.connect_view(view)

    def cancel(self):
        """Hide the completion window and return False."""
        return False

    def connect_view(self, view):
        """Connect to view's signals."""
        handler_ids = []
        callback = self.on_view_key_press_event
        handler_id = view.connect("key-press-event", callback)
        handler_ids.append(handler_id)
        view.set_data(self.name, handler_ids)

    def do_create_configure_widget(self):
        return ConfigurationDialog().create_widget()

    def do_deactivate(self):
        """Deactivate plugin."""
        if configuration.getShowApiInfoPanel():
            self.apiInfoPanel.remove()
        widgets = [self.window]
        widgets.append(self.window.get_views())
        widgets.append(self.window.get_documents())
        for widget in widgets:
            if widget != [] and 'get_data' in widget:
                handler_ids = widget.get_data(self.name)
                for handler_id in handler_ids:
                    widget.disconnect(handler_id)
                widget.set_data(self.name, None)
        self.window = None

    def do_update_state(self):
        #print "Window %s state updated." % self.window
        pass

    def on_window_tab_added(self, window, tab):
        """Connect the document and view in tab."""
        self.connect_view(tab.get_view())

    def is_configurable(self):
        """Show the plugin as configurable in gedits plugin list."""
        return True

    def on_view_key_press_event(self, view, event):
        #self.t = time.time()
        #print self.t
        #print "on_view_key_press_event started"

        #print "view event.keyval %s" % event.keyval
        if self.islaunching:
            self.tempstring += event.string
        """Display the completion window or complete the current word."""
        active_doc = self.window.get_active_document()
        #print "active_doc.get_uri_for_display() %s ." % active_doc.get_uri_for_display()
        #print "active_doc.get_short_name_for_display() %s ." % active_doc.get_short_name_for_display()
        # If the current tab is not a Haxe sourcefile, do not attempt to complete
        # TODO It would be best to never get called in the first place.

        try:
            if not active_doc.get_uri_for_display().endswith('hx'):
                return self.cancel()
        except:
            return self.cancel()

        #  The "Alt"-key might be mapped to something else
        # TODO Find out which keybinding are already in use.
        keybinding = configuration.getKeybindingCompleteTuple()

        ctrl_pressed = (
            event.state
            & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK
        alt_pressed = (
            event.state
            & Gdk.ModifierType.MOD1_MASK) == Gdk.ModifierType.MOD1_MASK
        shift_pressed = (
            event.state
            & Gdk.ModifierType.SHIFT_MASK) == Gdk.ModifierType.SHIFT_MASK
        # It's ok if a key is pressed and it's needed or
        # if a key is not pressed if it isn't needed.
        ctrl_ok = not (keybinding[configuration.MODIFIER_CTRL] ^ ctrl_pressed)
        alt_ok = not (keybinding[configuration.MODIFIER_ALT] ^ alt_pressed)
        shift_ok = not (keybinding[configuration.MODIFIER_SHIFT]
                        ^ shift_pressed)

        keyval = Gdk.keyval_from_name(keybinding[configuration.KEY])
        key_ok = (event.keyval == keyval)

        #print time.time() - self.t
        #print "on_view_key_press_event done"
        #self.t = time.time()

        if event.keyval == Gdk.KEY_period and configuration.getDotComplete():
            return self.display_completions(view, event)
        elif ctrl_ok and alt_ok and shift_ok and key_ok:
            return self.display_completions(view, event)

        return self.cancel()

    def display_completions(self, view, event):
        """Find completions and display them."""
        doc = view.get_buffer()
        insert = doc.get_iter_at_mark(doc.get_insert())

        offset = 0
        incomplete = u''
        text = unicode(
            doc.get_text(*doc.get_bounds(), include_hidden_chars=True),
            'utf-8')

        # We first get the line we're in and verify we're not in a line comment
        linestart = insert.copy()
        linestart.set_offset(insert.get_offset() - insert.get_line_offset())
        current_line = unicode(
            doc.get_text(linestart, insert, include_hidden_chars=True),
            'utf-8')
        comment_index = current_line.find('//')
        if comment_index != -1 and insert.get_line_offset() > comment_index:
            return self.cancel()

        # Now we look if we're potentially inside a multi line comment, in which
        # case, we cancel the completion as well
        closing_multi_comment_index = text.rfind('*/', 0, insert.get_offset())
        opening_multi_comment_index = text.rfind('/*', 0, insert.get_offset())
        if opening_multi_comment_index > closing_multi_comment_index:
            return self.cancel()

        # Check if cursor is inside of a string (between two "s or 's)
        # If so, cancel the completion
        # TODO improve re_singleline_comments regex so it doesn't match comments inside a string

        #self.t = time.time()
        #print "inside-of-string check started"
        text_copy_1 = text[:insert.get_offset()]
        text_copy_1 = text_copy_1.replace("\\\"", "").replace("\\\'", "")
        text_copy_1 = re.sub(self.re_multiline_comments, "", text_copy_1)
        #ignore multi-line comments
        """ remove slashes inside of strings """
        quote_open = False
        quote_type = ''
        last_char_slash = False
        comment_open = False
        for i1, c1 in enumerate(text_copy_1):
            if c1 == '\"':
                if (quote_open) and (quote_type == '\"'):
                    quote_open = False
                    quote_type = ''
                if (not quote_open) and (not comment_open):
                    quote_open = True
                    quote_type = '\"'
            elif c1 == '\'':
                if (quote_open) and (quote_type == '\''):
                    quote_open = False
                    quote_type = ''
                if (not quote_open) and (not comment_open):
                    quote_open = True
                    quote_type = '\''
            elif c1 == '/':
                if last_char_slash and (not quote_open):
                    comment_open = True
                if quote_open:
                    text_copy_1 = text_copy_1[:i1] + text_copy_1[i1 + 1:]
                last_char_slash = True
            elif c1 == '\n':
                comment_open = False
            else:
                last_char_slash = False
        """ remove slashes inside of strings done """
        text_copy_1 = re.sub(self.re_singleline_comments, "", text_copy_1)
        #ignore single-line comments

        quote_exists = True
        while quote_exists:
            doublequote_start_index = text_copy_1.find("\"", 0)
            singlequote_start_index = text_copy_1.find("'", 0)
            if ((doublequote_start_index < singlequote_start_index)
                    or singlequote_start_index
                    == -1) and (doublequote_start_index != -1):
                doublequote_end_index = text_copy_1.find(
                    "\"", doublequote_start_index + 1)
                if doublequote_end_index != -1:
                    text_copy_1 = (text_copy_1[:doublequote_start_index] +
                                   text_copy_1[doublequote_end_index + 1:])
                else:
                    #print time.time() - self.t
                    #print "inside-of-string check done"
                    return self.cancel()
            elif ((singlequote_start_index < doublequote_start_index)
                  or doublequote_start_index
                  == -1) and (singlequote_start_index != -1):
                singlequote_end_index = text_copy_1.find(
                    "'", singlequote_start_index + 1)
                if singlequote_end_index != -1:
                    text_copy_1 = (text_copy_1[:singlequote_start_index] +
                                   text_copy_1[singlequote_end_index + 1:])
                else:
                    #print time.time() - self.t
                    #print "inside-of-string check done"
                    return self.cancel()
            else:
                quote_exists = False
                break

        #print time.time() - self.t
        #print "inside-of-string check done"

        # We get the incomplete word.
        start = insert.copy()
        while start.backward_char():
            char = unicode(start.get_char(), 'utf-8')
            if not self.re_alpha.match(char) or char == ".":
                start.forward_char()
                break
        incomplete = unicode(
            doc.get_text(start, insert, include_hidden_chars=True), 'utf-8')

        # If the number we're trying to complete is a digit, we stop the completion
        if incomplete.isdigit():
            return self.cancel()

        if event.keyval == Gdk.KEY_period:
            # If we complete with the dot key, gedit won't have inserted it prior to calling
            # this function, which is why we add the dot manually
            offset = insert.get_offset()
            text = text[:offset] + '.' + text[offset:]
            if text[offset -
                    1] == '.':  # We don't want to complete something we KNOW is incorrect.
                # This is just to avoid waiting when doing the ... operator.
                return self.cancel()
            offset += 1
            incomplete = ""  # When pressing . the incomplete word is the one right before the dot.
        else:
            offset = start.get_offset()

        offset = len(text[:offset].encode("utf-8"))

        # We call the haxe engine to get the list of completion.
        completes = haxe_complete(doc.get_uri_for_display(), text, offset)

        # Nothing in the completion list, so no need to do anything
        if not completes:
            return self.cancel()
        #print incomplete
        #print time.time() - self.t
        #print "display_completions done"
        #self.t = time.time()

        self.show_popup(view, completes, incomplete)

    def show_popup(self, view, completions, tempstr=""):
        """Show the completion window."""
        self.islaunching = True
        self.tempstring = ""

        # Determine the position in x, y of the insert mark to later place the window
        wd = Gtk.TextWindowType.TEXT
        doc = view.get_buffer()
        rect = view.get_iter_location(doc.get_iter_at_mark(doc.get_insert()))
        x, y = view.buffer_to_window_coords(wd, rect.x, rect.y)
        x, y = view.translate_coordinates(self.window, x, y)

        # Get the original position of the Window
        root_x, root_y = self.window.get_position()
        # Create the popup
        """
        if self.w == None:
            self.w = CompletionWindow(self.window, self)
        popup = self.w
        """
        popup = CompletionWindow(self.window, self)
        popup.apiInfoPanel = self.apiInfoPanel
        # Set its font accordingly
        context = view.get_pango_context()
        font_desc = context.get_font_description()
        popup.set_font_description(font_desc)
        # Position it
        # FIXME : Position should depend on the font.
        popup.move(root_x + x + 24, root_y + y + 44)
        # Set the completion list

        popup.set_completions(completions, tempstr)
        popup.show_all()
Example #22
0
class SourceCodeBrowserPlugin(GObject.Object, Gedit.WindowActivatable, PeasGtk.Configurable):
    """
    Source Code Browser Plugin for Gedit 3.x
    
    Adds a tree view to the side panel of a Gedit window which provides a list
    of programming symbols (functions, classes, variables, etc.).
    
    https://live.gnome.org/Gedit/PythonPluginHowTo
    """
    __gtype_name__ = "SourceCodeBrowserPlugin"
    window = GObject.property(type=Gedit.Window)

    def __init__(self):
        GObject.Object.__init__(self)
        self._log = logging.getLogger(self.__class__.__name__)
        self._log.setLevel(LOG_LEVEL)
        self._is_loaded = False
        self._ctags_version = None

        filename = os.path.join(ICON_DIR, "source-code-browser.png")
        self.icon = Gtk.Image.new_from_file(filename)
    
    def do_create_configure_widget(self):
        return Config().get_widget(self._has_settings_schema())
        
    def do_activate(self):
        """ Activate plugin """
        self._log.debug("Activating plugin")
        self._init_settings()
        self._version_check()
        self._sourcetree = SourceTree()
        self._sourcetree.ctags_executable = self.ctags_executable
        self._sourcetree.show_line_numbers = self.show_line_numbers
        self._sourcetree.expand_rows = self.expand_rows
        self._sourcetree.sort_list = self.sort_list
        panel = self.window.get_side_panel()
        panel.add_item(self._sourcetree, "SymbolBrowserPlugin", "Source Code", self.icon)
        self._handlers = []
        hid = self._sourcetree.connect("focus", self.on_sourcetree_focus)
        self._handlers.append((self._sourcetree, hid))
        if self.ctags_version is not None:
            hid = self._sourcetree.connect('tag-activated', self.on_tag_activated)
            self._handlers.append((self._sourcetree, hid))
            hid = self.window.connect("active-tab-state-changed", self.on_tab_state_changed)
            self._handlers.append((self.window, hid))
            hid = self.window.connect("active-tab-changed", self.on_active_tab_changed)
            self._handlers.append((self.window, hid))
            hid = self.window.connect("tab-removed", self.on_tab_removed)
            self._handlers.append((self.window, hid))
        else:
            self._sourcetree.set_sensitive(False)
    
    def do_deactivate(self):
        """ Deactivate the plugin """
        self._log.debug("Deactivating plugin")
        for obj, hid in self._handlers:
            obj.disconnect(hid)
        self._handlers = None
        pane = self.window.get_side_panel()
        pane.remove_item(self._sourcetree)
        self._sourcetree = None
    
    def _has_settings_schema(self):
        schemas = Gio.Settings.list_schemas()
        if not SETTINGS_SCHEMA in schemas:
            return False
        else:
            return True
            
    def _init_settings(self):
        """ Initialize GSettings if available. """
        if self._has_settings_schema():
            settings = Gio.Settings.new(SETTINGS_SCHEMA)
            self.load_remote_files = settings.get_boolean("load-remote-files")
            self.show_line_numbers = settings.get_boolean("show-line-numbers")
            self.expand_rows = settings.get_boolean("expand-rows")
            self.sort_list = settings.get_boolean("sort-list")
            self.ctags_executable = settings.get_string("ctags-executable")
            settings.connect("changed::load-remote-files", self.on_setting_changed)
            settings.connect("changed::show-line-numbers", self.on_setting_changed)
            settings.connect("changed::expand-rows", self.on_setting_changed)
            settings.connect("changed::sort-list", self.on_setting_changed)
            settings.connect("changed::ctags-executable", self.on_setting_changed)
            self._settings = settings
        else:
            self._log.warn("Settings schema not installed. Plugin will not be configurable.")
            self._settings = None
            self.load_remote_files = True
            self.show_line_numbers = False
            self.expand_rows = True
            self.sort_list = True
            self.ctags_executable = 'ctags'
   
    def _load_active_document_symbols(self):
        """ Load the symbols for the given URI. """
        self._sourcetree.clear()
        self._is_loaded = False
        # do not load if not the active tab in the panel
        panel = self.window.get_side_panel()
        if not panel.item_is_active(self._sourcetree):
            return

        document = self.window.get_active_document()
        if document:
            location = document.get_location()
            if location:
                uri = location.get_uri()
                self._log.debug("Loading %s...", uri)
                if uri is not None:
                    if uri[:7] == "file://":
                        # use get_parse_name() to get path in UTF-8
                        filename = location.get_parse_name() 
                        self._sourcetree.parse_file(filename, uri)
                    elif self.load_remote_files:
                        basename = location.get_basename()
                        fd, filename = tempfile.mkstemp('.'+basename)
                        contents = document.get_text(document.get_start_iter(),
                                                     document.get_end_iter(),
                                                     True)
                        os.write(fd, contents)
                        os.close(fd)
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        self._sourcetree.parse_file(filename, uri)
                        os.unlink(filename)
                    self._loaded_document = document
        self._is_loaded = True
            
    def on_active_tab_changed(self, window, tab, data=None):
        self._load_active_document_symbols()
    
    def on_setting_changed(self, settings, key, data=None):
        """
        self.load_remote_files = True
        self.show_line_numbers = False
        self.expand_rows = True
        self.ctags_executable = 'ctags'
        """
        if key == 'load-remote-files':
            self.load_remote_files = self._settings.get_boolean(key)
        elif key == 'show-line-numbers':
            self.show_line_numbers = self._settings.get_boolean(key)
        elif key == 'expand-rows':
            self.expand_rows = self._settings.get_boolean(key)
        elif key == 'sort-list':
            self.sort_list = self._settings.get_boolean(key)
        elif key == 'ctags-executable':
            self.ctags_executable = self._settings.get_string(key)
        
        if self._sourcetree is not None:
            self._sourcetree.ctags_executable = self.ctags_executable
            self._sourcetree.show_line_numbers = self.show_line_numbers
            self._sourcetree.expand_rows = self.expand_rows
            self._sourcetree.sort_list = self.sort_list
            self._sourcetree.expanded_rows = {}
            self._load_active_document_symbols()
    
    def on_sourcetree_focus(self, direction, data=None):
        if not self._is_loaded:
            self._load_active_document_symbols()
        return False
        
    def on_tab_state_changed(self, window, data=None):
        self._load_active_document_symbols()
    
    def on_tab_removed(self, window, tab, data=None):
        if not self.window.get_active_document():
            self._sourcetree.clear()
        
    def on_tag_activated(self, sourcetree, location, data=None):
        """ Go to the line where the double-clicked symbol is defined. """
        #pdb.set_trace()
        os.system('gnome-terminal -x vi -t ' + location[0].get_value(location[1], 1) + ' &')     
 
    def _version_check(self):
        """ Make sure the exhuberant ctags is installed. """
        self.ctags_version = ctags.get_ctags_version(self.ctags_executable) 
        if not self.ctags_version:
            self._log.warn("Could not find ctags executable: %s" % 
                           (self.ctags_executable))
Example #23
0
class Port(GObject.GObject):
    """Generic (sink/source/card) port class."""
    def get_is_active(self):
        """is_active: defines whether the associated port is the active one for its sink/source."""
        return self._is_active

    def set_is_active(self, value: bool):
        self._is_active = value

        # If activated, also activate the item that corresponds to the port
        if self.is_active and self.menu_item:
            # Inhibit the activate event
            with self.menu_item.handler_block(self.handler_id):
                self.menu_item.set_active(True)

    is_active = GObject.property(type=bool,
                                 default=False,
                                 getter=get_is_active,
                                 setter=set_is_active)

    def get_is_available(self):
        """is_available: defines whether the associated port is the available for the user."""
        return self._is_available or self.is_dummy  # A dummy port is considered "always available"

    def set_is_available(self, value: bool):
        self._is_available = value

        # Show or hide the corresponding menu item
        if self.menu_item:
            if self.is_available or self.always_avail:
                self.menu_item.show()
            else:
                self.menu_item.hide()

    is_available = GObject.property(type=bool,
                                    default=False,
                                    getter=get_is_available,
                                    setter=set_is_available)

    def __init__(self, name: str, description, display_name: str,
                 priority: int, is_available: bool, is_visible: bool,
                 direction: int, profiles, pref_profile, always_avail: bool):
        """Constructor.
        :param name:          (Internal) name of the port
        :param description:   Default 'human friendly' name of the port
        :param display_name:  Port display name overriden by user. If empty, description is to be used
        :param priority:      Port priority as reported by PulseAudio
        :param is_available:  Whether the port is available for the user
        :param is_visible:    Whether the port is visible for the user
        :param direction:     Port direction (input/output), one of the PA_DIRECTION_* constants
        :param profiles:      List of strings, name of the profiles that support this port
        :param pref_profile:  Name of the preferred profile for the port, if any, otherwise None
        :param always_avail:  If True, the port is always shown disregarding its availability state
        """
        GObject.GObject.__init__(self)
        self.name = name
        self.description = description
        self.display_name = display_name
        self.priority = priority
        self._is_available = is_available
        self.is_visible = is_visible
        self.direction = direction
        self.profiles = profiles
        self.pref_profile = pref_profile
        self.always_avail = always_avail

        # Initialise other properties
        # -- Owner source/sink in case this is a source/sink port, otherwise None
        self.owner_stream = None
        # -- Owner card in case this is a card port, otherwise None
        self.owner_card = None
        # -- Menu item associated with this port, if any, otherwise None
        self.menu_item = None
        # -- Property storage: is_active
        self._is_active = False

        # Initialise derived properties
        # -- Whether it's a dummy port created for a portless device
        self.is_dummy = description is None
        # -- Whether it's an output port
        self.is_output = direction == lib_pulseaudio.PA_DIRECTION_OUTPUT

        # Activate signal's handler ID (will be used for inhibiting the handler later)
        self.handler_id = None

    def get_display_name(self) -> str:
        """Returns display name for the port."""
        return self.display_name or self.description

    def get_menu_item_title(self) -> str:
        """Returns the title to be used with menu item."""
        # Port on a physical device
        if self.owner_card is not None:
            owner_name = self.owner_card.get_display_name()
        # Port on a network sink/source
        elif self.owner_stream is not None:
            owner_name = self.owner_stream.get_display_name()
        # WTF port
        else:
            owner_name = _('(unknown device)')

        # Append port name if it isn't dummy
        return owner_name + ('' if self.is_dummy else ' ‣ ' +
                             self.get_display_name())
Example #24
0
class DesktopViewModel(GObject.GObject):
    __gtype_name__ = 'SugarDesktopViewModel'
    __gsignals__ = {
        'desktop-view-icons-changed':
        (GObject.SignalFlags.RUN_FIRST, None, ([]))
    }

    def __init__(self):
        GObject.GObject.__init__(self)

        self._number_of_views = 1
        self._view_icons = None
        self._favorite_icons = None

        self._ensure_view_icons()

        client = GConf.Client.get_default()
        client.add_dir(_VIEW_KEY, GConf.ClientPreloadType.PRELOAD_NONE)
        self._gconf_id = client.notify_add(_VIEW_KEY, self.__gconf_changed_cb,
                                           None)

    def get_view_icons(self):
        return self._view_icons

    view_icons = GObject.property(type=object, getter=get_view_icons)

    def get_number_of_views(self):
        return self._number_of_views

    number_of_views = GObject.property(type=object, getter=get_number_of_views)

    def get_favorite_icons(self):
        return self._favorite_icons

    favorite_icons = GObject.property(type=object, getter=get_favorite_icons)

    def _ensure_view_icons(self, update=False):
        if self._view_icons is not None and not update:
            return

        client = GConf.Client.get_default()

        options = client.get(_VIEW_KEY)
        self._view_icons = []
        if options is not None:
            for gval in options.get_list():
                self._view_icons.append(gval.get_string())

        if self._view_icons is None or self._view_icons == []:
            self._view_icons = _VIEW_ICONS[:]

        self._number_of_views = len(self._view_icons)

        options = client.get(_FAVORITE_KEY)
        if options is not None:
            self._favorite_icons = []
            for gval in options.get_list():
                self._favorite_icons.append(gval.get_string())
        else:
            self._favorite_icons = _FAVORITE_ICONS[:]

        if len(self._favorite_icons) < self._number_of_views:
            for i in range(self._number_of_views - len(self._favorite_icons)):
                self._favorite_icons.append(_FAVORITE_ICONS[0])

        self.emit('desktop-view-icons-changed')

    def __del__(self):
        client = GConf.Client.get_default()
        client.notify_remove(self._gconf_id)

    def __gconf_changed_cb(self, client, timestamp, entry, *extra):
        self._ensure_view_icons(update=True)
Example #25
0
class WsOverviewWin(GObject.Object):
    mode_index = GObject.property(type=int, default=2)

    def __init__(self):

        GObject.Object.__init__(self)
        settings = Gio.Settings.new(
            "org.ubuntubudgie.plugins.budgie-wsoverview")
        settings.bind("ws-overview-index", self, 'mode_index',
                      Gio.SettingsBindFlags.DEFAULT)
        # general
        self.mode = modes[self.mode_index]

        self.appbutton = Gtk.Button.new()
        self.appbutton.set_relief(Gtk.ReliefStyle.NONE)

        icon = Gtk.Image.new_from_icon_name("ws1-symbolic", Gtk.IconSize.MENU)
        self.appbutton.set_image(icon)

        self.menu = Gtk.Menu()
        self.create_menu()
        self.update = Thread(target=self.show_seconds)
        # daemonize the thread to make the indicator stopable
        self.update.setDaemon(True)
        self.update.start()

    def create_menu(self):
        message = Gtk.MenuItem('Starting up...')
        self.menu.append(message)
        self.menu.show_all()
        self.popup = self.menu
        self.appbutton.connect('clicked', self.popup_menu)

    def edit_menu(self):
        for i in self.menu.get_children():
            self.menu.remove(i)
        for m in self.newmenu:
            add = Gtk.MenuItem(m)
            add.connect('activate', self.get_choice)
            self.menu.append(add)
        # fake separator
        self.menu.append(Gtk.MenuItem(''))
        newspace = Gtk.MenuItem('+')
        newspace.connect('activate', self.add_space)
        self.menu.append(newspace)
        self.change_onthefly()
        self.menu.show_all()

    def edit_menu2(self):
        for i in self.menu.get_children():
            self.menu.remove(i)
        for m in self.newmenu:
            ws = str(m[0] + 1)
            space = Gtk.MenuItem(ws)
            self.menu.append(space)
            if m[1]:
                # flattened submenu
                self.submenu = Gtk.Menu()
                for sl in [d for d in m[1]]:
                    app = sl[0]
                    wins = [[it[0], it[1]] for it in sl[1]]
                    for w in wins:
                        name = self.shortname(w[0]) + "  -  " + app
                        winmention = Gtk.MenuItem(name)
                        self.submenu.append(winmention)
                        winmention.connect('activate', self.move_to, w[1])
                space.set_submenu(self.submenu)
            else:
                space.connect('activate', self.get_choice)
        # fake separator
        self.menu.append(Gtk.MenuItem(''))
        newspace = Gtk.MenuItem('+')
        newspace.connect('activate', self.add_space)
        self.menu.append(newspace)
        self.change_onthefly()
        self.menu.show_all()

    def change_onthefly(self):
        modesep = Gtk.SeparatorMenuItem()
        self.menu.add(modesep)
        mode_mention = Gtk.MenuItem("Mode")
        applet_modes = Gtk.Menu()
        active = modes.index(self.mode)
        self.mode_index = active
        self.menulist = ["\t" + m for m in modes]
        self.menulist[active] = "⁕\t" + str(modes[active]) + ""
        for item in self.menulist:
            md = Gtk.MenuItem(item)
            md.connect('activate', self.set_mode, item)
            applet_modes.append(md)
        mode_mention.set_submenu(applet_modes)
        self.menu.add(mode_mention)

    def set_mode(self, widget, arg):
        self.mode = modes[self.menulist.index(arg)]

    def shortname(self, name):
        """shorten too long names for the menu"""
        limit = 35
        return name[:limit - 3] + "..." if len(name) >= limit else name

    def get_choice(self, mention, *args):
        # move to selected workspace
        index = self.menu.get_children().index(self.menu.get_active())
        subprocess.Popen(["wmctrl", "-s", str(index)])

    def move_to(self, button, wid):
        subprocess.Popen(["wmctrl", "-ia", wid])

    def add_space(self, *args):
        # add one workspace
        settings = Gio.Settings.new("org.gnome.desktop.wm.preferences")
        wkspace = settings["num-workspaces"]
        new_n = wkspace + 1
        settings.set_int("num-workspaces", new_n)

    def show_seconds(self):
        wsdata1, n_ws, curr_ws, curr_windata = None, None, None, None
        update_menu, update_menuset, set_icon = False, False, False
        menuset = []
        # cycle time (sec) for update windowlist
        c2 = 3
        t2 = 0
        self.mode1 = self.mode

        while True:
            # master cycle = 1 sec
            time.sleep(1)
            # test_1: windowlist (cycle2 = 3 sec)
            if all([self.mode in modes[0:3], t2 == 0]):
                # see if mode has changed, act if needed
                self.mode2 = self.mode
                if self.mode2 != self.mode1:
                    update_menu = True
                    update_menuset = True
                    self.mode1 = self.mode2
                """see if data on windowslist changed, if not,
                don't rebuild menudata
                """
                new_windata = wtls.get(["wmctrl", "-l"])
                if all([new_windata, new_windata != curr_windata]):
                    update_menu, update_menuset = True, True
                    curr_windata = new_windata
            elif t2 == 0:
                new_windata = wtls.update_winmenu(curr_windata)
                if new_windata != curr_windata:
                    update_menu, update_menuset = True, True
                    curr_windata = new_windata
            t2 = 0 if t2 == c2 - 1 else t2 + 1
            # test_2: workspace- changes (master_cycle)
            wsdata2 = wtls.get(["wmctrl", "-d"])
            if wsdata2 != wsdata1:
                sp = wtls.getspaces(wsdata2)
                sp0 = sp[0]
                sp1 = sp[1]
                if sp0 != n_ws:
                    update_menu = True
                    n_ws = sp0
                if sp1 != curr_ws:
                    set_icon = True
                    curr_ws = sp1
                wsdata1 = wsdata2
            # apply possible results
            if set_icon:
                newic = wtls.new_icon(curr_ws)
                icon = Gtk.Image.new_from_icon_name(newic, Gtk.IconSize.MENU)
                GObject.idle_add(self.appbutton.set_image,
                                 icon,
                                 priority=GObject.PRIORITY_DEFAULT)
            if self.mode == "Menu":
                if update_menuset:
                    # update window-contents for menu
                    menuset = new_windata[1]
                if update_menu:
                    self.newmenu = []
                    for n in range(n_ws):
                        try:
                            appdata = [
                                applist[1:] for applist in menuset
                                if applist[0] == n
                            ][0]
                            self.newmenu.append([n, appdata])
                        except IndexError:
                            self.newmenu.append([n, None])
                    GObject.idle_add(self.edit_menu2,
                                     priority=GObject.PRIORITY_DEFAULT)
            else:
                if update_menuset:
                    # update window-contents for menu
                    menuset = wtls.get_menuset(curr_windata)
                if update_menu:
                    if self.mode == "Dotted":
                        self.newmenu = [
                            " ".join([str(n + 1),
                                      menuset.count(str(n)) * "•"])
                            for n in range(n_ws)
                        ]
                    # limit dots to 3
                    elif self.mode == "Elipsed dotted":
                        self.newmenu = []
                        for n in range(n_ws):
                            n_dots = menuset.count(str(n))
                            n_dots = n_dots if n_dots <= 3 else 3
                            self.newmenu.append(" ".join(
                                [str(n + 1), n_dots * "•"]))
                    if self.mode == "Simple":
                        self.newmenu = [str(n + 1) for n in range(n_ws)]
                        pass
                    GObject.idle_add(self.edit_menu,
                                     priority=GObject.PRIORITY_DEFAULT)
                    # reset all
            set_icon, update_menuset, update_menu = False, False, False

    def popup_menu(self, *args):
        self.popup.popup(None, None, None, None, 0,
                         Gtk.get_current_event_time())
Example #26
0
class LooperPlugin(GObject.Object, Peas.Activatable):
    """
    Loops part of the song defined by Start and End Gtk sliders.
    """
    object = GObject.property(type=GObject.Object)

    # Available positions in the RB GUI.
    # They are chosen through user settings.
    POSITIONS = {
        'TOP': RB.ShellUILocation.MAIN_TOP,
        'BOTTOM': RB.ShellUILocation.MAIN_BOTTOM,
        'SIDEBAR': RB.ShellUILocation.SIDEBAR,
        'RIGHT SIDEBAR': RB.ShellUILocation.RIGHT_SIDEBAR,
    }

    STATUS_TPL = Template('[Loop Duration: $duration] ' +
                          '[Current time: $time]')

    # Number of seconds that the End slider is less than a song
    # duration. Its needed because in that period Rhythmbox would
    # change to the next song. Thats why we dont go there.
    SEC_BEFORE_END = 3

    LOOPS_FILENAME = '.loops.json'

    LOOPS_PER_ROW = 8

    MAX_LOOPS_NUM = 32

    UI = """
    <ui>
        <menubar name="MenuBar">
            <menu name="ViewMenu" action="View">
                <menuitem name="LooperPlugin" action="ActivateLooper" />
            </menu>
        </menubar>
    </ui>
    """

    def __init__(self):
        super(LooperPlugin, self).__init__()

    def do_activate(self):
        self.load_css()
        self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.looper")
        # old value will be needed when removing/adding(moving) GUI
        self.gui_position = self.POSITIONS[self.settings['position']]
        self.shell = self.object
        self.shell_player = self.shell.props.shell_player
        self.player = self.shell_player.props.player
        self.db = self.shell.props.db

        self.appshell = ApplicationShell(self.shell)
        self.main_box = Gtk.Box()
        self.main_box.set_orientation(Gtk.Orientation.VERTICAL)

        self.controls_box = Gtk.Box()
        self.controls_box.set_orientation(Gtk.Orientation.VERTICAL)

        self._create_main_action()

        self.controls = Controls(self)
        controls_frame = Gtk.Frame()
        controls_frame.add(self.controls)
        controls_frame.set_property('margin-left', 2)
        controls_frame.set_property('margin-right', 2)
        self.rbpitch = RbPitch(self)
        rbpitch_frame = Gtk.Frame()
        rbpitch_frame.add(self.rbpitch)
        rbpitch_frame.set_property('margin-left', 2)
        rbpitch_frame.set_property('margin-right', 2)
        self.controls_box.pack_start(rbpitch_frame, True, True, 5)
        self.controls_box.pack_start(controls_frame, True, True, 5)

        self.loops_box = Gtk.Grid()
        self.loops_box.set_row_spacing(2)
        self.loops_box.set_column_spacing(2)
        self.loops_box.set_column_homogeneous(True)
        self.loops_box.set_row_homogeneous(True)
        self.loops_box.set_border_width(0)

        self.rb_slider = self.find_rb_slider()

        self.main_box.pack_start(self.controls_box, True, True, 10)
        self.main_box.pack_start(
            Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), True, True,
            0)
        self.main_box.pack_start(self.loops_box, True, True, 10)

        # position = self.POSITIONS[self.settings['position']]
        self.shell.add_widget(self.main_box, self.gui_position, True, False)

        self.rbpitch.tempo.slider.set_value(self.rbpitch.tempo_val)
        self.rbpitch.pitch.slider.set_value(self.rbpitch.pitch_val)
        self.rbpitch.rate.slider.set_value(self.rbpitch.rate_val)

        self.save_crossfade_settings()

        self.settings_changed_sigid = self.settings.connect(
            'changed', self.on_settings_changed)

        self.song_changed_sigid = self.shell_player.connect(
            "playing-song-changed", self.on_playing_song_changed)

        # a song COULD be playing, so refresh sliders
        self.refresh_widgets()
        self.refresh_status_label()

        if self.settings['always-show']:
            self.refresh_rb_position_slider()
            self.main_box.show_all()

        self.loops = {}
        self.load_loops_file()
        self.loops_box.hide()

    def load_css(self):
        cssProvider = Gtk.CssProvider()
        css_path = rb.find_plugin_file(self, 'looper.css')
        cssProvider.load_from_path(css_path)
        screen = Gdk.Screen.get_default()
        styleContext = Gtk.StyleContext()
        styleContext.add_provider_for_screen(screen, cssProvider,
                                             Gtk.STYLE_PROVIDER_PRIORITY_USER)

    def on_activation(self, *args):
        action = self.actions.get_action('ActivateLooper')
        if action.get_active():
            # connect elapsed handler/signal to handle the loop
            self.elapsed_changed_sigid = self.shell_player.connect(
                "elapsed-changed", self.loop)

            # Disable cross fade. It interferes at the edges of the song ..
            if (self.crossfade and self.crossfade.get_active()):
                self.crossfade.set_active(False)
            self.refresh_rb_position_slider()
            self.controls_box.show_all()
            if self.shell_player.get_playing_song_duration() > -1:
                self.loops_box.show_all()
        else:
            # disconnect the elapsed handler from hes duty
            self.shell_player.disconnect(self.elapsed_changed_sigid)
            del self.elapsed_changed_sigid
            # Restore users crossfade if it was enabled
            if self.crossfade and self.was_crossfade_active:
                self.crossfade.set_active(True)
            if not self.settings['always-show']:
                self.main_box.hide()
                if hasattr(self, 'rb_slider'):
                    self.rb_slider.clear_marks()
            self.loops_box.hide()

        self.refresh_status_label()

    def _create_main_action(self):
        self.actions = ActionGroup(self.shell, 'LooperActionGroup')
        self.actions.add_action(func=self.on_activation,
                                action_name='ActivateLooper',
                                label=_("Looper"),
                                action_state=ActionGroup.TOGGLE,
                                action_type='app',
                                accel="<Ctrl>e",
                                tooltip=_("Loop part of the song"))
        self.appshell.insert_action_group(self.actions)
        self.appshell.add_app_menuitems(self.UI, 'LooperActionGroup', 'view')
        self.action = self.appshell.lookup_action('LooperActionGroup',
                                                  'ActivateLooper', 'app')

    def find_rb_slider(self):
        rb_toolbar = self.find(self.shell.props.window, 'ToolBar', 'by_name')
        if not rb_toolbar:
            rb_toolbar = self.find(self.shell.props.window, 'main-toolbar',
                                   'by_id')
        return self.find(rb_toolbar, 'GtkScale', 'by_name')

    # Couldn't find better way to find widgets than loop through them
    def find(self, node, search_id, search_type):
        if isinstance(node, Gtk.Buildable):
            if search_type == 'by_id':
                if Gtk.Buildable.get_name(node) == search_id:
                    return node
            elif search_type == 'by_name':
                if node.get_name() == search_id:
                    return node

        if isinstance(node, Gtk.Container):
            for child in node.get_children():
                ret = self.find(child, search_id, search_type)
                if ret:
                    return ret
        return None

    def save_crossfade_settings(self):
        # We need to disable cross fade while Looper is active. So store
        # RB's xfade widget and user preference for later use.
        # Next line throws error in the Rhythmbox's console. Dont know why.
        prefs = self.shell.props.prefs  # <-- error. Unreleased refs maybe.
        self.crossfade = self.find(prefs, 'use_xfade_backend', 'by_id')
        self.was_crossfade_active = False
        if self.crossfade and self.crossfade.get_active():
            self.was_crossfade_active = True

    def on_settings_changed(self, settings, setting):
        """Handles changes to settings."""
        if setting == 'position':
            self.shell.remove_widget(self.main_box, self.gui_position)
            new_gui_position = self.POSITIONS[self.settings['position']]
            self.shell.add_widget(self.main_box, new_gui_position, True, False)
            self.gui_position = new_gui_position
        elif setting == 'always-show':
            action = self.actions.get_action('ActivateLooper')
            if settings['always-show']:
                self.main_box.show_all()
                if action.get_active() is not True:
                    self.loops_box.hide()
            else:
                if action.get_active() is not True:
                    self.main_box.hide()

    def on_playing_song_changed(self, source, user_data):
        """Refresh sliders and RB's position marks."""
        self.refresh_widgets()
        self.clear_loops()
        self.load_song_loops()
        action = self.actions.get_action('ActivateLooper')
        if action.get_active() is True:
            self.refresh_rb_position_slider()
            self.loops_box.show_all()
        else:
            self.loops_box.hide()

    def refresh_song_duration(self):
        duration = self.shell_player.get_playing_song_duration()
        if duration != -1 and duration >= (self.SEC_BEFORE_END + 2):
            self.duration = duration - self.SEC_BEFORE_END
        else:
            self.duration = None

    def refresh_rb_position_slider(self):
        """
        Add marks to RB's position slider with Looper's start/end time values.
        """
        # Add start and end marks to the position slider
        action = self.actions.get_action('ActivateLooper')
        if self.rb_slider and (action.get_active()
                               or self.settings['always-show']):
            self.rb_slider.clear_marks()

            start_time = seconds_to_time(
                self.controls.start_slider.get_value())
            end_time = seconds_to_time(self.controls.end_slider.get_value())

            self.rb_slider.add_mark(self.controls.start_slider.get_value(),
                                    Gtk.PositionType.TOP, start_time)
            self.rb_slider.add_mark(self.controls.end_slider.get_value(),
                                    Gtk.PositionType.TOP, end_time)

    def clear_loops(self):
        for child in self.loops_box.get_children():
            child.deactivate()
            self.loops_box.remove(child)

    def load_song_loops(self):
        song_id = self.get_song_id()
        if song_id:
            if song_id and song_id in self.loops:
                self.load_loops(self.loops[song_id])

    def on_save_loop(self, button):
        song_id = self.get_song_id()
        if song_id:
            if song_id not in self.loops:
                self.loops[song_id] = []
            if len(self.loops[song_id]) >= self.MAX_LOOPS_NUM:
                return
            name = '{} - {}'.format(
                seconds_to_time(self.controls.start_slider.get_value()),
                seconds_to_time(self.controls.end_slider.get_value()),
            )
            loop = {
                'end': self.controls.end_slider.get_value(),
                'start': self.controls.start_slider.get_value(),
                'name': name
            }
            self.loops[song_id].append(loop)
            self.save_loops()
            self.clear_loops()
            self.load_loops(self.loops[song_id])

    def refresh_status_label(self):
        status_vars = {'duration': '00:00', 'time': '00:00'}
        status_text = self.STATUS_TPL.substitute(status_vars)
        self.controls.status_label.set_text(status_text)
        self.controls.status_label.set_fraction(0)

    def refresh_widgets(self):
        self.refresh_song_duration()
        self.controls.refresh_min_range_button()
        self.controls.refresh_sliders()

    def load_loops_file(self):
        loops_file = self.get_loops_file_path()
        if loops_file:
            with open(loops_file, 'r') as f:
                try:
                    self.loops = json.loads(f.read())
                except ValueError as e:
                    sys.stderr.write('Error on loading %s: %s\n' %
                                     (loops_file, e))

    def get_loops_file_path(self):
        home = os.path.expanduser('~')
        loops_file = os.path.join(home, self.LOOPS_FILENAME)
        if not os.path.isfile(loops_file):
            with open(loops_file, 'w') as f:
                data = {}
                f.write(json.dumps(data))
        return loops_file

    def get_grid_column_and_row(self):
        number_of_children = len(self.loops_box.get_children())
        row, column = divmod(number_of_children, self.LOOPS_PER_ROW)
        return column, row

    def get_song_id(self):
        if not self.entry:
            return None
        song_id = u'{0}-{1}'.format(self.song_artist, self.song_title)
        if not song_id:
            song_id = self.entry.get_playback_uri()
        return hashlib.md5(song_id.encode('utf8')).hexdigest()

    @property
    def entry(self):
        return self.shell_player.get_playing_entry()

    @property
    def song_title(self):
        if self.entry:
            return self.entry.get_string(RB.RhythmDBPropType.TITLE)
        return ''

    @property
    def song_artist(self):
        if self.entry:
            return self.entry.get_string(RB.RhythmDBPropType.ARTIST)
        return ''

    @property
    def song_path(self):
        if self.entry:
            return self.entry.get_string(RB.RhythmDBPropType.LOCATION)
        return ''

    def load_loops(self, loops):
        for index, loop in enumerate(loops):
            loop = loops[index]
            loop_control = LoopControl(self, index, loop['name'],
                                       loop['start'], loop['end'])
            # TODO:
            # Use ScrolledWindow instead of limited numbers of loops per grid row.
            # For now we will use limited number of loops per row as the
            # Gtk.ScrolledWindow is not working for some reason.
            row, column = self.get_grid_column_and_row()
            self.loops_box.attach(loop_control, row, column, 1, 1)
        action = self.actions.get_action('ActivateLooper')
        if action.get_active():
            self.loops_box.show_all()

    def save_loops(self):
        Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE,
                             self.save_loops_to_file)

    def save_loops_to_file(self):
        if self.loops:
            loops_file = self.get_loops_file_path()
            with open(loops_file, 'w') as f:
                f.write(json.dumps(self.loops))

    @property
    def start_slider_max(self):
        if self.duration:
            return self.duration - MIN_RANGE
        return 0

    @property
    def start_slider_min(self):
        return 0

    @property
    def end_slider_max(self):
        if self.duration:
            return self.duration
        return 0

    @property
    def end_slider_min(self):
        if self.duration:
            return MIN_RANGE
        return 0

    def loop(self, player, elapsed):
        """
        Signal handler called every second of the current playing song.
        Forces the song to stay inside Looper's slider limits.
        """
        # Start and End sliders values
        start = int(self.controls.start_slider.get_value())
        end = int(self.controls.end_slider.get_value())

        if self.rbpitch.gst_pitch and self.controls.rbpitch_btn.get_active(
        ) is True:
            tempo = self.rbpitch.tempo.slider.get_value()
            rate = self.rbpitch.rate.slider.get_value()
            start = math.floor(start / (tempo / 100) / (rate / 100))
            end = math.ceil(end / (tempo / 100) / (rate / 100))

        if elapsed < start:
            # current time is bellow Start slider so fast forward
            seek_time = start - elapsed
        elif elapsed >= end:
            # current time is above End slider so rewind
            seek_time = start - elapsed
        else:
            # current position is within sliders, so chill
            seek_time = False

        # Sometimes song change event interferes with seeking. Therefore
        # dont do anything if elapsed time is 0 (less than 1).
        if seek_time and elapsed > 0:
            try:
                self.shell_player.seek(seek_time)
            except GObject.GError:
                sys.stderr.write('Seek to ' + str(seek_time) + 's failed\n')
        self.update_label(elapsed, start, end)

    def update_label(self, elapsed, start, end):
        """Update label based on current song time and sliders positions."""
        current_loop_seconds = elapsed - start
        loop_duration_seconds = end - start
        if current_loop_seconds > 0 and (current_loop_seconds <=
                                         loop_duration_seconds):
            current_loop_time = seconds_to_time(current_loop_seconds)
            loop_duration = seconds_to_time(loop_duration_seconds)

            label = self.STATUS_TPL.substitute(duration=loop_duration,
                                               time=current_loop_time)
            self.controls.status_label.set_text(label)
            fraction = current_loop_seconds / loop_duration_seconds
            self.controls.status_label.set_fraction(fraction)

    def do_deactivate(self):
        self.save_loops_to_file()

        self.controls.destroy_widgets()
        self.rbpitch.destroy_widgets()

        # Restore users crossfade preference
        if self.crossfade and self.was_crossfade_active:
            self.crossfade.set_active(True)

        self.settings.disconnect(self.settings_changed_sigid)
        self.shell_player.disconnect(self.song_changed_sigid)
        if hasattr(self, 'elapsed_changed_sigid'):
            self.shell_player.disconnect(self.elapsed_changed_sigid)

        self.appshell.cleanup()

        self.main_box.set_visible(False)
        self.shell.remove_widget(self.main_box, RB.ShellUILocation.MAIN_TOP)

        if hasattr(self, 'rb_slider'):
            self.rb_slider.clear_marks()
            del self.rb_slider

        del self.controls
        del self.loops_box
        del self.crossfade
        del self.was_crossfade_active
        del self.shell_player
        del self.shell
        del self.player
        del self.db
        del self.appshell
        del self.main_box
        del self.controls_box
        del self.rbpitch
        del self.actions
        del self.action

    def log(self, *args):
        output = ', '.join([str(arg) for arg in args]) + '\n'
        sys.stdout.write(output)
Example #27
0
class FullscreenMargins(GObject.Object, Xed.WindowActivatable):
    __gtype_name__ = "FullscreenMargins"

    window = GObject.property(type=Xed.Window)

    def __init__(self):
        GObject.Object.__init__(self)
        self.margins = 0  # Currently used margins width

    def do_activate(self):
        self.handlers = [
            self.window.connect("window-state-event", self.on_state_changed),
            self.window.connect("tab-added", self.on_tab_added)
        ]

    def do_deactivate(self):
        # Remove event handelers
        for handler in self.handlers:
            self.window.disconnect(handler)
        # Remove margins
        if self.margins > 0:
            self.margins = 0
            self.set_all_margins()

    def do_update_state(self):
        pass

    def on_state_changed(self, win, state):
        """React to change of window state."""
        if (state.new_window_state & Gdk.WindowState.FULLSCREEN):
            self.margins = self.compute_size()
            self.set_all_margins()
        else:
            if self.margins > 0:
                self.margins = 0
                self.set_all_margins()

    def on_tab_added(self, win, tab):
        """Set margins for new tabs."""
        if self.margins > 0:
            self.set_margins(tab.get_view())

    def get_char_width(self):
        """Try to get current default font and calculate character width."""
        try:
            view = self.window.get_active_view()
            # Get font description (code from "text size" plugin)
            context = view.get_style_context()
            description = context.get_font(context.get_state()).copy()
            # Get font metrics
            pango_context = view.get_pango_context()
            lang = Pango.Language.get_default()
            metrics = pango_context.get_metrics(description, lang)
            # Calculate char width in pixels
            width = metrics.get_approximate_char_width() / Pango.SCALE
            return width
        except:
            return 8  # If it didn't work, just use some appropriate value

    def compute_size(self):
        """Compute optimal size of margins."""
        # Get current monitor number
        screen = self.window.get_screen()
        monitor_n = screen.get_monitor_at_window(self.window.get_window())
        # and its width
        scr_width = screen.get_monitor_geometry(monitor_n).width
        # Get gutter width
        view = self.window.get_active_view()
        gutter_win = view.get_gutter(Gtk.TextWindowType.LEFT).get_window()
        gutter_width = gutter_win.get_width() if gutter_win else 0
        # Get scrollbar width
        scrollbar = view.get_parent().get_vscrollbar()
        scrollbar_width = scrollbar.get_allocated_width()
        # Calculate text width (use right_margin_position for column width)
        char_width = self.get_char_width()
        text_width = char_width * view.get_right_margin_position() + 4
        # Get sidepanel width
        sidepanel = self.window.get_side_panel()
        sidepanel_visible = sidepanel.get_visible()
        sidepanel_width = sidepanel.get_allocated_width(
        ) if sidepanel_visible else 0
        # Calculate margins
        margins = scr_width - text_width - gutter_width - scrollbar_width - sidepanel_width
        return int(margins / 2)

    def set_all_margins(self):
        """Set margins width to self.margins in all views."""
        for view in self.window.get_views():
            self.set_margins(view)

    def set_margins(self, view):
        """Set margins width to self.margins in a view."""
        margins = self.margins
        if margins < 2:
            margins = 2
        # Don't add left margin if margin indicator is shown,
        # because Gedit would do it automatically
        if not view.get_show_right_margin():
            view.set_left_margin(margins)
        view.set_right_margin(margins)
Example #28
0
class Box(GObject.GObject):
    __gtype_name__ = 'PDFCutterBox'
    __gsignals__ = {
        'changed': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ([]))
    }

    def __init__(self):
        GObject.GObject.__init__(self)
        self._spage = 0
        self._sx = 0
        self._sy = 0
        self._width = 0
        self._height = 0
        self._model = None

        self._dscale = 1.0
        self._dpage = 0
        self._dx = 0
        self._dy = 0

    def get_sx(self):
        return self._sx

    def set_sx(self, value):
        # Keep the source and dest value in sync to some extend
        self._dx += value - self._sx
        self._sx = value
        self._emit_change()

    def get_sy(self):
        return self._sy

    def set_sy(self, value):
        self._sy = value
        self._emit_change()

    def get_spage(self):
        return self._spage

    def set_spage(self, value):
        self._spage = value
        self._emit_change()

    def get_width(self):
        return self._width

    def set_width(self, value):
        self._width = value
        self._emit_change()

    def get_height(self):
        return self._height

    def set_height(self, value):
        self._height = value
        self._emit_change()

    def get_dx(self):
        return self._dx

    def set_dx(self, value):
        self._dx = value
        self._emit_change()

    def get_dy(self):
        return self._dy

    def set_dy(self, value):
        self._dy = value
        self._emit_change()

    def get_dpage(self):
        return self._dpage

    def set_dpage(self, value):
        self._dpage = value
        self._emit_change()

        if self._model:
            # Keep boxes sorted
            self._model.sort_boxes()
            # Emit the models signal ...
            self._model.emit("box-zpos-changed", self)

    def get_dscale(self):
        return self._dscale

    def set_dscale(self, value):
        self._dscale = value
        self._emit_change()

    def _emit_change(self):
        self.emit("changed")
        # so we do not need to connect to all the boxes in the model
        if self._model:
            self._model.emit("box-changed", self)

    sx = GObject.property(type=float, getter=get_sx, setter=set_sx)
    sy = GObject.property(type=float, getter=get_sy, setter=set_sy)
    spage = GObject.property(type=int, getter=get_spage, setter=set_spage)
    width = GObject.property(type=float, getter=get_width, setter=set_width)
    height = GObject.property(type=float, getter=get_height, setter=set_height)

    dx = GObject.property(type=float, getter=get_dx, setter=set_dx)
    dy = GObject.property(type=float, getter=get_dy, setter=set_dy)
    dpage = GObject.property(type=int, getter=get_dpage, setter=set_dpage)
    dscale = GObject.property(type=float, getter=get_dscale, setter=set_dscale)
Example #29
0
class buffer(GObject.GObject):
 name = GObject.property(type=str)

 def __init__(self, obj):
  super(buffer, self).__init__()
  self.buffer = obj
Example #30
0
class PythonConsolePlugin(GObject.Object, Peas.Activatable):
    __gtype_name__ = 'PythonConsolePlugin'

    object = GObject.property(type=GObject.Object)

    def __init__(self):
        GObject.Object.__init__(self)

        self.totem = None
        self.window = None

    def do_activate(self):
        self.totem = self.object

        data = dict()
        manager = self.totem.get_ui_manager()

        self.action_group = Gtk.ActionGroup(name='Python')

        action = Gtk.Action(name='Python',
                            label='Python',
                            tooltip=_('Python Console Menu'),
                            stock_id=None)
        self.action_group.add_action(action)

        action = Gtk.Action(name='PythonConsole',
                            label=_('_Python Console'),
                            tooltip=_("Show Totem's Python console"),
                            stock_id='gnome-mime-text-x-python')
        action.connect('activate', self._show_console)
        self.action_group.add_action(action)

        action = Gtk.Action (name = 'PythonDebugger',
                             label = _('Python Debugger'),
                             tooltip = _("Enable remote Python debugging "\
                                          "with rpdb2"),
                             stock_id = None)
        if HAVE_RPDB2:
            action.connect('activate', self._enable_debugging)
        else:
            action.set_visible(False)
        self.action_group.add_action(action)

        manager.insert_action_group(self.action_group, 0)
        self.ui_id = manager.add_ui_from_string(UI_STR)
        manager.ensure_update()

    def _show_console(self, _action):
        if not self.window:
            console = PythonConsole(namespace={
                '__builtins__': __builtins__,
                'Totem': Totem,
                'totem_object': self.totem
            },
                                    destroy_cb=self._destroy_console)

            console.set_size_request(600, 400)  # pylint: disable-msg=E1101
            console.eval ('print("%s" %% totem_object)' % _("You can access "\
                "the Totem.Object through “totem_object” :\\n%s"), False)

            self.window = Gtk.Window()
            self.window.set_title(_('Totem Python Console'))
            self.window.add(console)
            self.window.connect('destroy', self._destroy_console)
            self.window.show_all()
        else:
            self.window.show_all()
            self.window.grab_focus()

    @classmethod
    def _enable_debugging(cls, _action):
        msg = _("After you press OK, Totem will wait until you connect to it "\
                 "with winpdb or rpdb2. If you have not set a debugger "\
                 "password in DConf, it will use the default password "\
                 "(“totem”).")
        dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO,
                                   Gtk.ButtonsType.OK_CANCEL, msg)
        if dialog.run() == Gtk.ResponseType.OK:
            schema = 'org.gnome.totem.plugins.pythonconsole'
            settings = Gio.Settings.new(schema)
            password = settings.get_string('rpdb2-password') or "totem"

            def start_debugger(password):
                rpdb2.start_embedded_debugger(password)
                return False

            GObject.idle_add(start_debugger, password)
        dialog.destroy()

    def _destroy_console(self, *_args):
        self.window.destroy()
        self.window = None

    def do_deactivate(self):
        manager = self.totem.get_ui_manager()
        manager.remove_ui(self.ui_id)
        manager.remove_action_group(self.action_group)
        manager.ensure_update()

        if self.window is not None:
            self.window.destroy()
Example #31
0
class Activity(GObject.GObject):
    """Activity which appears in the "Home View" of the Sugar shell

    This class stores the Sugar Shell's metadata regarding a
    given activity/application in the system.  It interacts with
    the sugar3.activity.* modules extensively in order to
    accomplish its tasks.
    """

    __gtype_name__ = 'SugarHomeActivity'

    __gsignals__ = {
        'pause': (GObject.SignalFlags.RUN_FIRST, None, ([])),
        'resume': (GObject.SignalFlags.RUN_FIRST, None, ([])),
        'stop': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_BOOLEAN, ([])),
    }

    LAUNCHING = 0
    LAUNCH_FAILED = 1
    LAUNCHED = 2

    def __init__(self, activity_info, activity_id, color, window=None):
        """Initialise the HomeActivity

        activity_info -- sugar3.activity.registry.ActivityInfo instance,
            provides the information required to actually
            create the new instance.  This is, in effect,
            the "type" of activity being created.
        activity_id -- unique identifier for this instance
            of the activity type
        _windows -- WnckWindows registered for the activity. The lowest
                    one in the stack is the main window.
        """
        GObject.GObject.__init__(self)

        self._windows = []
        self._service = None
        self._shell_windows = []
        self._activity_id = activity_id
        self._activity_info = activity_info
        self._launch_time = time.time()
        self._launch_status = Activity.LAUNCHING

        if color is not None:
            self._color = color
        else:
            self._color = profile.get_color()

        if window is not None:
            self.add_window(window)

        self._retrieve_service()

        self._name_owner_changed_handler = None
        if not self._service:
            bus = dbus.SessionBus()
            self._name_owner_changed_handler = bus.add_signal_receiver(
                self._name_owner_changed_cb,
                signal_name='NameOwnerChanged',
                dbus_interface='org.freedesktop.DBus')

        self._launch_completed_hid = \
            get_model().connect('launch-completed',
                                self.__launch_completed_cb)
        self._launch_failed_hid = get_model().connect('launch-failed',
                                                      self.__launch_failed_cb)

    def get_launch_status(self):
        return self._launch_status

    launch_status = GObject.property(getter=get_launch_status)

    def add_window(self, window, is_main_window=False):
        """Add a window to the windows stack."""
        if not window:
            raise ValueError('window must be valid')
        self._windows.append(window)

        if is_main_window:
            window.connect('state-changed', self._state_changed_cb)

    def push_shell_window(self, window):
        """Attach a shell run window (eg. view source) to the activity."""
        self._shell_windows.append(window)

    def pop_shell_window(self, window):
        """
        Detach a shell run window (eg. view source) to the activity.
        Only call this on **user initiated** deletion (loop issue).
        """
        self._shell_windows.remove(window)

    def has_shell_window(self):
        return bool(self._shell_windows)

    def stop(self):
        # For web activities the Apisocket will connect to the 'stop'
        # signal, thus preventing the window close.  Then, on the
        # 'activity.close' method, it will call close_window()
        # directly.
        close_window = not self.emit('stop')
        if close_window:
            self.close_window()

    def close_window(self):
        if self.get_window() is not None:
            self.get_window().close(GLib.get_current_time())

        for w in self._shell_windows:
            w.destroy()

    def remove_window_by_xid(self, xid):
        """Remove a window from the windows stack."""
        for wnd in self._windows:
            if wnd.get_xid() == xid:
                self._windows.remove(wnd)
                return True
        return False

    def get_service(self):
        """Get the activity service

        Note that non-native Sugar applications will not have
        such a service, so the return value will be None in
        those cases.
        """

        return self._service

    def get_title(self):
        """Retrieve the application's root window's suggested title"""
        if self._windows:
            return self._windows[0].get_name()
        else:
            return None

    def get_icon_path(self):
        """Retrieve the activity's icon (file) name"""
        if self.is_journal():
            icon_theme = Gtk.IconTheme.get_default()
            info = icon_theme.lookup_icon('activity-journal',
                                          Gtk.IconSize.SMALL_TOOLBAR, 0)
            if not info:
                return None
            fname = info.get_filename()
            del info
            return fname
        elif self._activity_info:
            return self._activity_info.get_icon()
        else:
            return None

    def get_icon_color(self):
        """Retrieve the appropriate icon colour for this activity

        Uses activity_id to index into the PresenceService's
        set of activity colours, if the PresenceService does not
        have an entry (implying that this is not a Sugar-shared application)
        uses the local user's profile colour for the icon.
        """
        return self._color

    def get_activity_id(self):
        """Retrieve the "activity_id" passed in to our constructor

        This is a "globally likely unique" identifier generated by
        sugar3.util.unique_id
        """
        return self._activity_id

    def get_bundle_id(self):
        """ Returns the activity's bundle id"""
        if self._activity_info is None:
            return None
        else:
            return self._activity_info.get_bundle_id()

    def get_xid(self):
        """Retrieve the X-windows ID of our root window"""
        if self._windows:
            return self._windows[0].get_xid()
        else:
            return None

    def has_xid(self, xid):
        """Check if an X-window with the given xid is in the windows stack"""
        if self._windows:
            for wnd in self._windows:
                if wnd.get_xid() == xid:
                    return True
        return False

    def get_window(self):
        """Retrieve the X-windows root window of this application

        This was stored by the add_window method, which was
        called by HomeModel._add_activity, which was called
        via a callback that looks for all 'window-opened'
        events.

        We keep a stack of the windows. The lowest window in the
        stack that is still valid we consider the main one.

        HomeModel currently uses a dbus service query on the
        activity to determine to which HomeActivity the newly
        launched window belongs.
        """
        if self._windows:
            return self._windows[0]
        return None

    def get_type(self):
        """Retrieve the activity bundle id for future reference"""
        if not self._windows:
            return None
        else:
            return SugarExt.wm_get_bundle_id(self._windows[0].get_xid())

    def is_journal(self):
        """Returns boolean if the activity is of type JournalActivity"""
        return self.get_type() == 'org.laptop.JournalActivity'

    def get_launch_time(self):
        """Return the time at which the activity was first launched

        Format is floating-point time.time() value
        (seconds since the epoch)
        """
        return self._launch_time

    def get_pid(self):
        """Returns the activity's PID"""
        if not self._windows:
            return None
        return self._windows[0].get_pid()

    def get_bundle_path(self):
        """Returns the activity's bundle directory"""
        if self._activity_info is None:
            return None
        else:
            return self._activity_info.get_path()

    def get_activity_name(self):
        """Returns the activity's bundle name"""
        if self._activity_info is None:
            return None
        else:
            return self._activity_info.get_name()

    def equals(self, activity):
        if self._activity_id and activity.get_activity_id():
            return self._activity_id == activity.get_activity_id()
        if self._windows[0].get_xid() and activity.get_xid():
            return self._windows[0].get_xid() == activity.get_xid()
        return False

    def _get_service_name(self):
        if self._activity_id:
            return _SERVICE_NAME + self._activity_id
        else:
            return None

    def _retrieve_service(self):
        if not self._activity_id:
            return

        try:
            bus = dbus.SessionBus()
            proxy = bus.get_object(self._get_service_name(),
                                   _SERVICE_PATH + '/' + self._activity_id)
            self._service = dbus.Interface(proxy, _SERVICE_INTERFACE)
        except dbus.DBusException:
            self._service = None

    def _name_owner_changed_cb(self, name, old, new):
        if name == self._get_service_name():
            if old and not new:
                logging.debug(
                    'Activity._name_owner_changed_cb: '
                    'activity %s went away', name)
                self._name_owner_changed_handler.remove()
                self._name_owner_changed_handler = None
                self._service = None
            elif not old and new:
                logging.debug(
                    'Activity._name_owner_changed_cb: '
                    'activity %s started up', name)
                self._retrieve_service()
                self.set_active(True)

    def set_active(self, state):
        """Propagate the current state to the activity object"""
        if self._service is not None:
            self._service.SetActive(state,
                                    reply_handler=self._set_active_success,
                                    error_handler=self._set_active_error)

    def _set_active_success(self):
        pass

    def _set_active_error(self, err):
        logging.error('set_active() failed: %s', err)

    def _set_launch_status(self, value):
        get_model().disconnect(self._launch_completed_hid)
        get_model().disconnect(self._launch_failed_hid)
        self._launch_completed_hid = None
        self._launch_failed_hid = None
        self._launch_status = value
        self.notify('launch_status')

    def __launch_completed_cb(self, model, home_activity):
        if home_activity is self:
            self._set_launch_status(Activity.LAUNCHED)

    def __launch_failed_cb(self, model, home_activity):
        if home_activity is self:
            self._set_launch_status(Activity.LAUNCH_FAILED)

    def _state_changed_cb(self, main_window, changed_mask, new_state):
        if changed_mask & Wnck.WindowState.MINIMIZED:
            if new_state & Wnck.WindowState.MINIMIZED:
                self.emit('pause')
            else:
                self.emit('resume')
Example #32
0
 def testDefaults(self):
     p1 = GObject.property(type=bool, default=True)
     p2 = GObject.property(type=bool, default=False)
Example #33
0
class EqualizerPlugin(GObject.Object, Peas.Activatable):
    __gtype_name__ = 'EqualizerPlugin'
    object = GObject.property(type=GObject.Object)

    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        self.shell = self.object
        self.sp = self.shell.props.shell_player

        self.conf = Conf.Config()
        Gst.init(None)
        self.eq = Gst.ElementFactory.make('equalizer-10bands', None)
        self.conf.apply_settings(self.eq)

        self.glade_f = self.find_file("equalizer-prefs.ui")

        self.dialog = ConfDialog(self.glade_f, self.conf, self.eq, self)
        self.dialog.add_ui(self.shell)

        self.psc_id = self.sp.connect('playing-song-changed',
                                      self.playing_song_changed)

        try:

            if (self.sp.get_playing()):
                self.sp.stop()
                self.sp.props.player.add_filter(self.eq)
                self.sp.play()
            else:
                self.sp.props.player.add_filter(self.eq)

        except:
            pass

    def do_deactivate(self):

        self.sp.disconnect(self.psc_id)

        self.dialog.cleanup()

        try:

            if (self.sp.get_playing()):
                self.sp.stop()
                self.sp.props.player.remove_filter(self.eq)
                self.sp.play()
            else:
                self.sp.props.player.remove_filter(self.eq)

        except:
            pass

        del self.sp
        del self.glade_f
        del self.shell
        del self.conf
        del self.dialog
        del self.eq

    def playing_song_changed(self, sp, entry):
        if entry == None:
            return

        genre = entry.get_string(RB.RhythmDBPropType.GENRE)
        if self.conf.preset_exists(genre):
            self.conf.change_preset(genre, self.eq)

    def create_configure_dialog(self, dialog=None):
        dialog = self.dialog.get_dialog()
        dialog.present()
        return dialog

    def find_file(self, filename):
        info = self.plugin_info
        data_dir = info.get_data_dir()
        path = os.path.join(data_dir, filename)

        if os.path.exists(path):
            return path

        return RB.file(filename)
Example #34
0
class OpenURIContextMenuPlugin(GObject.Object, Gedit.WindowActivatable):
    __gtype_name__ = "OpenURIContextMenuPlugin"
    window = GObject.property(type=Gedit.Window)

    def __init__(self):
        GObject.Object.__init__(self)
        self.uri = ""
        self.window = None
        self.encoding = GtkSource.Encoding.get_from_charset("UTF-8")

    def do_activate(self):
        handler_ids = []
        for signal in ('tab-added', 'tab-removed'):
            method = getattr(self, 'on_window_' + signal.replace('-', '_'))
            handler_ids.append(self.window.connect(signal, method))
        self.window.OpenURIContextMenuPluginID = handler_ids
        for view in self.window.get_views():
            self.connect_view(view)

    def do_deactivate(self):
        widgets = [self.window] + self.window.get_views()
        for widget in widgets:
            handler_ids = widget.OpenURIContextMenuPluginID
            if not handler_ids is None:
                for handler_id in handler_ids:
                    widget.disconnect(handler_id)
            widget.OpenURIContextMenuPluginID = None
        self.window = None

    def connect_view(self, view):
        handler_id = view.connect('populate-popup',
                                  self.on_view_populate_popup)
        view.OpenURIContextMenuPluginID = [handler_id]

    def update_ui(self, window):
        pass

    def browse_url(self, menu_item, url):
        command = ['xdg-open', url]
        # Avoid to run the browser as user root
        if os.getuid() == 0 and os.environ.has_key('SUDO_USER'):
            command = ['sudo', '-u', os.environ['SUDO_USER']] + command
        subprocess.Popen(command)

    def on_window_tab_added(self, window, tab):
        self.connect_view(tab.get_view())

    def on_window_tab_removed(self, window, tab):
        pass

    def on_view_populate_popup(self, view, menu):
        doc = view.get_buffer()
        win = view.get_window(Gtk.TextWindowType.TEXT)
        ptr_window, x, y, mod = win.get_pointer()
        x, y = view.window_to_buffer_coords(Gtk.TextWindowType.TEXT, x, y)

        # First try at pointer location
        # Starting with GTK 3.20, get_iter_at_location returns a tuple of type
        # (gboolean, GtkTextIter).  Earlier versions return only the GtkTextIter.
        insert = view.get_iter_at_location(x, y)
        if isinstance(insert, tuple):
            insert = insert[1] if insert[0] else None

        # Second try at cursor
        if insert == None:
            insert = doc.get_iter_at_mark(doc.get_insert())
            if isinstance(insert, tuple):
                insert = insert[1] if insert[0] else None

        while insert.forward_char():
            if not RE_DELIM.match(insert.get_char()):
                break

        start = insert.copy()
        while start.backward_char():
            if not RE_DELIM.match(start.get_char()):
                start.forward_char()
                break

        word = doc.get_text(start, insert, False)
        if len(word) == 0:
            return True

        word = self.validate_uri(word)
        if not word:
            return True

        displayed_word = word
        if len(displayed_word) > 50:
            displayed_word = displayed_word[:50] + u"\u2026"

        browse_to = False
        if word.startswith("http://") or word.startswith("https://"):
            browse_to = True

        if browse_to:
            browse_uri_item = Gtk.ImageMenuItem(
                _("Browse to '%s'") % (displayed_word))
            browse_uri_item.set_image(
                Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU))
            browse_uri_item.connect('activate', self.browse_url, word)
            browse_uri_item.show()

        displayed_word = displayed_word.replace('file://', '')
        open_uri_item = Gtk.ImageMenuItem(_("Open '%s'") % (displayed_word))
        open_uri_item.set_image(
            Gtk.Image.new_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.MENU))
        open_uri_item.connect('activate', self.on_open_uri_activate, word)
        open_uri_item.show()

        separator = Gtk.SeparatorMenuItem()
        separator.show()
        menu.prepend(separator)
        menu.prepend(open_uri_item)

        if browse_to:
            menu.prepend(browse_uri_item)
        return True

    def on_open_uri_activate(self, menu_item, uri):
        self.open_uri(uri)
        return True

    def validate_uri(self, uri):
        m = RE_URI_RFC2396.search(uri)
        if not m:
            return False

        target = m.group()

        if m.group(4) == None or m.group(4) == "/":
            return False

        if m.group(2) != None:
            if m.group(3) in ACCEPTED_SCHEMES:
                return target
            else:
                return False
        else:
            if m.group(4).startswith("www."):
                return 'http://' + target

        target = os.path.expanduser(target)
        if os.path.isfile(target):
            if os.path.isabs(target):
                return 'file://' + target

        doc_dir = self.window.get_active_document().get_uri_for_display()
        if doc_dir != None:
            if doc_dir.startswith('file://'):
                f = os.path.join(os.path.dirname(doc_dir), target)
                if os.path.isfile(f.replace('file://', '', 1)):
                    return f
            else:
                return os.path.join(os.path.dirname(doc_dir), target)

        paths = string.split(os.environ["PATH"], os.pathsep)
        for dirname in paths:
            f = os.path.join(os.path.dirname(dirname), 'include', target)
            if os.path.isfile(f):
                return 'file://' + f

        return False

    def get_document_by_uri(self, uri):
        docs = self.window.get_documents()
        for d in docs[:]:
            if d.get_location() == uri:
                return d
        return None

    def open_uri(self, uri):
        doc = self.get_document_by_uri(uri)
        if doc != None:
            tab = Gedit.tab_get_from_document(doc)
            self.window.set_active_tab(tab)
        else:
            self.window.create_tab_from_location(Gio.file_new_for_uri(uri),
                                                 self.encoding, 0, 0, False,
                                                 True)
            status = self.window.get_statusbar()
            status_id = status.push(
                status.get_context_id("OpenURIContextMenuPlugin"),
                _("Loading file '%s'...") % (uri))
            GObject.timeout_add(
                4000, self.on_statusbar_timeout, status,
                status.get_context_id("OpenURIContextMenuPlugin"), status_id)

    def on_statusbar_timeout(self, status, context_id, status_id):
        status.remove(context_id, status_id)
        return False
Example #35
0
class LyricsDisplayPlugin(GObject.Object, Peas.Activatable):
    __gtype_name__ = 'LyricsDisplayPlugin'
    object = GObject.property(type=GObject.Object)

    def __init__(self):
        GObject.Object.__init__(self)
        self.window = None

    def do_activate(self):
        shell = self.object

        self.action = Gio.SimpleAction.new("view-lyrics", None)
        self.action.connect("activate", self.show_song_lyrics, shell)
        # set accelerator?
        window = shell.props.window
        window.add_action(self.action)

        app = shell.props.application
        item = Gio.MenuItem.new(label=_("Song Lyrics"),
                                detailed_action="win.view-lyrics")
        app.add_plugin_menu_item("view", "view-lyrics", item)

        sp = shell.props.shell_player
        self.pec_id = sp.connect('playing-song-changed',
                                 self.playing_entry_changed)
        self.playing_entry_changed(sp, sp.get_playing_entry())

        self.csi_id = shell.connect('create_song_info', self.create_song_info)

        db = shell.props.db
        self.lyric_req_id = db.connect_after(
            'entry-extra-metadata-request::rb:lyrics', self.lyrics_request)

    def do_deactivate(self):
        shell = self.object

        app = shell.props.application
        app.remove_plugin_menu_item("view", "view-lyrics")
        app.remove_action("view-lyrics")
        self.action = None

        sp = shell.props.shell_player
        sp.disconnect(self.pec_id)

        shell.disconnect(self.csi_id)

        shell.props.db.disconnect(self.lyric_req_id)

        if self.window is not None:
            self.window.destroy()
            self.window = None

    def show_song_lyrics(self, action, parameter, shell):

        if self.window is not None:
            self.window.destroy()
            self.window = None

        sp = shell.props.shell_player
        entry = sp.get_playing_entry()

        if entry is not None:
            self.window = LyricWindow(shell)
            self.window.connect("destroy", self.window_deleted)
            self.window.update_song_lyrics(entry)

    def playing_entry_changed(self, sp, entry):
        if entry is not None:
            self.action.set_enabled(True)
            if self.window is not None:
                self.window.update_song_lyrics(entry)
        else:
            self.action.set_enabled(False)

    def window_deleted(self, window):
        print("lyrics window destroyed")
        self.window = None

    def create_song_info(self, shell, song_info, is_multiple):
        if is_multiple is False:
            x = LyricPane(shell.props.db, song_info)

    def lyrics_request(self, db, entry):
        def lyrics_results(text):
            if text:
                db.emit_entry_extra_metadata_notify(entry, 'rb:lyrics', text)

        lyrics_grabber = LyricGrabber(db, entry)
        lyrics_grabber.search_lyrics(lyrics_results)
Example #36
0
 def test_float_min(self):
     GObject.property(type=float, minimum=-1)
     GObject.property(type=GObject.TYPE_FLOAT, minimum=-1)
     GObject.property(type=GObject.TYPE_DOUBLE, minimum=-1)