Recuento/src/main.vala

351 lines
11 KiB
Vala

const string S = Config.WS;
const string SQLITE_PATH = ".config" + S + "recuento.sqlite";
const string ENVIRONMENT_VARIABLE_DATADIR = "DATADIR_RECUENTO";
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" +
");"
};
string datadir_maybe_env () {
string datadir_env = GLib.Environment.get_variable(ENVIRONMENT_VARIABLE_DATADIR);
if (datadir_env != null) {
return datadir_env;
}
return Config.DATADIR;
}
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;
string preloaded_images_path;
string psoe_image;
string pp_image;
string vox_image;
void main () {
var app = new Adw.Application ("me.sergiotarxz.recuento", 0);
preloaded_images_path = datadir_maybe_env () + S + "recuento" + S + "preloaded_images";
psoe_image = preloaded_images_path + S + "psoe.jpg";
pp_image = preloaded_images_path + S + "pp.jpg";
vox_image = preloaded_images_path + S + "vox.jpg";
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<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) => {
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<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 ();
GLib.DirUtils.create_with_parents (GLib.Path.get_dirname (@"$home$S$SQLITE_PATH"), 700);
string final_sqlite_path = @"$home$S$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;
}