aboutsummarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gtk_win.c205
-rw-r--r--gtk_win.h13
-rw-r--r--window_main.glade126
3 files changed, 264 insertions, 80 deletions
diff --git a/gtk_win.c b/gtk_win.c
index 1a345e2d41c3..e45edbd950e4 100644
--- a/gtk_win.c
+++ b/gtk_win.c
@@ -7,50 +7,59 @@ char column_names[16][NUM_COLS];
void window_main(void) {
gtk_init(NULL, NULL);
- // Read glade XML config file
+ // Set defaults
app.portfolio_data = NULL;
+ app.portfolio_string = NULL;
app.builder = gtk_builder_new();
- gtk_builder_add_from_file(app.builder, "/usr/share/tick/window_main.glade", NULL);
+ app.password[0] = '\0';
+
+ // Read glade XML config file
+ gtk_builder_add_from_file(app.builder, "window_main.glade", NULL);
+ // Copy names of columns into array
GtkTreeView* tree_view = GTK_TREE_VIEW(gtk_builder_get_object(app.builder, "check_tree_view"));
- for (gint i = 0; i < NUM_COLS; i++) // Copy names of columns into array
+ for (gint i = 0; i < NUM_COLS; i++)
strcpy(column_names[i], gtk_tree_view_column_get_title(gtk_tree_view_get_column(
tree_view, i)));
gtk_builder_connect_signals(app.builder, NULL); // Connect signals
+
+ // Show check window as default opening window
gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(app.builder, "check_window")));
gtk_main(); // Main loop
}
-void create_check_list(void) {
- if (app.portfolio_data != NULL)
- return;
-
+void check_list_create_from_string(void) {
app.portfolio_data = portfolio_info_array_init_from_portfolio_string(app.portfolio_string);
format_cells(app.portfolio_data);
- GtkTreeIter iters[app.portfolio_data->length + 1];
GtkListStore* pListStore = GTK_LIST_STORE(gtk_builder_get_object(app.builder, "check_list"));
Info* idx; // Append pListStore store with portfolio data
for (size_t i = 0; i < app.portfolio_data->length + 1; i++) {
- gtk_list_store_append(pListStore, &iters[i]);
+ GtkTreeIter iter;
+ gtk_list_store_append(pListStore, &iter);
if (i == app.portfolio_data->length) {
idx = app.portfolio_data->totals;
} else {
idx = app.portfolio_data->array[i];
- gtk_list_store_set(pListStore, &iters[i], AMOUNT, idx->famount, -1);
+ gtk_list_store_set(pListStore, &iter, AMOUNT, idx->famount, -1);
}
- gtk_list_store_set(pListStore, &iters[i], SYMBOL, idx->symbol, SPENT, idx->ftotal_spent,
- VALUE, "Loading...", -1);
+ gtk_list_store_set(pListStore, &iter, SYMBOL, idx->symbol, SPENT, idx->ftotal_spent, -1);
}
+}
- api_info_array_store_check_data(app.portfolio_data);
+void check_list_add_api_data(void) {
+ GtkListStore* pListStore = GTK_LIST_STORE(gtk_builder_get_object(app.builder, "check_list"));
format_cells(app.portfolio_data);
-
+ Info* idx;
+ GtkTreeIter iter;
for (size_t i = 0; i < app.portfolio_data->length + 1; i++) {
if (i == app.portfolio_data->length)
idx = app.portfolio_data->totals;
else idx = app.portfolio_data->array[i];
- gtk_list_store_set(pListStore, &iters[i], VALUE, idx->fcurrent_value, PROFIT,
+ if (i == 0)
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pListStore), &iter);
+ else gtk_tree_model_iter_next(GTK_TREE_MODEL(pListStore), &iter);
+ gtk_list_store_set(pListStore, &iter, VALUE, idx->fcurrent_value, 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,
@@ -60,24 +69,120 @@ void create_check_list(void) {
}
void on_load_button_clicked(GtkButton* button) {
- if (app.portfolio_string == NULL)
+ // Already loaded
+ if (app.portfolio_string != NULL && is_string_json_array(app.portfolio_string)
+ && app.portfolio_data != NULL)
+ return;
+
+ if (app.portfolio_string == NULL) { // Portfolio not loaded
+ FILE* fp = fopen(portfolio_file_path, "a"); // Touch file to get a valid String if empty
+ if (fp)
+ fclose(fp);
+
+ // Will be String with length 0 on new portfolio
app.portfolio_string = file_get_string(portfolio_file_path);
+ }
- // Error reading file
- if (app.portfolio_string == NULL) {
- gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(
- app.builder, "portfolio_file_get_string_error_dialog")));
+ if (app.portfolio_string == NULL) { // Error reading file
+ GValue gtext = G_VALUE_INIT;
+ g_value_init(&gtext, G_TYPE_STRING);
+ g_value_set_string(&gtext, "There was an error opening your portfolio file. This may be due"
+ " to the file not existing or invalid permissions on the file.");
+ GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(
+ app.builder, "generic_check_window_error_dialog"));
+ g_object_set_property((GObject*) dialog, "text", &gtext);
+ gtk_widget_show(dialog);
+ return;
+ }
+
+ if (app.portfolio_string->len == 0) { // On new portfolio, create empty JSON array
+ Json* jobj = json_object_new_array();
+ const char* str = json_object_get_string(jobj);
+ app.portfolio_string->len = strlen(str);
+ app.portfolio_string->data = realloc(app.portfolio_string->data, app
+ .portfolio_string->len + 1);
+ strcpy(app.portfolio_string->data, str);
+ json_object_put(jobj);
return;
}
- Json* jobj = json_tokener_parse(app.portfolio_string->data);
- if (jobj == NULL || !json_object_is_type(jobj, json_type_array)) { // Encrypted String
- GtkWidget* dialog = (GtkWidget*) GTK_MESSAGE_DIALOG(gtk_builder_get_object(
- app.builder, "get_password_dialog"));
+ // If encrypted file
+ if (!is_string_json_array(app.portfolio_string)) {
+ GtkWidget* dialog = (GtkWidget*) GTK_MESSAGE_DIALOG(gtk_builder_get_object(app.builder,
+ "get_password_dialog"));
gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(app.builder, "password_entry")), "");
+ gtk_widget_show(dialog); // Decode
+ return;
+ }
+
+ app.portfolio_data = portfolio_info_array_init_from_portfolio_string(app.portfolio_string);
+ if (app.portfolio_data != NULL) {
+ check_list_create_from_string();
+ api_info_array_store_check_data(app.portfolio_data);
+ check_list_add_api_data();
+ }
+}
+
+void on_modify_button_clicked(GtkButton* button) {
+ if (app.portfolio_string == NULL) {
+ GValue gtext = G_VALUE_INIT;
+ g_value_init(&gtext, G_TYPE_STRING);
+ g_value_set_string(&gtext, "Your portfolio hasn't been loaded yet. Click the button Load "
+ "Portfolio to the left.");
+ GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(
+ app.builder, "generic_check_window_error_dialog"));
+ g_object_set_property((GObject*) dialog, "text", &gtext);
gtk_widget_show(dialog);
- } else create_check_list(); // Unencrypted String
- json_object_put(jobj);
+ return;
+ }
+
+ GtkMessageDialog* dialog = GTK_MESSAGE_DIALOG(gtk_builder_get_object(
+ app.builder, "portfolio_modify_dialog"));
+
+ const gchar* label = gtk_button_get_label(button);
+ GValue gtext = G_VALUE_INIT;
+ g_value_init(&gtext, G_TYPE_STRING);
+ g_value_set_string(&gtext, label);
+
+ g_object_set_property((GObject*) dialog, "text", &gtext);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(app.builder, "modify_symbol_entry")), "");
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(app.builder, "modify_amount_entry")), "");
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(app.builder, "modify_spent_entry")), "");
+ gtk_widget_show((GtkWidget*) dialog);
+}
+
+void on_modify_entry_activate(GtkEntry* entry, gpointer dialog) {
+ gtk_widget_hide((GtkWidget*) dialog);
+ GtkEntry* symbol_entry = GTK_ENTRY(gtk_builder_get_object(app.builder, "modify_symbol_entry"));
+ GtkEntry* amount_entry = GTK_ENTRY(gtk_builder_get_object(app.builder, "modify_amount_entry"));
+ GtkEntry* spent_entry = GTK_ENTRY(gtk_builder_get_object(app.builder, "modify_spent_entry"));
+ const gchar* gsymbol = gtk_entry_get_text(symbol_entry);
+ char symbol[strlen(gsymbol) + 1];
+ strcpy(symbol, gsymbol);
+ strtoupper(symbol);
+ double amount = strtod(gtk_entry_get_text(amount_entry), NULL);
+ double spent = strtod(gtk_entry_get_text(spent_entry), NULL);
+
+ GValue gtext = G_VALUE_INIT;
+ g_value_init(&gtext, G_TYPE_STRING);
+ g_object_get_property(G_OBJECT(gtk_builder_get_object(app.builder, "portfolio_modify_dialog")),
+ "text", &gtext);
+ const gchar* text = g_value_get_string(&gtext);
+ int modop;
+ if (strcmp(text, "Add") == 0)
+ modop = ADD;
+ else if (strcmp(text, "Remove") == 0)
+ modop = REMOVE;
+ else modop = SET;
+
+ if (!portfolio_modify_string(app.portfolio_string, symbol, amount, spent, modop)) // Success
+ list_store_update();
+}
+
+void on_portfolio_modify_dialog_response(GtkDialog* dialog, gint response_id) {
+ if (response_id == GTK_RESPONSE_CANCEL)
+ gtk_widget_hide((GtkWidget*) dialog);
+ else on_modify_entry_activate(NULL, NULL);
}
void on_password_entry_activate(GtkEntry* entry, gpointer dialog) {
@@ -89,29 +194,43 @@ void on_password_entry_activate(GtkEntry* entry, gpointer dialog) {
char modified_pw[PASS_MAX];
sprintf(modified_pw, "%s\n", password); // See portfolio_write_encrypt_decrypt (rc4.c)
rc4_encode_string(app.portfolio_string, modified_pw);
- Json* jobj = json_tokener_parse(app.portfolio_string->data);
- if (jobj == NULL || !json_object_is_type(jobj, json_type_array)) { // Wrong password
+
+ if (!is_string_json_array(app.portfolio_string)) { // Wrong password
// Reverse the failed decryption to its original state
rc4_encode_string(app.portfolio_string, modified_pw);
- gtk_widget_show((GtkWidget*) GTK_MESSAGE_DIALOG(gtk_builder_get_object(
- app.builder, "wrong_password_dialog")));
- } else create_check_list();
- json_object_put(jobj);
+
+ // Error dialog
+ GValue gtext = G_VALUE_INIT;
+ g_value_init(&gtext, G_TYPE_STRING);
+ g_value_set_string(&gtext, "Wrong password!");
+ GtkWidget* err_dialog = GTK_WIDGET(gtk_builder_get_object(
+ app.builder, "generic_check_window_error_dialog"));
+ g_object_set_property((GObject*) err_dialog, "text", &gtext);
+ gtk_widget_show(err_dialog);
+ } else { // Correct password
+ // Copy password to app
+ strcpy(app.password, modified_pw);
+
+ // Load portfolio
+ on_load_button_clicked(NULL);
+ }
}
void on_get_password_dialog_response(GtkDialog* dialog, gint response_id, gpointer entry) {
- if (response_id == GTK_RESPONSE_CANCEL) {
+ if (response_id == GTK_RESPONSE_CANCEL)
gtk_widget_hide((GtkWidget*) dialog);
- return;
- }
- on_password_entry_activate(entry, dialog);
+ else on_password_entry_activate(entry, dialog);
}
-
void on_check_window_destroy(void) {
g_object_unref(app.builder);
- if (app.portfolio_data != NULL)
- api_info_array_destroy(&app.portfolio_data);
+ if (app.portfolio_string != NULL) {
+ if (app.password[0] != '\0')
+ rc4_encode_string(app.portfolio_string, app.password);
+ string_write_file(app.portfolio_string, portfolio_file_path);
+ }
+ string_destroy(&app.portfolio_string);
+ api_info_array_destroy(&app.portfolio_data);
gtk_main_quit();
}
@@ -193,4 +312,12 @@ void list_store_sort(GtkListStore* list_store, Col_Index idx) {
} while (gtk_tree_model_iter_next(model, &iter1) &&
gtk_tree_model_iter_next(model, &iter2));
}
+}
+
+void list_store_update(void) {
+ api_info_array_destroy(&app.portfolio_data);
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_builder_get_object(app.builder, "check_list")));
+ check_list_create_from_string();
+ api_info_array_store_check_data(app.portfolio_data);
+ check_list_add_api_data();
} \ No newline at end of file
diff --git a/gtk_win.h b/gtk_win.h
index 511115698a04..b8f5110af4a5 100644
--- a/gtk_win.h
+++ b/gtk_win.h
@@ -13,6 +13,7 @@ typedef struct app_data {
Info_Array* portfolio_data;
String* portfolio_string;
GtkBuilder* builder;
+ char password[PASS_MAX];
} App_Data;
/**
@@ -20,12 +21,20 @@ typedef struct app_data {
*/
void window_main(void);
-void create_check_list(void);
+void check_list_create_from_string(void);
+
+void check_list_add_api_data(void);
/** SIGNALS **/
void on_load_button_clicked(GtkButton* button);
+void on_modify_button_clicked(GtkButton* button);
+
+void on_modify_entry_activate(GtkEntry* entry, gpointer dialog);
+
+void on_portfolio_modify_dialog_response(GtkDialog* dialog, gint response_id);
+
void on_password_entry_activate(GtkEntry* entry, gpointer dialog);
void on_get_password_dialog_response(GtkDialog* dialog, gint response_id, gpointer entry);
@@ -58,4 +67,6 @@ void format_cells(Info_Array* portfolio_data);
*/
void list_store_sort(GtkListStore* list_store, Col_Index idx);
+void list_store_update(void);
+
#endif \ No newline at end of file
diff --git a/window_main.glade b/window_main.glade
index 3f9a3814b603..ad596b15c22e 100644
--- a/window_main.glade
+++ b/window_main.glade
@@ -64,6 +64,7 @@ https://github.com/aokellermann/
</object>
<object class="GtkApplicationWindow" id="check_window">
<property name="can_focus">False</property>
+ <property name="show_menubar">False</property>
<signal name="destroy" handler="on_check_window_destroy" swapped="no"/>
<child>
<placeholder/>
@@ -115,6 +116,7 @@ https://github.com/aokellermann/
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <signal name="clicked" handler="on_modify_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -128,6 +130,7 @@ https://github.com/aokellermann/
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <signal name="clicked" handler="on_modify_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -141,6 +144,7 @@ https://github.com/aokellermann/
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <signal name="clicked" handler="on_modify_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -459,20 +463,14 @@ https://github.com/aokellermann/
</object>
</child>
</object>
- <object class="GtkMessageDialog" id="get_password_dialog">
+ <object class="GtkMessageDialog" id="generic_check_window_error_dialog">
<property name="can_focus">False</property>
- <property name="modal">True</property>
- <property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="urgency_hint">True</property>
<property name="transient_for">check_window</property>
- <property name="message_type">other</property>
- <property name="buttons">ok-cancel</property>
- <property name="text" translatable="yes">Enter your password:</property>
- <signal name="close" handler="gtk_widget_hide" swapped="no"/>
+ <property name="message_type">error</property>
+ <property name="buttons">ok</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
- <signal name="response" handler="on_get_password_dialog_response" object="password_entry" swapped="no"/>
+ <signal name="response" handler="gtk_widget_hide" swapped="no"/>
<child>
<placeholder/>
</child>
@@ -493,33 +491,23 @@ https://github.com/aokellermann/
<property name="position">0</property>
</packing>
</child>
- <child>
- <object class="GtkEntry" id="password_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="invisible_char">●</property>
- <property name="max_width_chars">31</property>
- <property name="input_purpose">password</property>
- <signal name="activate" handler="on_password_entry_activate" object="get_password_dialog" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
</object>
</child>
</object>
- <object class="GtkMessageDialog" id="portfolio_file_get_string_error_dialog">
+ <object class="GtkMessageDialog" id="get_password_dialog">
<property name="can_focus">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="urgency_hint">True</property>
<property name="transient_for">check_window</property>
- <property name="message_type">error</property>
- <property name="buttons">ok</property>
- <property name="text" translatable="yes">There was an error opening your portfolio file. This may be due to the file not existing or invalid permissions on the file.</property>
- <signal name="response" handler="gtk_widget_hide" swapped="no"/>
+ <property name="message_type">question</property>
+ <property name="buttons">ok-cancel</property>
+ <property name="text" translatable="yes">Enter your password:</property>
+ <signal name="close" handler="gtk_widget_hide" swapped="no"/>
+ <signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
+ <signal name="response" handler="on_get_password_dialog_response" object="password_entry" swapped="no"/>
<child>
<placeholder/>
</child>
@@ -540,29 +528,41 @@ https://github.com/aokellermann/
<property name="position">0</property>
</packing>
</child>
+ <child>
+ <object class="GtkEntry" id="password_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <property name="max_width_chars">31</property>
+ <property name="input_purpose">password</property>
+ <signal name="activate" handler="on_password_entry_activate" object="get_password_dialog" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
</object>
</child>
</object>
- <object class="GtkMessageDialog" id="wrong_password_dialog">
+ <object class="GtkMessageDialog" id="portfolio_modify_dialog">
<property name="can_focus">False</property>
<property name="modal">True</property>
- <property name="window_position">center-on-parent</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="urgency_hint">True</property>
<property name="transient_for">check_window</property>
- <property name="message_type">error</property>
- <property name="buttons">ok</property>
- <property name="text" translatable="yes">Wrong password!</property>
+ <property name="message_type">question</property>
+ <property name="buttons">ok-cancel</property>
<signal name="close" handler="gtk_widget_hide" swapped="no"/>
<signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
- <signal name="response" handler="gtk_widget_hide" swapped="no"/>
+ <signal name="response" handler="on_portfolio_modify_dialog_response" swapped="no"/>
<child>
<placeholder/>
</child>
<child internal-child="vbox">
- <object class="GtkBox">
+ <object class="GtkBox" id="portfolio_modify_vbox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
@@ -578,6 +578,52 @@ https://github.com/aokellermann/
<property name="position">0</property>
</packing>
</child>
+ <child>
+ <object class="GtkEntry" id="modify_symbol_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">16</property>
+ <property name="caps_lock_warning">False</property>
+ <property name="placeholder_text" translatable="yes">Symbol</property>
+ <property name="input_purpose">alpha</property>
+ <signal name="activate" handler="on_modify_entry_activate" object="portfolio_modify_dialog" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="modify_amount_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">16</property>
+ <property name="placeholder_text" translatable="yes">Quantity</property>
+ <property name="input_purpose">digits</property>
+ <signal name="activate" handler="on_modify_entry_activate" object="portfolio_modify_dialog" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="modify_spent_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">16</property>
+ <property name="placeholder_text" translatable="yes">Price per share</property>
+ <property name="input_purpose">digits</property>
+ <signal name="activate" handler="on_modify_entry_activate" object="portfolio_modify_dialog" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
</object>
</child>
</object>