const string S = Config.WS; const string SQLITE_PATH = ".config" + S + "recuento.sqlite"; const string PRELOADED_IMAGES_PATH = Config.DATADIR + S + "recuento" + S + "preloaded_images"; const string PSOE_IMAGE = PRELOADED_IMAGES_PATH + S + "psoe.jpg"; const string PP_IMAGE = PRELOADED_IMAGES_PATH + S + "pp.jpg"; const string VOX_IMAGE = PRELOADED_IMAGES_PATH + S + "vox.jpg"; const string[] MIGRATIONS = { "CREATE TABLE options (key TEXT PRIMARY KEY, value TEXT);", "CREATE TABLE parties (" + "name TEXT PRIMARY KEY NOT NULL," + "image TEXT DEFAULT ''," + "votes INTEGER DEFAULT 0 NOT NULL" + ");" }; class Party { public string name; public string image; public uint64 votes = 0; public Party (string name, string image) { this.name = name; this.image = image; } } GLib.List list_party_widgets; void main () { var app = new Adw.Application ("me.sergiotarxz.recuento", 0); app.activate.connect ( () => { activate (app); }); app.run (); } void activate (Adw.Application app) { var app_window = new Adw.ApplicationWindow (app); var header_bar = new Adw.HeaderBar (); var scrolled_window = new Gtk.ScrolledWindow(); var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); var window_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); var parties_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 2); parties_container.margin_bottom = 50; scrolled_window.vexpand = true; Sqlite.Database db = get_db (); load_inital_data (db); app_window.title = "Votes count"; app_window.set_default_size (800, 800); main_box.append (parties_container); scrolled_window.set_child (main_box); window_box.append (header_bar); window_box.append (scrolled_window); app_window.set_content (window_box); list_party_widgets = create_list_parties_menu (db, parties_container, main_box); create_new_party_menu (parties_container, main_box); app_window.show (); } GLib.List create_list_parties_menu (Sqlite.Database db, Gtk.Box parties_container, Gtk.Box main_box) { parties_container.hexpand = true; parties_container.margin_start = 10; parties_container.margin_top = 10; parties_container.margin_end = 10; var list_inserted_widgets = fill_parties_container (db, parties_container); return (owned) list_inserted_widgets; } GLib.List fill_parties_container(Sqlite.Database db, Gtk.Box parties_container) { var list_inserted_widgets = new GLib.List(); var parties = list_parties (db); parties.foreach ((party) => { var party_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 10); var party_picture = new Gtk.Picture(); var party_name_widget = new Gtk.Label (party.name); var adjustment = new Gtk.Adjustment (party.votes, 0, 50000, 1, 1, 1); var votes_widget = new Gtk.SpinButton (adjustment, 0.1, 0); party_box.hexpand = true; party_picture.height_request = 200; party_picture.can_shrink = true; party_picture.set_filename (party.image); votes_widget.value_changed.connect (() => { var db_lambda = get_db (); party.votes = (uint64) votes_widget.get_value (); modify_party_votes (db_lambda, party); }); party_box.append (party_picture); party_box.append (party_name_widget); party_box.append (votes_widget); parties_container.append (party_box); list_inserted_widgets.append (party_box); }); return (owned) list_inserted_widgets; } void modify_party_votes (Sqlite.Database db, Party party) { Sqlite.Statement stmt; int rc; rc = db.prepare_v2 ("update parties set votes = ? where name = ?;", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (db.errmsg ()); return; } stmt.bind_int64(1, (int64) party.votes); stmt.bind_text(2, party.name); rc = stmt.step (); if (rc == Sqlite.ERROR) { GLib.critical (db.errmsg ()); } } GLib.List list_parties (Sqlite.Database db) { Sqlite.Statement stmt; int rc; var result_list = new GLib.List (); rc = db.prepare_v2 ("select name, image, votes from parties;", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (db.errmsg ()); return result_list; } while ((rc = stmt.step ()) == Sqlite.ROW) { string name = stmt.column_text (0); string image = stmt.column_text (1); uint64 votes = stmt.column_int64(2); var party = new Party(name, image); party.votes = votes; result_list.append (party); } return result_list; } void load_inital_data (Sqlite.Database db) { if (check_if_loaded_initial_data (db)) { return; } insert_party (db, "PSOE", PSOE_IMAGE); insert_party (db, "PP", PP_IMAGE); insert_party (db, "VOX", VOX_IMAGE); set_loaded_initial_data (db); } void set_loaded_initial_data (Sqlite.Database db) { Sqlite.Statement stmt; int rc; rc = db.prepare_v2 ("insert into options (key, value) values (?, ?)", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (db.errmsg ()); return; } stmt.bind_text(1, "loaded_initial_data"); stmt.bind_int64(2, 1); rc = stmt.step (); if (rc == Sqlite.ERROR) { GLib.critical (db.errmsg ()); } } bool check_if_loaded_initial_data (Sqlite.Database db) { Sqlite.Statement stmt; int rc; rc = db.prepare_v2 ("select value from options where key = ?;", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (db.errmsg ()); return true; } stmt.bind_text(1, "loaded_initial_data"); rc = stmt.step (); if (rc == Sqlite.ROW) { return stmt.column_int64(0) != 0; } else if (rc == Sqlite.DONE) { return false; } else if (rc == Sqlite.ERROR) { GLib.critical (db.errmsg ()); } return true; } bool insert_party (Sqlite.Database db, string name, string file) { Sqlite.Statement stmt; int rc; rc = db.prepare_v2 ("insert into parties (name, image) values (?, ?)", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (@"$(db.errmsg ())"); return false; } stmt.bind_text(1, name); stmt.bind_text(2, file); rc = stmt.step(); if (rc == Sqlite.ERROR) { GLib.critical (@"$(db.errmsg ())"); return false; } return true; } void create_new_party_menu (Gtk.Box parties_container, Gtk.Box box) { var container = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); var image_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 30); var label_title_party_menu = new Gtk.Label (""); var input_picture = new Gtk.Picture(); label_title_party_menu.set_markup("Insert a party"); image_container.set_halign(Gtk.Align.CENTER); container.append (label_title_party_menu); image_container.append (input_picture); container.append (image_container); create_input_party (parties_container, input_picture, container); box.append (container); } void create_input_party (Gtk.Box parties_container, Gtk.Picture input_picture, Gtk.Box container) { var party_name_widget = new Gtk.Entry (); var input_fields = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 30); var submit_button = new Gtk.Button.with_label ("Create"); input_fields.set_halign(Gtk.Align.CENTER); input_fields.append (submit_button); input_fields.append (party_name_widget); submit_button.clicked.connect (() => { // Sqlite dislikes lambdas. var db_lambda = get_db (); var file = input_picture.get_file (); var file_path = file != null ? file .get_path () : ""; if (file_path == null) { file_path = ""; } if (insert_party(db_lambda, party_name_widget.get_text (), file_path)) { for (uint i = 0; i < list_party_widgets.length (); i++) { unowned Gtk.Widget widget = list_party_widgets.nth_data(i); parties_container.remove (widget); } while (list_party_widgets.length () > 0) { unowned Gtk.Widget widget = list_party_widgets.nth_data(0); list_party_widgets.remove (widget); } var new_list_party_widgets = fill_parties_container (db_lambda, parties_container); foreach (unowned Gtk.Widget widget in new_list_party_widgets) { list_party_widgets.append (widget); } party_name_widget.text = ""; input_picture.set_filename(""); input_picture.height_request = 0; } }); create_select_party_image_button (input_picture, input_fields); container.append (input_fields); } void create_select_party_image_button (Gtk.Picture picture, Gtk.Box input_fields) { var button_select_image = new Gtk.Button.from_icon_name ("folder-symbolic"); button_select_image.clicked.connect (() => { var file_chooser = new Gtk.FileChooserDialog ("Select an image", null, Gtk.FileChooserAction.OPEN); file_chooser.response.connect (() => { picture.set_file(file_chooser.get_file ()); picture.height_request = 200; picture.can_shrink = true; picture.set_hexpand(false); picture.set_vexpand(false); }); file_chooser.show (); }); input_fields.append (button_select_image); } Sqlite.Database get_db () { Sqlite.Database db; string home = GLib.Environment.get_home_dir (); GLib.DirUtils.create_with_parents (GLib.Path.get_dirname (@"$home$S$SQLITE_PATH"), 700); string final_sqlite_path = @"$home$S$SQLITE_PATH"; print(final_sqlite_path); int rc = Sqlite.Database.open (final_sqlite_path, out db); apply_migrations (db); if (rc != Sqlite.OK) { GLib.critical ("Unable to open the database"); } return db; } void apply_migrations (Sqlite.Database db) { for (int64 i = get_current_migration(db); i < MIGRATIONS.length - 1; i++) { int rc; int64 next_migration = i + 1; Sqlite.Statement stmt; rc = db.prepare_v2 (MIGRATIONS[next_migration], -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (@"$(db.errmsg ())"); return; } rc = stmt.step(); if (rc == Sqlite.ERROR) { GLib.critical (@"$(db.errmsg ())"); return; } increment_migration (db); } } void increment_migration (Sqlite.Database db) { Sqlite.Statement stmt; int rc; rc = db.prepare_v2 ("update options set value=value + 1 where key = ?;", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (@"Unable to prepare query: $(db.errmsg ())"); return; } stmt.bind_text (1, "migration"); rc = stmt.step (); if (get_current_migration (db) != -1) { return; } rc = db.prepare_v2 ("insert into options (key, value) values (?, ?);", -1, out stmt); if (rc != Sqlite.OK) { GLib.critical (@"Unable to prepare query: $(db.errmsg ())"); return; } stmt.bind_text (1, "migration"); stmt.bind_int64 (2, 0); rc = stmt.step (); if (rc != Sqlite.DONE) { GLib.critical (@"Error executing the creation of migration column $(db.errmsg ())"); } } int64 get_current_migration (Sqlite.Database db) { Sqlite.Statement stmt; int rc; rc = db.prepare_v2 ( "select value from options where key = ?;", -1, out stmt); stmt.bind_text(1, "migration"); if (rc == Sqlite.OK && stmt.step () == Sqlite.ROW) { return stmt.column_int64 (0); } return -1; }