Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Anderson <tony_anderson@usa.net>2009-08-28 10:12:23 (GMT)
committer Tony Anderson <tony_anderson@usa.net>2009-08-28 10:12:23 (GMT)
commit3a340cf8d2a28b1ffe1ee33636277780eb14e328 (patch)
treeea8d10ec175962efa94a6df599d6df0654474410
parentf87a9f50e983491e70ac310e212bb4fa0d38d957 (diff)
version 1
-rwxr-xr-xactivity/activity.info4
-rw-r--r--[-rwxr-xr-x]activity/datamanager-activity.svg44
-rwxr-xr-xactivity/permissions.info1
-rw-r--r--datamanager.py301
-rwxr-xr-xicons/btncancel.pngbin3684 -> 0 bytes
-rwxr-xr-xicons/btngo.pngbin1141 -> 0 bytes
-rwxr-xr-xicons/info.svg37
-rw-r--r--icons/up_arrow.svg12
8 files changed, 266 insertions, 133 deletions
diff --git a/activity/activity.info b/activity/activity.info
index bdb6d9e..790a935 100755
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,7 +1,7 @@
[Activity]
-name = Data Manager
+name = DataManager
service_name = org.olenepal.DataManager
class = datamanager.DataManager
icon = datamanager-activity
-activity_version = 2
+activity_version = 1
show_launcher = yes
diff --git a/activity/datamanager-activity.svg b/activity/datamanager-activity.svg
index 1ae35db..0049daa 100755..100644
--- a/activity/datamanager-activity.svg
+++ b/activity/datamanager-activity.svg
@@ -1,11 +1,33 @@
-<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
- <!ENTITY stroke_color "#666666">
- <!ENTITY fill_color "#ffffff">
-]><svg enable-background="new 0 0 55 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="activity-journal">
- <path d="M45.866,44.669 c0,2.511-1.528,4.331-4.332,4.331H12.077V6h29.458c2.15,0,4.332,2.154,4.332,4.33L45.866,44.669L45.866,44.669z" fill="&fill_color;" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
-
- <line fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="21.341" x2="21.341" y1="6.121" y2="48.881"/>
- <path d="M7.384,14.464 c0,0,2.084,0.695,4.17,0.695c2.086,0,4.173-0.695,4.173-0.695" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
- <path d="M7.384,28.021 c0,0,1.912,0.695,4.345,0.695s3.999-0.695,3.999-0.695" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
- <path d="M7.384,41.232 c0,0,1.736,0.695,4.518,0.695c2.781,0,3.825-0.695,3.825-0.695" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
-</g></svg> \ No newline at end of file
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg>
+<svg version="1.1" baseProfile="full" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" zoomAndPan="magnify"
+ id="Test File"
+ viewBox="-21 -21 42 42"
+ width="800"
+ height="800">
+
+<defs>
+<radialGradient id="shine"
+ cx=".2" cy=".2" r=".5" fx=".2" fy=".2">
+ <stop offset="0" stop-color="white" stop-opacity=".7"/>
+ <stop offset="1" stop-color="white" stop-opacity="0"/>
+</radialGradient>
+<radialGradient id="grad"
+ cx=".5" cy=".5" r=".5" >
+ <stop offset="0" stop-color="yellow"/>
+ <stop offset=".75" stop-color="yellow"/>
+ <stop offset=".95" stop-color="#ee0"/>
+ <stop offset="1" stop-color="#e8e800"/>
+</radialGradient>
+
+</defs>
+
+<circle r="20" stroke="black" stroke-width=".15" fill="url(#grad)"/>
+<circle r="20" fill="url(#shine)"/>
+<g id="right">
+ <ellipse rx="2.5" ry="4" cx="-6" cy="-7" fill="black"/>
+ <path fill="none" stroke="black" stroke-width=".5" stroke-linecap="round" d="M 10.6,2.7 a 4,4,0 0,0 4,3"/>
+</g>
+<use xlink:href="#right" transform="scale(-1,1)"/>
+<path fill="none" stroke="black" stroke-width=".75" d="M -12,5 A 13.5,13.5,0 0,0 12,5 A 13,13,0 0,1 -12,5"/>
+</svg>
diff --git a/activity/permissions.info b/activity/permissions.info
deleted file mode 100755
index 585d713..0000000
--- a/activity/permissions.info
+++ /dev/null
@@ -1 +0,0 @@
-constant-uid
diff --git a/datamanager.py b/datamanager.py
index 548b262..4732e72 100644
--- a/datamanager.py
+++ b/datamanager.py
@@ -21,12 +21,15 @@
from sugar.activity import activity
from sugar.datastore import datastore
from sugar.graphics.toolbutton import ToolButton
-from sugar.graphics.alert import Alert
from sugar.graphics.menuitem import MenuItem
-import os, sys
+import os, sys, time
import subprocess
+
+import pygtk
+pygtk.require('2.0')
import gtk
+
from path import path
from datetime import datetime
from time import strftime
@@ -43,11 +46,14 @@ COMMONSPATH = path("/library/Commons")
LOCALPATH = path("/home/olpc/.sugar/default/datastore/store")
DATEFORMAT = "%Y-%m-%d %H:%M:%S"
+ACTIVITYTOOLBAR = 0
USAGETOOLBAR = 1
+DEFAULTTOOLBAR = ACTIVITYTOOLBAR
class DataManager(activity.Activity):
def __init__(self, handle):
+ global store
print 'activity started'
global SERIALNUMBER
activity.Activity.__init__(self, handle)
@@ -58,52 +64,42 @@ class DataManager(activity.Activity):
SERIALNUMBER = t[:11]
print 'serial-number', SERIALNUMBER
subprocess.call("mkdir -p " + WORKPATH, shell=True)
- #set up alert
- #self.alert = Alert()
- #self.alert.props.title='Updating Datastore'
- #self.alert.props.msg='Please be patient - updating store'
#Create the usage toolbar
self.usageTB = UsageToolbar(self)
#Create the standard activity toolbox
toolbox = activity.ActivityToolbox(self)
toolbox.add_toolbar("Usage", self.usageTB)
- toolbox.set_current_toolbar(USAGETOOLBAR)
+ toolbox.set_current_toolbar(DEFAULTTOOLBAR)
self.set_toolbox(toolbox)
toolbox.show()
self.viewer = Listview()
- #treeView = self.viewer.get_treeView()
- #treeView.set_model(self.viewer.create_model(self.usageTB))
#set up main screen
self.main_screen = gtk.VBox()
- #self.main_screen.pack_start(self.alert, False, False, 0)
self.main_screen.pack_start(self.viewer, True, True, 0)
self.set_canvas(self.main_screen)
print 'canvas set'
self.show_all()
self.viewer.set_label('canvas set')
- #self.alert.hide()
-
- def start_cb(self, widget):
treeView = self.viewer.get_treeView()
treeView.set_model(self.viewer.create_model(self.usageTB))
+ txt = 'ready: ' + str(len(store)) + ' documents '
+ txt = txt + str(MB) + '/1000 ' + str(PCT) + '%'
+ self.viewer.set_label(txt)
- def close_cb(self, widget):
- #debug switch to cancel uploads
- #return True
+ def write_file(self, file_path):
global SERIALNUMBER
global store
global online
action_request = 5
status = 4
+ f = open(file_path, 'w')
+ f.write('this is a dummy file')
+ f.close()
if not online:
return True
self.viewer.set_label('closing - online')
- self.logger('')
- self.logger('can_close_online')
print 'can_close online'
#now the real work begins
- #self.alert.show()
- self.logger('showing alert')
remove_list = []
for row in store:
self.viewer.set_label('reviewing status: ' + row[0])
@@ -117,32 +113,27 @@ class DataManager(activity.Activity):
row[action_request] = +1
if row[action_request] < 0:
remove_list.append(row[0])
- if row[action_request] == 1:
+ if row[action_request] == 1 and row[status] == 'Red':
#upload it
self.viewer.set_label('reviewing status: ' + row[0] + ' upload')
- self.logger('upload' + row[0] + '.metadata')
mname = row[0] + '.metadata'
fname = row[0]
- src = LOCALPATH / mname
- pfx = SERIALNUMBER + '@schoolserver:'
- dst = STOREPATH / SERIALNUMBER
- cmd = 'scp ' + src + ' ' + pfx + dst
- print 'metadata upload', cmd
- self.logger('call scp')
- subprocess.call(cmd, shell=True)
- self.logger('metadata uploaded')
- src = LOCALPATH / fname
- cmd = 'scp ' + src + ' ' + pfx + dst
- if src.exists():
+ srcm = LOCALPATH / mname
+ srcf = LOCALPATH / fname
+ if srcm.exists() and srcf.exists():
+ pfx = SERIALNUMBER + '@schoolserver:'
+ dst = STOREPATH / SERIALNUMBER
+ cmd = 'scp ' + srcm + ' ' + pfx + dst
+ print 'metadata upload', cmd
+ subprocess.call(cmd, shell=True)
+ cmd = 'scp ' + srcf + ' ' + pfx + dst
print 'file upload', cmd
subprocess.call(cmd, shell=True)
- self.logger('file uploaded')
else:
- print 'src path does not exist', src
- if row[action_request] > 1 and row[status] == "light green":
+ print 'upload request but no document', row[1], srcf
+ if row[action_request] > 1 and row[status] == "Green":
#download from schoolserver
self.viewer.set_label('reviewing status: ' + row[0] + ' download')
- self.logger('download ' + row[0] )
mname = row[0] + '.metadata'
fname = row[0]
dst = LOCALPATH
@@ -151,65 +142,89 @@ class DataManager(activity.Activity):
cmd = 'scp ' + pfx + src + ' ' + dst
print 'metadata download', cmd
subprocess.call(cmd, shell=True)
- self.logger('metadata downloaded')
src = STOREPATH / SERIALNUMBER / fname
cmd = 'scp ' + pfx + src + ' ' + dst
+ print 'file download', cmd
subprocess.call(cmd, shell=True)
- self.logger('file downloaded')
if row[action_request] > 1 and row[status] == "cyan":
#download from Commons
self.viewer.set_label('reviewing status: ' + row[0] + ' download')
- self.logger('download from Commons: ' + row[0])
#if mime_type is activity - install it
mname = row[0] + '.metadata'
fname = row[0]
dst = LOCALPATH
pfx = SERIALNUMBER + '@schoolserver:'
- src = COMMONSPATH / SERIALNUMBER / mname
+ src = COMMONSPATH / mname
cmd = 'scp ' + pfx + src + ' ' + dst
print 'metadata download', cmd
subprocess.call(cmd, shell=True)
- src = COMMONSPATH / SERIALNUMBER / fname
+ src = COMMONSPATH / fname
cmd = 'scp ' + pfx + src + ' ' + dst
+ print 'file download', cmd
+ subprocess.call(cmd, shell=True)
+ if row[action_request] == 1 and row[status] == 'cyan':
+ #upload entry to Commons
+ self.viewer.set_label('reviewing status: ' + row[0] + ' upload')
+ #if mime_type is activity - install it
+ mname = row[0] + '.metadata'
+ fname = row[0]
+ dst = COMMONSPATH / mname
+ pfx = SERIALNUMBER + '@schoolserver:'
+ src = LOCALPATH / mname
+ cmd = 'scp ' + src + ' ' + pfx + dst
+ print 'metadata upload', cmd
+ subprocess.call(cmd, shell=True)
+ src = LOCALPATH / fname
+ cmd = 'scp ' + src + ' ' + pfx + dst
+ print 'file upload', cmd
subprocess.call(cmd, shell=True)
#now remove
- self.logger('now remove')
- try:
- ds_objects,num_objects = datastore.find(dict())
- except:
- print 'datastore.find failed', sys.exc_info()[0]
- num_objects = 0
- #self.logger(str(num_objects) + ', ' + str(len(remove_list)))
- self.logger('datastore.find complete')
- print 'remove_list', len(remove_list), remove_list
- for i in xrange(0, num_objects, 1):
- ds_objects[i].destroy()
- obj = ds_objects[i].object_id
- self.viewer.set_label('checking files ' + obj)
- if str(obj) in remove_list:
- datastore.delete(obj)
- self.logger(obj + ' deleted')
- txt = 'deleted ' + obj
- self.viewer.set_label('checking files ' + obj + ' deleted')
- #self.remove_alert(self.alert)
- self.logger('done')
- self.viewer.set_label('done')
- return
-
- def logger(self, log):
- l = ''
- logpth = path('/tmp/log')
- if logpth.exists():
- f = open(logpth,'r')
- l = f.read()
- f.close()
- l = l + log + '\n'
- if len(log) == 0:
- l = ''
- f = open(logpth, 'w')
- f.write(l)
+ self.viewer.set_label('removing files ...')
+
+ count = 0
+ for obj in remove_list:
+ pth = LOCALPATH / obj
+ if pth.exists():
+ pth.remove()
+ fn = obj + '.metadata'
+ pth = LOCALPATH / fn
+ if pth.exists():
+ pth.remove()
+ #datastore.delete(obj)
+ count += 1
+ txt = 'done ' + str(count) + ' files deleted'
+ self.viewer.set_label(txt)
+
+ def show_properties(self, widget):
+ treeView = self.viewer.get_treeView()
+ treeselection = treeView.get_selection()
+ model, row = treeselection.get_selected()
+ obj = model[row][0]
+ fn = obj + '.metadata'
+ pth = LOCALPATH / fn
+ print 'on_activated:', pth
+ f = open(pth,'r')
+ metadata = eval(f.read())
f.close()
+ tstr = ""
+ for k, v in metadata.iteritems():
+ try:
+ if len(str(v)) > 0:
+ tstr = tstr + k + ':' + v + '\n'
+ except:
+ tstr = tstr + k + ':' + "" + '\n'
+ self.viewer.label.set_text(tstr)
+
+ def upload_commons(self, widget):
+ treeView = self.viewer.get_treeView()
+ treeselection = treeView.get_selection()
+ model, selection = treeselection.get_selected()
+ row = model[selection]
+ if row[4] == 'Green':
+ row[4] = 'cyan'
+ row[5] = 1
+
# based on PyGTK tutorial by jan bodnar, zetcode.com, February 2009
class Listview(gtk.VBox):
def __init__(self):
@@ -225,6 +240,7 @@ class Listview(gtk.VBox):
self.treeView = gtk.TreeView()
self.treeView.connect("row-activated", self.on_activated)
self.treeView.set_rules_hint(True)
+ self.treeView.show_all()
sw.add(self.treeView)
print 'create_columns'
@@ -235,7 +251,7 @@ class Listview(gtk.VBox):
print 'set up metadata display'
self.label = gtk.Label("")
self.label.show()
- self.frame = gtk.Frame(label='metadata')
+ self.frame = gtk.Frame(label='Status')
self.frame.add(self.label)
self.frame.show()
self.pack_start(self.frame, False, False, 0)
@@ -247,6 +263,8 @@ class Listview(gtk.VBox):
def set_label(self, txt):
self.label.set_text(txt)
+ while gtk.events_pending():
+ gtk.main_iteration(False)
def create_model(self, tb):
global online
@@ -300,8 +318,13 @@ class Listview(gtk.VBox):
#is this file already on the schoolserver?
if f.namebase not in storelist and f.ext == '.metadata':
m = open(f, 'r')
- metadata = eval(m.read())
+ mstr = m.read()
m.close()
+ try:
+ metadata = eval(mstr)
+ except:
+ metadata = {}
+ print 'metadata eval failed', sys.exc_info()[0]
obj = f.namebase
try:
title = metadata['title']
@@ -331,9 +354,26 @@ class Listview(gtk.VBox):
fpath = LOCALPATH / obj
if fpath.exists():
color = 'Red'
+ if mime_type == 'application/zip':
+ fn = metadata['filename']
+ if fn.find('.smxo') > 0:
+ mime_type = 'application/x-smile'
+ if fn.find('.cpxo') > 0:
+ mime_type = 'application/x-classroompresenter'
+ if fn.find('.iqxo') > 0:
+ mime_type = 'application/x-imagequiz'
+ if not mime_type == 'application/zip':
+ metadata['mime_type'] = mime_type
+ mstr = repr(metadata)
+ m = open(f, 'w')
+ m.write(mstr)
+ m.close()
+ if metadata['activity'] == 'org.olenepal.DataManager':
+ color = 'White'
else:
color = 'White'
store.append([obj, title, mime_type, date, color, 0])
+
store.set_sort_column_id(3, gtk.SORT_DESCENDING)
print 'return store'
return store
@@ -344,6 +384,10 @@ class Listview(gtk.VBox):
treeView = self.get_treeView()
soup = BeautifulSoup(response)
entry_candidates = soup.findAll('a')
+ if source == "Blue":
+ base = COMMONSPATH
+ else:
+ base = STOREPATH / SERIALNUMBER
for entry in entry_candidates:
t = str(entry)
pos = t.find('metadata')
@@ -351,19 +395,25 @@ class Listview(gtk.VBox):
pos2 = t.find('href=')
obj = t[pos2+6:pos-1]
fn = t[pos2+6:pos+8]
- if source == "blue":
- pth = COMMONSPATH / fn
- else:
- pth = STOREPATH / SERIALNUMBER / fn
+ pth = base / fn
cmd = "scp " + SERIALNUMBER + "@schoolserver:" + pth
cmd = cmd + " " + WORKPATH
subprocess.call(cmd, shell=True)
pth = WORKPATH / fn
- f = open(pth, 'r')
- t = f.read()
- f.close()
+ if pth.exists():
+ f = open(pth, 'r')
+ t = f.read()
+ f.close()
+ else:
+ print 'addentries: no f', f.name
subprocess.call("rm -rf pth", shell=True)
- metadata = eval(t)
+ try:
+ metadata = eval(t)
+ except:
+ print 'addentries eval failed', sys.exc_info()[0]
+ print 'addentries eval failed', f.name
+ print 'addentries eval failed', len(t), t
+ metadata = {}
try:
title = metadata['title']
except:
@@ -371,12 +421,20 @@ class Listview(gtk.VBox):
title = metadata['title:text']
except:
title = ''
- mime_type = metadata['mime_type']
- mtime = metadata['mtime'].replace('T',' ')
- pos = mtime.find('.')
- date = mtime[:pos]
+ try:
+ mime_type = metadata['mime_type']
+ except:
+ mime_type = ""
+ try:
+ mtime = metadata['mtime'].replace('T',' ')
+ pos = mtime.find('.')
+ date = mtime[:pos]
+ except:
+ date = ""
store.append([obj, title, mime_type, date, source, 0])
treeView.set_model(store)
+ treeView.scroll_to_cell(len(store)-1)
+ self.set_label(title)
def create_columns(self, treeView):
@@ -397,34 +455,34 @@ class Listview(gtk.VBox):
column.set_sort_column_id(3)
treeView.append_column(column)
- def on_activated(self, widget, row, col):
+ def on_activated(self, widget, selection, col):
#what we really want to do
#is define a request to upload/remove/download an entry
- print 'on_activated', widget, row, col
model = widget.get_model()
- obj = model[row][0]
- fn = obj + '.metadata'
- pth = LOCALPATH / fn
- print 'on_activated:', pth
- f = open(pth,'r')
- metadata = eval(f.read())
- f.close()
-
- tstr = ""
- for k, v in metadata.iteritems():
- try:
- if len(str(v)) > 0:
- tstr = tstr + k + ':' + v + '\n'
- except:
- tstr = tstr + k + ':' + "" + '\n'
- self.label.set_text(tstr)
- print 'on_activated', tstr
+ row = model[selection]
+ row4 = row[4]
+ row5 = row[5]
+ if row[4] == 'light green':
+ row[4] = 'Green'
+ row[5] = 2
+ elif row[4] == 'cyan':
+ row[4] = 'Blue'
+ row[5] = 2
+ elif row[4] == 'Green':
+ row[4] = 'light green'
+ row[5] = -1
+ elif row[4] == 'Blue':
+ row[4] = 'cyan'
+ row[5] = -1
+ print 'on_activiated', row[1], row4, '->', row[4], row5, '->', row[5]
class UsageToolbar(gtk.Toolbar):
def __init__(self, activity):
global SERIALNUMBER
+ global MB
+ global PCT
gtk.Toolbar.__init__(self)
self.lbl1 = gtk.Label("XO ")
@@ -453,9 +511,12 @@ class UsageToolbar(gtk.Toolbar):
print 'df', len(t), t
totalsize = t.split('\n')
t1 = totalsize[1]
+ pos = t1.find('M')
+ MB = t1[pos-4:pos].strip()
pos = t1.find('%')
rslt = t1[pos-4:pos].strip()
print 'rslt', float(rslt) * .01
+ PCT = rslt
self.fuelguage1.set_fraction(float(rslt) * .01)
self.fuelguage1.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
self.fuelguage1.show()
@@ -490,12 +551,14 @@ class UsageToolbar(gtk.Toolbar):
self.item2.add(self.fuelguage2)
self.insert(self.item2, -1)
- self.startbtn = ToolButton('btngo')
- self.startbtn.connect('clicked', activity.start_cb)
- self.startbtn.show()
- self.insert(self.startbtn, -1)
-
- self.closebtn = ToolButton("btncancel")
- self.closebtn.connect('clicked', activity.close_cb)
- self.closebtn.show()
- self.insert(self.closebtn, -1)
+ self.__infobtn = ToolButton('info')
+ self.__infobtn.set_tooltip("Show properties")
+ self.__infobtn.connect('clicked', activity.show_properties)
+ self.insert(self.__infobtn, -1)
+ self.__infobtn.show()
+
+ self.__uploadbtn = ToolButton('up_arrow')
+ self.__uploadbtn.set_tooltip("Upload document to Commons")
+ self.__uploadbtn.connect('clicked', activity.upload_commons)
+ self.insert(self.__uploadbtn, -1)
+ self.__uploadbtn.show()
diff --git a/icons/btncancel.png b/icons/btncancel.png
deleted file mode 100755
index fb13ad5..0000000
--- a/icons/btncancel.png
+++ /dev/null
Binary files differ
diff --git a/icons/btngo.png b/icons/btngo.png
deleted file mode 100755
index b67609b..0000000
--- a/icons/btngo.png
+++ /dev/null
Binary files differ
diff --git a/icons/info.svg b/icons/info.svg
new file mode 100755
index 0000000..10cf3a8
--- /dev/null
+++ b/icons/info.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" ?>
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ viewBox="0 0 62 62"
+ width="620" height="620"
+ version="1.0">
+ <defs>
+ <linearGradient id="fieldGradient"
+ gradientUnits="userSpaceOnUse"
+ x1="42.9863" y1="7.01270"
+ x2="22.0144" y2="51.9871">
+ <stop offset="0.0" stop-color="#BCD6FE" />
+ <stop offset="1.0" stop-color="#6787D3" />
+ </linearGradient>
+ <linearGradient id="edgeGradient"
+ gradientUnits="userSpaceOnUse"
+ x1="55.4541" y1="42.7529"
+ x2="9.54710" y2="16.2485">
+ <stop offset="0.0" stop-color="#3057A7" />
+ <stop offset="1.0" stop-color="#5A7AC6" />
+ </linearGradient>
+ <radialGradient id="shadowGradient">
+ <stop offset="0.0" stop-color="#C0C0C0" />
+ <stop offset="0.88" stop-color="#C0C0C0" />
+ <stop offset="1.0" stop-color="#C0C0C0" stop-opacity="0.0" />
+ </radialGradient>
+ </defs>
+ <circle id="shadow" r="26.5" cy="29.5" cx="32.5"
+ fill="url(#shadowGradient)"
+ transform="matrix(1.0648,0.0,0.0,1.064822,-2.1,1.0864)" />
+ <circle id="field" r="25.8" cx="31" cy="31"
+ fill="url(#fieldGradient)" stroke="url(#edgeGradient)" stroke-width="2" />
+ <g id="info" fill="white">
+ <polygon points="23,25 35,25 35,44 39,44 39,48 23,48 23,44 27,44 27,28 23,28 23,25" />
+ <circle r="4" cx="31" cy="17" />
+ </g>
+</svg>
diff --git a/icons/up_arrow.svg b/icons/up_arrow.svg
new file mode 100644
index 0000000..4df2d33
--- /dev/null
+++ b/icons/up_arrow.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="200px" height="200px" zoomAndPan="disable" >
+ <defs>
+ <linearGradient id="green_shade" x1="25%" y1="0%" x2="75%" y2="100%">
+ <stop offset="0%" style="stop-color:LawnGreen; stop-opacity:0.8"/>
+ <stop offset="100%" style="stop-color:LimeGreen; stop-opacity:1"/>
+ </linearGradient>
+ </defs>
+ <circle cx="100" cy="100" r="99" style="fill:url(#green_shade); stroke:DarkGreen; stroke-width:2;"/>
+ <polygon points="100,15 150,65 120,65 120,185 80,185 80,65 50,65" style="fill:Green; stroke:DarkGreen; stroke-width:2;"/>
+</svg>