[gnome-applets] invest-applet: rewrite in c
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-applets] invest-applet: rewrite in c
- Date: Wed, 21 Sep 2016 10:36:01 +0000 (UTC)
commit 025f69cd4cd0bfcab2d0aabb31b79b7135faa72b
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date: Wed Sep 21 13:27:04 2016 +0300
invest-applet: rewrite in c
Makefile.am | 1 -
configure.ac | 16 +-
gnome-applets.doap | 1 -
invest-applet/data/Makefile.am | 36 +-
invest-applet/data/financialchart.ui | 120 +-
...g.gnome.applets.InvestApplet.panel-applet.in.in | 3 +-
...rg.gnome.gnome-applets.invest.gschema.xml.in.in | 20 +
...ome.panel.applet.InvestAppletFactory.service.in | 3 -
invest-applet/data/prefs-dialog.ui | 37 +-
invest-applet/invest/Makefile.am | 103 +-
invest-applet/invest/__init__.py | 226 ---
invest-applet/invest/about.py | 35 -
invest-applet/invest/applet.py | 251 ----
invest-applet/invest/chart.py | 258 ----
invest-applet/invest/currencies.py | 145 --
invest-applet/invest/defs.py.in | 10 -
invest-applet/invest/help.py | 8 -
invest-applet/invest/invest-applet.c | 483 +++++++
invest-applet/invest/invest-applet.h | 46 +
invest-applet/invest/invest-applet.py | 82 --
invest-applet/invest/invest-cache.c | 290 ++++
invest-applet/invest/invest-cache.h | 66 +
invest-applet/invest/invest-chart | 15 -
invest-applet/invest/invest-chart.c | 473 +++++++
invest-applet/invest/invest-chart.h | 39 +
invest-applet/invest/invest-currencies.c | 190 +++
invest-applet/invest/invest-currencies.h | 44 +
invest-applet/invest/invest-image-retriever.c | 248 ++++
invest-applet/invest/invest-image-retriever.h | 44 +
invest-applet/invest/invest-preferences.c | 1137 ++++++++++++++++
invest-applet/invest/invest-preferences.h | 43 +
invest-applet/invest/invest-quotes-retriever.c | 372 +++++
invest-applet/invest/invest-quotes-retriever.h | 45 +
invest-applet/invest/invest-quotes.c | 1427 ++++++++++++++++++++
invest-applet/invest/invest-quotes.h | 48 +
invest-applet/invest/invest-widget.c | 638 +++++++++
invest-applet/invest/invest-widget.h | 40 +
invest-applet/invest/invest-window.c | 166 +++
invest-applet/invest/invest-window.h | 42 +
invest-applet/invest/networkmanager.py | 24 -
invest-applet/invest/preferences.py | 391 ------
invest-applet/invest/quotes.py | 627 ---------
invest-applet/invest/test.py | 30 -
invest-applet/invest/widgets.py | 173 ---
po/POTFILES.in | 13 +-
po/POTFILES.skip | 1 +
46 files changed, 6047 insertions(+), 2463 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 536daa0..e1e6a29 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,7 +108,6 @@ MAINTAINERCLEANFILES = \
$(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \
`find "$(srcdir)/m4" -type f -name "*.m4" -print` \
$(srcdir)/INSTALL \
- $(srcdir)/build-aux/py-compile \
$(srcdir)/config.h.in~ \
$(srcdir)/configure \
$(NULL)
diff --git a/configure.ac b/configure.ac
index 0643ceb..c04453a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -59,7 +59,7 @@ LT_LIB_M
dnl ***************************************************************************
dnl *** Minimum library versions for GNOME-APPLETS ***
dnl ***************************************************************************
-GTK_REQUIRED=3.15.2
+GTK_REQUIRED=3.20.0
GLIB_REQUIRED=2.44.0
GIO_REQUIRED=2.26.0
LIBPANEL_REQUIRED=3.18.0
@@ -88,7 +88,6 @@ AC_PROG_CC
AC_ISC_POSIX
AC_STDC_HEADERS
AC_PATH_XTRA
-AM_PATH_PYTHON(3)
X_LIBS="$X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS"
AC_SUBST(X_LIBS)
@@ -598,19 +597,6 @@ if test "$gtk_ok" = "yes"; then
[Define if _NL_MEASUREMENT_MEASUREMENT is available])
fi
-dnl ***************************************************************************
-dnl *** Set install directories ***
-dnl ***************************************************************************
-AC_ARG_WITH([pythondir],
- AS_HELP_STRING([--with-pythondir=DIR], [installation path for private Python modules @<:@auto@:>@]),
- [ac_with_pythondir=$withval], [ac_with_pythondir=""])
-if test "$ac_with_pythondir" != ""; then
- pythondir=${ac_with_pythondir}
-fi
-
-AC_MSG_NOTICE([installing private Python modules in $pythondir])
-AC_SUBST(pythondir)
-
dnl **************************************************************************
dnl Process .in files
dnl **************************************************************************
diff --git a/gnome-applets.doap b/gnome-applets.doap
index 4c7978c..2b102ff 100644
--- a/gnome-applets.doap
+++ b/gnome-applets.doap
@@ -12,7 +12,6 @@
<bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=gnome-applets" />
<programming-language>C</programming-language>
- <programming-language>Python</programming-language>
<maintainer>
<foaf:Person>
diff --git a/invest-applet/data/Makefile.am b/invest-applet/data/Makefile.am
index af13377..24b82e8 100644
--- a/invest-applet/data/Makefile.am
+++ b/invest-applet/data/Makefile.am
@@ -3,31 +3,25 @@ SUBDIRS = art
# ******************************************************************************
# Panel Applet stuff
# ******************************************************************************
-servicedir = $(datadir)/dbus-1/services
-service_in_files = org.gnome.panel.applet.InvestAppletFactory.service.in
-service_DATA = $(service_in_files:.service.in=.service)
-
-org.gnome.panel.applet.InvestAppletFactory.service: $(service_in_files)
- $(AM_V_GEN)sed \
- -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \
- $< > $@
appletdir = $(LIBPANEL_APPLET_DIR)
applet_in_files = org.gnome.applets.InvestApplet.panel-applet.in
applet_DATA = $(applet_in_files:.panel-applet.in=.panel-applet)
+LOCATION=$(pkglibdir)/$(LIBPANEL_APPLET_API_VERSION)/libinvest-applet.so
+
$(applet_in_files): $(applet_in_files).in Makefile
$(AM_V_GEN)sed \
- -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \
+ -e "s|\@LOCATION\@|$(LOCATION)|" \
-e "s|\@VERSION\@|$(PACKAGE_VERSION)|" \
$< > $@
%.panel-applet: %.panel-applet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE)
$(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
-
# ******************************************************************************
# Misc data
# ******************************************************************************
+
uidir = $(pkgdatadir)/ui
ui_DATA = \
invest-applet-menu.xml
@@ -42,20 +36,30 @@ investbindir = $(libdir)/invest-applet
# ******************************************************************************
# Build rules
# ******************************************************************************
-@INTLTOOL_SERVER_RULE@
+
@INTLTOOL_SCHEMAS_RULE@
+gsettings_schemas_in_in = \
+ org.gnome.gnome-applets.invest.gschema.xml.in.in
+
+@INTLTOOL_XML_NOMERGE_RULE@
+
+gsettings_schemas_in = $(gsettings_schemas_in_in:.xml.in.in=.xml.in)
+gsettings_SCHEMAS = $(gsettings_schemas_in:.xml.in=.xml)
+%.gschema.xml.in: %.gschema.xml.in.in Makefile
+ $(AM_V_GEN) $(SED) -e 's^\@GETTEXT_PACKAGE\@^$(GETTEXT_PACKAGE)^g' < $< > $@
-CLEANFILES = $(applet_DATA) $(applet_DATA).in $(service_DATA)
+@GSETTINGS_RULES@
-DISTCLEANFILES = \
- $(server_in_files) \
- $(server_in_files:.server.in=.server)
+CLEANFILES = $(applet_DATA) $(applet_DATA).in \
+ $(gsettings_SCHEMAS_in) \
+ $(gsettings_SCHEMAS) \
+ *.gschema.valid
EXTRA_DIST = \
org.gnome.applets.InvestApplet.panel-applet.in.in \
- $(service_in_files) \
+ $(gsettings_schemas_in_in) \
$(ui_DATA) \
$(builder_DATA)
diff --git a/invest-applet/data/financialchart.ui b/invest-applet/data/financialchart.ui
index da326a8..6f1c071 100644
--- a/invest-applet/data/financialchart.ui
+++ b/invest-applet/data/financialchart.ui
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.18.3 -->
+<!-- Generated with glade 3.20.0 -->
<interface>
- <requires lib="gtk+" version="3.12"/>
- <object class="GtkWindow" id="window">
+ <requires lib="gtk+" version="3.20"/>
+ <template class="InvestChart" parent="GtkWindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">6</property>
@@ -41,6 +41,8 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
+ <signal name="activate" handler="s_activate_cb" object="InvestChart" swapped="no"/>
+ <signal name="changed" handler="s_changed_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -50,13 +52,12 @@
</child>
<child>
<object class="GtkCheckButton" id="autorefresh">
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="autorefresh_toggled_cb" object="InvestChart" swapped="no"/>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
@@ -108,15 +109,17 @@
<object class="GtkComboBoxText" id="t">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="active">0</property>
<items>
- <item translatable="yes">Today</item>
- <item translatable="yes">5 Days</item>
- <item translatable="yes">3 Months</item>
- <item translatable="yes">6 Months</item>
- <item translatable="yes">1 Year</item>
- <item translatable="yes">5 Years</item>
- <item translatable="yes">Maximum</item>
+ <item id="1d" translatable="yes">Today</item>
+ <item id="5d" translatable="yes">5 Days</item>
+ <item id="3m" translatable="yes">3 Months</item>
+ <item id="6m" translatable="yes">6 Months</item>
+ <item id="1y" translatable="yes">1 Year</item>
+ <item id="5y" translatable="yes">5 Years</item>
+ <item id="my" translatable="yes">Maximum</item>
</items>
+ <signal name="changed" handler="changed_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -196,7 +199,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
- <property name="xalign">1</property>
<property name="label" translatable="yes" comments="see
http://biz.yahoo.com/charts/guide10.html and
http://en.wikipedia.org/wiki/Technical_indicator#Charting_terms_and_indicators">Indicators: </property>
</object>
<packing>
@@ -212,11 +214,13 @@
<object class="GtkComboBoxText" id="q">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="active">0</property>
<items>
- <item translatable="yes">Line</item>
- <item translatable="yes">Bar</item>
- <item translatable="yes">Candle</item>
+ <item id="l" translatable="yes">Line</item>
+ <item id="b" translatable="yes">Bar</item>
+ <item id="c" translatable="yes">Candle</item>
</items>
+ <signal name="changed" handler="changed_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -247,10 +251,12 @@
<object class="GtkComboBoxText" id="l">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="active">0</property>
<items>
- <item translatable="yes">Linear</item>
- <item translatable="yes">Logarithmic</item>
+ <item id="off" translatable="yes">Linear</item>
+ <item id="on" translatable="yes">Logarithmic</item>
</items>
+ <signal name="changed" handler="changed_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -283,13 +289,12 @@
<child>
<object class="GtkCheckButton" id="pm5">
<property name="label" translatable="yes">5</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -300,13 +305,12 @@
<child>
<object class="GtkCheckButton" id="pm10">
<property name="label" translatable="yes">10</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -317,14 +321,13 @@
<child>
<object class="GtkCheckButton" id="pm20">
<property name="label" translatable="yes">20</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -335,13 +338,12 @@
<child>
<object class="GtkCheckButton" id="pm50">
<property name="label" translatable="yes">50</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -352,13 +354,12 @@
<child>
<object class="GtkCheckButton" id="pm100">
<property name="label" translatable="yes">100</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -369,13 +370,12 @@
<child>
<object class="GtkCheckButton" id="pm200">
<property name="label" translatable="yes">200</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -397,14 +397,13 @@
<child>
<object class="GtkCheckButton" id="pe5">
<property name="label" translatable="yes">5</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -415,13 +414,12 @@
<child>
<object class="GtkCheckButton" id="pe10">
<property name="label" translatable="yes">10</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -432,14 +430,13 @@
<child>
<object class="GtkCheckButton" id="pe20">
<property name="label" translatable="yes">20</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -450,13 +447,12 @@
<child>
<object class="GtkCheckButton" id="pe50">
<property name="label" translatable="yes">50</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -467,13 +463,12 @@
<child>
<object class="GtkCheckButton" id="pe100">
<property name="label" translatable="yes">100</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -484,13 +479,12 @@
<child>
<object class="GtkCheckButton" id="pe200">
<property name="label" translatable="yes">200</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -512,14 +506,13 @@
<child>
<object class="GtkCheckButton" id="pb">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://biz.yahoo.com/charts/guide13.html and
http://en.wikipedia.org/wiki/Bollinger_bands">Bollinger</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -530,13 +523,12 @@
<child>
<object class="GtkCheckButton" id="pp">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://biz.yahoo.com/charts/guide16.html and
http://en.wikipedia.org/wiki/Parabolic_SAR">SAR</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -547,13 +539,12 @@
<child>
<object class="GtkCheckButton" id="ps">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://biz.yahoo.com/charts/guide6.html and
http://en.wikipedia.org/wiki/Stock_split">Splits</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -564,14 +555,13 @@
<child>
<object class="GtkCheckButton" id="pv">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://biz.yahoo.com/charts/guide20.html and
http://en.wikipedia.org/wiki/Volume_%28finance%29">Volumes</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -594,14 +584,13 @@
<child>
<object class="GtkCheckButton" id="am">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/MACD">MACD</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@@ -611,14 +600,13 @@
<child>
<object class="GtkCheckButton" id="af">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Money_flow_index">MFI</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -628,13 +616,12 @@
<child>
<object class="GtkCheckButton" id="ap">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Rate_of_change_%28technical_analysis%29">ROC</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -644,13 +631,12 @@
<child>
<object class="GtkCheckButton" id="ar">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Relative_Strength_Index">RSI</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@@ -660,13 +646,12 @@
<child>
<object class="GtkCheckButton" id="av">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Volume_%28finance%29">Vol</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@@ -676,14 +661,13 @@
<child>
<object class="GtkCheckButton" id="ass">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Stochastic_oscillator">Slow stoch</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -693,13 +677,12 @@
<child>
<object class="GtkCheckButton" id="avm">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Volume_%28finance%29">Vol+MA</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -709,13 +692,12 @@
<child>
<object class="GtkCheckButton" id="afs">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Stochastic_oscillator">Fast stoch</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -725,14 +707,13 @@
<child>
<object class="GtkCheckButton" id="aw">
<property name="label" translatable="yes" comments="Please keep this term short. For
its meaning, see http://en.wikipedia.org/wiki/Williams_%25R">W%R</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
- <property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="toggled_cb" object="InvestChart" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -792,7 +773,6 @@
</child>
<child>
<object class="GtkLabel" id="progress">
- <property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Downloading chart from
<b>Yahoo!</b></property>
<property name="use_markup">True</property>
@@ -806,5 +786,5 @@
</child>
</object>
</child>
- </object>
+ </template>
</interface>
diff --git a/invest-applet/data/org.gnome.applets.InvestApplet.panel-applet.in.in
b/invest-applet/data/org.gnome.applets.InvestApplet.panel-applet.in.in
index afedcdb..7116c40 100644
--- a/invest-applet/data/org.gnome.applets.InvestApplet.panel-applet.in.in
+++ b/invest-applet/data/org.gnome.applets.InvestApplet.panel-applet.in.in
@@ -1,6 +1,7 @@
[Applet Factory]
Id=InvestAppletFactory
-Location=@LIBEXECDIR@/invest-applet
+InProcess=true
+Location=@LOCATION@
_Name=Invest Applet Factory
_Description=Factory for creating the invest applet.
diff --git a/invest-applet/data/org.gnome.gnome-applets.invest.gschema.xml.in.in
b/invest-applet/data/org.gnome.gnome-applets.invest.gschema.xml.in.in
new file mode 100644
index 0000000..a112cd4
--- /dev/null
+++ b/invest-applet/data/org.gnome.gnome-applets.invest.gschema.xml.in.in
@@ -0,0 +1,20 @@
+<schemalist gettext-domain="@GETTEXT_PACKAGE@">
+ <schema id="org.gnome.gnome-applets.invest">
+ <key name="currency" type="s">
+ <default>""</default>
+ <_summary>Currency</_summary>
+ </key>
+ <key name="hidecharts" type="b">
+ <default>false</default>
+ <_summary>Hide charts in quotes list</_summary>
+ </key>
+ <key name="indexexpansion" type="b">
+ <default>true</default>
+ <_summary>Show stocks of index values</_summary>
+ </key>
+ <key type="a{bv}" name="stocks">
+ <default>{}</default>
+ <summary>Stocks</summary>
+ </key>
+ </schema>
+</schemalist>
diff --git a/invest-applet/data/prefs-dialog.ui b/invest-applet/data/prefs-dialog.ui
index c7cc9d9..a137e89 100644
--- a/invest-applet/data/prefs-dialog.ui
+++ b/invest-applet/data/prefs-dialog.ui
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
<interface>
- <requires lib="gtk+" version="3.8"/>
- <object class="GtkDialog" id="preferences">
+ <requires lib="gtk+" version="3.20"/>
+ <template class="InvestPreferences" parent="GtkDialog">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Invest Preferences</property>
- <property name="modal">True</property>
<property name="window_position">center</property>
<property name="default_height">450</property>
<property name="destroy_with_parent">True</property>
@@ -25,7 +25,6 @@
<child>
<object class="GtkButton" id="help">
<property name="label">gtk-help</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
@@ -40,7 +39,6 @@
<child>
<object class="GtkButton" id="close">
<property name="label">gtk-close</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
@@ -92,17 +90,16 @@
<object class="GtkLabel" id="stocklabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="xalign">0</property>
<property name="label" translatable="yes">Stocks</property>
<property name="use_underline">True</property>
- <property name="justify">center</property>
+ <property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">False</property>
+ <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -133,6 +130,7 @@
<property name="reorderable">True</property>
<property name="rules_hint">True</property>
<property name="enable_tree_lines">True</property>
+ <signal name="key-press-event" handler="stocks_key_press_event_cb"
swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
@@ -146,7 +144,7 @@
</packing>
</child>
<child>
- <object class="GtkHBox" id="hbox1">
+ <object class="GtkBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
@@ -154,11 +152,11 @@
<child>
<object class="GtkButton" id="addstock">
<property name="label">gtk-add</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
+ <signal name="clicked" handler="addstock_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -169,10 +167,10 @@
<child>
<object class="GtkButton" id="addgroup">
<property name="label" translatable="yes" context=" " comments="Instead of
adding a single stock to the list of stocks, the 'Add Group' button adds a group (kind of a sub folder) to
which numerous stocks can be added. A group here refers to a group of stocks.">Add Group</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <signal name="clicked" handler="addgroup_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -183,11 +181,11 @@
<child>
<object class="GtkButton" id="remove">
<property name="label">gtk-remove</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
+ <signal name="clicked" handler="remove_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -205,13 +203,11 @@
<child>
<object class="GtkCheckButton" id="indexexpansion">
<property name="label" translatable="yes" comments="An index value (for instance
the NASDAQ Composite) is based on a number of stocks. This option allows to also show the quotes of the
stocks an index is based on. ">Show stocks of index values</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="has_tooltip">True</property>
<property name="tooltip_markup" translatable="yes">An index value, for instance
the <i>NASDAQ Composite</i> (^IXIC), is based on a number of stocks. This option allows to also
show the quotes of the <i><b>stocks</b></i> an index is based on.</property>
- <property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
@@ -224,13 +220,11 @@
<child>
<object class="GtkCheckButton" id="hidecharts">
<property name="label" translatable="yes">Hide charts in quotes list</property>
- <property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">A small chart image is shown
next to each quote. The retrieval of each chart image causes network traffic. Hiding charts reduces the
network bandwidth demand significantly.</property>
<property name="relief">none</property>
- <property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
@@ -243,9 +237,9 @@
<object class="GtkLabel" id="default_info">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="xalign">0</property>
<property name="label"
translatable="yes"><i><small><b>Source:</b> <a
href="http://finance.yahoo.com/">Yahoo! Finance</a> (at least 15 minutes
delayed)</small></i></property>
<property name="use_markup">True</property>
+ <property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
@@ -266,17 +260,16 @@
<object class="GtkLabel" id="currencylabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="xalign">0</property>
<property name="label" translatable="yes">Currency</property>
<property name="use_underline">True</property>
- <property name="justify">center</property>
+ <property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">False</property>
+ <property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
@@ -290,6 +283,8 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Type the target currency to which
all stock quotes will be converted to.</property>
+ <signal name="activate" handler="currency_activate_cb" swapped="no"/>
+ <signal name="focus-out-event" handler="currency_focus_out_event_cb" swapped="no"/>
</object>
</child>
</object>
@@ -320,5 +315,5 @@
<action-widget response="1">help</action-widget>
<action-widget response="-7">close</action-widget>
</action-widgets>
- </object>
+ </template>
</interface>
diff --git a/invest-applet/invest/Makefile.am b/invest-applet/invest/Makefile.am
index 4b0d656..774d8e7 100644
--- a/invest-applet/invest/Makefile.am
+++ b/invest-applet/invest/Makefile.am
@@ -1,55 +1,52 @@
-invest-applet: invest-applet.py
- sed -e "s|\@PYTHONDIR\@|$(pythondir)/|" $< > $@
-
-defs.py: defs.py.in
- sed \
- -e "s|\@DATADIR\@|$(datadir)|" \
- -e "s|\@LIBDIR\@|$(libdir)|" \
- -e "s|\@VERSION\@|$(VERSION)|" \
- -e "s|\@PACKAGE\@|$(PACKAGE)|" \
- -e "s|\@PKGDATADIR\@|$(pkgdatadir)|" \
- -e "s|\@PYTHONDIR\@|$(pythondir)|" \
- -e "s|\@GETTEXT_PACKAGE\@|$(GETTEXT_PACKAGE)|" \
- -e "s|\@GNOMELOCALEDIR\@|$(localedir)|" \
- -e "s|\@BUILDERDIR\@|$(pkgdatadir)/builder|" \
- -e "s|\@LIBPANEL_APPLET_API_VERSION\@|$(LIBPANEL_APPLET_API_VERSION)|" \
- $< > $@
-
-bin_SCRIPTS = invest-chart
-
-libexec_SCRIPTS = invest-applet
-
-investdir = $(pythondir)/invest
-invest_PYTHON = \
- __init__.py \
- about.py \
- help.py \
- applet.py \
- chart.py \
- currencies.py \
- widgets.py \
- quotes.py \
- networkmanager.py \
- preferences.py
-nodist_invest_PYTHON = \
- defs.py
-
-BUILT_SOURCES = \
- invest-applet
-
-CLEANFILES = \
- $(BUILT_SOURCES)
-
-DISTCLEANFILES = \
- defs.py \
- $(CLEANFILES)
-
-EXTRA_DIST = \
- defs.py.in \
- invest-applet.py \
- invest-chart \
- test.py
-
-#TESTS = test.py
+NULL =
+
+invest_applet_libdir = $(pkglibdir)/$(LIBPANEL_APPLET_API_VERSION)
+invest_applet_lib_LTLIBRARIES = libinvest-applet.la
+
+libinvest_applet_la_SOURCES = \
+ invest-applet.c \
+ invest-applet.h \
+ invest-cache.c \
+ invest-cache.h \
+ invest-chart.c \
+ invest-chart.h \
+ invest-currencies.c \
+ invest-currencies.h \
+ invest-image-retriever.c \
+ invest-image-retriever.h \
+ invest-preferences.c \
+ invest-preferences.h \
+ invest-quotes.c \
+ invest-quotes.h \
+ invest-quotes-retriever.c \
+ invest-quotes-retriever.h \
+ invest-widget.c \
+ invest-widget.h \
+ invest-window.c \
+ invest-window.h \
+ $(NULL)
+
+libinvest_applet_la_CPPFLAGS = \
+ -DARTDIR=\""$(pkgdatadir)/invest-applet"\" \
+ -DBUILDERDIR=\""$(pkgdatadir)/builder"\" \
+ -DGNOMELOCALEDIR=\""$(localedir)"\" \
+ -DUIDIR=\""$(pkgdatadir)/ui"\" \
+ $(NULL)
+
+libinvest_applet_la_CFLAGS = \
+ $(GNOME_APPLETS_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(AM_CFLAGS) \
+ $(NULL)
+
+libinvest_applet_la_LIBADD = \
+ $(GNOME_APPLETS_LIBS) \
+ $(NULL)
+
+libinvest_applet_la_LDFLAGS = \
+ -module -avoid-version \
+ $(WARN_LDFLAGS) \
+ $(AM_LDFLAGS) \
+ $(NULL)
-include $(top_srcdir)/git.mk
diff --git a/invest-applet/invest/invest-applet.c b/invest-applet/invest/invest-applet.c
new file mode 100644
index 0000000..79d268e
--- /dev/null
+++ b/invest-applet/invest/invest-applet.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "invest-applet.h"
+#include "invest-preferences.h"
+#include "invest-quotes.h"
+#include "invest-window.h"
+
+struct _InvestApplet
+{
+ PanelApplet parent;
+
+ GSettings *settings;
+
+ GtkWidget *icon;
+ GtkWidget *preferences;
+
+ InvestQuotes *quotes;
+
+ GtkWidget *window;
+};
+
+struct _InvestAppletClass
+{
+ PanelAppletClass parent_class;
+};
+
+G_DEFINE_TYPE (InvestApplet, invest_applet, PANEL_TYPE_APPLET)
+
+static void
+update_popup_position (InvestApplet *applet)
+{
+ GtkWindow *popup_window;
+ GdkDisplay *display;
+ GdkWindow *applet_window;
+ GdkMonitor *monitor;
+ GdkRectangle geometry;
+ GtkAllocation allocation;
+ gint x, y, w, h;
+ GdkGravity gravity;
+
+ g_assert (applet->window != NULL);
+
+ popup_window = GTK_WINDOW (applet->window);
+
+ display = gdk_display_get_default ();
+ applet_window = gtk_widget_get_window (GTK_WIDGET (applet));
+ monitor = gdk_display_get_monitor_at_window (display, applet_window);
+
+ gdk_monitor_get_geometry (monitor, &geometry);
+
+ gtk_widget_get_allocation (GTK_WIDGET (applet), &allocation);
+ gdk_window_get_origin (applet_window, &x, &y);
+ gtk_window_get_size (popup_window, &w, &h);
+
+ gravity = GDK_GRAVITY_NORTH_WEST;
+ switch (panel_applet_get_orient (PANEL_APPLET (applet)))
+ {
+ case PANEL_APPLET_ORIENT_RIGHT:
+ x += allocation.width;
+ if ((y + h) > geometry.y + geometry.height)
+ y -= (y + h) - (geometry.y + geometry.height);
+
+ if ((y + h) > (geometry.height / 2))
+ gravity = GDK_GRAVITY_SOUTH_WEST;
+ else
+ gravity = GDK_GRAVITY_NORTH_WEST;
+ break;
+
+ case PANEL_APPLET_ORIENT_LEFT:
+ x -= w;
+ if ((y + h) > geometry.y + geometry.height)
+ y -= (y + h) - (geometry.y + geometry.height);
+
+ if ((y + h) > (geometry.height / 2))
+ gravity = GDK_GRAVITY_SOUTH_EAST;
+ else
+ gravity = GDK_GRAVITY_NORTH_EAST;
+ break;
+
+ case PANEL_APPLET_ORIENT_DOWN:
+ y += allocation.height;
+ if ((x + w) > geometry.x + geometry.width)
+ x -= (x + w) - (geometry.x + geometry.width);
+
+ gravity = GDK_GRAVITY_NORTH_WEST;
+ break;
+
+ case PANEL_APPLET_ORIENT_UP:
+ y -= h;
+ if ((x + w) > geometry.x + geometry.width)
+ x -= (x + w) - (geometry.x + geometry.width);
+
+ gravity = GDK_GRAVITY_SOUTH_WEST;
+ break;
+
+ default:
+ break;
+ }
+
+ gtk_window_move (popup_window, x, y);
+ gtk_window_set_gravity (popup_window, gravity);
+}
+
+static void
+preferences_destroy_cb (GtkWidget *widget,
+ InvestApplet *applet)
+{
+ applet->preferences = NULL;
+}
+
+static void
+show_preferences (InvestApplet *applet,
+ const gchar *explanation)
+{
+ InvestPreferences *preferences;
+
+ if (applet->preferences != NULL)
+ {
+ preferences = INVEST_PREFERENCES (applet->preferences);
+
+ invest_preferences_set_explanation (preferences, explanation);
+ gtk_window_present (GTK_WINDOW (applet->preferences));
+
+ return;
+ }
+
+ applet->preferences = invest_preferences_new (applet->settings);
+ preferences = INVEST_PREFERENCES (applet->preferences);
+
+ g_signal_connect (preferences, "destroy",
+ G_CALLBACK (preferences_destroy_cb), applet);
+
+ invest_preferences_set_explanation (preferences, explanation);
+ gtk_window_present (GTK_WINDOW (applet->preferences));
+}
+
+static void
+set_icon (InvestApplet *applet,
+ gint change)
+{
+ GError *error;
+ GdkPixbuf *pixbuf;
+
+ error = NULL;
+
+ if (change == 1)
+ {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (ARTDIR "/invest-22_up.png",
+ -1, -1, &error);
+ }
+ else if (change == 0)
+ {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (ARTDIR "/invest-22_neutral.png",
+ -1, -1, &error);
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (ARTDIR "/invest-22_down.png",
+ -1, -1, &error);
+ }
+
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (applet->icon), pixbuf);
+ g_clear_object (&pixbuf);
+}
+
+static void
+setup_applet (InvestApplet *applet)
+{
+ GtkWidget *hbox;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (applet), hbox);
+
+ applet->icon = gtk_image_new ();
+ gtk_container_add (GTK_CONTAINER (hbox), applet->icon);
+ gtk_widget_show (applet->icon);
+
+ set_icon (applet, 0);
+}
+
+static void
+refresh_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ InvestApplet *applet;
+
+ applet = INVEST_APPLET (user_data);
+
+ invest_quotes_refresh (applet->quotes);
+}
+
+static void
+preferences_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ InvestApplet *applet;
+
+ applet = INVEST_APPLET (user_data);
+
+ show_preferences (applet, NULL);
+}
+
+static void
+help_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ GdkScreen *screen;
+
+ screen = gdk_screen_get_default ();
+
+ gtk_show_uri (screen, "help:invest-applet", GDK_CURRENT_TIME, NULL);
+}
+
+static const gchar *authors[] = {
+ "Raphael Slinckx <raphael slinckx net>",
+ "Enrico Minack <enrico-minack gmx de>",
+ NULL
+};
+
+static void
+about_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ const gchar *copyright;
+ GdkPixbuf *logo;
+
+ copyright = "Copyright © 2004-2005 Raphael Slinckx.\nCopyright © 2009-2011 Enrico Minack.";
+ logo = gdk_pixbuf_new_from_file_at_size (ARTDIR "/invest_neutral.svg", 96, 96, NULL);
+
+ gtk_show_about_dialog (NULL,
+ "authors", authors,
+ "comments", _("Track your invested money."),
+ "copyright", copyright,
+ "logo", logo,
+ "program-name", _("Invest"),
+ "version", VERSION,
+ "translator-credits", _("translator-credits"),
+ NULL);
+
+ g_clear_object (&logo);
+}
+
+static const GActionEntry menu_actions[] = {
+ { "refresh", refresh_cb },
+ { "preferences", preferences_cb },
+ { "help", help_cb },
+ { "about", about_cb }
+};
+
+static void
+setup_menu (PanelApplet *applet)
+{
+ GSimpleActionGroup *action_group;
+ const gchar *file;
+
+ action_group = g_simple_action_group_new ();
+ file = UIDIR "/invest-applet-menu.xml";
+
+ g_action_map_add_action_entries (G_ACTION_MAP (action_group), menu_actions,
+ G_N_ELEMENTS (menu_actions), applet);
+
+ panel_applet_setup_menu_from_file (applet, file, action_group,
+ GETTEXT_PACKAGE);
+
+ gtk_widget_insert_action_group (GTK_WIDGET (applet), "invest",
+ G_ACTION_GROUP (action_group));
+
+ g_object_unref (action_group);
+}
+
+static void
+update_icon_cb (InvestQuotes *quotes,
+ gint icon,
+ InvestApplet *applet)
+{
+ set_icon (applet, icon);
+}
+
+static void
+update_tooltip_cb (InvestQuotes *quotes,
+ const gchar *text,
+ InvestApplet *applet)
+{
+ gtk_widget_set_tooltip_text (GTK_WIDGET (applet), text);
+}
+
+static void
+invest_applet_setup (PanelApplet *papplet)
+{
+ InvestApplet *applet;
+ const gchar *schema;
+
+ applet = INVEST_APPLET (papplet);
+
+ schema = "org.gnome.gnome-applets.invest";
+ applet->settings = panel_applet_settings_new (papplet, schema);
+
+ setup_applet (applet);
+ setup_menu (papplet);
+
+ applet->quotes = invest_quotes_new (applet->settings);
+
+ g_signal_connect (applet->quotes, "update-icon",
+ G_CALLBACK (update_icon_cb), applet);
+
+ g_signal_connect (applet->quotes, "update-tooltip",
+ G_CALLBACK (update_tooltip_cb), applet);
+
+ gtk_widget_show_all (GTK_WIDGET (applet));
+}
+
+static void
+popup_size_allocate_cb (GtkWidget *widget,
+ GtkAllocation *allocation,
+ InvestApplet *applet)
+{
+ update_popup_position (applet);
+}
+
+static gboolean
+button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ InvestApplet *applet)
+{
+ if (event->button != 1 || event->type != GDK_BUTTON_PRESS)
+ return FALSE;
+
+ if (!invest_quotes_has_stocks (applet->quotes))
+ {
+ const gchar *explanation;
+
+ explanation = _("<b>You have not entered any stock information yet</b>");
+
+ g_clear_pointer (&applet->window, gtk_widget_destroy);
+ show_preferences (applet, explanation);
+ }
+ else if (!invest_quotes_is_valid (applet->quotes))
+ {
+ GtkWidget *dialog;
+ GtkMessageDialog *message;
+ const gchar *markup;
+ const gchar *text;
+
+ dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE, NULL);
+
+ message = GTK_MESSAGE_DIALOG (dialog);
+ markup = _("<b>No stock quotes are currently available</b>");
+ text = _("The server could not be contacted. The computer is either "
+ "offline or the servers are down. Try again later.");
+
+ gtk_message_dialog_set_markup (message, markup);
+ gtk_message_dialog_format_secondary_text (message, "%s", text);
+
+ g_signal_connect_swapped (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ dialog);
+
+ g_clear_pointer (&applet->window, gtk_widget_destroy);
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+ else
+ {
+ if (applet->window != NULL)
+ {
+ gtk_widget_destroy (applet->window);
+ applet->window = NULL;
+ }
+ else
+ {
+ applet->window = invest_window_new (applet->settings,
+ applet->quotes);
+
+ g_signal_connect (applet->window, "size-allocate",
+ G_CALLBACK (popup_size_allocate_cb),
+ applet);
+
+ gtk_window_present (GTK_WINDOW (applet->window));
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+change_orient_cb (PanelApplet *papplet,
+ PanelAppletOrient orient,
+ InvestApplet *applet)
+{
+ if (applet->window != NULL)
+ update_popup_position (applet);
+}
+
+static void
+invest_applet_dispose (GObject *object)
+{
+ InvestApplet *applet;
+
+ applet = INVEST_APPLET (object);
+
+ g_clear_object (&applet->quotes);
+ g_clear_pointer (&applet->preferences, gtk_widget_destroy);
+ g_clear_pointer (&applet->window, gtk_widget_destroy);
+ g_clear_object (&applet->settings);
+
+ G_OBJECT_CLASS (invest_applet_parent_class)->dispose (object);
+}
+
+static void
+invest_applet_class_init (InvestAppletClass *applet_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (applet_class);
+
+ object_class->dispose = invest_applet_dispose;
+}
+
+static void
+invest_applet_init (InvestApplet *applet)
+{
+ panel_applet_set_flags (PANEL_APPLET (applet), PANEL_APPLET_EXPAND_MINOR);
+
+ g_signal_connect (applet, "button-press-event",
+ G_CALLBACK (button_press_event_cb),
+ applet);
+
+ g_signal_connect (applet, "change-orient",
+ G_CALLBACK (change_orient_cb),
+ applet);
+}
+
+static gboolean
+invest_applet_factory (PanelApplet *applet,
+ const gchar *iid,
+ gpointer data)
+{
+ if (g_strcmp0 (iid, "InvestApplet") != 0)
+ return FALSE;
+
+ invest_applet_setup (applet);
+
+ return TRUE;
+}
+
+PANEL_APPLET_IN_PROCESS_FACTORY ("InvestAppletFactory", INVEST_TYPE_APPLET,
+ invest_applet_factory, NULL)
diff --git a/invest-applet/invest/invest-applet.h b/invest-applet/invest/invest-applet.h
new file mode 100644
index 0000000..b2fe06e
--- /dev/null
+++ b/invest-applet/invest/invest-applet.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_APPLET_H
+#define INVEST_APPLET_H
+
+#include <panel-applet.h>
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_APPLET (invest_applet_get_type ())
+#define INVEST_APPLET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INVEST_TYPE_APPLET, InvestApplet))
+#define INVEST_APPLET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), INVEST_TYPE_APPLET, InvestAppletClass))
+#define INVEST_IS_APPLET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INVEST_TYPE_APPLET))
+#define INVEST_IS_APPLET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), INVEST_TYPE_APPLET))
+#define INVEST_APPLET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), INVEST_TYPE_APPLET, InvestAppletClass))
+
+typedef struct _InvestApplet InvestApplet;
+typedef struct _InvestAppletClass InvestAppletClass;
+
+GType invest_applet_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-cache.c b/invest-applet/invest/invest-cache.c
new file mode 100644
index 0000000..48d5271
--- /dev/null
+++ b/invest-applet/invest/invest-cache.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <string.h>
+
+#include "invest-cache.h"
+#include "invest-currencies.h"
+
+typedef enum
+{
+ QUOTE_STATE_UNQUOTED,
+ QUOTE_STATE_QUOTED,
+ QUOTE_STATE_QUOTE,
+} QuoteState;
+
+static gchar **
+get_fields (const gchar *line)
+{
+ GPtrArray *fields;
+ GString *field;
+ QuoteState state;
+ guint i;
+
+ fields = g_ptr_array_new ();
+ field = g_string_new (NULL);
+ state = QUOTE_STATE_UNQUOTED;
+
+ for (i = 0; line[i] != '\0'; i++)
+ {
+ gchar c;
+
+ c = line[i];
+
+ if (c == ',' && state != QUOTE_STATE_QUOTED)
+ {
+ g_string_append_c (field, '\0');
+ g_ptr_array_add (fields, g_string_free (field, FALSE));
+ field = g_string_new (NULL);
+ state = QUOTE_STATE_UNQUOTED;
+ continue;
+ }
+
+ if (c == '"')
+ {
+ if (state == QUOTE_STATE_UNQUOTED)
+ {
+ state = QUOTE_STATE_QUOTED;
+ continue;
+ }
+
+ if (state == QUOTE_STATE_QUOTED)
+ {
+ state = QUOTE_STATE_QUOTE;
+ continue;
+ }
+
+ if (state == QUOTE_STATE_QUOTE)
+ state = QUOTE_STATE_QUOTED;
+ }
+
+ if (state == QUOTE_STATE_QUOTE)
+ state = QUOTE_STATE_UNQUOTED;
+
+ field = g_string_append_c (field, c);
+ }
+
+ g_string_append_c (field, '\0');
+ g_ptr_array_add (fields, g_string_free (field, FALSE));
+ g_ptr_array_add (fields, NULL);
+
+ return (gchar **) g_ptr_array_free (fields, FALSE);
+}
+
+static InvestQuote *
+invest_quote_new (const gchar *str)
+{
+ gchar **fields;
+ InvestQuote *quote;
+ gdouble tmp;
+
+ if (str == NULL || *str == '\0')
+ return NULL;
+
+ fields = get_fields (str);
+ if (g_strv_length (fields) != 11)
+ {
+ g_strfreev (fields);
+ return NULL;
+ }
+
+ quote = g_new0 (InvestQuote, 1);
+
+ quote->symbol = g_strdup (fields[0]);
+ quote->name = g_strdup (fields[1]);
+ quote->currency = g_utf8_strup (fields[2], -1);
+ quote->last_trade = g_ascii_strtod (fields[3], NULL);
+ quote->last_trade_date = g_strdup (fields[4]);
+ quote->last_trade_time = g_strdup (fields[5]);
+ quote->change = g_ascii_strtod (fields[6], NULL);
+ quote->open = g_ascii_strtod (fields[7], NULL);
+ quote->hight = g_ascii_strtod (fields[8], NULL);
+ quote->low = g_ascii_strtod (fields[9], NULL);
+ quote->volume = (gint) g_ascii_strtoll (fields[10], NULL, 10);
+
+ /* FIXME: use p2 instead of c1 to get change in percent */
+ tmp = quote->last_trade - quote->change_pct;
+ quote->change_pct = tmp != 0.0 ? (quote->change / tmp * 100) : 0.0;
+
+ /* The currency of currency conversion rates like EURUSD=X is wrong in
+ * csv this can be fixed easily by reusing the latter currency in the
+ * symbol.
+ */
+ if (strlen (quote->symbol) == 8 && g_str_has_suffix (quote->symbol, "=X"))
+ {
+ g_free (quote->currency);
+ quote->currency = g_strndup (quote->symbol + 3, 3);
+ }
+
+ /* Indices should not have a currency, though yahoo says so.
+ */
+ if (g_str_has_prefix (quote->symbol, "^"))
+ {
+ g_free (quote->currency);
+ quote->currency = g_strdup ("");
+ }
+
+ /* Sometimes, funny currencies are returned (special characters), only
+ * consider known currencies.
+ */
+ if (!invest_currencies_contains (quote->currency))
+ {
+ g_free (quote->currency);
+ quote->currency = g_strdup ("");
+ }
+
+ g_strfreev (fields);
+ return quote;
+}
+
+static GPtrArray *
+parse_csv (const gchar *csv)
+{
+ GPtrArray *array;
+ gchar **lines;
+ guint i;
+
+ array = g_ptr_array_new ();
+ lines = g_strsplit (csv, "\n", -1);
+
+ for (i = 0; lines[i] != NULL; i++)
+ {
+ InvestQuote *quote;
+
+ quote = invest_quote_new (lines[i]);
+
+ if (quote != NULL)
+ g_ptr_array_add (array, quote);
+ }
+
+ g_strfreev (lines);
+
+ return array;
+}
+
+static void
+get_mtime (GFile *file,
+ GTimeVal *mtime)
+{
+ GFileInfo *info;
+
+ info = g_file_query_info (file, "time::modified", 0, NULL, NULL);
+ if (info == NULL)
+ return;
+
+ g_file_info_get_modification_time (info, mtime);
+ g_object_unref (info);
+}
+
+static void
+invest_quote_free (InvestQuote *quote)
+{
+ g_free (quote->symbol);
+ g_free (quote->name);
+ g_free (quote->currency);
+ g_free (quote->last_trade_date);
+ g_free (quote->last_trade_time);
+
+ g_free (quote);
+}
+
+InvestCache *
+invest_cache_read (const gchar *filename)
+{
+ const gchar *dir;
+ gchar *path;
+ GFile *file;
+ gchar *csv;
+ GError *error;
+ GPtrArray *array;
+ InvestCache *cache;
+
+ dir = g_get_user_cache_dir ();
+ path = g_build_filename (dir, "gnome-applets", "invest-applet",
+ filename, NULL);
+
+ file = g_file_new_for_path (path);
+ g_free (path);
+
+ csv = NULL;
+ error = NULL;
+
+ if (!g_file_load_contents (file, NULL, &csv, NULL, NULL, &error))
+ {
+ g_warning ("Could not load cached file %s: %s",
+ filename, error->message);
+
+ g_free (csv);
+ g_error_free (error);
+ g_object_unref (file);
+
+ return NULL;
+ }
+
+ array = parse_csv (csv);
+ g_free (csv);
+
+ cache = g_new0 (InvestCache, 1);
+
+ cache->n_quotes = array->len;
+ cache->quotes = (InvestQuote **) g_ptr_array_free (array, FALSE);
+
+ get_mtime (file, &cache->mtime);
+ g_object_unref (file);
+
+ return cache;
+}
+
+void
+invest_cache_free (InvestCache *cache)
+{
+ guint i;
+
+ if (cache == NULL)
+ return;
+
+ for (i = 0; i < cache->n_quotes; i++)
+ invest_quote_free (cache->quotes[i]);
+
+ g_free (cache);
+}
+
+InvestQuote *
+invest_cache_get (InvestCache *cache,
+ const gchar *symbol)
+
+{
+ guint i;
+
+ for (i = 0; i < cache->n_quotes; i++)
+ {
+ if (g_strcmp0 (cache->quotes[i]->symbol, symbol) == 0)
+ return cache->quotes[i];
+ }
+
+ return NULL;
+}
diff --git a/invest-applet/invest/invest-cache.h b/invest-applet/invest/invest-cache.h
new file mode 100644
index 0000000..452cd33
--- /dev/null
+++ b/invest-applet/invest/invest-cache.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_CACHE_H
+#define INVEST_CACHE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gchar *symbol;
+ gchar *name;
+ gchar *currency;
+ gdouble last_trade;
+ gchar *last_trade_date;
+ gchar *last_trade_time;
+ gdouble change;
+ gdouble open;
+ gdouble hight;
+ gdouble low;
+ gint volume;
+
+ gdouble change_pct;
+} InvestQuote;
+
+typedef struct
+{
+ InvestQuote **quotes;
+ guint n_quotes;
+
+ GTimeVal mtime;
+} InvestCache;
+
+InvestCache *invest_cache_read (const gchar *filename);
+
+void invest_cache_free (InvestCache *cache);
+
+InvestQuote *invest_cache_get (InvestCache *cache,
+ const gchar *symbol);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-chart.c b/invest-applet/invest/invest-chart.c
new file mode 100644
index 0000000..32e6797
--- /dev/null
+++ b/invest-applet/invest/invest-chart.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+/*
+ * p:
+ * eX = Exponential Moving Average
+ * mX = Moving Average
+ * b = Bollinger Bands Overlay
+ * v = Volume Overlay
+ * p = Parabolic SAR overlay
+ * s = Splits Overlay
+ * q:
+ * l = Line
+ * c = Candles
+ * b = Bars
+ * l:
+ * on = Logarithmic
+ * off = Linear
+ * z:
+ * l = Large
+ * m = Medium
+ * t:
+ * Xd = X Days
+ * Xm = X Months
+ * Xy = X Years
+ * a:
+ * fX = MFI X days
+ * ss = Slow Stochastic
+ * fs = Fast Stochastic
+ * wX = W%R X Days
+ * mX-Y-Z = MACD X Days, Y Days, Signal
+ * pX = ROC X Days
+ * rX = RSI X Days
+ * v = Volume
+ * vm = Volume +MA
+ * c:
+ * X = compare with X
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "invest-chart.h"
+#include "invest-image-retriever.h"
+
+#define AUTOREFRESH_TIMEOUT 20 * 60 * 1000
+#define CHART_URL "http://chart.finance.yahoo.com/z?s=%s&c=%s&t=%s&q=%s&l=%s&z=l&p=%s&a=%s"
+
+struct _InvestChart
+{
+ GtkWindow parent;
+
+ GtkWidget *s;
+ GtkWidget *autorefresh;
+ GtkWidget *plot;
+ GtkWidget *progress;
+ GtkWidget *t;
+ GtkWidget *q;
+ GtkWidget *l;
+ GtkWidget *pm5;
+ GtkWidget *pm10;
+ GtkWidget *pm20;
+ GtkWidget *pm50;
+ GtkWidget *pm100;
+ GtkWidget *pm200;
+ GtkWidget *pe5;
+ GtkWidget *pe10;
+ GtkWidget *pe20;
+ GtkWidget *pe50;
+ GtkWidget *pe100;
+ GtkWidget *pe200;
+ GtkWidget *pb;
+ GtkWidget *pp;
+ GtkWidget *ps;
+ GtkWidget *pv;
+ GtkWidget *ar;
+ GtkWidget *af;
+ GtkWidget *ap;
+ GtkWidget *aw;
+ GtkWidget *am;
+ GtkWidget *ass;
+ GtkWidget *afs;
+ GtkWidget *av;
+ GtkWidget *avm;
+
+ guint autorefresh_id;
+
+ InvestImageRetriever *retriever;
+};
+
+G_DEFINE_TYPE (InvestChart, invest_chart, GTK_TYPE_WINDOW)
+
+static void
+completed_cb (InvestImageRetriever *retriever,
+ GdkPixbuf *pixbuf,
+ InvestChart *chart)
+{
+ GtkImage *image;
+ GtkLabel *label;
+
+ image = GTK_IMAGE (chart->plot);
+ label = GTK_LABEL (chart->progress);
+
+ if (pixbuf != NULL)
+ {
+ gtk_image_set_from_pixbuf (image, pixbuf);
+ gtk_label_set_text (label, _("Chart downloaded"));
+ }
+ else
+ {
+ GdkPixbuf *logo;
+
+ logo = gdk_pixbuf_new_from_file_at_size (ARTDIR "/invest_neutral.svg",
+ 96, 96, NULL);
+
+ gtk_image_set_from_pixbuf (image, logo);
+ gtk_label_set_text (label, _("Chart could not be downloaded"));
+
+ g_clear_object (&logo);
+ }
+}
+
+static gchar *
+get_p (InvestChart *chart)
+{
+ GPtrArray *array;
+ gchar **elements;
+ gchar *p;
+
+ array = g_ptr_array_new ();
+
+#define ADD_P(variable, value) \
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chart->variable))) \
+ g_ptr_array_add (array, g_strdup (value));
+
+ ADD_P (pm5, "m5")
+ ADD_P (pm10, "m10")
+ ADD_P (pm20, "m20")
+ ADD_P (pm50, "m50")
+ ADD_P (pm100, "m100")
+ ADD_P (pm200, "m200")
+ ADD_P (pe5, "e5")
+ ADD_P (pe10, "e10")
+ ADD_P (pe20, "e20")
+ ADD_P (pe50, "e50")
+ ADD_P (pe100, "e100")
+ ADD_P (pe200, "e200")
+ ADD_P (pb, "b")
+ ADD_P (pp, "p")
+ ADD_P (ps, "s")
+ ADD_P (pv, "v")
+
+#undef ADD_p
+
+ g_ptr_array_add (array, NULL);
+ elements = (gchar **) g_ptr_array_free (array, FALSE);
+
+ p = g_strjoinv (",", elements);
+ g_strfreev (elements);
+
+ return p;
+}
+
+static gchar *
+get_a (InvestChart *chart)
+{
+ GPtrArray *array;
+ gchar **elements;
+ gchar *a;
+
+ array = g_ptr_array_new ();
+
+#define ADD_A(variable, value) \
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chart->variable))) \
+ g_ptr_array_add (array, g_strdup (value));
+
+ ADD_A (ar, "r14")
+ ADD_A (af, "f14")
+ ADD_A (ap, "p12")
+ ADD_A (aw, "w14")
+ ADD_A (am, "m26-12-9")
+ ADD_A (ass, "ss")
+ ADD_A (afs, "fs")
+ ADD_A (av, "v")
+ ADD_A (avm, "vm")
+
+#undef ADD_A
+
+ g_ptr_array_add (array, NULL);
+ elements = (gchar **) g_ptr_array_free (array, FALSE);
+
+ a = g_strjoinv (",", elements);
+ g_strfreev (elements);
+
+ return a;
+}
+
+static gboolean
+refresh_chart (InvestChart *chart)
+{
+ const gchar *text;
+ gchar *tickers;
+ gchar **symbols;
+ gchar *tail;
+ gchar *title;
+ const gchar *t, *q, *l;
+ gchar *s, *c, *p, *a;
+ gchar *url;
+
+ text = gtk_entry_get_text (GTK_ENTRY (chart->s));
+ tickers = g_strstrip (g_utf8_strup (text, -1));
+
+ if (!tickers || *tickers == '\0')
+ return TRUE;
+
+ symbols = g_strsplit (tickers, " ", -1);
+ g_free (tickers);
+
+ tail = g_strjoinv (" / ", symbols);
+ title = g_strdup_printf (_("Financial Chart - %s"), tail);
+ g_free (tail);
+
+ gtk_window_set_title (GTK_WINDOW (chart), title);
+ g_free (title);
+
+ t = gtk_combo_box_get_active_id (GTK_COMBO_BOX (chart->t));
+ q = gtk_combo_box_get_active_id (GTK_COMBO_BOX (chart->q));
+ l = gtk_combo_box_get_active_id (GTK_COMBO_BOX (chart->l));
+
+ s = g_strdup (symbols[0]);
+ c = g_strdup ("");
+
+ if (g_strv_length (symbols) > 1)
+ {
+ guint i;
+
+ for (i = 1; i < g_strv_length (symbols); i++)
+ {
+ gchar *tmp;
+
+ if (*c == '\0')
+ tmp = g_strdup (symbols[i]);
+ else
+ tmp = g_strdup_printf ("%s,%s", c, symbols[i]);
+
+ g_free (c);
+ c = tmp;
+ }
+ }
+
+ g_strfreev (symbols);
+
+ p = get_p (chart);
+ a = get_a (chart);
+
+ url = g_strdup_printf (CHART_URL, s, c, t, q, l, p, a);
+ g_free (s); g_free (c); g_free (p); g_free (a);
+
+ gtk_label_set_text (GTK_LABEL (chart->progress), _("Opening Chart"));
+ gtk_widget_show (chart->progress);
+
+ g_clear_object (&chart->retriever);
+ chart->retriever = invest_image_retriever_new (url);
+ g_free (url);
+
+ g_signal_connect (chart->retriever, "completed",
+ G_CALLBACK (completed_cb), chart);
+
+ invest_image_retriever_start (chart->retriever);
+
+ return TRUE;
+}
+
+static void
+autorefresh_toggled_cb (GtkToggleButton *togglebutton,
+ InvestChart *chart)
+{
+ gboolean active;
+
+ active = gtk_toggle_button_get_active (togglebutton);
+
+ if (!active && chart->autorefresh_id != 0)
+ {
+ g_source_remove (chart->autorefresh_id);
+ chart->autorefresh_id = 0;
+ }
+
+ if (active && chart->autorefresh_id == 0)
+ {
+ chart->autorefresh_id = g_timeout_add (AUTOREFRESH_TIMEOUT,
+ (GSourceFunc) refresh_chart,
+ chart);
+
+ refresh_chart (chart);
+ }
+}
+
+static void
+s_activate_cb (GtkEntry *entry,
+ InvestChart *chart)
+{
+ refresh_chart (chart);
+}
+
+static void
+s_changed_cb (GtkEditable *editable,
+ InvestChart *chart)
+{
+ refresh_chart (chart);
+}
+
+static void
+changed_cb (GtkComboBox *widget,
+ InvestChart *chart)
+{
+ refresh_chart (chart);
+}
+
+static void
+toggled_cb (GtkToggleButton *togglebutton,
+ InvestChart *chart)
+{
+ refresh_chart (chart);
+}
+
+static void
+invest_chart_constructed (GObject *object)
+{
+ InvestChart *chart;
+ GdkPixbuf *logo;
+
+ chart = INVEST_CHART (object);
+
+ G_OBJECT_CLASS (invest_chart_parent_class)->constructed (object);
+
+ gtk_window_set_title (GTK_WINDOW (chart), _("Financial Chart"));
+
+ logo = gdk_pixbuf_new_from_file_at_size (ARTDIR "/invest_neutral.svg", 96, 96, NULL);
+ gtk_image_set_from_pixbuf (GTK_IMAGE (chart->plot), logo);
+ g_clear_object (&logo);
+
+ autorefresh_toggled_cb (GTK_TOGGLE_BUTTON (chart->autorefresh), chart);
+}
+
+static void
+invest_chart_dispose (GObject *object)
+{
+ InvestChart *chart;
+
+ chart = INVEST_CHART (object);
+
+ if (chart->autorefresh_id != 0)
+ {
+ g_source_remove (chart->autorefresh_id);
+ chart->autorefresh_id = 0;
+ }
+
+ g_clear_object (&chart->retriever);
+
+ G_OBJECT_CLASS (invest_chart_parent_class)->dispose (object);
+}
+
+static void
+invest_chart_bind_template (GtkWidgetClass *widget_class)
+{
+ gchar *contents;
+ gsize length;
+ GBytes *bytes;
+
+ g_file_get_contents (BUILDERDIR "/financialchart.ui",
+ &contents, &length, NULL);
+
+ bytes = g_bytes_new_take (contents, length);
+ gtk_widget_class_set_template (widget_class, bytes);
+ g_bytes_unref (bytes);
+
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, s);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, autorefresh);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, plot);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, progress);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, t);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, q);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, l);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pm5);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pm10);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pm20);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pm50);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pm100);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pm200);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pe5);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pe10);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pe20);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pe50);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pe100);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pe200);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pb);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pp);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, ps);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, pv);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, ar);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, af);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, ap);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, aw);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, am);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, ass);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, afs);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, av);
+ gtk_widget_class_bind_template_child (widget_class, InvestChart, avm);
+
+ gtk_widget_class_bind_template_callback (widget_class, autorefresh_toggled_cb);
+ gtk_widget_class_bind_template_callback (widget_class, s_activate_cb);
+ gtk_widget_class_bind_template_callback (widget_class, s_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, toggled_cb);
+}
+
+static void
+invest_chart_class_init (InvestChartClass *chart_class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = G_OBJECT_CLASS (chart_class);
+ widget_class = GTK_WIDGET_CLASS (chart_class);
+
+ object_class->constructed = invest_chart_constructed;
+ object_class->dispose = invest_chart_dispose;
+
+ invest_chart_bind_template (widget_class);
+}
+
+static void
+invest_chart_init (InvestChart *chart)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (chart);
+
+ gtk_widget_init_template (widget);
+}
+
+void
+invest_chart_show_chart (const gchar *symbol)
+{
+ InvestChart *chart;
+
+ chart = g_object_new (INVEST_TYPE_CHART, NULL);
+
+ gtk_entry_set_text (GTK_ENTRY (chart->s), symbol);
+ gtk_window_present (GTK_WINDOW (chart));
+}
diff --git a/invest-applet/invest/invest-chart.h b/invest-applet/invest/invest-chart.h
new file mode 100644
index 0000000..e3ed87a
--- /dev/null
+++ b/invest-applet/invest/invest-chart.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_CHART_H
+#define INVEST_CHART_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_CHART invest_chart_get_type ()
+G_DECLARE_FINAL_TYPE (InvestChart, invest_chart, INVEST, CHART, GtkWindow)
+
+void invest_chart_show_chart (const gchar *symbol);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-currencies.c b/invest-applet/invest/invest-currencies.c
new file mode 100644
index 0000000..e14faa8
--- /dev/null
+++ b/invest-applet/invest/invest-currencies.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+#include "invest-currencies.h"
+
+const InvestCurrencies currencies[] =
+ {
+ { "BZD", "Belize Dollar" },
+ { "NLG", "Dutch Guilder" },
+ { "SLL", "Sierra Leone Leone" },
+ { "FRF", "French Franc" },
+ { "NGN", "Nigerian Naira" },
+ { "CRC", "Costa Rican Colon" },
+ { "LAK", "Laos Kip" },
+ { "CLP", "Chilean Peso" },
+ { "DZD", "Algerian Dinar" },
+ { "SZL", "Swaziland Lilangeni" },
+ { "MUR", "Mauritius Rupee" },
+ { "WST", "Western Samoa Tala" },
+ { "MMK", "Myanmar Kyat" },
+ { "IDR", "Indonesian Rupiah" },
+ { "GTQ", "Guatemala Quetzal" },
+ { "CAD", "Canadian Dollar" },
+ { "AWG", "Aruban Florin" },
+ { "TTD", "Trinidad Dollar" },
+ { "PKR", "Pakistani Rupee" },
+ { "XCD", "East Caribbean Dollar" },
+ { "VUV", "Vanuatu Vatu" },
+ { "XOF", "CFA Franc (BCEAO)" },
+ { "ROL", "Romanian Leu" },
+ { "KMF", "Comoros Franc" },
+ { "SIT", "Slovenian Tolar" },
+ { "VEB", "Venezuelan Bolivar" },
+ { "ANG", "Netherlands Antilles Guilder" },
+ { "MNT", "Mongolian Tugrik" },
+ { "LBP", "Lebanese Pound" },
+ { "KES", "Kenyan Shilling" },
+ { "BTN", "Bhutan Ngultrum" },
+ { "GBP", "British Pound" },
+ { "SEK", "Swedish Krona" },
+ { "ZMK", "Zambia Kwacha" },
+ { "SKK", "Slovak Koruna" },
+ { "DKK", "Danish Krone" },
+ { "AFA", "Afganistan Afghani" },
+ { "CYP", "Cypriot Pound" },
+ { "SCR", "Seychelles Rupee" },
+ { "FJD", "Fiji Dollar" },
+ { "SRG", "Surinam Guilder" },
+ { "SHP", "St. Helena Pound" },
+ { "ALL", "Albanian Lek" },
+ { "TOP", "Tonga Isl Pa'anga" },
+ { "UGX", "Ugandan Shilling" },
+ { "OMR", "Oman Rial" },
+ { "DJF", "Djibouti Franc" },
+ { "BND", "Brunei Dollar" },
+ { "TND", "Tunisian Dinar" },
+ { "PTE", "Portuguese Escudo" },
+ { "IEP", "Irish Punt" },
+ { "SBD", "Salomon Islands Dollar" },
+ { "GNF", "Guinea Franc" },
+ { "BOB", "Bolivian Boliviano" },
+ { "CVE", "Cape Verde Escudo" },
+ { "ARS", "Argentinian Peso" },
+ { "GMD", "Gambia Dalasi" },
+ { "ZWD", "Zimbabwean Dollar" },
+ { "MWK", "Malawi Kwacha" },
+ { "BDT", "Bangladesh Taka" },
+ { "GRD", "Greek Drachma" },
+ { "KWD", "Kuwaiti Dinar" },
+ { "EUR", "Euro" },
+ { "TRL", "Turkish Lira" },
+ { "CHF", "Swiss Franc" },
+ { "DOP", "Dominican Peso" },
+ { "PEN", "Peruvian Sol" },
+ { "SVC", "El Salvador Colon" },
+ { "SGD", "Singapore Dollar" },
+ { "TWD", "Taiwan New Dollar" },
+ { "USD", "US Dollar" },
+ { "BGN", "Bulgarian Lev" },
+ { "MAD", "Moroccan Dirham" },
+ { "SAR", "Saudi Arabian Riyal" },
+ { "AUD", "Australian Dollar" },
+ { "KYD", "Cayman Islands Dollar" },
+ { "GHC", "Ghanaian Cedi" },
+ { "KRW", "South Korean Won" },
+ { "GIP", "Gibraltar Pound" },
+ { "NAD", "Namibian Dollar" },
+ { "CZK", "Czech Koruna" },
+ { "JMD", "Jamaican Dollar" },
+ { "MXN", "Mexican Peso" },
+ { "BWP", "Botswana Pula" },
+ { "GYD", "Guyana Dollar" },
+ { "EGP", "Egyptian Pound" },
+ { "THB", "Thai Baht" },
+ { "AED", "United Arab Emirates Dirham" },
+ { "JPY", "Japanese Yen" },
+ { "JOD", "Jordanian Dinar" },
+ { "HRK", "Croatian Kuna" },
+ { "ZAR", "South African Rand" },
+ { "CUP", "Cuban Peso" },
+ { "BBD", "Barbados Dollar" },
+ { "PGK", "Papua New Guinea Kina" },
+ { "LKR", "Sri Lanka Rupee" },
+ { "BEF", "Belgian Franc" },
+ { "PLN", "Polish Zloty" },
+ { "MYR", "Malaysian Ringgit" },
+ { "FIM", "Finnish Markka" },
+ { "CNY", "Renmimbi Yuan" },
+ { "SDD", "Sudanese Dinar" },
+ { "LVL", "Latvian Lats" },
+ { "ITL", "Italian Lira" },
+ { "INR", "Indian Rupee" },
+ { "NIO", "Nicaraguan Cordoba" },
+ { "PHP", "Philippines Peso" },
+ { "HNL", "Honduras Lempira" },
+ { "HKD", "Hong Kong Dollar" },
+ { "NZD", "New Zealand Dollar" },
+ { "BRL", "Brazilian Real" },
+ { "MTL", "Maltese Pound" },
+ { "ATS", "Austrian Schilling" },
+ { "EEK", "Estonian Kroon" },
+ { "NOK", "Norwegian Krone" },
+ { "ISK", "Iceland Krona" },
+ { "ILS", "Israeli Shekel" },
+ { "LSL", "Lesotho Loti" },
+ { "HUF", "Hungarian Forint" },
+ { "ESP", "Spanish Peseta" },
+ { "UAH", "Ukraine Hryvnia" },
+ { "RUB", "Russian Ruble" },
+ { "BMD", "Bermuda Dollar" },
+ { "MVR", "Maldives Rufiyan" },
+ { "QAR", "Qatari Rial" },
+ { "VND", "Vietnam Dong" },
+ { "MRO", "Mauritania Ouguiya" },
+ { "MZM", "Mozambique Metical" },
+ { "NPR", "Nepal Rupee" },
+ { "COP", "Colombian Peso" },
+ { "TZS", "Tanzanian Shilling" },
+ { "MGF", "Malagasy Franc" },
+ { "KHR", "Cambodian Riel" },
+ { "SYP", "Syria Pound" },
+ { "HTG", "Haitian Gourde" },
+ { "DEM", "German Mark" },
+ { "BHD", "Bahraini Dinar" },
+ { "XAF", "CFA Franc(BEAC)" },
+ { "STD", "Sao Tome & Principe Dobra" },
+ { "LTL", "Lithuanian Litas" },
+ { "ETB", "Ethiopian Birr" },
+ { "XPF", "CFP Franc" },
+ { NULL, NULL }
+ };
+
+gboolean
+invest_currencies_contains (const gchar *currency)
+{
+ guint i;
+
+ if (currency == NULL)
+ return FALSE;
+
+ for (i = 0; currencies[i].code != NULL; i++)
+ {
+ if (g_strcmp0 (currencies[i].code, currency) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/invest-applet/invest/invest-currencies.h b/invest-applet/invest/invest-currencies.h
new file mode 100644
index 0000000..3c125c1
--- /dev/null
+++ b/invest-applet/invest/invest-currencies.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_CURRENCIES_H
+#define INVEST_CURRENCIES_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ const gchar *code;
+ const gchar *label;
+} InvestCurrencies;
+
+extern const InvestCurrencies currencies[];
+
+gboolean invest_currencies_contains (const gchar *currency);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-image-retriever.c b/invest-applet/invest/invest-image-retriever.c
new file mode 100644
index 0000000..4927bb6
--- /dev/null
+++ b/invest-applet/invest/invest-image-retriever.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "invest-image-retriever.h"
+
+#define CHUNK_SIZE 512 * 1024
+
+struct _InvestImageRetriever
+{
+ GObject parent;
+
+ GCancellable *cancellable;
+
+ gchar *url;
+
+ guint8 *chunk;
+ GByteArray *array;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_URL,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+enum
+{
+ COMPLETED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (InvestImageRetriever, invest_image_retriever, G_TYPE_OBJECT)
+
+static void
+download_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GdkPixbuf *pixbuf;
+ InvestImageRetriever *retriever;
+
+ error = NULL;
+ pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ retriever = INVEST_IMAGE_RETRIEVER (user_data);
+
+ if (error != NULL)
+ {
+ g_signal_emit (retriever, signals[COMPLETED], 0, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_signal_emit (retriever, signals[COMPLETED], 0, pixbuf);
+ g_object_unref (pixbuf);
+}
+
+static void
+ready_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GFileInputStream *stream;
+ InvestImageRetriever *retriever;
+
+ error = NULL;
+ stream = g_file_read_finish (G_FILE (object), res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ retriever = INVEST_IMAGE_RETRIEVER (user_data);
+
+ if (error != NULL)
+ {
+ g_signal_emit (retriever, signals[COMPLETED], 0, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ gdk_pixbuf_new_from_stream_async (G_INPUT_STREAM (stream),
+ retriever->cancellable,
+ download_cb, retriever);
+
+ g_object_unref (stream);
+}
+
+static void
+invest_image_retriever_dispose (GObject *object)
+{
+ InvestImageRetriever *retriever;
+
+ retriever = INVEST_IMAGE_RETRIEVER (object);
+
+ if (retriever->cancellable != NULL)
+ {
+ g_cancellable_cancel (retriever->cancellable);
+ g_clear_object (&retriever->cancellable);
+ }
+
+ G_OBJECT_CLASS (invest_image_retriever_parent_class)->dispose (object);
+}
+
+static void
+invest_image_retriever_finalize (GObject *object)
+{
+ InvestImageRetriever *retriever;
+
+ retriever = INVEST_IMAGE_RETRIEVER (object);
+
+ g_free (retriever->url);
+
+ g_free (retriever->chunk);
+ g_byte_array_free (retriever->array, TRUE);
+
+ G_OBJECT_CLASS (invest_image_retriever_parent_class)->finalize (object);
+}
+
+static void
+invest_image_retriever_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ InvestImageRetriever *retriever;
+
+ retriever = INVEST_IMAGE_RETRIEVER (object);
+
+ switch (property_id)
+ {
+ case PROP_URL:
+ retriever->url = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+invest_image_retriever_install_properties (GObjectClass *object_class)
+{
+ properties[PROP_URL] =
+ g_param_spec_string ("url", "url", "url", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+invest_image_retriever_install_signals (GObjectClass *object_class)
+{
+ signals[COMPLETED] =
+ g_signal_new ("completed", INVEST_TYPE_IMAGE_RETRIEVER, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GDK_TYPE_PIXBUF);
+}
+
+static void
+invest_image_retriever_class_init (InvestImageRetrieverClass *retriever_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (retriever_class);
+
+ object_class->dispose = invest_image_retriever_dispose;
+ object_class->finalize = invest_image_retriever_finalize;
+ object_class->set_property = invest_image_retriever_set_property;
+
+ invest_image_retriever_install_properties (object_class);
+ invest_image_retriever_install_signals (object_class);
+}
+
+static void
+invest_image_retriever_init (InvestImageRetriever *retriever)
+{
+ retriever->cancellable = g_cancellable_new ();
+
+ retriever->chunk = g_malloc0 (CHUNK_SIZE + 1);
+ retriever->array = g_byte_array_new ();
+}
+
+InvestImageRetriever *
+invest_image_retriever_new (const gchar *url)
+{
+ return g_object_new (INVEST_TYPE_IMAGE_RETRIEVER, "url", url, NULL);
+}
+
+void
+invest_image_retriever_start (InvestImageRetriever *retriever)
+{
+ GFile *file;
+
+ file = g_file_new_for_uri (retriever->url);
+ g_file_read_async (file, 0, retriever->cancellable, ready_cb, retriever);
+ g_object_unref (file);
+}
+
+const gchar *
+invest_image_retriever_get_url (InvestImageRetriever *retriever)
+{
+ return retriever->url;
+}
diff --git a/invest-applet/invest/invest-image-retriever.h b/invest-applet/invest/invest-image-retriever.h
new file mode 100644
index 0000000..402bc90
--- /dev/null
+++ b/invest-applet/invest/invest-image-retriever.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_IMAGE_RETRIEVER_H
+#define INVEST_IMAGE_RETRIEVER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_IMAGE_RETRIEVER invest_image_retriever_get_type ()
+G_DECLARE_FINAL_TYPE (InvestImageRetriever, invest_image_retriever,
+ INVEST, IMAGE_RETRIEVER, GObject)
+
+InvestImageRetriever *invest_image_retriever_new (const gchar *url);
+
+void invest_image_retriever_start (InvestImageRetriever *retriever);
+
+const gchar *invest_image_retriever_get_url (InvestImageRetriever *retriever);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-preferences.c b/invest-applet/invest/invest-preferences.c
new file mode 100644
index 0000000..bf8ac2b
--- /dev/null
+++ b/invest-applet/invest/invest-preferences.c
@@ -0,0 +1,1137 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include "invest-currencies.h"
+#include "invest-preferences.h"
+
+struct _InvestPreferences
+{
+ GtkDialog parent;
+
+ GSettings *settings;
+ gulong settings_changed_id;
+ gboolean ignore_changed_signal;
+
+ GtkWidget *explanation;
+
+ GtkTreeStore *store;
+ GtkWidget *stocks;
+
+ GtkWidget *indexexpansion;
+ GtkWidget *hidecharts;
+
+ GtkWidget *currency;
+ gchar *currency_code;
+};
+
+enum
+{
+ COL_SYMBOL,
+ COL_LABEL,
+ COL_AMOUNT,
+ COL_PRICE,
+ COL_COMMISION,
+ COL_CURRENCY_RATE,
+
+ NUM_COLS
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_SETTINGS,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+G_DEFINE_TYPE (InvestPreferences, invest_preferences, GTK_TYPE_DIALOG)
+
+static gchar *
+format_currency (const gchar *label,
+ const gchar *code)
+{
+ if (code == NULL)
+ return g_strdup (label);
+
+ return g_strdup_printf ("%s (%s)", label, code);
+}
+
+static gboolean
+is_group (InvestPreferences *preferences,
+ GtkTreeIter *iter)
+{
+ gchar *label;
+ gboolean is_group;
+
+ label = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (preferences->store), iter, 1, &label, -1);
+
+ is_group = label == NULL ? TRUE : FALSE;
+ g_free (label);
+
+ return is_group;
+}
+
+static gboolean
+is_stock (InvestPreferences *preferences,
+ GtkTreeIter *iter)
+{
+ return !is_group (preferences, iter);
+}
+
+static GVariant *
+get_stocks (InvestPreferences *preferences,
+ GtkTreeModel *model,
+ GtkTreeIter *parent)
+{
+ GVariantBuilder builder;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{bv}"));
+
+ valid = gtk_tree_model_iter_children (model, &iter, parent);
+
+ while (valid)
+ {
+ if (is_group (preferences, &iter))
+ {
+ gchar *name;
+
+ gtk_tree_model_get (model, &iter, COL_SYMBOL, &name, -1);
+
+ g_variant_builder_add (&builder, "{bv}", TRUE,
+ g_variant_new ("(sv)", name,
+ get_stocks (preferences,
+ model, &iter)));
+
+ g_free (name);
+ }
+ else
+ {
+ gchar *symbol;
+ gchar *label;
+ gdouble amount;
+ gdouble price;
+ gdouble comission;
+ gdouble currency_rate;
+
+ gtk_tree_model_get (model, &iter,
+ COL_SYMBOL, &symbol,
+ COL_LABEL, &label,
+ COL_AMOUNT, &amount,
+ COL_PRICE, &price,
+ COL_COMMISION, &comission,
+ COL_CURRENCY_RATE, ¤cy_rate,
+ -1);
+
+ g_variant_builder_add (&builder, "{bv}", FALSE,
+ g_variant_new ("(ssdddd)", symbol, label,
+ amount, price, comission,
+ currency_rate));
+
+ g_free (label);
+ g_free (symbol);
+ }
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ return g_variant_builder_end (&builder);
+}
+
+static void
+save_stocks (InvestPreferences *preferences)
+{
+ GtkTreeModel *model;
+ GVariant *stocks;
+
+ model = GTK_TREE_MODEL (preferences->store);
+ stocks = get_stocks (preferences, model, NULL);
+
+ preferences->ignore_changed_signal = TRUE;
+ g_settings_set_value (preferences->settings, "stocks", stocks);
+ preferences->ignore_changed_signal = FALSE;
+}
+
+static void
+add_clicked_cb (InvestPreferences *preferences,
+ gboolean group,
+ gboolean save)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+ GtkTreeStore *store;
+ GtkTreeModel *model;
+ gboolean has_parent;
+ GtkTreeIter parent;
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+
+ tree_view = GTK_TREE_VIEW (preferences->stocks);
+ selection = gtk_tree_view_get_selection (tree_view);
+ store = preferences->store;
+ has_parent = FALSE;
+ path = NULL;
+ column = NULL;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &parent))
+ {
+ GtkTreeIter iter;
+
+ has_parent = TRUE;
+
+ while (has_parent && is_stock (preferences, &parent))
+ {
+ has_parent = gtk_tree_model_iter_parent (model, &iter, &parent);
+
+ if (has_parent)
+ {
+ parent = iter;
+ }
+ }
+ }
+
+ if (group == TRUE)
+ {
+ GtkTreeIter iter;
+
+ gtk_tree_store_append (store, &iter, has_parent ? &parent : NULL);
+ gtk_tree_store_set (store, &iter, COL_SYMBOL, _("Stock Group"), -1);
+
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
+ add_clicked_cb (preferences, FALSE, FALSE);
+
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+ }
+ else
+ {
+ GtkTreeIter iter;
+
+ gtk_tree_store_append (store, &iter, has_parent ? &parent : NULL);
+ gtk_tree_store_set (store, &iter,
+ COL_SYMBOL, "YHOO",
+ COL_LABEL, "Yahoo! Inc.",
+ COL_AMOUNT, 0.0,
+ COL_PRICE, 0.0,
+ COL_COMMISION, 0.0,
+ COL_CURRENCY_RATE, 0.0,
+ -1);
+
+ if (has_parent)
+ {
+ path = gtk_tree_model_get_path (model, &parent);
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+ }
+
+ path = gtk_tree_model_get_path (model, &iter);
+ }
+
+ column = gtk_tree_view_get_column (tree_view, COL_SYMBOL);
+ gtk_tree_view_set_cursor (tree_view, path, column, TRUE);
+
+ if (save == TRUE)
+ {
+ save_stocks (preferences);
+ }
+}
+
+static void
+addstock_clicked_cb (GtkButton *button,
+ InvestPreferences *preferences)
+{
+ add_clicked_cb (preferences, FALSE, TRUE);
+}
+
+static void
+addgroup_clicked_cb (GtkButton *button,
+ InvestPreferences *preferences)
+{
+ add_clicked_cb (preferences, TRUE, TRUE);
+}
+
+static void
+remove_clicked_cb (GtkButton *button,
+ InvestPreferences *preferences)
+{
+ gboolean removed;
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+ GList *selected_rows;
+ GtkTreeModel *model;
+ GList *row;
+
+ removed = FALSE;
+ tree_view = GTK_TREE_VIEW (preferences->stocks);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (row = selected_rows; row != NULL; row = row->next)
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = (GtkTreePath *) row->data;
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ continue;
+
+ if (gtk_tree_model_iter_n_children (model, &iter) > 0)
+ {
+ GtkDialogFlags flags;
+ GtkWidget *dialog;
+ GtkWidget *content_area;
+ const gchar *text;
+ GtkWidget *label;
+ gint response;
+
+ flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
+
+ /* Translators: Asks the user to confirm deletion of a group of stocks */
+ dialog = gtk_dialog_new_with_buttons (_("Delete entire stock group?"),
+ GTK_WINDOW (preferences), flags,
+ _("_Cancel"), GTK_RESPONSE_REJECT,
+ _("_OK"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ gtk_widget_set_margin_start (content_area, 10);
+ gtk_widget_set_margin_end (content_area, 10);
+ gtk_widget_set_margin_bottom (content_area, 10);
+
+ /* Translators: stocks can be grouped together into a "stock group".
+ * The user wants to delete a group, but the group still contains
+ * stocks. By deleting the group, also all stocks will be removed
+ * from configuration.
+ */
+ text = _("This removes all stocks contained in this stock group!\n"
+ "Do you really want to remove this stock group?");
+ label = gtk_label_new (text);
+
+ gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 10);
+ gtk_widget_show (label);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ if (response == GTK_RESPONSE_REJECT)
+ continue;
+ }
+
+ gtk_tree_store_remove (preferences->store, &iter);
+ removed = TRUE;
+ }
+
+ g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
+
+ if (removed == TRUE)
+ {
+ save_stocks (preferences);
+ }
+}
+
+static gboolean
+stocks_key_press_event_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ InvestPreferences *preferences)
+{
+ if (event->keyval == 65535)
+ remove_clicked_cb (NULL, preferences);
+
+ return FALSE;
+}
+
+typedef struct
+{
+ InvestPreferences *preferences;
+ gint column;
+ GType type;
+} CellData;
+
+static CellData *
+cell_data_new (InvestPreferences *preferences,
+ gint column,
+ GType type)
+{
+ CellData *data;
+
+ data = g_new0 (CellData, 1);
+
+ data->preferences = preferences;
+ data->column = column;
+ data->type = type;
+
+ return data;
+}
+
+static void
+cell_data_free (gpointer data,
+ GClosure *closure)
+{
+ g_free (data);
+}
+
+static void
+edited_cb (GtkCellRendererText *renderer,
+ gchar *path,
+ gchar *new_text,
+ CellData *data)
+{
+ InvestPreferences *preferences;
+ GtkTreeStore *store;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ preferences = data->preferences;
+ store = preferences->store;
+ model = GTK_TREE_MODEL (store);
+
+ if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
+ return;
+
+ if (data->column != 0 && is_group (preferences, &iter))
+ return;
+
+ if (data->column == 0 && is_stock (preferences, &iter))
+ {
+ gchar *text;
+
+ text = g_utf8_strup (new_text, -1);
+
+ if (text != NULL && text[0] != '\0')
+ gtk_tree_store_set (store, &iter, data->column, text, -1);
+ g_free (text);
+ }
+ else if (data->column < 2)
+ {
+ if (new_text != NULL && new_text[0] != '\0')
+ gtk_tree_store_set (store, &iter, data->column, new_text, -1);
+ }
+ else
+ {
+ gdouble value;
+
+ value = g_ascii_strtod (new_text, NULL);
+
+ gtk_tree_store_set (store, &iter, data->column, value, -1);
+ }
+
+ save_stocks (preferences);
+}
+
+static void
+cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ CellData *cell_data;
+ InvestPreferences *preferences;
+
+ cell_data = (CellData *) data;
+ preferences = cell_data->preferences;
+
+ if (is_group (preferences, iter))
+ {
+ if (cell_data->column == 0)
+ {
+ gchar *text;
+ gchar *markup;
+
+ gtk_tree_model_get (tree_model, iter, cell_data->column, &text, -1);
+
+ markup = g_strdup_printf ("<b>%s</b>", text);
+ g_free (text);
+
+ g_object_set (cell, "markup", markup, NULL);
+ g_free (markup);
+ }
+ else
+ {
+ g_object_set (cell, "text", "", NULL);
+ }
+ }
+ else
+ {
+ gchar *text;
+
+ if (cell_data->type == G_TYPE_DOUBLE)
+ {
+ gdouble val;
+
+ gtk_tree_model_get (tree_model, iter, cell_data->column, &val, -1);
+
+ text = g_strdup_printf ("%.2f", val);
+ }
+ else
+ {
+ gtk_tree_model_get (tree_model, iter, cell_data->column, &text, -1);
+ }
+
+ g_object_set (cell, "text", text, NULL);
+ g_free (text);
+ }
+}
+
+static void
+create_cell (InvestPreferences *preferences,
+ GtkTreeView *tree_view,
+ gint column,
+ const gchar *name,
+ GType type)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *view_column;
+ CellData *data;
+
+ renderer = gtk_cell_renderer_text_new ();
+ view_column = gtk_tree_view_column_new_with_attributes (name, renderer,
+ "text", column,
+ NULL);
+
+ if (type == G_TYPE_DOUBLE)
+ gtk_cell_renderer_set_alignment (renderer, 1.0, 0.5);
+
+ g_object_set (renderer, "editable", TRUE, NULL);
+
+ data = cell_data_new (preferences, column, type);
+ g_signal_connect_data (renderer, "edited", G_CALLBACK (edited_cb),
+ data, cell_data_free, G_CONNECT_AFTER);
+
+ if (type == G_TYPE_STRING)
+ gtk_tree_view_column_set_sort_column_id (view_column, column);
+
+ data = cell_data_new (preferences, column, type);
+ gtk_tree_view_column_set_cell_data_func (view_column, renderer,
+ cell_data_func, data, g_free);
+
+ gtk_tree_view_append_column (tree_view, view_column);
+}
+
+static void
+add_exchange_column (InvestPreferences *preferences)
+{
+ GtkTreeView *tree_view;
+
+ tree_view = GTK_TREE_VIEW (preferences->stocks);
+
+ if (gtk_tree_view_get_n_columns (tree_view) != NUM_COLS)
+ {
+ create_cell (preferences, tree_view,
+ COL_CURRENCY_RATE, _("Currency Rate"), G_TYPE_DOUBLE);
+ }
+}
+
+static void
+remove_exchange_column (InvestPreferences *preferences)
+{
+ GtkTreeView *tree_view;
+
+ tree_view = GTK_TREE_VIEW (preferences->stocks);
+
+ if (gtk_tree_view_get_n_columns (tree_view) == NUM_COLS)
+ {
+ GtkTreeViewColumn *column;
+
+ column = gtk_tree_view_get_column (tree_view, COL_CURRENCY_RATE);
+
+ gtk_tree_view_remove_column (tree_view, column);
+ }
+}
+
+static void
+pick_currency (InvestPreferences *preferences,
+ const gchar *currency,
+ gboolean save)
+{
+ gchar *code;
+ gchar *label;
+
+ code = g_strdup (currency);
+
+ if (code == NULL)
+ {
+ label = g_strdup ("");
+ remove_exchange_column (preferences);
+ }
+ else
+ {
+ gint i;
+
+ for (i = 0; currencies[i].code != NULL; i++)
+ {
+ if (g_strcmp0 (code, currencies[i].code) == 0)
+ {
+ label = format_currency (currencies[i].label, code);
+ break;
+ }
+ }
+
+ if (label == NULL)
+ label = g_strdup ("");
+
+ add_exchange_column (preferences);
+ }
+
+ g_free (preferences->currency_code);
+ preferences->currency_code = code;
+
+ gtk_entry_set_text (GTK_ENTRY (preferences->currency), label);
+ g_free (label);
+
+ if (save)
+ {
+ g_settings_set_string (preferences->settings, "currency",
+ code != NULL ? code : "");
+ }
+}
+
+static void
+match_currency (InvestPreferences *preferences)
+{
+ GtkEntry *entry;
+ gchar *text;
+ guint16 length;
+ gint i;
+
+ entry = GTK_ENTRY (preferences->currency);
+ length = gtk_entry_get_text_length (entry);
+
+ if (length == 0)
+ {
+ pick_currency (preferences, NULL, TRUE);
+
+ return;
+ }
+ else if (length == 3)
+ {
+ text = g_utf8_strup (gtk_entry_get_text (entry), -1);
+
+ for (i = 0; currencies[i].code != NULL; i++)
+ {
+ if (g_strcmp0 (text, currencies[i].code) == 0)
+ {
+ pick_currency (preferences, text, TRUE);
+ g_free (text);
+
+ return;
+ }
+ }
+
+ g_free (text);
+ }
+ else
+ {
+ text = g_utf8_strup (gtk_entry_get_text (entry), -1);
+
+ for (i = 0; currencies[i].code != NULL; i++)
+ {
+ gchar *label;
+ gchar *formatted;
+ gboolean found;
+
+ label = g_utf8_strup (currencies[i].label, -1);
+ formatted = format_currency (label, currencies[i].code);
+ found = FALSE;
+
+ if (g_strcmp0 (label, text) == 0 ||
+ g_strcmp0 (formatted, text) == 0)
+ {
+ pick_currency (preferences, currencies[i].code, TRUE);
+ found = TRUE;
+ }
+
+ g_free (formatted);
+ g_free (label);
+
+ if (found)
+ {
+ g_free (text);
+ return;
+ }
+ }
+
+ g_free (text);
+ }
+
+ pick_currency (preferences, preferences->currency_code, TRUE);
+}
+
+static gboolean
+currency_focus_out_event_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ InvestPreferences *preferences)
+{
+ match_currency (preferences);
+
+ return FALSE;
+}
+
+static void
+currency_activate_cb (GtkEntry *entry,
+ InvestPreferences *preferences)
+{
+ match_currency (preferences);
+}
+
+static void
+prefs_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GdkScreen *screen;
+
+ screen = gdk_screen_get_default ();
+
+ switch (response_id)
+ {
+ case 1:
+ gtk_show_uri (screen, "help:invest-applet/invest-applet-usage",
+ GDK_CURRENT_TIME, NULL);
+ break;
+
+ default:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+ }
+}
+
+static void
+setup_stocks (InvestPreferences *preferences)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSortable *sortable;
+
+ tree_view = GTK_TREE_VIEW (preferences->stocks);
+
+ preferences->store = gtk_tree_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_DOUBLE, G_TYPE_DOUBLE,
+ G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ sortable = GTK_TREE_SORTABLE (preferences->store);
+ gtk_tree_sortable_set_sort_column_id (sortable, 0, GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (preferences->store));
+
+ create_cell (preferences, tree_view, COL_SYMBOL, _("Symbol"), G_TYPE_STRING);
+ create_cell (preferences, tree_view, COL_LABEL, _("Label"), G_TYPE_STRING);
+ create_cell (preferences, tree_view, COL_AMOUNT, _("Amount"), G_TYPE_DOUBLE);
+ create_cell (preferences, tree_view, COL_PRICE, _("Price"), G_TYPE_DOUBLE);
+ create_cell (preferences, tree_view, COL_COMMISION, _("Commission"), G_TYPE_DOUBLE);
+
+ if (preferences->currency_code != NULL)
+ add_exchange_column (preferences);
+}
+
+static gboolean
+match_selected_cb (GtkEntryCompletion *widget,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ InvestPreferences *preferences)
+{
+ GValue value = G_VALUE_INIT;
+
+ gtk_tree_model_get_value (model, iter, 1, &value);
+
+ pick_currency (preferences, g_value_get_string (&value), TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+match_func (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GValue value = G_VALUE_INIT;
+ GtkTreeModel *model;
+ gchar *label;
+ gchar **tokens;
+ gchar **words;
+ gboolean match;
+ gint i;
+
+ model = gtk_entry_completion_get_model (completion);
+ gtk_tree_model_get_value (model, iter, 0, &value);
+
+ label = g_utf8_strdown (g_value_get_string (&value), -1);
+ tokens = g_strsplit (label, " ", -1);
+ g_free (label);
+
+ words = g_strsplit (key, " ", -1);
+ match = TRUE;
+
+ for (i = 0; words[i] != NULL; i++)
+ {
+ gboolean found;
+ gint j;
+
+ found = FALSE;
+
+ for (j = 0; tokens[j] != NULL; j++)
+ {
+ const gchar *token;
+
+ token = tokens[j];
+ if (g_str_has_prefix (token, "("))
+ token++;
+
+ if (g_str_has_prefix (token, words[i]))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found == FALSE)
+ {
+ match = FALSE;
+ break;
+ }
+ }
+
+ g_strfreev (tokens);
+ g_strfreev (words);
+
+ return match;
+}
+
+static void
+setup_completion (InvestPreferences *preferences)
+{
+ GtkListStore *store;
+ GtkEntryCompletion *completion;
+ gint i;
+
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+ completion = gtk_entry_completion_new ();
+
+ for (i = 0; currencies[i].code != NULL; i++)
+ {
+ gchar *formatted;
+ GtkTreeIter iter;
+
+ formatted = format_currency (currencies[i].label, currencies[i].code);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, formatted,
+ 1, currencies[i].code, -1);
+
+ g_free (formatted);
+ }
+
+ g_signal_connect (completion, "match-selected",
+ G_CALLBACK (match_selected_cb), preferences);
+
+ gtk_entry_completion_set_match_func (completion, match_func, NULL, NULL);
+ gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (store));
+ gtk_entry_completion_set_text_column (completion, 0);
+ gtk_entry_set_completion (GTK_ENTRY (preferences->currency), completion);
+}
+
+static void
+add_to_store (GtkTreeStore *store,
+ GVariant *stocks,
+ GtkTreeIter *parent)
+{
+ GVariantIter iter;
+ gboolean group;
+ GVariant *variant;
+
+ g_variant_iter_init (&iter, stocks);
+ while (g_variant_iter_loop (&iter, "{bv}", &group, &variant))
+ {
+ if (group == TRUE)
+ {
+ const gchar *name;
+ GVariant *list;
+ GtkTreeIter row;
+
+ g_variant_get (variant, "(&sv)", &name, &list);
+
+ gtk_tree_store_append (store, &row, parent);
+ gtk_tree_store_set (store, &row, COL_SYMBOL, name, -1);
+
+ add_to_store (store, list, &row);
+ g_variant_unref (list);
+ }
+ else
+ {
+ const gchar *symbol;
+ const gchar *label;
+ gdouble amount;
+ gdouble price;
+ gdouble comission;
+ gdouble currency_rate;
+ GtkTreeIter row;
+
+ g_variant_get (variant, "(&s&sdddd)", &symbol, &label,
+ &amount, &price, &comission, ¤cy_rate);
+
+ gtk_tree_store_append (store, &row, parent);
+ gtk_tree_store_set (store, &row,
+ COL_SYMBOL, symbol,
+ COL_LABEL, label,
+ COL_AMOUNT, amount,
+ COL_PRICE, price,
+ COL_COMMISION, comission,
+ COL_CURRENCY_RATE, currency_rate,
+ -1);
+ }
+ }
+}
+
+static void
+settings_changed_cb (GSettings *settings,
+ const gchar *key,
+ InvestPreferences *preferences)
+{
+ if (preferences->ignore_changed_signal == TRUE)
+ return;
+
+ if (key == NULL || g_strcmp0 (key, "currency") == 0)
+ {
+ gboolean found;
+ gchar *currency;
+ gint i;
+
+ found = FALSE;
+ currency = g_settings_get_string (settings, "currency");
+
+ for (i = 0; currencies[i].code != NULL; i++)
+ {
+ if (g_strcmp0 (currency, currencies[i].code) == 0)
+ {
+ found = TRUE;
+ pick_currency (preferences, currency, FALSE);
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ pick_currency (preferences, NULL, FALSE);
+ }
+
+ g_free (currency);
+ }
+
+ if (key == NULL || g_strcmp0 (key, "stocks") == 0)
+ {
+ GVariant *stocks;
+
+ stocks = g_settings_get_value (preferences->settings, "stocks");
+
+ gtk_tree_store_clear (preferences->store);
+ add_to_store (preferences->store, stocks, NULL);
+ g_variant_unref (stocks);
+ }
+}
+
+static void
+invest_preferences_constructed (GObject *object)
+{
+ InvestPreferences *preferences;
+
+ preferences = INVEST_PREFERENCES (object);
+
+ G_OBJECT_CLASS (invest_preferences_parent_class)->constructed (object);
+
+ setup_stocks (preferences);
+ setup_completion (preferences);
+
+ preferences->settings_changed_id =
+ g_signal_connect (preferences->settings, "changed",
+ G_CALLBACK (settings_changed_cb), preferences);
+
+ g_settings_bind (preferences->settings, "indexexpansion",
+ preferences->indexexpansion, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (preferences->settings, "hidecharts",
+ preferences->hidecharts, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ settings_changed_cb (preferences->settings, NULL, preferences);
+}
+
+static void
+invest_preferences_dispose (GObject *object)
+{
+ InvestPreferences *prefs;
+
+ prefs = INVEST_PREFERENCES (object);
+
+ if (prefs->settings_changed_id > 0)
+ {
+ g_signal_handler_disconnect (prefs->settings, prefs->settings_changed_id);
+ prefs->settings_changed_id = 0;
+ }
+
+ g_clear_object (&prefs->store);
+ g_clear_object (&prefs->settings);
+
+ G_OBJECT_CLASS (invest_preferences_parent_class)->dispose (object);
+}
+
+static void
+invest_preferences_finalize (GObject *object)
+{
+ InvestPreferences *preferences;
+
+ preferences = INVEST_PREFERENCES (object);
+
+ g_free (preferences->currency_code);
+
+ G_OBJECT_CLASS (invest_preferences_parent_class)->finalize (object);
+}
+
+static void
+invest_preferences_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ InvestPreferences *preferences;
+
+ preferences = INVEST_PREFERENCES (object);
+
+ switch (property_id)
+ {
+ case PROP_SETTINGS:
+ preferences->settings = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+invest_preferences_install_properties (GObjectClass *object_class)
+{
+ properties[PROP_SETTINGS] =
+ g_param_spec_object ("settings", "settings", "settings", G_TYPE_SETTINGS,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+invest_preferences_bind_template (GtkWidgetClass *widget_class)
+{
+ gchar *contents;
+ gsize length;
+ GBytes *bytes;
+
+ g_file_get_contents (BUILDERDIR "/prefs-dialog.ui", &contents, &length, NULL);
+
+ bytes = g_bytes_new_take (contents, length);
+ gtk_widget_class_set_template (widget_class, bytes);
+ g_bytes_unref (bytes);
+
+ gtk_widget_class_bind_template_child (widget_class, InvestPreferences, explanation);
+
+ gtk_widget_class_bind_template_callback (widget_class, addstock_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, addgroup_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, remove_clicked_cb);
+
+ gtk_widget_class_bind_template_child (widget_class, InvestPreferences, stocks);
+ gtk_widget_class_bind_template_callback (widget_class, stocks_key_press_event_cb);
+
+ gtk_widget_class_bind_template_child (widget_class, InvestPreferences, indexexpansion);
+ gtk_widget_class_bind_template_child (widget_class, InvestPreferences, hidecharts);
+
+ gtk_widget_class_bind_template_child (widget_class, InvestPreferences, currency);
+ gtk_widget_class_bind_template_callback (widget_class, currency_focus_out_event_cb);
+ gtk_widget_class_bind_template_callback (widget_class, currency_activate_cb);
+
+ gtk_widget_class_bind_template_callback (widget_class, prefs_dialog_response_cb);
+}
+
+static void
+invest_preferences_class_init (InvestPreferencesClass *preferences_class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = G_OBJECT_CLASS (preferences_class);
+ widget_class = GTK_WIDGET_CLASS (preferences_class);
+
+ object_class->constructed = invest_preferences_constructed;
+ object_class->dispose = invest_preferences_dispose;
+ object_class->finalize = invest_preferences_finalize;
+ object_class->set_property = invest_preferences_set_property;
+
+ invest_preferences_install_properties (object_class);
+ invest_preferences_bind_template (widget_class);
+}
+
+static void
+invest_preferences_init (InvestPreferences *preferences)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (preferences);
+
+ gtk_widget_init_template (widget);
+}
+
+GtkWidget *
+invest_preferences_new (GSettings *settings)
+{
+ return g_object_new (INVEST_TYPE_PREFERENCES,
+ "settings", settings,
+ NULL);
+}
+
+void
+invest_preferences_set_explanation (InvestPreferences *preferences,
+ const gchar *explanation)
+{
+ if (explanation == NULL)
+ {
+ gtk_widget_hide (preferences->explanation);
+ }
+ else
+ {
+ gtk_label_set_markup (GTK_LABEL (preferences->explanation), explanation);
+ gtk_widget_show (preferences->explanation);
+ }
+}
diff --git a/invest-applet/invest/invest-preferences.h b/invest-applet/invest/invest-preferences.h
new file mode 100644
index 0000000..fe96da1
--- /dev/null
+++ b/invest-applet/invest/invest-preferences.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_PREFERENCES_H
+#define INVEST_PREFERENCES_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_PREFERENCES invest_preferences_get_type ()
+G_DECLARE_FINAL_TYPE (InvestPreferences, invest_preferences,
+ INVEST, PREFERENCES, GtkDialog)
+
+GtkWidget *invest_preferences_new (GSettings *settings);
+
+void invest_preferences_set_explanation (InvestPreferences *preferences,
+ const gchar *explanation);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-quotes-retriever.c b/invest-applet/invest/invest-quotes-retriever.c
new file mode 100644
index 0000000..e25a4d4
--- /dev/null
+++ b/invest-applet/invest/invest-quotes-retriever.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include "invest-quotes-retriever.h"
+
+#define QUOTES_URL "http://finance.yahoo.com/d/quotes.csv?s=%s&f=snc4l1d1t1c1ohgv"
+#define CHUNK_SIZE 512 * 1024
+
+struct _InvestQuotesRetriever
+{
+ GObject parent;
+
+ GCancellable *cancellable;
+
+ gchar *symbol;
+ gchar *filename;
+
+ guint8 *chunk;
+ GByteArray *array;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_SYMBOL,
+ PROP_FILENAME,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+enum
+{
+ COMPLETED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (InvestQuotesRetriever, invest_quotes_retriever, G_TYPE_OBJECT)
+
+static void
+write_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ gsize bytes_written;
+ gboolean result;
+ InvestQuotesRetriever *retriever;
+
+ error = NULL;
+ result = g_output_stream_write_all_finish (G_OUTPUT_STREAM (object), res,
+ &bytes_written, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ retriever = INVEST_QUOTES_RETRIEVER (user_data);
+
+ g_output_stream_close (G_OUTPUT_STREAM (object), NULL, NULL);
+ g_signal_emit (retriever, signals[COMPLETED], 0, result);
+ g_clear_error (&error);
+}
+
+static void
+save_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GFileOutputStream *stream;
+ InvestQuotesRetriever *retriever;
+
+ error = NULL;
+ stream = g_file_replace_finish (G_FILE (object), res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ retriever = INVEST_QUOTES_RETRIEVER (user_data);
+
+ if (error != NULL)
+ {
+ g_signal_emit (retriever, signals[COMPLETED], 0, FALSE);
+ g_error_free (error);
+ return;
+ }
+
+ g_output_stream_write_all_async (G_OUTPUT_STREAM (stream),
+ (const void *) retriever->array->data,
+ retriever->array->len,
+ 0, retriever->cancellable,
+ write_cb, retriever);
+
+ g_object_unref (stream);
+}
+
+static void
+download_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ gssize done;
+ InvestQuotesRetriever *retriever;
+
+ error = NULL;
+ done = g_input_stream_read_finish (G_INPUT_STREAM (object), res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ retriever = INVEST_QUOTES_RETRIEVER (user_data);
+
+ if (error != NULL)
+ {
+ g_signal_emit (retriever, signals[COMPLETED], 0, FALSE);
+ g_error_free (error);
+ return;
+ }
+
+ if (done > 0)
+ {
+ g_byte_array_append (retriever->array, retriever->chunk, done);
+
+ g_free (retriever->chunk);
+ retriever->chunk = g_malloc0 (CHUNK_SIZE + 1);
+
+ g_input_stream_read_async (G_INPUT_STREAM (object),
+ retriever->chunk, CHUNK_SIZE,
+ 0, retriever->cancellable,
+ download_cb, retriever);
+ }
+ else if (done == 0)
+ {
+ const gchar *dir;
+ gchar *path;
+ GFile *file;
+
+ dir = g_get_user_cache_dir ();
+
+ path = g_build_filename (dir, "gnome-applets", "invest-applet", NULL);
+ g_mkdir_with_parents (path, 0700);
+ g_free (path);
+
+ path = g_build_filename (dir, "gnome-applets", "invest-applet",
+ retriever->filename, NULL);
+
+ file = g_file_new_for_path (path);
+ g_free (path);
+
+ g_file_replace_async (file, NULL, FALSE, 0, 0,
+ retriever->cancellable,
+ save_cb, retriever);
+
+ g_object_unref (file);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+}
+
+static void
+ready_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GFileInputStream *stream;
+ InvestQuotesRetriever *retriever;
+
+ error = NULL;
+ stream = g_file_read_finish (G_FILE (object), res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ retriever = INVEST_QUOTES_RETRIEVER (user_data);
+
+ if (error != NULL)
+ {
+ g_signal_emit (retriever, signals[COMPLETED], 0, FALSE);
+ g_error_free (error);
+ return;
+ }
+
+ g_input_stream_read_async (G_INPUT_STREAM (stream),
+ retriever->chunk, CHUNK_SIZE,
+ 0, retriever->cancellable,
+ download_cb, retriever);
+
+ g_object_unref (stream);
+}
+
+static void
+invest_quotes_retriever_dispose (GObject *object)
+{
+ InvestQuotesRetriever *retriever;
+
+ retriever = INVEST_QUOTES_RETRIEVER (object);
+
+ if (retriever->cancellable != NULL)
+ {
+ g_cancellable_cancel (retriever->cancellable);
+ g_clear_object (&retriever->cancellable);
+ }
+
+ G_OBJECT_CLASS (invest_quotes_retriever_parent_class)->dispose (object);
+}
+
+static void
+invest_quotes_retriever_finalize (GObject *object)
+{
+ InvestQuotesRetriever *retriever;
+
+ retriever = INVEST_QUOTES_RETRIEVER (object);
+
+ g_free (retriever->symbol);
+ g_free (retriever->filename);
+
+ g_free (retriever->chunk);
+ g_byte_array_free (retriever->array, TRUE);
+
+ G_OBJECT_CLASS (invest_quotes_retriever_parent_class)->finalize (object);
+}
+
+static void
+invest_quotes_retriever_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ InvestQuotesRetriever *retriever;
+
+ retriever = INVEST_QUOTES_RETRIEVER (object);
+
+ switch (property_id)
+ {
+ case PROP_SYMBOL:
+ retriever->symbol = g_value_dup_string (value);
+ break;
+
+ case PROP_FILENAME:
+ retriever->filename = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+invest_quotes_retriever_install_properties (GObjectClass *object_class)
+{
+ properties[PROP_SYMBOL] =
+ g_param_spec_string ("symbol", "symbol", "symbol", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_FILENAME] =
+ g_param_spec_string ("filename", "filename", "filename", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+invest_quotes_retriever_install_signals (GObjectClass *object_class)
+{
+ signals[COMPLETED] =
+ g_signal_new ("completed", INVEST_TYPE_QUOTES_RETRIEVER, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+invest_quotes_retriever_class_init (InvestQuotesRetrieverClass *retriever_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (retriever_class);
+
+ object_class->dispose = invest_quotes_retriever_dispose;
+ object_class->finalize = invest_quotes_retriever_finalize;
+ object_class->set_property = invest_quotes_retriever_set_property;
+
+ invest_quotes_retriever_install_properties (object_class);
+ invest_quotes_retriever_install_signals (object_class);
+}
+
+static void
+invest_quotes_retriever_init (InvestQuotesRetriever *retriever)
+{
+ retriever->cancellable = g_cancellable_new ();
+
+ retriever->chunk = g_malloc0 (CHUNK_SIZE + 1);
+ retriever->array = g_byte_array_new ();
+}
+
+InvestQuotesRetriever *
+invest_quotes_retriever_new (const gchar *symbol,
+ const gchar *filename)
+{
+ return g_object_new (INVEST_TYPE_QUOTES_RETRIEVER,
+ "symbol", symbol,
+ "filename", filename,
+ NULL);
+}
+
+void
+invest_quotes_retriever_start (InvestQuotesRetriever *retriever)
+{
+ gchar *uri;
+ GFile *file;
+
+ uri = g_strdup_printf (QUOTES_URL, retriever->symbol);
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ g_file_read_async (file, 0, retriever->cancellable, ready_cb, retriever);
+ g_object_unref (file);
+}
+
+const gchar *
+invest_quotes_retriever_get_symbol (InvestQuotesRetriever *retriever)
+{
+ return retriever->symbol;
+}
diff --git a/invest-applet/invest/invest-quotes-retriever.h b/invest-applet/invest/invest-quotes-retriever.h
new file mode 100644
index 0000000..d033278
--- /dev/null
+++ b/invest-applet/invest/invest-quotes-retriever.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_QUOTES_RETRIEVER_H
+#define INVEST_QUOTES_RETRIEVER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_QUOTES_RETRIEVER invest_quotes_retriever_get_type ()
+G_DECLARE_FINAL_TYPE (InvestQuotesRetriever, invest_quotes_retriever,
+ INVEST, QUOTES_RETRIEVER, GObject)
+
+InvestQuotesRetriever *invest_quotes_retriever_new (const gchar *symbol,
+ const gchar *filename);
+
+void invest_quotes_retriever_start (InvestQuotesRetriever *retriever);
+
+const gchar *invest_quotes_retriever_get_symbol (InvestQuotesRetriever *retriever);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-quotes.c b/invest-applet/invest/invest-quotes.c
new file mode 100644
index 0000000..d5d1f10
--- /dev/null
+++ b/invest-applet/invest/invest-quotes.c
@@ -0,0 +1,1427 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "invest-cache.h"
+#include "invest-image-retriever.h"
+#include "invest-quotes.h"
+#include "invest-quotes-retriever.h"
+
+#define AUTOREFRESH_TIMEOUT 15 * 60 * 1000
+
+struct _InvestQuotes
+{
+ GtkTreeStore parent;
+
+ GSettings *settings;
+ GHashTable *retrievers;
+
+ GVariant *stocks;
+ gchar **symbols;
+
+ gboolean simple_quotes_only;
+
+ guint update_id;
+
+ gint count;
+ gdouble change;
+ gdouble avg_change;
+ gchar *updated;
+
+ GPtrArray *currencies;
+ GHashTable *statistics;
+
+ gboolean valid;
+};
+
+enum
+{
+ COL_SYMBOL,
+ COL_LABEL,
+ COL_CURRENCY,
+ COL_TICKER_ONLY,
+ COL_BALANCE,
+ COL_BALANCE_PCT,
+ COL_VALUE,
+ COL_VARIATION_PCT,
+ COL_PIXBUF,
+ COL_VARIANT,
+
+ NUM_COLS
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_SETTINGS,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+enum
+{
+ UPDATE_ICON,
+ UPDATE_TOOLTIP,
+
+ RELOADED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (InvestQuotes, invest_quotes, GTK_TYPE_TREE_STORE)
+
+typedef struct
+{
+ InvestQuotes *quotes;
+ GtkTreeIter row;
+} ImageData;
+
+static void
+image_completed_cb (InvestImageRetriever *retriever,
+ GdkPixbuf *pixbuf,
+ ImageData *data)
+{
+ GtkTreeStore *store;
+ const gchar *url;
+
+ store = GTK_TREE_STORE (data->quotes);
+ url = invest_image_retriever_get_url (retriever);
+
+ gtk_tree_store_set (store, &data->row, COL_PIXBUF, pixbuf, -1);
+
+ g_hash_table_remove (data->quotes->retrievers, url);
+}
+
+static void
+retrieve_image (InvestQuotes *quotes,
+ const gchar *symbol,
+ GtkTreeIter *row)
+{
+ gchar *url;
+ InvestImageRetriever *retriever;
+ ImageData *data;
+
+ if (g_settings_get_boolean (quotes->settings, "hidecharts"))
+ return;
+
+ url = g_strdup_printf ("http://ichart.yahoo.com/h?s=%s", symbol);
+ retriever = invest_image_retriever_new (url);
+
+ data = g_new0 (ImageData, 1);
+
+ data->quotes = quotes;
+ data->row = *row;
+
+ g_signal_connect_data (retriever, "completed",
+ G_CALLBACK (image_completed_cb),
+ data, (GClosureNotify) g_free,
+ G_CONNECT_AFTER);
+
+ g_hash_table_replace (quotes->retrievers, url, retriever);
+ invest_image_retriever_start (retriever);
+}
+
+typedef struct
+{
+ gdouble balance;
+ gdouble paid;
+} StatisticsData;
+
+static void
+update_tooltip (InvestQuotes *quotes)
+{
+ GPtrArray *array;
+ gchar *tmp;
+ gchar **tooltip;
+ gchar *text;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ array = g_ptr_array_new ();
+
+ if (quotes->count > 0)
+ {
+ /* Translators: This is share-market jargon. It is the average
+ * percentage change of all stock prices. The %s gets replaced
+ * with the string value of the change (localized), including
+ * the percent sign.
+ */
+ tmp = g_strdup_printf (_("Average change: %+.2f%%"), quotes->avg_change);
+ g_ptr_array_add (array, tmp);
+ }
+
+ g_hash_table_iter_init (&iter, quotes->statistics);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const gchar *currency;
+ StatisticsData *data;
+ gdouble change;
+
+ currency = (const gchar *) key;
+ data = (StatisticsData *) value;
+
+ change = data->balance / data->paid * 100;
+
+ /* Translators: This is share-market jargon. It refers to the total
+ * difference between the current price and purchase price for all
+ * the shares put together for a particular currency. i.e. How much
+ * money would be earned if they were sold right now. The first string
+ * is the change value, the second the currency, and the third value
+ * is the percentage of the change, formatted using user's locale.
+ */
+ tmp = g_strdup_printf (_("Positions balance: %'+.2f %s (%'+.2f%%)"),
+ data->balance, currency, change);
+ g_ptr_array_add (array, tmp);
+ }
+
+ if (quotes->updated != NULL)
+ {
+ tmp = g_strdup_printf (_("Updated at %s"), quotes->updated);
+ g_ptr_array_add (array, tmp);
+ }
+
+ g_ptr_array_add (array, NULL);
+ tooltip = (gchar **) g_ptr_array_free (array, FALSE);
+
+ text = g_strjoinv ("\n", tooltip);
+ g_strfreev (tooltip);
+
+ g_signal_emit (quotes, signals[UPDATE_TOOLTIP], 0, text);
+ g_free (text);
+}
+
+static void
+add_balance_change (InvestQuotes *quotes,
+ gdouble balance,
+ gdouble change,
+ const gchar *currency)
+{
+ StatisticsData *data;
+
+ if (balance == 0.0 && change == 0.0)
+ return;
+
+ if (currency == NULL)
+ currency = "";
+
+ data = (StatisticsData *) g_hash_table_lookup (quotes->statistics, currency);
+
+ if (data != NULL)
+ {
+ data->balance += balance;
+ data->paid += balance / change * 100;
+ }
+ else
+ {
+ data = g_new0 (StatisticsData, 1);
+
+ data->balance = balance;
+ data->paid = balance / change * 100;
+
+ g_hash_table_insert (quotes->statistics, g_strdup (currency), data);
+ }
+}
+
+static InvestQuote *
+get_quote (InvestQuotes *quotes,
+ InvestCache *cache,
+ const gchar *symbol)
+{
+ InvestQuote *quote;
+
+ quote = invest_cache_get (cache, symbol);
+ if (quote == NULL)
+ return NULL;
+
+ if (quote->currency != NULL)
+ {
+ gboolean found;
+ guint i;
+
+ found = FALSE;
+ for (i = 0; i < quotes->currencies->len; i++)
+ {
+ const gchar *currency;
+
+ currency = (const gchar *) quotes->currencies->pdata[i];
+
+ if (g_strcmp0 (currency, quote->currency) == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found == FALSE)
+ g_ptr_array_add (quotes->currencies, g_strdup (quote->currency));
+ }
+
+ return quote;
+}
+
+static void
+add_quotes (InvestQuotes *quotes,
+ InvestCache *cache,
+ GVariant *stocks,
+ GtkTreeIter *parent)
+{
+ GtkTreeStore *store;
+ GVariantIter iter;
+ gboolean group;
+ GVariant *variant;
+
+ store = GTK_TREE_STORE (quotes);
+
+ g_variant_iter_init (&iter, stocks);
+ while (g_variant_iter_loop (&iter, "{bv}", &group, &variant))
+ {
+ if (group == TRUE)
+ {
+ const gchar *name;
+ GVariant *list;
+ GtkTreeIter row;
+
+ g_variant_get (variant, "(&sv)", &name, &list);
+
+ gtk_tree_store_append (store, &row, parent);
+ gtk_tree_store_set (store, &row,
+ COL_LABEL, name,
+ COL_TICKER_ONLY, TRUE,
+ -1);
+
+ add_quotes (quotes, cache, list, &row);
+ g_variant_unref (list);
+ }
+ else
+ {
+ const gchar *symbol;
+ const gchar *label;
+ gdouble amount;
+ gdouble price;
+ gdouble comission;
+ gdouble currency_rate;
+ InvestQuote *quote;
+ GtkTreeIter row;
+
+ g_variant_get (variant, "(&s&sdddd)", &symbol, &label,
+ &amount, &price, &comission, ¤cy_rate);
+
+ quote = get_quote (quotes, cache, symbol);
+ if (quote == NULL)
+ continue;
+
+ if (strlen (label) == 0)
+ {
+ if (strlen (quote->name) != 0)
+ label = quote->name;
+ else
+ label = symbol;
+ }
+
+ gtk_tree_store_append (store, &row, parent);
+
+ if (amount == 0.0)
+ {
+ gtk_tree_store_set (store, &row,
+ COL_SYMBOL, symbol,
+ COL_LABEL, label,
+ COL_CURRENCY, quote->currency,
+ COL_TICKER_ONLY, TRUE,
+ COL_BALANCE, 0.0,
+ COL_BALANCE_PCT, 0.0,
+ COL_VALUE, quote->last_trade,
+ COL_VARIATION_PCT, quote->change_pct,
+ COL_PIXBUF, NULL,
+ COL_VARIANT, variant,
+ -1);
+ }
+ else
+ {
+ gdouble current;
+ gdouble paid;
+ gdouble balance;
+ gdouble change;
+
+ current = amount * quote->last_trade;
+ paid = amount * price + comission;
+ balance = current - paid;
+
+ if (paid != 0.0)
+ {
+ change = 100 * balance / paid;
+ }
+ else
+ {
+ /* Not technically correct, but it will look more
+ * intuitive than the real result of infinity.
+ */
+ change = 100;
+ }
+
+ gtk_tree_store_set (store, &row,
+ COL_SYMBOL, symbol,
+ COL_LABEL, label,
+ COL_CURRENCY, quote->currency,
+ COL_TICKER_ONLY, FALSE,
+ COL_BALANCE, balance,
+ COL_BALANCE_PCT, change,
+ COL_VALUE, quote->last_trade,
+ COL_VARIATION_PCT, quote->change_pct,
+ COL_PIXBUF, NULL,
+ COL_VARIANT, variant,
+ -1);
+
+ add_balance_change (quotes, balance,change, quote->currency);
+ }
+
+ quotes->change += quote->change_pct;
+ quotes->count++;
+
+ retrieve_image (quotes, symbol, &row);
+ }
+ }
+}
+
+static void
+populate (InvestQuotes *quotes,
+ InvestCache *cache)
+{
+ add_quotes (quotes, cache, quotes->stocks, NULL);
+
+ if (quotes->count > 0)
+ {
+ gint icon;
+
+ quotes->avg_change = quotes->change / quotes->count;
+
+ icon = 0;
+ if (quotes->avg_change != 0.0)
+ icon = quotes->avg_change / ABS (quotes->avg_change);
+
+ g_signal_emit (quotes, signals[UPDATE_ICON], 0, icon);
+ }
+ else
+ {
+ quotes->avg_change = 0.0;
+ }
+
+ quotes->valid = TRUE;
+}
+
+static void
+add_indices (GPtrArray *symbols,
+ GVariant *stocks,
+ GtkTreeIter *parent)
+{
+ GVariantIter iter;
+ gboolean group;
+ GVariant *variant;
+
+ g_variant_iter_init (&iter, stocks);
+ while (g_variant_iter_loop (&iter, "{bv}", &group, &variant))
+ {
+ if (group == TRUE)
+ {
+ const gchar *name;
+ GVariant *list;
+ GtkTreeIter row;
+
+ g_variant_get (variant, "(&sv)", &name, &list);
+
+ add_indices (symbols, list, &row);
+ g_variant_unref (list);
+ }
+ else
+ {
+ const gchar *symbol;
+ const gchar *label;
+ gdouble amount;
+ gdouble price;
+ gdouble comission;
+ gdouble currency_rate;
+ gboolean found;
+ guint i;
+
+ g_variant_get (variant, "(&s&sdddd)", &symbol, &label,
+ &amount, &price, &comission, ¤cy_rate);
+
+ if (!g_str_has_prefix (symbol, "^"))
+ continue;
+
+ found = FALSE;
+ for (i = 0; i < symbols->len; i++)
+ {
+ const gchar *tmp;
+
+ tmp = (const gchar *) symbols->pdata[i];
+
+ if (g_strcmp0 (symbol, tmp) == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ g_ptr_array_add (symbols, g_strdup (symbol));
+ }
+ }
+}
+
+static gchar **
+get_indices (InvestQuotes *quotes)
+{
+ GPtrArray *indices;
+
+ indices = g_ptr_array_new ();
+
+ add_indices (indices, quotes->stocks, NULL);
+ g_ptr_array_add (indices, NULL);
+
+ return (gchar **) g_ptr_array_free (indices, FALSE);
+}
+
+static void
+add_symbols (InvestQuotes *quotes,
+ GPtrArray *symbols,
+ GVariant *stocks,
+ GtkTreeIter *parent)
+{
+ GVariantIter iter;
+ gboolean group;
+ GVariant *variant;
+
+ g_variant_iter_init (&iter, stocks);
+ while (g_variant_iter_loop (&iter, "{bv}", &group, &variant))
+ {
+ if (group == TRUE)
+ {
+ const gchar *name;
+ GVariant *list;
+ GtkTreeIter row;
+
+ g_variant_get (variant, "(&sv)", &name, &list);
+
+ add_symbols (quotes, symbols, list, &row);
+ g_variant_unref (list);
+ }
+ else
+ {
+ const gchar *symbol;
+ const gchar *label;
+ gdouble amount;
+ gdouble price;
+ gdouble comission;
+ gdouble currency_rate;
+ gboolean found;
+ guint i;
+
+ g_variant_get (variant, "(&s&sdddd)", &symbol, &label,
+ &amount, &price, &comission, ¤cy_rate);
+
+ found = FALSE;
+ for (i = 0; i < symbols->len; i++)
+ {
+ const gchar *tmp;
+
+ tmp = (const gchar *) symbols->pdata[i];
+
+ if (g_strcmp0 (symbol, tmp) == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (amount != 0.0)
+ quotes->simple_quotes_only = FALSE;
+
+ if (!found)
+ g_ptr_array_add (symbols, g_strdup (symbol));
+ }
+ }
+}
+
+static void
+load_symbols (InvestQuotes *quotes)
+{
+ GPtrArray *symbols;
+
+ symbols = g_ptr_array_new ();
+
+ quotes->simple_quotes_only = TRUE;
+
+ add_symbols (quotes, symbols, quotes->stocks, NULL);
+ g_ptr_array_add (symbols, NULL);
+
+ g_strfreev (quotes->symbols);
+ quotes->symbols = (gchar **) g_ptr_array_free (symbols, FALSE);
+}
+
+static void
+load_quotes (InvestQuotes *quotes)
+{
+ GtkTreeStore *store;
+ InvestCache *cache;
+ GDateTime *time;
+
+ store = GTK_TREE_STORE (quotes);
+
+ gtk_tree_store_clear (store);
+
+ quotes->count = 0;
+ quotes->change = 0.0;
+ quotes->avg_change = 0.0;
+ quotes->valid = FALSE;
+
+ if (quotes->currencies != NULL)
+ g_ptr_array_free (quotes->currencies, TRUE);
+ quotes->currencies = g_ptr_array_new ();
+
+ g_hash_table_remove_all (quotes->statistics);
+
+ cache = invest_cache_read ("quotes.csv");
+ if (cache == NULL)
+ return;
+
+ populate (quotes, cache);
+
+ time = g_date_time_new_from_timeval_local (&cache->mtime);
+ if (time != NULL)
+ {
+ g_free (quotes->updated);
+ quotes->updated = g_date_time_format (time, "%H:%M");
+ g_date_time_unref (time);
+ }
+ else
+ {
+ g_free (quotes->updated);
+ quotes->updated = NULL;
+ }
+
+ update_tooltip (quotes);
+}
+
+typedef struct
+{
+ gchar *symbol;
+ GPtrArray *paths;
+} ForeachData;
+
+static gboolean
+find_stock_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ ForeachData *data;
+ gchar *symbol;
+
+ data = (ForeachData *) user_data;
+
+ gtk_tree_model_get (model, iter, COL_SYMBOL, &symbol, -1);
+
+ if (g_strcmp0 (symbol, data->symbol) == 0)
+ g_ptr_array_add (data->paths, gtk_tree_path_to_string (path));
+
+ g_free (symbol);
+
+ return FALSE;
+}
+
+static gboolean
+expand_index (InvestQuotes *quotes,
+ const gchar *index)
+{
+ gchar *filename;
+ InvestCache *cache;
+ GtkTreeModel *model;
+ ForeachData *data;
+ gchar **paths;
+ gboolean valid;
+ guint i;
+
+ filename = g_strdup_printf ("quotes.%s.csv", index);
+ cache = invest_cache_read (filename);
+ g_free (filename);
+
+ if (cache == NULL)
+ return FALSE;
+
+ model = GTK_TREE_MODEL (quotes);
+ data = g_new0 (ForeachData, 1);
+
+ data->symbol = g_strdup (index);
+ data->paths = g_ptr_array_new ();
+
+ gtk_tree_model_foreach (model, find_stock_cb, data);
+ g_ptr_array_add (data->paths, NULL);
+
+ g_free (data->symbol);
+ paths = (gchar **) g_ptr_array_free (data->paths, FALSE);
+ g_free (data);
+
+ valid = FALSE;
+ for (i = 0; paths[i] != NULL; i++)
+ {
+ guint j;
+
+ for (j = 0; j < cache->n_quotes; j++)
+ {
+ const gchar *symbol;
+ InvestQuote *quote;
+
+ symbol = cache->quotes[j]->symbol;
+ quote = get_quote (quotes, cache, symbol);
+
+ if (quote != NULL)
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new_from_string (paths[i]);
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ {
+ GtkTreeIter row;
+
+ gtk_tree_store_insert (GTK_TREE_STORE (quotes), &row, &iter, 0);
+ gtk_tree_store_set (GTK_TREE_STORE (quotes), &row,
+ COL_SYMBOL, symbol,
+ COL_LABEL, quote->name,
+ COL_CURRENCY, quote->currency,
+ COL_TICKER_ONLY, TRUE,
+ COL_BALANCE, 0.0,
+ COL_BALANCE_PCT, 0.0,
+ COL_VALUE, quote->last_trade,
+ COL_VARIATION_PCT, quote->change_pct,
+ COL_PIXBUF, NULL,
+ COL_VARIANT, NULL,
+ -1);
+
+ retrieve_image (quotes, symbol, &row);
+ }
+
+ gtk_tree_path_free (path);
+ }
+ }
+
+ valid = TRUE;
+ }
+
+ g_strfreev (paths);
+ invest_cache_free (cache);
+
+ return valid;
+}
+
+static void
+load_all_index_quotes (InvestQuotes *quotes)
+{
+ const gchar *cache;
+ gchar *path;
+ GDir *dir;
+ GRegex *regex;
+ const gchar *filename;
+
+ if (!g_settings_get_boolean (quotes->settings, "indexexpansion"))
+ return;
+
+ cache = g_get_user_cache_dir ();
+ path = g_build_filename (cache, "gnome-applets", "invest-applet", NULL);
+ dir = g_dir_open (path, 0, NULL);
+
+ if (dir == NULL)
+ {
+ g_free (path);
+ return;
+ }
+
+ regex = g_regex_new ("quotes.([^.]+).csv", 0, 0, NULL);
+ filename = g_dir_read_name (dir);
+
+ while (filename != NULL)
+ {
+ GMatchInfo *match_info;
+
+ g_regex_match (regex, filename, 0, &match_info);
+
+ while (g_match_info_matches (match_info))
+ {
+ gchar *index;
+
+ index = g_match_info_fetch (match_info, 1);
+
+ if (expand_index (quotes, index) == 0)
+ {
+ gchar *delete_filename;
+
+ delete_filename = g_build_filename (path, filename, NULL);
+
+ g_unlink (delete_filename);
+ g_free (delete_filename);
+ }
+
+ g_free (index);
+
+ g_match_info_next (match_info, NULL);
+ }
+
+ g_match_info_free (match_info);
+ filename = g_dir_read_name (dir);
+ }
+
+ g_regex_unref (regex);
+ g_dir_close (dir);
+ g_free (path);
+}
+
+typedef struct
+{
+ gdouble rate;
+} RateData;
+
+static void
+convert_currencies (InvestQuotes *quotes,
+ InvestCache *cache,
+ const gchar *target_currency)
+{
+ GHashTable *rates;
+ guint i;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ /* reset the overall balance */
+ g_hash_table_remove_all (quotes->statistics);
+
+ /* collect the rates for the currencies */
+ rates = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ for (i = 0; i < cache->n_quotes; i++)
+ {
+ InvestQuote *quote;
+ RateData *data;
+
+ quote = cache->quotes[i];
+
+ data = g_new0 (RateData, 1);
+ data->rate = quote->last_trade;
+
+ g_hash_table_insert (rates, g_strndup (quote->symbol, 3), data);
+ }
+
+ /* convert all non target currencies */
+ model = GTK_TREE_MODEL (quotes);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid)
+ {
+ gchar *currency;
+ gchar *symbol;
+ gdouble balance;
+ gdouble change;
+
+ gtk_tree_model_get (model, &iter, COL_CURRENCY, ¤cy, -1);
+
+ if (currency == NULL)
+ {
+ valid = gtk_tree_model_iter_next (model, &iter);
+ continue;
+ }
+
+ gtk_tree_model_get (model, &iter, COL_SYMBOL, &symbol, -1);
+
+ /* Ignore stocks that are currency conversions and only convert
+ * stocks that are not in the target currency and if we have a
+ * conversion rate.
+ */
+ if (!(strlen (symbol) == 8 && g_str_has_suffix (symbol, "=X")) &&
+ g_strcmp0 (currency, target_currency) != 0 &&
+ g_hash_table_lookup (rates, currency) != NULL)
+ {
+ RateData *data;
+ GtkTreeStore *store;
+ gboolean ticker_only;
+ gdouble value;
+
+ data = (RateData *) g_hash_table_lookup (rates, currency);
+ store = GTK_TREE_STORE (quotes);
+
+ gtk_tree_model_get (model, &iter,
+ COL_TICKER_ONLY, &ticker_only,
+ COL_VALUE, &value,
+ -1);
+
+ /* first convert the balance, it needs the original value */
+ if (ticker_only == FALSE)
+ {
+ GVariant *variant;
+ gdouble amount;
+ gdouble price;
+ gdouble comission;
+ gdouble currency_rate;
+ gdouble current;
+ gdouble paid;
+
+ gtk_tree_model_get (model, &iter, COL_VARIANT, &variant, -1);
+ g_variant_get (variant, "(&s&sdddd)", NULL, NULL, &amount,
+ &price, &comission, ¤cy_rate);
+
+ current = 0.0;
+ paid = 0.0;
+
+ if (amount != 0.0)
+ {
+ /* if the buy rate is invalid, use 1.0 */
+ if (currency_rate <= 0.0)
+ currency_rate = 1.0;
+
+ /* current value is the current rate * amount * value */
+ current = data->rate * amount * value;
+
+ /* paid is buy rate * (amount * price + commission) */
+ paid = currency_rate * (amount * price + comission);
+ }
+
+ balance = current - paid;
+
+ if (paid != 0.0)
+ {
+ change = 100 * balance / paid;
+ }
+ else
+ {
+ /* Not technically correct, but it will look more
+ * intuitive than the real result of infinity.
+ */
+ change = 100;
+ }
+
+ gtk_tree_store_set (store, &iter,
+ COL_BALANCE, balance,
+ COL_BALANCE_PCT, change,
+ -1);
+
+ add_balance_change (quotes, balance, change, target_currency);
+ }
+
+ /* now, convert the value */
+ gtk_tree_store_set (store, &iter,
+ COL_VALUE, value * data->rate,
+ COL_CURRENCY, target_currency,
+ -1);
+ }
+ else
+ {
+ gtk_tree_model_get (model, &iter,
+ COL_BALANCE, &balance,
+ COL_BALANCE_PCT, &change,
+ -1);
+
+ add_balance_change (quotes, balance, change, currency);
+ }
+
+ g_free (currency);
+ g_free (symbol);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ g_hash_table_destroy (rates);
+}
+
+static void
+load_currencies (InvestQuotes *quotes)
+{
+ gchar *target_currency;
+ InvestCache *cache;
+
+ /* If there is no target currency, this method should never have
+ * been called.
+ */
+ target_currency = g_settings_get_string (quotes->settings, "currency");
+ if (!target_currency || *target_currency == '\0')
+ {
+ g_free (target_currency);
+ return;
+ }
+
+ cache = invest_cache_read ("currencies.csv");
+ if (cache == NULL)
+ {
+ g_free (target_currency);
+ return;
+ }
+
+ convert_currencies (quotes, cache, target_currency);
+
+ invest_cache_free (cache);
+ g_free (target_currency);
+}
+
+static void
+currencies_completed_cb (InvestQuotesRetriever *retriever,
+ gboolean retrieved,
+ InvestQuotes *quotes)
+{
+ if (!retrieved)
+ g_warning ("Failed to retrieve currency rates!");
+ else
+ load_currencies (quotes);
+
+ update_tooltip (quotes);
+
+ g_hash_table_remove (quotes->retrievers, "currencies");
+}
+
+static void
+retrieve_currencies (InvestQuotes *quotes)
+{
+ gchar *target_currency;
+ GPtrArray *array;
+ guint i;
+ gchar **symbols;
+
+ target_currency = g_settings_get_string (quotes->settings, "currency");
+ if (!target_currency || *target_currency == '\0')
+ {
+ g_free (target_currency);
+ return;
+ }
+
+ array = g_ptr_array_new ();
+ for (i = 0; i < quotes->currencies->len; i++)
+ {
+ const gchar *currency;
+ gchar *symbol;
+
+ currency = (const gchar *) quotes->currencies->pdata[i];
+
+ if (g_strcmp0 (currency, target_currency) == 0)
+ continue;
+
+ symbol = g_strdup_printf ("%s%s=X", currency, target_currency);
+ g_ptr_array_add (array, symbol);
+ }
+
+ g_free (target_currency);
+
+ g_ptr_array_add (array, NULL);
+ symbols = (gchar **) g_ptr_array_free (array, FALSE);
+
+ if (g_strv_length (symbols) > 0)
+ {
+ gchar *tmp;
+ InvestQuotesRetriever *retriever;
+
+ tmp = g_strjoinv (",", symbols);
+ retriever = invest_quotes_retriever_new (tmp, "currencies.csv");
+ g_free (tmp);
+
+ g_signal_connect (retriever, "completed",
+ G_CALLBACK (currencies_completed_cb),
+ quotes);
+
+ g_hash_table_replace (quotes->retrievers, g_strdup ("currencies"), retriever);
+ invest_quotes_retriever_start (retriever);
+ }
+
+ g_strfreev (symbols);
+}
+
+static void
+index_completed_cb (InvestQuotesRetriever *retriever,
+ gboolean retrieved,
+ InvestQuotes *quotes)
+{
+ const gchar *index;
+
+ index = invest_quotes_retriever_get_symbol (retriever);
+
+ if (!retrieved)
+ {
+ g_warning ("Failed to retrieve quotes for index %s!", index);
+ return;
+ }
+
+ expand_index (quotes, index);
+ retrieve_currencies (quotes);
+
+ g_hash_table_remove (quotes->retrievers, index);
+}
+
+static void
+expand_indices (InvestQuotes *quotes)
+{
+ gchar **indices;
+ guint i;
+
+ if (!g_settings_get_boolean (quotes->settings, "indexexpansion"))
+ {
+ retrieve_currencies (quotes);
+ return;
+ }
+
+ indices = get_indices (quotes);
+
+ for (i = 0; indices[i] != NULL; i++)
+ {
+ gchar *filename;
+ InvestQuotesRetriever *retriever;
+
+ filename = g_strdup_printf ("quotes.%s.csv", indices[i]);
+ retriever = invest_quotes_retriever_new (indices[i], filename);
+ g_free (filename);
+
+ g_signal_connect (retriever, "completed",
+ G_CALLBACK (index_completed_cb),
+ quotes);
+
+ g_hash_table_replace (quotes->retrievers, g_strdup (indices[i]), retriever);
+ invest_quotes_retriever_start (retriever);
+ }
+
+ g_strfreev (indices);
+}
+
+static void
+quotes_completed_cb (InvestQuotesRetriever *retriever,
+ gboolean retrieved,
+ InvestQuotes *quotes)
+{
+ if (retrieved)
+ {
+ load_quotes (quotes);
+ expand_indices (quotes);
+ }
+ else
+ {
+ const gchar *text;
+
+ text = _("Invest could not connect to Yahoo! Finance");
+
+ g_signal_emit (quotes, signals[UPDATE_TOOLTIP], 0, text);
+ }
+
+ g_hash_table_remove (quotes->retrievers, "quotes");
+}
+
+static void
+add_update_timeout (InvestQuotes *quotes)
+{
+ gint interval;
+ GSourceFunc func;
+
+ if (quotes->update_id != 0)
+ return;
+
+ interval = AUTOREFRESH_TIMEOUT;
+ func = (GSourceFunc) invest_quotes_refresh;
+
+ quotes->update_id = g_timeout_add (interval, func, quotes);
+}
+
+static void
+invest_quotes_reload (InvestQuotes *quotes)
+{
+ g_clear_pointer ("es->stocks, g_variant_unref);
+ quotes->stocks = g_settings_get_value (quotes->settings, "stocks");
+
+ g_hash_table_remove_all (quotes->retrievers);
+
+ load_symbols (quotes);
+ load_quotes (quotes);
+ load_all_index_quotes (quotes);
+ load_currencies (quotes);
+
+ g_signal_emit (quotes, signals[RELOADED], 0);
+
+ add_update_timeout (quotes);
+ invest_quotes_refresh (quotes);
+}
+
+static void
+settings_changed_cb (GSettings *settings,
+ const gchar *key,
+ InvestQuotes *quotes)
+{
+ invest_quotes_reload (quotes);
+}
+
+static void
+network_changed_cb (GNetworkMonitor *monitor,
+ gboolean available,
+ InvestQuotes *quotes)
+{
+ if (!available)
+ return;
+
+ add_update_timeout (quotes);
+ invest_quotes_refresh (quotes);
+}
+
+static void
+invest_quotes_constructed (GObject *object)
+{
+ InvestQuotes *quotes;
+
+ quotes = INVEST_QUOTES (object);
+
+ G_OBJECT_CLASS (invest_quotes_parent_class)->constructed (object);
+
+ g_signal_connect (quotes->settings, "changed",
+ G_CALLBACK (settings_changed_cb), quotes);
+
+ g_signal_connect (g_network_monitor_get_default (), "network-changed",
+ G_CALLBACK (network_changed_cb), quotes);
+
+ invest_quotes_reload (quotes);
+}
+
+static void
+invest_quotes_dispose (GObject *object)
+{
+ InvestQuotes *quotes;
+
+ quotes = INVEST_QUOTES (object);
+
+ if (quotes->update_id != 0)
+ {
+ g_source_remove (quotes->update_id);
+ quotes->update_id = 0;
+ }
+
+ g_clear_pointer ("es->stocks, g_variant_unref);
+ g_clear_pointer ("es->retrievers, g_hash_table_destroy);
+ g_clear_pointer ("es->statistics, g_hash_table_destroy);
+ g_clear_object ("es->settings);
+
+ G_OBJECT_CLASS (invest_quotes_parent_class)->dispose (object);
+}
+
+static void
+invest_quotes_finalize (GObject *object)
+{
+ InvestQuotes *quotes;
+
+ quotes = INVEST_QUOTES (object);
+
+ g_strfreev (quotes->symbols);
+ g_free (quotes->updated);
+
+ if (quotes->currencies != NULL)
+ g_ptr_array_free (quotes->currencies, TRUE);
+
+ G_OBJECT_CLASS (invest_quotes_parent_class)->finalize (object);
+}
+
+static void
+invest_quotes_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ InvestQuotes *quotes;
+
+ quotes = INVEST_QUOTES (object);
+
+ switch (property_id)
+ {
+ case PROP_SETTINGS:
+ quotes->settings = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+invest_quotes_install_properties (GObjectClass *object_class)
+{
+ properties[PROP_SETTINGS] =
+ g_param_spec_object ("settings", "settings", "settings", G_TYPE_SETTINGS,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+invest_quotes_install_signals (GObjectClass *object_class)
+{
+ signals[UPDATE_ICON] =
+ g_signal_new ("update-icon", INVEST_TYPE_QUOTES, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
+
+ signals[UPDATE_TOOLTIP] =
+ g_signal_new ("update-tooltip", INVEST_TYPE_QUOTES, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[RELOADED] =
+ g_signal_new ("reloaded", INVEST_TYPE_QUOTES, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+invest_quotes_class_init (InvestQuotesClass *quotes_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (quotes_class);
+
+ object_class->constructed = invest_quotes_constructed;
+ object_class->dispose = invest_quotes_dispose;
+ object_class->finalize = invest_quotes_finalize;
+ object_class->set_property = invest_quotes_set_property;
+
+ invest_quotes_install_properties (object_class);
+ invest_quotes_install_signals (object_class);
+}
+
+static void
+invest_quotes_init (InvestQuotes *quotes)
+{
+ GtkTreeStore *store;
+ GType *types;
+
+ store = GTK_TREE_STORE (quotes);
+ types = g_new0 (GType, NUM_COLS);
+
+ types[COL_SYMBOL] = G_TYPE_STRING;
+ types[COL_LABEL] = G_TYPE_STRING;
+ types[COL_CURRENCY] = G_TYPE_STRING;
+ types[COL_TICKER_ONLY] = G_TYPE_BOOLEAN;
+ types[COL_BALANCE] = G_TYPE_DOUBLE;
+ types[COL_BALANCE_PCT] = G_TYPE_DOUBLE;
+ types[COL_VALUE] = G_TYPE_DOUBLE;
+ types[COL_VARIATION_PCT] = G_TYPE_DOUBLE;
+ types[COL_PIXBUF] = GDK_TYPE_PIXBUF;
+ types[COL_VARIANT] = G_TYPE_VARIANT;
+
+ gtk_tree_store_set_column_types (store, NUM_COLS, types);
+ g_free (types);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COL_LABEL,
+ GTK_SORT_ASCENDING);
+
+ quotes->retrievers = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+
+ quotes->statistics = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+}
+
+InvestQuotes *
+invest_quotes_new (GSettings *settings)
+{
+ return g_object_new (INVEST_TYPE_QUOTES,
+ "settings", settings,
+ NULL);
+}
+
+gboolean
+invest_quotes_has_stocks (InvestQuotes *quotes)
+{
+ gboolean valid;
+ gint i;
+
+ if (!quotes->symbols)
+ return FALSE;
+
+ valid = FALSE;
+ for (i = 0; quotes->symbols[i] != NULL; i++)
+ {
+ const gchar *symbol;
+
+ symbol = quotes->symbols[i];
+
+ if (symbol != NULL && symbol[0] != '\0')
+ {
+ valid = TRUE;
+ break;
+ }
+ }
+
+ return valid;
+}
+
+gboolean
+invest_quotes_is_valid (InvestQuotes *quotes)
+{
+ return quotes->valid;
+}
+
+gboolean
+invest_quotes_simple_quotes_only (InvestQuotes *quotes)
+{
+ return quotes->simple_quotes_only;
+}
+
+gboolean
+invest_quotes_refresh (InvestQuotes *quotes)
+{
+ GNetworkMonitor *nm;
+ gchar *symbols;
+ InvestQuotesRetriever *retriever;
+
+ nm = g_network_monitor_get_default ();
+
+ if (!g_network_monitor_get_network_available (nm))
+ {
+ if (quotes->update_id != 0)
+ {
+ g_source_remove (quotes->update_id);
+ quotes->update_id = 0;
+ }
+
+ return FALSE;
+ }
+
+ if (!invest_quotes_has_stocks (quotes))
+ return TRUE;
+
+ symbols = g_strjoinv (",", quotes->symbols);
+ retriever = invest_quotes_retriever_new (symbols, "quotes.csv");
+ g_free (symbols);
+
+ g_signal_connect (retriever, "completed",
+ G_CALLBACK (quotes_completed_cb),
+ quotes);
+
+ g_hash_table_replace (quotes->retrievers, g_strdup ("quotes"), retriever);
+ invest_quotes_retriever_start (retriever);
+
+ return TRUE;
+}
diff --git a/invest-applet/invest/invest-quotes.h b/invest-applet/invest/invest-quotes.h
new file mode 100644
index 0000000..36e824e
--- /dev/null
+++ b/invest-applet/invest/invest-quotes.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_QUOTES_H
+#define INVEST_QUOTES_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_QUOTES invest_quotes_get_type ()
+G_DECLARE_FINAL_TYPE (InvestQuotes, invest_quotes,
+ INVEST, QUOTES, GtkTreeStore)
+
+InvestQuotes *invest_quotes_new (GSettings *settings);
+
+gboolean invest_quotes_has_stocks (InvestQuotes *quotes);
+
+gboolean invest_quotes_is_valid (InvestQuotes *quotes);
+
+gboolean invest_quotes_simple_quotes_only (InvestQuotes *quotes);
+
+gboolean invest_quotes_refresh (InvestQuotes *quotes);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-widget.c b/invest-applet/invest/invest-widget.c
new file mode 100644
index 0000000..24af592
--- /dev/null
+++ b/invest-applet/invest/invest-widget.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "invest-chart.h"
+#include "invest-widget.h"
+
+#define LIGHT -3
+#define MEDIUM -1
+
+struct _InvestWidget
+{
+ GtkTreeView parent;
+
+ GSettings *settings;
+ InvestQuotes *quotes;
+
+ GtkTreeViewColumn *chart;
+ GtkTreeViewColumn *gain;
+ GtkTreeViewColumn *gain_pct;
+
+ gulong changed_id;
+ gulong reloaded_id;
+};
+
+enum
+{
+ COL_SYMBOL,
+ COL_LABEL,
+ COL_CURRENCY,
+ COL_TICKER_ONLY,
+ COL_BALANCE,
+ COL_BALANCE_PCT,
+ COL_VALUE,
+ COL_VARIATION_PCT,
+ COL_PIXBUF,
+ COL_VARIANT,
+
+ NUM_COLS
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_SETTINGS,
+ PROP_QUOTES,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+G_DEFINE_TYPE (InvestWidget, invest_widget, GTK_TYPE_TREE_VIEW)
+
+static const gchar *positive[] =
+ {
+ "#ffffff", "#ad7fa8", "#75507b", "#5c3566", "#729fcf",
+ "#3465a4", "#204a87", "#8ae234", "#73d216", "#4e9a06"
+ };
+
+static const gchar *negative[] =
+ {
+ "#ffffff", "#fce94f", "#e9b96e", "#fcaf3e", "#c17d11",
+ "#f57900", "#ce5c00", "#ef2929", "#cc0000", "#a40000"
+ };
+
+static void
+row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ InvestWidget *widget)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *symbol;
+
+ model = GTK_TREE_MODEL (widget->quotes);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter, COL_SYMBOL, &symbol, -1);
+
+ if (symbol == NULL)
+ return;
+
+ invest_chart_show_chart (symbol);
+ g_free (symbol);
+}
+
+static gboolean
+is_selected (InvestWidget *widget,
+ GtkTreeIter *iter)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter selected;
+ GtkTreePath *a;
+ GtkTreePath *b;
+ gboolean ret;
+
+ tree_view = GTK_TREE_VIEW (widget);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (!gtk_tree_selection_get_selected (selection, &model, &selected))
+ return FALSE;
+
+ a = gtk_tree_model_get_path (GTK_TREE_MODEL (widget->quotes), iter);
+ b = gtk_tree_model_get_path (model, &selected);
+
+ ret = FALSE;
+ if (gtk_tree_path_compare (a, b) == 0)
+ ret = TRUE;
+
+ gtk_tree_path_free (a);
+ gtk_tree_path_free (b);
+
+ return ret;
+}
+
+static const gchar *
+get_color (InvestWidget *widget,
+ GtkTreeIter *iter,
+ gint field)
+{
+ gdouble value;
+ gint intensity;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ field, &value, -1);
+
+ intensity = MEDIUM;
+ if (is_selected (widget, iter))
+ intensity = LIGHT;
+
+ if (value < 0.0)
+ return negative[G_N_ELEMENTS (negative) + intensity];
+
+ return positive[G_N_ELEMENTS (positive) + intensity];
+}
+
+static void
+changed_hidecharts_cb (GSettings *settings,
+ const gchar *key,
+ InvestWidget *widget)
+{
+ gboolean hidecharts;
+
+ hidecharts = g_settings_get_boolean (widget->settings, key);
+ gtk_tree_view_column_set_visible (widget->chart, !hidecharts);
+}
+
+static void
+reloaded_cb (InvestQuotes *quotes,
+ InvestWidget *widget)
+{
+ gboolean simple_quotes_only;
+
+ simple_quotes_only = invest_quotes_simple_quotes_only (quotes);
+
+ gtk_tree_view_column_set_visible (widget->gain, !simple_quotes_only);
+ gtk_tree_view_column_set_visible (widget->gain_pct, !simple_quotes_only);
+}
+
+static gboolean
+is_group (InvestWidget *widget,
+ GtkTreeIter *iter)
+{
+ gchar *symbol;
+ gboolean is_group;
+
+ symbol = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_SYMBOL, &symbol, -1);
+
+ is_group = symbol == NULL ? TRUE : FALSE;
+ g_free (symbol);
+
+ return is_group;
+}
+
+static gboolean
+is_stock (InvestWidget *widget,
+ GtkTreeIter *iter)
+{
+ return !is_group (widget, iter);
+}
+
+static void
+ticker_cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ InvestWidget *widget;
+ gchar *label;
+
+ widget = INVEST_WIDGET (data);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_LABEL, &label, -1);
+
+ if (is_stock (widget, iter))
+ {
+ g_object_set (cell, "text", label, NULL);
+ g_free (label);
+ }
+ else
+ {
+ gchar *markup;
+
+ markup = g_strdup_printf ("<b>%s</b>", label);
+ g_free (label);
+
+ g_object_set (cell, "markup", markup, NULL);
+ g_free (markup);
+ }
+}
+
+static void
+add_column_ticker (InvestWidget *widget,
+ GtkTreeView *tree_view)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Ticker"), renderer, NULL);
+
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ ticker_cell_data_func,
+ widget, NULL);
+
+ gtk_tree_view_column_set_sort_column_id (column, COL_LABEL);
+ gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+last_cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ InvestWidget *widget;
+ gdouble value;
+ gchar *currency;
+
+ widget = INVEST_WIDGET (data);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_VALUE, &value, COL_CURRENCY, ¤cy, -1);
+
+ if (currency == NULL)
+ {
+ g_object_set (cell, "text", "", NULL);
+ }
+ else
+ {
+ gchar *text;
+
+ text = g_strdup_printf ("%'.2f %s", value, currency);
+
+ g_object_set (cell, "text", text, NULL);
+ g_free (text);
+ }
+
+ g_free (currency);
+}
+
+static void
+add_column_last (InvestWidget *widget,
+ GtkTreeView *tree_view)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Last"), renderer, NULL);
+
+ gtk_cell_renderer_set_alignment (renderer, 1.0, 0.5);
+
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ last_cell_data_func,
+ widget, NULL);
+
+ gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+change_cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ InvestWidget *widget;
+ const gchar *color;
+ gdouble value;
+ gchar *markup;
+
+ widget = INVEST_WIDGET (data);
+
+ if (is_group (widget, iter))
+ {
+ g_object_set (cell, "text", "", NULL);
+ return;
+ }
+
+ color = get_color (widget, iter, COL_VARIATION_PCT);
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_VARIATION_PCT, &value, -1);
+
+ markup = g_strdup_printf ("<span foreground='%s'>%'+.2f%%</span>",
+ color, value);
+
+ g_object_set (cell, "markup", markup, NULL);
+ g_free (markup);
+}
+
+static void
+add_column_change (InvestWidget *widget,
+ GtkTreeView *tree_view)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Change %"), renderer, NULL);
+
+ gtk_cell_renderer_set_alignment (renderer, 1.0, 0.5);
+
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ change_cell_data_func,
+ widget, NULL);
+
+ gtk_tree_view_column_set_sort_column_id (column, COL_VARIATION_PCT);
+ gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+add_column_chart (InvestWidget *widget,
+ GtkTreeView *tree_view)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Chart"), renderer,
+ "pixbuf", COL_PIXBUF,
+ NULL);
+
+ widget->chart = column;
+
+ gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+gain_cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ InvestWidget *widget;
+ gboolean ticker_only;
+
+ widget = INVEST_WIDGET (data);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_TICKER_ONLY, &ticker_only, -1);
+
+ if (ticker_only)
+ {
+ g_object_set (cell, "text", "", NULL);
+ }
+ else
+ {
+ const gchar *color;
+ gdouble value;
+ gchar *currency;
+ gchar *markup;
+
+ color = get_color (widget, iter, COL_BALANCE);
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_BALANCE, &value, COL_CURRENCY, ¤cy,
+ -1);
+
+ markup = g_strdup_printf ("<span foreground='%s'>%'+.2f %s</span>",
+ color, value, currency);
+
+ g_free (currency);
+
+ g_object_set (cell, "markup", markup, NULL);
+ g_free (markup);
+ }
+}
+
+static void
+add_column_gain (InvestWidget *widget,
+ GtkTreeView *tree_view)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Gain"), renderer, NULL);
+
+ gtk_cell_renderer_set_alignment (renderer, 1.0, 0.5);
+
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ gain_cell_data_func,
+ widget, NULL);
+
+ widget->gain = column;
+
+ gtk_tree_view_column_set_sort_column_id (column, COL_BALANCE);
+ gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+gain_pct_cell_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ InvestWidget *widget;
+ gboolean ticker_only;
+
+ widget = INVEST_WIDGET (data);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_TICKER_ONLY, &ticker_only, -1);
+
+ if (ticker_only)
+ {
+ g_object_set (cell, "text", "", NULL);
+ }
+ else
+ {
+ const gchar *color;
+ gdouble value;
+ gchar *markup;
+
+ color = get_color (widget, iter, COL_BALANCE_PCT);
+ gtk_tree_model_get (GTK_TREE_MODEL (widget->quotes), iter,
+ COL_BALANCE_PCT, &value, -1);
+
+ markup = g_strdup_printf ("<span foreground='%s'>%'+.2f%%</span>",
+ color, value);
+
+ g_object_set (cell, "markup", markup, NULL);
+ g_free (markup);
+ }
+}
+
+static void
+add_column_gain_pct (InvestWidget *widget,
+ GtkTreeView *tree_view)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Gain %"), renderer, NULL);
+
+ gtk_cell_renderer_set_alignment (renderer, 1.0, 0.5);
+
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ gain_pct_cell_data_func,
+ widget, NULL);
+
+ widget->gain_pct = column;
+
+ gtk_tree_view_column_set_sort_column_id (column, COL_BALANCE_PCT);
+ gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+invest_widget_constructed (GObject *object)
+{
+ InvestWidget *widget;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+
+ G_OBJECT_CLASS (invest_widget_parent_class)->constructed (object);
+
+ widget = INVEST_WIDGET (object);
+ tree_view = GTK_TREE_VIEW (widget);
+ model = GTK_TREE_MODEL (widget->quotes);
+
+ add_column_ticker (widget, tree_view);
+ add_column_last (widget, tree_view);
+ add_column_change (widget, tree_view);
+ add_column_chart (widget, tree_view);
+ add_column_gain (widget, tree_view);
+ add_column_gain_pct (widget, tree_view);
+
+ widget->changed_id =
+ g_signal_connect (widget->settings,"changed::hidecharts",
+ G_CALLBACK (changed_hidecharts_cb), widget);
+
+ widget->reloaded_id =
+ g_signal_connect (widget->quotes, "reloaded",
+ G_CALLBACK (reloaded_cb), widget);
+
+ changed_hidecharts_cb (widget->settings, "hidecharts", widget);
+ reloaded_cb (widget->quotes, widget);
+
+ gtk_tree_view_set_hover_selection (tree_view, TRUE);
+ gtk_tree_view_set_model (tree_view, model);
+}
+
+static void
+invest_widget_dispose (GObject *object)
+{
+ InvestWidget *widget;
+
+ widget = INVEST_WIDGET (object);
+
+ if (widget->changed_id != 0)
+ {
+ g_signal_handler_disconnect (widget->settings, widget->changed_id);
+ widget->changed_id = 0;
+ }
+
+ if (widget->reloaded_id != 0)
+ {
+ g_signal_handler_disconnect (widget->quotes, widget->reloaded_id);
+ widget->reloaded_id = 0;
+ }
+
+ g_clear_object (&widget->settings);
+ g_clear_object (&widget->quotes);
+
+ G_OBJECT_CLASS (invest_widget_parent_class)->dispose (object);
+}
+
+static void
+invest_widget_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ InvestWidget *widget;
+
+ widget = INVEST_WIDGET (object);
+
+ switch (property_id)
+ {
+ case PROP_SETTINGS:
+ widget->settings = g_value_dup_object (value);
+ break;
+
+ case PROP_QUOTES:
+ widget->quotes = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+invest_widget_install_properties (GObjectClass *object_class)
+{
+ properties[PROP_SETTINGS] =
+ g_param_spec_object ("settings", "settings", "settings", G_TYPE_SETTINGS,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_QUOTES] =
+ g_param_spec_object ("quotes", "quotes", "quotes", INVEST_TYPE_QUOTES,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+invest_widget_class_init (InvestWidgetClass *widget_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (widget_class);
+
+ object_class->constructed = invest_widget_constructed;
+ object_class->dispose = invest_widget_dispose;
+ object_class->set_property = invest_widget_set_property;
+
+ invest_widget_install_properties (object_class);
+}
+
+static void
+invest_widget_init (InvestWidget *widget)
+{
+ g_signal_connect (widget, "row-activated",
+ G_CALLBACK (row_activated_cb), widget);
+}
+
+GtkWidget *
+invest_widget_new (GSettings *settings,
+ InvestQuotes *quotes)
+{
+ return g_object_new (INVEST_TYPE_WIDGET,
+ "settings", settings,
+ "quotes", quotes,
+ NULL);
+}
diff --git a/invest-applet/invest/invest-widget.h b/invest-applet/invest/invest-widget.h
new file mode 100644
index 0000000..6d11b6c
--- /dev/null
+++ b/invest-applet/invest/invest-widget.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_WIDGET_H
+#define INVEST_WIDGET_H
+
+#include "invest-quotes.h"
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_WIDGET invest_widget_get_type ()
+G_DECLARE_FINAL_TYPE (InvestWidget, invest_widget, INVEST, WIDGET, GtkTreeView)
+
+GtkWidget *invest_widget_new (GSettings *settings,
+ InvestQuotes *quotes);
+
+G_END_DECLS
+
+#endif
diff --git a/invest-applet/invest/invest-window.c b/invest-applet/invest/invest-window.c
new file mode 100644
index 0000000..900d6cc
--- /dev/null
+++ b/invest-applet/invest/invest-window.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#include "config.h"
+#include "invest-widget.h"
+#include "invest-window.h"
+
+struct _InvestWindow
+{
+ GtkWindow parent;
+
+ GSettings *settings;
+ InvestQuotes *quotes;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_SETTINGS,
+ PROP_QUOTES,
+
+ LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+G_DEFINE_TYPE (InvestWindow, invest_window, GTK_TYPE_WINDOW)
+
+static void
+invest_window_constructed (GObject *object)
+{
+ InvestWindow *window;
+ GtkWidget *scrolled;
+ GtkWidget *invest;
+
+ G_OBJECT_CLASS (invest_window_parent_class)->constructed (object);
+
+ window = INVEST_WINDOW (object);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (window), scrolled);
+ gtk_widget_show (scrolled);
+
+ invest = invest_widget_new (window->settings, window->quotes);
+ gtk_container_add (GTK_CONTAINER (scrolled), invest);
+ gtk_widget_show (invest);
+
+ gtk_widget_set_size_request (GTK_WIDGET (window), 520, 220);
+ gtk_container_set_border_width (GTK_CONTAINER (window), 4);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+}
+
+static void
+invest_window_dispose (GObject *object)
+{
+ InvestWindow *window;
+
+ window = INVEST_WINDOW (object);
+
+ g_clear_object (&window->settings);
+ g_clear_object (&window->quotes);
+
+ G_OBJECT_CLASS (invest_window_parent_class)->dispose (object);
+}
+
+static void
+invest_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ InvestWindow *window;
+
+ window = INVEST_WINDOW (object);
+
+ switch (property_id)
+ {
+ case PROP_SETTINGS:
+ window->settings = g_value_dup_object (value);
+ break;
+
+ case PROP_QUOTES:
+ window->quotes = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+invest_window_install_properties (GObjectClass *object_class)
+{
+ properties[PROP_SETTINGS] =
+ g_param_spec_object ("settings", "settings", "settings", G_TYPE_SETTINGS,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_QUOTES] =
+ g_param_spec_object ("quotes", "quotes", "quotes", INVEST_TYPE_QUOTES,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+invest_window_class_init (InvestWindowClass *window_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (window_class);
+
+ object_class->constructed = invest_window_constructed;
+ object_class->dispose = invest_window_dispose;
+ object_class->set_property = invest_window_set_property;
+
+ invest_window_install_properties (object_class);
+}
+
+static void
+invest_window_init (InvestWindow *window)
+{
+ GtkWindow *gtk_window;
+
+ gtk_window = GTK_WINDOW (window);
+
+ gtk_window_set_decorated (gtk_window, FALSE);
+ gtk_window_set_type_hint (gtk_window, GDK_WINDOW_TYPE_HINT_DOCK);
+ gtk_window_stick (gtk_window);
+}
+
+GtkWidget *
+invest_window_new (GSettings *settings,
+ InvestQuotes *quotes)
+{
+ return g_object_new (INVEST_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "settings", settings,
+ "quotes", quotes,
+ NULL);
+}
diff --git a/invest-applet/invest/invest-window.h b/invest-applet/invest/invest-window.h
new file mode 100644
index 0000000..dfe3edf
--- /dev/null
+++ b/invest-applet/invest/invest-window.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004-2005 Raphael Slinckx
+ * Copyright (C) 2009-2011 Enrico Minack
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alberts Muktupāvels <alberts muktupavels gmail com>
+ * Enrico Minack <enrico-minack gmx de>
+ * Raphael Slinckx <raphael slinckx net>
+ */
+
+#ifndef INVEST_WINDOW_H
+#define INVEST_WINDOW_H
+
+#include <gtk/gtk.h>
+
+#include "invest-quotes.h"
+
+G_BEGIN_DECLS
+
+#define INVEST_TYPE_WINDOW invest_window_get_type ()
+G_DECLARE_FINAL_TYPE (InvestWindow, invest_window, INVEST, WINDOW, GtkWindow)
+
+GtkWidget *invest_window_new (GSettings *settings,
+ InvestQuotes *quotes);
+
+G_END_DECLS
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8ac7554..71aed84 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -57,14 +57,13 @@ inhibit/src/inhibit-applet.c
[type: gettext/glade]invest-applet/data/financialchart.ui
[type: gettext/glade]invest-applet/data/invest-applet-menu.xml
[type: gettext/ini]invest-applet/data/org.gnome.applets.InvestApplet.panel-applet.in.in
+invest-applet/data/org.gnome.gnome-applets.invest.gschema.xml.in.in
[type: gettext/glade]invest-applet/data/prefs-dialog.ui
-invest-applet/invest/about.py
-invest-applet/invest/applet.py
-invest-applet/invest/chart.py
-invest-applet/invest/invest-applet.py
-invest-applet/invest/preferences.py
-invest-applet/invest/quotes.py
-invest-applet/invest/widgets.py
+invest-applet/invest/invest-applet.c
+invest-applet/invest/invest-chart.c
+invest-applet/invest/invest-preferences.c
+invest-applet/invest/invest-quotes.c
+invest-applet/invest/invest-widget.c
mini-commander/src/about.c
mini-commander/src/cmd_completion.c
mini-commander/src/command_line.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 35b1029..4740000 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -21,6 +21,7 @@ gweather/org.gnome.applets.GWeatherApplet.panel-applet.in
gweather/org.gnome.gnome-applets.gweather.gschema.xml.in
inhibit/org.gnome.InhibitApplet.panel-applet.in
invest-applet/data/org.gnome.applets.InvestApplet.panel-applet.in
+invest-applet/data/org.gnome.gnome-applets.invest.gschema.xml.in
mini-commander/src/org.gnome.applets.MiniCommanderApplet.panel-applet.in
mini-commander/src/org.gnome.gnome-applets.mini-commander.gschema.xml.in
modem-lights/org.gnome.applets.ModemApplet.panel-applet.in
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]