diff options
author | Jan-Michael Brummer <jan.brummer@tabos.org> | 2020-05-29 23:04:17 +0200 |
---|---|---|
committer | Patrick Griffis <pgriffis@igalia.com> | 2020-06-26 10:08:04 -0700 |
commit | 5bdb5caed2f729a6a1844cc3020c4d4ed278db62 (patch) | |
tree | b33a54d5072b22a518c7727d1a0a948762d60a79 | |
parent | 4c6b7d1550a299e1afe1db217759a67e9806d62b (diff) | |
download | libsoup-5bdb5caed2f729a6a1844cc3020c4d4ed278db62.tar.gz |
soup-directory-input-stream: Redesign directory listing layout
- Fixes encoding issues on file names
- Adds sorting support
- Adds translations
- Add CSS for nicer design
Closes !123
-rw-r--r-- | libsoup/directory.css | 70 | ||||
-rw-r--r-- | libsoup/directory.js | 76 | ||||
-rw-r--r-- | libsoup/meson.build | 7 | ||||
-rw-r--r-- | libsoup/soup-directory-input-stream.c | 72 | ||||
-rw-r--r-- | libsoup/soup.gresource.xml | 7 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
6 files changed, 223 insertions, 10 deletions
diff --git a/libsoup/directory.css b/libsoup/directory.css new file mode 100644 index 00000000..82e86516 --- /dev/null +++ b/libsoup/directory.css @@ -0,0 +1,70 @@ +:root { + background-color: #f6f5f4; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +table { + width: 100%; + table-layout: fixed; + margin: 0 auto; +} + +th > a { + color: inherit; +} + +table[order] > thead > tr > th::after { + display: none; + width: .8em; + margin-inline-end: -.8em; + text-align: end; +} + +table[order="asc"] > thead > tr > th::after { + content: "\2193"; +} +table[order="desc"] > thead > tr > th::after { + content: "\2191"; +} + +table[order][order-by="0"] > thead > tr > th:first-child > a , +table[order][order-by="1"] > thead > tr > th:first-child + th > a , +table[order][order-by="2"] > thead > tr > th:first-child + th + th > a { + text-decoration: underline; +} + +table[order][order-by="0"] > thead > tr > th:first-child::after , +table[order][order-by="1"] > thead > tr > th:first-child + th::after , +table[order][order-by="2"] > thead > tr > th:first-child + th + th::after { + display: inline-block; +} + +td:first-child { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +tr:nth-child(even) { + background-color: #f2f2f2; +} + +@media (min-width: 550px) { + body { + padding: 3em; + background-color: white; + border-radius: 1em; + border: 2px solid rgba(211, 215, 207); + max-width: 65em; + margin: 2em auto; + } +} + diff --git a/libsoup/directory.js b/libsoup/directory.js new file mode 100644 index 00000000..b1380bd8 --- /dev/null +++ b/libsoup/directory.js @@ -0,0 +1,76 @@ + +/* This code is based on Firefox directory listing, distributed under + * Mozilla Public License. + */ + +'use strict'; + +var gTable, gOrderBy, gTBody, gRows; + +document.addEventListener("DOMContentLoaded", function() { + gTable = document.getElementsByTagName("table")[0]; + gTBody = gTable.tBodies[0]; + if (gTBody.rows.length < 2) + return; + var headCells = gTable.tHead.rows[0].cells; + + function rowAction(i) { + return function(event) { + event.preventDefault(); + orderBy(i); + } + } + + for (var i = headCells.length - 1; i >= 0; i--) { + var anchor = document.createElement("a"); + anchor.href = ""; + anchor.appendChild(headCells[i].firstChild); + headCells[i].appendChild(anchor); + headCells[i].addEventListener("click", rowAction(i), true); + } + gTable.setAttribute("order", ""); + orderBy(0); +}, "false"); + +function compareRows(rowA, rowB) { + var a = rowA.cells[gOrderBy].getAttribute("sortable-data") || rowA.cells[gOrderBy].innerHTML; + var b = rowB.cells[gOrderBy].getAttribute("sortable-data") || rowB.cells[gOrderBy].innerHTML; + var intA = +a; + var intB = +b; + if (a == intA && b == intB) { + a = intA; + b = intB; + } else { + a = a.toLowerCase(); + b = b.toLowerCase(); + } + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +function orderBy(column) { + if (!gRows) + gRows = Array.from(gTBody.rows); + var order; + if (gOrderBy == column) { + order = gTable.getAttribute("order") == "asc" ? "desc" : "asc"; + } else { + order = "asc"; + gOrderBy = column; + gTable.setAttribute("order-by", column); + gRows.sort(compareRows); + } + gTable.removeChild(gTBody); + gTable.setAttribute("order", order); + if (order == "asc") + for (var i = 0; i < gRows.length; i++) + gTBody.appendChild(gRows[i]); + else + for (var i = gRows.length - 1; i >= 0; i--) + gTBody.appendChild(gRows[i]); + gTable.appendChild(gTBody); +} + diff --git a/libsoup/meson.build b/libsoup/meson.build index 73bb1188..613d68a4 100644 --- a/libsoup/meson.build +++ b/libsoup/meson.build @@ -1,6 +1,13 @@ pkg = import('pkgconfig') +resources = gnome.compile_resources('soup-resources', + 'soup.gresource.xml', + c_name: 'soup', + source_dir: '.' +) + soup_sources = [ + resources, 'soup-address.c', 'soup-auth.c', 'soup-auth-basic.c', diff --git a/libsoup/soup-directory-input-stream.c b/libsoup/soup-directory-input-stream.c index e0ccfd90..43bc1d9f 100644 --- a/libsoup/soup-directory-input-stream.c +++ b/libsoup/soup-directory-input-stream.c @@ -25,11 +25,12 @@ #include <string.h> +#include <glib/gi18n-lib.h> + #include "soup-directory-input-stream.h" #include "soup.h" -#define INIT_STRING "<html>\n<body>\n<table><th align=\"left\">Name</th><th>Size</th><th>Date Modified</th>\n" -#define ROW_FORMAT "<td><a href=\"%s\">%s</a></td><td align=\"right\">%s</td><td align=\"right\" margin=8>%s</td>\n" +#define ROW_FORMAT "<td sortable-data=\"%s\"><a href=\"%s\">%s</a></td><td align=\"right\" sortable-data=\"%ld\">%s</td><td align=\"right\" sortable-data=\"%ld\">%s %s</td>\n" #define EXIT_STRING "</table>\n</html>\n" G_DEFINE_TYPE (SoupDirectoryInputStream, soup_directory_input_stream, G_TYPE_INPUT_STREAM) @@ -41,7 +42,9 @@ soup_directory_input_stream_parse_info (SoupDirectoryInputStream *stream, SoupBuffer *buffer; GString *string; const char *file_name; - char *escaped, *path, *xml_string, *size, *time; + char *escaped, *path, *xml_string, *size, *date, *time, *name; + goffset raw_size; + gint64 timestamp; #if !GLIB_CHECK_VERSION (2, 61, 2) GTimeVal modified; #endif @@ -62,21 +65,35 @@ soup_directory_input_stream_parse_info (SoupDirectoryInputStream *stream, xml_string = g_markup_escape_text (file_name, -1); escaped = g_uri_escape_string (file_name, NULL, FALSE); path = g_strconcat (stream->uri, G_DIR_SEPARATOR_S, escaped, NULL); - size = g_format_size (g_file_info_get_size (info)); + raw_size = g_file_info_get_size (info); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) + size = g_format_size (raw_size); + else + size = g_strdup(""); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + name = g_strdup_printf("1.%s", path); + else + name = g_strdup_printf("%s", path); + #if GLIB_CHECK_VERSION (2, 61, 2) modification_time = g_file_info_get_modification_date_time (info); #else g_file_info_get_modification_time (info, &modified); modification_time = g_date_time_new_from_timeval_local (&modified); #endif - time = g_date_time_format (modification_time, "%X %x"); + time = g_date_time_format (modification_time, "%X"); + date = g_date_time_format (modification_time, "%x"); + timestamp = g_date_time_to_unix (modification_time); g_date_time_unref (modification_time); - g_string_append_printf (string, ROW_FORMAT, path, xml_string, size, time); + g_string_append_printf (string, ROW_FORMAT, name, path, xml_string, raw_size, size, timestamp, time, date); g_string_append (string, "</tr>\n"); buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len); g_free (time); + g_free (date); g_free (escaped); g_free (size); g_free (path); @@ -190,12 +207,46 @@ soup_directory_input_stream_class_init (SoupDirectoryInputStreamClass *stream_cl inputstream_class->close_fn = soup_directory_input_stream_close; } +static +char *soup_directory_input_stream_create_header (SoupDirectoryInputStream *stream) +{ + char *header; + GBytes *css = g_resources_lookup_data ("/org/gnome/libsoup/directory.css", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); + GBytes *js = g_resources_lookup_data ("/org/gnome/libsoup/directory.js", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); + + header = g_strdup_printf ("<html><head>" \ + "<title>%s</title>" \ + "<meta http-equiv=\"Content-Type\" content=\"text/html;\" charset=\"UTF-8\">" \ + "<style>%s</style>" \ + "<script>%s</script>" \ + "</head>" \ + "<body>" \ + "<table>" \ + "<thead>" \ + "<th align=\"left\">%s</th><th align=\"right\">%s</th><th align=\"right\">%s</th>" \ + "</thead>", + stream->uri, + css ? (gchar *)g_bytes_get_data (css, NULL) : "", + js ? (gchar *)g_bytes_get_data (js, NULL) : "", + _("Name"), + _("Size"), + _("Date Modified")); + return header; +} + static void soup_directory_input_stream_init (SoupDirectoryInputStream *stream) { - stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC, - INIT_STRING, - sizeof (INIT_STRING)); +} + +static void +soup_directory_input_stream_setup_buffer (SoupDirectoryInputStream *stream) +{ + char *init = soup_directory_input_stream_create_header (stream); + + stream->buffer = soup_buffer_new (SOUP_MEMORY_TAKE, + init, + strlen (init)); } GInputStream * @@ -212,6 +263,7 @@ soup_directory_input_stream_new (GFileEnumerator *enumerator, SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator); SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE); + soup_directory_input_stream_setup_buffer (SOUP_DIRECTORY_INPUT_STREAM (stream)); + return stream; } - diff --git a/libsoup/soup.gresource.xml b/libsoup/soup.gresource.xml new file mode 100644 index 00000000..da3ad10b --- /dev/null +++ b/libsoup/soup.gresource.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/org/gnome/libsoup"> + <file>directory.css</file> + <file>directory.js</file> + </gresource> +</gresources> diff --git a/po/POTFILES.in b/po/POTFILES.in index 3cd20bf4..61d52750 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,5 +1,6 @@ libsoup/soup-body-input-stream.c libsoup/soup-cache-input-stream.c +libsoup/soup-directory-input-stream.c libsoup/soup-converter-wrapper.c libsoup/soup-message-client-io.c libsoup/soup-message-io.c |