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)
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
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)
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")
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
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
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)
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)
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 []
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)
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()
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)
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
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 + ")")
class SyncObject(GObject.Object): __gsignals__ = { 'sync-done': (GObject.SignalFlags.RUN_LAST, None, []), } sync_status = GObject.property(type=object) auth = GObject.property(type=GObject.Object)
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({ '<': '<', '>': '>', '&': '&' }) 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()
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()
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))
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())
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)
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())
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)
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)
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)
class buffer(GObject.GObject): name = GObject.property(type=str) def __init__(self, obj): super(buffer, self).__init__() self.buffer = obj
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()
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')
def testDefaults(self): p1 = GObject.property(type=bool, default=True) p2 = GObject.property(type=bool, default=False)
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)
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
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)
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)