2022-06-30 10:21:45 +02:00
|
|
|
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";
|
2022-06-26 01:15:28 +02:00
|
|
|
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<Gtk.Widget> 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);
|
2022-06-30 10:00:12 +02:00
|
|
|
var parties_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 2);
|
|
|
|
parties_container.margin_bottom = 50;
|
2022-06-26 01:15:28 +02:00
|
|
|
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<Gtk.Widget> 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<Gtk.Widget> fill_parties_container(Sqlite.Database db,
|
|
|
|
Gtk.Box parties_container) {
|
|
|
|
var list_inserted_widgets = new GLib.List<Gtk.Widget>();
|
|
|
|
var parties = list_parties (db);
|
|
|
|
parties.foreach ((party) => {
|
2022-06-30 10:00:12 +02:00
|
|
|
var party_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 10);
|
2022-06-26 01:15:28 +02:00
|
|
|
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<Party> list_parties (Sqlite.Database db) {
|
|
|
|
Sqlite.Statement stmt;
|
|
|
|
int rc;
|
|
|
|
var result_list = new GLib.List<Party> ();
|
|
|
|
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("<big>Insert a party</big>");
|
|
|
|
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 ();
|
2022-06-30 10:21:45 +02:00
|
|
|
string final_sqlite_path = @"$home$S$SQLITE_PATH";
|
2022-06-30 10:07:51 +02:00
|
|
|
print(final_sqlite_path);
|
2022-06-26 01:15:28 +02:00
|
|
|
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;
|
|
|
|
}
|