pamac-classic/src/transaction.vala

1578 lines
54 KiB
Vala
Raw Normal View History

2014-10-22 13:44:02 -03:00
/*
* pamac-vala
*
* Copyright (C) 2014-2016 Guillaume Benoit <guillaume@manjaro.org>
2014-10-22 13:44:02 -03:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a get of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2016-08-28 05:57:22 -03:00
const string VERSION = "4.1.3";
2014-10-22 13:44:02 -03:00
namespace Pamac {
[DBus (name = "org.manjaro.pamac")]
interface Daemon : Object {
2016-02-02 05:28:07 -03:00
public abstract void set_environment_variables (HashTable<string,string> variables) throws IOError;
public abstract ErrorInfos get_current_error () throws IOError;
2015-08-20 10:11:18 -03:00
public abstract void start_get_authorization () throws IOError;
2015-03-04 11:55:36 -03:00
public abstract void start_write_pamac_config (HashTable<string,Variant> new_pamac_conf) throws IOError;
public abstract void start_write_alpm_config (HashTable<string,Variant> new_alpm_conf) throws IOError;
public abstract void start_write_mirrors_config (HashTable<string,Variant> new_mirrors_conf) throws IOError;
2015-08-20 10:11:18 -03:00
public abstract void start_generate_mirrors_list () throws IOError;
public abstract void clean_cache (uint keep_nb, bool only_uninstalled) throws IOError;
2015-03-04 11:55:36 -03:00
public abstract void start_set_pkgreason (string pkgname, uint reason) throws IOError;
2016-04-14 13:19:20 -03:00
public abstract AlpmPackage get_installed_pkg (string pkgname) throws IOError;
2016-02-02 05:28:07 -03:00
public abstract void start_refresh (bool force) throws IOError;
2016-04-14 13:19:20 -03:00
public abstract bool get_checkspace () throws IOError;
public abstract string[] get_ignorepkgs () throws IOError;
public abstract bool should_hold (string pkgname) throws IOError;
public abstract uint get_pkg_reason (string pkgname) throws IOError;
public abstract uint get_pkg_origin (string pkgname) throws IOError;
public abstract async AlpmPackage[] get_installed_pkgs () throws IOError;
public abstract async AlpmPackage[] get_foreign_pkgs () throws IOError;
public abstract async AlpmPackage[] get_orphans () throws IOError;
public abstract AlpmPackage find_installed_satisfier (string depstring) throws IOError;
public abstract AlpmPackage get_sync_pkg (string pkgname) throws IOError;
public abstract AlpmPackage find_sync_satisfier (string depstring) throws IOError;
public abstract async AlpmPackage[] search_pkgs (string search_string) throws IOError;
public abstract async AURPackage[] search_in_aur (string search_string) throws IOError;
public abstract async string[] get_aur_build_list (string pkgname) throws IOError;
public abstract string[] get_repos_names () throws IOError;
public abstract async AlpmPackage[] get_repo_pkgs (string repo) throws IOError;
public abstract string[] get_groups_names () throws IOError;
public abstract async AlpmPackage[] get_group_pkgs (string groupname) throws IOError;
public abstract AlpmPackageDetails get_pkg_details (string pkgname) throws IOError;
public abstract async AURPackageDetails get_aur_details (string pkgname) throws IOError;
public abstract string[] get_pkg_uninstalled_optdeps (string pkgname) throws IOError;
2016-02-02 05:28:07 -03:00
public abstract void start_get_updates (bool check_aur_updates) throws IOError;
2016-08-27 09:51:34 -03:00
public abstract void start_sysupgrade_prepare (bool enable_downgrade, string[] temporary_ignorepkgs) throws IOError;
public abstract void start_trans_prepare (int transflags, string[] to_install, string[] to_remove, string[] to_load) throws IOError;
2014-10-22 13:44:02 -03:00
public abstract void choose_provider (int provider) throws IOError;
2016-04-14 13:19:20 -03:00
public abstract TransactionSummary get_transaction_summary () throws IOError;
2015-03-04 11:55:36 -03:00
public abstract void start_trans_commit () throws IOError;
2016-02-02 05:28:07 -03:00
public abstract void trans_release () throws IOError;
[DBus (no_reply = true)]
2014-10-22 13:44:02 -03:00
public abstract void trans_cancel () throws IOError;
[DBus (no_reply = true)]
public abstract void quit () throws IOError;
2014-12-03 12:02:14 -03:00
public signal void emit_event (uint primary_event, uint secondary_event, string[] details);
2014-10-22 13:44:02 -03:00
public signal void emit_providers (string depend, string[] providers);
2016-02-02 05:28:07 -03:00
public signal void emit_progress (uint progress, string pkgname, uint percent, uint n_targets, uint current_target);
2014-10-22 13:44:02 -03:00
public signal void emit_download (string filename, uint64 xfered, uint64 total);
public signal void emit_totaldownload (uint64 total);
public signal void emit_log (uint level, string msg);
2015-03-04 11:55:36 -03:00
public signal void set_pkgreason_finished ();
2016-02-02 05:28:07 -03:00
public signal void refresh_finished (bool success);
public signal void get_updates_finished (Updates updates);
public signal void trans_prepare_finished (bool success);
public signal void trans_commit_finished (bool success);
2015-08-20 10:11:18 -03:00
public signal void get_authorization_finished (bool authorized);
2016-02-02 05:28:07 -03:00
public signal void write_pamac_config_finished (bool recurse, uint64 refresh_period, bool no_update_hide_icon,
2015-08-24 11:13:18 -03:00
bool enable_aur, bool search_aur, bool check_aur_updates,
2015-08-20 10:11:18 -03:00
bool no_confirm_build);
public signal void write_alpm_config_finished (bool checkspace);
public signal void write_mirrors_config_finished (string choosen_country, string choosen_generation_method);
public signal void generate_mirrors_list_data (string line);
public signal void generate_mirrors_list_finished ();
2014-10-22 13:44:02 -03:00
}
2016-02-02 05:28:07 -03:00
public enum Mode {
MANAGER,
UPDATER
}
2014-10-22 13:44:02 -03:00
public class Transaction: Object {
enum Type {
STANDARD = (1 << 0),
UPDATE = (1 << 1),
BUILD = (1 << 2)
}
Daemon daemon;
2016-04-14 13:19:20 -03:00
Pamac.Config pamac_config;
public bool check_aur_updates { get { return pamac_config.check_aur_updates; } }
public bool enable_aur { get { return pamac_config.enable_aur; } }
public unowned GLib.HashTable<string,string> environment_variables { get {return pamac_config.environment_variables; } }
public bool no_confirm_build { get { return pamac_config.no_confirm_build; } }
public bool no_update_hide_icon { get { return pamac_config.no_update_hide_icon; } }
2016-05-13 12:57:43 -03:00
public bool recurse { get { return pamac_config.recurse; } }
2016-04-14 13:19:20 -03:00
public uint64 refresh_period { get { return pamac_config.refresh_period; } }
public bool search_aur { get { return pamac_config.search_aur; } }
2016-02-02 05:28:07 -03:00
2016-04-14 13:19:20 -03:00
//Alpm.TransFlag
int flags;
2016-02-02 05:28:07 -03:00
2016-04-14 13:19:20 -03:00
public GenericSet<string?> to_install;
public GenericSet<string?> to_remove;
public GenericSet<string?> to_load;
public GenericSet<string?> to_build;
2016-04-14 13:19:20 -03:00
GenericSet<string?> previous_to_install;
GenericSet<string?> previous_to_remove;
public GenericSet<string?> transaction_summary;
public GenericSet<string?> temporary_ignorepkgs;
2016-04-14 13:19:20 -03:00
public Mode mode { get; set; }
2014-10-22 13:44:02 -03:00
uint64 total_download;
uint64 already_downloaded;
string previous_textbar;
2016-02-02 05:28:07 -03:00
float previous_percent;
2014-10-22 13:44:02 -03:00
string previous_filename;
uint pulse_timeout_id;
2014-10-22 13:44:02 -03:00
bool sysupgrade_after_trans;
2016-02-02 05:28:07 -03:00
bool enable_downgrade;
uint64 previous_xfered;
uint64 download_rate;
uint64 rates_nb;
Timer timer;
bool success;
2016-08-27 09:51:34 -03:00
StringBuilder warning_textbuffer;
2014-10-22 13:44:02 -03:00
//dialogs
TransactionSumDialog transaction_sum_dialog;
2016-05-17 13:11:49 -04:00
public ProgressBox progress_box;
Vte.Terminal term;
Vte.Pty pty;
public Gtk.Grid term_grid;
2014-10-22 13:44:02 -03:00
//parent window
2016-04-14 13:19:20 -03:00
public Gtk.ApplicationWindow? application_window { get; private set; }
2014-10-22 13:44:02 -03:00
2016-04-14 13:19:20 -03:00
public signal void start_transaction ();
2016-05-17 13:11:49 -04:00
public signal void important_details_outpout (bool must_show);
2016-04-14 13:19:20 -03:00
public signal void alpm_handle_refreshed ();
public signal void finished (bool success);
public signal void set_pkgreason_finished ();
public signal void get_updates_finished (Updates updates);
public signal void write_pamac_config_finished (bool recurse, uint64 refresh_period, bool no_update_hide_icon,
bool enable_aur, bool search_aur, bool check_aur_updates,
bool no_confirm_build);
public signal void write_alpm_config_finished (bool checkspace);
public signal void write_mirrors_config_finished (string choosen_country, string choosen_generation_method);
public signal void generate_mirrors_list ();
2014-10-22 13:44:02 -03:00
2016-02-02 05:28:07 -03:00
public Transaction (Gtk.ApplicationWindow? application_window) {
pamac_config = new Pamac.Config ("/etc/pamac.conf");
2016-04-14 13:19:20 -03:00
flags = (1 << 4); //Alpm.TransFlag.CASCADE
2016-02-02 05:28:07 -03:00
if (pamac_config.recurse) {
2016-04-14 13:19:20 -03:00
flags |= (1 << 5); //Alpm.TransFlag.RECURSE
2016-02-02 05:28:07 -03:00
}
2016-04-14 13:19:20 -03:00
to_install = new GenericSet<string?> (str_hash, str_equal);
to_remove = new GenericSet<string?> (str_hash, str_equal);
to_load = new GenericSet<string?> (str_hash, str_equal);
to_build = new GenericSet<string?> (str_hash, str_equal);
2016-04-14 13:19:20 -03:00
previous_to_install = new GenericSet<string?> (str_hash, str_equal);
previous_to_remove = new GenericSet<string?> (str_hash, str_equal);
transaction_summary = new GenericSet<string?> (str_hash, str_equal);
temporary_ignorepkgs = new GenericSet<string?> (str_hash, str_equal);
2014-10-22 13:44:02 -03:00
connecting_dbus_signals ();
//creating dialogs
2016-02-02 05:28:07 -03:00
this.application_window = application_window;
transaction_sum_dialog = new TransactionSumDialog (application_window);
2016-05-17 13:11:49 -04:00
progress_box = new ProgressBox ();
progress_box.progressbar.text = "";
//creating terminal
term = new Vte.Terminal ();
term.scroll_on_output = false;
term.expand = true;
term.height_request = 200;
term.visible = true;
// creating pty for term
try {
pty = term.pty_new_sync (Vte.PtyFlags.NO_HELPER);
} catch (Error e) {
stderr.printf ("Error: %s\n", e.message);
}
// add term in a grid with a scrollbar
term_grid = new Gtk.Grid ();
term_grid.expand = true;
term_grid.visible = true;
var sb = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL, term.vadjustment);
sb.visible = true;
term_grid.attach (term, 0, 0, 1, 1);
term_grid.attach (sb, 1, 0, 1, 1);
2014-10-24 10:29:39 -03:00
// connect to child_exited signal which will only be emit after a call to watch_child
2016-05-17 13:11:49 -04:00
term.child_exited.connect (on_term_child_exited);
2014-10-22 13:44:02 -03:00
// progress data
previous_textbar = "";
previous_filename = "";
sysupgrade_after_trans = false;
2016-02-02 05:28:07 -03:00
timer = new Timer ();
success = false;
2016-08-27 09:51:34 -03:00
warning_textbuffer = new StringBuilder ();
}
public void run_history_dialog () {
var file = GLib.File.new_for_path ("/var/log/pacman.log");
if (!file.query_exists ()) {
GLib.stderr.printf ("File '%s' doesn't exist.\n", file.get_path ());
} else {
StringBuilder text = new StringBuilder ();
try {
// Open file for reading and wrap returned FileInputStream into a
// DataInputStream, so we can read line by line
var dis = new DataInputStream (file.read ());
string line;
// Read lines until end of file (null) is reached
while ((line = dis.read_line ()) != null) {
// construct text in reverse order
text.prepend (line + "\n");
}
} catch (GLib.Error e) {
GLib.stderr.printf ("%s\n", e.message);
}
var history_dialog = new HistoryDialog (application_window);
history_dialog.textview.buffer.set_text (text.str, (int) text.len);
history_dialog.show ();
history_dialog.response.connect (() => {
history_dialog.destroy ();
});
while (Gtk.events_pending ()) {
Gtk.main_iteration ();
}
}
2016-02-02 05:28:07 -03:00
}
public async void run_preferences_dialog () {
SourceFunc callback = run_preferences_dialog.callback;
ulong handler_id = daemon.get_authorization_finished.connect ((authorized) => {
if (authorized) {
var preferences_dialog = new PreferencesDialog (this);
preferences_dialog.run ();
preferences_dialog.destroy ();
while (Gtk.events_pending ()) {
Gtk.main_iteration ();
}
}
Idle.add((owned) callback);
});
start_get_authorization ();
yield;
daemon.disconnect (handler_id);
}
public void run_about_dialog () {
Gtk.show_about_dialog (
application_window,
"program_name", "Pamac",
"logo_icon_name", "system-software-install",
"comments", dgettext (null, "A Gtk3 frontend for libalpm"),
"copyright", "Copyright Ā© 2016 Guillaume Benoit",
"version", VERSION,
"license_type", Gtk.License.GPL_3_0,
"website", "http://manjaro.org");
}
2016-02-02 05:28:07 -03:00
public ErrorInfos get_current_error () {
try {
return daemon.get_current_error ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
return ErrorInfos ();
}
2014-10-22 13:44:02 -03:00
}
2015-08-20 10:11:18 -03:00
public void start_get_authorization () {
try {
daemon.start_get_authorization ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2015-03-04 11:55:36 -03:00
public void start_write_pamac_config (HashTable<string,Variant> new_pamac_conf) {
2014-10-22 13:44:02 -03:00
try {
2015-03-04 11:55:36 -03:00
daemon.start_write_pamac_config (new_pamac_conf);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2015-03-04 11:55:36 -03:00
public void start_write_alpm_config (HashTable<string,Variant> new_alpm_conf) {
try {
2015-03-04 11:55:36 -03:00
daemon.start_write_alpm_config (new_alpm_conf);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2015-03-04 11:55:36 -03:00
public void start_write_mirrors_config (HashTable<string,Variant> new_mirrors_conf) {
try {
2015-03-04 11:55:36 -03:00
daemon.start_write_mirrors_config (new_mirrors_conf);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2016-05-17 13:11:49 -04:00
void spawn_in_term (string[] args, out Pid child_pid = null) {
try {
Process.spawn_async (null, args, null, SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD, pty.child_setup, out child_pid);
} catch (SpawnError e) {
stderr.printf ("SpawnError: %s\n", e.message);
}
term.set_pty (pty);
2016-04-14 13:19:20 -03:00
}
2016-05-17 13:11:49 -04:00
void reset_progress_box (string action) {
spawn_in_term ({"echo", action});
progress_box.action_label.label = action;
progress_box.progressbar.fraction = 0;
progress_box.progressbar.text = "";
2016-04-14 13:19:20 -03:00
}
2016-08-27 09:51:34 -03:00
void start_progressbar_pulse () {
pulse_timeout_id = Timeout.add (500, (GLib.SourceFunc) progress_box.progressbar.pulse);
}
void stop_progressbar_pulse () {
if (pulse_timeout_id != 0) {
Source.remove (pulse_timeout_id);
pulse_timeout_id = 0;
progress_box.progressbar.fraction = 0;
}
}
2015-08-20 10:11:18 -03:00
public void start_generate_mirrors_list () {
string action = dgettext (null, "Refreshing mirrors list") + "...";
2016-05-17 13:11:49 -04:00
reset_progress_box (action);
2016-08-27 09:51:34 -03:00
start_progressbar_pulse ();
2016-05-17 13:11:49 -04:00
important_details_outpout (false);
generate_mirrors_list ();
2015-08-20 10:11:18 -03:00
try {
daemon.start_generate_mirrors_list ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
2016-08-27 09:51:34 -03:00
stop_progressbar_pulse ();
2015-08-20 10:11:18 -03:00
}
}
public void clean_cache (uint keep_nb, bool only_uninstalled) {
try {
daemon.clean_cache (keep_nb, only_uninstalled);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2016-04-14 13:19:20 -03:00
public void start_set_pkgreason (string pkgname, uint reason) {
try {
2016-04-14 13:19:20 -03:00
daemon.start_set_pkgreason (pkgname, reason);
2014-10-22 13:44:02 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2016-02-02 05:28:07 -03:00
public void start_refresh (bool force) {
2014-10-26 08:30:04 -03:00
string action = dgettext (null, "Synchronizing package databases") + "...";
2016-05-17 13:11:49 -04:00
reset_progress_box (action);
2015-03-04 11:55:36 -03:00
try {
2015-08-20 10:11:18 -03:00
daemon.refresh_finished.connect (on_refresh_finished);
daemon.start_refresh (force);
2015-03-04 11:55:36 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
2015-08-20 10:11:18 -03:00
daemon.refresh_finished.disconnect (on_refresh_finished);
success = false;
2016-02-27 06:11:04 -03:00
finish_transaction ();
2015-03-04 11:55:36 -03:00
}
}
2016-04-14 13:19:20 -03:00
public bool get_checkspace () {
bool checkspace = false;
try {
checkspace = daemon.get_checkspace ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return checkspace;
}
public string[] get_ignorepkgs () {
string[] ignorepkgs = {};
try {
ignorepkgs = daemon.get_ignorepkgs ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return ignorepkgs;
}
public AlpmPackage get_installed_pkg (string pkgname) {
try {
return daemon.get_installed_pkg (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
return AlpmPackage () {
name = "",
version = "",
desc = "",
repo = ""
};
}
}
public AlpmPackage find_installed_satisfier (string depstring) {
try {
return daemon.find_installed_satisfier (depstring);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
return AlpmPackage () {
name = "",
version = "",
desc = "",
repo = ""
};
}
}
public bool should_hold (string pkgname) {
bool should_hold = false;
try {
should_hold = daemon.should_hold (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return should_hold;
}
public uint get_pkg_reason (string pkgname) {
uint reason = 0;
try {
reason = daemon.get_pkg_reason (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return reason;
}
public uint get_pkg_origin (string pkgname) {
uint origin = 0;
try {
origin = daemon.get_pkg_origin (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return origin;
}
public async AlpmPackage[] get_installed_pkgs () {
AlpmPackage[] pkgs = {};
try {
pkgs = yield daemon.get_installed_pkgs ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public async AlpmPackage[] get_foreign_pkgs () {
AlpmPackage[] pkgs = {};
try {
pkgs = yield daemon.get_foreign_pkgs ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public async AlpmPackage[] get_orphans () {
AlpmPackage[] pkgs = {};
try {
pkgs = yield daemon.get_orphans ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public AlpmPackage get_sync_pkg (string pkgname) {
try {
return daemon.get_sync_pkg (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
return AlpmPackage () {
name = "",
version = "",
desc = "",
repo = ""
};
}
}
public AlpmPackage find_sync_satisfier (string depstring) {
try {
return daemon.find_sync_satisfier (depstring);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
return AlpmPackage () {
name = "",
version = "",
desc = "",
repo = ""
};
}
}
public async AlpmPackage[] search_pkgs (string search_string) {
AlpmPackage[] pkgs = {};
try {
pkgs = yield daemon.search_pkgs (search_string);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public async AURPackage[] search_in_aur (string search_string) {
AURPackage[] pkgs = {};
try {
pkgs = yield daemon.search_in_aur (search_string);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public async string[] get_aur_build_list (string pkgname) {
string[] names = {};
try {
names = yield daemon.get_aur_build_list (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return names;
}
public string[] get_repos_names () {
string[] repos_names = {};
try {
repos_names = daemon.get_repos_names ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return repos_names;
}
public async AlpmPackage[] get_repo_pkgs (string repo) {
AlpmPackage[] pkgs = {};
try {
pkgs = yield daemon.get_repo_pkgs (repo);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public string[] get_groups_names () {
string[] groups_names = {};
try {
groups_names = daemon.get_groups_names ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return groups_names;
}
public async AlpmPackage[] get_group_pkgs (string group_name) {
AlpmPackage[] pkgs = {};
try {
pkgs = yield daemon.get_group_pkgs (group_name);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkgs;
}
public string[] get_pkg_uninstalled_optdeps (string pkgname) {
string[] optdeps = {};
try {
optdeps = daemon.get_pkg_uninstalled_optdeps (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return optdeps;
}
public AlpmPackageDetails get_pkg_details (string pkgname) {
try {
return daemon.get_pkg_details (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
return AlpmPackageDetails () {
name = "",
version = "",
desc = "",
repo = "",
url = "",
packager = "",
builddate = "",
installdate = "",
reason = "",
has_signature = ""
};
}
}
public async AURPackageDetails get_aur_details (string pkgname) {
var pkg = AURPackageDetails () {
name = "",
version = "",
desc = "",
packagebase = "",
url = "",
maintainer = ""
};
try {
pkg = yield daemon.get_aur_details (pkgname);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
return pkg;
}
2016-02-02 05:28:07 -03:00
public void start_get_updates () {
daemon.get_updates_finished.connect (on_get_updates_finished);
try {
daemon.start_get_updates (pamac_config.enable_aur && pamac_config.check_aur_updates);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
success = false;
finish_transaction ();
}
}
void start_get_updates_for_sysupgrade () {
daemon.get_updates_finished.connect (on_get_updates_for_sysupgrade_finished);
2015-03-04 11:55:36 -03:00
try {
2016-02-02 05:28:07 -03:00
daemon.start_get_updates (pamac_config.enable_aur && pamac_config.check_aur_updates);
2015-03-04 11:55:36 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
success = false;
2016-02-27 06:11:04 -03:00
finish_transaction ();
2015-03-04 11:55:36 -03:00
}
}
2016-08-27 09:51:34 -03:00
void sysupgrade_simple (bool enable_downgrade) {
progress_box.progressbar.fraction = 0;
string[] temporary_ignorepkgs_ = {};
foreach (unowned string pkgname in temporary_ignorepkgs) {
2016-08-27 09:51:34 -03:00
temporary_ignorepkgs_ += pkgname;
}
2014-10-22 13:44:02 -03:00
try {
2016-08-27 09:51:34 -03:00
// this will respond with trans_prepare_finished signal
daemon.start_sysupgrade_prepare (enable_downgrade, temporary_ignorepkgs_);
2014-10-22 13:44:02 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
2016-08-27 09:51:34 -03:00
success = false;
finish_transaction ();
2014-10-22 13:44:02 -03:00
}
}
2016-02-02 05:28:07 -03:00
public void sysupgrade (bool enable_downgrade) {
this.enable_downgrade = enable_downgrade;
2014-10-26 08:30:04 -03:00
string action = dgettext (null, "Starting full system upgrade") + "...";
2016-05-17 13:11:49 -04:00
reset_progress_box (action);
start_get_updates_for_sysupgrade ();
2016-02-02 05:28:07 -03:00
}
void on_get_updates_finished (Updates updates) {
daemon.get_updates_finished.disconnect (on_get_updates_finished);
get_updates_finished (updates);
}
void on_get_updates_for_sysupgrade_finished (Updates updates) {
daemon.get_updates_finished.disconnect (on_get_updates_for_sysupgrade_finished);
2016-02-02 05:28:07 -03:00
// get syncfirst updates
if (updates.is_syncfirst) {
clear_lists ();
if (mode == Mode.MANAGER) {
sysupgrade_after_trans = true;
}
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in updates.repos_updates) {
to_install.add (infos.name);
2016-02-02 05:28:07 -03:00
}
// run as a standard transaction
run ();
} else {
if (updates.aur_updates.length != 0) {
2015-03-04 11:55:36 -03:00
clear_lists ();
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in updates.aur_updates) {
if (!(infos.name in temporary_ignorepkgs)) {
2016-02-02 05:28:07 -03:00
to_build.add (infos.name);
2015-03-04 11:55:36 -03:00
}
}
2016-08-27 09:51:34 -03:00
if (updates.repos_updates.length != 0) {
sysupgrade_simple (enable_downgrade);
} else {
on_trans_prepare_finished (true);
}
2016-02-02 05:28:07 -03:00
} else {
2016-08-27 09:51:34 -03:00
if (updates.repos_updates.length != 0) {
sysupgrade_simple (enable_downgrade);
} else {
finish_transaction ();
stop_progressbar_pulse ();
2016-08-27 09:51:34 -03:00
}
2014-10-22 13:44:02 -03:00
}
2016-02-02 05:28:07 -03:00
}
2014-10-22 13:44:02 -03:00
}
public void clear_lists () {
2016-04-14 13:19:20 -03:00
to_install.remove_all ();
to_remove.remove_all ();
to_build.remove_all ();
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
void clear_previous_lists () {
previous_to_install.remove_all ();
previous_to_remove.remove_all ();
}
2014-10-22 13:44:02 -03:00
public void run () {
string action = dgettext (null, "Preparing") + "...";
2016-05-17 13:11:49 -04:00
reset_progress_box (action);
2014-10-22 13:44:02 -03:00
// run
2016-04-14 13:19:20 -03:00
if (to_install.length == 0
&& to_remove.length == 0
&& to_load.length == 0
&& to_build.length != 0) {
2014-10-22 13:44:02 -03:00
// there only AUR packages to build so no need to prepare transaction
2016-02-02 05:28:07 -03:00
on_trans_prepare_finished (true);
2014-10-22 13:44:02 -03:00
} else {
2016-08-27 09:51:34 -03:00
string [] to_install_ = {};
string [] to_remove_ = {};
string [] to_load_ = {};
foreach (unowned string name in to_install) {
to_install_ += name;
}
foreach (unowned string name in to_remove) {
to_remove_ += name;
}
foreach (unowned string path in to_load) {
to_load_ += path;
}
2014-10-22 13:44:02 -03:00
try {
2016-08-27 09:51:34 -03:00
daemon.start_trans_prepare (flags, to_install_, to_remove_, to_load_);
2014-10-22 13:44:02 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
2016-02-02 05:28:07 -03:00
success = false;
2016-08-27 09:51:34 -03:00
finish_transaction ();
2014-10-22 13:44:02 -03:00
}
}
}
void choose_provider (string depend, string[] providers) {
2016-02-02 05:28:07 -03:00
var choose_provider_dialog = new ChooseProviderDialog (application_window);
choose_provider_dialog.title = dgettext (null, "Choose a provider for %s").printf (depend);
unowned Gtk.Box box = choose_provider_dialog.get_content_area ();
Gtk.RadioButton? last_radiobutton = null;
Gtk.RadioButton? first_radiobutton = null;
2016-02-02 05:28:07 -03:00
foreach (unowned string provider in providers) {
var radiobutton = new Gtk.RadioButton.with_label_from_widget (last_radiobutton, provider);
radiobutton.visible = true;
// active first provider
if (last_radiobutton == null) {
radiobutton.active = true;
first_radiobutton = radiobutton;
}
last_radiobutton = radiobutton;
box.add (radiobutton);
2016-02-02 05:28:07 -03:00
}
2014-10-22 13:44:02 -03:00
choose_provider_dialog.run ();
// get active provider
int index = 0;
// list is given in reverse order so reverse it !
SList<unowned Gtk.RadioButton> list = last_radiobutton.get_group ().copy ();
list.reverse ();
foreach (var radiobutton in list) {
if (radiobutton.active) {
try {
daemon.choose_provider (index);
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
index++;
2014-10-22 13:44:02 -03:00
}
choose_provider_dialog.destroy ();
while (Gtk.events_pending ()) {
Gtk.main_iteration ();
}
2014-10-22 13:44:02 -03:00
}
Type set_transaction_sum () {
2015-01-02 18:29:04 -03:00
// return 0 if transaction_sum is empty, 2, if there are only aur updates, 1 otherwise
Type type = 0;
2014-10-22 13:44:02 -03:00
uint64 dsize = 0;
2016-04-14 13:19:20 -03:00
transaction_summary.remove_all ();
var summary = TransactionSummary ();
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.clear ();
try {
2016-04-14 13:19:20 -03:00
summary = daemon.get_transaction_summary ();
2014-10-22 13:44:02 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
2016-04-14 13:19:20 -03:00
var iter = Gtk.TreeIter ();
if (summary.to_remove.length > 0) {
type |= Type.STANDARD;
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in summary.to_remove) {
transaction_summary.add (infos.name);
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.insert_with_values (out iter, -1,
2016-04-14 13:19:20 -03:00
1, infos.name,
2, infos.old_version);
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
Gtk.TreePath path = transaction_sum_dialog.sum_list.get_path (iter);
int pos = (path.get_indices ()[0]) - (summary.to_remove.length - 1);
transaction_sum_dialog.sum_list.get_iter (out iter, new Gtk.TreePath.from_indices (pos));
transaction_sum_dialog.sum_list.set (iter, 0, "<b>%s</b>".printf (dgettext (null, "To remove") + ":"));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
if (summary.to_downgrade.length > 0) {
type |= Type.STANDARD;
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in summary.to_downgrade) {
dsize += infos.download_size;
transaction_summary.add (infos.name);
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.insert_with_values (out iter, -1,
2016-04-14 13:19:20 -03:00
1, infos.name,
2, infos.new_version,
3, "(%s)".printf (infos.old_version));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
Gtk.TreePath path = transaction_sum_dialog.sum_list.get_path (iter);
int pos = (path.get_indices ()[0]) - (summary.to_downgrade.length - 1);
transaction_sum_dialog.sum_list.get_iter (out iter, new Gtk.TreePath.from_indices (pos));
transaction_sum_dialog.sum_list.set (iter, 0, "<b>%s</b>".printf (dgettext (null, "To downgrade") + ":"));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
if (to_build.length > 0) {
type |= Type.BUILD;
2016-04-14 13:19:20 -03:00
foreach (unowned string name in to_build) {
transaction_summary.add (name);
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.insert_with_values (out iter, -1,
2016-04-14 13:19:20 -03:00
1, name);
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
Gtk.TreePath path = transaction_sum_dialog.sum_list.get_path (iter);
int pos = (path.get_indices ()[0]) - ((int) to_build.length - 1);
transaction_sum_dialog.sum_list.get_iter (out iter, new Gtk.TreePath.from_indices (pos));
transaction_sum_dialog.sum_list.set (iter, 0, "<b>%s</b>".printf (dgettext (null, "To build") + ":"));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
if (summary.to_install.length > 0) {
type |= Type.STANDARD;
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in summary.to_install) {
dsize += infos.download_size;
transaction_summary.add (infos.name);
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.insert_with_values (out iter, -1,
2016-04-14 13:19:20 -03:00
1, infos.name,
2, infos.new_version);
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
Gtk.TreePath path = transaction_sum_dialog.sum_list.get_path (iter);
int pos = (path.get_indices ()[0]) - (summary.to_install.length - 1);
transaction_sum_dialog.sum_list.get_iter (out iter, new Gtk.TreePath.from_indices (pos));
transaction_sum_dialog.sum_list.set (iter, 0, "<b>%s</b>".printf (dgettext (null, "To install") + ":"));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
if (summary.to_reinstall.length > 0) {
type |= Type.STANDARD;
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in summary.to_reinstall) {
dsize += infos.download_size;
transaction_summary.add (infos.name);
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.insert_with_values (out iter, -1,
2016-04-14 13:19:20 -03:00
1, infos.name,
2, infos.old_version);
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
Gtk.TreePath path = transaction_sum_dialog.sum_list.get_path (iter);
int pos = (path.get_indices ()[0]) - (summary.to_reinstall.length - 1);
transaction_sum_dialog.sum_list.get_iter (out iter, new Gtk.TreePath.from_indices (pos));
transaction_sum_dialog.sum_list.set (iter, 0, "<b>%s</b>".printf (dgettext (null, "To reinstall") + ":"));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
if (summary.to_upgrade.length > 0) {
type |= Type.UPDATE;
2015-01-03 10:52:26 -03:00
if (mode != Mode.UPDATER) {
2016-04-14 13:19:20 -03:00
foreach (unowned UpdateInfos infos in summary.to_upgrade) {
dsize += infos.download_size;
transaction_summary.add (infos.name);
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.sum_list.insert_with_values (out iter, -1,
2016-04-14 13:19:20 -03:00
1, infos.name,
2, infos.new_version,
3, "(%s)".printf (infos.old_version));
2014-10-22 13:44:02 -03:00
}
2016-04-14 13:19:20 -03:00
Gtk.TreePath path = transaction_sum_dialog.sum_list.get_path (iter);
int pos = (path.get_indices ()[0]) - (summary.to_upgrade.length - 1);
transaction_sum_dialog.sum_list.get_iter (out iter, new Gtk.TreePath.from_indices (pos));
transaction_sum_dialog.sum_list.set (iter, 0, "<b>%s</b>".printf (dgettext (null, "To update") + ":"));
2014-10-22 13:44:02 -03:00
}
}
2015-03-04 11:55:36 -03:00
if (dsize == 0) {
transaction_sum_dialog.top_label.visible = false;
2015-03-04 11:55:36 -03:00
} else {
transaction_sum_dialog.top_label.set_markup ("<b>%s: %s</b>".printf (dgettext (null, "Total download size"), format_size (dsize)));
transaction_sum_dialog.top_label.visible = true;
2014-10-22 13:44:02 -03:00
}
2015-01-03 10:52:26 -03:00
return type;
2014-10-22 13:44:02 -03:00
}
2015-03-04 11:55:36 -03:00
public void start_commit () {
2014-10-30 10:44:09 -03:00
try {
2015-03-04 11:55:36 -03:00
daemon.start_trans_commit ();
2014-10-30 10:44:09 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
success = false;
2016-02-27 06:11:04 -03:00
finish_transaction ();
2014-10-30 10:44:09 -03:00
}
2014-10-22 13:44:02 -03:00
}
public void build_aur_packages () {
string action = dgettext (null, "Building packages") + "...";
2016-05-17 13:11:49 -04:00
reset_progress_box (action);
term.grab_focus ();
2016-08-27 09:51:34 -03:00
start_progressbar_pulse ();
2016-05-17 13:11:49 -04:00
important_details_outpout (true);
start_transaction ();
string[] cmds = {"yaourt", "-S"};
2015-08-20 10:11:18 -03:00
if (pamac_config.no_confirm_build) {
cmds += "--noconfirm";
}
2016-02-02 05:28:07 -03:00
foreach (unowned string name in to_build) {
2014-10-24 10:29:39 -03:00
cmds += name;
}
2014-10-24 10:29:39 -03:00
Pid child_pid;
2016-05-17 13:11:49 -04:00
spawn_in_term (cmds, out child_pid);
2014-10-24 10:29:39 -03:00
// watch_child is needed in order to have the child_exited signal emitted
2016-05-17 13:11:49 -04:00
term.watch_child (child_pid);
2016-04-14 13:19:20 -03:00
//~ foreach (unowned string pkgname in to_build) {
//~ stdout.printf("aur deps for %s:\n", pkgname);
//~ get_aur_build_list.begin (pkgname, (obj, res) => {
//~ string[] names = get_aur_build_list.end (res);
//~ foreach (unowned string name in names) {
//~ stdout.printf("\t%s\n", name);
//~ }
//~ });
//~ }
}
2014-10-22 13:44:02 -03:00
public void cancel () {
try {
daemon.trans_cancel ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
2016-05-17 13:11:49 -04:00
progress_box.hide ();
spawn_in_term ({"/usr/bin/echo", dgettext (null, "Transaction cancelled") + ".\n"});
2016-08-27 09:51:34 -03:00
warning_textbuffer = new StringBuilder ();
2014-10-22 13:44:02 -03:00
}
public void release () {
try {
daemon.trans_release ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
2016-08-27 09:51:34 -03:00
warning_textbuffer = new StringBuilder ();
2014-10-22 13:44:02 -03:00
}
public void stop_daemon () {
try {
daemon.quit ();
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
2014-12-03 12:02:14 -03:00
void on_emit_event (uint primary_event, uint secondary_event, string[] details) {
2016-04-14 13:19:20 -03:00
string? action = null;
string? detailed_action = null;
2014-12-03 12:02:14 -03:00
switch (primary_event) {
case 0: //special case: wait for database lock
action = dgettext (null, "Waiting for another package manager to quit") + "...";
start_progressbar_pulse ();
break;
2016-04-14 13:19:20 -03:00
case 1: //Alpm.Event.Type.CHECKDEPS_START
action = dgettext (null, "Checking dependencies") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 3: //Alpm.Event.Type.FILECONFLICTS_START
action = dgettext (null, "Checking file conflicts") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 5: //Alpm.Event.Type.RESOLVEDEPS_START
action = dgettext (null, "Resolving dependencies") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 7: //Alpm.Event.Type.INTERCONFLICTS_START
action = dgettext (null, "Checking inter-conflicts") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 9: //Alpm.Event.Type.TRANSACTION_START
start_transaction ();
2016-02-02 05:28:07 -03:00
break;
2016-04-14 13:19:20 -03:00
case 11: //Alpm.Event.Type.PACKAGE_OPERATION_START
2014-12-03 12:02:14 -03:00
switch (secondary_event) {
2016-05-17 13:11:49 -04:00
// special case handle differently
2016-04-14 13:19:20 -03:00
case 1: //Alpm.Package.Operation.INSTALL
2014-12-03 12:02:14 -03:00
previous_filename = details[0];
2016-04-14 13:19:20 -03:00
string msg = dgettext (null, "Installing %s").printf (details[0]) + "...";
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = msg;
spawn_in_term ({"echo", dgettext (null, "Installing %s").printf ("%s (%s)".printf (details[0], details[1])) + "..."});
2014-12-03 12:02:14 -03:00
break;
2016-04-14 13:19:20 -03:00
case 2: //Alpm.Package.Operation.UPGRADE
2014-12-03 12:02:14 -03:00
previous_filename = details[0];
2016-04-14 13:19:20 -03:00
string msg = dgettext (null, "Upgrading %s").printf (details[0]) + "...";
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = msg;
spawn_in_term ({"echo", dgettext (null, "Upgrading %s").printf ("%s (%s -> %s)".printf (details[0], details[1], details[2])) + "..."});
2014-12-03 12:02:14 -03:00
break;
2016-04-14 13:19:20 -03:00
case 3: //Alpm.Package.Operation.REINSTALL
2014-12-03 12:02:14 -03:00
previous_filename = details[0];
2016-04-14 13:19:20 -03:00
string msg = dgettext (null, "Reinstalling %s").printf (details[0]) + "...";
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = msg;
spawn_in_term ({"echo", dgettext (null, "Reinstalling %s").printf ("%s (%s)".printf (details[0], details[1])) + "..."});
2014-12-03 12:02:14 -03:00
break;
2016-04-14 13:19:20 -03:00
case 4: //Alpm.Package.Operation.DOWNGRADE
2014-12-03 12:02:14 -03:00
previous_filename = details[0];
2016-04-14 13:19:20 -03:00
string msg = dgettext (null, "Downgrading %s").printf (details[0]) + "...";
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = msg;
spawn_in_term ({"echo", dgettext (null, "Downgrading %s").printf ("%s (%s -> %s)".printf (details[0], details[1], details[2])) + "..."});
2014-12-03 12:02:14 -03:00
break;
2016-04-14 13:19:20 -03:00
case 5: //Alpm.Package.Operation.REMOVE
2014-12-03 12:02:14 -03:00
previous_filename = details[0];
2016-04-14 13:19:20 -03:00
string msg = dgettext (null, "Removing %s").printf (details[0]) + "...";
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = msg;
spawn_in_term ({"echo", dgettext (null, "Removing %s").printf ("%s (%s)".printf (details[0], details[1])) + "..."});
2014-12-03 12:02:14 -03:00
break;
}
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 13: //Alpm.Event.Type.INTEGRITY_START
action = dgettext (null, "Checking integrity") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 15: //Alpm.Event.Type.LOAD_START
action = dgettext (null, "Loading packages files") + "...";
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 17: //Alpm.Event.Type.DELTA_INTEGRITY_START
action = dgettext (null, "Checking delta integrity") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 19: //Alpm.Event.Type.DELTA_PATCHES_START
action = dgettext (null, "Applying deltas") + "...";
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 21: //Alpm.Event.Type.DELTA_PATCH_START
detailed_action = dgettext (null, "Generating %s with %s").printf (details[0], details[1]) + "...";
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 22: //Alpm.Event.Type.DELTA_PATCH_DONE
detailed_action = dgettext (null, "Generation succeeded") + "...";
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 23: //Alpm.Event.Type.DELTA_PATCH_FAILED
detailed_action = dgettext (null, "Generation failed") + "...";
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 24: //Alpm.Event.Type.SCRIPTLET_INFO
progress_box.action_label.label = dgettext (null, "Configuring %s").printf (previous_filename) + "...";
2016-04-14 13:19:20 -03:00
detailed_action = details[0].replace ("\n", "");
2016-05-17 13:11:49 -04:00
important_details_outpout (false);
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 25: //Alpm.Event.Type.RETRIEVE_START
action = dgettext (null, "Downloading") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 28: //Alpm.Event.Type.PKGDOWNLOAD_START
2016-05-17 13:11:49 -04:00
// special case handle differently
spawn_in_term ({"echo", dgettext (null, "Downloading %s").printf (details[0]) + "..."});
2016-04-14 13:19:20 -03:00
string name_version_release = details[0].slice (0, details[0].last_index_of_char ('-'));
string name_version = name_version_release.slice (0, name_version_release.last_index_of_char ('-'));
string name = name_version.slice (0, name_version.last_index_of_char ('-'));
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = dgettext (null, "Downloading %s").printf (name) + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 31: //Alpm.Event.Type.DISKSPACE_START
action = dgettext (null, "Checking available disk space") + "...";
2014-12-03 12:02:14 -03:00
break;
2016-04-14 13:19:20 -03:00
case 33: //Alpm.Event.Type.OPTDEP_REMOVAL
detailed_action = dgettext (null, "%s optionally requires %s").printf (details[0], details[1]);
2016-08-27 09:51:34 -03:00
warning_textbuffer.append (detailed_action + "\n");
2014-10-26 08:30:04 -03:00
break;
2016-04-14 13:19:20 -03:00
case 34: //Alpm.Event.Type.DATABASE_MISSING
detailed_action = dgettext (null, "Database file for %s does not exist").printf (details[0]);
break;
case 35: //Alpm.Event.Type.KEYRING_START
action = dgettext (null, "Checking keyring") + "...";
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 37: //Alpm.Event.Type.KEY_DOWNLOAD_START
action = dgettext (null, "Downloading required keys") + "...";
2014-12-03 12:02:14 -03:00
break;
2016-04-14 13:19:20 -03:00
case 39: //Alpm.Event.Type.PACNEW_CREATED
detailed_action = dgettext (null, "%s installed as %s.pacnew").printf (details[0]);
break;
case 40: //Alpm.Event.Type.PACSAVE_CREATED
detailed_action = dgettext (null, "%s installed as %s.pacsave").printf (details[0]);
break;
case 41: //Alpm.Event.Type.HOOK_START
switch (secondary_event) {
case 1: //Alpm.HookWhen.PRE_TRANSACTION
action = dgettext (null, "Running pre-transaction hooks") + "...";
break;
case 2: //Alpm.HookWhen.POST_TRANSACTION
action = dgettext (null, "Running post-transaction hooks") + "...";
break;
default:
break;
}
break;
case 43: // Alpm.Event.Type.HOOK_RUN_START
float fraction = (float) int.parse (details[2]) / int.parse (details[3]);
if (fraction != previous_percent) {
previous_percent = fraction;
2016-05-17 13:11:49 -04:00
progress_box.progressbar.fraction = fraction;
2016-04-14 13:19:20 -03:00
}
2016-07-02 04:25:07 -04:00
string textbar = "%s/%s".printf (details[2], details[3]);
if (textbar != previous_textbar) {
previous_textbar = textbar;
progress_box.progressbar.text = textbar;
}
2016-04-14 13:19:20 -03:00
if (details[1] != "") {
2016-05-17 13:11:49 -04:00
detailed_action = details[1];
2016-04-14 13:19:20 -03:00
} else {
2016-05-17 13:11:49 -04:00
detailed_action = details[0];
2016-04-14 13:19:20 -03:00
}
2014-12-03 12:02:14 -03:00
break;
2014-10-22 13:44:02 -03:00
default:
break;
}
2016-04-14 13:19:20 -03:00
if (action != null) {
2016-05-17 13:11:49 -04:00
progress_box.action_label.label = action;
spawn_in_term ({"echo", action});
2016-04-14 13:19:20 -03:00
}
if (detailed_action != null) {
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", detailed_action});
2015-03-18 12:07:18 -03:00
}
2014-10-22 13:44:02 -03:00
}
void on_emit_providers (string depend, string[] providers) {
choose_provider (depend, providers);
}
2016-02-02 05:28:07 -03:00
void on_emit_progress (uint progress, string pkgname, uint percent, uint n_targets, uint current_target) {
float fraction;
2014-10-22 13:44:02 -03:00
switch (progress) {
2016-04-14 13:19:20 -03:00
case 0: //Alpm.Progress.ADD_START
case 1: //Alpm.Progress.UPGRADE_START
case 2: //Alpm.Progress.DOWNGRADE_START
case 3: //Alpm.Progress.REINSTALL_START
case 4: //Alpm.Progress.REMOVE_START
2016-02-02 05:28:07 -03:00
fraction = ((float) (current_target - 1) / n_targets) + ((float) percent / (100 * n_targets));
2014-10-22 13:44:02 -03:00
break;
2016-04-14 13:19:20 -03:00
case 5: //Alpm.Progress.CONFLICTS_START
case 6: //Alpm.Progress.DISKSPACE_START
case 7: //Alpm.Progress.INTEGRITY_START
case 8: //Alpm.Progress.LOAD_START
case 9: //Alpm.Progress.KEYRING_START
2014-10-22 13:44:02 -03:00
default:
2016-02-02 05:28:07 -03:00
fraction = (float) percent / 100;
2014-10-22 13:44:02 -03:00
break;
}
string textbar = "%lu/%lu".printf (current_target, n_targets);
if (textbar != previous_textbar) {
previous_textbar = textbar;
2016-05-17 13:11:49 -04:00
progress_box.progressbar.text = textbar;
2014-10-22 13:44:02 -03:00
}
if (fraction != previous_percent) {
previous_percent = fraction;
2016-05-17 13:11:49 -04:00
progress_box.progressbar.fraction = fraction;
2014-10-22 13:44:02 -03:00
}
}
void on_emit_download (string filename, uint64 xfered, uint64 total) {
2016-02-02 05:28:07 -03:00
var text = new StringBuilder ();
float fraction;
2014-10-22 13:44:02 -03:00
if (total_download > 0) {
2016-02-02 05:28:07 -03:00
if (xfered == 0) {
2016-05-17 13:11:49 -04:00
// start download pkg is handled by Alpm.Event.Type.PKGDOWNLOAD_START
2016-02-02 05:28:07 -03:00
previous_xfered = 0;
fraction = previous_percent;
text.append (previous_textbar);
timer.start ();
2015-03-18 12:07:18 -03:00
} else {
if (timer.elapsed () > 0) {
download_rate = ((download_rate * rates_nb) + (uint64) ((xfered - previous_xfered) / timer.elapsed ())) / (rates_nb + 1);
rates_nb++;
}
2016-02-02 05:28:07 -03:00
previous_xfered = xfered;
uint64 downloaded_total = xfered + already_downloaded;
fraction = (float) downloaded_total / total_download;
if (fraction <= 1) {
text.append ("%s/%s ".printf (format_size (xfered + already_downloaded), format_size (total_download)));
uint64 remaining_seconds = 0;
if (download_rate > 0) {
remaining_seconds = (total_download - downloaded_total) / download_rate;
}
// display remaining time after 5s and only if more than 10s are remaining
if (remaining_seconds > 9 && rates_nb > 9) {
2016-02-02 05:28:07 -03:00
if (remaining_seconds <= 50) {
text.append (dgettext (null, "About %u seconds remaining").printf ((uint) Math.ceilf ((float) remaining_seconds / 10) * 10));
} else {
uint remaining_minutes = (uint) Math.ceilf ((float) remaining_seconds / 60);
text.append (dngettext (null, "About %lu minute remaining",
"About %lu minutes remaining", remaining_minutes).printf (remaining_minutes));
}
}
} else {
text.append ("%s".printf (format_size (xfered + already_downloaded)));
}
if (xfered == total) {
previous_filename = "";
already_downloaded += total;
} else {
timer.start ();
}
2015-03-18 12:07:18 -03:00
}
2014-10-22 13:44:02 -03:00
} else {
2016-02-02 05:28:07 -03:00
if (xfered == 0) {
previous_xfered = 0;
download_rate = 0;
rates_nb = 0;
fraction = 0;
timer.start ();
2016-04-14 13:19:20 -03:00
if (filename.has_suffix (".db")) {
string action = dgettext (null, "Refreshing %s").printf (filename.replace (".db", "")) + "...";
reset_progress_box (action);
2016-04-14 13:19:20 -03:00
}
2016-02-02 05:28:07 -03:00
} else if (xfered == total) {
timer.stop ();
fraction = 1;
previous_filename = "";
2015-03-18 12:07:18 -03:00
} else {
if (timer.elapsed () > 0) {
download_rate = ((download_rate * rates_nb) + (uint64) ((xfered - previous_xfered) / timer.elapsed ())) / (rates_nb + 1);
rates_nb++;
}
2016-02-02 05:28:07 -03:00
previous_xfered = xfered;
fraction = (float) xfered / total;
if (fraction <= 1) {
text.append ("%s/%s ".printf (format_size (xfered), format_size (total)));
uint64 remaining_seconds = 0;
if (download_rate > 0) {
remaining_seconds = (total - xfered) / download_rate;
}
// display remaining time after 5s and only if more than 10s are remaining
if (remaining_seconds > 9 && rates_nb > 9) {
2016-02-02 05:28:07 -03:00
if (remaining_seconds <= 50) {
text.append (dgettext (null, "About %u seconds remaining").printf ((uint) Math.ceilf ((float) remaining_seconds / 10) * 10));
} else {
uint remaining_minutes = (uint) Math.ceilf ((float) remaining_seconds / 60);
text.append (dngettext (null, "About %lu minute remaining",
"About %lu minutes remaining", remaining_minutes).printf (remaining_minutes));
}
}
} else {
text.append ("%s".printf (format_size (xfered)));
}
// reinitialize timer
timer.start ();
2014-11-16 07:31:44 -03:00
}
2015-03-18 12:07:18 -03:00
}
2016-02-02 05:28:07 -03:00
if (fraction != previous_percent) {
previous_percent = fraction;
2016-05-17 13:11:49 -04:00
progress_box.progressbar.fraction = fraction;
2014-10-22 13:44:02 -03:00
}
2016-02-02 05:28:07 -03:00
if (text.str != previous_textbar) {
previous_textbar = text.str;
2016-05-17 13:11:49 -04:00
progress_box.progressbar.text = text.str;
2014-11-16 07:31:44 -03:00
}
2014-10-22 13:44:02 -03:00
}
void on_emit_totaldownload (uint64 total) {
2016-02-02 05:28:07 -03:00
download_rate = 0;
rates_nb = 0;
previous_percent = 0;
previous_textbar = "";
2014-10-22 13:44:02 -03:00
total_download = total;
2016-02-02 05:28:07 -03:00
// this is emitted at the end of the total download
// with the value 0 so stop our timer
if (total == 0) {
timer.stop ();
2016-05-17 13:11:49 -04:00
progress_box.progressbar.text = "";
2016-02-02 05:28:07 -03:00
}
2014-10-22 13:44:02 -03:00
}
void on_emit_log (uint level, string msg) {
// msg ends with \n
string? line = null;
2016-04-14 13:19:20 -03:00
if (level == 1) { //Alpm.LogLevel.ERROR
if (previous_filename != "") {
line = dgettext (null, "Error") + ": " + previous_filename + ": " + msg;
} else {
line = dgettext (null, "Error") + ": " + msg;
}
2016-05-17 13:11:49 -04:00
important_details_outpout (false);
2016-04-14 13:19:20 -03:00
} else if (level == (1 << 1)) { //Alpm.LogLevel.WARNING
2015-03-18 12:12:35 -03:00
// do not show warning when manjaro-system remove db.lck
if (previous_filename != "manjaro-system") {
if (previous_filename != "") {
line = dgettext (null, "Warning") + ": " + previous_filename + ": " + msg;
} else {
line = dgettext (null, "Warning") + ": " + msg;
}
2016-08-27 09:51:34 -03:00
warning_textbuffer.append (msg);
2015-03-18 12:07:18 -03:00
}
2014-10-22 13:44:02 -03:00
}
if (line != null) {
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", "-n", line});
2014-10-22 13:44:02 -03:00
}
}
void show_warnings () {
2016-08-27 09:51:34 -03:00
if (warning_textbuffer.len > 0) {
var dialog = new Gtk.Dialog.with_buttons (dgettext (null, "Warning"),
application_window,
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR);
dialog.deletable = false;
unowned Gtk.Widget widget = dialog.add_button (dgettext (null, "_Close"), Gtk.ResponseType.CLOSE);
widget.can_focus = true;
widget.has_focus = true;
widget.can_default = true;
widget.has_default = true;
var scrolledwindow = new Gtk.ScrolledWindow (null, null);
2016-08-27 09:51:34 -03:00
var label = new Gtk.Label (warning_textbuffer.str);
label.margin = 12;
scrolledwindow.visible = true;
label.visible = true;
scrolledwindow.add (label);
scrolledwindow.expand = true;
unowned Gtk.Box box = dialog.get_content_area ();
box.add (scrolledwindow);
dialog.default_width = 600;
dialog.default_height = 300;
dialog.run ();
dialog.destroy ();
2016-08-27 09:51:34 -03:00
warning_textbuffer = new StringBuilder ();
2014-10-22 13:44:02 -03:00
}
}
2016-04-14 13:19:20 -03:00
void display_error (string message, string[] details) {
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", "-n", message});
var dialog = new Gtk.MessageDialog (application_window,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
message);
2016-04-14 13:19:20 -03:00
if (details.length != 0) {
var textbuffer = new StringBuilder ();
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", ":"});
2016-04-14 13:19:20 -03:00
foreach (unowned string detail in details) {
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", detail});
textbuffer.append (detail + "\n");
2016-02-27 06:11:04 -03:00
}
dialog.secondary_text = textbuffer.str;
2016-04-14 13:19:20 -03:00
}
dialog.run ();
dialog.destroy ();
2016-04-14 13:19:20 -03:00
}
void handle_error (ErrorInfos error) {
if (error.message != "") {
2016-05-17 13:11:49 -04:00
reset_progress_box ("");
2016-04-14 13:19:20 -03:00
display_error (error.message, error.details);
2016-02-27 06:11:04 -03:00
}
finish_transaction ();
}
void finish_transaction () {
2016-04-14 13:19:20 -03:00
transaction_summary.remove_all ();
2016-05-17 13:11:49 -04:00
reset_progress_box ("");
finished (success);
success = false;
2014-10-22 13:44:02 -03:00
}
void on_refresh_finished (bool success) {
stop_progressbar_pulse ();
this.success = success;
clear_lists ();
2016-02-02 05:28:07 -03:00
if (success) {
2016-03-15 05:27:35 -03:00
finished (success);
2016-05-17 13:11:49 -04:00
reset_progress_box ("");
2016-03-15 05:27:35 -03:00
success = false;
2014-10-22 13:44:02 -03:00
} else {
2016-02-02 05:28:07 -03:00
handle_error (get_current_error ());
2014-10-22 13:44:02 -03:00
}
previous_filename = "";
2015-08-20 10:11:18 -03:00
daemon.refresh_finished.disconnect (on_refresh_finished);
2014-10-22 13:44:02 -03:00
}
void on_trans_prepare_finished (bool success) {
stop_progressbar_pulse ();
this.success = success;
2016-02-02 05:28:07 -03:00
if (success) {
2014-10-26 08:30:04 -03:00
show_warnings ();
Type type = set_transaction_sum ();
if (type == Type.UPDATE && mode == Mode.UPDATER) {
2015-01-03 10:52:26 -03:00
// there only updates
2015-03-04 11:55:36 -03:00
start_commit ();
2015-01-03 10:52:26 -03:00
} else if (type != 0) {
2015-03-04 11:55:36 -03:00
if (transaction_sum_dialog.run () == Gtk.ResponseType.OK) {
2014-10-22 13:44:02 -03:00
transaction_sum_dialog.hide ();
2015-03-18 12:07:18 -03:00
while (Gtk.events_pending ()) {
2014-10-22 13:44:02 -03:00
Gtk.main_iteration ();
2015-03-18 12:07:18 -03:00
}
if (type == Type.BUILD) {
2015-01-03 10:52:26 -03:00
// there only AUR packages to build
2016-02-02 05:28:07 -03:00
on_trans_commit_finished (true);
2015-03-18 12:07:18 -03:00
} else {
2016-04-14 13:19:20 -03:00
// backup to_install and to_remove
foreach (unowned string name in to_install) {
previous_to_install.add (name);
}
foreach (unowned string name in to_remove) {
previous_to_remove.add (name);
}
to_install.remove_all ();
to_remove.remove_all ();
2015-03-04 11:55:36 -03:00
start_commit ();
2015-03-18 12:07:18 -03:00
}
2014-10-22 13:44:02 -03:00
} else {
transaction_sum_dialog.hide ();
2016-02-27 06:11:04 -03:00
unowned string action = dgettext (null, "Transaction cancelled");
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", action + ".\n"});
progress_box.action_label.label = action;
2014-10-22 13:44:02 -03:00
release ();
2016-04-14 13:19:20 -03:00
transaction_summary.remove_all ();
2014-11-24 17:34:23 -03:00
sysupgrade_after_trans = false;
success = false;
2016-02-27 06:11:04 -03:00
finish_transaction ();
2014-10-22 13:44:02 -03:00
}
} else {
2015-03-07 06:43:44 -03:00
//var err = ErrorInfos ();
//err.message = dgettext (null, "Nothing to do") + "\n";
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", dgettext (null, "Nothing to do") + ".\n"});
2014-10-22 13:44:02 -03:00
release ();
clear_lists ();
2016-02-27 06:11:04 -03:00
finish_transaction ();
2014-10-22 13:44:02 -03:00
//handle_error (err);
}
} else {
2016-08-27 09:51:34 -03:00
warning_textbuffer = new StringBuilder ();
2016-02-02 05:28:07 -03:00
handle_error (get_current_error ());
2014-10-22 13:44:02 -03:00
}
}
void on_trans_commit_finished (bool success) {
this.success = success;
2016-02-02 05:28:07 -03:00
if (success) {
2016-08-27 09:51:34 -03:00
show_warnings ();
if (to_build.length != 0) {
2016-04-14 13:19:20 -03:00
if (previous_to_install.length != 0
|| previous_to_remove.length != 0
|| to_load.length != 0) {
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", dgettext (null, "Transaction successfully finished") + ".\n"});
2014-10-24 10:29:39 -03:00
}
2014-10-22 13:44:02 -03:00
build_aur_packages ();
} else {
2016-04-14 13:19:20 -03:00
clear_previous_lists ();
2014-10-22 13:44:02 -03:00
if (sysupgrade_after_trans) {
sysupgrade_after_trans = false;
2016-02-02 05:28:07 -03:00
sysupgrade (false);
2014-10-22 13:44:02 -03:00
} else {
2016-02-27 06:11:04 -03:00
unowned string action = dgettext (null, "Transaction successfully finished");
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", action + ".\n"});
progress_box.action_label.label = action;
2016-02-27 06:11:04 -03:00
finish_transaction ();
2014-10-22 13:44:02 -03:00
}
}
} else {
2016-03-01 11:43:51 -03:00
// if it is an authentication or a download error, database was not modified
2016-02-02 05:28:07 -03:00
var err = get_current_error ();
2016-04-14 13:19:20 -03:00
if (err.message == dgettext (null, "Authentication failed")
|| err.errno == 54) { //Alpm.Errno.EXTERNAL_DOWNLOAD
// recover old pkgnames
foreach (unowned string name in previous_to_install) {
to_install.add (name);
}
foreach (unowned string name in previous_to_remove) {
to_remove.add (name);
}
2016-02-02 05:28:07 -03:00
}
2016-04-14 13:19:20 -03:00
clear_previous_lists ();
2016-08-27 09:51:34 -03:00
warning_textbuffer = new StringBuilder ();
handle_error (err);
2014-10-22 13:44:02 -03:00
}
total_download = 0;
already_downloaded = 0;
previous_filename = "";
2014-10-22 13:44:02 -03:00
}
void on_term_child_exited (int status) {
2016-08-27 09:51:34 -03:00
stop_progressbar_pulse ();
2016-04-14 13:19:20 -03:00
clear_previous_lists ();
to_build.remove_all ();
2016-02-02 05:28:07 -03:00
// let the time to the daemon to update databases
Timeout.add (1000, () => {
2016-02-02 05:28:07 -03:00
if (status == 0) {
success = true;
2016-02-27 06:11:04 -03:00
unowned string action = dgettext (null, "Transaction successfully finished");
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo", action + "."});
progress_box.action_label.label = action;
2016-02-02 05:28:07 -03:00
} else {
success = false;
2016-02-02 05:28:07 -03:00
}
2016-05-17 13:11:49 -04:00
finish_transaction ();
return false;
});
2015-03-04 11:55:36 -03:00
}
void on_set_pkgreason_finished () {
set_pkgreason_finished ();
}
void on_write_pamac_config_finished (bool recurse, uint64 refresh_period, bool no_update_hide_icon,
bool enable_aur, bool search_aur, bool check_aur_updates,
bool no_confirm_build) {
2016-02-02 05:28:07 -03:00
pamac_config.reload ();
2016-04-14 13:19:20 -03:00
if (recurse) {
flags |= (1 << 5); //Alpm.TransFlag.RECURSE
}
write_pamac_config_finished (recurse, refresh_period, no_update_hide_icon,
enable_aur, search_aur, check_aur_updates,
no_confirm_build);
}
void on_write_alpm_config_finished (bool checkspace) {
write_alpm_config_finished (checkspace);
}
void on_write_mirrors_config_finished (string choosen_country, string choosen_generation_method) {
write_mirrors_config_finished (choosen_country, choosen_generation_method);
}
2015-08-20 10:11:18 -03:00
void on_generate_mirrors_list_data (string line) {
spawn_in_term ({"echo", line});
}
2015-08-20 10:11:18 -03:00
void on_generate_mirrors_list_finished () {
2016-08-27 09:51:34 -03:00
stop_progressbar_pulse ();
2016-05-17 13:11:49 -04:00
spawn_in_term ({"echo"});
// force a dbs refresh
2016-02-02 05:28:07 -03:00
start_refresh (true);
}
2014-10-22 13:44:02 -03:00
void connecting_dbus_signals () {
try {
2016-02-02 05:28:07 -03:00
daemon = Bus.get_proxy_sync (BusType.SYSTEM, "org.manjaro.pamac", "/org/manjaro/pamac");
// Set environment variables
daemon.set_environment_variables (pamac_config.environment_variables);
2014-10-22 13:44:02 -03:00
// Connecting to signals
daemon.emit_event.connect (on_emit_event);
daemon.emit_providers.connect (on_emit_providers);
daemon.emit_progress.connect (on_emit_progress);
daemon.emit_download.connect (on_emit_download);
daemon.emit_totaldownload.connect (on_emit_totaldownload);
daemon.emit_log.connect (on_emit_log);
2015-03-04 11:55:36 -03:00
daemon.trans_prepare_finished.connect (on_trans_prepare_finished);
daemon.trans_commit_finished.connect (on_trans_commit_finished);
daemon.set_pkgreason_finished.connect (on_set_pkgreason_finished);
daemon.write_mirrors_config_finished.connect (on_write_mirrors_config_finished);
daemon.write_alpm_config_finished.connect (on_write_alpm_config_finished);
2015-03-04 11:55:36 -03:00
daemon.write_pamac_config_finished.connect (on_write_pamac_config_finished);
2015-08-20 10:11:18 -03:00
daemon.generate_mirrors_list_data.connect (on_generate_mirrors_list_data);
daemon.generate_mirrors_list_finished.connect (on_generate_mirrors_list_finished);
2014-10-22 13:44:02 -03:00
} catch (IOError e) {
stderr.printf ("IOError: %s\n", e.message);
}
}
}
}