new portfolio features, metal graphs, preparing for 0.4 release
[stockthis] / stockthis.py
index 578af6d..7ca72b4 100644 (file)
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #
-# StocksPy: Application to get stocks data from Yahoo Finance.
-# Version 0.1
+# StocksThis: Application to get stocks data from Yahoo Finance.
 #
 
+_version = "StockThis 0.4 rev1"
+VERSION = "0.4"
+
 import urllib2
 import gtk, gobject
 import os
 import hildon
-
-#import osso
-#osso_c = osso.Context("net.yerga.stockthis", "0.3", False)
-
-from marketdata import markets, idmarket, localmarkets, localids
+import marketdata
 import settings
+import logging
+import sys
+
+from portrait import FremantleRotation
 
+import osso
+osso_c = osso.Context("net.yerga.stockthis", "0.3", False)
 
 #detect if is ran locally or not
-import sys
 runningpath = sys.path[0]
 
-if '/usr/share' in runningpath:
+if '/opt/' in runningpath:
     runninglocally = False
 else:
     runninglocally = True
@@ -46,6 +49,23 @@ HOME = os.path.expanduser("~")
 settingsdb, imgdir, configdir, logfile = \
     settings.define_paths(runninglocally, HOME)
 
+logger = logging.getLogger('st')
+logging.basicConfig(filename=logfile,level=logging.ERROR, filemode='w')
+
+DEBUG = True
+
+if DEBUG:
+    #set the main logger to DEBUG
+    logger.setLevel(logging.DEBUG)
+
+    #Create a handler for console debug
+    console = logging.StreamHandler()
+    console.setLevel(logging.DEBUG)
+    # set a format which is simpler for console use
+    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
+    # tell the handler to use this format
+    console.setFormatter(formatter)
+    logging.getLogger('').addHandler(console)
 
 fhsize = gtk.HILDON_SIZE_FINGER_HEIGHT
 horbtn = hildon.BUTTON_ARRANGEMENT_HORIZONTAL
@@ -53,6 +73,9 @@ ui_normal = gtk.HILDON_UI_MODE_NORMAL
 ui_edit = gtk.HILDON_UI_MODE_EDIT
 winprogind = hildon.hildon_gtk_window_set_progress_indicator
 
+allnames = []
+allsymbols = []
+
 gtk.gdk.threads_init()
 
 class StocksPy:
@@ -66,20 +89,19 @@ class StocksPy:
         self.program.add_window(self.window)
         self.window.connect("destroy", gtk.main_quit)
 
-        menu = hildon.AppMenu()
-        self.window.set_app_menu(menu)
-        button = gtk.Button("About")
-        button.connect("clicked", self.on_about)
-        menu.append(button)
-        menu.show_all()
+        FremantleRotation('StockThis', None, "0.3", 0)
+
+        self.create_menu(self.window)
 
         vbox = gtk.VBox()
-        toolbar = self.main_toolbar(False, False)
+        toolbar = self.main_toolbar(False, False, None, '', '', True)
 
         parea = hildon.PannableArea()
         tv = hildon.GtkTreeView(ui_normal)
-        inmodel = self.__create_model(markets, idmarket)
-        tv.connect("row-activated", self.show_instrument_view, inmodel)
+        inmodel = self.__create_model(marketdata.main, marketdata.idmain)
+        tv.connect("row-activated", self.show_instrument_view, inmodel,
+                    marketdata.localmarkets, marketdata.localids,
+                    marketdata.idmain)
         tv.set_model(inmodel)
         self._tv_columns(tv)
         parea.add(tv)
@@ -91,21 +113,41 @@ class StocksPy:
         self.window.add(vbox)
         self.window.show_all()
 
-    def show_instrument_view(self, widget, path, column, inmodel):
-        market = inmodel[path][0]
+        self.show_info_banner(self.window,
+            ("StockThis uses your network connection to get data.\n"
+            "Be aware of the high costs that your network provider may apply."))
+
+    def create_menu(self, window):
+        menu = hildon.AppMenu()
+        window.set_app_menu(menu)
+        button = gtk.Button("About")
+        button.connect("clicked", About)
+        menu.append(button)
+        menu.show_all()
 
-        names = localmarkets[idmarket.index(market)]
-        ids = localids[idmarket.index(market)]
+    def show_instrument_view(self, widget, path, column, inmodel, names,
+        ids, mindex):
+        market = inmodel[path][0]
+        names = names[mindex.index(market)]
+        ids = ids[mindex.index(market)]
 
         window = hildon.StackableWindow()
+        self.create_menu(window)
+        window.set_title(inmodel[path][1])
 
         vbox = gtk.VBox()
-        toolbar = self.main_toolbar(False, False)
+        toolbar = self.main_toolbar(False, False, None, '', '', False)
 
         parea = hildon.PannableArea()
+        parea.connect("horizontal-movement", self.horizontal_mov)
         tv = hildon.GtkTreeView(ui_normal)
         model = self.__create_model(names, ids)
-        tv.connect("row-activated", self.show_quotes_view, model, False)
+
+        if market == 'metals':
+            tv.connect("row-activated", self.show_metal_graphs, model)
+        else:
+            tv.connect("row-activated", self.show_quotes_view, model, False)
+
         tv.set_model(model)
         self._tv_columns(tv)
         parea.add(tv)
@@ -117,109 +159,191 @@ class StocksPy:
         window.add(vbox)
         window.show_all()
 
+    def horizontal_mov(self, parea, direction, initial_x, initial_y):
+        #direction = 2 right-to-left
+        #direction = 3 lefto-to-right
+
+        vadj = parea.get_vadjustment()
+        val = vadj.get_value()
+
+        if direction == 2:
+            if int(val)-2500 < 0:
+                 parea.scroll_to(-1, 0)
+            else:
+                parea.scroll_to(-1, int(val)-2500)
+        elif direction == 3:
+            parea.scroll_to(-1, int(val)+3500)
+
+        #print val
 
     def show_quotes_view(self, widget, path, column, model, portfolio):
         quote = model[path][0], model[path][1]
+        #print "quote:", quote[0]
+        #('EURUSD=X', 'EUR/USD')
+
+        #Currencies and ETFs should show the list now -> view = True
+        #Other items show a new list with options
+        view = False
+        for i in marketdata.localids[(len(marketdata.localids)-2):]:
+            for j in i:
+                if quote[0] == j:
+                    #print j
+                    view = True
+
+        if not view:
+            if quote[0] in marketdata.idindexes:
+                self.show_instrument_view(widget, path, column, model,
+                                        marketdata.wnamesindexes,
+                                        marketdata.widsindexes,
+                                        marketdata.idindexes)
+                return
+            if quote[0] in marketdata.idotmarkets:
+                self.show_instrument_view(widget, path, column, model,
+                                        marketdata.omnames,
+                                        marketdata.omsymbols,
+                                        marketdata.idotmarkets)
+                return
+            if quote[0] in marketdata.ideumarkets:
+                self.show_instrument_view(widget, path, column, model,
+                                        marketdata.eunames,
+                                        marketdata.eusymbols,
+                                        marketdata.ideumarkets)
+                return
+            if quote[0] in marketdata.idusmarkets:
+                self.show_instrument_view(widget, path, column, model,
+                                        marketdata.usnames,
+                                        marketdata.ussymbols,
+                                        marketdata.idusmarkets)
+                return
 
-        self.stocks_id = quote[0]
-        self.stocks_name = quote[1]
-        print quote
 
-        self.quotes_win = hildon.StackableWindow()
+        win = hildon.StackableWindow()
+        self.create_menu(win)
+        win.set_title(quote[1])
 
         vbox = gtk.VBox()
-        toolbar = self.main_toolbar(True, portfolio)
 
-
-        self.titlelbl = gtk.Label('')
-        self.titlelbl.set_markup('<b><big>' + quote[1].replace('&', '') +
+        ltitle = gtk.Label('')
+        ltitle.set_markup('<b><big>' + quote[1].replace('&', '') +
                                  '</big></b>')
         color = gtk.gdk.color_parse("#03A5FF")
-        self.titlelbl.modify_fg(gtk.STATE_NORMAL, color)
+        ltitle.modify_fg(gtk.STATE_NORMAL, color)
 
         parea = hildon.PannableArea()
+        parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
 
         vbox1 = gtk.VBox()
 
         hbox = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>Price:</big></b>')
-        self.lprice = gtk.Label('')
-        hbox.pack_start(label, False, False, 20)
-        hbox.pack_start(self.lprice, False, False, 245)
+        label.set_markup('<b><big>%39s:</big></b>' % '<u>Price</u>')
+        lprice = gtk.Label('')
+        hbox.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox.pack_start(lprice, False, False, 25)
+        #else:
+        #    hbox.pack_start(lprice, False, False, 245)
+
         vbox1.pack_start(hbox, True, True, 0)
 
         hbox = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>Change:</big></b>')
-        self.lchange = gtk.Label('')
-        self.lpercent = gtk.Label('')
-        hbox.pack_start(label, False, False, 20)
-        hbox.pack_start(self.lchange, False, False, 205)
-        hbox.pack_start(self.lpercent, False, False, 0)
+        label.set_markup('<b><big>%35s:</big></b>' % '<u>Change</u>')
+        lchange = gtk.Label('')
+        lpercent = gtk.Label('')
+        hbox.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox.pack_start(lchange, False, False, 25)
+        hbox.pack_start(lpercent, False, False, 0)
+        #else:
+        #    hbox.pack_start(lchange, False, False, 205)
+        #    hbox.pack_start(lpercent, False, False, 0)
+
         vbox1.pack_start(hbox, True, True, 0)
 
         hbox = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>Volume:</big></b>')
-        self.lvolume = gtk.Label('')
-        hbox.pack_start(label, False, False, 20)
-        hbox.pack_start(self.lvolume, False, False, 207)
+        label.set_markup('<b><big>%35s:</big></b>' % '<u>Volume</u>')
+        lvolume = gtk.Label('')
+        hbox.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox.pack_start(lvolume, False, False, 25)
+        #else:
+        #    hbox.pack_start(lvolume, False, False, 207)
+
         vbox1.pack_start(hbox, True, True, 0)
 
         hbox = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>52 week high:</big></b>')
-        self.l52whigh = gtk.Label('')
-        hbox.pack_start(label, False, False, 20)
-        hbox.pack_start(self.l52whigh, False, False, 110)
+        label.set_markup('<b><big>%30s:</big></b>' % '<u>52 week high</u>')
+        l52whigh = gtk.Label('')
+        hbox.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox.pack_start(l52whigh, False, False, 25)
+        #else:
+        #    hbox.pack_start(l52whigh, False, False, 125)
+
         vbox1.pack_start(hbox, True, True, 0)
 
         hbox = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>52 week low:</big></b>')
-        self.l52wlow = gtk.Label('')
-        hbox.pack_start(label, False, False, 20)
-        hbox.pack_start(self.l52wlow, False, False, 125)
+        label.set_markup('<b><big>%30s:</big></b>' % '<u>52 week low</u>')
+        l52wlow = gtk.Label('')
+        hbox.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox.pack_start(l52wlow, False, False, 26)
+        #else:
+        #    hbox.pack_start(l52wlow, False, False, 140)
         vbox1.pack_start(hbox, True, True, 0)
 
+        #if self.is_portrait():
+        #    hbox = gtk.VBox()
+        #else:
         hbox = gtk.HBox()
         button1 = hildon.PickerButton(fhsize, horbtn)
         data = ["50", "100", "200", "300", "400", "500", "600", "700", "800",
                 "900", "1000"]
         selector = self.create_selector(data, True)
         button1.set_selector(selector)
-        button1.set_title("Shares")
-        #FIXME: Improve as it's shown you have a component in your portfolio
-        shares = self.get_shares_from_symbol()
+        button1.set_title("Your shares")
+        shares = self.get_shares_from_symbol(quote[0])
         button1.set_value(shares)
         hbox.pack_start(button1, True, True, 0)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_title("Add to Portfolio")
-        button.connect("clicked", self.add_to_portfolio, button1)
+        button.connect("clicked", self.add_to_portfolio, button1, quote[0], quote[1])
         hbox.pack_start(button, True, True, 0)
 
         hbox1 = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>Shares:</big></b>')
-        self.shares = gtk.Label(shares)
-        hbox1.pack_start(label, False, False, 20)
-        hbox1.pack_start(self.shares, False, False, 215)
+        label.set_markup('<b><big>%37s:</big></b>' % '<u>Shares</u>')
+        lshares = gtk.Label(shares)
+        hbox1.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox1.pack_start(lshares, False, False, 25)
+        #else:
+        #    hbox1.pack_start(lshares, False, False, 220)
 
         hbox2 = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup('<b><big>Holdings Value:</big></b>')
-        self.holdingsvalue = gtk.Label("")
-        hbox2.pack_start(label, False, False, 20)
-        hbox2.pack_start(self.holdingsvalue, False, False, 85)
+        label.set_markup('<b><big>%29s:</big></b>' % '<u>Holdings Value</u>')
+        holdingsvalue = gtk.Label("")
+        hbox2.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox2.pack_start(holdingsvalue, False, False, 25)
+        #else:
+        #    hbox2.pack_start(holdingsvalue, False, False, 105)
 
         hbox3 = gtk.HBox()
         label = gtk.Label('')
-        label.set_markup("<b><big>Day's Value Change:</big></b>")
-        self.dayvaluechange = gtk.Label("")
-        hbox3.pack_start(label, False, False, 20)
-        hbox3.pack_start(self.dayvaluechange, False, False, 10)
+        label.set_markup("<b><big>%25s:</big></b>" % "<u>Day's Value Change</u>")
+        dayvaluechange = gtk.Label("")
+        hbox3.pack_start(label, False, False, 0)
+        #if self.is_portrait():
+        hbox3.pack_start(dayvaluechange, False, False, 25)
+        #else:
+        #    hbox3.pack_start(dayvaluechange, False, False, 45)
 
         if not portfolio:
             vbox1.pack_start(hbox, False, False, 0)
@@ -230,70 +354,92 @@ class StocksPy:
 
         parea.add_with_viewport(vbox1)
 
-        vbox.pack_start(self.titlelbl, False, False, 0)
+        widgets = [win, ltitle, lprice, lchange,  lpercent, lvolume, l52whigh,
+                    l52wlow, lshares, holdingsvalue, dayvaluechange]
+
+        toolbar = self.main_toolbar(True, portfolio, widgets, quote[0], quote[1], False)
+
+        vbox.pack_start(ltitle, False, False, 0)
         vbox.pack_start(gtk.HSeparator(), False, False, 0)
         vbox.pack_start(parea, True, True, 0)
         vbox.pack_start(gtk.HSeparator(), False, False, 5)
         vbox.pack_start(toolbar, False, False, 0)
 
-        self.quotes_win.add(vbox)
-        self.quotes_win.show_all()
-        self.show_data(quote[0], self.quotes_win, shares)
 
-    def get_shares_from_symbol(self):
-        portfolio_data = settings.load_portfolio(settingsdb)
+        win.add(vbox)
+        win.show_all()
+        self.show_data(quote[0], widgets, shares)
+
+    def is_portrait(self):
+        width = gtk.gdk.screen_width()
+        height = gtk.gdk.screen_height()
+        if width > height:
+            return False
+        else:
+            return True
+
+    def get_shares_from_symbol(self, symbol):
         shares = "0"
-        for item in portfolio_data :
-            if self.stocks_id in item:
-                shares = item[2]
-        return shares
+        try:
+            portfolio_data = settings.load_portfolio(settingsdb)
+            for item in portfolio_data :
+                if symbol in item:
+                    shares = item[2]
+            return shares
+        except:
+            logger.exception("Getting shares from symbol: %s" % symbol)
+            return shares
 
-    def add_to_portfolio(self, widget, button):
+    def add_to_portfolio(self, widget, button, symbol, name):
         shares = button.get_value()
-        self.stocks_id
-        self.stocks_name
 
-        portfolio = settings.load_portfolio(settingsdb)
-        index = "None"
-        for item in portfolio:
-            if self.stocks_id in item:
-                index = portfolio.index(item)
+        try:
+            portfolio = settings.load_portfolio(settingsdb)
+            index = "None"
+            for item in portfolio:
+                if symbol in item:
+                    index = portfolio.index(item)
 
-        item = [self.stocks_id, self.stocks_name, shares, '-']
+            item = [symbol, name, shares, '-']
 
-        if index is "None":
-            settings.insert_new_item_to_portfolio(settingsdb, item)
-        else:
-            settings.delete_item_from_portfolio(settingsdb, self.stocks_id)
-            settings.insert_new_item_to_portfolio(settingsdb, item)
+            if index is "None":
+                settings.insert_new_item_to_portfolio(settingsdb, item)
+            else:
+                settings.delete_item_from_portfolio(settingsdb, symbol)
+                settings.insert_new_item_to_portfolio(settingsdb, item)
 
-        self.show_info_banner(widget, "Added to portfolio")
+            self.show_info_banner(widget, "Added to portfolio")
+        except:
+            logger.exception("Adding to portfolio: %s, %s" % (symbol, name))
+            self.show_info_banner(widget, "Error adding to portfolio")
 
 
     def create_selector(self, data, entry):
         if entry:
             selector = hildon.TouchSelectorEntry(text=True)
         else:
-            selector = hildon.hildon_touch_selector_new_text()
+            selector = hildon.TouchSelector(text=True)
         for i in range(len(data)):
             selector.append_text(data[i])
 
         return selector
 
-    def show_data(self, quote, win, shares):
+    def show_data(self, symbol, widgets, shares):
         import thread
-        winprogind(win, 1)
-        thread.start_new_thread(self.get_data, (quote, win, shares))
+        winprogind(widgets[0], 1)
+        thread.start_new_thread(self.get_data, (symbol, widgets, shares))
 
-    def get_data(self, quote, win, shares):
+    def get_data(self, symbol, widgets, shares):
         from ystockquote import ystockquote as yt
+        win, ltitle, lprice, lchange,  lpercent, lvolume, l52whigh, l52wlow, lshares, holdingsvalue, dayvaluechange = widgets
+
         try:
-            data = yt.get_all(quote)
+            data = yt.get_all(symbol)
         except:
-            print 'Failed to get internet data'
+            logger.exception("Getting data from Yahoo: %s" % symbol)
             data = {'price': 'N/A', 'change': 'N/A', 'volume':'N/A',
                     '52_week_high': 'N/A', '52_week_low': 'N/A'}
-            self.titlelbl.set_markup('<b><big>Failed to get data</big></b>')
+            ltitle.set_markup('<b><big>Failed to get data</big></b>')
 
         try:
             ch_percent = \
@@ -302,21 +448,21 @@ class StocksPy:
         except ValueError:
             ch_percent = 0.0
 
-        self.lprice.set_label(data['price'])
-        self.lchange.set_label(data['change'])
-        self.lpercent.set_label('%6.2f %%' % ch_percent)
+        lprice.set_label(data['price'])
+        lchange.set_label(data['change'])
+        lpercent.set_label('%6.2f %%' % ch_percent)
 
         if '-' in data['change']:
             color = gtk.gdk.color_parse("#FF0000")
         else:
             color = gtk.gdk.color_parse("#16EB78")
 
-        self.lpercent.modify_fg(gtk.STATE_NORMAL, color)
-        self.lchange.modify_fg(gtk.STATE_NORMAL, color)
+        lpercent.modify_fg(gtk.STATE_NORMAL, color)
+        lchange.modify_fg(gtk.STATE_NORMAL, color)
 
-        self.lvolume.set_label(data['volume'])
-        self.l52whigh.set_label(data['52_week_high'])
-        self.l52wlow.set_label(data['52_week_low'])
+        lvolume.set_label(data['volume'])
+        l52whigh.set_label(data['52_week_high'])
+        l52wlow.set_label(data['52_week_low'])
 
         try:
             daychange = float(shares)*float(data['change'])
@@ -327,75 +473,83 @@ class StocksPy:
         except ValueError:
             holdvalue = 'N/A'
 
-        self.dayvaluechange.set_label(str(daychange))
-        self.holdingsvalue.set_label(str(holdvalue))
+        dayvaluechange.set_label(str(daychange))
+        holdingsvalue.set_label(str(holdvalue))
 
         winprogind(win, 0)
 
-    def refresh_stock_data(self, widget, portfolio):
+    def refresh_stock_data(self, widget, portfolio, widgets, symbol):
         if portfolio:
-            shares = self.get_shares_from_symbol()
+            shares = self.get_shares_from_symbol(symbol)
         else:
             shares = "0"
 
-        self.show_data(self.stocks_id, self.quotes_win, shares)
+        self.show_data(symbol, widgets, shares)
 
-    def show_graph_view(self, widget):
+    def show_graph_view(self, widget, symbol, name):
         win = hildon.StackableWindow()
+        self.create_menu(win)
+        win.set_title(name)
 
         vbox = gtk.VBox()
-        toolbar = self.main_toolbar(False, True)
+        toolbar = self.main_toolbar(False, True, None, '', '', False)
 
-        self.graphs_title = gtk.Label(self.stocks_name)
+        self.graphs_title = gtk.Label(name)
         color = gtk.gdk.color_parse("#03A5FF")
         self.graphs_title.modify_fg(gtk.STATE_NORMAL, color)
 
         parea = hildon.PannableArea()
+        parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
+
+        vbox1 = gtk.VBox()
 
         hbox = gtk.HBox()
         hbox.set_homogeneous(True)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('1d')
-        button.connect("clicked", self.show_graph, '1d', win)
+        button.connect("clicked", self.show_graph, '1d', win, symbol)
         hbox.pack_start(button)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('5d')
-        button.connect("clicked", self.show_graph, '5d', win)
+        button.connect("clicked", self.show_graph, '5d', win, symbol)
         hbox.pack_start(button)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('3m')
-        button.connect("clicked", self.show_graph, '3m', win)
+        button.connect("clicked", self.show_graph, '3m', win, symbol)
         hbox.pack_start(button)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('6m')
-        button.connect("clicked", self.show_graph, '6m', win)
+        button.connect("clicked", self.show_graph, '6m', win, symbol)
         hbox.pack_start(button)
 
+        vbox1.pack_start(hbox, False, False, 0)
+        hbox = gtk.HBox()
+        hbox.set_homogeneous(True)
+
         button = hildon.Button(fhsize, horbtn)
         button.set_label('1y')
-        button.connect("clicked", self.show_graph, '1y', win)
+        button.connect("clicked", self.show_graph, '1y', win, symbol)
         hbox.pack_start(button)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('2y')
-        button.connect("clicked", self.show_graph, '2y', win)
+        button.connect("clicked", self.show_graph, '2y', win, symbol)
         hbox.pack_start(button)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('5y')
-        button.connect("clicked", self.show_graph, '5y', win)
+        button.connect("clicked", self.show_graph, '5y', win, symbol)
         hbox.pack_start(button)
 
         button = hildon.Button(fhsize, horbtn)
         button.set_label('Max')
-        button.connect("clicked", self.show_graph, 'max', win)
+        button.connect("clicked", self.show_graph, 'max', win, symbol)
         hbox.pack_start(button)
 
-        vbox1 = gtk.VBox()
         vbox1.pack_start(hbox, False, False, 0)
 
         self.graph = gtk.Image()
@@ -412,31 +566,30 @@ class StocksPy:
         win.add(vbox)
         win.show_all()
 
-        self.show_graph(None, '1d', win)
+        self.show_graph(None, '1d', win, symbol)
 
-    def show_graph(self, widget, option, win):
+    def show_graph(self, widget, option, win, symbol):
         import thread
         winprogind(win, 1)
-        thread.start_new_thread(self.get_graph_data, (option, win))
+        thread.start_new_thread(self.get_graph_data, (option, win, symbol))
 
-    def get_graph_data(self, option, win):
-        kind = self.stocks_id
+    def get_graph_data(self, option, win, symbol):
         if option == '1d':
-            url = 'http://uk.ichart.yahoo.com/b?s=%s' % kind
+            url = 'http://uk.ichart.yahoo.com/b?s=%s' % symbol
         elif option == '5d':
-            url = 'http://uk.ichart.yahoo.com/w?s=%s' % kind
+            url = 'http://uk.ichart.yahoo.com/w?s=%s' % symbol
         elif option == '3m':
-            url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % kind.lower()
+            url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % symbol.lower()
         elif option == '6m':
-            url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % kind.lower()
+            url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % symbol.lower()
         elif option == '1y':
-            url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % kind.lower()
+            url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % symbol.lower()
         elif option == '2y':
-            url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % kind.lower()
+            url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % symbol.lower()
         elif option == '5y':
-            url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % kind.lower()
+            url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % symbol.lower()
         elif option == 'max':
-            url = 'http://chart.finance.yahoo.com/c/my/s/%s' % kind.lower()
+            url = 'http://chart.finance.yahoo.com/c/my/s/%s' % symbol.lower()
 
         try:
             myimg = urllib2.urlopen(url)
@@ -446,14 +599,198 @@ class StocksPy:
             pbl.write(imgdata)
 
             pbuf = pbl.get_pixbuf()
+            pbuf = pbuf.scale_simple(475, 235, gtk.gdk.INTERP_TILES)
             pbl.close()
             self.graph.set_from_pixbuf(pbuf)
             winprogind(win, 0)
         except:
+            logger.exception("Getting graph data: %s" % url)
             winprogind(win, 0)
             self.graphs_title.set_label('Failed to get data')
             self.graph.destroy()
 
+    def show_metal_graphs(self, widget, path, column, model):
+        metal = model[path][1]
+        metal_urls = self.get_metal_graphs_urls(metal)
+        options =  []
+        for i in metal_urls:
+            options.append(i[0])
+
+        win = hildon.StackableWindow()
+        #self.create_menu(win)
+        win.set_title(metal)
+
+        vbox = gtk.VBox()
+        #toolbar = self.main_toolbar(False, True, None, '', '', False)
+
+        self.metalgraph_title = gtk.Label(metal)
+        color = gtk.gdk.color_parse("#03A5FF")
+        self.metalgraph_title.modify_fg(gtk.STATE_NORMAL, color)
+
+        parea = hildon.PannableArea()
+        parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
+
+        vbox1 = gtk.VBox()
+
+        hbox = gtk.HBox()
+        hbox.set_homogeneous(True)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('live')
+        button.connect("clicked", self.show_metalgraph, 'live', win, metal_urls)
+        if 'live' in options:
+            hbox.pack_start(button)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('30d')
+        button.connect("clicked", self.show_metalgraph, '30d', win, metal_urls)
+        if '30d' in options:
+            hbox.pack_start(button)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('60d')
+        button.connect("clicked", self.show_metalgraph, '60d', win, metal_urls)
+        if '60d' in options:
+            hbox.pack_start(button)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('6m')
+        button.connect("clicked", self.show_metalgraph, '6m', win, metal_urls)
+        if '6m' in options:
+            hbox.pack_start(button)
+
+        vbox1.pack_start(hbox, False, False, 0)
+        hbox = gtk.HBox()
+        hbox.set_homogeneous(True)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('1y')
+        button.connect("clicked", self.show_metalgraph, '1y', win, metal_urls)
+        if '1y' in options:
+            hbox.pack_start(button)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('5y')
+        button.connect("clicked", self.show_metalgraph, '5y', win, metal_urls)
+        if '5y' in options:
+            hbox.pack_start(button)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label('10y')
+        button.connect("clicked", self.show_metalgraph, '10y', win, metal_urls)
+        if '10y' in options:
+            hbox.pack_start(button)
+
+        vbox1.pack_start(hbox, False, False, 0)
+
+        self.metalgraph = gtk.Image()
+        vbox1.pack_start(self.metalgraph, True, True, 0)
+
+        parea.add_with_viewport(vbox1)
+
+        vbox.pack_start(self.metalgraph_title, False, False, 0)
+        vbox.pack_start(gtk.HSeparator(), False, False, 0)
+        vbox.pack_start(parea, True, True, 0)
+        vbox.pack_start(gtk.HSeparator(), False, False, 5)
+        #vbox.pack_start(toolbar, False, False, 0)
+
+        win.add(vbox)
+        win.show_all()
+
+        self.show_metalgraph(None, options[0], win, metal_urls)
+
+    def show_metalgraph(self, widget, option, win, metal_urls):
+        import thread
+        winprogind(win, 1)
+        thread.start_new_thread(self.get_metalgraph_data, (option, win, metal_urls))
+
+    def get_metalgraph_data(self, option, win, metal_urls):
+        for i in metal_urls:
+            if i[0] == option:
+                print i[1]
+                url = i[1]
+
+        try:
+            myimg = urllib2.urlopen(url)
+            imgdata = myimg.read()
+
+            pbl = gtk.gdk.PixbufLoader()
+            pbl.write(imgdata)
+
+
+            pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 475, 235)
+            pixbuf.fill(0xffffffff)
+
+
+            pbuf = pbl.get_pixbuf()
+            pbuf = pbuf.scale_simple(475, 235, gtk.gdk.INTERP_TILES)
+
+            pixbuf.composite(pbuf, 0, 0, pixbuf.props.width, pixbuf.props.height, 0, 0, 1.0, 1.0, gtk.gdk.INTERP_HYPER, 55)
+
+
+            pbl.close()
+            self.metalgraph.set_from_pixbuf(pbuf)
+            winprogind(win, 0)
+        except:
+            logger.exception("Getting graph data: %s" % url)
+            winprogind(win, 0)
+            self.metalgraph_title.set_label('Failed to get data')
+            self.metalgraph.destroy()
+
+    def get_metal_graphs_urls(self, metal):
+        metal_urls = []
+        liveurl = 'http://www.kitco.com/images/live/%s'
+        graphurl = 'http://www.kitco.com/LFgif/%s'
+        t24url = 'http://www.kitconet.com/charts/metals/base/%s'
+        if metal == 'Gold':
+            metal_urls.append(['live', liveurl % 'gold.gif'])
+            metal_urls.append(['30d', graphurl % 'au0030lnb.gif'])
+            metal_urls.append(['60d', graphurl % 'au0060lnb.gif'])
+            metal_urls.append(['6m', graphurl % 'au0182nyb.gif'])
+            metal_urls.append(['1y', graphurl % 'au0365nyb.gif'])
+            metal_urls.append(['5y', graphurl % 'au1825nyb.gif'])
+            metal_urls.append(['10y', graphurl % 'au3650nyb.gif'])
+        elif metal == 'Silver':
+            metal_urls.append(['live', liveurl % 'silver.gif'])
+            metal_urls.append(['30d', graphurl % 'ag0030lnb.gif'])
+            metal_urls.append(['60d', graphurl % 'ag0060lnb.gif'])
+            metal_urls.append(['6m', graphurl % 'ag0182nyb.gif'])
+            metal_urls.append(['1y', graphurl % 'ag0365nyb.gif'])
+            metal_urls.append(['5y', graphurl % 'ag1825nyb.gif'])
+            metal_urls.append(['10y', graphurl % 'ag3650nyb.gif'])
+        elif metal == 'Platinum':
+            metal_urls.append(['live', liveurl % 'plati.gif'])
+            metal_urls.append(['30d', graphurl % 'pt0030lnb.gif'])
+            metal_urls.append(['60d', graphurl % 'pt0060lnb.gif'])
+            metal_urls.append(['6m', graphurl % 'pt0182nyb.gif'])
+            metal_urls.append(['1y', graphurl % 'pt0365nyb.gif'])
+            metal_urls.append(['5y', graphurl % 'pt1825nyb.gif'])
+        elif metal == 'Palladium':
+            metal_urls.append(['live', liveurl % 'plad.gif'])
+            metal_urls.append(['30d', graphurl % 'pd0030lnb.gif'])
+            metal_urls.append(['60d', graphurl % 'pd0060lnb.gif'])
+            metal_urls.append(['6m', graphurl % 'pd0182nyb.gif'])
+            metal_urls.append(['1y', graphurl % 'pd0365nyb.gif'])
+            metal_urls.append(['5y', graphurl % 'pd1825nyb.gif'])
+        elif metal == 'Rhodium':
+            metal_urls.append(['30d', graphurl % 'rh0030lnb.gif'])
+            metal_urls.append(['60d', graphurl % 'rh0060lnb.gif'])
+            metal_urls.append(['6m', graphurl % 'rh0182lnb.gif'])
+            metal_urls.append(['1y', graphurl % 'rh0365lnb.gif'])
+            metal_urls.append(['5y', graphurl % 'rh1825lnb.gif'])
+        elif metal == 'Copper':
+            metal_urls.append(['live', t24url % 't24_cp450x275.gif'])
+        elif metal == 'Nickel':
+            metal_urls.append(['live', t24url % 't24_nk450x275.gif'])
+        elif metal == 'Aluminium':
+            metal_urls.append(['live', t24url % 't24_al450x275.gif'])
+        elif metal == 'Zinc':
+            metal_urls.append(['live', t24url % 't24_zc450x275.gif'])
+        elif metal == 'Lead':
+            metal_urls.append(['live', t24url % 't24_ld450x275.gif'])
+
+        return metal_urls
+
     def _tv_columns(self, treeview):
         column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
         column.set_visible(False)
@@ -469,9 +806,9 @@ class StocksPy:
             lstore.set(iter, 0, ids[item], 1, names[item])
         return lstore
 
-    def main_toolbar(self, quotesview, portfolio):
+    def main_toolbar(self, quotesview, portfolio, widgets, symbol, name, initial):
         toolbar = gtk.HBox()
-        toolbar.set_homogeneous(True)
+        #toolbar.set_homogeneous(True)
 
         portfolio_btn = hildon.Button(fhsize, horbtn)
         portfolio_btn.set_title("Portfolio")
@@ -479,34 +816,173 @@ class StocksPy:
 
         graph_btn = hildon.Button(fhsize, horbtn)
         graph_btn.set_title("Graph")
-        graph_btn.connect("clicked", self.show_graph_view)
+        graph_btn.connect("clicked", self.show_graph_view, symbol, name)
 
         refresh_btn = hildon.Button(fhsize, horbtn)
         refresh_btn.set_title("Refresh")
-        refresh_btn.connect("clicked", self.refresh_stock_data, portfolio)
+        refresh_btn.connect("clicked", self.refresh_stock_data, portfolio,
+                            widgets, symbol)
+
+
+        info_btn = hildon.Button(fhsize, horbtn)
+        img = gtk.image_new_from_icon_name("general_information", gtk.ICON_SIZE_SMALL_TOOLBAR)
+        info_btn.set_image(img)
+        info_btn.connect("clicked", self.show_app_information)
+
+        search_btn = hildon.Button(fhsize, horbtn)
+        img = gtk.image_new_from_icon_name("general_search", gtk.ICON_SIZE_SMALL_TOOLBAR)
+        search_btn.set_image(img)
+        search_btn.connect("clicked", self.show_search_dialog)
 
         if not portfolio:
             toolbar.pack_start(portfolio_btn)
+            if not quotesview:
+                toolbar.pack_start(info_btn, False, False, 0)
         if quotesview:
             toolbar.pack_start(graph_btn)
             toolbar.pack_start(refresh_btn)
 
+        if initial:
+            toolbar.pack_start(search_btn, False, False, 0)
+
         toolbar.show_all()
 
         return toolbar
 
+    def show_search_dialog(self, widget):
+        dlg = gtk.Dialog(title='Search company', parent=None, flags=0)
+        dlg.set_has_separator(False)
+
+        hbox = gtk.HBox()
+
+        entry = hildon.Entry(fhsize)
+        entry.connect("activate", self.do_search, entry, dlg)
+        hbox.pack_start(entry, True, True, 0)
+
+        button = hildon.Button(fhsize, horbtn)
+        img = gtk.image_new_from_icon_name("general_search", gtk.ICON_SIZE_SMALL_TOOLBAR)
+        button.set_image(img)
+        button.connect("clicked", self.do_search, entry, dlg)
+        hbox.pack_start(button, False, False, 0)
+
+        dlg.vbox.pack_start(hbox, False, False, 0)
+
+        dlg.show_all()
+        dlg.run()
+        dlg.destroy()
+
+
+    def do_search(self, widget, entry, dlg):
+        import thread
+        text = entry.get_text()
+        dlg.destroy()
+
+        winprogind(self.window, 1)
+        thread.start_new_thread(self._really_do_search, (text,))
+
+    def _really_do_search(self, text):
+
+        if allnames == []:
+            for market in marketdata.eunames:
+                for company in market:
+                    allnames.append(company)
+
+            for market in marketdata.omnames:
+                for company in market:
+                    allnames.append(company)
+
+            for market in marketdata.usnames:
+                for company in market:
+                    allnames.append(company)
+
+        if allsymbols == []:
+            for market in marketdata.eusymbols:
+                for company in market:
+                    allsymbols.append(company)
+
+            for market in marketdata.omsymbols:
+                for company in market:
+                    allsymbols.append(company)
+
+            for market in marketdata.ussymbols:
+                for company in market:
+                    allsymbols.append(company)
+
+        new_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
+        for i in range(len(allnames)):
+            if text.lower() in allnames[i].lower():
+                niter = new_model.append()
+                #print allsymbols[i], allnames[i]
+                #FIXME: repeated companies in the results list
+                #print if allnames[i]
+                #for j in new_model:
+                #    if j[1] == allnames[i]:
+                new_model.set(niter, 0, allsymbols[i], 1, allnames[i])
+
+        if len(new_model) == 0:
+            winprogind(self.window, 0)
+            gtk.gdk.threads_enter()
+            self.show_info_banner(self.window, "No items found for this search")
+            gtk.gdk.threads_leave()
+            return
+
+        gtk.gdk.threads_enter()
+        self.show_search_screen(new_model, text)
+        gtk.gdk.threads_leave()
+        winprogind(self.window, 0)
+
+
+    def show_search_screen(self, model, text):
+        window = hildon.StackableWindow()
+        self.create_menu(window)
+        window.set_title("Search for " + text)
+
+        vbox = gtk.VBox()
+        toolbar = self.main_toolbar(False, False, None, '', '', False)
+
+        parea = hildon.PannableArea()
+        parea.connect("horizontal-movement", self.horizontal_mov)
+        tv = hildon.GtkTreeView(ui_normal)
+        tv.connect("row-activated", self.show_quotes_view, model, False)
+        tv.set_model(model)
+        self._tv_columns(tv)
+        parea.add(tv)
+
+        vbox.pack_start(parea, True, True, 0)
+        vbox.pack_start(gtk.HSeparator(), False, False, 5)
+        vbox.pack_start(toolbar, False, False, 0)
+
+        window.add(vbox)
+        window.show_all()
+
+    def show_app_information(self, widget):
+        self.show_information_note(self.window, (
+        "The data is got from Yahoo! Finance.\n"
+        "It could be delayed or even wrong.\n"
+        "The author doesn't validate in any way this data and therefore he is not responsible for any damage that may occur.\n\n"
+        "You can scroll large list with gestures:\n"
+        "Left-to-right gesture: scroll down.\n"
+        "Right-to-left gesture: scroll up."))
+
     def show_portfolio_view(self, widget):
         data = settings.load_portfolio(settingsdb)
+        for item in data:
+            item.append('-')
+            item.append('-')
 
         win = hildon.StackableWindow()
+        self.create_menu(win)
+        win.set_title("Portfolio")
 
         vbox = gtk.VBox()
 
         parea = hildon.PannableArea()
+        parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
         tv = hildon.GtkTreeView(ui_normal)
         tv.set_headers_visible(True)
         self.portfolio_model = self._create_portfolio_model(data)
-        tv.connect("row-activated", self.show_quotes_view, self.portfolio_model, True)
+        self.quotes_id = tv.connect("row-activated", self.show_quotes_view,
+                                    self.portfolio_model, True)
         tv.set_model(self.portfolio_model)
         self._tv_portfolio_columns(tv)
         parea.add(tv)
@@ -518,15 +994,120 @@ class StocksPy:
         hbox.pack_start(button, True, True, 0)
 
         button = hildon.Button(fhsize, horbtn)
+        button.set_title("Add manually")
+        button.connect("clicked", self.add_item_dlg, None, None)
+        hbox.pack_start(button, True, True, 0)
+
+        button = hildon.Button(fhsize, horbtn)
         button.set_title("Remove")
         button.connect("clicked", self.remove_item)
         hbox.pack_start(button, True, True, 0)
 
+        button = hildon.CheckButton(fhsize)
+        button.set_label("Edit")
+
+        button.connect("toggled", self.edit_toggled, tv)
+        hbox.pack_start(button, True, True, 0)
+
+
         vbox.pack_start(parea, True, True, 0)
         vbox.pack_start(hbox, False, False, 0)
         win.add(vbox)
         win.show_all()
 
+    def edit_toggled(self, widget, tv):
+        if widget.get_active():
+            tv.disconnect(self.quotes_id)
+            self.edit_id = tv.connect("row-activated",
+                            self.edit_portfolio_item, self.portfolio_model)
+        else:
+            tv.disconnect(self.edit_id)
+            self.quotes_id = tv.connect("row-activated",
+                            self.show_quotes_view, self.portfolio_model, True)
+
+    def edit_portfolio_item(self, widget, path, column, model):
+        seliter = model.get_iter(path)
+
+        symbol = model[path][0]
+        name = model[path][1]
+        shares = model[path][2]
+
+        data = [shares, symbol, name]
+        self.add_item_dlg(widget, data, seliter)
+
+    def add_item_dlg(self, widget, data, seliter):
+        if data:
+            shares = data[0]
+            symbol = data[1]
+            name = data[2]
+            title = "Edit item from portfolio"
+            btntext = "Edit"
+            edit = True
+        else:
+            shares = "0"
+            symbol = ""
+            name = ""
+            title = "Add to portfolio"
+            btntext = "Add"
+            edit = False
+
+        dlg = gtk.Dialog(title=title, parent=None, flags=0)
+        dlg.set_has_separator(False)
+
+        button1 = hildon.PickerButton(fhsize, horbtn)
+        data = ["50", "100", "200", "300", "400", "500", "600", "700", "800",
+                "900", "1000"]
+        selector = self.create_selector(data, True)
+        button1.set_selector(selector)
+        button1.set_title("Your shares")
+        button1.set_value(shares)
+        dlg.vbox.pack_start(button1, False, False, 0)
+
+        entry1 = hildon.Entry(fhsize)
+        entry1.set_text(name)
+        entry2 = hildon.Entry(fhsize)
+        entry2.set_text(symbol)
+
+        entry1.set_placeholder("Name")
+        entry1.connect("activate", self.add_item, dlg, button1, entry1,
+                        entry2, edit, seliter)
+        dlg.vbox.pack_start(entry1, False, False, 0)
+
+        entry2.connect("activate", self.add_item, dlg, button1, entry1,
+                        entry2, edit, seliter)
+        entry2.set_placeholder("Yahoo Finance symbol")
+        dlg.vbox.pack_start(entry2, False, False, 0)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_label(btntext)
+        button.connect("clicked", self.add_item, dlg, button1, entry1,
+                        entry2, edit, seliter)
+        dlg.vbox.pack_start(button, False, False, 0)
+
+        dlg.show_all()
+        dlg.run()
+        dlg.destroy()
+
+    def add_item(self, widget, dlg, button, entry1, entry2, edit, seliter):
+        symbol = entry2.get_text()
+        name = entry1.get_text()
+        shares = button.get_value()
+
+        if name == '' or symbol == '':
+            self.show_info_banner(widget, "Must add the name and symbol")
+            return
+
+        self.add_to_portfolio(widget, button, symbol, name)
+        dlg.destroy()
+
+        if edit:
+            self.portfolio_model.set(seliter, 0, symbol, 1, name, 2, shares,
+                                       3, "-", 4, "-", 5, "-", 6, "green")
+        else:
+            niter = self.portfolio_model.append()
+            self.portfolio_model.set(niter, 0, symbol, 1, name, 2, shares,
+                                    3, "-", 4, "-", 5, "-", 6, "green")
+
     def remove_item(self, widget):
         win = hildon.StackableWindow()
         win.fullscreen()
@@ -557,12 +1138,16 @@ class StocksPy:
         conf = self.show_confirmation(win, "Delete items?")
 
         if conf:
-            selmodel, selected = selection.get_selected_rows()
-            iters = [selmodel.get_iter(path) for path in selected]
-            for i in iters:
-                symbol = selmodel.get_value(i, 0)
-                settings.delete_item_from_portfolio(settingsdb, symbol)
-                selmodel.remove(i)
+            try:
+                selmodel, selected = selection.get_selected_rows()
+                iters = [selmodel.get_iter(path) for path in selected]
+                for i in iters:
+                    symbol = selmodel.get_value(i, 0)
+                    settings.delete_item_from_portfolio(settingsdb, symbol)
+                    selmodel.remove(i)
+            except:
+                logger.exception("Deleting item from portfolio")
+                self.info_banner(widget, "Error deleting item")
 
     def _tv_remove_portfolio_columns(self, treeview):
         column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
@@ -583,30 +1168,54 @@ class StocksPy:
 
     def refresh_portfolio(self, widget, tv, win):
         data = settings.load_portfolio(settingsdb)
+        for item in data:
+            item.append('-')
+            item.append('-')
         import thread
         winprogind(win, 1)
         thread.start_new_thread(self._do_refresh_portfolio, (data, tv, win))
 
     def _do_refresh_portfolio(self, data, tv, win):
+        print data
         for item in data:
-            item[3] = self.get_price(item[0])
+            item[3], item[4] = self.get_portfolio_data(item[0])
+            try:
+                ch_percent = \
+                        100.0 * float(item[4])/(float(item[3]) - \
+                        float(item[4]))
+            except ValueError:
+                ch_percent = 0.0
+
+            item[5] = '%6.2f %%' % ch_percent
 
+
+        print data
         self.portfolio_model = self._create_portfolio_model(data)
         tv.set_model(self.portfolio_model)
         winprogind(win, 0)
 
-    def get_price(self, symbol):
+    def get_portfolio_data(self, symbol):
         from ystockquote import ystockquote as yt
-        price = yt.get_price(symbol)
-
-        return price
+        try:
+            data = yt.get_all(symbol)
+            return data['price'], data['change']
+        except:
+            logger.exception("Getting price from Yahoo: %s" % symbol)
+            return "-", "-"
 
     def _create_portfolio_model(self, data):
         lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
-                                gobject.TYPE_STRING, gobject.TYPE_STRING)
+                                gobject.TYPE_STRING, gobject.TYPE_STRING,
+                                gobject.TYPE_STRING, gobject.TYPE_STRING,
+                                gobject.TYPE_STRING)
         for item in data:
             iter = lstore.append()
-            lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3])
+            if '+' in item[4]:
+                color = 'green'
+            else:
+                color = 'red'
+            lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3],
+                        4, item[4], 5, item[5], 6, color)
         return lstore
 
     def _tv_portfolio_columns(self, treeview):
@@ -624,6 +1233,15 @@ class StocksPy:
         column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
         treeview.append_column(column)
 
+        column = gtk.TreeViewColumn('Change', gtk.CellRendererText(), text=4)
+        treeview.append_column(column)
+
+
+        renderer = gtk.CellRendererText()
+        renderer.set_property("foreground-set", True)
+        column = gtk.TreeViewColumn('%', renderer, text=5, foreground=6)
+        treeview.append_column(column)
+
     def show_confirmation(self, window, msg):
         dialog = hildon.hildon_note_new_confirmation(window, msg)
         dialog.show_all()
@@ -635,6 +1253,12 @@ class StocksPy:
         dialog.destroy()
         return False
 
+    def show_information_note(self, window, msg):
+        dialog = hildon.hildon_note_new_information(window, msg)
+        dialog.show_all()
+        result = dialog.run()
+        dialog.destroy()
+
     def show_info_banner(self, widget, msg):
         hildon.hildon_banner_show_information(widget, 'qgn_note_infoprint', msg)
 
@@ -646,20 +1270,375 @@ class StocksPy:
         else:
             return True
 
-    def on_about(self, widget):
-        dialog = gtk.AboutDialog()
-        dialog.set_name("StockThis")
-        dialog.set_version("0.3")
-        dialog.set_copyright("Copyright © 2009")
-        dialog.set_website("http://stockthis.garage.maemo.org")
-        dialog.set_authors(["Daniel Martin Yerga <dyerga@gmail.com>"])
-        logo = gtk.gdk.pixbuf_new_from_file(imgdir + "stockthis.png")
-        dialog.set_logo(logo)
-        dialog.set_license("This program is released under the GNU\nGeneral Public License. Please visit \nhttp://www.gnu.org/copyleft/gpl.html\nfor details.")
-        dialog.set_artists(["Logo by Daniel Martin Yerga"])
+class About:
+
+    def __init__(self, widget):
+        self.abdialog = gtk.Dialog()
+        self.abdialog.set_title("About StockThis")
+
+        notebook = gtk.Notebook()
+        notebook.set_show_tabs(False)
+        notebook.set_scrollable(False)
+        notebook.set_show_border(False)
+
+        # Description page #
+        vbox = gtk.VBox()
+
+        label = gtk.Label()
+        label.set_markup("<b><big>StockThis %s</big></b>" % VERSION)
+        vbox.pack_start(label, True, True, 0)
+
+        label = gtk.Label("Stocks application with big database")
+        vbox.pack_start(label, True, True, 0)
+
+        label = gtk.Label("GNU General Public License")
+        vbox.pack_start(label, True, True, 0)
+
+        url = "http://stockthis.garage.maemo.org"
+        webbtn = gtk.LinkButton(url, "Web")
+        vbox.pack_start(webbtn, True, True, 0)
+        gtk.link_button_set_uri_hook(self.launch_browser)
+
+        notebook.append_page(vbox, gtk.Label())
+
+        # Credits page #
+        vbox = gtk.VBox()
+        textview = hildon.TextView()
+        textview.set_cursor_visible(False)
+        textview.set_wrap_mode(gtk.WRAP_WORD)
+        text = "Written by Daniel Martin Yerga (dyerga@gmail.com)"
+        textview.get_buffer().set_text(text)
+
+        parea = hildon.PannableArea()
+        parea.add(textview)
+
+        vbox.pack_start(parea, True, True, 0)
+        notebook.append_page(vbox, gtk.Label())
+
+
+        # Donate page #
+        vbox = gtk.VBox()
+
+        textview = hildon.TextView()
+        textview.set_cursor_visible(False)
+        textview.set_wrap_mode(gtk.WRAP_WORD)
+        text = """StockThis is a free software application.
+Developing good software takes time and hard work.
+StockThis's author develops the program in his spare time.
+If you like the program and it's helpful, consider donating a small amount of money.
+Donations are a great incentive and help the developer feel that the hard work is appreciated.
+"""
+        textview.get_buffer().set_text(text)
+
+        parea = hildon.PannableArea()
+        parea.add(textview)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_title("Make donation")
+        url = "http://stockthis.garage.maemo.org/donate.html"
+        button.connect("clicked", self.launch_browser, url)
+        vbox.pack_start(button, False, False, 0)
+        vbox.pack_start(parea, True, True, 0)
+
+        notebook.append_page(vbox, gtk.Label())
+
+        # Report page #
+        vbox = gtk.VBox()
+
+        textview = hildon.TextView()
+        textview.set_cursor_visible(False)
+        textview.set_wrap_mode(gtk.WRAP_WORD)
+        text = """StockThis is being improved thanks to bug reports that users have submitted. The author appreciates these reports.
+If the application is having an error when you're using it, you have two choices to report this error:
+1) Send the log from the button above (if there's an error in the log).
+2) Press the button and read how to report a bug."""
+        textview.get_buffer().set_text(text)
+
+        parea = hildon.PannableArea()
+        parea.add(textview)
+
+        hbox = gtk.HBox()
+        hbox.set_homogeneous(True)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_title("Report error")
+        url = "http://stockthis.garage.maemo.org/reporting.html"
+        button.connect("clicked", self.launch_browser, url)
+        hbox.pack_start(button, True, True, 0)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_title("Log")
+        button.connect("clicked", self.on_show_log)
+        hbox.pack_start(button, True, True, 0)
+
+        vbox.pack_start(hbox, False, False, 0)
+        vbox.pack_start(parea, True, True, 0)
+
+        notebook.append_page(vbox, gtk.Label())
+
+        # Rate page #
+        vbox = gtk.VBox()
+
+        textview = hildon.TextView()
+        textview.set_cursor_visible(False)
+        textview.set_wrap_mode(gtk.WRAP_WORD)
+        text = """The downloads section in maemo.org has a nice system where you can rate applications.
+If you consider StockThis a good application (or a bad one too), you could rate it in maemo.org site."""
+        textview.get_buffer().set_text(text)
+
+        button = hildon.Button(fhsize, horbtn)
+        button.set_title("Rate StockThis")
+        url = "http://maemo.org/downloads/product/Maemo5/stockthis"
+        button.connect("clicked", self.launch_browser, url)
+        image = gtk.Image()
+        image.set_from_file(imgdir + "maemoorg.png")
+        vbox.pack_start(button, False, False, 0)
+        vbox.pack_start(image, False, False, 5)
+        vbox.pack_start(textview, True, True, 0)
+
+        notebook.append_page(vbox, gtk.Label())
+
+        # Buttons #
+        self.abdialog.vbox.pack_start(notebook, True, True, 0)
+
+        hbox = gtk.HBox()
+
+        descbutton = hildon.GtkRadioButton(fhsize)
+        descbutton.set_mode(False)
+        descbutton.set_active(True)
+        descbutton.set_label('Description')
+        descbutton.connect("toggled", self.change_tab, notebook, 0)
+        hbox.pack_start(descbutton, True, True, 0)
+
+        button = hildon.GtkRadioButton(fhsize)
+        button.set_mode(False)
+        button.set_active(True)
+        button.set_label('Credits')
+        button.set_group(descbutton)
+        button.connect("toggled", self.change_tab, notebook, 1)
+        hbox.pack_start(button, True, True, 0)
+
+        button = hildon.GtkRadioButton(fhsize)
+        button.set_mode(False)
+        button.set_label('Donate')
+        button.set_group(descbutton)
+        button.connect("clicked", self.change_tab, notebook, 2)
+        hbox.pack_start(button, True, True, 0)
+
+        button = hildon.GtkRadioButton(fhsize)
+        button.set_mode(False)
+        button.set_label('Report')
+        button.set_group(descbutton)
+        button.connect("clicked", self.change_tab, notebook, 3)
+        hbox.pack_start(button, True, True, 0)
+
+        button = hildon.GtkRadioButton(fhsize)
+        button.set_mode(False)
+        button.set_label('Rate')
+        button.set_group(descbutton)
+        button.connect("clicked", self.change_tab, notebook, 4)
+        hbox.pack_start(button, True, True, 0)
+
+        self.abdialog.vbox.pack_start(hbox, False, False, 0)
+
+        self.abdialog.show_all()
+        self.abdialog.run()
+        self.abdialog.destroy()
+
+    def change_tab(self, widget, notebook, number):
+        notebook.set_current_page(number)
+
+    def launch_browser(self, widget, url):
+        import dbus
+        bus = dbus.SystemBus()
+        proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
+        iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
+
+        self.abdialog.destroy()
+
+        iface.open_new_window(url)
+
+    def on_show_log(self, widget):
+        Log(widget, logfile)
+
+
+class Log:
+
+    def __init__(self, widget, logfile):
+        #Log dialog UI
+        dialog = gtk.Dialog(title='Log', parent=None)
+
+        dialog.set_size_request(600, 350)
+
+        parea = hildon.PannableArea()
+        parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
+
+        textview = hildon.TextView()
+        textview.set_property("editable", False)
+        textview.set_property("wrap-mode", gtk.WRAP_WORD)
+
+        log = open(logfile, 'r')
+        logtext = log.read()
+        log.close()
+
+        textview.get_buffer().set_text(logtext)
+        parea.add(textview)
+
+        dialog.vbox.pack_start(parea, True, True, 0)
+
+        hbox = gtk.HBox()
+
+        save_btn = hildon.Button(fhsize, horbtn)
+        save_btn.set_title("Save")
+        save_btn.connect('clicked', self.save, logfile, dialog)
+
+        clear_btn = hildon.Button(fhsize, horbtn)
+        clear_btn.set_title("Clear")
+        clear_btn.connect('clicked', self.clear, textview, logfile)
+
+        send_btn = hildon.Button(fhsize, horbtn)
+        send_btn.set_title('Send')
+        send_btn.connect('clicked', self.send, dialog, logfile)
+
+        hbox.pack_start(save_btn, True, True, 0)
+        hbox.pack_start(clear_btn, True, True, 0)
+        hbox.pack_start(send_btn, True, True, 0)
+
+        dialog.vbox.pack_start(hbox, False, False, 0)
+
+        dialog.show_all()
         dialog.run()
         dialog.destroy()
 
+    def show_filechooser(self, window, title, name, EXT):
+        action = gtk.FILE_CHOOSER_ACTION_SAVE
+
+        m = hildon.FileSystemModel()
+        file_dialog = hildon.FileChooserDialog(window, action, m)
+        file_dialog.set_title(title)
+
+        file_dialog.set_current_name(name)
+        HOME = os.path.expanduser("~")
+
+        if os.path.exists(HOME + '/MyDocs/.documents'):
+            file_dialog.set_current_folder(HOME + '/MyDocs/.documents')
+        else:
+            file_dialog.set_current_folder(HOME)
+
+        file_dialog.set_default_response(gtk.RESPONSE_CANCEL)
+
+        result = file_dialog.run()
+        if result == gtk.RESPONSE_OK:
+            namefile = file_dialog.get_filename()
+            namefile, extension = os.path.splitext(namefile)
+            namefile = namefile + "." + EXT
+        else:
+            namefile = None
+        file_dialog.destroy()
+
+        return namefile
+
+
+    def clear(self, widget, textview, logfile):
+        textview.get_buffer().set_text('')
+        f = open(logfile, 'w')
+        f.close()
+
+    def save(self, widget, logfile, dlg):
+        import shutil
+        filename = self.show_filechooser(dlg, "Save log file",
+                    "stockthis-log", "txt")
+
+        if not filename:
+            return
+
+        try:
+            shutil.copyfile(logfile, filename)
+            stockspy.show_info_banner(widget, 'Log file saved')
+        except:
+            logger.exception("Saving log file")
+            stockspy.show_info_banner(widget, 'Error saving the log file')
+
+    def send(self, widget, dlg, logfile):
+        sendtxt = ("You are going to send the log to the developers.\n"
+        "This helps the developers to track problems with the application.\n"
+        "It doesn't send any personal information (like passwords or similar).")
+
+        dialog = hildon.hildon_note_new_confirmation(dlg, sendtxt)
+        dialog.set_button_texts("Send", "Cancel")
+        dialog.show_all()
+        response = dialog.run()
+        if response == gtk.RESPONSE_OK:
+            self.do_pre_send(dlg, logfile)
+
+        dialog.destroy()
+
+    def do_pre_send(self, dlg, logfile):
+        import thread
+        hildon.hildon_gtk_window_set_progress_indicator(dlg, 1)
+        thread.start_new_thread(self._do_send, (dlg, logfile))
+
+    def _do_send(self, dlg, logfile):
+        import pycurl, shutil, random, commands
+        try:
+            rname = ''
+            for i in random.sample('abcdefghijkl123456789', 18):
+                rname += i
+
+            rnamepath = HOME + "/.stockthis/" + rname
+            shutil.copyfile(logfile, rnamepath)
+
+            gtkversion = "%s.%s.%s" % gtk.ver
+            if os.path.exists("/etc/maemo_version"):
+                mfile = open("/etc/maemo_version", 'r')
+                maemoversion = mfile.read()
+                mfile.close()
+            else:
+                maemoversion = ''
+
+            opsystem = ' '.join(os.uname())
+            pyversion = os.sys.version
+            pid = os.getpid()
+            comm = ("awk '/Private_Dirty/{sum+=$2}END{print sum \"kB\"}'"
+            " /proc/%s/smaps") % pid
+            status, dirtymem = commands.getstatusoutput(comm)
+
+            lfile = open(rnamepath, 'r')
+            log = lfile.read()
+            lfile.close()
+
+
+            log = ("%s\nPython version: %s\nGtk version: %s\n"
+            "Maemo version: %sOperating system: %s\n"
+            "Dirty Memory: %s\nLog:\n%s") % (_version, pyversion, gtkversion,
+            maemoversion, opsystem, dirtymem, log)
+
+            lfile = open(rnamepath, 'w')
+            lfile.write(log)
+            lfile.close()
+
+            url = "http://yerga.net/logs/uploader.php"
+            data = [('uploadedfile', (pycurl.FORM_FILE, rnamepath)),]
+            mycurl = pycurl.Curl()
+            mycurl.setopt(pycurl.URL, url)
+            mycurl.setopt(pycurl.HTTPPOST, data)
+
+            mycurl.perform()
+            mycurl.close()
+            os.remove(rnamepath)
+
+            gtk.gdk.threads_enter()
+            stockspy.show_info_banner(dlg, 'Log sent')
+            gtk.gdk.threads_leave()
+            hildon.hildon_gtk_window_set_progress_indicator(dlg, 0)
+        except:
+            logger.exception("Sending log file")
+            gtk.gdk.threads_enter()
+            stockspy.show_info_banner(dlg, 'Error sending the log file')
+            gtk.gdk.threads_leave()
+            hildon.hildon_gtk_window_set_progress_indicator(dlg, 0)
+
+
 if __name__ == "__main__":
     stockspy = StocksPy()
     gtk.gdk.threads_enter()