From 9efb3255bc865db68830da69bd4200d351cba1f9 Mon Sep 17 00:00:00 2001 From: bjschuma Date: Sun, 16 May 2010 23:21:20 -0400 Subject: [PATCH] Many changes. Reworked most of ocarina-core. I have also began a new xml system for creating the gui. --- src/config.py | 42 +++-- src/core/cli.py | 38 ---- src/core/coredefaults.py | 38 ---- src/core/ct/__init__.py | 2 +- src/core/ct/call.py | 77 +++++--- src/core/ct/dict.py | 17 -- src/core/ct/plugin.py | 49 ----- src/core/ct/slist.py | 22 --- src/core/ct/tags.py | 19 ++ src/core/ct/times.py | 37 ++++ src/core/event.py | 52 +++--- src/core/gstreamer.py | 267 --------------------------- src/core/gstreamer/__init__.py | 50 +++++ src/core/gstreamer/playback.py | 34 ++++ src/core/gstreamer/position.py | 57 ++++++ src/core/gstreamer/volume.py | 35 ++++ src/core/ocarina-core.py | 36 ++-- src/core/ocarina.py | 38 ++-- src/core/variables.py | 29 +++ src/extra/db.py | 2 +- src/extra/et/sql.py | 2 +- src/extra/extradefaults.py | 21 --- src/extra/gtkextras/__init__.py | 5 + src/extra/gtkextras/dialog.py | 34 ++++ src/extra/gtknodes/__init__.py | 46 +++++ src/extra/gtknodes/box.py | 53 ++++++ src/extra/gtknodes/importnode.py | 19 ++ src/extra/gtknodes/menu/__init__.py | 20 ++ src/extra/gtknodes/menu/__init__.pyc | Bin 0 -> 1119 bytes src/extra/gtknodes/menu/bar.py | 23 +++ src/extra/gtknodes/menu/bar.pyc | Bin 0 -> 1042 bytes src/extra/gtknodes/menu/item.py | 27 +++ src/extra/gtknodes/menu/item.pyc | Bin 0 -> 1202 bytes src/extra/gtknodes/menu/playback.py | 25 +++ src/extra/gtknodes/menu/playback.pyc | Bin 0 -> 1261 bytes src/extra/gtknodes/menu/theme.py | 32 ++++ src/extra/gtknodes/menu/theme.pyc | Bin 0 -> 1804 bytes src/extra/gtknodes/node.py | 70 +++++++ src/extra/gtknodes/window.py | 96 ++++++++++ src/extra/gui.py | 43 +++++ src/extra/guibuilder.py | 154 --------------- src/extra/index.py | 12 +- src/extra/oGtk/__init__.py | 4 +- src/extra/oGtk/button.py | 12 +- src/extra/oGtk/label.py | 36 +++- src/extra/oGtk/list.py | 35 ++-- src/extra/oGtk/menu.py | 68 +++---- src/extra/oGtk/progbar.py | 4 +- src/extra/oGtk/tabs.py | 35 +++- src/extra/oGtk/volume.py | 4 +- src/extra/ocarina-extra.py | 37 ++-- src/themes/classic.xml | 11 +- src/themes/menu.xml | 15 ++ src/themes/simple2.xml | 21 +++ src/themes/simple3.xml | 20 ++ 55 files changed, 1127 insertions(+), 798 deletions(-) delete mode 100644 src/core/cli.py delete mode 100644 src/core/coredefaults.py delete mode 100644 src/core/ct/dict.py delete mode 100644 src/core/ct/plugin.py delete mode 100644 src/core/ct/slist.py create mode 100644 src/core/ct/tags.py delete mode 100644 src/core/gstreamer.py create mode 100644 src/core/gstreamer/__init__.py create mode 100644 src/core/gstreamer/playback.py create mode 100644 src/core/gstreamer/position.py create mode 100644 src/core/gstreamer/volume.py create mode 100644 src/core/variables.py delete mode 100644 src/extra/extradefaults.py create mode 100644 src/extra/gtkextras/__init__.py create mode 100644 src/extra/gtkextras/dialog.py create mode 100644 src/extra/gtknodes/__init__.py create mode 100644 src/extra/gtknodes/box.py create mode 100644 src/extra/gtknodes/importnode.py create mode 100644 src/extra/gtknodes/menu/__init__.py create mode 100644 src/extra/gtknodes/menu/__init__.pyc create mode 100644 src/extra/gtknodes/menu/bar.py create mode 100644 src/extra/gtknodes/menu/bar.pyc create mode 100644 src/extra/gtknodes/menu/item.py create mode 100644 src/extra/gtknodes/menu/item.pyc create mode 100644 src/extra/gtknodes/menu/playback.py create mode 100644 src/extra/gtknodes/menu/playback.pyc create mode 100644 src/extra/gtknodes/menu/theme.py create mode 100644 src/extra/gtknodes/menu/theme.pyc create mode 100644 src/extra/gtknodes/node.py create mode 100644 src/extra/gtknodes/window.py create mode 100644 src/extra/gui.py delete mode 100644 src/extra/guibuilder.py create mode 100644 src/themes/menu.xml create mode 100644 src/themes/simple2.xml create mode 100644 src/themes/simple3.xml diff --git a/src/config.py b/src/config.py index 892ce9a0..4afdce52 100644 --- a/src/config.py +++ b/src/config.py @@ -16,10 +16,10 @@ from ct.path import * from ct.call import * # Helpful variables used by the application -# vars["$user"] is the users home directory -# vars["$ocarina"] is the directory ocarina uses to store all its files -# vars["$lockfile"] is the lockfile used by the application -# vars["$writeenable"] controls enables printing from ct.call.write() +# vars.USER is the users home directory +# vars.OCARINA is the directory ocarina uses to store all its files +# vars.LOCKFILE is the lockfile used by the application +# vars.WRITEENABLE controls enables printing from ct.call.write() ################################################################################ ################################################################################ @@ -35,29 +35,35 @@ from ct.call import * # Set this variable to force verbosity to always be at the same value. # Calling ocarina with the -v option will have no effect. A higher value will # print more output -#vars["$verbose"] = 0 +#vars.VERBOSE = 0 # Setting this variable to change the prompt that is presented to the user # by the built in command line. This variable is only used in ocarina-core -#vars["$prompt"] = ">>>" +#vars.PROMPT = ">>>" # This variable controls if ocarina begins playback as soon as a song is loaded # The default value is True -vars["$playonload"] = False +vars.PLAYONLOAD = False # This variable controls the gstreamer volume at startup. Values must be # between 0.0 and 1.0, values less than 0.0 will be changed to 0.0 and values # greater than 1.0 will be changed to 1.0 -#vars["$volume"] = 1.0 +#vars.VOLUME = 1.0 # This variable controls how much the volume changes when calling # volup() or voldown() -#vars["$volumeincr"] = 0.05 +#vars.VOLUMEINCR = 0.05 # This variable controls the port that ocarina listens on for requests. Other # ocarina instances will connect to this port to forward a song to load. # Setting this variable to 0 will cause ocarina to use a random port. -#vars["$port"] = 12345 +#vars.PORT = 12345 + +# These next variables are the initial text shown in the artist, album, title +# labels. They will be overwritten when a song is loaded. +#vars.ARTIST = "" +#vars.ALBUM = "" +#vars.TITLE = "" @@ -80,7 +86,7 @@ readline.parse_and_bind("tab:complete") # Configure readline history. # This file is written after every command entered, but you can comment out # these lines to ignore the file -history = join(ocarina.vars["$ocarina"],"history") +history = join(vars.OCARINA,"history") if exists( history ): readline.read_history_file(history) @@ -104,14 +110,10 @@ readline.set_history_length(50) # Ocarina uses an xml-based system to build up the GUI. This allows you to # create your own custom gui. -#vars["$theme"] = "themes/simple.xml" -vars["$theme"] = "themes/classic.xml" - -# These next variables are the initial text shown in the artist, album, title -# labels. They will be overwritten when a song begins playback. -#vars["$artist"] = "" -#vars["$album"] = "" -#vars["$title"] = "" +#vars.THEME = "themes/simple.xml" +#vars.THEME = "themes/classic.xml" +#vars.THEME = "themes/ocarina3.xml" +vars.THEME = "themes/simple2.xml" # Initial text for library / playlist / queue filtering -#vars["$filterText"] = "" \ No newline at end of file +#vars.FILTERTEXT = "" \ No newline at end of file diff --git a/src/core/cli.py b/src/core/cli.py deleted file mode 100644 index 8cf4e75a..00000000 --- a/src/core/cli.py +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/python - -# To change this template, choose Tools | Templates -# and open the template in the editor. - -__author__="bjschuma" -__date__ ="$Mar 13, 2010 9:08:56 PM$" - -import ocarina - -from ct.path import * -from ct.call import * -import readline - -global history -history = join(ocarina.vars["$ocarina"],"history") - - -def loop(): - while True: - try: - state = ocarina.vars["$writeenable"] - ocarina.vars["$writeenable"] = True - exec raw_input(ocarina.vars["$prompt"] + " ") - ocarina.vars["$writeenable"] = state - - global history - readline.write_history_file(history) - - # Catch this so that we can use ctrl-d to exit - except EOFError: - print "" - exit() - except Exception,e: - print e - - -#ocarina.events.invite("ocarina-start",loop) \ No newline at end of file diff --git a/src/core/coredefaults.py b/src/core/coredefaults.py deleted file mode 100644 index fee0eedd..00000000 --- a/src/core/coredefaults.py +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/python - -__author__="bjschuma" -__date__ ="$Mar 15, 2010 9:53:46 PM$" - -import ocarina -import os -from ct import path -from ct import opts -from ocarina import vars - - -# Set the default values of ocarina variables -vars["$user"] = path.expand("~") -vars["$ocarina"] = path.join(vars["$user"],".ocarina3") -vars["$lockfile"] = path.join(vars["$ocarina"],"lock") -vars["$writeenable"] = True - -vars["$verbose"] = 0 -vars["$prompt"] = ">>>" -vars["$playonload"] = True -vars["$playing"] = False -vars["$volume"] = 1.0 -vars["$volumeincr"] = 0.05 -vars["$port"] = 0 - - -opts.parse() -path.mkdir(vars["$ocarina"]) - - -# Set verbose value -if opts.opts.has("v") == True: - vars["$verbose"] += opts.opts["v"] -if opts.opts.has("verbose") == True: - vars["$verbose"] += opts.opts["verbose"] - -#import gstreamer diff --git a/src/core/ct/__init__.py b/src/core/ct/__init__.py index 24d9c675..0a5a2396 100644 --- a/src/core/ct/__init__.py +++ b/src/core/ct/__init__.py @@ -2,4 +2,4 @@ __author__="bjschuma" __date__ ="$Mar 13, 2010 4:20:16 PM$" -__all__ = ["call", "dict", "opts", "path", "plugin", "slist", "tcp", "times"] \ No newline at end of file +__all__ = ["call", "opts", "path", "tags", "tcp", "times"] \ No newline at end of file diff --git a/src/core/ct/call.py b/src/core/ct/call.py index 3bfebb53..72c594a6 100644 --- a/src/core/ct/call.py +++ b/src/core/ct/call.py @@ -7,27 +7,28 @@ __author__="bjschuma" __date__ ="$Mar 30, 2010 11:24:43 PM$" -import ocarina -import sys +from ocarina import events +from ocarina import vars from ct import times from ct import path def exit(): '''Safely exit ocarina''' - path.rm(ocarina.vars["$lockfile"]) + path.rm(vars.LOCKFILE) import gstreamer - ocarina.events.start("ocarina-close") + import sys + events.start(events.OCARINA_QUIT) sys.exit(0) def about(): - print "Ocarina Version 3.0" - print "Written by Bryan Schumaker (bjschuma@umich.edu)" + write("Ocarina Version 3.0") + write("Written by Bryan Schumaker (bjschuma@umich.edu)") -def write(s,verbose): - if (ocarina.vars["$writeenable"]==True) and (ocarina.vars["$verbose"]>=verbose): +def write(s,verbose=0): + if vars.VERBOSE >= verbose: print str(s) @@ -35,22 +36,24 @@ def load(path): '''Load the song located at path''' import gstreamer gstreamer.load(path) + from ct import tags + tags.get(path) def seek(prcnt): ''' Seek to prcnt% of the song. If 0 < prcnt < 1, then we will seek to (prcnt * 100)% of song's duration. If prcnt < 100, then we will seek to prcnt% of the song's duration.''' - import gstreamer - good = gstreamer.seek(prcnt) + from gstreamer import position + good = position.seek(prcnt) if good == -1: write("There was an error seeking to: "+str(prcnt)) def progress(): '''Return the fraction of the song that we have already played''' - import gstreamer - progress = gstreamer.getProgress() + from gstreamer import position + progress = position.getProgress() write(progress) return progress @@ -59,8 +62,8 @@ def duration(): '''Return the total duration of the song. If message printing is enabled, then we will also print out string representing the duration in hh:mm:ss form''' - import gstreamer - duration = gstreamer.duration() + from gstreamer import position + duration = position.duration() write(times.ms2str(duration)) return duration @@ -69,28 +72,50 @@ def time(): '''Returns how far into the song gstreamer currently is. If message printing is enabled, then we will also print out a string representing the duration in hh:mm:ss form''' - import gstreamer - time = gstreamer.currentpos() - message.write(times.ms2str(time)) + from gstreamer import position + time = position.currentpos() + write(times.ms2str(time)) return time def gsstate(): '''Returns the current gstreamer state''' - import gstreamer - state = gstreamer.getstate() - message.write(state) + from gstreamer import playback + state = playback.getstate() + write(state) return state +def playing(disp=True): + '''Returns true if ocarina is currently playing''' + from gstreamer import playback + import gst + state = playback.getstate() == gst.STATE_PLAYING + if disp==True: + if state == True: + write("Ocarina is playing") + else: + write("Ocarina is not playing") + return state + + +def tags(disp=True): + '''Returns (title, artist, album) for the current track''' + if disp == True: + print vars.TITLE + print "by",vars.ARTIST + print "from",vars.ALBUM + return (vars.TITLE, vars.ARTIST, vars.ALBUM) + + def play(): '''Begin playback of the loaded song''' - ocarina.events.start("ocarina-play") + events.start(events.OCARINA_PLAY) def pause(): '''Pause playback of the current song''' - ocarina.events.start("ocarina-pause") + events.start(events.OCARINA_PAUSE) def stop(): @@ -101,14 +126,14 @@ def stop(): def volup(): '''Increase the volume''' - import gstreamer - gstreamer.volup() + from gstreamer import volume + volume.volup() def voldown(): '''Decrease the volume''' - import gstreamer - gstreamer.voldown() + from gstreamer import volume + volume.voldown() def pyfile(file): diff --git a/src/core/ct/dict.py b/src/core/ct/dict.py deleted file mode 100644 index c8a9a53e..00000000 --- a/src/core/ct/dict.py +++ /dev/null @@ -1,17 +0,0 @@ -#! /usr/bin/python - -# To change this template, choose Tools | Templates -# and open the template in the editor. - -__author__="bjschuma" -__date__ ="$Feb 24, 2010 8:59:47 PM$" - - - -class Dict(dict): - def __init__(self): - dict.__init__(self) - - - def has(self,key): - return (key in self.keys()) diff --git a/src/core/ct/plugin.py b/src/core/ct/plugin.py deleted file mode 100644 index 5a59dece..00000000 --- a/src/core/ct/plugin.py +++ /dev/null @@ -1,49 +0,0 @@ -#! /usr/bin/python - -# To change this template, choose Tools | Templates -# and open the template in the editor. - -__author__="bjschuma" -__date__ ="$Mar 14, 2010 2:58:02 PM$" - -import inspect -import os -from ct.message import write - - -class Plugin: - def __init__(self): - self.enabled = True - self.help = dict() - self.usage = dict() - self.minarg = 0 - - - def open(self): - pass - - - def close(self): - pass - - - def run(self,args): - return None - - - def start(self,args=None): - if len(args) < self.minarg: - return - try: - return self.run(args) - - # Find out information about the error - except Exception,e: - trace = inspect.trace() - frame = trace[len(trace)-1] - - filename = frame[1] - lineno = str(frame[2]) - - filename = filename.rsplit(os.sep,1)[1] - write(filename+" ("+lineno+"): "+str(e)) diff --git a/src/core/ct/slist.py b/src/core/ct/slist.py deleted file mode 100644 index 101190e9..00000000 --- a/src/core/ct/slist.py +++ /dev/null @@ -1,22 +0,0 @@ - -__author__="bjschuma" -__date__ ="$Feb 22, 2010 10:52:13 PM$" - - -# This is a sorted list (as long as items are inserted with add() ) -class Slist(list): - def __init__(self): - list.__init__(self) - - - def add(self, item, priority): - self += [(priority,item)] - self.sort() - - - def remove(self, item): - for tuple in self: - if tuple[1] == item: - list.remove(self,tuple) - break - diff --git a/src/core/ct/tags.py b/src/core/ct/tags.py new file mode 100644 index 00000000..c04f8dc9 --- /dev/null +++ b/src/core/ct/tags.py @@ -0,0 +1,19 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 12, 2010 10:34:18 AM$" + + +import tagpy +from ocarina import vars + + +def get(path): + file = tagpy.FileRef(path) + tags = file.tag() + vars.ARTIST = tags.artist + vars.ALBUM = tags.album + vars.TITLE = tags.title diff --git a/src/core/ct/times.py b/src/core/ct/times.py index 6a7c186d..a4bb3682 100644 --- a/src/core/ct/times.py +++ b/src/core/ct/times.py @@ -39,3 +39,40 @@ def ms2str(ms): # Convert ms to s time = int(ms) / 1000000000 return ftime(time) + + +def hms2sec(hms): + if hms == None: + return 0 + split = hms.split(":")[::-1] + + sec = int( split[0] ) + if len(split) >= 2: + sec += int( split[1] ) * 60 + if len(split) == 3: + sec += int( split[2] ) * 60 * 60 + + return sec + + +def sec2text(s): + def fmt(time, field): + if time <= 0: + return "" + if time > 1: + field += "s" + return str(time) + " " + field + " " + + sec = s + day = sec / 86500 + sec -= day*86500 + + hour = sec / 3600 + sec -= hour*3600 + + min = sec / 60 + sec -= min*60 + + st = fmt(day,"day") + fmt(hour,"hour") + fmt(min,"minute") + fmt(sec,"second") + return st + diff --git a/src/core/event.py b/src/core/event.py index 208403ca..162ad901 100644 --- a/src/core/event.py +++ b/src/core/event.py @@ -7,39 +7,43 @@ __author__="bjschuma" __date__ ="$Feb 24, 2010 9:13:41 PM$" -from ct.dict import Dict -from ct.slist import Slist -from ct.call import * - # Maintain the list of guests -class GuestList(Slist): - def __init__(self,name): - Slist.__init__(self) - self.name = name +class GuestList(list): + def __init__(self): + list.__init__(self) self.active = False + def add(self, item, priority): + self += [(priority, item)] + self.sort() -class Event(Dict): +class Event(dict): def __init__(self): - Dict.__init__(self) + dict.__init__(self) + self.nextEvent = 0 + self.create("OCARINA_START") + self.create("OCARINA_QUIT") + self.create("OCARINA_PLAY") + self.create("OCARINA_PAUSE") - def type(self,guest): - return type(guest).__name__ + def create(self,name): + var = "self." + name.upper() + exec var + " = " + str(self.nextEvent) + self.nextEvent += 1 + exec "self["+var+"] = GuestList()" + + + def has(self,key): + return (key in self.keys()) # Add a "guest" (function or script) to this event def invite(self, name, guest, priority=None): - type = self.type(guest) if priority==None: - if type=='str' or type=='unicode': - priority = 200 - else: priority = 100 - if self.has(name) == False: - self[name] = GuestList(name) self[name].add(guest, priority) @@ -60,17 +64,13 @@ class Event(Dict): self[name].active = True for priority,guest in self[name]: - if self[name].active == True: - if self.type(guest)=='unicode' or self.type(guest)=='str': - pyfile(guest) - elif args==None: + if self[name].active == False: + break + elif self[name].active == True: + if args==None: guest() else: guest(args) self.stop(name) - - - - diff --git a/src/core/gstreamer.py b/src/core/gstreamer.py deleted file mode 100644 index 5dfc00e7..00000000 --- a/src/core/gstreamer.py +++ /dev/null @@ -1,267 +0,0 @@ -#! /usr/bin/python - -__author__ = "bjschuma" -__date__ = "$Feb 5, 2010 7:53:19 PM$" - -from ct import path -from ct.call import write -from ct.opts import args -import gst -import ocarina - -global player -global time - -player = gst.element_factory_make("playbin2", "player") -time = gst.Format(gst.FORMAT_TIME) -bus = player.get_bus() - - - -def getstate(): - global player - state = player.get_state()[1] - write("Gstreamer state: "+str(state), 3) - return player.get_state()[1] - - -def play(): - global player - player.set_state(gst.STATE_PLAYING) - if getstate() != gst.STATE_PLAYING: - ocarina.events.stop("ocarina-play") - else: - ocarina.vars["$playing"] = True - - -def pause(): - global player - player.set_state(gst.STATE_PAUSED) - if getstate() != gst.STATE_PAUSED: - ocarina.events.stop("ocarina-pause") - else: - ocarina.vars["$playing"] = False - - -def load(song): - song = path.expand(song) - if path.exists(song) == False: - write("Path does not exist: " + song) - return - - curstate = getstate() - pause() - write("Loading file: " + song, 1) - global player - player.set_state(gst.STATE_NULL) - player.set_property("uri", "file://" + song) - if ocarina.vars["$playonload"]==True or curstate==gst.STATE_PLAYING: - play() - else: - pause() - #player.set_state(gst.STATE_PAUSED) - - -def uninit(): - global player - player.set_state(gst.STATE_NULL) - - - -def onMessage(bus, message): - #print message.type - if message.type == gst.MESSAGE_TAG: - taglist = message.parse_tag() - for tag in taglist.keys(): - write("Found tag: ("+tag+", "+str(taglist[tag])+")",1) - if tag == "title": - ocarina.vars["$title"] = taglist[tag] - elif tag == "artist": - ocarina.vars["$artist"]= taglist[tag] - elif tag == "album": - ocarina.vars["$album"] = taglist[tag] - ocarina.events.start("tags-changed") - - -def duration(): - global player - global time - state = getstate() - if (state!=gst.STATE_PLAYING) and (state!=gst.STATE_PAUSED): - return 0 - total = player.query_duration(time)[0] - return total - - -def currentpos(): - global player - global time - state = getstate() - if (state!=gst.STATE_PLAYING) and (state!=gst.STATE_PAUSED): - return 0 - position = player.query_position(time)[0] - return position - - -def getProgress(): - global player - global time - state = getstate() - if (state!=gst.STATE_PLAYING) and (state!=gst.STATE_PAUSED): - return 0 - total = duration() - current = currentpos() - fraction = float(current) / float(total) - if fraction < 0.0: - return 0.0 - elif fraction > 1.0: - return 1.0 - return fraction - - -# Seek to the desired percent of the song. -# Fraction is True if prcnt is already a fraction -def seek(prcnt): - global player - global time - if prcnt < 0: - prcnt = 0 - elif prcnt > 100: - prcnt = 1 - elif prcnt > 1: - prcnt = float(prcnt) / 100.0 - newTime = duration() * prcnt - player.seek_simple(time,gst.SEEK_FLAG_FLUSH,newTime) - return 0 - - -def setvol(volume): - global player - if volume > 1.0: - volume = 1.0 - elif volume < 0.0: - volume = 0.0 - player.set_property("volume",volume) - ocarina.vars["$volume"] = volume - - -def volup(): - volume = ocarina.vars["$volume"] - volume += ocarina.vars["$volumeincr"] - setvol(volume) - - -def voldown(): - volume = ocarina.vars["$volume"] - volume -= ocarina.vars["$volumeincr"] - setvol(volume) - - -bus.add_signal_watch() -bus.connect("message", onMessage) -ocarina.events.invite("ocarina-close", uninit) -ocarina.events.invite("ocarina-play", play,50) -ocarina.events.invite("ocarina-pause", pause,50) -setvol(ocarina.vars["$volume"]) - -# -#def setvol(value): -# global pipeline -# curvol = float( settings.get("volume") ) -# -# vol = pipeline.get_by_name("vol") -# if value == "up": -# value = curvol + 0.05 -# elif value == "down": -# value = curvol - 0.05 -# else: -# # Prevent converting strings -# try: -# value = float(value) -# except: -# return -# -# if value > 1.0: -# value = 1.0 -# if value < 0.0: -# value = 0.0 -# settings.set("volume",value) -# vol.set_property("volume",value ) -# -# -#def getProgress(tuple=False): -# global pipeline -# global time -# -# # Don't bother to go on if the pipeline isn't playing -# if not pipeline.get_state()[1] == gst.STATE_PLAYING: -# return -1 -# -# position = pipeline.query_position(time)[0] -# total = pipeline.query_duration(time)[0] -# if tuple==False: -# return float(position) / float(total) -# else: -# return (position,total) -# -# -## Draw the progress bar on the command line -#def drawProgress(): -# p = getProgress() -# if p == -1: -# return -# win = settings.get("maxyx") -# max = int(win[1] * p) -# if max > win[1]: -# max = win[1] -# if max == 0: -# cline.message.disp(" "*(win[1]-1) ,win[0]-1) -# else: -# cline.message.disp("="*max, win[0]-1) -# -# -## A callback when there are messages on the bus -#def onMessage(bus,message): -# if message.type == gst.MESSAGE_EOS: -# manager.run("next") -# -# -## Manually check the bus for messages -#def checkBus(): -# global bus -# if bus.peek() == None: -# return -# onMessage(bus,bus.pop()) -# -# -# -#def init(): -# # Register signals -# signal.register("play",play) -# signal.register("pause",pause) -# signal.register("cliloop",drawProgress) -# signal.register("cliloop",checkBus,90) -# -# # Check for settings values -# if settings.has("args") == True: -# input = settings.get("args") -# if not input == []: -# join = ' ' -# path = join.join(input) -# load(path) -# else: -# if settings.has("curtrk")==True: -# track = settings.get("curtrk") -# if track > 0: -# manager.run("next",[track,False]) -# if settings.has("volume") == False: -# settings.set("volume",1.0) -# setvol(settings.get("volume")) -# -# -#def close(): -# global pipeline -# global bin -# pause() -# pipeline.set_state(gst.STATE_NULL) -# bin.set_state(gst.STATE_NULL) diff --git a/src/core/gstreamer/__init__.py b/src/core/gstreamer/__init__.py new file mode 100644 index 00000000..25648de9 --- /dev/null +++ b/src/core/gstreamer/__init__.py @@ -0,0 +1,50 @@ + +__author__="bjschuma" +__date__ ="$May 11, 2010 10:31:27 PM$" + + +__all__ = ["playback", "position", "volume"] + + +import gst + +from ocarina import vars +from ocarina import events +from ct import path +from ct import call + +global player +global time + +player = gst.element_factory_make("playbin2", "player") +time = gst.Format(gst.FORMAT_TIME) + + + +def load(song): + song = path.expand(song) + if path.exists(song) == False: + call.write("Path does not exist: " + song) + return + + playing = call.playing(False) + if playing == True: + call.pause() + + call.write("Loading file: " + song, 1) + + global player + player.set_state(gst.STATE_NULL) + player.set_property("uri", "file://" + song) + + if (vars.PLAYONLOAD == True) or (playing==True): + call.play() + + +def init(): + from gstreamer import playback + from gstreamer import volume + from gstreamer import position + + +init() \ No newline at end of file diff --git a/src/core/gstreamer/playback.py b/src/core/gstreamer/playback.py new file mode 100644 index 00000000..331278c5 --- /dev/null +++ b/src/core/gstreamer/playback.py @@ -0,0 +1,34 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 11, 2010 10:55:54 PM$" + +import gst +from ocarina import events +from gstreamer import player +from ct import call + + +def getstate(): + state = player.get_state()[1] + call.write("Gstreamer state: "+str(state), 3) + return player.get_state()[1] + + +def play(): + player.set_state(gst.STATE_PLAYING) + if getstate() != gst.STATE_PLAYING: + events.stop(events.OCARINA_PLAY) + + +def pause(): + player.set_state(gst.STATE_PAUSED) + if getstate() != gst.STATE_PAUSED: + events.stop(events.OCARINA_PAUSE) + + +events.invite(events.OCARINA_PLAY, play, 50) +events.invite(events.OCARINA_PAUSE, pause, 50) diff --git a/src/core/gstreamer/position.py b/src/core/gstreamer/position.py new file mode 100644 index 00000000..c0d7081c --- /dev/null +++ b/src/core/gstreamer/position.py @@ -0,0 +1,57 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 11, 2010 11:50:23 PM$" + + +import gst +from gstreamer import player +from gstreamer import time +from gstreamer.playback import getstate + + +def duration(): + state = getstate() + if (state!=gst.STATE_PLAYING) and (state!=gst.STATE_PAUSED): + return 0 + total = player.query_duration(time)[0] + return total + + +def currentpos(): + state = getstate() + if (state!=gst.STATE_PLAYING) and (state!=gst.STATE_PAUSED): + return 0 + position = player.query_position(time)[0] + return position + + +def getProgress(): + state = getstate() + if (state!=gst.STATE_PLAYING) and (state!=gst.STATE_PAUSED): + return 0 + total = duration() + current = currentpos() + fraction = float(current) / float(total) + if fraction < 0.0: + return 0.0 + elif fraction > 1.0: + return 1.0 + return fraction + + +# Seek to the desired percent of the song. +# Fraction is True if prcnt is already a fraction +def seek(prcnt): + if prcnt < 0: + prcnt = 0 + elif prcnt > 100: + prcnt = 1 + elif prcnt > 1: + prcnt = float(prcnt) / 100.0 + newTime = duration() * prcnt + player.seek_simple(time,gst.SEEK_FLAG_FLUSH,newTime) + return 0 diff --git a/src/core/gstreamer/volume.py b/src/core/gstreamer/volume.py new file mode 100644 index 00000000..21a8c10e --- /dev/null +++ b/src/core/gstreamer/volume.py @@ -0,0 +1,35 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 11, 2010 10:32:12 PM$" + + +from gstreamer import player +from ocarina import vars + + +def setvol(volume): + if volume > 1.0: + volume = 1.0 + elif volume < 0.0: + volume = 0.0 + player.set_property("volume",volume) + vars.VOLUME = volume + + +def volup(): + volume = vars.VOLUME + volume += vars.VOLUMEINCR + setvol(volume) + + +def voldown(): + volume = vars.VOLUME + volume -= vars.VOLUMEINCR + setvol(volume) + + +setvol(vars.VOLUME) diff --git a/src/core/ocarina-core.py b/src/core/ocarina-core.py index 05576b2a..1b7af207 100644 --- a/src/core/ocarina-core.py +++ b/src/core/ocarina-core.py @@ -3,23 +3,31 @@ __author__="bjschuma" __date__ ="$Mar 13, 2010 4:19:31 PM$" + import ocarina -import coredefaults -import cli +print "Welcome to Ocarina 3.0 (core)" + +ocarina.config() + + +ocarina.events.start(ocarina.events.OCARINA_START) + + from ct.call import * -from ct import tcp +from ct.path import * +import readline +# ocarina-core main loop +while True: + try: + exec raw_input(ocarina.vars.PROMPT + " ") + readline.write_history_file(ocarina.vars.HISTORYFILE) -def main(): - # Potentially the first thing printed - write("Welcome to Ocarina (core)", 1) - - ocarina.events.start("ocarina-start") - code = ocarina.config() - if code == 0: - cli.loop() - - -if __name__ == "__main__":main() + # Catch this so that we can use ctrl-d to exit + except EOFError: + print "" + exit() + except Exception,e: + print 1,e diff --git a/src/core/ocarina.py b/src/core/ocarina.py index 926bbc04..03c02033 100644 --- a/src/core/ocarina.py +++ b/src/core/ocarina.py @@ -3,36 +3,45 @@ __author__="bjschuma" __date__ ="$Mar 13, 2010 4:19:39 PM$" - -from ct.dict import Dict -from ct import path +import variables import event - global vars global events - -vars = Dict() +vars = variables.Vars() events = event.Event() +from ct import path +from ct import opts + +path.mkdir(vars.OCARINA) +opts.parse() +# Set verbose value +if opts.opts.has("v") == True: + vars.VERBOSE += opts.opts["v"] +if opts.opts.has("verbose") == True: + vars.VERBOSE += opts.opts["verbose"] + + +import gstreamer + + def postConfig(): from ct import tcp global vars - lock = vars["$lockfile"] + lock = vars.LOCKFILE from ct.opts import args # Load a song if we were passed one on load space = ' ' song = space.join(args) - #if exists( song ): - # load(song) # Check if another instance already has the lock if path.exists(lock) == True: # Print a warning message, and tell where the lock file is so the user # can remove it if this message is in error print "Warning: an instance of Ocarina may already be running!" - print "If this is not the case, remove the lock file located at:" + print "If this is not the case, remove the lock file located at:" print lock # Find the port of the other application fin = open(lock) @@ -43,7 +52,7 @@ def postConfig(): tcp.send(port,song) return -1 except: - print "Alert: I could not connect to port",port + print "Warning: I could not connect to port",port print "We will continue as if this is the only Ocarina" # Become a tcp server @@ -67,7 +76,10 @@ def config(): global vars from ct.call import pyfile config = "config.py" - pyfile(path.join(vars["$ocarina"],config)) + pyfile(path.join(vars.OCARINA,config)) pyfile(config) - return postConfig() + if postConfig() != 0: + import sys + sys.exit() + diff --git a/src/core/variables.py b/src/core/variables.py new file mode 100644 index 00000000..4bca1a87 --- /dev/null +++ b/src/core/variables.py @@ -0,0 +1,29 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 10, 2010 5:57:02 PM$" + + +from ct import path + + +class Vars: + def __init__(self): + self.USER = path.expand("~") + self.OCARINA = path.join(self.USER, ".ocarina3") + self.LOCKFILE = path.join(self.OCARINA, "lock") + self.HISTORYFILE = path.join(self.OCARINA, "history") + + self.PLAYONLOAD = True + self.PORT = 0 + self.PROMPT = ">>>" + self.VERBOSE = 0 + self.VOLUME = 1.0 + self.VOLUMEINCR = 0.05 + self.ARTIST = "" + self.ALBUM = "" + self.TITLE = "" + diff --git a/src/extra/db.py b/src/extra/db.py index eb732f8d..f26bf60d 100644 --- a/src/extra/db.py +++ b/src/extra/db.py @@ -162,7 +162,7 @@ def gettrack(trid): select = "track.id,track.name,track.length,artist.name,album.name,track.count" where = "track.artist=artist.id AND track.album=album.id AND track.id="+str(trid) sel = sql.Select(select,"track,artist,album",where) - return sel.execute().fetchone() + return list( sel.execute().fetchone() ) init() \ No newline at end of file diff --git a/src/extra/et/sql.py b/src/extra/et/sql.py index d01d00f7..9740579b 100644 --- a/src/extra/et/sql.py +++ b/src/extra/et/sql.py @@ -14,7 +14,7 @@ import ocarina def getdb(): - return join(ocarina.vars["$ocarina"],"ocarina.db") + return join(ocarina.vars.OCARINA,"ocarina.db") def dbexists(): diff --git a/src/extra/extradefaults.py b/src/extra/extradefaults.py deleted file mode 100644 index a9a60632..00000000 --- a/src/extra/extradefaults.py +++ /dev/null @@ -1,21 +0,0 @@ -#! /usr/bin/python - -# To change this template, choose Tools | Templates -# and open the template in the editor. - -__author__="bjschuma" -__date__ ="$Mar 15, 2010 9:56:53 PM$" - - -from ocarina import vars -import coredefaults -vars["$theme"] = "themes/simple.xml" -#vars["$theme"] = "themes/classic.xml" -vars["$artist"] = "" -vars["$album"] = "" -vars["$title"] = "" -vars["$filterText"] = "" - - -import guibuilder -from oGtk import * \ No newline at end of file diff --git a/src/extra/gtkextras/__init__.py b/src/extra/gtkextras/__init__.py new file mode 100644 index 00000000..bc80b556 --- /dev/null +++ b/src/extra/gtkextras/__init__.py @@ -0,0 +1,5 @@ +__author__="bjschuma" +__date__ ="$May 16, 2010 7:21:27 PM$" + + +__all__ = ["dialog"] \ No newline at end of file diff --git a/src/extra/gtkextras/dialog.py b/src/extra/gtkextras/dialog.py new file mode 100644 index 00000000..27004a34 --- /dev/null +++ b/src/extra/gtkextras/dialog.py @@ -0,0 +1,34 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 16, 2010 7:21:50 PM$" + +import gtk + + +class FileChooser(gtk.FileChooserDialog): + def __init__(self,title,file=None,seldir=False): + buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK) + action=gtk.FILE_CHOOSER_ACTION_OPEN + gtk.FileChooserDialog.__init__(self,title,None,action=action,buttons=buttons) + + if file != None: + self.select_filename(file) + + if seldir == True: + self.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) + + + def choose(self): + response = self.run() + self.hide() + file = "" + #print response + if response == gtk.RESPONSE_OK: + file = self.get_filename() + if response != gtk.RESPONSE_OK: + return None + return file diff --git a/src/extra/gtknodes/__init__.py b/src/extra/gtknodes/__init__.py new file mode 100644 index 00000000..d22c446a --- /dev/null +++ b/src/extra/gtknodes/__init__.py @@ -0,0 +1,46 @@ +__author__="bjschuma" +__date__ ="$May 13, 2010 10:37:20 AM$" + + +__all__ = ["box", "importnode", "menu", "node", "window"] + + +from ct.call import write + + +global parts +parts = dict() + + + +def make(child): + global parts + name = str(child.nodeName).lower() + if (name in parts.keys()) == False: + return None + node = parts[name](child) + if name == "import": + return node.part + return node + + +from gtknodes.box import * +parts["vbox"] = VBox +parts["hbox"] = HBox + + +from gtknodes.importnode import * +parts["import"] = Import + + +from gtknodes.menu import * +parts["menubar"] = bar.MenuBar +parts["menuitem"] = item.MenuItem +parts["menuchangetheme"] = theme.MenuChangeTheme +parts["menureloadtheme"] = theme.MenuReloadTheme +parts["menuselectsong"] = playback.MenuSelectSong + + +from gtknodes.window import * +parts["window"] = Window +parts["scrollwindow"] = ScrolledWindow diff --git a/src/extra/gtknodes/box.py b/src/extra/gtknodes/box.py new file mode 100644 index 00000000..1607dc75 --- /dev/null +++ b/src/extra/gtknodes/box.py @@ -0,0 +1,53 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 13, 2010 11:46:36 AM$" + + +from gtknodes.node import Node +import gtk + + +class BoxNode(Node): + def __init__(self, elm, box): + Node.__init__(self, elm) + self["pack"] = "start" + self["expand"] = "false" + self["fill"] = "false" + self["pad"] = 0 + self.part = box() + self.setattrs() + self.pack() + + + def packval(self,key,node): + if ( key in node.keys() ) == True: + return node[key] + return self[key] + + + def pack(self): + for child in self.children: + pack = self.packval("pack", child) + expand = self.packval("expand", child) == "true" + fill = self.packval("fill", child) == "true" + pad = int( self.packval("pad", child) ) + + if pack == "start": + self.part.pack_start(child.part, expand, fill, pad) + else: + self.part.pack_end(child.part, expand, fill, pad) + + + +class HBox(BoxNode): + def __init__(self,elm): + BoxNode.__init__(self, elm, gtk.HBox) + + +class VBox(BoxNode): + def __init__(self,elm): + BoxNode.__init__(self, elm, gtk.VBox) diff --git a/src/extra/gtknodes/importnode.py b/src/extra/gtknodes/importnode.py new file mode 100644 index 00000000..4c658686 --- /dev/null +++ b/src/extra/gtknodes/importnode.py @@ -0,0 +1,19 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 16, 2010 10:24:55 PM$" + + +from gtknodes.node import Node + + +class Import(Node): + def __init__(self,elm): + Node.__init__(self,elm) + self["show"] = False + self.setattrs() + from gui import buildfile + self.part = buildfile(self["src"]) \ No newline at end of file diff --git a/src/extra/gtknodes/menu/__init__.py b/src/extra/gtknodes/menu/__init__.py new file mode 100644 index 00000000..749252a5 --- /dev/null +++ b/src/extra/gtknodes/menu/__init__.py @@ -0,0 +1,20 @@ +__author__="bjschuma" +__date__ ="$May 16, 2010 7:23:22 PM$" + + +__all__ = ["bar", "item", "playback", "theme"] + + +import gtk +from gtknodes import Node + + +class MenuNode(Node): + def __init__(self, elm, title): + Node.__init__(self,elm) + self.part = gtk.MenuItem(title) + self.setattrs() + self.part.connect("activate", self.onclick) + + def onclick(self, menu): + pass \ No newline at end of file diff --git a/src/extra/gtknodes/menu/__init__.pyc b/src/extra/gtknodes/menu/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5dd09b92d11a2e0bf5677f8180d1f635751b4428 GIT binary patch literal 1119 zcmcIi&2AGh5FTfL@-t_%NUA2ECa|fkMRJOA(Rp15r|?4CW32h8^Je#9l)oY%P*6AIM@%+U43?X zv96SRgo~4@+KkVhKN(L>&rZiL&L_{#CzJ8T^uz~Ls#+i7-+EJV@2XUrE2Wp7@cMF~}?d=!kO+vb4%!wi? zZEd}jtijwbZ~43@9f;)$h2g9-UU}a--^Z+O>e^_}aZ~Hk>SaPCxW$?Bng^P)>g2P& zGzo44J4ZCrMN^q{e?#d-+ss>4xwO%$wY5rJt5fsUw<<+gHLB~<3UyBRh@Y-D88J9S za}-KDNCW9K5D5WcEv2kg zWl;7JW~8i|VqM~r31!sv4-7|lhA76_2WXC<#9|mHbelIY)Y>na7Eui_CuM=p1s7Bx pr7BCrH>6_P-QNknq>QHiC&YZ(%TAzIbbpQ{i$xq92M6LP_zQ`v^6>xw literal 0 HcmV?d00001 diff --git a/src/extra/gtknodes/menu/bar.py b/src/extra/gtknodes/menu/bar.py new file mode 100644 index 00000000..7e3afb43 --- /dev/null +++ b/src/extra/gtknodes/menu/bar.py @@ -0,0 +1,23 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 16, 2010 7:25:18 PM$" + +from gtknodes import Node +import gtk + + +class MenuBar(Node): + def __init__(self,elm): + Node.__init__(self,elm) + self.part = gtk.MenuBar() + self.setattrs() + self.makemenu() + + + def makemenu(self): + for child in self.children: + self.part.append(child.part) diff --git a/src/extra/gtknodes/menu/bar.pyc b/src/extra/gtknodes/menu/bar.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2e7d26e1ed1a1f53e5b0bce8898d05ff021b584 GIT binary patch literal 1042 zcmb_aO=}cE5UrZ8=wLJ#5An2zJq##LHlUFpAw+MoOPD=~1JZP6I@xh|X4dXrNFV`W z{e%7!@egQKnfP(vfqGNb-PQH#b$$D>oyK3jzpLQ(i|Kz%=1vG|fRYfQM5qI(LzoA! z2%!uCIgVf+K^Z|Eg9+iB4khpvU=8r0jCp&SZP7LU9>LisSIt(fcDob|$NFk8diiXy zzc<<&931YyI2^qioQ{XhPx> zN;O^MmGWHS!p{q?CLJub_0qpHUVCqy1g)N%mO2u8a?Y8>xn!F~n^7_dxFhoNS>Kxc zPQU!r_7_&SF7GRCn@;D>R=K(KR_9c?BgI_Kcjsr?K3`rHOz9z+W1@gN_y|+1nEHQ` zJf(%pp|lO~_Tw(xh&Uus6Rd$sDst z@cvfD%x)Rzm7n#N2(cte)!H*V4(=XLrmEmvnWD`TlxnYkOL4^1WHJ&=f*pJi{sNN8 B*kk|z literal 0 HcmV?d00001 diff --git a/src/extra/gtknodes/menu/item.py b/src/extra/gtknodes/menu/item.py new file mode 100644 index 00000000..d489f757 --- /dev/null +++ b/src/extra/gtknodes/menu/item.py @@ -0,0 +1,27 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 16, 2010 7:26:59 PM$" + + +from gtknodes import Node +import gtk + +class MenuItem(Node): + def __init__(self,elm): + Node.__init__(self,elm) + self["name"] = "menu" + self.part = gtk.MenuItem() + self.menu = gtk.Menu() + self.part.set_submenu(self.menu) + self.setattrs() + self.part.set_label(self["name"].title()) + self.makemenu() + + + def makemenu(self): + for child in self.children: + self.menu.append(child.part) diff --git a/src/extra/gtknodes/menu/item.pyc b/src/extra/gtknodes/menu/item.pyc new file mode 100644 index 0000000000000000000000000000000000000000..375caf00101ca8d1de1c476782198306c5f12ab6 GIT binary patch literal 1202 zcmcIi-)q!B5S~qPX?tz8;8Xi_5BCsIOk1>KDJtrV$Q>np5Dz45lWlK%N$$+c2oahcUV>2m_$#PTYM2 zU`mvbvZEwGxM0C2#CeC~t{^;-Rdh*ospvYuL}FmB10RqLc?<;eh}i5b%$S`vBD*9? zOxKDOyOudaGz3>Th*F!j%kD+as^kFYQ4#{dP=>5AnkbE~O|k}mCIm2Rd2So)^W1a# zsh?y#g(S~(>qTK^G7kGy2xolm=EqW#3&7Uix6ZHJ1}lARD$i^7)>lTzt@Xt44IxX4 zI#ZoUPgB(ia!G`H4J{o{Ym+Y4nI5*&(^l6moff*ajZR%#q~_eWItAVa3v?;hPA#_g zbas((>jyY)1A^3s>Z*tMKf-rQ6+HPrn|ch#d0)6DdjDmUu4GNvoDFz$yeMqoYnMI= zuUuf5M36FCF}79Nn&yU~7;ZhAnWoJ6VamK2gAqa`_nxO+M{$1fj$4!JQmpR4IlS1^U5jBnn7xj+7yU9Rnzqgbu1CJgNsf}Z>Fv<0GN`0~8ep5d{vSf;$q cjIYRB{0|7F-N6kRueg!p{;`U~4Yd~h0WDYg8UO$Q literal 0 HcmV?d00001 diff --git a/src/extra/gtknodes/menu/playback.py b/src/extra/gtknodes/menu/playback.py new file mode 100644 index 00000000..81664eef --- /dev/null +++ b/src/extra/gtknodes/menu/playback.py @@ -0,0 +1,25 @@ +#! /usr/bin/python + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +__author__="bjschuma" +__date__ ="$May 16, 2010 10:08:35 PM$" + + +from gtknodes.menu import MenuNode + + +class MenuSelectSong(MenuNode): + def __init__(self,elm): + MenuNode.__init__(self, elm, "Select a song") + self.file = None + + + def onclick(self, menu): + from gtkextras import dialog + song = dialog.FileChooser("Select a song", self.file).choose() + if song != None: + from ct import call + self.file = song + call.load(song) diff --git a/src/extra/gtknodes/menu/playback.pyc b/src/extra/gtknodes/menu/playback.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3501b71e6cba0e4f5770e15ab597bd11ec993d25 GIT binary patch literal 1261 zcmcIj!H&~F4D~c=+Ab7T>>)yE(}F-~g%Dz;y`WaB^h7VwY%*=Lon%&-DWXz) zDu2Y6@CEz=;s?NX=)!^H5@{UU@z~GL&etFPqi;XQ^0?0csjk=i1k+{0H*>YV<`v{Gb zvD}2CmruiI!_hDt4bO(J&Yr&rFUKdvPkbVAxHVSQdSY_bczkq!l(lsaP@zv#rB&vp zrdr%KL08}5;W`SNKvY1eW(Zoa;z2TvS{Rd&phJnz@z@ZB11JP+0k(Kmz@8dL10eM% zRdaP>`xx5P4`pZ(LPS}TTj*(8RE0}ZNA8KK6f5&WE3VC!ReJ6`{H}Bv;R{$;f)X!H zsp11`@nvlmwJdFHGFcaujBTC8>XWNwyl|@uDY9{ijKnuuZmwjuI=$H>RGFxJhGx&qJwb}bOw78E>j?78q@kETyU-FdKO3a&jcPmmS7hiedk?!eB+ zd-$DM+WJs*DRwVFRCsctjp|PRnr}d*3#rZGA6znxgff#_M?F*=3G9`dwh6oLq3Z$} zJzpAQRh>{uMDcD5=SLXmFXf|9aE|M zC_oIvW6?+16TC$H!=z~?ONIMGz|*ue`C6mNLror{?+?1W-5T0QT5jH&K=j&^h>2=& z$hBLV8ew%ARxTZ3r1--go-&&}K<>6#*o4NAQ11VPn*qIUXnI3CU{uc{${@a+}`~5^~d`J7QX=J6C7rYA_W+t6fhK! zHX!w2(tsf<`1fJrLGHuQ2SMv5Oq!4fFbr@VfDYgaA8o)FfH}a2;Rd9wyWRG3w?#$= zolSIRTPQBCO){Pq$~?x!eox(mM=zd*-NU29@a1v$`SD>lJn!vizwp=Z2zuqC_@rv% zR%D~6%W1z#wSBNyNS#+Iy%_64FLBb_H#p1_6dG}2us~rEJi&?wa?okuaYBLy`8vg6 z_E1=WOu!6ahED~|J;V)&g)a8Z7E0$gWXHTM>5Os4aaLwFj(M9g7@c2P56^X8bZ`O| z<^;89TopRHOHFiMSEE`LCaMxuXQhfvokaSxtyMI#lM<1esK6XVmJlC&ypa?)1!TxS zh$S9M0^_-lio-LE1C3W@l4r?e2h~O7 z9YV55V5tfn$CjuQ$3>M+b6hf1Nqeh+)|g1j#628_;s`|BUvlhptjdx8{~U9E|K=E{ zVfB?HRrM+%>m$`J<>C)gQCes)sRpQEVPJEZ0OSI08z5#4$gsdXK&odx%yHSI{&4HT z%%cjt_MqCsdwe7I0f~^c=!Pi8f|=x?&n>{!Jb_Cyl~*J45Y>Usbz(ymGX2A=EZ3bj z?pqRAAY3U%GUe%8zQqftcmfTT@*B9^3dS{j_Cw#;_?^mUkL`>O2!CaJdS(0UE zkYZzxIIpvUiP6-l3@IRX#8a^?+QP*^C?q!=a{uqxzm3)(;aIZY<<(W7n%Z$yV?k1G z;y6{7%8|0=YB}Lv + + + + @@ -61,7 +65,12 @@ - + + + + + + diff --git a/src/themes/menu.xml b/src/themes/menu.xml new file mode 100644 index 00000000..bc636061 --- /dev/null +++ b/src/themes/menu.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/themes/simple2.xml b/src/themes/simple2.xml new file mode 100644 index 00000000..0d2c3ad9 --- /dev/null +++ b/src/themes/simple2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/src/themes/simple3.xml b/src/themes/simple3.xml new file mode 100644 index 00000000..f27f34e8 --- /dev/null +++ b/src/themes/simple3.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + +