diff --git a/daemon/remote.c b/daemon/remote.c index 651ca24d8..3c9d46749 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1523,8 +1523,6 @@ printSession (tr_variant * top) printf (" Configuration directory: %s\n", str); if (tr_variantDictFindStr (args, TR_KEY_download_dir, &str, NULL)) printf (" Download directory: %s\n", str); - if (tr_variantDictFindInt (args, TR_KEY_download_dir_free_space, &i)) - printf (" Download directory free space: %s\n", strlsize (buf, i, sizeof buf)); if (tr_variantDictFindInt (args, TR_KEY_peer_port, &i)) printf (" Listenport: %" PRId64 "\n", i); if (tr_variantDictFindBool (args, TR_KEY_port_forwarding_enabled, &boolVal)) diff --git a/extras/rpc-spec.txt b/extras/rpc-spec.txt index 9058812c9..6520c72e1 100644 --- a/extras/rpc-spec.txt +++ b/extras/rpc-spec.txt @@ -457,7 +457,6 @@ "cache-size-mb" | number | maximum size of the disk cache (MB) "config-dir" | string | location of transmission's configuration directory "download-dir" | string | default path to download torrents - "download-dir-free-space" | number | number of free bytes available in download-dir, or -1 if it can't be calculated "download-queue-size" | number | max number of torrents to download at once (see download-queue-enabled) "download-queue-enabled" | boolean | if true, limit how many torrents can be downloaded at once "dht-enabled" | boolean | true means allow dht in public torrents @@ -516,8 +515,8 @@ Method name: "session-set" Request arguments: one or more of 4.1's arguments, except: "blocklist-size", - "config-dir", "download-dir-free-space", "rpc-version", - "rpc-version-minimum", and "version" + "config-dir", "rpc-version", "rpc-version-minimum", and + "version" Response arguments: none 4.1.2. Accessors @@ -598,6 +597,27 @@ Response arguments: none +4.7. Free Space + + This method tests how much free space is available in a + client-specified folder. + + Method name: "free-space" + + Request arguments: + + string | value type & description + ------------+---------------------------------------------------------- + "path" | string the directory to query + + Response arguments: + + string | value type & description + ------------+---------------------------------------------------------- + "path" | string same as the Request argument + "size-bytes"| number the size, in bytes, of the free space in that directory + + 5.0. Protocol Versions The following changes have been made to the RPC interface: @@ -738,6 +758,8 @@ | | yes | | new method "queue-move-bottom" | | yes | | new method "torrent-start-now" ------+---------+-----------+--------------------------+------------------------------- - 15 | 2.80 | yes | torrent-get | new arg "etaIdle" + 15 | 2.80 | NO | session-get | removed arg "download-dir-free-space" + | | yes | torrent-get | new arg "etaIdle" | | yes | torrent-rename-path | new method + | | yes | free-space | new method diff --git a/gtk/open-dialog.c b/gtk/open-dialog.c index 91e7dae36..64f5c8eda 100644 --- a/gtk/open-dialog.c +++ b/gtk/open-dialog.c @@ -94,6 +94,7 @@ struct OpenData GtkWidget * run_check; GtkWidget * trash_check; GtkWidget * priority_combo; + GtkWidget * freespace_label; char * filename; char * downloadDir; tr_torrent * tor; @@ -226,6 +227,8 @@ downloadDirChanged (GtkFileChooserButton * b, gpointer gdata) g_free (data->downloadDir); data->downloadDir = g_strdup (fname); updateTorrent (data); + + gtr_freespace_label_set_dir (data->freespace_label, data->downloadDir); } g_free (fname); @@ -339,6 +342,13 @@ gtr_torrent_options_dialog_new (GtkWindow * parent, TrCore * core, tr_ctor * cto g_signal_connect (w, "selection-changed", G_CALLBACK (downloadDirChanged), data); + row++; + l = data->freespace_label = gtr_freespace_label_new (core, data->downloadDir); + gtk_widget_set_margin_bottom (l, GUI_PAD_BIG); + gtk_misc_set_alignment (GTK_MISC (l), 1.0f, 0.5f); + gtk_grid_attach (grid, l, 0, row, 2, 1); + + // file list row row++; w = data->file_list; diff --git a/gtk/tr-prefs.c b/gtk/tr-prefs.c index 64bad8603..dd8d58c02 100644 --- a/gtk/tr-prefs.c +++ b/gtk/tr-prefs.c @@ -25,6 +25,23 @@ #include "tr-prefs.h" #include "util.h" +/** +*** +**/ + +struct prefs_dialog_data +{ + TrCore * core; + gulong core_prefs_tag; + + GtkWidget * freespace_label; + + GtkWidget * port_label; + GtkWidget * port_button; + GtkWidget * port_spin; +}; + + /** *** **/ @@ -256,7 +273,7 @@ target_cb (GtkWidget * tb, gpointer target) ****/ static GtkWidget* -downloadingPage (GObject * core) +downloadingPage (GObject * core, struct prefs_dialog_data * data) { GtkWidget * t; GtkWidget * w; @@ -289,6 +306,10 @@ downloadingPage (GObject * core) w = new_path_chooser_button (TR_KEY_download_dir, core); hig_workarea_add_row (t, &row, _("Save to _Location:"), w, NULL); + l = data->freespace_label = gtr_freespace_label_new (TR_CORE(core), NULL); + gtk_misc_set_alignment (GTK_MISC (l), 1.0f, 0.5f); + hig_workarea_add_wide_control (t, &row, l); + hig_workarea_add_section_divider (t, &row); hig_workarea_add_section_title (t, &row, _("Download Queue")); @@ -1242,11 +1263,49 @@ networkPage (GObject * core) ***** ****/ +static void +on_prefs_dialog_destroyed (gpointer gdata, GObject * dead_dialog G_GNUC_UNUSED) +{ + struct prefs_dialog_data * data = gdata; + + if (data->core_prefs_tag > 0) + g_signal_handler_disconnect (data->core, data->core_prefs_tag); + + g_free (data); +} + +static void +on_core_prefs_changed (TrCore * core, const tr_quark key, gpointer gdata) +{ + struct prefs_dialog_data * data = gdata; + +#if 0 + if (key == TR_KEY_peer_port) + { + gtr_label_set_text (GTK_LABEL (data->port_label), _("Status unknown")); + gtk_widget_set_sensitive (data->port_button, TRUE); + gtk_widget_set_sensitive (data->port_spin, TRUE); + } +#endif + if (key == TR_KEY_download_dir) + { + const char * downloadDir = tr_sessionGetDownloadDir (gtr_core_session (core)); + gtr_freespace_label_set_dir (data->freespace_label, downloadDir); + } +} + GtkWidget * gtr_prefs_dialog_new (GtkWindow * parent, GObject * core) { + size_t i; GtkWidget * d; GtkWidget * n; + struct prefs_dialog_data * data; + const tr_quark prefs_quarks[] = { TR_KEY_peer_port, TR_KEY_download_dir }; + + data = g_new0 (struct prefs_dialog_data, 1); + data->core = TR_CORE (core); + data->core_prefs_tag = g_signal_connect (TR_CORE (core), "prefs-changed", G_CALLBACK (on_core_prefs_changed), data); d = gtk_dialog_new_with_buttons (_("Transmission Preferences"), parent, @@ -1254,6 +1313,7 @@ gtr_prefs_dialog_new (GtkWindow * parent, GObject * core) GTK_STOCK_HELP, GTK_RESPONSE_HELP, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + g_object_weak_ref (G_OBJECT(d), on_prefs_dialog_destroyed, data); gtk_window_set_role (GTK_WINDOW (d), "transmission-preferences-dialog"); gtk_container_set_border_width (GTK_CONTAINER (d), GUI_PAD); @@ -1262,7 +1322,7 @@ gtr_prefs_dialog_new (GtkWindow * parent, GObject * core) gtk_notebook_append_page (GTK_NOTEBOOK (n), speedPage (core), gtk_label_new (_("Speed"))); - gtk_notebook_append_page (GTK_NOTEBOOK (n), downloadingPage (core), + gtk_notebook_append_page (GTK_NOTEBOOK (n), downloadingPage (core, data), gtk_label_new (C_("Gerund", "Downloading"))); gtk_notebook_append_page (GTK_NOTEBOOK (n), seedingPage (core), gtk_label_new (C_("Gerund", "Seeding"))); @@ -1275,6 +1335,10 @@ gtr_prefs_dialog_new (GtkWindow * parent, GObject * core) gtk_notebook_append_page (GTK_NOTEBOOK (n), remotePage (core), gtk_label_new (_("Remote"))); + /* init from prefs keys */ + for (i=0; ifreespace_icon = w; - g_object_set (G_OBJECT(w), "margin-left", GUI_PAD, NULL); - gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1); - sibling = w; - w = gtk_label_new (NULL); - g_object_set (G_OBJECT(w), "margin-left", GUI_PAD_BIG*2, NULL); - p->freespace_lb = GTK_LABEL (w); - gtk_label_set_single_line_mode (p->freespace_lb, TRUE); - gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1); - sibling = w; - - /* spacer */ - w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f); - gtk_widget_set_hexpand (w, TRUE); - gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1); - sibling = w; - /* download */ w = dl_lb = gtk_label_new (NULL); p->dl_lb = GTK_LABEL (w); @@ -814,45 +793,6 @@ gtr_window_new (GtkApplication * app, GtkUIManager * ui_mgr, TrCore * core) return self; } -static void -updateFreeSpace (PrivateData * p) -{ - GtkWidget * w; - bool visible = false; - - g_return_if_fail (p != NULL); - - w = GTK_WIDGET (p->freespace_lb); - - if (p->core != NULL) - { - tr_session * session = gtr_core_session (p->core); - const int64_t n = tr_sessionGetDownloadDirFreeSpace (session); - const char * downloadDir = tr_sessionGetDownloadDir (session); - - visible = n >= 0; - - if (visible) - { - char * str; - char sizeStr[32]; - - tr_strlsize (sizeStr, n, sizeof(sizeStr)); - - str = g_strdup_printf (_("%s Free"), sizeStr); - gtk_label_set_text (p->freespace_lb, str); - g_free (str); - - str = g_strdup_printf (_("Download folder \"%1$s\" has %2$s free"), downloadDir, sizeStr); - gtk_widget_set_tooltip_text (w, str); - g_free (str); - } - } - - gtk_widget_set_visible (w, visible); - gtk_widget_set_visible (p->freespace_icon, visible); -} - static void updateStats (PrivateData * p) { @@ -958,7 +898,6 @@ gtr_window_refresh (GtkWindow * self) { updateSpeeds (p); updateStats (p); - updateFreeSpace (p); } } diff --git a/gtk/util.c b/gtk/util.c index 03424221b..5973e2501 100644 --- a/gtk/util.c +++ b/gtk/util.c @@ -26,6 +26,7 @@ #include "conf.h" #include "hig.h" +#include "tr-core.h" #include "tr-prefs.h" #include "util.h" @@ -631,3 +632,122 @@ gtr_label_set_text (GtkLabel * lb, const char * newstr) if (tr_strcmp0 (oldstr, newstr)) gtk_label_set_text (lb, newstr); } + +/*** +**** +***/ + +struct freespace_label_data +{ + guint timer_id; + TrCore * core; + GtkLabel * label; + char * dir; +}; + +static void on_freespace_label_core_destroyed (gpointer gdata, GObject * dead_core); +static void on_freespace_label_destroyed (gpointer gdata, GObject * dead_label); + +static void +freespace_label_data_free (gpointer gdata) +{ + struct freespace_label_data * data = gdata; + + if (data->core != NULL) + g_object_weak_unref (G_OBJECT(data->core), on_freespace_label_core_destroyed, data); + + if (data->label != NULL) + g_object_weak_ref (G_OBJECT(data->label), on_freespace_label_destroyed, data); + + g_source_remove (data->timer_id); + g_free (data->dir); + g_free (data); +} + +static GQuark +freespace_label_data_quark (void) +{ + static GQuark q = 0; + + if (G_UNLIKELY(!q)) + q = g_quark_from_static_string ("data"); + + return q; +} + +static void +on_freespace_label_core_destroyed (gpointer gdata, GObject * dead_core G_GNUC_UNUSED) +{ + struct freespace_label_data * data = gdata; + data->core = NULL; + freespace_label_data_free (data); +} + +static void +on_freespace_label_destroyed (gpointer gdata, GObject * dead_label G_GNUC_UNUSED) +{ + struct freespace_label_data * data = gdata; + data->label = NULL; + freespace_label_data_free (data); +} + +static gboolean +on_freespace_timer (gpointer gdata) +{ + char text[128]; + char markup[128]; + int64_t bytes; + tr_session * session; + struct freespace_label_data * data = gdata; + + session = gtr_core_session (data->core); + bytes = tr_sessionGetDirFreeSpace (session, data->dir); + + if (bytes < 0) + { + g_snprintf (text, sizeof(text), _("Error")); + } + else + { + char size[128]; + tr_strlsize (size, bytes, sizeof(size)); + g_snprintf (text, sizeof(text), _("%s free"), size); + } + + g_snprintf (markup, sizeof(markup), "%s", text); + gtk_label_set_markup (data->label, markup); + + return G_SOURCE_CONTINUE; +} + +GtkWidget * +gtr_freespace_label_new (struct _TrCore * core, const char * dir) +{ + struct freespace_label_data * data; + + data = g_new0 (struct freespace_label_data, 1); + data->timer_id = g_timeout_add_seconds (3, on_freespace_timer, data); + data->core = core; + data->label = GTK_LABEL (gtk_label_new (NULL)); + data->dir = g_strdup (dir); + + /* when either the core or the label is destroyed, stop updating */ + g_object_weak_ref (G_OBJECT(core), on_freespace_label_core_destroyed, data); + g_object_weak_ref (G_OBJECT(data->label), on_freespace_label_destroyed, data); + + g_object_set_qdata (G_OBJECT(data->label), freespace_label_data_quark (), data); + on_freespace_timer (data); + return GTK_WIDGET (data->label); +} + +void +gtr_freespace_label_set_dir (GtkWidget * label, const char * dir) +{ + struct freespace_label_data * data; + + data = g_object_get_qdata (G_OBJECT(label), freespace_label_data_quark ()); + + tr_free (data->dir); + data->dir = g_strdup (dir); + on_freespace_timer (data); +} diff --git a/gtk/util.h b/gtk/util.h index 755c71bd3..da6d0fe0f 100644 --- a/gtk/util.h +++ b/gtk/util.h @@ -109,6 +109,16 @@ void gtr_combo_box_set_active_enum (GtkComboBox *, int value); **** ***/ +struct _TrCore; + +GtkWidget * gtr_freespace_label_new (struct _TrCore * core, const char * dir); + +void gtr_freespace_label_set_dir (GtkWidget * label, const char * dir); + +/*** +**** +***/ + void gtr_unrecognized_url_dialog (GtkWidget * parent, const char * url); void gtr_http_failure_dialog (GtkWidget * parent, const char * url, long response_code); diff --git a/libtransmission/platform.c b/libtransmission/platform.c index 5820d6183..881a22741 100644 --- a/libtransmission/platform.c +++ b/libtransmission/platform.c @@ -56,6 +56,7 @@ #endif #include +#include #include #include #include @@ -81,6 +82,8 @@ #include #endif +#include /* evutil_ascii_strcasecmp () */ + #include "transmission.h" #include "session.h" #include "list.h" @@ -732,7 +735,7 @@ tr_getWebClientDir (const tr_session * session UNUSED) ***/ #ifndef WIN32 -static char * +static const char * getdev (const char * path) { #ifdef HAVE_GETMNTENT @@ -783,7 +786,7 @@ getdev (const char * path) #endif } -static char * +static const char * getfstype (const char * device) { @@ -833,12 +836,12 @@ getfstype (const char * device) #endif } -static char * +static const char * getblkdev (const char * path) { char * c; char * dir; - char * device; + const char * device; dir = tr_strdup(path); @@ -860,7 +863,7 @@ getblkdev (const char * path) } static int64_t -getquota (char * device) +getquota (const char * device) { struct dqblk dq; int64_t limit; @@ -960,41 +963,24 @@ getxfsquota (char * device) #endif /* WIN32 */ static int64_t -tr_getQuotaFreeSpace (const char * path, char * device, char * fstype) +tr_getQuotaFreeSpace (const struct tr_device_info * info) { - int64_t ret=-1; + int64_t ret = -1; #ifndef WIN32 - /* save device for future use */ - if (!*device) - { - char * d = getblkdev (path); - if (d == NULL) - return ret; - tr_strlcpy (device, d, PATH_MAX + 1); - } - - /* save FS type for future use */ - if (!*fstype) - { - char * fs = getfstype (device); - if (fs != NULL) - tr_strlcpy (fstype, fs, PATH_MAX + 1); - } - - if (strcasecmp(fstype, "xfs") == 0) + if (info->fstype && !evutil_ascii_strcasecmp(info->fstype, "xfs")) { #ifdef HAVE_XQM - ret = getxfsquota(device); + ret = getxfsquota (info->device); #endif } else { - ret = getquota(device); + ret = getquota (info->device); } - #endif /* WIN32 */ + return ret; } @@ -1021,15 +1007,50 @@ tr_getDiskFreeSpace (const char * path) #endif } -int64_t -tr_getFreeSpace (const char * path, char * device, char * fstype) +struct tr_device_info * +tr_device_info_create (const char * path) { - int64_t i = tr_getQuotaFreeSpace (path, device, fstype); + struct tr_device_info * info; - if (i < 0) - i = tr_getDiskFreeSpace (path); + info = tr_new0 (struct tr_device_info, 1); + info->path = tr_strdup (path); + info->device = tr_strdup (getblkdev (path)); + info->fstype = tr_strdup (getfstype (path)); - return i; + return info; +} + +void +tr_device_info_free (struct tr_device_info * info) +{ + if (info != NULL) + { + tr_free (info->fstype); + tr_free (info->device); + tr_free (info->path); + tr_free (info); + } +} + +int64_t +tr_device_info_get_free_space (const struct tr_device_info * info) +{ + int64_t free_space; + + if ((info == NULL) || (info->path == NULL)) + { + errno = EINVAL; + free_space = -1; + } + else + { + free_space = tr_getQuotaFreeSpace (info); + + if (free_space < 0) + free_space = tr_getDiskFreeSpace (info->path); + } + + return free_space; } /*** diff --git a/libtransmission/platform.h b/libtransmission/platform.h index a06a319cc..28fe8a4a5 100644 --- a/libtransmission/platform.h +++ b/libtransmission/platform.h @@ -42,9 +42,20 @@ const char * tr_getTorrentDir (const tr_session *); /** @brief return the directory where the Web Client's web ui files are kept */ const char * tr_getWebClientDir (const tr_session *); +struct tr_device_info +{ + char * path; + char * device; + char * fstype; +}; + +struct tr_device_info * tr_device_info_create (const char * path); + /** If the disk quota is enabled and readable, this returns how much is available in the quota. Otherwise, it returns how much is available on the disk, or -1 on error. */ -int64_t tr_getFreeSpace (const char * path, char * device, char * fstype); +int64_t tr_device_info_get_free_space (const struct tr_device_info * info); + +void tr_device_info_free (struct tr_device_info * info); /** @} */ diff --git a/libtransmission/quark.c b/libtransmission/quark.c index f0381156d..d379218cf 100644 --- a/libtransmission/quark.c +++ b/libtransmission/quark.c @@ -78,7 +78,6 @@ static const struct tr_key_struct my_static[] = { "done-date", 9 }, { "doneDate", 8 }, { "download-dir", 12 }, - { "download-dir-free-space", 23 }, { "download-queue-enabled", 22 }, { "download-queue-size", 19 }, { "downloadCount", 13 }, diff --git a/libtransmission/quark.h b/libtransmission/quark.h index 39fcd5393..9a5795342 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -88,7 +88,6 @@ enum TR_KEY_done_date, TR_KEY_doneDate, TR_KEY_download_dir, - TR_KEY_download_dir_free_space, TR_KEY_download_queue_enabled, TR_KEY_download_queue_size, TR_KEY_downloadCount, diff --git a/libtransmission/rpc-test.c b/libtransmission/rpc-test.c index fc4681e9f..eb4fca684 100644 --- a/libtransmission/rpc-test.c +++ b/libtransmission/rpc-test.c @@ -106,7 +106,6 @@ test_session_get_and_set (void) check (tr_variantDictFind (args, TR_KEY_config_dir) != NULL); check (tr_variantDictFind (args, TR_KEY_dht_enabled) != NULL); check (tr_variantDictFind (args, TR_KEY_download_dir) != NULL); - check (tr_variantDictFind (args, TR_KEY_download_dir_free_space) != NULL); check (tr_variantDictFind (args, TR_KEY_download_queue_enabled) != NULL); check (tr_variantDictFind (args, TR_KEY_download_queue_size) != NULL); check (tr_variantDictFind (args, TR_KEY_encryption) != NULL); diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c index 2572b53e6..6f7831f21 100644 --- a/libtransmission/rpcimpl.c +++ b/libtransmission/rpcimpl.c @@ -1726,8 +1726,8 @@ torrentAdd (tr_session * session, static const char* sessionSet (tr_session * session, - tr_variant * args_in, - tr_variant * args_out UNUSED, + tr_variant * args_in, + tr_variant * args_out UNUSED, struct tr_rpc_idle_data * idle_data UNUSED) { int64_t i; @@ -1905,7 +1905,6 @@ sessionGet (tr_session * s, tr_variantDictAddStr (d, TR_KEY_download_dir, tr_sessionGetDownloadDir (s)); tr_variantDictAddBool (d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled (s, TR_DOWN)); tr_variantDictAddInt (d, TR_KEY_download_queue_size, tr_sessionGetQueueSize (s, TR_DOWN)); - tr_variantDictAddInt (d, TR_KEY_download_dir_free_space, tr_sessionGetDownloadDirFreeSpace (s)); tr_variantDictAddInt (d, TR_KEY_peer_limit_global, tr_sessionGetPeerLimit (s)); tr_variantDictAddInt (d, TR_KEY_peer_limit_per_torrent, tr_sessionGetPeerLimitPerTorrent (s)); tr_variantDictAddStr (d, TR_KEY_incomplete_dir, tr_sessionGetIncompleteDir (s)); @@ -1948,6 +1947,33 @@ sessionGet (tr_session * s, return NULL; } +static const char* +freeSpace (tr_session * session, + tr_variant * args_in, + tr_variant * args_out, + struct tr_rpc_idle_data * idle_data UNUSED) +{ + int tmperr; + const char * path = NULL; + const char * err = NULL; + int64_t free_space = -1; + + /* get the free space */ + tr_variantDictFindStr (args_in, TR_KEY_path, &path, NULL); + tmperr = errno; + errno = 0; + free_space = tr_sessionGetDirFreeSpace (session, path); + if (free_space < 0) + err = tr_strerror (errno); + errno = tmperr; + + /* response */ + if (path != NULL) + tr_variantDictAddStr (args_out, TR_KEY_path, path); + tr_variantDictAddInt (args_out, TR_KEY_size_bytes, free_space); + return err; +} + /*** **** ***/ @@ -1978,6 +2004,7 @@ methods[] = { { "port-test", false, portTest }, { "blocklist-update", false, blocklistUpdate }, + { "free-space", true, freeSpace }, { "session-close", true, sessionClose }, { "session-get", true, sessionGet }, { "session-set", true, sessionSet }, diff --git a/libtransmission/session.c b/libtransmission/session.c index e128dbbe2..de0722632 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -386,7 +386,7 @@ tr_sessionGetSettings (tr_session * s, tr_variant * d) tr_variantDictAddBool (d, TR_KEY_dht_enabled, s->isDHTEnabled); tr_variantDictAddBool (d, TR_KEY_utp_enabled, s->isUTPEnabled); tr_variantDictAddBool (d, TR_KEY_lpd_enabled, s->isLPDEnabled); - tr_variantDictAddStr (d, TR_KEY_download_dir, s->downloadDir); + tr_variantDictAddStr (d, TR_KEY_download_dir, tr_sessionGetDownloadDir (s)); tr_variantDictAddInt (d, TR_KEY_download_queue_size, tr_sessionGetQueueSize (s, TR_DOWN)); tr_variantDictAddBool (d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled (s, TR_DOWN)); tr_variantDictAddInt (d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps (s, TR_DOWN)); @@ -953,33 +953,40 @@ tr_sessionSet (tr_session * session, tr_variant * settings) void tr_sessionSetDownloadDir (tr_session * session, const char * dir) { - assert (tr_isSession (session)); + struct tr_device_info * info = NULL; - if (session->downloadDir != dir) - { - tr_free (session->downloadDir); - session->downloadDir = tr_strdup (dir); - memset (session->downloadDirBlkDev, 0, sizeof(session->downloadDirBlkDev)); - memset (session->downloadDirFsType, 0, sizeof(session->downloadDirFsType)); - } + assert (tr_isSession (session)); + + if (dir != NULL) + info = tr_device_info_create (dir); + tr_device_info_free (session->downloadDir); + session->downloadDir = info; } const char * tr_sessionGetDownloadDir (const tr_session * session) { - assert (tr_isSession (session)); + const char * dir = NULL; - return session->downloadDir; + assert (tr_isSession (session)); + + if ((session != NULL) && (session->downloadDir != NULL)) + dir = session->downloadDir->path; + + return dir; } int64_t -tr_sessionGetDownloadDirFreeSpace (tr_session * session) +tr_sessionGetDirFreeSpace (tr_session * session, const char * dir) { - assert (tr_isSession (session)); + int64_t free_space; - return tr_getFreeSpace (session->downloadDir, - session->downloadDirBlkDev, - session->downloadDirFsType); + if (!tr_strcmp0 (dir, tr_sessionGetDownloadDir (session))) + free_space = tr_device_info_get_free_space (session->downloadDir); + else + free_space = tr_getDirFreeSpace (dir); + + return free_space; } /*** @@ -1883,12 +1890,12 @@ tr_sessionClose (tr_session * session) tr_variantFree (session->metainfoLookup); tr_free (session->metainfoLookup); } + tr_device_info_free (session->downloadDir); tr_free (session->torrentDoneScript); tr_free (session->tag); tr_free (session->configDir); tr_free (session->resumeDir); tr_free (session->torrentDir); - tr_free (session->downloadDir); tr_free (session->incompleteDir); tr_free (session->blocklist_url); tr_free (session->peer_congestion_algorithm); diff --git a/libtransmission/session.h b/libtransmission/session.h index 780f1454a..2d402d2f4 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -56,6 +56,7 @@ struct tr_announcer_udp; struct tr_bindsockets; struct tr_cache; struct tr_fdInfo; +struct tr_device_info; typedef void (tr_web_config_func)(tr_session * session, void * curl_pointer, const char * url, void * user_data); @@ -183,15 +184,14 @@ struct tr_session char * tag; char * configDir; - char * downloadDir; - char downloadDirBlkDev[TR_PATH_MAX + 1]; - char downloadDirFsType[TR_PATH_MAX + 1]; char * resumeDir; char * torrentDir; char * incompleteDir; char * blocklist_url; + struct tr_device_info * downloadDir; + struct tr_list * blocklists; struct tr_peerMgr * peerMgr; struct tr_shared * shared; diff --git a/libtransmission/torrent-ctor.c b/libtransmission/torrent-ctor.c index 3a85fe1a1..2e19a0aee 100644 --- a/libtransmission/torrent-ctor.c +++ b/libtransmission/torrent-ctor.c @@ -452,7 +452,7 @@ tr_ctorNew (const tr_session * session) tr_ctorSetDeleteSource (ctor, tr_sessionGetDeleteSource (session)); tr_ctorSetPaused (ctor, TR_FALLBACK, tr_sessionGetPaused (session)); tr_ctorSetPeerLimit (ctor, TR_FALLBACK, session->peerLimitPerTorrent); - tr_ctorSetDownloadDir (ctor, TR_FALLBACK, session->downloadDir); + tr_ctorSetDownloadDir (ctor, TR_FALLBACK, tr_sessionGetDownloadDir(session)); } tr_ctorSetSave (ctor, true); return ctor; diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index b2dd695fa..02f2092f3 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -82,7 +82,7 @@ tr_torrentName (const tr_torrent * tor) int tr_torrentId (const tr_torrent * tor) { - return tor->uniqueId; + return tor ? tor->uniqueId : -1; } tr_torrent* diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index c48fd850e..00389fe1e 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -291,10 +291,10 @@ void tr_sessionSetDownloadDir (tr_session * session, const char * downloadDir); const char * tr_sessionGetDownloadDir (const tr_session * session); /** - * @brief Get available disk space (in bytes) for the default download folder. + * @brief Get available disk space (in bytes) for the specified directory. * @return zero or positive integer on success, -1 in case of error. */ -int64_t tr_sessionGetDownloadDirFreeSpace (tr_session * session); +int64_t tr_sessionGetDirFreeSpace (tr_session * session, const char * dir); /** * @brief Set the torrent's bandwidth priority. diff --git a/libtransmission/utils.c b/libtransmission/utils.c index fa9fd2088..6e9279550 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -420,6 +420,27 @@ tr_fileExists (const char * filename, time_t * mtime) return ok; } +int64_t +tr_getDirFreeSpace (const char * dir) +{ + int64_t free_space; + + if (!dir || !*dir) + { + errno = EINVAL; + free_space = -1; + } + else + { + struct tr_device_info * info; + info = tr_device_info_create (dir); + free_space = tr_device_info_get_free_space (info); + tr_device_info_free (info); + } + + return free_space; +} + /**** ***** ****/ diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 09704491c..28ad6ba6c 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -140,6 +140,12 @@ char* tr_buildPath (const char * first_element, ...) TR_GNUC_NULL_TERMINATED bool tr_fileExists (const char * filename, time_t * mtime); +/** + * @brief Get available disk space (in bytes) for the specified folder. + * @return zero or positive integer on success, -1 in case of error. + */ +int64_t tr_getDirFreeSpace (const char * path); + struct event; diff --git a/qt/freespace-label.cc b/qt/freespace-label.cc new file mode 100644 index 000000000..25a955f96 --- /dev/null +++ b/qt/freespace-label.cc @@ -0,0 +1,86 @@ +/* + * This file Copyright (C) Mnemosyne LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * $Id:$ + */ + +#include +#include + +#include "formatter.h" +#include "freespace-label.h" +#include "session.h" + +namespace +{ + static const int INTERVAL_MSEC = 5000; +} + +FreespaceLabel :: FreespaceLabel (Session & session, + const QString & path, + QWidget * parent): + QLabel (parent), + mySession (session), + myTag (-1), + myPath (path), + myTimer (this) +{ + myTimer.setSingleShot (false); + myTimer.setInterval (INTERVAL_MSEC); + myTimer.start (); + + connect (&myTimer, SIGNAL(timeout()), this, SLOT(onTimer())); + + connect (&mySession, SIGNAL(executed(int64_t, const QString&, struct tr_variant*)), + this, SLOT(onSessionExecuted(int64_t, const QString&, struct tr_variant*))); + + onTimer (); +} + +void +FreespaceLabel :: onTimer () +{ + const int64_t tag = mySession.getUniqueTag (); + const QByteArray myPathUtf8 = myPath.toUtf8 (); + + myTag = tag; + tr_variant top; + tr_variantInitDict (&top, 3); + tr_variantDictAddStr (&top, TR_KEY_method, "free-space"); + tr_variantDictAddInt (&top, TR_KEY_tag, tag); + tr_variant * args = tr_variantDictAddDict (&top, TR_KEY_arguments, 1); + tr_variantDictAddStr (args, TR_KEY_path, myPathUtf8.constData()); + mySession.exec (&top); + tr_variantFree (&top); +} + +void +FreespaceLabel :: onSessionExecuted (int64_t tag, const QString& result, struct tr_variant * arguments) +{ + if (tag != myTag) + return; + + QString str; + + // update the label + int64_t bytes = -1; + tr_variantDictFindInt (arguments, TR_KEY_size_bytes, &bytes); + if (bytes < 0) + str = tr("Error: %1").arg(result); + else + str = tr("%1 free").arg(Formatter::sizeToString (bytes)); + setText (QString("%1").arg(str)); + + // update the tooltip + size_t len = 0; + const char * path = 0; + tr_variantDictFindStr (arguments, TR_KEY_path, &path, &len); + str = QString::fromUtf8 (path, len); + setToolTip (str); +} diff --git a/qt/freespace-label.h b/qt/freespace-label.h new file mode 100644 index 000000000..ea844c1f4 --- /dev/null +++ b/qt/freespace-label.h @@ -0,0 +1,44 @@ +/* + * This file Copyright (C) Mnemosyne LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * $Id:$ + */ + +#ifndef QTR_FREESPACE_LABEL_H +#define QTR_FREESPACE_LABEL_H + +#include +#include + +#include + +class Session; + +class FreespaceLabel: public QLabel +{ + Q_OBJECT + + public: + FreespaceLabel (Session&, const QString& path, QWidget *parent=0); + virtual ~FreespaceLabel () {} + void setPath (const QString& folder) { myPath=folder; onTimer(); } + + private: + Session& mySession; + int64_t myTag; + QString myPath; + QTimer myTimer; + + private slots: + void onSessionExecuted (int64_t tag, const QString& result, struct tr_variant * arguments); + void onTimer (); +}; + +#endif // QTR_FREESPACE_LABEL_H + diff --git a/qt/mainwin.cc b/qt/mainwin.cc index 2670f1be7..660819497 100644 --- a/qt/mainwin.cc +++ b/qt/mainwin.cc @@ -281,7 +281,6 @@ TrMainWindow :: TrMainWindow (Session& session, Prefs& prefs, TorrentModel& mode connect (&mySession, SIGNAL (sourceChanged ()), this, SLOT (onSessionSourceChanged ())); connect (&mySession, SIGNAL (statsUpdated ()), this, SLOT (refreshStatusBar ())); - connect (&mySession, SIGNAL (sessionUpdated ()), this, SLOT (refreshFreeSpace ())); connect (&mySession, SIGNAL (dataReadProgress ()), this, SLOT (dataReadProgress ())); connect (&mySession, SIGNAL (dataSendProgress ()), this, SLOT (dataSendProgress ())); connect (&mySession, SIGNAL (httpAuthenticationRequired ()), this, SLOT (wrongAuthentication ())); @@ -303,7 +302,6 @@ TrMainWindow :: TrMainWindow (Session& session, Prefs& prefs, TorrentModel& mode refreshActionSensitivitySoon (); refreshTrayIconSoon (); refreshStatusBar (); - refreshFreeSpace (); refreshTitle (); } @@ -389,16 +387,6 @@ TrMainWindow :: createStatusBar () l = myNetworkLabel = new QLabel; h->addWidget (l); - h->addStretch (1); - - l = myFreeSpaceIconLabel = new QLabel (this); - l->setPixmap (getStockIcon ("drive-harddisk", QStyle::SP_DriveHDIcon).pixmap (smallIconSize)); - h->addWidget (l); - l = myFreeSpaceTextLabel = new QLabel (this); - const int minimumFreeSpaceWidth = l->fontMetrics ().width (Formatter::sizeToString (1024 * 1024)); - l->setMinimumWidth (minimumFreeSpaceWidth); - h->addWidget (l); - h->addStretch (1); l = myDownloadSpeedLabel = new QLabel (this); @@ -712,28 +700,6 @@ TrMainWindow :: refreshTitle () setWindowTitle (title); } -void -TrMainWindow :: refreshFreeSpace () -{ - const int64_t bytes (mySession.downloadDirFreeSpace ()); - - if (bytes >= 0) - { - const QString sizeStr = Formatter::sizeToString (bytes); - - const QString tip = tr ("Download folder \"%1\" has %2 free") - .arg (myPrefs.getString (Prefs::DOWNLOAD_DIR)) - .arg (sizeStr); - - myFreeSpaceTextLabel->setText (tr("%1 Free").arg(sizeStr)); - myFreeSpaceTextLabel->setToolTip (tip); - myFreeSpaceIconLabel->setToolTip (tip); - } - - myFreeSpaceTextLabel->setVisible (bytes >= 0); - myFreeSpaceIconLabel->setVisible (bytes >= 0); -} - void TrMainWindow :: refreshTrayIconSoon () { diff --git a/qt/mainwin.h b/qt/mainwin.h index 681cbb99b..1b1b0988c 100644 --- a/qt/mainwin.h +++ b/qt/mainwin.h @@ -104,7 +104,6 @@ class TrMainWindow: public QMainWindow void showTotalTransfer (); void showSessionRatio (); void showSessionTransfer (); - void refreshFreeSpace (); void refreshTitle (); void refreshStatusBar (); void refreshTrayIcon (); @@ -158,8 +157,6 @@ class TrMainWindow: public QMainWindow QLabel * myStatsLabel; QLabel * myDownloadSpeedLabel; QLabel * myUploadSpeedLabel; - QLabel * myFreeSpaceTextLabel; - QLabel * myFreeSpaceIconLabel; QLabel * myNetworkLabel; public slots: diff --git a/qt/options.cc b/qt/options.cc index 495c72f71..2f60866ba 100644 --- a/qt/options.cc +++ b/qt/options.cc @@ -36,6 +36,7 @@ #include "add-data.h" #include "file-tree.h" +#include "freespace-label.h" #include "hig.h" #include "options.h" #include "prefs.h" @@ -118,10 +119,11 @@ Options :: Options( Session& session, const Prefs& prefs, const AddData& addme, l = new QLabel( tr( "&Destination folder:" ) ); layout->addWidget( l, ++row, 0, Qt::AlignLeft ); + const QString downloadDir (prefs.getString (Prefs::DOWNLOAD_DIR)); if( session.isLocal( ) ) { - myDestination.setPath( prefs.getString( Prefs :: DOWNLOAD_DIR ) ); + myDestination.setPath (downloadDir); p = myDestinationButton = new QPushButton; p->setIcon( folderPixmap ); p->setStyleSheet( "text-align: left; padding-left: 5; padding-right: 5" ); @@ -133,11 +135,15 @@ Options :: Options( Session& session, const Prefs& prefs, const AddData& addme, else { QLineEdit * e = myDestinationEdit = new QLineEdit; - e->setText( prefs.getString( Prefs :: DOWNLOAD_DIR ) ); + e->setText (downloadDir); layout->addWidget( e, row, 1 ); l->setBuddy( e ); } + l = myFreespaceLabel = new FreespaceLabel (mySession, downloadDir, this); + layout->addWidget (l, ++row, 0, 1, 2, Qt::Alignment (Qt::AlignRight | Qt::AlignTop)); + layout->setRowMinimumHeight (row, l->height() + HIG::PAD); + myTree = new FileTreeView (0, false); layout->addWidget( myTree, ++row, 0, 1, 2 ); if( !session.isLocal( ) ) @@ -174,7 +180,7 @@ Options :: Options( Session& session, const Prefs& prefs, const AddData& addme, connect( b, SIGNAL(accepted()), this, SLOT(onAccepted()) ); layout->addWidget( b, ++row, 0, 1, 2 ); - layout->setRowStretch( 2, 2 ); + layout->setRowStretch( 3, 2 ); layout->setColumnStretch( 1, 2 ); layout->setSpacing( HIG :: PAD ); @@ -448,13 +454,14 @@ Options :: onDestinationClicked( ) } void -Options :: onDestinationsSelected( const QStringList& destinations ) +Options :: onDestinationsSelected (const QStringList& destinations) { - if( destinations.size() == 1 ) + if (destinations.size() == 1) { - const QString& destination( destinations.first( ) ); - myDestination.setPath( destination ); - refreshDestinationButton( ); + const QString& destination (destinations.first ()); + myFreespaceLabel->setPath (destination); + myDestination.setPath (destination); + refreshDestinationButton (); } } diff --git a/qt/options.h b/qt/options.h index f377fb229..d275ffb4a 100644 --- a/qt/options.h +++ b/qt/options.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -32,10 +31,13 @@ #include "add-data.h" // AddData #include "file-tree.h" // FileList -class FileTreeView; -class Prefs; class QCheckBox; class QComboBox; +class QPushButton; + +class FileTreeView; +class FreespaceLabel; +class Prefs; class Session; extern "C" { struct tr_variant; }; @@ -78,6 +80,7 @@ class Options: public QDialog bool myHaveInfo; tr_info myInfo; FileTreeView * myTree; + FreespaceLabel * myFreespaceLabel; QCheckBox * myStartCheck; QCheckBox * myTrashCheck; QComboBox * myPriorityCombo; diff --git a/qt/prefs-dialog.cc b/qt/prefs-dialog.cc index e60d7c540..f41fad414 100644 --- a/qt/prefs-dialog.cc +++ b/qt/prefs-dialog.cc @@ -36,6 +36,7 @@ #include #include +#include "freespace-label.h" #include "formatter.h" #include "hig.h" #include "prefs.h" @@ -588,6 +589,13 @@ PrefsDialog :: createDownloadingTab( ) connect( b, SIGNAL(clicked(bool)), this, SLOT(onDestinationClicked(void)) ); hig->addRow( tr( "Save to &Location:" ), b ); + const QString downloadDir (myPrefs.getString(Prefs::DOWNLOAD_DIR)); + l = myFreespaceLabel = new FreespaceLabel (mySession, downloadDir, this); + QHBoxLayout * h = new QHBoxLayout (); + h->addStretch (1); + h->addWidget (l); + hig->addWideControl (h); + hig->addSectionDivider( ); hig->addSectionTitle( tr( "Download Queue" ) ); @@ -746,8 +754,9 @@ PrefsDialog :: refreshPref( int key ) break; case Prefs :: DOWNLOAD_DIR: { - QString path( myPrefs.getString( key ) ); + const QString path( myPrefs.getString( key ) ); myDestinationButton->setText( QFileInfo(path).fileName() ); + myFreespaceLabel->setPath (path); break; } diff --git a/qt/prefs-dialog.h b/qt/prefs-dialog.h index 288077229..f0f10fae5 100644 --- a/qt/prefs-dialog.h +++ b/qt/prefs-dialog.h @@ -32,6 +32,7 @@ class QPushButton; class QMessageBox; class QHttp; +class FreespaceLabel; class Prefs; class Session; @@ -108,6 +109,7 @@ class PrefsDialog: public QDialog QWidgetList mySchedWidgets; QWidgetList myBlockWidgets; QWidgetList myUnsupportedWhenRemote; + FreespaceLabel * myFreespaceLabel; int myBlocklistHttpTag; QHttp * myBlocklistHttp; diff --git a/qt/qtr.pro b/qt/qtr.pro index 117cd19a1..aaae24441 100644 --- a/qt/qtr.pro +++ b/qt/qtr.pro @@ -52,6 +52,7 @@ SOURCES += about.cc \ file-tree.cc \ filterbar.cc \ filters.cc \ + freespace-label.cc \ formatter.cc \ hig.cc \ license.cc \ diff --git a/qt/session.cc b/qt/session.cc index c115b6f88..092c57904 100644 --- a/qt/session.cc +++ b/qt/session.cc @@ -258,8 +258,7 @@ Session :: Session (const char * configDir, Prefs& prefs): myPrefs (prefs), mySession (0), myConfigDir (QString::fromUtf8 (configDir)), - myNAM (0), - myDownloadDirFreeSpace (-1) + myNAM (0) { myStats.ratio = TR_RATIO_NA; myStats.uploadedBytes = 0; @@ -1008,9 +1007,6 @@ Session :: updateInfo (tr_variant * d) if (tr_variantDictFindStr (d, TR_KEY_version, &str, NULL) && (mySessionVersion != str)) mySessionVersion = str; - if (tr_variantDictFindInt (d, TR_KEY_download_dir_free_space, &i) && (myDownloadDirFreeSpace != i)) - myDownloadDirFreeSpace = i; - //std::cerr << "Session :: updateInfo end" << std::endl; connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (updatePref (int))); diff --git a/qt/session.h b/qt/session.h index fb905c352..3fd30ef99 100644 --- a/qt/session.h +++ b/qt/session.h @@ -57,7 +57,6 @@ class Session: public QObject const struct tr_session_stats& getStats () const { return myStats; } const struct tr_session_stats& getCumulativeStats () const { return myCumulativeStats; } const QString& sessionVersion () const { return mySessionVersion; } - int64_t downloadDirFreeSpace () const { return myDownloadDirFreeSpace; } public: int64_t blocklistSize () const { return myBlocklistSize; } @@ -160,7 +159,6 @@ class Session: public QObject struct tr_session_stats myStats; struct tr_session_stats myCumulativeStats; QString mySessionVersion; - int64_t myDownloadDirFreeSpace; }; #endif