aboutsummarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntony Kellermann2018-06-17 20:41:10 -0400
committerAntony Kellermann2018-06-17 20:41:10 -0400
commitcc6d8b9c302fd8babba9eeb42de9933dd61451c8 (patch)
tree067be195e421be83f1e8f2f149da6b930ec8d267
parent9b7ef358cb19dd264e967e06fae2e81f9fc43ea6 (diff)
downloadaur-cc6d8b9c302fd8babba9eeb42de9933dd61451c8.tar.gz
Implemented basic GTK+ check window
-rw-r--r--gtk_win.c132
-rw-r--r--gtk_win.h43
-rw-r--r--main.c24
-rw-r--r--window_main.glade344
4 files changed, 533 insertions, 10 deletions
diff --git a/gtk_win.c b/gtk_win.c
new file mode 100644
index 000000000000..307952c48285
--- /dev/null
+++ b/gtk_win.c
@@ -0,0 +1,132 @@
+#include "gtk_win.h"
+
+char column_names[16][NUM_COLS];
+
+void window_main(void) {
+ gtk_init(NULL, NULL);
+ GtkBuilder* builder = gtk_builder_new();
+
+ // Read glade XML config file
+ gtk_builder_add_from_file(builder, "/usr/share/tick/window_main.glade", NULL);
+
+ // Get widgets
+ GtkWidget* window = GTK_WIDGET(gtk_builder_get_object(builder, "window_main"));
+ GtkListStore* list = GTK_LIST_STORE(gtk_builder_get_object(builder, "check_list"));
+ GtkTreeView* tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "check_tree_view"));
+ for (gint i = 0; i < NUM_COLS; i++) // Copy names of columns into array
+ strcpy(column_names[i], gtk_tree_view_column_get_title(gtk_tree_view_get_column(
+ tree_view, i)));
+
+ Info_Array* portfolio_data = portfolio_get_info_array(); // Portfolio data
+ format_cells(portfolio_data); // Format portfolio data to strings
+
+ Info* idx; // Append list store with portfolio data
+ for (size_t i = 0; i < portfolio_data->length; i++) {
+ GtkTreeIter iter;
+ idx = portfolio_data->array[i];
+ gtk_list_store_append(list, &iter);
+ gtk_list_store_set(list, &iter, AMOUNT, idx->famount, SYMBOL, idx->symbol, VALUE,
+ idx->fcurrent_value, SPENT, idx->ftotal_spent, PROFIT,
+ idx->fprofit_total, PROFIT_PERCENT, idx->fprofit_total_percent,
+ PROFIT_24H, idx->fprofit_last_close, PROFIT_24H_PERCENT,
+ idx->fprofit_last_close_percent, PROFIT_7D, idx->fprofit_7d,
+ PROFIT_7D_PERCENT, idx->fprofit_7d_percent, PROFIT_30D, idx->fprofit_30d,
+ PROFIT_30D_PERCENT, idx->fprofit_30d_percent, -1);
+ }
+
+ GtkTreeIter iter;
+ gtk_list_store_append(list, &iter); // Add totals
+ gtk_list_store_set(list, &iter, SYMBOL, "TOTALS", VALUE, portfolio_data->fcurrent_value, SPENT,
+ portfolio_data->ftotal_spent, PROFIT, portfolio_data->fprofit_total,
+ PROFIT_PERCENT, portfolio_data->fprofit_total_percent, PROFIT_24H,
+ portfolio_data->fprofit_last_close, PROFIT_24H_PERCENT,
+ portfolio_data->fprofit_last_close_percent, PROFIT_7D,
+ portfolio_data->fprofit_7d, PROFIT_7D_PERCENT,
+ portfolio_data->fprofit_7d_percent, PROFIT_30D, portfolio_data->fprofit_30d,
+ PROFIT_30D_PERCENT, portfolio_data->fprofit_30d_percent, -1);
+
+ gtk_builder_connect_signals(builder, NULL); // Connect signals
+ g_object_unref(builder);
+ gtk_widget_show_all(window);
+ gtk_main(); // Main loop
+ api_info_array_destroy(&portfolio_data);
+}
+
+void format_cells(Info_Array* portfolio_data) {
+ Info* idx;
+ for (size_t i = 0; i < portfolio_data->length; i++) {
+ idx = portfolio_data->array[i];
+ sprintf(idx->famount, "%.2lf", idx->amount);
+ sprintf(idx->ftotal_spent, "%.2lf", idx->total_spent);
+ sprintf(idx->fcurrent_value, "%.2lf", idx->current_value);
+ sprintf(idx->fprofit_total, "%.2lf", idx->profit_total);
+ sprintf(idx->fprofit_total_percent, "%.2lf", idx->profit_total_percent);
+ sprintf(idx->fprofit_last_close, "%.2lf", idx->profit_last_close);
+ sprintf(idx->fprofit_last_close_percent, "%.2lf", idx->profit_last_close_percent);
+ sprintf(idx->fprofit_7d, "%.2lf", idx->profit_7d);
+ sprintf(idx->fprofit_7d_percent, "%.2lf", idx->profit_7d_percent);
+ sprintf(idx->fprofit_30d, "%.2lf", idx->profit_30d);
+ sprintf(idx->fprofit_30d_percent, "%.2lf", idx->profit_30d_percent);
+ }
+ sprintf(portfolio_data->ftotal_spent, "%.2lf", portfolio_data->total_spent);
+ sprintf(portfolio_data->fcurrent_value, "%.2lf", portfolio_data->current_value);
+ sprintf(portfolio_data->fprofit_total, "%.2lf", portfolio_data->profit_total);
+ sprintf(portfolio_data->fprofit_total_percent, "%.2lf", portfolio_data->profit_total_percent);
+ sprintf(portfolio_data->fprofit_last_close, "%.2lf", portfolio_data->profit_last_close);
+ sprintf(portfolio_data->fprofit_last_close_percent, "%.2lf",
+ portfolio_data->profit_last_close_percent);
+ sprintf(portfolio_data->fprofit_7d, "%.2lf", portfolio_data->profit_7d);
+ sprintf(portfolio_data->fprofit_7d_percent, "%.2lf", portfolio_data->profit_7d_percent);
+ sprintf(portfolio_data->fprofit_30d, "%.2lf", portfolio_data->profit_30d);
+ sprintf(portfolio_data->fprofit_30d_percent, "%.2lf",
+ portfolio_data->profit_30d_percent);
+}
+
+void on_main_win_destroy(void) {
+ gtk_main_quit();
+}
+
+void on_column_clicked(GtkTreeViewColumn* column, GtkListStore* list_store) {
+ int idx = -1;
+ for (int i = 0; i < NUM_COLS; i++)
+ if (strcmp(gtk_tree_view_column_get_title(column), column_names[i]) == 0)
+ idx = i;
+
+ list_store_sort(list_store, (Col_Index) idx);
+}
+
+void list_store_sort(GtkListStore* list_store, Col_Index idx) {
+ GtkTreeModel* model = (GtkTreeModel*) list_store;
+ int loop_flag = 1;
+ gchar* str1, * str2, * sym1, * sym2;
+ while (loop_flag) { // Bubble sort
+ loop_flag = 0;
+ GtkTreeIter iter1, iter2;
+ gtk_tree_model_get_iter_first(model, &iter1);
+ gtk_tree_model_get_iter_first(model, &iter2);
+ if (!gtk_tree_model_iter_next(model, &iter2)) // List length 1
+ return;
+ do {
+ gtk_tree_model_get(model, &iter1, SYMBOL, &sym1, -1);
+ gtk_tree_model_get(model, &iter2, SYMBOL, &sym2, -1);
+
+ // Don't sort TOTALS
+ if (strcmp(sym1, "TOTALS") != 0 && strcmp(sym2, "TOTALS") != 0) {
+ gtk_tree_model_get(model, &iter1, idx, &str1, -1);
+ gtk_tree_model_get(model, &iter2, idx, &str2, -1);
+
+ // Compare doubles
+ if ((idx != SYMBOL && strtod(str1, NULL) < strtod(str2, NULL)) ||
+ (idx == SYMBOL && strcmp(str1, str2) > 0)) { // Compare strings
+ gtk_list_store_swap(list_store, &iter1, &iter2);
+ loop_flag = 1;
+ }
+ g_free(str1);
+ g_free(str2);
+ }
+ g_free(sym1);
+ g_free(sym2);
+ } while (gtk_tree_model_iter_next(model, &iter1) &&
+ gtk_tree_model_iter_next(model, &iter2));
+ }
+} \ No newline at end of file
diff --git a/gtk_win.h b/gtk_win.h
new file mode 100644
index 000000000000..dcb1e7a7c416
--- /dev/null
+++ b/gtk_win.h
@@ -0,0 +1,43 @@
+#ifndef TICK_GTK_WIN_H
+#define TICK_GTK_WIN_H
+
+#include <gtk/gtk.h>
+#include "portfolio.h"
+
+typedef enum column_index {
+ AMOUNT, SYMBOL, VALUE, SPENT, PROFIT, PROFIT_PERCENT, PROFIT_24H, PROFIT_24H_PERCENT, PROFIT_7D,
+ PROFIT_7D_PERCENT, PROFIT_30D, PROFIT_30D_PERCENT, NUM_COLS
+} Col_Index;
+
+/**
+ * Opens GTK+ Window and prints portfolio data to it. Currently cannot handle encrypted portfolios.
+ */
+void window_main(void);
+
+/**
+ * Formats text in Info structs for printing to window
+ * @param portfolio_data
+ */
+void format_cells(Info_Array* portfolio_data);
+
+/**
+ * Signal handler for destroying main window. Exits the program.
+ */
+void on_main_win_destroy(void);
+
+/**
+ * Signal handler for clicking column labels. Sorts the ListStore struct according to which
+ * column was clicked.
+ * @param column clicked column
+ * @param list_store portfolio data list
+ */
+void on_column_clicked(GtkTreeViewColumn* column, GtkListStore* list_store);
+
+/**
+ * Sorts a ListStore struct according to idx.
+ * @param list_store ListStore to sort
+ * @param idx column number
+ */
+void list_store_sort(GtkListStore* list_store, Col_Index idx);
+
+#endif \ No newline at end of file
diff --git a/main.c b/main.c
index cdeac363c7c8..19f63825be11 100644
--- a/main.c
+++ b/main.c
@@ -1,16 +1,15 @@
#include "portfolio.h"
#include "info.h"
+#include "gtk_win.h"
int main(int argc, char* argv[]) {
- if (argc < 2) {
- puts("Invalid arguments. Type \"man tick\" for help.");
- return 0;
+ char* cmd = NULL, * sym = NULL;
+ if (argc > 1) {
+ cmd = malloc(strlen(argv[1]) + 1);
+ pointer_alloc_check(cmd);
+ strcpy(cmd, argv[1]);
+ strtolower(cmd);
}
- char cmd[strlen(argv[1]) + 1];
- strcpy(cmd, argv[1]);
- strtolower(cmd);
-
- char* sym = NULL;
if (argc > 2) {
sym = malloc(strlen(argv[2]) + 1);
pointer_alloc_check(sym);
@@ -27,8 +26,12 @@ int main(int argc, char* argv[]) {
// Portfolio modify operation
int modop = -1;
- // News
- if (strcmp(cmd, "news") == 0 && (argc == 3 || argc == 4)) {
+ // GTK+ window
+ if (argc == 1)
+ window_main();
+
+ // News
+ else if (strcmp(cmd, "news") == 0 && (argc == 3 || argc == 4)) {
int num_articles = 3; // Default
if (argc == 4)
num_articles = (int) strtol(argv[3], NULL, 10);
@@ -103,6 +106,7 @@ int main(int argc, char* argv[]) {
}
free(portfolio_file);
free(sym);
+ free(cmd);
curl_global_cleanup();
return 0;
} \ No newline at end of file
diff --git a/window_main.glade b/window_main.glade
new file mode 100644
index 000000000000..fdde875888ed
--- /dev/null
+++ b/window_main.glade
@@ -0,0 +1,344 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1
+
+The MIT License (MIT)
+
+Copyright (c)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Authors:
+Antony Kellermann
+https://github.com/aokellermann/
+
+-->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <!-- interface-license-type mit -->
+ <!-- interface-name Tick -->
+ <!-- interface-description Command line stock and cryptocurrency portfolio tracker. -->
+ <!-- interface-authors Antony Kellermann\nhttps://github.com/aokellermann/ -->
+ <object class="GtkListStore" id="check_list">
+ <columns>
+ <!-- column-name Amount -->
+ <column type="gchararray"/>
+ <!-- column-name Symbol -->
+ <column type="gchararray"/>
+ <!-- column-name Value -->
+ <column type="gchararray"/>
+ <!-- column-name Spent -->
+ <column type="gchararray"/>
+ <!-- column-name Profit -->
+ <column type="gchararray"/>
+ <!-- column-name Profit1 -->
+ <column type="gchararray"/>
+ <!-- column-name Profit24H -->
+ <column type="gchararray"/>
+ <!-- column-name 24H% -->
+ <column type="gchararray"/>
+ <!-- column-name Profit7D -->
+ <column type="gchararray"/>
+ <!-- column-name 7D% -->
+ <column type="gchararray"/>
+ <!-- column-name Profit30D -->
+ <column type="gchararray"/>
+ <!-- column-name 30D% -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkApplicationWindow" id="window_main">
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scroll_window">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="check_tree_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscroll_policy">natural</property>
+ <property name="vscroll_policy">natural</property>
+ <property name="model">check_list</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">True</property>
+ <property name="show_expanders">False</property>
+ <property name="enable_grid_lines">both</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="amount_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">100</property>
+ <property name="title" translatable="yes">Amount</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="amount_renderer">
+ <property name="xalign">1</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="symbol_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">100</property>
+ <property name="title" translatable="yes">Symbol</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="symbol_renderer">
+ <property name="xalign">0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="value_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">100</property>
+ <property name="title" translatable="yes">Value</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="value_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="spent_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">100</property>
+ <property name="title" translatable="yes">Spent</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="spent_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">100</property>
+ <property name="title" translatable="yes">Profit</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_percent_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">65</property>
+ <property name="title" translatable="yes">%</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_percent_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_24h_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">65</property>
+ <property name="title" translatable="yes">24H</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_24h_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">6</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_24h_percent_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">65</property>
+ <property name="title" translatable="yes">24H%</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_24h_percent_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">7</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_7d_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">65</property>
+ <property name="title" translatable="yes">7D</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_7d_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">8</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_7d_percent_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">65</property>
+ <property name="title" translatable="yes">7D%</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_7d_percent_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">9</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_30d_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">80</property>
+ <property name="title" translatable="yes">30D</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_30d_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">10</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="profit_30d_percent_column">
+ <property name="resizable">True</property>
+ <property name="sizing">fixed</property>
+ <property name="min_width">65</property>
+ <property name="title" translatable="yes">30D%</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <signal name="clicked" handler="on_column_clicked" object="check_list" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="profit_30d_percent_renderer">
+ <property name="xalign">1</property>
+ <property name="alignment">right</property>
+ </object>
+ <attributes>
+ <attribute name="text">11</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>