Merge branch 'master' into patch-4

This commit is contained in:
Mitch Livingston 2020-04-20 07:55:12 -04:00 committed by GitHub
commit 8a28db8c1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
285 changed files with 8416 additions and 2598 deletions

View File

@ -44,8 +44,8 @@ set(TR_NAME ${PROJECT_NAME})
# "Z" for unsupported trunk builds,
# "0" for stable, supported releases
# these should be the only two lines you need to change
set(TR_USER_AGENT_PREFIX "2.94+")
set(TR_PEER_ID_PREFIX "-TR294Z-")
set(TR_USER_AGENT_PREFIX "3.00+")
set(TR_PEER_ID_PREFIX "-TR300Z-")
string(REGEX MATCH "^([0-9]+)\\.([0-9]+).*" TR_VERSION "${TR_USER_AGENT_PREFIX}")
set(TR_VERSION_MAJOR "${CMAKE_MATCH_1}")
@ -378,12 +378,12 @@ endif()
if(CMAKE_VERSION VERSION_LESS "3.1")
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
endif()
else()
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

2
NEWS
View File

@ -3,7 +3,7 @@
==== All Platforms ====
==== OS X Client Client ====
* Dark Mode support
* Sparkle updated to 1.21.2
* Sparkle updated to 1.22.0
* Minimum OS version updated to 10.10
==== Qt Client ====
==== Web Client ====

File diff suppressed because it is too large Load Diff

View File

@ -194,5 +194,5 @@ macro(tr_qt_add_resources)
endmacro()
macro(tr_qt_add_translation)
qt5_add_translation(${ARGN})
qt5_add_translation(${ARGN} OPTIONS -silent)
endmacro()

View File

@ -32,7 +32,7 @@ BEGIN
VALUE "FileDescription", "${TR_FILE_DESCRIPTION}"
VALUE "FileVersion", LONG_VERSION_STRING
VALUE "InternalName", "${TR_INTERNAL_NAME}"
VALUE "LegalCopyright", "2005-2019 Transmission Project"
VALUE "LegalCopyright", "2005-2020 Transmission Project"
VALUE "OriginalFilename", "${TR_ORIGINAL_FILENAME}"
VALUE "ProductName", "Transmission"
VALUE "ProductVersion", LONG_VERSION_STRING

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env bash
set -euo pipefail

View File

@ -3,8 +3,8 @@ dnl STATUS: "X" for prerelease beta builds,
dnl "Z" for unsupported trunk builds,
dnl "0" for stable, supported releases
dnl these should be the only two lines you need to change
m4_define([user_agent_prefix],[2.94+])
m4_define([peer_id_prefix],[-TR294Z-])
m4_define([user_agent_prefix],[3.00+])
m4_define([peer_id_prefix],[-TR300Z-])
AC_INIT([transmission],[user_agent_prefix],[https://github.com/transmission/transmission])
AC_SUBST(USERAGENT_PREFIX,[user_agent_prefix])

View File

@ -148,11 +148,25 @@
(1) An optional "ids" array as described in 3.1.
(2) A required "fields" array of keys. (see list below)
(3) An optional "format" string specifying how to format the
"torrents" response field. Allowed values are "objects" (default)
and "table". (see "Response arguments" below)
Response arguments:
(1) A "torrents" array of objects, each of which contains
the key/value pairs matching the request's "fields" argument.
(1) A "torrents" array.
If the "format" request was "objects" (default), "torrents" will
be an array of objects, each of which contains the key/value
pairs matching the request's "fields" arg. This was the only
format before Transmission 3 and has some obvious programmer
conveniences, such as parsing directly into Javascript objects.
If the format was "table", then "torrents" will be an array of
arrays. The first row holds the keys and each remaining row holds
a torrent's values for those keys. This format is more efficient
in terms of JSON generation and JSON parsing.
(2) If the request's "ids" field was "recently-active",
a "removed" array of torrent-id numbers of recently-removed
torrents.
@ -176,6 +190,7 @@
downloadedEver | number | tr_stat
downloadLimit | number | tr_torrent
downloadLimited | boolean | tr_torrent
editDate | number | tr_stat
error | number | tr_stat
errorString | string | tr_stat
eta | number | tr_stat
@ -792,6 +807,8 @@
| | yes | session-get | new arg "session-id"
| | yes | torrent-get | new arg "labels"
| | yes | torrent-set | new arg "labels"
| | yes | torrent-set | new arg "editDate"
| | yes | torrent-get | new arg "format"
5.1. Upcoming Breakage

View File

@ -1226,7 +1226,7 @@ static GtkWidget* info_page_new(struct DetailsImpl* di)
gtk_frame_set_shadow_type(GTK_FRAME(fr), GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(fr), sw);
w = hig_workarea_add_tall_row(t, &row, _("Comment:"), fr, NULL);
gtk_misc_set_alignment(GTK_MISC(w), 0.0F, 0.0F);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_START, NULL);
hig_workarea_add_section_divider(t, &row);
return t;
@ -2028,7 +2028,6 @@ static GtkWidget* peer_page_new(struct DetailsImpl* di)
store = di->webseed_store = webseed_model_new();
v = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_signal_connect(v, "button-release-event", G_CALLBACK(on_tree_view_button_released), NULL);
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(v), TRUE);
g_object_unref(store);
str = getWebseedColumnNames(WEBSEED_COL_URL);
@ -2614,7 +2613,7 @@ static void on_edit_trackers(GtkButton* button, gpointer data)
gtk_label_set_markup(GTK_LABEL(l), _("To add a backup URL, add it on the line after the primary URL.\n"
"To add another primary URL, add it after a blank line."));
gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
hig_workarea_add_wide_control(t, &row, l);
w = gtk_text_view_new();
@ -2714,7 +2713,6 @@ static void on_tracker_list_add_button_clicked(GtkButton* button UNUSED, gpointe
g_string_append_printf(gstr, _("%s - Add Tracker"), tr_torrentName(tor));
w = gtk_dialog_new_with_buttons(gstr->str, GTK_WINDOW(di->dialog), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(w), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
g_signal_connect(w, "response", G_CALLBACK(on_add_tracker_response), gdi);
row = 0;
@ -2804,7 +2802,6 @@ static GtkWidget* tracker_page_new(struct DetailsImpl* di)
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(v), FALSE);
g_signal_connect(v, "button-press-event", G_CALLBACK(on_tree_view_button_pressed), NULL);
g_signal_connect(v, "button-release-event", G_CALLBACK(on_tree_view_button_released), NULL);
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(v), TRUE);
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(v));
g_signal_connect(sel, "changed", G_CALLBACK(on_tracker_list_selection_changed), di);
@ -2840,7 +2837,7 @@ static GtkWidget* tracker_page_new(struct DetailsImpl* di)
gtk_box_pack_start(GTK_BOX(v), w, FALSE, FALSE, 0);
w = gtk_button_new_with_mnemonic(_("_Edit"));
gtk_button_set_image(GTK_BUTTON(w), gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON));
gtk_button_set_image(GTK_BUTTON(w), gtk_image_new_from_icon_name(GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON));
g_signal_connect(w, "clicked", G_CALLBACK(on_edit_trackers), di);
di->edit_trackers_button = w;
gtk_box_pack_start(GTK_BOX(v), w, FALSE, FALSE, 0);

View File

@ -154,7 +154,6 @@ void gtr_confirm_remove(GtkWindow* parent, TrCore* core, GSList* torrent_ids, gb
gtk_dialog_add_buttons(GTK_DIALOG(d), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
delete_files ? GTK_STOCK_DELETE : GTK_STOCK_REMOVE, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(d), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
g_signal_connect(d, "response", G_CALLBACK(on_remove_dialog_response), dd);
gtk_widget_show_all(d);

View File

@ -305,7 +305,8 @@ static gboolean refreshModel(gpointer file_data)
struct ActiveData
{
GtkTreeSelection* sel;
GArray* array;
tr_file_index_t* indexBuf;
size_t indexCount;
};
static gboolean getSelectedFilesForeach(GtkTreeModel* model, GtkTreePath* path UNUSED, GtkTreeIter* iter, gpointer gdata)
@ -335,27 +336,29 @@ static gboolean getSelectedFilesForeach(GtkTreeModel* model, GtkTreePath* path U
{
unsigned int i;
gtk_tree_model_get(model, iter, FC_INDEX, &i, -1);
g_array_append_val(data->array, i);
data->indexBuf[data->indexCount++] = i;
}
}
return FALSE; /* keep walking */
}
static GArray* getSelectedFilesAndDescendants(GtkTreeView* view)
static size_t getSelectedFilesAndDescendants(GtkTreeView* view, tr_file_index_t* indexBuf)
{
struct ActiveData data;
data.sel = gtk_tree_view_get_selection(view);
data.array = g_array_new(FALSE, FALSE, sizeof(tr_file_index_t));
data.indexBuf = indexBuf;
data.indexCount = 0;
gtk_tree_model_foreach(gtk_tree_view_get_model(view), getSelectedFilesForeach, &data);
return data.array;
return data.indexCount;
}
struct SubtreeForeachData
{
GArray* array;
GtkTreePath* path;
tr_file_index_t* indexBuf;
size_t indexCount;
};
static gboolean getSubtreeForeach(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer gdata)
@ -370,43 +373,46 @@ static gboolean getSubtreeForeach(GtkTreeModel* model, GtkTreePath* path, GtkTre
{
unsigned int i;
gtk_tree_model_get(model, iter, FC_INDEX, &i, -1);
g_array_append_val(data->array, i);
data->indexBuf[data->indexCount++] = i;
}
}
return FALSE; /* keep walking */
}
static void getSubtree(GtkTreeView* view, GtkTreePath* path, GArray* indices)
static size_t getSubtree(GtkTreeView* view, GtkTreePath* path, tr_file_index_t* indexBuf)
{
struct SubtreeForeachData tmp;
tmp.array = indices;
tmp.indexBuf = indexBuf;
tmp.indexCount = 0;
tmp.path = path;
gtk_tree_model_foreach(gtk_tree_view_get_model(view), getSubtreeForeach, &tmp);
return tmp.indexCount;
}
/* if `path' is a selected row, all selected rows are returned.
* otherwise, only the row indicated by `path' is returned.
* this is for toggling all the selected rows' states in a batch.
*
* indexBuf should be large enough to hold tr_inf.fileCount files.
*/
static GArray* getActiveFilesForPath(GtkTreeView* view, GtkTreePath* path)
static size_t getActiveFilesForPath(GtkTreeView* view, GtkTreePath* path, tr_file_index_t* indexBuf)
{
GArray* indices;
size_t indexCount;
GtkTreeSelection* sel = gtk_tree_view_get_selection(view);
if (gtk_tree_selection_path_is_selected(sel, path))
{
/* clicked in a selected row... use the current selection */
indices = getSelectedFilesAndDescendants(view);
indexCount = getSelectedFilesAndDescendants(view, indexBuf);
}
else
{
/* clicked OUTSIDE of the selected row... just use the clicked row */
indices = g_array_new(FALSE, FALSE, sizeof(tr_file_index_t));
getSubtree(view, path, indices);
indexCount = getSubtree(view, path, indexBuf);
}
return indices;
return indexCount;
}
/***
@ -709,7 +715,8 @@ static gboolean onViewPathToggled(GtkTreeView* view, GtkTreeViewColumn* col, Gtk
if (tor != NULL && (cid == FC_PRIORITY || cid == FC_ENABLED))
{
GtkTreeIter iter;
GArray* indices = getActiveFilesForPath(view, path);
tr_file_index_t* const indexBuf = g_new0(tr_file_index_t, tr_torrentInfo(tor)->fileCount);
size_t const indexCount = getActiveFilesForPath(view, path, indexBuf);
GtkTreeModel* model = data->model;
gtk_tree_model_get_iter(model, &iter, path);
@ -734,7 +741,7 @@ static gboolean onViewPathToggled(GtkTreeView* view, GtkTreeViewColumn* col, Gtk
break;
}
tr_torrentSetFilePriorities(tor, (tr_file_index_t*)indices->data, (tr_file_index_t)indices->len, priority);
tr_torrentSetFilePriorities(tor, indexBuf, indexCount, priority);
}
else
{
@ -742,11 +749,11 @@ static gboolean onViewPathToggled(GtkTreeView* view, GtkTreeViewColumn* col, Gtk
gtk_tree_model_get(model, &iter, FC_ENABLED, &enabled, -1);
enabled = !enabled;
tr_torrentSetFileDLs(tor, (tr_file_index_t*)indices->data, (tr_file_index_t)indices->len, enabled);
tr_torrentSetFileDLs(tor, indexBuf, indexCount, enabled);
}
refresh(data);
g_array_free(indices, TRUE);
g_free(indexBuf);
handled = TRUE;
}
@ -925,7 +932,6 @@ GtkWidget* gtr_file_list_new(TrCore* core, int torrentId)
/* create the view */
view = gtk_tree_view_new();
tree_view = GTK_TREE_VIEW(view);
gtk_tree_view_set_rules_hint(tree_view, TRUE);
gtk_container_set_border_width(GTK_CONTAINER(view), GUI_PAD_BIG);
g_signal_connect(view, "button-press-event", G_CALLBACK(onViewButtonPressed), data);
g_signal_connect(view, "row_activated", G_CALLBACK(onRowActivated), data);

View File

@ -959,20 +959,20 @@ GtkWidget* gtr_filter_bar_new(tr_session* session, GtkTreeModel* tmodel, GtkTree
gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
gtk_box_pack_start(h_box, l, FALSE, FALSE, 0);
gtk_box_pack_start(h_box, w, TRUE, TRUE, 0);
/* add a spacer */
w = gtk_alignment_new(0.0F, 0.0F, 0.0F, 0.0F);
gtk_widget_set_size_request(w, 0U, GUI_PAD_BIG);
gtk_box_pack_start(h_box, w, FALSE, FALSE, 0);
#if GTK_CHECK_VERSION(3, 12, 0)
gtk_widget_set_margin_end(w, GUI_PAD);
#else
gtk_widget_set_margin_right(w, GUI_PAD);
#endif
/* add the tracker combobox */
w = tracker;
gtk_box_pack_start(h_box, w, TRUE, TRUE, 0);
/* add a spacer */
w = gtk_alignment_new(0.0F, 0.0F, 0.0F, 0.0F);
gtk_widget_set_size_request(w, 0U, GUI_PAD_BIG);
gtk_box_pack_start(h_box, w, FALSE, FALSE, 0);
#if GTK_CHECK_VERSION(3, 12, 0)
gtk_widget_set_margin_end(w, GUI_PAD);
#else
gtk_widget_set_margin_right(w, GUI_PAD);
#endif
/* add the entry field */
s = gtk_entry_new();

View File

@ -22,7 +22,7 @@ GtkWidget* hig_workarea_create(void)
void hig_workarea_add_section_divider(GtkWidget* t, guint* row)
{
GtkWidget* w = gtk_alignment_new(0.0F, 0.0F, 0.0F, 0.0F);
GtkWidget* w = gtk_fixed_new();
gtk_widget_set_size_request(w, 0U, 6U);
gtk_grid_attach(GTK_GRID(t), w, 0, *row, 2, 1);
@ -43,7 +43,7 @@ void hig_workarea_add_section_title(GtkWidget* t, guint* row, char const* sectio
g_snprintf(buf, sizeof(buf), "<b>%s</b>", section_title);
l = gtk_label_new(buf);
gtk_misc_set_alignment(GTK_MISC(l), 0.0F, 0.5F);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_label_set_use_markup(GTK_LABEL(l), TRUE);
hig_workarea_add_section_title_widget(t, row, l);
}
@ -51,7 +51,11 @@ void hig_workarea_add_section_title(GtkWidget* t, guint* row, char const* sectio
void hig_workarea_add_wide_control(GtkWidget* t, guint* row, GtkWidget* w)
{
gtk_widget_set_hexpand(w, TRUE);
#if GTK_CHECK_VERSION(3, 12, 0)
gtk_widget_set_margin_start(w, 18);
#else
gtk_widget_set_margin_left(w, 18);
#endif
gtk_grid_attach(GTK_GRID(t), w, 0, *row, 2, 1);
++*row;
}
@ -74,11 +78,15 @@ GtkWidget* hig_workarea_add_wide_checkbutton(GtkWidget* t, guint* row, char cons
void hig_workarea_add_label_w(GtkWidget* t, guint row, GtkWidget* w)
{
#if GTK_CHECK_VERSION(3, 12, 0)
gtk_widget_set_margin_start(w, 18);
#else
gtk_widget_set_margin_left(w, 18);
#endif
if (GTK_IS_MISC(w))
{
gtk_misc_set_alignment(GTK_MISC(w), 0.0F, 0.5F);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
}
if (GTK_IS_LABEL(w))
@ -93,7 +101,7 @@ static void hig_workarea_add_tall_control(GtkWidget* t, guint row, GtkWidget* co
{
if (GTK_IS_MISC(control))
{
gtk_misc_set_alignment(GTK_MISC(control), 0.0F, 0.5F);
g_object_set(control, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
}
g_object_set(control, "expand", TRUE, NULL);
@ -104,7 +112,7 @@ static void hig_workarea_add_control(GtkWidget* t, guint row, GtkWidget* control
{
if (GTK_IS_MISC(control))
{
gtk_misc_set_alignment(GTK_MISC(control), 0.0F, 0.5F);
g_object_set(control, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
}
gtk_widget_set_hexpand(control, TRUE);

View File

@ -533,7 +533,7 @@ static void on_startup(GApplication* application, gpointer user_data)
win = GTK_WINDOW(gtr_window_new(GTK_APPLICATION(application), ui_manager, cbdata->core));
g_signal_connect(win, "size-allocate", G_CALLBACK(on_main_window_size_allocated), cbdata);
g_application_hold(application);
g_object_weak_ref(G_OBJECT(win), (GWeakNotify)g_application_release, application);
g_object_weak_ref(G_OBJECT(win), (GWeakNotify)(GCallback)g_application_release, application);
app_setup(win, cbdata);
tr_sessionSetRPCCallback(session, on_rpc_changed, cbdata);
@ -848,7 +848,7 @@ static void on_drag_data_received(GtkWidget* widget UNUSED, GdkDragContext* drag
open_files(files, gdata);
/* cleanup */
g_slist_foreach(files, (GFunc)g_object_unref, NULL);
g_slist_foreach(files, (GFunc)(GCallback)g_object_unref, NULL);
g_slist_free(files);
g_strfreev(uris);
@ -885,7 +885,7 @@ static gboolean on_session_closed(gpointer gdata)
struct cbdata* cbdata = gdata;
tmp = g_slist_copy(cbdata->details);
g_slist_foreach(tmp, (GFunc)gtk_widget_destroy, NULL);
g_slist_foreach(tmp, (GFunc)(GCallback)gtk_widget_destroy, NULL);
g_slist_free(tmp);
if (cbdata->prefs != NULL)
@ -905,9 +905,9 @@ static gboolean on_session_closed(gpointer gdata)
g_object_unref(cbdata->icon);
}
g_slist_foreach(cbdata->error_list, (GFunc)g_free, NULL);
g_slist_foreach(cbdata->error_list, (GFunc)(GCallback)g_free, NULL);
g_slist_free(cbdata->error_list);
g_slist_foreach(cbdata->duplicates_list, (GFunc)g_free, NULL);
g_slist_foreach(cbdata->duplicates_list, (GFunc)(GCallback)g_free, NULL);
g_slist_free(cbdata->duplicates_list);
return G_SOURCE_REMOVE;
@ -938,9 +938,7 @@ static void exit_now_cb(GtkWidget* w UNUSED, gpointer data UNUSED)
static void on_app_exit(gpointer vdata)
{
GtkWidget* r;
GtkWidget* p;
GtkWidget* b;
GtkWidget* w;
GtkWidget* c;
struct cbdata* cbdata = vdata;
@ -970,32 +968,29 @@ static void on_app_exit(gpointer vdata)
c = GTK_WIDGET(cbdata->wind);
gtk_container_remove(GTK_CONTAINER(c), gtk_bin_get_child(GTK_BIN(c)));
r = gtk_alignment_new(0.5, 0.5, 0.01, 0.01);
gtk_container_add(GTK_CONTAINER(c), r);
p =
g_object_new(GTK_TYPE_GRID, "column-spacing", GUI_PAD_BIG, "halign", GTK_ALIGN_CENTER, "valign", GTK_ALIGN_CENTER,
NULL);
gtk_container_add(GTK_CONTAINER(c), p);
p = gtk_grid_new();
gtk_grid_set_column_spacing(GTK_GRID(p), GUI_PAD_BIG);
gtk_container_add(GTK_CONTAINER(r), p);
w = gtk_image_new_from_stock(GTK_STOCK_NETWORK, GTK_ICON_SIZE_DIALOG);
w = gtk_image_new_from_icon_name(GTK_STOCK_NETWORK, GTK_ICON_SIZE_DIALOG);
gtk_grid_attach(GTK_GRID(p), w, 0, 0, 1, 2);
w = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(w), _("<b>Closing Connections</b>"));
gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_grid_attach(GTK_GRID(p), w, 1, 0, 1, 1);
w = gtk_label_new(_("Sending upload/download totals to tracker…"));
gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_grid_attach(GTK_GRID(p), w, 1, 1, 1, 1);
b = gtk_alignment_new(0.0, 1.0, 0.01, 0.01);
w = gtk_button_new_with_mnemonic(_("_Quit Now"));
g_object_set(w, "margin-top", GUI_PAD, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_END, NULL);
g_signal_connect(w, "clicked", G_CALLBACK(exit_now_cb), NULL);
gtk_container_add(GTK_CONTAINER(b), w);
gtk_grid_attach(GTK_GRID(p), b, 1, 2, 1, 1);
gtk_grid_attach(GTK_GRID(p), w, 1, 2, 1, 1);
gtk_widget_show_all(r);
gtk_widget_show_all(p);
gtk_widget_grab_focus(w);
/* clear the UI */
@ -1031,7 +1026,7 @@ static void show_torrent_errors(GtkWindow* window, char const* primary, GSList**
gtk_widget_show(w);
g_string_free(s, TRUE);
g_slist_foreach(*files, (GFunc)g_free, NULL);
g_slist_foreach(*files, (GFunc)(GCallback)g_free, NULL);
g_slist_free(*files);
*files = NULL;
}
@ -1488,7 +1483,7 @@ static tr_torrent* get_first_selected_torrent(struct cbdata* data)
}
}
g_list_foreach(l, (GFunc)gtk_tree_path_free, NULL);
g_list_foreach(l, (GFunc)(GCallback)gtk_tree_path_free, NULL);
g_list_free(l);
return tor;
}

View File

@ -192,7 +192,7 @@ static void makeProgressDialog(GtkWidget* parent, MakeMetaUI* ui)
gtk_container_add(GTK_CONTAINER(fr), v);
l = gtk_label_new(_("Creating torrent…"));
gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
ui->progress_label = l;
gtk_box_pack_start(GTK_BOX(v), l, FALSE, FALSE, 0);
@ -493,7 +493,7 @@ GtkWidget* gtr_torrent_creation_dialog_new(GtkWindow* parent, TrCore* core)
gtk_label_set_markup(GTK_LABEL(l), _("To add a backup URL, add it on the line after the primary URL.\n"
"To add another primary URL, add it after a blank line."));
gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_box_pack_start(GTK_BOX(v), l, FALSE, FALSE, 0);
hig_workarea_add_tall_row(t, &row, str, v, NULL);

View File

@ -209,7 +209,6 @@ static void onSaveRequest(GtkWidget* w, gpointer data)
GtkWidget* d = gtk_file_chooser_dialog_new(_("Save Log"), window, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(d), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
g_signal_connect(d, "response", G_CALLBACK(onSaveDialogResponse), data);
gtk_widget_show(d);
}
@ -490,7 +489,7 @@ GtkWidget* gtr_message_log_window_new(GtkWindow* parent, TrCore* core)
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
w = gtk_label_new(_("Level"));
gtk_misc_set_padding(GTK_MISC(w), GUI_PAD, 0);
g_object_set(w, "margin", GUI_PAD, NULL);
item = gtk_tool_item_new();
gtk_container_add(GTK_CONTAINER(item), w);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
@ -527,7 +526,6 @@ GtkWidget* gtr_message_log_window_new(GtkWindow* parent, TrCore* core)
g_object_unref(data->sort);
g_signal_connect(view, "button-release-event", G_CALLBACK(on_tree_view_button_released), NULL);
data->view = GTK_TREE_VIEW(view);
gtk_tree_view_set_rules_hint(data->view, TRUE);
appendColumn(data->view, COL_SEQUENCE);
appendColumn(data->view, COL_NAME);
appendColumn(data->view, COL_MESSAGE);

View File

@ -191,7 +191,8 @@ void gtr_notify_torrent_completed(TrCore* core, int torrent_id)
tor = gtr_core_find_torrent(core, torrent_id);
n = g_new0(TrNotification, 1);
n->core = g_object_ref(G_OBJECT(core));
g_object_ref(G_OBJECT(core));
n->core = core;
n->torrent_id = torrent_id;
g_variant_builder_init(&actions_builder, G_VARIANT_TYPE("as"));

View File

@ -84,7 +84,7 @@ static void save_recent_destination(TrCore* core, char const* dir)
gtr_pref_save(gtr_core_session(core));
/* cleanup */
g_slist_foreach(list, (GFunc)g_free, NULL);
g_slist_foreach(list, (GFunc)(GCallback)g_free, NULL);
g_slist_free(list);
}
@ -284,7 +284,6 @@ GtkWidget* gtr_torrent_options_dialog_new(GtkWindow* parent, TrCore* core, tr_ct
d = gtk_dialog_new_with_buttons(_("Torrent Options"), parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_ACCEPT);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(d), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
if (!tr_ctorGetDownloadDir(ctor, TR_FORCE, &str))
{
@ -317,7 +316,7 @@ GtkWidget* gtr_torrent_options_dialog_new(GtkWindow* parent, TrCore* core, tr_ct
/* "torrent file" row */
l = gtk_label_new_with_mnemonic(_("_Torrent file:"));
gtk_misc_set_alignment(GTK_MISC(l), 0.0F, 0.5F);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_grid_attach(grid, l, 0, row, 1, 1);
w = gtk_file_chooser_button_new(_("Select Source File"), GTK_FILE_CHOOSER_ACTION_OPEN);
source_chooser = w;
@ -330,7 +329,7 @@ GtkWidget* gtr_torrent_options_dialog_new(GtkWindow* parent, TrCore* core, tr_ct
/* "destination folder" row */
row++;
l = gtk_label_new_with_mnemonic(_("_Destination folder:"));
gtk_misc_set_alignment(GTK_MISC(l), 0.0F, 0.5F);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_grid_attach(grid, l, 0, row, 1, 1);
w = gtk_file_chooser_button_new(_("Select Destination Folder"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
@ -354,7 +353,7 @@ GtkWidget* gtr_torrent_options_dialog_new(GtkWindow* parent, TrCore* core, tr_ct
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);
g_object_set(l, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
gtk_grid_attach(grid, l, 0, row, 2, 1);
/* file list row */
@ -367,7 +366,7 @@ GtkWidget* gtr_torrent_options_dialog_new(GtkWindow* parent, TrCore* core, tr_ct
/* torrent priority row */
row++;
l = gtk_label_new_with_mnemonic(_("Torrent _priority:"));
gtk_misc_set_alignment(GTK_MISC(l), 0.0F, 0.5F);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_grid_attach(grid, l, 0, row, 1, 1);
w = data->priority_combo;
gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
@ -440,7 +439,7 @@ static void onOpenDialogResponse(GtkDialog* dialog, int response, gpointer core)
GSList* files = gtk_file_chooser_get_files(chooser);
gtr_core_add_files(core, files, do_start, do_prompt, do_notify);
g_slist_foreach(files, (GFunc)g_object_unref, NULL);
g_slist_foreach(files, (GFunc)(GCallback)g_object_unref, NULL);
g_slist_free(files);
}
@ -455,7 +454,6 @@ GtkWidget* gtr_torrent_open_from_file_dialog_new(GtkWindow* parent, TrCore* core
w = gtk_file_chooser_dialog_new(_("Open a Torrent"), parent, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(w), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(w), TRUE);
addTorrentFilters(GTK_FILE_CHOOSER(w));
g_signal_connect(w, "response", G_CALLBACK(onOpenDialogResponse), core);
@ -519,7 +517,6 @@ GtkWidget* gtr_torrent_open_from_url_dialog_new(GtkWindow* parent, TrCore* core)
w = gtk_dialog_new_with_buttons(_("Open URL"), parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(w), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
g_signal_connect(w, "response", G_CALLBACK(onOpenURLResponse), core);
row = 0;

View File

@ -139,7 +139,6 @@ GtkWidget* gtr_relocate_dialog_new(GtkWindow* parent, TrCore* core, GSList* torr
d = gtk_dialog_new_with_buttons(_("Set Torrent Location"), parent, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, NULL);
gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(d), GTK_RESPONSE_APPLY, GTK_RESPONSE_CANCEL, -1);
g_signal_connect(d, "response", G_CALLBACK(onResponse), NULL);
row = 0;

View File

@ -123,7 +123,6 @@ GtkWidget* gtr_stats_dialog_new(GtkWindow* parent, TrCore* core)
d = gtk_dialog_new_with_buttons(_("Statistics"), parent, GTK_DIALOG_DESTROY_WITH_PARENT, _("_Reset"), TR_RESPONSE_RESET,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_CLOSE);
gtk_dialog_set_alternative_button_order(GTK_DIALOG(d), GTK_RESPONSE_CLOSE, TR_RESPONSE_RESET, -1);
t = hig_workarea_create();
ui->core = core;

View File

@ -309,7 +309,7 @@ static void getStatusString(GString* gstr, tr_torrent const* tor, tr_stat const*
****
***/
struct TorrentCellRendererPrivate
typedef struct TorrentCellRendererPrivate
{
tr_torrent* tor;
GtkCellRenderer* text_renderer;
@ -329,7 +329,8 @@ struct TorrentCellRendererPrivate
double download_speed_KBps;
gboolean compact;
};
}
TorrentCellRendererPrivate;
/***
****
@ -852,7 +853,7 @@ static void torrent_cell_renderer_get_property(GObject* object, guint property_i
}
}
G_DEFINE_TYPE(TorrentCellRenderer, torrent_cell_renderer, GTK_TYPE_CELL_RENDERER)
G_DEFINE_TYPE_WITH_CODE(TorrentCellRenderer, torrent_cell_renderer, GTK_TYPE_CELL_RENDERER, G_ADD_PRIVATE(TorrentCellRenderer))
static void torrent_cell_renderer_dispose(GObject* o)
{
@ -876,8 +877,6 @@ static void torrent_cell_renderer_class_init(TorrentCellRendererClass* klass)
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
GtkCellRendererClass* cell_class = GTK_CELL_RENDERER_CLASS(klass);
g_type_class_add_private(klass, sizeof(struct TorrentCellRendererPrivate));
cell_class->render = torrent_cell_renderer_render;
cell_class->get_size = torrent_cell_renderer_get_size;
gobject_class->set_property = torrent_cell_renderer_set_property;
@ -904,7 +903,11 @@ static void torrent_cell_renderer_init(TorrentCellRenderer* self)
{
struct TorrentCellRendererPrivate* p;
#if GLIB_CHECK_VERSION(2, 58, 0)
p = self->priv = torrent_cell_renderer_get_instance_private(self);
#else
p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, TORRENT_CELL_RENDERER_TYPE, struct TorrentCellRendererPrivate);
#endif
p->tor = NULL;
p->gstr1 = g_string_new(NULL);

View File

@ -61,7 +61,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
static void core_maybe_inhibit_hibernation(TrCore* core);
struct TrCorePrivate
typedef struct TrCorePrivate
{
GFileMonitor* monitor;
gulong monitor_tag;
@ -79,14 +79,15 @@ struct TrCorePrivate
GtkTreeModel* sorted_model;
tr_session* session;
GStringChunk* string_chunk;
};
}
TrCorePrivate;
static int core_is_disposed(TrCore const* core)
{
return core == NULL || core->priv->sorted_model == NULL;
}
G_DEFINE_TYPE(TrCore, tr_core, G_TYPE_OBJECT)
G_DEFINE_TYPE_WITH_CODE(TrCore, tr_core, G_TYPE_OBJECT, G_ADD_PRIVATE(TrCore));
static void core_dispose(GObject* o)
{
@ -116,8 +117,6 @@ static void tr_core_class_init(TrCoreClass* core_class)
GObjectClass* gobject_class;
GType core_type = G_TYPE_FROM_CLASS(core_class);
g_type_class_add_private(core_class, sizeof(struct TrCorePrivate));
gobject_class = G_OBJECT_CLASS(core_class);
gobject_class->dispose = core_dispose;
gobject_class->finalize = core_finalize;
@ -168,7 +167,11 @@ static void tr_core_init(TrCore* core)
G_TYPE_INT /* MC_ACTIVE_PEER_COUNT */
};
#if GLIB_CHECK_VERSION(2, 58, 0)
p = core->priv = tr_core_get_instance_private(core);
#else
p = core->priv = G_TYPE_INSTANCE_GET_PRIVATE(core, TR_CORE_TYPE, struct TrCorePrivate);
#endif
/* create the model used to store torrent data */
g_assert(G_N_ELEMENTS(types) == MC_ROW_COUNT);
@ -742,7 +745,7 @@ static gboolean core_watchdir_idle(gpointer gcore)
core->priv->adding_from_watch_dir = TRUE;
gtr_core_add_files(core, unchanging, do_start, do_prompt, TRUE);
g_slist_foreach(unchanging, (GFunc)rename_torrent_and_unref_file, NULL);
g_slist_foreach(unchanging, (GFunc)(GCallback)rename_torrent_and_unref_file, NULL);
g_slist_free(unchanging);
core->priv->adding_from_watch_dir = FALSE;
}

View File

@ -38,7 +38,11 @@ static void popup(GtkStatusIcon* self, guint button, guint when, gpointer data U
{
GtkWidget* w = gtr_action_get_widget("/icon-popup");
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu_popup_at_widget(GTK_MENU(w), GTK_WIDGET(self), GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH, NULL);
#else
gtk_menu_popup(GTK_MENU(w), NULL, NULL, gtk_status_icon_position_menu, self, button, when);
#endif
}
void gtr_icon_refresh(gpointer vicon)

View File

@ -280,7 +280,7 @@ static GtkWidget* downloadingPage(GObject* core, struct prefs_dialog_data* data)
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);
g_object_set(l, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
hig_workarea_add_wide_control(t, &row, l);
hig_workarea_add_section_divider(t, &row);
@ -526,7 +526,7 @@ static GtkWidget* privacyPage(GObject* core)
target_cb(b, e);
w = gtk_label_new("");
gtk_misc_set_alignment(GTK_MISC(w), 0.0F, 0.5F);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
updateBlocklistText(w, TR_CORE(core));
data->label = w;
h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, GUI_PAD_BIG);
@ -834,8 +834,8 @@ static GtkWidget* remotePage(GObject* core)
s = _("Addresses:");
w = hig_workarea_add_row(t, &row, s, w, NULL);
gtk_misc_set_alignment(GTK_MISC(w), 0.0F, 0.0F);
gtk_misc_set_padding(GTK_MISC(w), 0, GUI_PAD);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_START, "margin-top", GUI_PAD, "margin-bottom", GUI_PAD,
NULL);
page->whitelist_widgets = g_slist_prepend(page->whitelist_widgets, w);
h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, GUI_PAD);
@ -847,11 +847,9 @@ static GtkWidget* remotePage(GObject* core)
w = gtk_button_new_from_stock(GTK_STOCK_ADD);
page->whitelist_widgets = g_slist_prepend(page->whitelist_widgets, w);
g_signal_connect(w, "clicked", G_CALLBACK(onAddWhitelistClicked), page);
g_object_set(h, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
gtk_box_pack_start(GTK_BOX(h), w, TRUE, TRUE, 0);
w = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start(GTK_BOX(w), gtk_alignment_new(0, 0, 0, 0), TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(w), h, FALSE, FALSE, 0);
hig_workarea_add_wide_control(t, &row, w);
hig_workarea_add_wide_control(t, &row, h);
}
refreshRPCSensitivity(page);
@ -994,10 +992,10 @@ static GtkWidget* speedPage(GObject* core)
h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, GUI_PAD);
g_snprintf(buf, sizeof(buf), "<b>%s</b>", _("Alternative Speed Limits"));
w = gtk_label_new(buf);
gtk_misc_set_alignment(GTK_MISC(w), 0.0F, 0.5F);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
gtk_box_pack_start(GTK_BOX(h), w, FALSE, FALSE, 0);
w = gtk_image_new_from_stock("alt-speed-on", -1);
w = gtk_image_new_from_icon_name("alt-speed-on", GTK_ICON_SIZE_MENU);
gtk_box_pack_start(GTK_BOX(h), w, FALSE, FALSE, 0);
hig_workarea_add_section_title_widget(t, &row, h);
@ -1005,7 +1003,7 @@ static GtkWidget* speedPage(GObject* core)
g_snprintf(buf, sizeof(buf), "<small>%s</small>", s);
w = gtk_label_new(buf);
gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
gtk_misc_set_alignment(GTK_MISC(w), 0.0F, 0.5F);
g_object_set(w, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
hig_workarea_add_wide_control(t, &row, w);
g_snprintf(buf, sizeof(buf), _("U_pload (%s):"), _(speed_K_str));
@ -1137,7 +1135,7 @@ static GtkWidget* networkPage(GObject* core)
h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, GUI_PAD_BIG);
l = data->portLabel = gtk_label_new(_("Status unknown"));
gtk_misc_set_alignment(GTK_MISC(l), 0.0F, 0.5F);
g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
gtk_box_pack_start(GTK_BOX(h), l, TRUE, TRUE, 0);
w = data->portButton = gtk_button_new_with_mnemonic(_("Te_st Port"));
gtk_box_pack_end(GTK_BOX(h), w, FALSE, FALSE, 0);

View File

@ -79,7 +79,11 @@ static void on_popup_menu(GtkWidget* self UNUSED, GdkEventButton* event)
{
GtkWidget* menu = gtr_action_get_widget("/main-window-popup");
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent*)event);
#else
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event != NULL ? event->button : 0, event != NULL ? event->time : 0);
#endif
}
static void view_row_activated(GtkTreeView* tree_view UNUSED, GtkTreePath* path UNUSED, GtkTreeViewColumn* column UNUSED,
@ -132,7 +136,6 @@ static GtkWidget* makeview(PrivateData* p)
gtk_tree_view_append_column(tree_view, col);
g_object_set(r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL);
gtk_tree_view_set_rules_hint(tree_view, TRUE);
sel = gtk_tree_view_get_selection(tree_view);
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_MULTIPLE);
@ -201,11 +204,15 @@ static void privateFree(gpointer vprivate)
g_free(p);
}
static void onYinYangReleased(GtkWidget* w UNUSED, gpointer vprivate)
static void onYinYangClicked(GtkWidget* w UNUSED, gpointer vprivate)
{
PrivateData* p = vprivate;
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu_popup_at_widget(GTK_MENU(p->status_menu), GTK_WIDGET(w), GDK_GRAVITY_NORTH_EAST, GDK_GRAVITY_SOUTH_EAST, NULL);
#else
gtk_menu_popup(GTK_MENU(p->status_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
#endif
}
#define STATS_MODE "stats-mode"
@ -251,7 +258,7 @@ static void syncAltSpeedButton(PrivateData* p)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), b);
gtk_image_set_from_stock(GTK_IMAGE(p->alt_speed_image), stock, -1);
gtk_button_set_alignment(GTK_BUTTON(w), 0.5, 0.5);
g_object_set(w, "halign", GTK_ALIGN_CENTER, "valign", GTK_ALIGN_CENTER, NULL);
gtk_widget_set_tooltip_text(w, str);
g_free(str);
@ -498,7 +505,7 @@ static GtkWidget* createOptionsMenu(PrivateData* p)
return top;
}
static void onOptionsClicked(GtkButton* button UNUSED, gpointer vp)
static void onOptionsClicked(GtkButton* button, gpointer vp)
{
char buf1[512];
char buf2[512];
@ -529,7 +536,12 @@ static void onOptionsClicked(GtkButton* button UNUSED, gpointer vp)
b = gtr_pref_flag_get(TR_KEY_ratio_limit_enabled);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(b ? p->ratio_on_item : p->ratio_off_item), TRUE);
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu_popup_at_widget(GTK_MENU(p->options_menu), GTK_WIDGET(button), GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_SOUTH_WEST,
NULL);
#else
gtk_menu_popup(GTK_MENU(p->options_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
#endif
}
/***
@ -541,7 +553,6 @@ GtkWidget* gtr_window_new(GtkApplication* app, GtkUIManager* ui_mgr, TrCore* cor
char const* pch;
char const* style;
PrivateData* p;
GtkWidget* sibling = NULL;
GtkWidget* ul_lb;
GtkWidget* dl_lb;
GtkWidget* mainmenu;
@ -625,18 +636,18 @@ GtkWidget* gtr_window_new(GtkApplication* app, GtkUIManager* ui_mgr, TrCore* cor
**/
grid_w = status = p->status = gtk_grid_new();
gtk_orientable_set_orientation(GTK_ORIENTABLE(grid_w), GTK_ORIENTATION_HORIZONTAL);
grid = GTK_GRID(grid_w);
gtk_container_set_border_width(GTK_CONTAINER(grid), GUI_PAD_SMALL);
/* gear */
w = gtk_button_new();
gtk_container_add(GTK_CONTAINER(w), gtk_image_new_from_stock("utilities", -1));
gtk_container_add(GTK_CONTAINER(w), gtk_image_new_from_icon_name("utilities", GTK_ICON_SIZE_MENU));
gtk_widget_set_tooltip_text(w, _("Options"));
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
p->options_menu = createOptionsMenu(p);
g_signal_connect(w, "clicked", G_CALLBACK(onOptionsClicked), p);
sibling = w;
gtk_container_add(GTK_CONTAINER(grid), w);
/* turtle */
p->alt_speed_image = gtk_image_new();
@ -644,46 +655,45 @@ GtkWidget* gtr_window_new(GtkApplication* app, GtkUIManager* ui_mgr, TrCore* cor
gtk_button_set_image(GTK_BUTTON(w), p->alt_speed_image);
gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
g_signal_connect(w, "toggled", G_CALLBACK(alt_speed_toggled_cb), p);
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
sibling = w;
gtk_container_add(GTK_CONTAINER(grid), w);
/* spacer */
w = gtk_alignment_new(0.0F, 0.0F, 0.0F, 0.0F);
w = gtk_fixed_new();
gtk_widget_set_hexpand(w, TRUE);
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
sibling = w;
gtk_container_add(GTK_CONTAINER(grid), w);
/* download */
w = dl_lb = gtk_label_new(NULL);
p->dl_lb = GTK_LABEL(w);
gtk_label_set_single_line_mode(p->dl_lb, TRUE);
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
sibling = w;
gtk_container_add(GTK_CONTAINER(grid), w);
/* upload */
w = ul_lb = gtk_label_new(NULL);
g_object_set(G_OBJECT(w), "margin-left", GUI_PAD, NULL);
p->ul_lb = GTK_LABEL(w);
gtk_label_set_single_line_mode(p->ul_lb, TRUE);
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
sibling = w;
gtk_container_add(GTK_CONTAINER(grid), w);
/* ratio */
w = gtk_label_new(NULL);
g_object_set(G_OBJECT(w), "margin-left", GUI_PAD_BIG, NULL);
p->stats_lb = GTK_LABEL(w);
gtk_label_set_single_line_mode(p->stats_lb, TRUE);
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
sibling = w;
gtk_container_add(GTK_CONTAINER(grid), w);
/* ratio selector */
w = gtk_button_new();
gtk_widget_set_tooltip_text(w, _("Statistics"));
gtk_container_add(GTK_CONTAINER(w), gtk_image_new_from_stock("ratio", -1));
gtk_container_add(GTK_CONTAINER(w), gtk_image_new_from_icon_name("ratio", GTK_ICON_SIZE_MENU));
gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
g_signal_connect(w, "clicked", G_CALLBACK(onYinYangReleased), p);
gtk_grid_attach_next_to(grid, w, sibling, GTK_POS_RIGHT, 1, 1);
sibling = w;
g_signal_connect(w, "clicked", G_CALLBACK(onYinYangClicked), p);
gtk_container_add(GTK_CONTAINER(grid), w);
/**
*** Workarea
**/
/* workarea */
p->view = makeview(p);
w = list = p->scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
@ -707,8 +717,8 @@ GtkWidget* gtr_window_new(GtkApplication* app, GtkUIManager* ui_mgr, TrCore* cor
pango_layout_get_pixel_size(pango_layout, &w, &h);
gtk_widget_set_size_request(ul_lb, w, h);
gtk_widget_set_size_request(dl_lb, w, h);
gtk_misc_set_alignment(GTK_MISC(ul_lb), 1.0, 0.5);
gtk_misc_set_alignment(GTK_MISC(dl_lb), 1.0, 0.5);
g_object_set(ul_lb, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
g_object_set(dl_lb, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
g_object_unref(G_OBJECT(pango_layout));
}

View File

@ -398,7 +398,11 @@ void gtr_open_uri(char const* uri)
if (!opened)
{
#if GTK_CHECK_VERSION(3, 22, 0)
opened = gtk_show_uri_on_window(NULL, uri, GDK_CURRENT_TIME, NULL);
#else
opened = gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, NULL);
#endif
}
if (!opened)

View File

@ -2,6 +2,18 @@ project(libtr)
configure_file(version.h.in version.h)
set(THIRD_PARTY_SOURCES ConvertUTF.c jsonsl.c wildmat.c)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(DISABLE_WARNINGS -w)
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(DISABLE_WARNINGS -w)
elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro")
set(DISABLE_WARNINGS -erroff)
elseif(MSVC)
set(DISABLE_WARNINGS /w)
endif()
set_source_files_properties(${THIRD_PARTY_SOURCES} PROPERTIES COMPILE_FLAGS ${DISABLE_WARNINGS})
set(${PROJECT_NAME}_SOURCES
announcer.c
announcer-http.c

View File

@ -37,11 +37,11 @@ static void tau_sockaddr_setport(struct sockaddr* sa, tr_port port)
{
if (sa->sa_family == AF_INET)
{
((struct sockaddr_in*)sa)->sin_port = htons(port);
TR_DISCARD_ALIGN(sa, struct sockaddr_in*)->sin_port = htons(port);
}
else if (sa->sa_family == AF_INET6)
{
((struct sockaddr_in6*)sa)->sin6_port = htons(port);
TR_DISCARD_ALIGN(sa, struct sockaddr_in6*)->sin6_port = htons(port);
}
}

View File

@ -279,10 +279,6 @@ char* tr_clientForId(char* buf, size_t buflen, void const* id_in)
{
four_digits(buf, buflen, "Avicora", id + 3);
}
else if (strncmp(chid + 1, "BB", 2) == 0)
{
four_digits(buf, buflen, "BitBuddy", id + 3);
}
else if (strncmp(chid + 1, "BE", 2) == 0)
{
four_digits(buf, buflen, "BitTorrent SDK", id + 3);
@ -319,10 +315,6 @@ char* tr_clientForId(char* buf, size_t buflen, void const* id_in)
{
four_digits(buf, buflen, "BitWombat", id + 3);
}
else if (strncmp(chid + 1, "BX", 2) == 0)
{
four_digits(buf, buflen, "BittorrentX", id + 3);
}
else if (strncmp(chid + 1, "EB", 2) == 0)
{
four_digits(buf, buflen, "EBit", id + 3);
@ -500,10 +492,6 @@ char* tr_clientForId(char* buf, size_t buflen, void const* id_in)
four_digits(buf, buflen, "Zona", id + 3);
}
/* */
else if (strncmp(chid + 1, "AG", 2) == 0)
{
three_digits(buf, buflen, "Ares", id + 3);
}
else if (strncmp(chid + 1, "A~", 2) == 0)
{
three_digits(buf, buflen, "Ares", id + 3);
@ -532,10 +520,6 @@ char* tr_clientForId(char* buf, size_t buflen, void const* id_in)
{
three_digits(buf, buflen, "pbTorrent", id + 3);
}
else if (strncmp(chid + 1, "TT", 2) == 0)
{
three_digits(buf, buflen, "TuoTu", id + 3);
}
else if (strncmp(chid + 1, "qB", 2) == 0)
{
three_digits(buf, buflen, "qBittorrent", id + 3);

View File

@ -120,6 +120,8 @@ void tr_error_clear(tr_error** error)
*error = NULL;
}
static void error_prefix_valist(tr_error** error, char const* prefix_format, va_list args) TR_GNUC_PRINTF(2, 0);
static void error_prefix_valist(tr_error** error, char const* prefix_format, va_list args)
{
TR_ASSERT(error != NULL);

View File

@ -62,7 +62,7 @@ tr_error* tr_error_new_literal(int code, char const* message);
*
* @return Newly allocated error object on success, `NULL` otherwise.
*/
tr_error* tr_error_new_valist(int code, char const* message_format, va_list args);
tr_error* tr_error_new_valist(int code, char const* message_format, va_list args) TR_GNUC_PRINTF(2, 0);
/**
* @brief Free memory used by error object.

View File

@ -578,7 +578,7 @@ bool tr_sys_file_write_line(tr_sys_file_t handle, char const* buffer, struct tr_
*
* @return `True` on success, `false` otherwise (with `error` set accordingly).
*/
bool tr_sys_file_write_fmt(tr_sys_file_t handle, char const* format, struct tr_error** error, ...);
bool tr_sys_file_write_fmt(tr_sys_file_t handle, char const* format, struct tr_error** error, ...) TR_GNUC_PRINTF(2, 4);
/* Directory-related wrappers */

View File

@ -1079,6 +1079,7 @@ static ReadState canRead(struct tr_peerIo* io, void* arg, size_t* piece)
break;
default:
ret = READ_ERR;
TR_ASSERT_MSG(false, "unhandled handshake state %d", (int)handshake->state);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,4 @@
/*
* jsonsl
* https://github.com/mnunberg/jsonsl
*
* Copyright (c) 2012 M. Nunberg, mnunberg@haskalah.org
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* https://github.com/mnunberg/jsonsl */
/**
* JSON Simple/Stacked/Stateful Lexer.
@ -30,6 +6,9 @@
* - Maintains state
* - Callback oriented
* - Lightweight and fast. One source file and one header file
*
* Copyright (C) 2012-2015 Mark Nunberg
* See included LICENSE file for license details.
*/
#ifndef JSONSL_H_
@ -54,8 +33,16 @@ typedef char jsonsl_char_t;
typedef unsigned char jsonsl_uchar_t;
#endif /* JSONSL_USE_WCHAR */
#ifdef JSONSL_PARSE_NAN
#define JSONSL__NAN_PROXY JSONSL_SPECIALf_NAN
#define JSONSL__INF_PROXY JSONSL_SPECIALf_INF
#else
#define JSONSL__NAN_PROXY 0
#define JSONSL__INF_PROXY 0
#endif
/* Stolen from http-parser.h, and possibly others */
#if defined(_WIN32) && !defined(__MINGW32__)
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
@ -74,8 +61,6 @@ typedef int ssize_t;
#if (!defined(JSONSL_STATE_GENERIC)) && (!defined(JSONSL_STATE_USER_FIELDS))
#warning "JSONSL_STATE_USER_FIELDS not defined. Define this for extra structure fields"
#warning "or define JSONSL_STATE_GENERIC"
#define JSONSL_STATE_GENERIC
#endif /* !defined JSONSL_STATE_GENERIC */
@ -83,9 +68,33 @@ typedef int ssize_t;
#define JSONSL_STATE_USER_FIELDS
#endif /* JSONSL_STATE_GENERIC */
/* Additional fields for component object */
#ifndef JSONSL_JPR_COMPONENT_USER_FIELDS
#define JSONSL_JPR_COMPONENT_USER_FIELDS
#endif
#ifndef JSONSL_API
/**
* We require a /DJSONSL_DLL so that users already using this as a static
* or embedded library don't get confused
*/
#if defined(_WIN32) && defined(JSONSL_DLL)
#define JSONSL_API __declspec(dllexport)
#else
#define JSONSL_API
#endif /* JSONSL_API */
#endif /* _WIN32 */
#endif /* !JSONSL_API */
#ifndef JSONSL_INLINE
#if defined(_MSC_VER)
#define JSONSL_INLINE __inline
#elif defined(__GNUC__)
#define JSONSL_INLINE __inline__
#else
#define JSONSL_INLINE inline
#endif /* _MSC_VER or __GNUC__ */
#endif /* JSONSL_INLINE */
#define JSONSL_MAX_LEVELS 512
@ -144,18 +153,36 @@ typedef enum {
X(NULL, 1<<4) \
X(FLOAT, 1<<5) \
X(EXPONENT, 1<<6) \
X(NONASCII, 1<<7)
X(NONASCII, 1<<7) \
X(NAN, 1<<8) \
X(INF, 1<<9)
typedef enum {
#define X(o,b) \
JSONSL_SPECIALf_##o = b,
JSONSL_XSPECIAL
#undef X
/* Handy flags for checking */
JSONSL_SPECIALf_UNKNOWN = 1 << 8,
JSONSL_SPECIALf_NUMERIC = (JSONSL_SPECIALf_SIGNED|JSONSL_SPECIALf_UNSIGNED),
JSONSL_SPECIALf_UNKNOWN = 1 << 10,
/** @private Private */
JSONSL_SPECIALf_ZERO = 1 << 11 | JSONSL_SPECIALf_UNSIGNED,
/** @private */
JSONSL_SPECIALf_DASH = 1 << 12,
/** @private */
JSONSL_SPECIALf_POS_INF = (JSONSL_SPECIALf_INF),
JSONSL_SPECIALf_NEG_INF = (JSONSL_SPECIALf_INF|JSONSL_SPECIALf_SIGNED),
/** Type is numeric */
JSONSL_SPECIALf_NUMERIC = (JSONSL_SPECIALf_SIGNED| JSONSL_SPECIALf_UNSIGNED),
/** Type is a boolean */
JSONSL_SPECIALf_BOOLEAN = (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE),
/* For non-simple numeric types */
JSONSL_SPECIALf_NUMNOINT = (JSONSL_SPECIALf_FLOAT|JSONSL_SPECIALf_EXPONENT)
/** Type is an "extended", not integral type (but numeric) */
JSONSL_SPECIALf_NUMNOINT =
(JSONSL_SPECIALf_FLOAT|JSONSL_SPECIALf_EXPONENT|JSONSL_SPECIALf_NAN
|JSONSL_SPECIALf_INF)
} jsonsl_special_t;
@ -182,11 +209,12 @@ typedef enum {
* Various errors which may be thrown while parsing JSON
*/
#define JSONSL_XERR \
X(SUCCESS) \
/* Trailing garbage characters */ \
X(GARBAGE_TRAILING) \
/* We were expecting a 'special' (numeric, true, false, null) */ \
X(SPECIAL_EXPECTED) \
/* The 'special' value was incomplete */ \
X(SPECIAL_INCOMPLETE) \
/* Found a stray token */ \
X(STRAY_TOKEN) \
/* We were expecting a token before this one */ \
@ -217,6 +245,8 @@ typedef enum {
X(TRAILING_COMMA) \
/* An invalid number was passed in a numeric field */ \
X(INVALID_NUMBER) \
/* Value is missing for object */ \
X(VALUE_EXPECTED) \
/* The following are for JPR Stuff */ \
\
/* Found a literal '%' but it was only followed by a single valid hex digit */ \
@ -226,9 +256,14 @@ typedef enum {
/* Duplicate slash */ \
X(JPR_DUPSLASH) \
/* No leading root */ \
X(JPR_NOROOT)
X(JPR_NOROOT) \
/* Allocation failure */ \
X(ENOMEM) \
/* Invalid unicode codepoint detected (in case of escapes) */ \
X(INVALID_CODEPOINT)
typedef enum {
JSONSL_ERROR_SUCCESS = 0,
#define X(e) \
JSONSL_ERROR_##e,
JSONSL_XERR
@ -251,30 +286,28 @@ struct jsonsl_state_st {
/**
* The JSON object type
*/
jsonsl_type_t type;
unsigned type;
/** If this element is special, then its extended type is here */
jsonsl_special_t special_flags;
unsigned special_flags;
/**
* Position offset variables. These are relative to jsn->pos.
* pos_begin is the position at which this state was first pushed
* to the stack. pos_cur is the position at which return last controlled
* to this state (i.e. an immediate child state was popped from it).
*/
/**
* The position at which this state was first PUSHed
* The position (in terms of number of bytes since the first call to
* jsonsl_feed()) at which the state was first pushed. This includes
* opening tokens, if applicable.
*
* @note For strings (i.e. type & JSONSL_Tf_STRINGY is nonzero) this will
* be the position of the first quote.
*
* @see jsonsl_st::pos which contains the _current_ position and can be
* used during a POP callback to get the length of the element.
*/
size_t pos_begin;
/**
* The position at which any immediate child was last POPped.
* Note that this field is only set when the item is popped.
*/
/**FIXME: This is redundant as the same information can be derived from
* jsonsl_st::pos at pop-time */
size_t pos_cur;
/**
* Level of recursion into nesting. This is mainly a convenience
* variable, as this can technically be deduced from the lexer's
@ -309,6 +342,9 @@ struct jsonsl_state_st {
/**
* Counter which is incremented each time an escape ('\') is encountered.
* This is used internally for non-string types and should only be
* inspected by the user if the state actually represents a string
* type.
*/
unsigned int nescapes;
@ -334,6 +370,26 @@ struct jsonsl_state_st {
#endif /* JSONSL_STATE_USER_FIELDS */
};
/**Gets the number of elements in the list.
* @param st The state. Must be of type JSONSL_T_LIST
* @return number of elements in the list
*/
#define JSONSL_LIST_SIZE(st) ((st)->nelem)
/**Gets the number of key-value pairs in an object
* @param st The state. Must be of type JSONSL_T_OBJECT
* @return the number of key-value pairs in the object
*/
#define JSONSL_OBJECT_SIZE(st) ((st)->nelem / 2)
/**Gets the numeric value.
* @param st The state. Must be of type JSONSL_T_SPECIAL and
* special_flags must have the JSONSL_SPECIALf_NUMERIC flag
* set.
* @return the numeric value of the state.
*/
#define JSONSL_NUMERIC_VALUE(st) ((st)->nelem)
/*
* So now we need some special structure for keeping the
* JPR info in sync. Preferrably all in a single block
@ -410,6 +466,9 @@ struct jsonsl_st {
/** This is the current level of the stack */
unsigned int level;
/** Flag set to indicate we should stop processing */
unsigned int stopfl;
/**
* This is the current position, relative to the beginning
* of the stream.
@ -428,7 +487,12 @@ struct jsonsl_st {
/** Default callback for any action, if neither PUSH or POP callbacks are defined */
jsonsl_stack_callback action_callback;
/** Do not invoke callbacks for objects deeper than this level */
/**
* Do not invoke callbacks for objects deeper than this level.
* NOTE: This field establishes the lower bound for ignored callbacks,
* and is thus misnamed. `min_ignore_level` would actually make more
* sense, but we don't want to break API.
*/
unsigned int max_callback_level;
/** The error callback. Invoked when an error happens. Should not be NULL */
@ -550,23 +614,54 @@ void jsonsl_destroy(jsonsl_t jsn);
* @param jsn the lexer
* @param cur the current nest, which should be a struct jsonsl_nest_st
*/
#define jsonsl_last_state(jsn, cur) \
(cur->level > 1 ) \
? (jsn->stack + (cur->level-1)) \
: NULL
static JSONSL_INLINE
struct jsonsl_state_st *jsonsl_last_state(const jsonsl_t jsn,
const struct jsonsl_state_st *state)
{
/* Don't complain about overriding array bounds */
if (state->level > 1) {
return jsn->stack + state->level - 1;
} else {
return NULL;
}
}
/**
* Gets the state of the last fully consumed child of this parent. This is
* only valid in the parent's POP callback.
*
* @param the lexer
* @return A pointer to the child.
*/
static JSONSL_INLINE
struct jsonsl_state_st *jsonsl_last_child(const jsonsl_t jsn,
const struct jsonsl_state_st *parent)
{
return jsn->stack + (parent->level + 1);
}
/**Call to instruct the parser to stop parsing and return. This is valid
* only from within a callback */
static JSONSL_INLINE
void jsonsl_stop(jsonsl_t jsn)
{
jsn->stopfl = 1;
}
/**
* This enables receiving callbacks on all events. Doesn't do
* anything special but helps avoid some boilerplate.
* This does not touch the UESCAPE callbacks or flags.
*/
#define jsonsl_enable_all_callbacks(jsn) \
jsn->call_HKEY = 1; \
jsn->call_STRING = 1; \
jsn->call_OBJECT = 1; \
jsn->call_SPECIAL = 1; \
static JSONSL_INLINE
void jsonsl_enable_all_callbacks(jsonsl_t jsn)
{
jsn->call_HKEY = 1;
jsn->call_STRING = 1;
jsn->call_OBJECT = 1;
jsn->call_SPECIAL = 1;
jsn->call_LIST = 1;
}
/**
* A macro which returns true if the current state object can
@ -639,7 +734,8 @@ void jsonsl_dump_global_metrics(void);
#define JSONSL_XMATCH \
X(COMPLETE,1) \
X(POSSIBLE,0) \
X(NOMATCH,-1)
X(NOMATCH,-1) \
X(TYPE_MISMATCH, -2)
typedef enum {
@ -663,11 +759,22 @@ typedef enum {
} jsonsl_jpr_type_t;
struct jsonsl_jpr_component_st {
/** The string the component points to */
char *pstr;
/** if this is a numeric type, the number is 'cached' here */
unsigned long idx;
/** The length of the string */
size_t len;
/** The type of component (NUMERIC or STRING) */
jsonsl_jpr_type_t ptype;
/** Set this to true to enforce type checking between dict keys and array
* indices. jsonsl_jpr_match() will return TYPE_MISMATCH if it detects
* that an array index is actually a child of a dictionary. */
short is_arridx;
/* Extra fields (for more advanced searches. Default is empty) */
JSONSL_JPR_COMPONENT_USER_FIELDS
};
struct jsonsl_jpr_st {
@ -675,6 +782,10 @@ struct jsonsl_jpr_st {
struct jsonsl_jpr_component_st *components;
size_t ncomponents;
/**Type of the match to be expected. If nonzero, will be compared against
* the actual type */
unsigned match_type;
/** Base of allocated string for components */
char *basestr;
@ -683,8 +794,6 @@ struct jsonsl_jpr_st {
size_t norig;
};
/**
* Create a new JPR object.
*
@ -723,10 +832,42 @@ void jsonsl_jpr_destroy(jsonsl_jpr_t jpr);
*/
JSONSL_API
jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr,
jsonsl_type_t parent_type,
unsigned int parent_type,
unsigned int parent_level,
const char *key, size_t nkey);
/**
* Alternate matching algorithm. This matching algorithm does not use
* JSONPointer but relies on a more structured searching mechanism. It
* assumes that there is a clear distinction between array indices and
* object keys. In this case, the jsonsl_path_component_st::ptype should
* be set to @ref JSONSL_PATH_NUMERIC for an array index (the
* jsonsl_path_comonent_st::is_arridx field will be removed in a future
* version).
*
* @param jpr The path
* @param parent The parent structure. Can be NULL if this is the root object
* @param child The child structure. Should not be NULL
* @param key Object key, if an object
* @param nkey Length of object key
* @return Status constant if successful
*
* @note
* For successful matching, both the key and the path itself should be normalized
* to contain 'proper' utf8 sequences rather than utf16 '\uXXXX' escapes. This
* should currently be done in the application. Another version of this function
* may use a temporary buffer in such circumstances (allocated by the application).
*
* Since this function also checks the state of the child, it should only
* be called on PUSH callbacks, and not POP callbacks
*/
JSONSL_API
jsonsl_jpr_match_t
jsonsl_path_match(jsonsl_jpr_t jpr,
const struct jsonsl_state_st *parent,
const struct jsonsl_state_st *child,
const char *key, size_t nkey);
/**
* Associate a set of JPR objects with a lexer instance.
@ -804,6 +945,13 @@ const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match);
* to escape a '/' - however this may also be desired behavior. the JSON
* spec is not clear on this, and therefore jsonsl leaves it up to you.
*
* Additionally, sometimes you may wish to _normalize_ JSON. This is specifically
* true when dealing with 'u-escapes' which can be expressed perfectly fine
* as utf8. One use case for normalization is JPR string comparison, in which
* case two effectively equivalent strings may not match because one is using
* u-escapes and the other proper utf8. To normalize u-escapes only, pass in
* an empty `toEscape` table, enabling only the `u` index.
*
* @param in The input string.
* @param out An allocated output (should be the same size as in)
* @param len the size of the buffer
@ -820,13 +968,26 @@ const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match);
* encountered.
*
* @return The effective size of the output buffer.
*
* @note
* This function now encodes the UTF8 equivalents of utf16 escapes (i.e.
* 'u-escapes'). Previously this would encode the escapes as utf16 literals,
* which while still correct in some sense was confusing for many (especially
* considering that the inputs were variations of char).
*
* @note
* The output buffer will never be larger than the input buffer, since
* standard escape sequences (i.e. '\t') occupy two bytes in the source
* but only one byte (when unescaped) in the output. Likewise u-escapes
* (i.e. \uXXXX) will occupy six bytes in the source, but at the most
* two bytes when escaped.
*/
JSONSL_API
size_t jsonsl_util_unescape_ex(const char *in,
char *out,
size_t len,
const int toEscape[128],
jsonsl_special_t *oflags,
unsigned *oflags,
jsonsl_error_t *err,
const char **errat);
@ -838,44 +999,6 @@ size_t jsonsl_util_unescape_ex(const char *in,
#endif /* JSONSL_NO_JPR */
/**
* HERE BE CHARACTER TABLES!
*/
#define JSONSL_CHARTABLE_string_nopass \
/* 0x00 */ 1 /* <NUL> */, /* 0x00 */ \
/* 0x01 */ 1 /* <SOH> */, /* 0x01 */ \
/* 0x02 */ 1 /* <STX> */, /* 0x02 */ \
/* 0x03 */ 1 /* <ETX> */, /* 0x03 */ \
/* 0x04 */ 1 /* <EOT> */, /* 0x04 */ \
/* 0x05 */ 1 /* <ENQ> */, /* 0x05 */ \
/* 0x06 */ 1 /* <ACK> */, /* 0x06 */ \
/* 0x07 */ 1 /* <BEL> */, /* 0x07 */ \
/* 0x08 */ 1 /* <BS> */, /* 0x08 */ \
/* 0x09 */ 1 /* <HT> */, /* 0x09 */ \
/* 0x0a */ 1 /* <LF> */, /* 0x0a */ \
/* 0x0b */ 1 /* <VT> */, /* 0x0b */ \
/* 0x0c */ 1 /* <FF> */, /* 0x0c */ \
/* 0x0d */ 1 /* <CR> */, /* 0x0d */ \
/* 0x0e */ 1 /* <SO> */, /* 0x0e */ \
/* 0x0f */ 1 /* <SI> */, /* 0x0f */ \
/* 0x10 */ 1 /* <DLE> */, /* 0x10 */ \
/* 0x11 */ 1 /* <DC1> */, /* 0x11 */ \
/* 0x12 */ 1 /* <DC2> */, /* 0x12 */ \
/* 0x13 */ 1 /* <DC3> */, /* 0x13 */ \
/* 0x14 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x21 */ \
/* 0x22 */ 1 /* <"> */, /* 0x22 */ \
/* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x42 */ \
/* 0x43 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ \
/* 0x5c */ 1 /* <\> */, /* 0x5c */ \
/* 0x5d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7c */ \
/* 0x7d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9c */ \
/* 0x9d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbc */ \
/* 0xbd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdc */ \
/* 0xdd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfc */ \
/* 0xfd */ 0,0 /* 0xfe */ \
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -176,33 +176,11 @@ void tr_netSetTOS(tr_socket_t s, int tos, tr_address_type type)
else if (type == TR_AF_INET6)
{
#if defined(IPV6_TCLASS) && !defined(_WIN32)
int dscp = 0;
switch (tos)
{
case 0x10:
dscp = 0x20; /* lowcost (CS1) */
break;
case 0x08:
dscp = 0x28; /* throughput (AF11) */
break;
case 0x04:
dscp = 0x04; /* reliability */
break;
case 0x02:
dscp = 0x30; /* low delay (AF12) */
break;
}
if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, (void const*)&dscp, sizeof(dscp)) == -1)
if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, (void const*)&tos, sizeof(tos)) == -1)
{
char err_buf[512];
tr_net_strerror(err_buf, sizeof(err_buf), sockerrno);
tr_logAddNamedInfo("Net", "Can't set IPv6 QoS '%d': %s", dscp, err_buf);
tr_logAddNamedInfo("Net", "Can't set IPv6 QoS '%d': %s", tos, err_buf);
}
#else
@ -610,11 +588,11 @@ fail:
}
/* We all hate NATs. */
static int global_unicast_address(struct sockaddr* sa)
static int global_unicast_address(struct sockaddr_storage* ss)
{
if (sa->sa_family == AF_INET)
if (ss->ss_family == AF_INET)
{
unsigned char const* a = (unsigned char*)&((struct sockaddr_in*)sa)->sin_addr;
unsigned char const* a = (unsigned char*)&((struct sockaddr_in*)ss)->sin_addr;
if (a[0] == 0 || a[0] == 127 || a[0] >= 224 || a[0] == 10 || (a[0] == 172 && a[1] >= 16 && a[1] <= 31) ||
(a[0] == 192 && a[1] == 168))
@ -624,9 +602,9 @@ static int global_unicast_address(struct sockaddr* sa)
return 1;
}
else if (sa->sa_family == AF_INET6)
else if (ss->ss_family == AF_INET6)
{
unsigned char const* a = (unsigned char*)&((struct sockaddr_in6*)sa)->sin6_addr;
unsigned char const* a = (unsigned char*)&((struct sockaddr_in6*)ss)->sin6_addr;
/* 2000::/3 */
return (a[0] & 0xE0) == 0x20 ? 1 : 0;
}
@ -680,7 +658,7 @@ static int tr_globalAddress(int af, void* addr, int* addr_len)
return -1;
}
if (global_unicast_address((struct sockaddr*)&ss) == 0)
if (global_unicast_address(&ss) == 0)
{
return -1;
}

View File

@ -118,6 +118,15 @@ static inline bool tr_address_is_valid(tr_address const* a)
* Sockets
**********************************************************************/
/* https://en.wikipedia.org/wiki/Differentiated_services#Class_Selector */
enum
{
TR_IPTOS_LOWCOST = 0x38, /* AF13: low prio, high drop */
TR_IPTOS_LOWDELAY = 0x70, /* AF32: high prio, mid drop */
TR_IPTOS_THRUPUT = 0x20, /* CS1: low prio, undef drop */
TR_IPTOS_RELIABLE = 0x28 /* AF11: low prio, low drop */
};
struct tr_session;
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);

View File

@ -1766,7 +1766,7 @@ static void peerCallbackFunc(tr_peer* peer, tr_peer_event const* e, void* vs)
tor->uploadedCur += e->length;
tr_announcerAddBytes(tor, TR_ANN_UP, e->length);
tr_torrentSetActivityDate(tor, now);
tr_torrentSetDateActive(tor, now);
tr_torrentSetDirty(tor);
tr_statsAddUploaded(tor->session, e->length);
@ -1784,7 +1784,7 @@ static void peerCallbackFunc(tr_peer* peer, tr_peer_event const* e, void* vs)
tr_torrent* tor = s->tor;
tor->downloadedCur += e->length;
tr_torrentSetActivityDate(tor, now);
tr_torrentSetDateActive(tor, now);
tr_torrentSetDirty(tor);
tr_statsAddDownloaded(tor->session, e->length);

View File

@ -90,7 +90,7 @@ bool tr_amInThread(tr_thread const* t)
#ifdef _WIN32
#define ThreadFuncReturnType unsigned WINAPI
#else
#define ThreadFuncReturnType void
#define ThreadFuncReturnType void*
#endif
static ThreadFuncReturnType ThreadFunc(void* _t)
@ -108,6 +108,8 @@ static ThreadFuncReturnType ThreadFunc(void* _t)
#ifdef _WIN32
_endthreadex(0);
return 0;
#else
return NULL;
#endif
}

View File

@ -105,6 +105,7 @@ static struct tr_key_struct const my_static[] =
Q("dropped"),
Q("dropped6"),
Q("e"),
Q("editDate"),
Q("encoding"),
Q("encryption"),
Q("error"),
@ -125,6 +126,7 @@ static struct tr_key_struct const my_static[] =
Q("filter-trackers"),
Q("flagStr"),
Q("flags"),
Q("format"),
Q("fromCache"),
Q("fromDht"),
Q("fromIncoming"),

View File

@ -105,6 +105,7 @@ enum
TR_KEY_dropped,
TR_KEY_dropped6,
TR_KEY_e,
TR_KEY_editDate,
TR_KEY_encoding,
TR_KEY_encryption,
TR_KEY_error,
@ -125,6 +126,7 @@ enum
TR_KEY_filter_trackers,
TR_KEY_flagStr,
TR_KEY_flags,
TR_KEY_format,
TR_KEY_fromCache,
TR_KEY_fromDht,
TR_KEY_fromIncoming,

View File

@ -914,7 +914,7 @@ static uint64_t loadFromFile(tr_torrent* tor, uint64_t fieldsToLoad, bool* didRe
if ((fieldsToLoad & TR_FR_ACTIVITY_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_activity_date, &i))
{
tr_torrentSetActivityDate(tor, i);
tr_torrentSetDateActive(tor, i);
fieldsLoaded |= TR_FR_ACTIVITY_DATE;
}

View File

@ -29,6 +29,7 @@
#include "stats.h"
#include "torrent.h"
#include "tr-assert.h"
#include "tr-macros.h"
#include "utils.h"
#include "variant.h"
#include "version.h"
@ -45,6 +46,13 @@
#define dbgmsg(...) tr_logAddDeepNamed("RPC", __VA_ARGS__)
#endif
typedef enum
{
TR_FORMAT_OBJECT = 0,
TR_FORMAT_TABLE
}
tr_format;
/***
****
***/
@ -532,183 +540,189 @@ static void addPeers(tr_torrent* tor, tr_variant* list)
tr_torrentPeersFree(peers, peerCount);
}
static void addField(tr_torrent* const tor, tr_info const* const inf, tr_stat const* const st, tr_variant* const d,
tr_quark const key)
static void initField(tr_torrent* const tor, tr_info const* const inf, tr_stat const* const st, tr_variant* const initme,
tr_quark key)
{
char* str;
switch (key)
{
case TR_KEY_activityDate:
tr_variantDictAddInt(d, key, st->activityDate);
tr_variantInitInt(initme, st->activityDate);
break;
case TR_KEY_addedDate:
tr_variantDictAddInt(d, key, st->addedDate);
tr_variantInitInt(initme, st->addedDate);
break;
case TR_KEY_bandwidthPriority:
tr_variantDictAddInt(d, key, tr_torrentGetPriority(tor));
tr_variantInitInt(initme, tr_torrentGetPriority(tor));
break;
case TR_KEY_comment:
tr_variantDictAddStr(d, key, inf->comment != NULL ? inf->comment : "");
tr_variantInitStr(initme, inf->comment != NULL ? inf->comment : "", TR_BAD_SIZE);
break;
case TR_KEY_corruptEver:
tr_variantDictAddInt(d, key, st->corruptEver);
tr_variantInitInt(initme, st->corruptEver);
break;
case TR_KEY_creator:
tr_variantDictAddStr(d, key, inf->creator != NULL ? inf->creator : "");
tr_variantInitStr(initme, inf->creator != NULL ? inf->creator : "", TR_BAD_SIZE);
break;
case TR_KEY_dateCreated:
tr_variantDictAddInt(d, key, inf->dateCreated);
tr_variantInitInt(initme, inf->dateCreated);
break;
case TR_KEY_desiredAvailable:
tr_variantDictAddInt(d, key, st->desiredAvailable);
tr_variantInitInt(initme, st->desiredAvailable);
break;
case TR_KEY_doneDate:
tr_variantDictAddInt(d, key, st->doneDate);
tr_variantInitInt(initme, st->doneDate);
break;
case TR_KEY_downloadDir:
tr_variantDictAddStr(d, key, tr_torrentGetDownloadDir(tor));
tr_variantInitStr(initme, tr_torrentGetDownloadDir(tor), TR_BAD_SIZE);
break;
case TR_KEY_downloadedEver:
tr_variantDictAddInt(d, key, st->downloadedEver);
tr_variantInitInt(initme, st->downloadedEver);
break;
case TR_KEY_downloadLimit:
tr_variantDictAddInt(d, key, tr_torrentGetSpeedLimit_KBps(tor, TR_DOWN));
tr_variantInitInt(initme, tr_torrentGetSpeedLimit_KBps(tor, TR_DOWN));
break;
case TR_KEY_downloadLimited:
tr_variantDictAddBool(d, key, tr_torrentUsesSpeedLimit(tor, TR_DOWN));
tr_variantInitBool(initme, tr_torrentUsesSpeedLimit(tor, TR_DOWN));
break;
case TR_KEY_error:
tr_variantDictAddInt(d, key, st->error);
tr_variantInitInt(initme, st->error);
break;
case TR_KEY_errorString:
tr_variantDictAddStr(d, key, st->errorString);
tr_variantInitStr(initme, st->errorString, TR_BAD_SIZE);
break;
case TR_KEY_eta:
tr_variantDictAddInt(d, key, st->eta);
tr_variantInitInt(initme, st->eta);
break;
case TR_KEY_files:
addFiles(tor, tr_variantDictAddList(d, key, inf->fileCount));
tr_variantInitList(initme, inf->fileCount);
addFiles(tor, initme);
break;
case TR_KEY_fileStats:
addFileStats(tor, tr_variantDictAddList(d, key, inf->fileCount));
tr_variantInitList(initme, inf->fileCount);
addFileStats(tor, initme);
break;
case TR_KEY_hashString:
tr_variantDictAddStr(d, key, tor->info.hashString);
tr_variantInitStr(initme, tor->info.hashString, TR_BAD_SIZE);
break;
case TR_KEY_haveUnchecked:
tr_variantDictAddInt(d, key, st->haveUnchecked);
tr_variantInitInt(initme, st->haveUnchecked);
break;
case TR_KEY_haveValid:
tr_variantDictAddInt(d, key, st->haveValid);
tr_variantInitInt(initme, st->haveValid);
break;
case TR_KEY_honorsSessionLimits:
tr_variantDictAddBool(d, key, tr_torrentUsesSessionLimits(tor));
tr_variantInitBool(initme, tr_torrentUsesSessionLimits(tor));
break;
case TR_KEY_id:
tr_variantDictAddInt(d, key, st->id);
tr_variantInitInt(initme, st->id);
break;
case TR_KEY_editDate:
tr_variantInitInt(initme, st->editDate);
break;
case TR_KEY_isFinished:
tr_variantDictAddBool(d, key, st->finished);
tr_variantInitBool(initme, st->finished);
break;
case TR_KEY_isPrivate:
tr_variantDictAddBool(d, key, tr_torrentIsPrivate(tor));
tr_variantInitBool(initme, tr_torrentIsPrivate(tor));
break;
case TR_KEY_isStalled:
tr_variantDictAddBool(d, key, st->isStalled);
tr_variantInitBool(initme, st->isStalled);
break;
case TR_KEY_labels:
addLabels(tor, tr_variantDictAdd(d, key));
addLabels(tor, initme);
break;
case TR_KEY_leftUntilDone:
tr_variantDictAddInt(d, key, st->leftUntilDone);
tr_variantInitInt(initme, st->leftUntilDone);
break;
case TR_KEY_manualAnnounceTime:
tr_variantDictAddInt(d, key, st->manualAnnounceTime);
tr_variantInitInt(initme, st->manualAnnounceTime);
break;
case TR_KEY_maxConnectedPeers:
tr_variantDictAddInt(d, key, tr_torrentGetPeerLimit(tor));
tr_variantInitInt(initme, tr_torrentGetPeerLimit(tor));
break;
case TR_KEY_magnetLink:
str = tr_torrentGetMagnetLink(tor);
tr_variantDictAddStr(d, key, str);
tr_variantInitStr(initme, str, TR_BAD_SIZE);
tr_free(str);
break;
case TR_KEY_metadataPercentComplete:
tr_variantDictAddReal(d, key, st->metadataPercentComplete);
tr_variantInitReal(initme, st->metadataPercentComplete);
break;
case TR_KEY_name:
tr_variantDictAddStr(d, key, tr_torrentName(tor));
tr_variantInitStr(initme, tr_torrentName(tor), TR_BAD_SIZE);
break;
case TR_KEY_percentDone:
tr_variantDictAddReal(d, key, st->percentDone);
tr_variantInitReal(initme, st->percentDone);
break;
case TR_KEY_peer_limit:
tr_variantDictAddInt(d, key, tr_torrentGetPeerLimit(tor));
tr_variantInitInt(initme, tr_torrentGetPeerLimit(tor));
break;
case TR_KEY_peers:
addPeers(tor, tr_variantDictAdd(d, key));
addPeers(tor, initme);
break;
case TR_KEY_peersConnected:
tr_variantDictAddInt(d, key, st->peersConnected);
tr_variantInitInt(initme, st->peersConnected);
break;
case TR_KEY_peersFrom:
{
tr_variant* tmp = tr_variantDictAddDict(d, key, 7);
tr_variantInitDict(initme, 7);
int const* f = st->peersFrom;
tr_variantDictAddInt(tmp, TR_KEY_fromCache, f[TR_PEER_FROM_RESUME]);
tr_variantDictAddInt(tmp, TR_KEY_fromDht, f[TR_PEER_FROM_DHT]);
tr_variantDictAddInt(tmp, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
tr_variantDictAddInt(tmp, TR_KEY_fromLpd, f[TR_PEER_FROM_LPD]);
tr_variantDictAddInt(tmp, TR_KEY_fromLtep, f[TR_PEER_FROM_LTEP]);
tr_variantDictAddInt(tmp, TR_KEY_fromPex, f[TR_PEER_FROM_PEX]);
tr_variantDictAddInt(tmp, TR_KEY_fromTracker, f[TR_PEER_FROM_TRACKER]);
tr_variantDictAddInt(initme, TR_KEY_fromCache, f[TR_PEER_FROM_RESUME]);
tr_variantDictAddInt(initme, TR_KEY_fromDht, f[TR_PEER_FROM_DHT]);
tr_variantDictAddInt(initme, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
tr_variantDictAddInt(initme, TR_KEY_fromLpd, f[TR_PEER_FROM_LPD]);
tr_variantDictAddInt(initme, TR_KEY_fromLtep, f[TR_PEER_FROM_LTEP]);
tr_variantDictAddInt(initme, TR_KEY_fromPex, f[TR_PEER_FROM_PEX]);
tr_variantDictAddInt(initme, TR_KEY_fromTracker, f[TR_PEER_FROM_TRACKER]);
break;
}
case TR_KEY_peersGettingFromUs:
tr_variantDictAddInt(d, key, st->peersGettingFromUs);
tr_variantInitInt(initme, st->peersGettingFromUs);
break;
case TR_KEY_peersSendingToUs:
tr_variantDictAddInt(d, key, st->peersSendingToUs);
tr_variantInitInt(initme, st->peersSendingToUs);
break;
case TR_KEY_pieces:
@ -717,148 +731,146 @@ static void addField(tr_torrent* const tor, tr_info const* const inf, tr_stat co
size_t byte_count = 0;
void* bytes = tr_torrentCreatePieceBitfield(tor, &byte_count);
char* str = tr_base64_encode(bytes, byte_count, NULL);
tr_variantDictAddStr(d, key, str != NULL ? str : "");
tr_variantInitStr(initme, str != NULL ? str : "", TR_BAD_SIZE);
tr_free(str);
tr_free(bytes);
}
else
{
tr_variantDictAddStr(d, key, "");
tr_variantInitStr(initme, "", 0);
}
break;
case TR_KEY_pieceCount:
tr_variantDictAddInt(d, key, inf->pieceCount);
tr_variantInitInt(initme, inf->pieceCount);
break;
case TR_KEY_pieceSize:
tr_variantDictAddInt(d, key, inf->pieceSize);
tr_variantInitInt(initme, inf->pieceSize);
break;
case TR_KEY_priorities:
tr_variantInitList(initme, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variant* p = tr_variantDictAddList(d, key, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variantListAddInt(p, inf->files[i].priority);
}
break;
tr_variantListAddInt(initme, inf->files[i].priority);
}
break;
case TR_KEY_queuePosition:
tr_variantDictAddInt(d, key, st->queuePosition);
tr_variantInitInt(initme, st->queuePosition);
break;
case TR_KEY_etaIdle:
tr_variantDictAddInt(d, key, st->etaIdle);
tr_variantInitInt(initme, st->etaIdle);
break;
case TR_KEY_rateDownload:
tr_variantDictAddInt(d, key, toSpeedBytes(st->pieceDownloadSpeed_KBps));
tr_variantInitInt(initme, toSpeedBytes(st->pieceDownloadSpeed_KBps));
break;
case TR_KEY_rateUpload:
tr_variantDictAddInt(d, key, toSpeedBytes(st->pieceUploadSpeed_KBps));
tr_variantInitInt(initme, toSpeedBytes(st->pieceUploadSpeed_KBps));
break;
case TR_KEY_recheckProgress:
tr_variantDictAddReal(d, key, st->recheckProgress);
tr_variantInitReal(initme, st->recheckProgress);
break;
case TR_KEY_seedIdleLimit:
tr_variantDictAddInt(d, key, tr_torrentGetIdleLimit(tor));
tr_variantInitInt(initme, tr_torrentGetIdleLimit(tor));
break;
case TR_KEY_seedIdleMode:
tr_variantDictAddInt(d, key, tr_torrentGetIdleMode(tor));
tr_variantInitInt(initme, tr_torrentGetIdleMode(tor));
break;
case TR_KEY_seedRatioLimit:
tr_variantDictAddReal(d, key, tr_torrentGetRatioLimit(tor));
tr_variantInitReal(initme, tr_torrentGetRatioLimit(tor));
break;
case TR_KEY_seedRatioMode:
tr_variantDictAddInt(d, key, tr_torrentGetRatioMode(tor));
tr_variantInitInt(initme, tr_torrentGetRatioMode(tor));
break;
case TR_KEY_sizeWhenDone:
tr_variantDictAddInt(d, key, st->sizeWhenDone);
tr_variantInitInt(initme, st->sizeWhenDone);
break;
case TR_KEY_startDate:
tr_variantDictAddInt(d, key, st->startDate);
tr_variantInitInt(initme, st->startDate);
break;
case TR_KEY_status:
tr_variantDictAddInt(d, key, st->activity);
tr_variantInitInt(initme, st->activity);
break;
case TR_KEY_secondsDownloading:
tr_variantDictAddInt(d, key, st->secondsDownloading);
tr_variantInitInt(initme, st->secondsDownloading);
break;
case TR_KEY_secondsSeeding:
tr_variantDictAddInt(d, key, st->secondsSeeding);
tr_variantInitInt(initme, st->secondsSeeding);
break;
case TR_KEY_trackers:
addTrackers(inf, tr_variantDictAddList(d, key, inf->trackerCount));
tr_variantInitList(initme, inf->trackerCount);
addTrackers(inf, initme);
break;
case TR_KEY_trackerStats:
{
int n;
tr_tracker_stat* s = tr_torrentTrackers(tor, &n);
addTrackerStats(s, n, tr_variantDictAddList(d, key, n));
tr_variantInitList(initme, n);
addTrackerStats(s, n, initme);
tr_torrentTrackersFree(s, n);
break;
}
case TR_KEY_torrentFile:
tr_variantDictAddStr(d, key, inf->torrent);
tr_variantInitStr(initme, inf->torrent, TR_BAD_SIZE);
break;
case TR_KEY_totalSize:
tr_variantDictAddInt(d, key, inf->totalSize);
tr_variantInitInt(initme, inf->totalSize);
break;
case TR_KEY_uploadedEver:
tr_variantDictAddInt(d, key, st->uploadedEver);
tr_variantInitInt(initme, st->uploadedEver);
break;
case TR_KEY_uploadLimit:
tr_variantDictAddInt(d, key, tr_torrentGetSpeedLimit_KBps(tor, TR_UP));
tr_variantInitInt(initme, tr_torrentGetSpeedLimit_KBps(tor, TR_UP));
break;
case TR_KEY_uploadLimited:
tr_variantDictAddBool(d, key, tr_torrentUsesSpeedLimit(tor, TR_UP));
tr_variantInitBool(initme, tr_torrentUsesSpeedLimit(tor, TR_UP));
break;
case TR_KEY_uploadRatio:
tr_variantDictAddReal(d, key, st->ratio);
tr_variantInitReal(initme, st->ratio);
break;
case TR_KEY_wanted:
tr_variantInitList(initme, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variant* w = tr_variantDictAddList(d, key, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variantListAddInt(w, inf->files[i].dnd ? 0 : 1);
}
break;
tr_variantListAddInt(initme, inf->files[i].dnd ? 0 : 1);
}
break;
case TR_KEY_webseeds:
addWebseeds(inf, tr_variantDictAddList(d, key, inf->webseedCount));
tr_variantInitList(initme, inf->webseedCount);
addWebseeds(inf, initme);
break;
case TR_KEY_webseedsSendingToUs:
tr_variantDictAddInt(d, key, st->webseedsSendingToUs);
tr_variantInitInt(initme, st->webseedsSendingToUs);
break;
default:
@ -866,26 +878,29 @@ static void addField(tr_torrent* const tor, tr_info const* const inf, tr_stat co
}
}
static void addInfo(tr_torrent* tor, tr_variant* d, tr_variant* fields)
static void addTorrentInfo(tr_torrent* tor, tr_format format, tr_variant* entry, tr_quark const* fields, size_t fieldCount)
{
int const n = tr_variantListSize(fields);
if (format == TR_FORMAT_TABLE)
{
tr_variantInitList(entry, fieldCount);
}
else
{
tr_variantInitDict(entry, fieldCount);
}
tr_variantInitDict(d, n);
if (n > 0)
if (fieldCount > 0)
{
tr_info const* const inf = tr_torrentInfo(tor);
tr_stat const* const st = tr_torrentStat((tr_torrent*)tor);
for (int i = 0; i < n; ++i)
for (size_t i = 0; i < fieldCount; ++i)
{
size_t len;
char const* str;
tr_variant* child = format == TR_FORMAT_TABLE ?
tr_variantListAdd(entry) :
tr_variantDictAdd(entry, fields[i]);
if (tr_variantGetStr(tr_variantListChild(fields, i), &str, &len))
{
addField(tor, inf, st, d, tr_quark_new(str, len));
}
initField(tor, inf, st, child, fields[i]);
}
}
}
@ -897,10 +912,20 @@ static char const* torrentGet(tr_session* session, tr_variant* args_in, tr_varia
int torrentCount;
tr_torrent** torrents = getTorrents(session, args_in, &torrentCount);
tr_variant* list = tr_variantDictAddList(args_out, TR_KEY_torrents, torrentCount);
tr_variant* list = tr_variantDictAddList(args_out, TR_KEY_torrents, torrentCount + 1);
tr_variant* fields;
char const* strVal;
char const* errmsg = NULL;
tr_format format;
if (tr_variantDictFindStr(args_in, TR_KEY_format, &strVal, NULL) && strcmp(strVal, "table") == 0)
{
format = TR_FORMAT_TABLE;
}
else /* default value */
{
format = TR_FORMAT_OBJECT;
}
if (tr_variantDictFindStr(args_in, TR_KEY_ids, &strVal, NULL) && strcmp(strVal, "recently-active") == 0)
{
@ -932,10 +957,35 @@ static char const* torrentGet(tr_session* session, tr_variant* args_in, tr_varia
}
else
{
/* make an array of property name quarks */
size_t keyCount = 0;
size_t const n = tr_variantListSize(fields);
tr_quark* keys = tr_new(tr_quark, n);
for (size_t i = 0; i < n; ++i)
{
size_t len;
if (tr_variantGetStr(tr_variantListChild(fields, i), &strVal, &len))
{
keys[keyCount++] = tr_quark_new(strVal, len);
}
}
if (format == TR_FORMAT_TABLE)
{
/* first entry is an array of property names */
tr_variant* names = tr_variantListAddList(list, keyCount);
for (size_t i = 0; i < keyCount; ++i)
{
tr_variantListAddQuark(names, keys[i]);
}
}
for (int i = 0; i < torrentCount; ++i)
{
addInfo(torrents[i], tr_variantListAdd(list), fields);
addTorrentInfo(torrents[i], format, tr_variantListAdd(list), keys, keyCount);
}
tr_free(keys);
}
tr_free(torrents);
@ -1704,19 +1754,20 @@ static void addTorrentImpl(struct tr_rpc_idle_data* data, tr_ctor* ctor)
if (tor != NULL && key != 0)
{
tr_variant fields;
tr_variantInitList(&fields, 3);
tr_variantListAddStr(&fields, "id");
tr_variantListAddStr(&fields, "name");
tr_variantListAddStr(&fields, "hashString");
addInfo(tor, tr_variantDictAdd(data->args_out, key), &fields);
tr_quark const fields[] =
{
TR_KEY_id,
TR_KEY_name,
TR_KEY_hashString
};
addTorrentInfo(tor, TR_FORMAT_OBJECT, tr_variantDictAdd(data->args_out, key), fields, TR_N_ELEMENTS(fields));
if (result == NULL)
{
notify(data->session, TR_RPC_TORRENT_ADDED, tor);
}
tr_variantFree(&fields);
result = NULL;
}

View File

@ -265,27 +265,27 @@ static int parse_tos(char const* str)
if (evutil_ascii_strcasecmp(str, "lowcost") == 0)
{
return 0x10;
return TR_IPTOS_LOWCOST;
}
if (evutil_ascii_strcasecmp(str, "mincost") == 0)
{
return 0x10;
return TR_IPTOS_LOWCOST;
}
if (evutil_ascii_strcasecmp(str, "throughput") == 0)
{
return 0x08;
return TR_IPTOS_THRUPUT;
}
if (evutil_ascii_strcasecmp(str, "reliability") == 0)
{
return 0x04;
return TR_IPTOS_RELIABLE;
}
if (evutil_ascii_strcasecmp(str, "lowdelay") == 0)
{
return 0x02;
return TR_IPTOS_LOWDELAY;
}
value = strtol(str, &p, 0);
@ -307,16 +307,16 @@ static char const* format_tos(int value)
case 0:
return "default";
case 0x10:
case TR_IPTOS_LOWCOST:
return "lowcost";
case 0x08:
case TR_IPTOS_THRUPUT:
return "throughput";
case 0x04:
case TR_IPTOS_RELIABLE:
return "reliability";
case 0x02:
case TR_IPTOS_LOWDELAY:
return "lowdelay";
default:

View File

@ -340,6 +340,7 @@ void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, in
tor->isStopping = true;
tor->magnetVerify = true;
tor->startAfterVerify = true;
tr_torrentMarkEdited(tor);
}
else /* drat. */
{

View File

@ -968,7 +968,7 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
TR_ASSERT(tor->downloadedCur == 0);
TR_ASSERT(tor->uploadedCur == 0);
tr_torrentSetAddedDate(tor, tr_time()); /* this is a default value to be overwritten by the resume file */
tr_torrentSetDateAdded(tor, tr_time()); /* this is a default value to be overwritten by the resume file */
torrentInitFromInfo(tor);
@ -1191,6 +1191,8 @@ void tr_torrentSetDownloadDir(tr_torrent* tor, char const* path)
{
tr_free(tor->downloadDir);
tor->downloadDir = tr_strdup(path);
tr_torrentMarkEdited(tor);
tr_torrentSetDirty(tor);
}
@ -1406,6 +1408,7 @@ tr_stat const* tr_torrentStat(tr_torrent* tor)
s->activityDate = tor->activityDate;
s->addedDate = tor->addedDate;
s->doneDate = tor->doneDate;
s->editDate = tor->editDate;
s->startDate = tor->startDate;
s->secondsSeeding = tor->secondsSeeding;
s->secondsDownloading = tor->secondsDownloading;
@ -2862,6 +2865,7 @@ bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_
tor->info.trackerCount = tmpInfo.trackerCount;
tmpInfo.trackers = swap.trackers;
tmpInfo.trackerCount = swap.trackerCount;
tr_torrentMarkEdited(tor);
tr_metainfoFree(&tmpInfo);
tr_variantToFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
@ -2905,7 +2909,14 @@ bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_
***
**/
void tr_torrentSetAddedDate(tr_torrent* tor, time_t t)
#define BACK_COMPAT_FUNC(oldname, newname) \
void oldname(tr_torrent * tor, time_t t) { newname(tor, t); }
BACK_COMPAT_FUNC(tr_torrentSetAddedDate, tr_torrentSetDateAdded)
BACK_COMPAT_FUNC(tr_torrentSetActivityDate, tr_torrentSetDateActive)
BACK_COMPAT_FUNC(tr_torrentSetDoneDate, tr_torrentSetDateDone)
#undef BACK_COMPAT_FUNC
void tr_torrentSetDateAdded(tr_torrent* tor, time_t t)
{
TR_ASSERT(tr_isTorrent(tor));
@ -2913,7 +2924,7 @@ void tr_torrentSetAddedDate(tr_torrent* tor, time_t t)
tor->anyDate = MAX(tor->anyDate, tor->addedDate);
}
void tr_torrentSetActivityDate(tr_torrent* tor, time_t t)
void tr_torrentSetDateActive(tr_torrent* tor, time_t t)
{
TR_ASSERT(tr_isTorrent(tor));
@ -2921,7 +2932,7 @@ void tr_torrentSetActivityDate(tr_torrent* tor, time_t t)
tor->anyDate = MAX(tor->anyDate, tor->activityDate);
}
void tr_torrentSetDoneDate(tr_torrent* tor, time_t t)
void tr_torrentSetDateDone(tr_torrent* tor, time_t t)
{
TR_ASSERT(tr_isTorrent(tor));
@ -3998,6 +4009,7 @@ static void torrentRenamePath(void* vdata)
tor->info.name = tr_strdup(newname);
}
tr_torrentMarkEdited(tor);
tr_torrentSetDirty(tor);
}
}

View File

@ -87,6 +87,12 @@ void tr_torrentSave(tr_torrent* tor);
void tr_torrentSetLocalError(tr_torrent* tor, char const* fmt, ...) TR_GNUC_PRINTF(2, 3);
void tr_torrentSetDateAdded(tr_torrent* torrent, time_t addedDate);
void tr_torrentSetDateActive(tr_torrent* torrent, time_t activityDate);
void tr_torrentSetDateDone(tr_torrent* torrent, time_t doneDate);
typedef enum
{
TR_VERIFY_NONE,
@ -185,11 +191,12 @@ struct tr_torrent
uint64_t etaULSpeedCalculatedAt;
unsigned int etaULSpeed_Bps;
time_t addedDate;
time_t activityDate;
time_t doneDate;
time_t startDate;
time_t addedDate;
time_t anyDate;
time_t doneDate;
time_t editDate;
time_t startDate;
int secondsDownloading;
int secondsSeeding;
@ -343,6 +350,14 @@ static inline void tr_torrentSetDirty(tr_torrent* tor)
tor->isDirty = true;
}
/* note that the torrent's tr_info just changed */
static inline void tr_torrentMarkEdited(tr_torrent* tor)
{
TR_ASSERT(tr_isTorrent(tor));
tor->editDate = tr_time();
}
uint32_t tr_getBlockSize(uint32_t pieceSize);
/**

View File

@ -125,6 +125,12 @@
#define TR_GNUC_MALLOC
#endif
#if __has_attribute(__fallthrough__) || TR_GNUC_CHECK_VERSION(7, 0)
#define TR_GNUC_FALLTHROUGH __attribute__((__fallthrough__))
#else
#define TR_GNUC_FALLTHROUGH
#endif
/***
****
***/
@ -159,6 +165,9 @@
****
***/
/* Only use this macro to suppress false-positive alignment warnings */
#define TR_DISCARD_ALIGN(ptr, type) ((type)(void*)(ptr))
#define SHA_DIGEST_LENGTH 20
#define TR_INET6_ADDRSTRLEN 46

View File

@ -1828,6 +1828,12 @@ typedef struct tr_stat
/** The last time we uploaded or downloaded piece data on this torrent. */
time_t activityDate;
/** The last time during this session that a rarely-changing field
changed -- e.g. any tr_info field (trackers, filenames, name)
or download directory. RPC clients can monitor this to know when
to reload fields that rarely change. */
time_t editDate;
/** Number of seconds since the last activity (or since started).
-1 if activity is not seeding or downloading. */
int idleSecs;
@ -1862,13 +1868,16 @@ tr_stat const* tr_torrentStat(tr_torrent* torrent);
reduce the CPU load if you're calling tr_torrentStat() frequently. */
tr_stat const* tr_torrentStatCached(tr_torrent* torrent);
/** @deprecated */
/** @deprecated because this should only be accessible to libtransmission.
private code, use tr_torentSetDateAdded() instead */
TR_DEPRECATED void tr_torrentSetAddedDate(tr_torrent* torrent, time_t addedDate);
/** @deprecated */
/** @deprecated because this should only be accessible to libtransmission.
private code, use tr_torentSetDateActive() instead */
TR_DEPRECATED void tr_torrentSetActivityDate(tr_torrent* torrent, time_t activityDate);
/** @deprecated */
/** @deprecated because this should only be accessible to libtransmission.
private code, use tr_torentSetDateDone() instead */
TR_DEPRECATED void tr_torrentSetDoneDate(tr_torrent* torrent, time_t doneDate);
/** @} */

View File

@ -425,6 +425,8 @@ static int test_truncd(void)
return 0;
}
static char* test_strdup_printf_valist(char const* fmt, ...) TR_GNUC_PRINTF(1, 2);
static char* test_strdup_printf_valist(char const* fmt, ...)
{
va_list args;

View File

@ -1686,7 +1686,7 @@ bool tr_moveFile(char const* oldpath, char const* newpath, tr_error** error)
char* buf = NULL;
tr_sys_path_info info;
uint64_t bytesLeft;
size_t const buflen = 1024 * 128; /* 128 KiB buffer */
size_t const buflen = 1024 * 1024; /* 1024 KiB buffer */
/* make sure the old file exists */
if (!tr_sys_path_get_info(oldpath, 0, &info, error))

View File

@ -225,8 +225,8 @@ void tr_quickfindFirstK(void* base, size_t nmemb, size_t size, tr_voidptr_compar
* @brief sprintf() a string into a newly-allocated buffer large enough to hold it
* @return a newly-allocated string that can be freed with tr_free()
*/
char* tr_strdup_printf(char const* fmt, ...) TR_GNUC_PRINTF(1, 2) TR_GNUC_MALLOC;
char* tr_strdup_vprintf(char const* fmt, va_list args) TR_GNUC_MALLOC;
char* tr_strdup_printf(char const* fmt, ...) TR_GNUC_MALLOC TR_GNUC_PRINTF(1, 2);
char* tr_strdup_vprintf(char const* fmt, va_list args) TR_GNUC_MALLOC TR_GNUC_PRINTF(1, 0);
/** @brief Portability wrapper for strlcpy() that uses the system implementation if available */
size_t tr_strlcpy(char* dst, void const* src, size_t siz);

View File

@ -45,6 +45,12 @@ struct json_wrapper_data
struct evbuffer* strbuf;
char const* source;
tr_ptrArray stack;
/* A very common pattern is for a container's children to be similar,
* e.g. they may all be objects with the same set of keys. So when
* a container is popped off the stack, remember its size to use as
* a preallocation heuristic for the next container at that depth. */
size_t preallocGuess[MAX_DEPTH];
};
static tr_variant* get_node(struct jsonsl_st* jsn)
@ -103,25 +109,22 @@ static void action_callback_PUSH(jsonsl_t jsn, jsonsl_action_t action UNUSED, st
tr_variant* node;
struct json_wrapper_data* data = jsn->data;
switch (state->type)
if ((state->type == JSONSL_T_LIST) || (state->type == JSONSL_T_OBJECT))
{
case JSONSL_T_LIST:
data->has_content = true;
node = get_node(jsn);
tr_variantInitList(node, 0);
tr_ptrArrayAppend(&data->stack, node);
break;
case JSONSL_T_OBJECT:
data->has_content = true;
node = get_node(jsn);
tr_variantInitDict(node, 0);
tr_ptrArrayAppend(&data->stack, node);
break;
default:
/* nothing else interesting on push */
break;
int const depth = tr_ptrArraySize(&data->stack);
size_t const n = depth < MAX_DEPTH ? data->preallocGuess[depth] : 0;
if (state->type == JSONSL_T_LIST)
{
tr_variantInitList(node, n);
}
else
{
tr_variantInitDict(node, n);
}
}
}
@ -318,7 +321,12 @@ static void action_callback_POP(jsonsl_t jsn, jsonsl_action_t action UNUSED, str
}
else if (state->type == JSONSL_T_LIST || state->type == JSONSL_T_OBJECT)
{
tr_ptrArrayPop(&data->stack);
int const depth = tr_ptrArraySize(&data->stack);
tr_variant const* v = tr_ptrArrayPop(&data->stack);
if (depth < MAX_DEPTH)
{
data->preallocGuess[depth] = v->val.l.count;
}
}
else if (state->type == JSONSL_T_SPECIAL)
{
@ -369,6 +377,10 @@ int tr_jsonParse(char const* source, void const* vbuf, size_t len, tr_variant* s
data.source = source;
data.keybuf = evbuffer_new();
data.strbuf = evbuffer_new();
for (int i = 0; i < MAX_DEPTH; ++i)
{
data.preallocGuess[i] = 0;
}
/* parse it */
jsonsl_feed(jsn, vbuf, len);

View File

@ -67,7 +67,9 @@ static void tr_watchdir_inotify_on_event(struct bufferevent* event, void* contex
TR_ASSERT(context != NULL);
tr_watchdir_t const handle = context;
#ifdef TR_ENABLE_ASSERTS
tr_watchdir_inotify* const backend = BACKEND_UPCAST(tr_watchdir_get_backend(handle));
#endif
struct inotify_event ev;
size_t nread;
size_t name_size = NAME_MAX + 1;

View File

@ -72,7 +72,7 @@
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2005-2018 The Transmission Project</string>
<string>Copyright © 2005-2020 The Transmission Project</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@ -72,7 +72,7 @@
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2005-2019 The Transmission Project</string>
<string>Copyright © 2005-2020 The Transmission Project</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@ -48,8 +48,9 @@
NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setAlignment: NSRightTextAlignment];
fAttributes = [[NSDictionary alloc] initWithObjects: @[[NSFont systemFontOfSize: 11.0], paragraphStyle]
forKeys: @[NSFontAttributeName, NSParagraphStyleAttributeName]];
fAttributes = @{NSFontAttributeName: [NSFont systemFontOfSize: 11.0],
NSForegroundColorAttributeName: NSColor.labelColor,
NSParagraphStyleAttributeName: paragraphStyle};
}
[[NSString percentString: [self floatValue] longDecimals: NO] drawInRect: cellFrame withAttributes: fAttributes];

View File

@ -21,6 +21,7 @@
*****************************************************************************/
#import "ProgressGradients.h"
#import "NSApplicationAdditions.h"
@implementation ProgressGradients (Private)
@ -45,52 +46,92 @@
+ (NSGradient *) progressWhiteGradient
{
return [[self class] progressGradientForRed: 0.95 green: 0.95 blue: 0.95];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.1 green: 0.1 blue: 0.1];
} else {
return [[self class] progressGradientForRed: 0.95 green: 0.95 blue: 0.95];
}
}
+ (NSGradient *) progressGrayGradient
{
return [[self class] progressGradientForRed: 0.7 green: 0.7 blue: 0.7];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.35 green: 0.35 blue: 0.35];
} else {
return [[self class] progressGradientForRed: 0.7 green: 0.7 blue: 0.7];
}
}
+ (NSGradient *) progressLightGrayGradient
{
return [[self class] progressGradientForRed: 0.87 green: 0.87 blue: 0.87];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.2 green: 0.2 blue: 0.2];
} else {
return [[self class] progressGradientForRed: 0.87 green: 0.87 blue: 0.87];
}
}
+ (NSGradient *) progressBlueGradient
{
return [[self class] progressGradientForRed: 0.35 green: 0.67 blue: 0.98];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.35 * 2.0/3.0 green: 0.67 * 2.0/3.0 blue: 0.98 * 2.0/3.0];
} else {
return [[self class] progressGradientForRed: 0.35 green: 0.67 blue: 0.98];
}
}
+ (NSGradient *) progressDarkBlueGradient
{
return [[self class] progressGradientForRed: 0.616 green: 0.722 blue: 0.776];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.616 * 2.0/3.0 green: 0.722 * 2.0/3.0 blue: 0.776 * 2.0/3.0];
} else {
return [[self class] progressGradientForRed: 0.616 green: 0.722 blue: 0.776];
}
}
+ (NSGradient *) progressGreenGradient
{
return [[self class] progressGradientForRed: 0.44 green: 0.89 blue: 0.40];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.44 * 2.0/3.0 green: 0.89 * 2.0/3.0 blue: 0.40 * 2.0/3.0];
} else {
return [[self class] progressGradientForRed: 0.44 green: 0.89 blue: 0.40];
}
}
+ (NSGradient *) progressLightGreenGradient
{
return [[self class] progressGradientForRed: 0.62 green: 0.99 blue: 0.58];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.62 * 3.0/4.0 green: 0.99 * 3.0/4.0 blue: 0.58 * 3.0/4.0];
} else {
return [[self class] progressGradientForRed: 0.62 green: 0.99 blue: 0.58];
}
}
+ (NSGradient *) progressDarkGreenGradient
{
return [[self class] progressGradientForRed: 0.627 green: 0.714 blue: 0.639];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.627 * 2.0/3.0 green: 0.714 * 2.0/3.0 blue: 0.639 * 2.0/3.0];
} else {
return [[self class] progressGradientForRed: 0.627 green: 0.714 blue: 0.639];
}
}
+ (NSGradient *) progressRedGradient
{
return [[self class] progressGradientForRed: 0.902 green: 0.439 blue: 0.451];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.902 * 2.0/3.0 green: 0.439 * 2.0/3.0 blue: 0.451 * 2.0/3.0];
} else {
return [[self class] progressGradientForRed: 0.902 green: 0.439 blue: 0.451];
}
}
+ (NSGradient *) progressYellowGradient
{
return [[self class] progressGradientForRed: 0.933 green: 0.890 blue: 0.243];
if ([NSApp isDarkMode]) {
return [[self class] progressGradientForRed: 0.933 * 0.8 green: 0.890 * 0.8 blue: 0.243 * 0.8];
} else {
return [[self class] progressGradientForRed: 0.933 green: 0.890 blue: 0.243];
}
}
@end

View File

@ -20,6 +20,7 @@
SU_EXPORT @interface SUAppcastItem : NSObject
@property (copy, readonly) NSString *title;
@property (copy, readonly) NSString *dateString;
@property (copy, readonly) NSDate *date;
@property (copy, readonly) NSString *itemDescription;
@property (strong, readonly) NSURL *releaseNotesURL;
@property (strong, readonly) SUSignatures *signatures;
@ -32,6 +33,7 @@ SU_EXPORT @interface SUAppcastItem : NSObject
@property (copy, readonly) NSString *displayVersionString;
@property (copy, readonly) NSDictionary *deltaUpdates;
@property (strong, readonly) NSURL *infoURL;
@property (copy, readonly) NSNumber* phasedRolloutInterval;
// Initializes with data from a dictionary provided by the RSS class.
- (instancetype)initWithDictionary:(NSDictionary *)dict;

View File

@ -9,7 +9,11 @@
#ifndef SUCODESIGNINGVERIFIER_H
#define SUCODESIGNINGVERIFIER_H
#if __has_feature(modules)
@import Foundation;
#else
#import <Foundation/Foundation.h>
#endif
#import "SUExport.h"
SU_EXPORT @interface SUCodeSigningVerifier : NSObject

View File

@ -29,6 +29,7 @@ typedef NS_ENUM(OSStatus, SUError) {
SUNoUpdateError = 1001,
SUAppcastError = 1002,
SURunningFromDiskImageError = 1003,
SURunningTranslocated = 1004,
// Download phase errors.
SUTemporaryDirectoryError = 2000,

View File

@ -189,6 +189,8 @@ SU_EXPORT @interface SUUpdater : NSObject
For UI-less/daemon apps that aren't usually quit, instead of this function,
you can use the delegate method
SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation:
or
SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationBlock:
to immediately start installation when an update was found.
A progress dialog is shown but the user will never be prompted to read the

View File

@ -117,6 +117,23 @@ SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey;
*/
- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item;
/*!
Called just before the scheduled update driver prompts the user to install an update.
\param updater The SUUpdater instance.
\return YES to allow the update prompt to be shown (the default behavior), or NO to suppress it.
*/
- (BOOL)updaterShouldShowUpdateAlertForScheduledUpdate:(SUUpdater *)updater forItem:(SUAppcastItem *)item;
/*!
Called after the user dismisses the update alert.
\param updater The SUUpdater instance.
\param permanently YES if the alert will not appear again for this update; NO if it may reappear.
*/
- (void)updater:(SUUpdater *)updater didDismissUpdateAlertPermanently:(BOOL)permanently forItem:(SUAppcastItem *)item;
/*!
Called when a valid update is not found.
@ -124,6 +141,13 @@ SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey;
*/
- (void)updaterDidNotFindUpdate:(SUUpdater *)updater;
/*!
Called when the user clicks the Skip This Version button.
\param updater The SUUpdater instance.
*/
- (void)updater:(SUUpdater *)updater userDidSkipThisVersion:(SUAppcastItem *)item;
/*!
Called immediately before downloading the specified update.
@ -195,6 +219,21 @@ SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey;
*/
- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation;
/*!
Returns whether the relaunch should be delayed in order to perform other tasks.
This is not called if the user didn't relaunch on the previous update,
in that case it will immediately restart.
This method acts as a simpler alternative to SUUpdaterDelegate::updater:shouldPostponeRelaunchForUpdate:untilInvoking: avoiding usage of NSInvocation, which is not available in Swift environments.
\param updater The SUUpdater instance.
\param item The appcast item corresponding to the update that is proposed to be installed.
\return \c YES to delay the relaunch.
*/
- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item;
/*!
Returns whether the application should be relaunched at all.
@ -280,6 +319,18 @@ SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey;
*/
- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation;
/*!
Called when an update is scheduled to be silently installed on quit.
This is after an update has been automatically downloaded in the background.
(i.e. SUUpdater::automaticallyDownloadsUpdates is YES)
This method acts as a more modern alternative to SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: using a block instead of NSInvocation, which is not available in Swift environments.
\param updater The SUUpdater instance.
\param item The appcast item corresponding to the update that is proposed to be installed.
\param installationBlock Can be used to trigger an immediate silent install and relaunch.
*/
- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationBlock:(void (^)(void))installationBlock;
/*!
Calls after an update that was scheduled to be silently installed on quit has been canceled.

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>18C54</string>
<string>19E224g</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.21.2 12-ga5b23cdbe</string>
<string>1.23.0 34-g962878b84</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleSupportedPlatforms</key>
@ -25,21 +25,21 @@
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1.21.2</string>
<string>1.23.0</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>10B61</string>
<string>11B44</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>18B71</string>
<string>19B68e</string>
<key>DTSDKName</key>
<string>macosx10.14</string>
<string>macosx10.15</string>
<key>DTXcode</key>
<string>1010</string>
<string>1120</string>
<key>DTXcodeBuild</key>
<string>10B61</string>
<string>11B44</string>
<key>LSBackgroundOnly</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>

View File

@ -0,0 +1,860 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
4McwRDEss5BzWwUMG2Xf93+ze08=
</data>
<key>Resources/SUStatus.nib</key>
<data>
+3Tn7V2+cRw0Aw2VoWT1DNAPpkE=
</data>
<key>Resources/ar.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
Rf4jjdgTqvfw5JO/6f9jHMURv/U=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ca.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
wGGx+QzPg/20zZTq7jwCTgf/Ubc=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/cs.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
OjCfTDR+NbVLvirUPiJKJF6UiS0=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/da.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
0t7SuLDMBZVsY240PAEsVfH/1qw=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/de.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
fsC7FJvExHE/2681tuUrjkSF2+A=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/el.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
NbIN+TRHORCL5Gfj68VRq4KdPXo=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/en.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
i3TNbMzmKR52wTyfnD/bkZ12elE=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/es.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
QPG88BN+x/l2Qk1NLLe3wRa26mQ=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/fi.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
yd6pIoSj19HMDIUos4Td1Fch7bs=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/fr.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
X3URilwJPVqMTGbtrYdorODwrMA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/he.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
U2WmlYGYmeeIlSW66R8awwmNXIE=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/hr.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
vyI2nHyZlhhSXXrQGoflg8oB9Ig=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/hu.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
bNEmsO2LyUsMjTESH1I42V9sAOo=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/is.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
8fxzD9ZhrvIZVZB1+QSJaPzg80M=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/it.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
bk1J6vpZjWeUFhBYWuWZf8TDv1A=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ja.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
f4EbR/GfMsKeWJ5DN/vhwg/lUoE=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ko.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
FRHRQPCWEk9GdJawYTuccg+E2tA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/nb.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
sgrDElwUxXtzdw8WaUFWyK3pG9Y=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/nl.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
PWbC08zHFLROqivY2MAklDh6gkA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/pl.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
o7deBXE2Ct8/vQxouej5KkwTcUA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/pt_BR.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
/adUv04OXQkCFv+Oed6qktFVQ3E=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/pt_PT.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
Mji9loJOJvuDY9hz3FhQ4H+HY5E=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ro.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
9U+OTz29kXKZHY/nmvbtemMsB3g=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ru.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
VpSLGNvZ6sbRYsF23L8m6TG+P6E=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/sk.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
qn/mo2EFOyw6keezS64Wo5ZGZXU=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/sl.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
kwvdisufBenuQzrVg8tYKTX+qgg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/sv.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
98/sk+A2Ew1fmKpuKZ3rq8eS1EM=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/th.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
HQwGW1Ebf0i+Bl4synks3x2SY2M=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/tr.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
whUQco5F2wcYdjc+cPKlk+mtx7Q=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/uk.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
JXhpqvLkX0yDWjbWgsk2wbSObKU=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/zh_CN.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
0vdFowZuJ1qLE3rDgG3BZ9SlNRw=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/zh_TW.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
1FLKoM5jZ8JGBG/nmyEIA+/aalA=
</data>
<key>optional</key>
<true/>
</dict>
</dict>
<key>files2</key>
<dict>
<key>MacOS/fileop</key>
<dict>
<key>cdhash</key>
<data>
m93UAGlWG1VExsGEVOlzraeDVJU=
</data>
<key>requirement</key>
<string>cdhash H"034b541bc0ce637d7c6cbab430b6ae5bf60b18d2" or cdhash H"9bddd40069561b5544c6c18454e973ada7835495"</string>
</dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash</key>
<data>
4McwRDEss5BzWwUMG2Xf93+ze08=
</data>
<key>hash2</key>
<data>
nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE=
</data>
</dict>
<key>Resources/SUStatus.nib</key>
<dict>
<key>hash</key>
<data>
+3Tn7V2+cRw0Aw2VoWT1DNAPpkE=
</data>
<key>hash2</key>
<data>
5qy5Hmt7D8O4gK4CkA7/lWikR3cX9ZSe6yTyluAeOh8=
</data>
</dict>
<key>Resources/ar.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
Rf4jjdgTqvfw5JO/6f9jHMURv/U=
</data>
<key>hash2</key>
<data>
2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ca.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
wGGx+QzPg/20zZTq7jwCTgf/Ubc=
</data>
<key>hash2</key>
<data>
om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/cs.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
OjCfTDR+NbVLvirUPiJKJF6UiS0=
</data>
<key>hash2</key>
<data>
06z1nY8VfM4M1SdHCc1jqewmP5Ue0g3mPDcNbeDVNIM=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/da.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
0t7SuLDMBZVsY240PAEsVfH/1qw=
</data>
<key>hash2</key>
<data>
wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/de.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
fsC7FJvExHE/2681tuUrjkSF2+A=
</data>
<key>hash2</key>
<data>
XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/el.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
NbIN+TRHORCL5Gfj68VRq4KdPXo=
</data>
<key>hash2</key>
<data>
wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/en.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
i3TNbMzmKR52wTyfnD/bkZ12elE=
</data>
<key>hash2</key>
<data>
Xl/5yA/K9T7cscvPi/4/lWUtjJlIvO+esCF4SRaguz4=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/es.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
QPG88BN+x/l2Qk1NLLe3wRa26mQ=
</data>
<key>hash2</key>
<data>
mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/fi.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
yd6pIoSj19HMDIUos4Td1Fch7bs=
</data>
<key>hash2</key>
<data>
+AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/fr.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
X3URilwJPVqMTGbtrYdorODwrMA=
</data>
<key>hash2</key>
<data>
fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/he.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
U2WmlYGYmeeIlSW66R8awwmNXIE=
</data>
<key>hash2</key>
<data>
4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/hr.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
vyI2nHyZlhhSXXrQGoflg8oB9Ig=
</data>
<key>hash2</key>
<data>
I1njQLVyH2bFsJecQOLDAB9N8q8niH7u+hqzxRqqerA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/hu.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
bNEmsO2LyUsMjTESH1I42V9sAOo=
</data>
<key>hash2</key>
<data>
sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/is.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
8fxzD9ZhrvIZVZB1+QSJaPzg80M=
</data>
<key>hash2</key>
<data>
xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/it.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
bk1J6vpZjWeUFhBYWuWZf8TDv1A=
</data>
<key>hash2</key>
<data>
Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ja.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
f4EbR/GfMsKeWJ5DN/vhwg/lUoE=
</data>
<key>hash2</key>
<data>
dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ko.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
FRHRQPCWEk9GdJawYTuccg+E2tA=
</data>
<key>hash2</key>
<data>
+bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/nb.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
sgrDElwUxXtzdw8WaUFWyK3pG9Y=
</data>
<key>hash2</key>
<data>
FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/nl.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
PWbC08zHFLROqivY2MAklDh6gkA=
</data>
<key>hash2</key>
<data>
xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/pl.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
o7deBXE2Ct8/vQxouej5KkwTcUA=
</data>
<key>hash2</key>
<data>
pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/pt_BR.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
/adUv04OXQkCFv+Oed6qktFVQ3E=
</data>
<key>hash2</key>
<data>
lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/pt_PT.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
Mji9loJOJvuDY9hz3FhQ4H+HY5E=
</data>
<key>hash2</key>
<data>
RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ro.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
9U+OTz29kXKZHY/nmvbtemMsB3g=
</data>
<key>hash2</key>
<data>
NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/ru.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
VpSLGNvZ6sbRYsF23L8m6TG+P6E=
</data>
<key>hash2</key>
<data>
wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/sk.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
qn/mo2EFOyw6keezS64Wo5ZGZXU=
</data>
<key>hash2</key>
<data>
e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/sl.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
kwvdisufBenuQzrVg8tYKTX+qgg=
</data>
<key>hash2</key>
<data>
t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/sv.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
98/sk+A2Ew1fmKpuKZ3rq8eS1EM=
</data>
<key>hash2</key>
<data>
mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/th.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
HQwGW1Ebf0i+Bl4synks3x2SY2M=
</data>
<key>hash2</key>
<data>
nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/tr.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
whUQco5F2wcYdjc+cPKlk+mtx7Q=
</data>
<key>hash2</key>
<data>
xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/uk.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
JXhpqvLkX0yDWjbWgsk2wbSObKU=
</data>
<key>hash2</key>
<data>
u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/zh_CN.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
0vdFowZuJ1qLE3rDgG3BZ9SlNRw=
</data>
<key>hash2</key>
<data>
ecJXF6vvj1f80iCr+Gk52cTyumQrea6H0NpOjwbbLiE=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/zh_TW.lproj/Sparkle.strings</key>
<dict>
<key>hash</key>
<data>
1FLKoM5jZ8JGBG/nmyEIA+/aalA=
</data>
<key>hash2</key>
<data>
Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ=
</data>
<key>optional</key>
<true/>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

Some files were not shown because too many files have changed in this diff Show More