Introduce Midori.SpeedDial and unit test
Fixes: https://bugs.launchpad.net/midori/+bug/1038634
This commit is contained in:
parent
287fc53da9
commit
db0ae6ab60
6 changed files with 312 additions and 276 deletions
|
@ -1578,92 +1578,6 @@ signal_handler (int signal_id)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static GKeyFile*
|
|
||||||
speeddial_new_from_file (const gchar* config,
|
|
||||||
GError** error)
|
|
||||||
{
|
|
||||||
|
|
||||||
GKeyFile* key_file = g_key_file_new ();
|
|
||||||
gchar* config_file = g_build_filename (config, "speeddial", NULL);
|
|
||||||
guint i = 0;
|
|
||||||
gchar* json_content;
|
|
||||||
gsize json_length;
|
|
||||||
GString* script;
|
|
||||||
JSGlobalContextRef js_context;
|
|
||||||
gchar* keyfile;
|
|
||||||
gchar* thumb_dir;
|
|
||||||
gchar** tiles;
|
|
||||||
|
|
||||||
if (g_key_file_load_from_file (key_file, config_file, G_KEY_FILE_NONE, error))
|
|
||||||
{
|
|
||||||
g_free (config_file);
|
|
||||||
return key_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
katze_assign (config_file, g_build_filename (config, "speeddial.json", NULL));
|
|
||||||
if (!g_file_get_contents (config_file, &json_content, &json_length, NULL))
|
|
||||||
{
|
|
||||||
katze_assign (json_content, g_strdup ("'{}'"));
|
|
||||||
json_length = strlen ("'{}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
script = g_string_sized_new (json_length);
|
|
||||||
g_string_append (script, "var json = JSON.parse (");
|
|
||||||
g_string_append_len (script, json_content, json_length);
|
|
||||||
g_string_append (script, "); "
|
|
||||||
"var keyfile = '';"
|
|
||||||
"for (var i in json['shortcuts']) {"
|
|
||||||
"var tile = json['shortcuts'][i];"
|
|
||||||
"keyfile += '[Dial ' + tile['id'].substring (1) + ']\\n'"
|
|
||||||
" + 'uri=' + tile['href'] + '\\n'"
|
|
||||||
" + 'img=' + tile['img'] + '\\n'"
|
|
||||||
" + 'title=' + tile['title'] + '\\n\\n';"
|
|
||||||
"} "
|
|
||||||
"var columns = json['width'] ? json['width'] : 3;"
|
|
||||||
"var rows = json['shortcuts'] ? json['shortcuts'].length / columns : 0;"
|
|
||||||
"keyfile += '[settings]\\n'"
|
|
||||||
" + 'columns=' + columns + '\\n'"
|
|
||||||
" + 'rows=' + (rows > 3 ? rows : 3) + '\\n\\n';"
|
|
||||||
"keyfile;");
|
|
||||||
g_free (json_content);
|
|
||||||
js_context = JSGlobalContextCreateInGroup (NULL, NULL);
|
|
||||||
keyfile = sokoke_js_script_eval (js_context, script->str, NULL);
|
|
||||||
JSGlobalContextRelease (js_context);
|
|
||||||
g_string_free (script, TRUE);
|
|
||||||
g_key_file_load_from_data (key_file, keyfile, -1, 0, NULL);
|
|
||||||
g_free (keyfile);
|
|
||||||
tiles = g_key_file_get_groups (key_file, NULL);
|
|
||||||
thumb_dir = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_cache_dir (), "thumbnails", NULL);
|
|
||||||
if (!g_file_test (thumb_dir, G_FILE_TEST_EXISTS))
|
|
||||||
katze_mkdir_with_parents (thumb_dir, 0700);
|
|
||||||
g_free (thumb_dir);
|
|
||||||
|
|
||||||
while (tiles[i] != NULL)
|
|
||||||
{
|
|
||||||
gsize sz;
|
|
||||||
gchar* uri = g_key_file_get_string (key_file, tiles[i], "uri", NULL);
|
|
||||||
gchar* img = g_key_file_get_string (key_file, tiles[i], "img", NULL);
|
|
||||||
if (img != NULL && (uri && *uri && *uri != '#'))
|
|
||||||
{
|
|
||||||
guchar* decoded = g_base64_decode (img, &sz);
|
|
||||||
gchar* thumb_path = sokoke_build_thumbnail_path (uri);
|
|
||||||
g_file_set_contents (thumb_path, (gchar*)decoded, sz, NULL);
|
|
||||||
g_free (thumb_path);
|
|
||||||
g_free (decoded);
|
|
||||||
}
|
|
||||||
g_free (img);
|
|
||||||
g_free (uri);
|
|
||||||
g_key_file_remove_key (key_file, tiles[i], "img", NULL);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
g_strfreev (tiles);
|
|
||||||
|
|
||||||
katze_assign (config_file, g_build_filename (config, "speeddial", NULL));
|
|
||||||
sokoke_key_file_save_to_file (key_file, config_file, NULL);
|
|
||||||
g_free (config_file);
|
|
||||||
return key_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
midori_soup_session_block_uris_cb (SoupSession* session,
|
midori_soup_session_block_uris_cb (SoupSession* session,
|
||||||
SoupMessage* msg,
|
SoupMessage* msg,
|
||||||
|
@ -1970,7 +1884,7 @@ main (int argc,
|
||||||
gchar** extensions;
|
gchar** extensions;
|
||||||
MidoriWebSettings* settings;
|
MidoriWebSettings* settings;
|
||||||
gchar* config_file;
|
gchar* config_file;
|
||||||
GKeyFile* speeddial;
|
MidoriSpeedDial* dial;
|
||||||
MidoriStartup load_on_startup;
|
MidoriStartup load_on_startup;
|
||||||
KatzeArray* search_engines;
|
KatzeArray* search_engines;
|
||||||
KatzeArray* bookmarks;
|
KatzeArray* bookmarks;
|
||||||
|
@ -2476,8 +2390,8 @@ main (int argc,
|
||||||
}
|
}
|
||||||
midori_startup_timer ("History read: \t%f");
|
midori_startup_timer ("History read: \t%f");
|
||||||
|
|
||||||
error = NULL;
|
katze_assign (config_file, g_build_filename (config, "speeddial", NULL));
|
||||||
speeddial = speeddial_new_from_file (config, &error);
|
dial = midori_speed_dial_new (config_file, NULL);
|
||||||
|
|
||||||
/* In case of errors */
|
/* In case of errors */
|
||||||
if (error_messages->len)
|
if (error_messages->len)
|
||||||
|
@ -2594,7 +2508,7 @@ main (int argc,
|
||||||
"trash", trash,
|
"trash", trash,
|
||||||
"search-engines", search_engines,
|
"search-engines", search_engines,
|
||||||
"history", history,
|
"history", history,
|
||||||
"speed-dial", speeddial,
|
"speed-dial", dial->keyfile,
|
||||||
NULL);
|
NULL);
|
||||||
g_object_unref (history);
|
g_object_unref (history);
|
||||||
g_object_unref (search_engines);
|
g_object_unref (search_engines);
|
||||||
|
@ -2657,7 +2571,7 @@ main (int argc,
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref (settings);
|
g_object_unref (settings);
|
||||||
g_key_file_free (speeddial);
|
g_object_unref (dial);
|
||||||
g_object_unref (app);
|
g_object_unref (app);
|
||||||
g_free (config_file);
|
g_free (config_file);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1234,75 +1234,27 @@ midori_view_save_as_cb (GtkWidget* menuitem,
|
||||||
midori_browser_save_uri (browser, MIDORI_VIEW (view), uri);
|
midori_browser_save_uri (browser, MIDORI_VIEW (view), uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gchar*
|
|
||||||
midori_browser_speed_dial_get_next_free_slot (MidoriView* view)
|
|
||||||
{
|
|
||||||
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
|
|
||||||
GKeyFile* key_file;
|
|
||||||
guint slot_count = 0, slot = 1, i;
|
|
||||||
gchar** groups;
|
|
||||||
|
|
||||||
g_object_get (browser, "speed-dial", &key_file, NULL);
|
|
||||||
|
|
||||||
groups = g_key_file_get_groups (key_file, NULL);
|
|
||||||
for (i = 0; groups[i]; i++)
|
|
||||||
{
|
|
||||||
if (g_key_file_has_key (key_file, groups[i], "uri", NULL))
|
|
||||||
slot_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (slot <= slot_count)
|
|
||||||
{
|
|
||||||
gchar* dial_id = g_strdup_printf ("Dial %d", slot);
|
|
||||||
if (!g_key_file_has_group (key_file, dial_id))
|
|
||||||
{
|
|
||||||
g_free (dial_id);
|
|
||||||
return g_strdup_printf ("s%d", slot);
|
|
||||||
}
|
|
||||||
g_free (dial_id);
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
return g_strdup_printf ("s%d", slot_count + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
midori_browser_add_speed_dial (MidoriBrowser* browser)
|
midori_browser_add_speed_dial (MidoriBrowser* browser)
|
||||||
{
|
{
|
||||||
GdkPixbuf* img;
|
GdkPixbuf* img;
|
||||||
GtkWidget* view = midori_browser_get_current_tab (browser);
|
GtkWidget* view = midori_browser_get_current_tab (browser);
|
||||||
gchar* uri = g_strdup (midori_view_get_display_uri (MIDORI_VIEW (view)));
|
gchar* slot_id = midori_speed_dial_get_next_free_slot_fk (browser->speeddial);
|
||||||
gchar* title = g_strdup (midori_view_get_display_title (MIDORI_VIEW (view)));
|
gchar* uri;
|
||||||
gchar* slot_id = midori_browser_speed_dial_get_next_free_slot (MIDORI_VIEW (view));
|
gchar* title;
|
||||||
|
|
||||||
if (slot_id == NULL)
|
if (slot_id == NULL)
|
||||||
{
|
|
||||||
g_free (uri);
|
|
||||||
g_free (title);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
|
uri = g_strdup (midori_view_get_display_uri (MIDORI_VIEW (view)));
|
||||||
|
title = g_strdup (midori_view_get_display_title (MIDORI_VIEW (view)));
|
||||||
if ((img = midori_view_get_snapshot (MIDORI_VIEW (view), 240, 160)))
|
if ((img = midori_view_get_snapshot (MIDORI_VIEW (view), 240, 160)))
|
||||||
{
|
{
|
||||||
GKeyFile* key_file;
|
|
||||||
gchar* dial_id = g_strdup_printf ("Dial %s", slot_id + 1);
|
gchar* dial_id = g_strdup_printf ("Dial %s", slot_id + 1);
|
||||||
gchar* file_path = sokoke_build_thumbnail_path (uri);
|
midori_speed_dial_add_fk (dial_id, uri, title, img, browser->speeddial);
|
||||||
gchar* thumb_dir = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_cache_dir (), "thumbnails", NULL);
|
|
||||||
g_object_get (browser, "speed-dial", &key_file, NULL);
|
|
||||||
|
|
||||||
g_key_file_set_string (key_file, dial_id, "uri", uri);
|
|
||||||
g_key_file_set_string (key_file, dial_id, "title", title);
|
|
||||||
|
|
||||||
if (!g_file_test (thumb_dir, G_FILE_TEST_EXISTS))
|
|
||||||
katze_mkdir_with_parents (thumb_dir, 0700);
|
|
||||||
|
|
||||||
gdk_pixbuf_save (img, file_path, "png", NULL, "compression", "7", NULL);
|
|
||||||
|
|
||||||
midori_view_save_speed_dial_config (MIDORI_VIEW (view), key_file);
|
|
||||||
|
|
||||||
g_object_unref (img);
|
|
||||||
g_free (file_path);
|
|
||||||
g_free (thumb_dir);
|
|
||||||
g_free (dial_id);
|
g_free (dial_id);
|
||||||
|
midori_view_save_speed_dial_config (MIDORI_VIEW (view), browser->speeddial);
|
||||||
|
g_object_unref (img);
|
||||||
}
|
}
|
||||||
g_free (uri);
|
g_free (uri);
|
||||||
g_free (title);
|
g_free (title);
|
||||||
|
|
232
midori/midori-speeddial.vala
Normal file
232
midori/midori-speeddial.vala
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2012 Christian Dywan <christian@twotoats.de>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Katze {
|
||||||
|
extern static string mkdir_with_parents (string pathname, int mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Sokoke {
|
||||||
|
extern static string js_script_eval (void* ctx, string script, void* error);
|
||||||
|
extern static string build_thumbnail_path (string uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Midori {
|
||||||
|
public class SpeedDial : GLib.Object {
|
||||||
|
public GLib.KeyFile keyfile;
|
||||||
|
|
||||||
|
public SpeedDial (string filename, string? fallback = null) {
|
||||||
|
keyfile = new GLib.KeyFile ();
|
||||||
|
try {
|
||||||
|
keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);
|
||||||
|
}
|
||||||
|
catch (GLib.Error io_error) {
|
||||||
|
string json;
|
||||||
|
size_t len;
|
||||||
|
try {
|
||||||
|
FileUtils.get_contents (fallback ?? (filename + ".json"),
|
||||||
|
out json, out len);
|
||||||
|
}
|
||||||
|
catch (GLib.Error fallback_error) {
|
||||||
|
json = "'{}'";
|
||||||
|
len = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
var script = new StringBuilder.sized (len);
|
||||||
|
script.append ("var json = JSON.parse (");
|
||||||
|
script.append_len (json, (ssize_t)len);
|
||||||
|
script.append ("""
|
||||||
|
);
|
||||||
|
var keyfile = '';
|
||||||
|
for (var i in json['shortcuts']) {
|
||||||
|
var tile = json['shortcuts'][i];
|
||||||
|
keyfile += '[Dial ' + tile['id'].substring (1) + ']\n'
|
||||||
|
+ 'uri=' + tile['href'] + '\n'
|
||||||
|
+ 'img=' + tile['img'] + '\n'
|
||||||
|
+ 'title=' + tile['title'] + '\n\n';
|
||||||
|
}
|
||||||
|
var columns = json['width'] ? json['width'] : 3;
|
||||||
|
var rows = json['shortcuts'] ? json['shortcuts'].length / columns : 0;
|
||||||
|
keyfile += '[settings]\n'
|
||||||
|
+ 'columns=' + columns + '\n'
|
||||||
|
+ 'rows=' + (rows > 3 ? rows : 3) + '\n\n';
|
||||||
|
keyfile;
|
||||||
|
""");
|
||||||
|
|
||||||
|
try {
|
||||||
|
keyfile.load_from_data (
|
||||||
|
Sokoke.js_script_eval (null, script.str, null),
|
||||||
|
-1, 0);
|
||||||
|
}
|
||||||
|
catch (GLib.Error eval_error) {
|
||||||
|
GLib.critical ("Failed to parse %s as speed dial JSON: %s",
|
||||||
|
fallback ?? (filename + ".json"), eval_error.message);
|
||||||
|
}
|
||||||
|
Katze.mkdir_with_parents (
|
||||||
|
Path.build_path (Path.DIR_SEPARATOR_S,
|
||||||
|
Environment.get_user_cache_dir (),
|
||||||
|
"midori", "thumbnails"), 0700);
|
||||||
|
|
||||||
|
foreach (string tile in keyfile.get_groups ()) {
|
||||||
|
try {
|
||||||
|
string img = keyfile.get_string (tile, "img");
|
||||||
|
string uri = keyfile.get_string (tile, "uri");
|
||||||
|
if (img != null && uri[0] != '\0' && uri[0] != '#') {
|
||||||
|
uchar[] decoded = Base64.decode (img);
|
||||||
|
FileUtils.set_data (Sokoke.build_thumbnail_path (uri), decoded);
|
||||||
|
}
|
||||||
|
keyfile.remove_key (tile, "img");
|
||||||
|
}
|
||||||
|
catch (GLib.Error img_error) {
|
||||||
|
/* img and uri can be missing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string get_next_free_slot_fk (KeyFile keyfile) {
|
||||||
|
uint slot_count = 0;
|
||||||
|
foreach (string tile in keyfile.get_groups ()) {
|
||||||
|
try {
|
||||||
|
if (keyfile.has_key (tile, "uri"))
|
||||||
|
slot_count++;
|
||||||
|
}
|
||||||
|
catch (KeyFileError error) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
uint slot = 1;
|
||||||
|
while (slot <= slot_count) {
|
||||||
|
string tile = "Dial %u".printf (slot);
|
||||||
|
if (!keyfile.has_group (tile))
|
||||||
|
return "s%u".printf (slot);
|
||||||
|
slot++;
|
||||||
|
}
|
||||||
|
return "s%u".printf (slot_count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void add_fk (string id, string uri, string title, Gdk.Pixbuf img, KeyFile keyfile) {
|
||||||
|
keyfile.set_string (id, "uri", uri);
|
||||||
|
keyfile.set_string (id, "title", title);
|
||||||
|
|
||||||
|
Katze.mkdir_with_parents (Path.build_path (Path.DIR_SEPARATOR_S,
|
||||||
|
Paths.get_cache_dir (), "thumbnails"), 0700);
|
||||||
|
string filename = Sokoke.build_thumbnail_path (uri);
|
||||||
|
try {
|
||||||
|
img.save (filename, "png", null, "compression", "7", null);
|
||||||
|
}
|
||||||
|
catch (Error error) {
|
||||||
|
critical ("Failed to save speed dial thumbnail: %s", error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? get_html_fk (KeyFile? keyfile,
|
||||||
|
bool close_buttons_left, GLib.Object view, bool load_missing) throws Error {
|
||||||
|
|
||||||
|
string? head = null;
|
||||||
|
string filename = Paths.get_res_filename ("speeddial-head.html");
|
||||||
|
if (keyfile != null
|
||||||
|
&& FileUtils.get_contents (filename, out head, null)) {
|
||||||
|
string header = head.replace ("{title}", _("Speed Dial")).
|
||||||
|
replace ("{click_to_add}", _("Click to add a shortcut")).
|
||||||
|
replace ("{enter_shortcut_address}", _("Enter shortcut address")).
|
||||||
|
replace ("{enter_shortcut_name}", _("Enter shortcut title")).
|
||||||
|
replace ("{are_you_sure}", _("Are you sure you want to delete this shortcut?"));
|
||||||
|
var markup = new StringBuilder (header);
|
||||||
|
|
||||||
|
uint slot_count = 1;
|
||||||
|
foreach (string tile in keyfile.get_groups ()) {
|
||||||
|
try {
|
||||||
|
if (keyfile.has_key (tile, "uri"))
|
||||||
|
slot_count++;
|
||||||
|
}
|
||||||
|
catch (KeyFileError error) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to guess the best X by X grid size */
|
||||||
|
uint grid_index = 3;
|
||||||
|
while ((grid_index * grid_index) < slot_count)
|
||||||
|
grid_index++;
|
||||||
|
|
||||||
|
/* Percent width size of one slot */
|
||||||
|
uint slot_size = (100 / grid_index);
|
||||||
|
|
||||||
|
/* No editing in private/ app mode or without scripts */
|
||||||
|
markup.append_printf (
|
||||||
|
"%s<style>.cross { display:none }</style>%s" +
|
||||||
|
"<style> div.shortcut { height: %d%%; width: %d%%; }</style>\n",
|
||||||
|
Paths.is_readonly () ? "" : "<noscript>",
|
||||||
|
Paths.is_readonly () ? "" : "</noscript>",
|
||||||
|
slot_size + 1, slot_size - 4);
|
||||||
|
|
||||||
|
/* Combined width of slots should always be less than 100%.
|
||||||
|
* Use half of the remaining percentage as a margin size */
|
||||||
|
uint div_factor;
|
||||||
|
if (slot_size * grid_index >= 100 && grid_index > 4)
|
||||||
|
div_factor = 8;
|
||||||
|
else
|
||||||
|
div_factor = 2;
|
||||||
|
uint margin = (100 - ((slot_size - 4) * grid_index)) / div_factor;
|
||||||
|
if (margin > 9)
|
||||||
|
margin = margin % 10;
|
||||||
|
|
||||||
|
markup.append_printf (
|
||||||
|
"<style> body { overflow:hidden } #content { margin-left: %u%%; }</style>", margin);
|
||||||
|
if (close_buttons_left)
|
||||||
|
markup.append_printf (
|
||||||
|
"<style>.cross { left: -14px }</style>");
|
||||||
|
|
||||||
|
foreach (string tile in keyfile.get_groups ()) {
|
||||||
|
try {
|
||||||
|
string uri = keyfile.get_string (tile, "uri");
|
||||||
|
if (uri != null && uri.str ("://") != null && tile.has_prefix ("Dial ")) {
|
||||||
|
string title = keyfile.get_string (tile, "title");
|
||||||
|
string thumb_filename = Sokoke.build_thumbnail_path (uri);
|
||||||
|
uint slot = tile.substring (5, -1).to_int ();
|
||||||
|
string encoded;
|
||||||
|
try {
|
||||||
|
uint8[] thumb;
|
||||||
|
FileUtils.get_data (thumb_filename, out thumb);
|
||||||
|
encoded = Base64.encode (thumb);
|
||||||
|
}
|
||||||
|
catch (FileError error) {
|
||||||
|
encoded = null;
|
||||||
|
if (load_missing)
|
||||||
|
/* FIXME: midori_view_speed_dial_get_thumb (view, tile, uri); */
|
||||||
|
critical ("FIXME midori_view_speed_dial_get_thumb");
|
||||||
|
}
|
||||||
|
markup.append_printf ("""
|
||||||
|
<div class="shortcut" id="s%u"><div class="preview">
|
||||||
|
<a class="cross" href="#" onclick='clearShortcut("s%u");'></a>
|
||||||
|
<a href="%s"><img src="data:image/png;base64,%s" title='%s'></a>
|
||||||
|
</div><div class="title" onclick='renameShortcut("s%u");'>%s</div></div>
|
||||||
|
""",
|
||||||
|
slot, slot, uri, encoded ?? "", title, slot, title ?? "");
|
||||||
|
}
|
||||||
|
else if (tile != "settings")
|
||||||
|
keyfile.remove_group (tile);
|
||||||
|
}
|
||||||
|
catch (KeyFileError error) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
markup.append_printf ("""
|
||||||
|
<div class="shortcut" id="s%u"><div class="preview new">
|
||||||
|
<a class="add" href="#" onclick='return getAction("s%u");'></a>
|
||||||
|
</div><div class="title">%s</div></div>
|
||||||
|
""",
|
||||||
|
slot_count + 1, slot_count + 1, _("Click to add a shortcut"));
|
||||||
|
markup.append_printf ("</div>\n</body>\n</html>\n");
|
||||||
|
return markup.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4230,134 +4230,10 @@ prepare_speed_dial_html (MidoriView* view,
|
||||||
gboolean load_missing)
|
gboolean load_missing)
|
||||||
{
|
{
|
||||||
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
|
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
|
||||||
GKeyFile* key_file;
|
GKeyFile* key_file = katze_object_get_object (browser, "speed-dial");
|
||||||
GString* markup = NULL;
|
return midori_speed_dial_get_html_fk (key_file,
|
||||||
guint slot_count = 1, i, grid_index = 3, slot_size;
|
katze_object_get_boolean (view->settings, "close-buttons-left"),
|
||||||
guint margin, div_factor;
|
G_OBJECT (view), load_missing, NULL);
|
||||||
gchar* speed_dial_head;
|
|
||||||
gchar* file_path;
|
|
||||||
gchar** groups;
|
|
||||||
|
|
||||||
g_object_get (browser, "speed-dial", &key_file, NULL);
|
|
||||||
file_path = midori_paths_get_res_filename ("speeddial-head.html");
|
|
||||||
|
|
||||||
if (key_file != NULL
|
|
||||||
&& g_access (file_path, F_OK) == 0
|
|
||||||
&& g_file_get_contents (file_path, &speed_dial_head, NULL, NULL))
|
|
||||||
{
|
|
||||||
gchar* header = sokoke_replace_variables (speed_dial_head,
|
|
||||||
"{title}", _("Speed Dial"),
|
|
||||||
"{click_to_add}", _("Click to add a shortcut"),
|
|
||||||
"{enter_shortcut_address}", _("Enter shortcut address"),
|
|
||||||
"{enter_shortcut_name}", _("Enter shortcut title"),
|
|
||||||
"{are_you_sure}", _("Are you sure you want to delete this shortcut?"),
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
markup = g_string_new (header);
|
|
||||||
|
|
||||||
g_free (speed_dial_head);
|
|
||||||
g_free (file_path);
|
|
||||||
g_free (header);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_free (file_path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
groups = g_key_file_get_groups (key_file, NULL);
|
|
||||||
for (i = 0; groups[i]; i++)
|
|
||||||
{
|
|
||||||
if (g_key_file_has_key (key_file, groups[i], "uri", NULL))
|
|
||||||
slot_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* try to guess the best X by X grid size */
|
|
||||||
while ((grid_index * grid_index) < slot_count)
|
|
||||||
grid_index++;
|
|
||||||
|
|
||||||
/* percent width size of one slot */
|
|
||||||
slot_size = (100 / grid_index);
|
|
||||||
/* No editing in private/ app mode or without scripts */
|
|
||||||
g_string_append_printf (markup,
|
|
||||||
"%s<style>.cross { display:none }</style>%s"
|
|
||||||
"<style> div.shortcut { height: %d%%; width: %d%%; }</style>\n",
|
|
||||||
midori_paths_is_readonly () ? "" : "<noscript>",
|
|
||||||
midori_paths_is_readonly () ? "" : "</noscript>",
|
|
||||||
slot_size + 1, slot_size - 4);
|
|
||||||
|
|
||||||
/* Combined width of slots should always be less than 100%.
|
|
||||||
* Use half of the remaining percentage as a margin size */
|
|
||||||
if (slot_size * grid_index >= 100 && grid_index > 4)
|
|
||||||
div_factor = 8;
|
|
||||||
else
|
|
||||||
div_factor = 2;
|
|
||||||
|
|
||||||
margin = (100 - ((slot_size - 4) * grid_index)) / div_factor;
|
|
||||||
|
|
||||||
if (margin > 9)
|
|
||||||
margin = margin % 10;
|
|
||||||
|
|
||||||
g_string_append_printf (markup,
|
|
||||||
"<style> body { overflow:hidden } #content { margin-left: %d%%; }</style>",
|
|
||||||
margin);
|
|
||||||
|
|
||||||
if (katze_object_get_boolean (view->settings, "close-buttons-left"))
|
|
||||||
g_string_append_printf (markup,
|
|
||||||
"<style>.cross { left: -14px }</style>");
|
|
||||||
|
|
||||||
for (i = 0; groups[i]; i++)
|
|
||||||
{
|
|
||||||
gchar* uri = g_key_file_get_string (key_file, groups[i], "uri", NULL);
|
|
||||||
if (uri && strstr (uri, "://"))
|
|
||||||
{
|
|
||||||
gchar* title = g_key_file_get_string (key_file, groups[i], "title", NULL);
|
|
||||||
gchar* thumb_file = sokoke_build_thumbnail_path (uri);
|
|
||||||
gchar* encoded;
|
|
||||||
guint slot = atoi (groups[i] + strlen ("Dial "));
|
|
||||||
|
|
||||||
if (g_access (thumb_file, F_OK) == 0)
|
|
||||||
{
|
|
||||||
gsize sz;
|
|
||||||
gchar* thumb_content;
|
|
||||||
g_file_get_contents (thumb_file, &thumb_content, &sz, NULL);
|
|
||||||
encoded = g_base64_encode ((guchar*)thumb_content, sz);
|
|
||||||
g_free (thumb_content);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
encoded = NULL;
|
|
||||||
if (load_missing)
|
|
||||||
midori_view_speed_dial_get_thumb (view, groups[i], uri);
|
|
||||||
}
|
|
||||||
g_free (thumb_file);
|
|
||||||
|
|
||||||
g_string_append_printf (markup,
|
|
||||||
"<div class=\"shortcut\" id=\"s%d\"><div class=\"preview\">"
|
|
||||||
"<a class=\"cross\" href=\"#\" onclick='clearShortcut(\"s%d\");'></a>"
|
|
||||||
"<a href=\"%s\"><img src=\"data:image/png;base64,%s\" title='%s'></a>"
|
|
||||||
"</div><div class=\"title\" onclick='renameShortcut(\"s%d\");'>%s</div></div>\n",
|
|
||||||
slot, slot, uri, encoded ? encoded : "", title, slot, title ? title : "");
|
|
||||||
|
|
||||||
g_free (title);
|
|
||||||
g_free (encoded);
|
|
||||||
}
|
|
||||||
else if (strcmp (groups[i], "settings"))
|
|
||||||
g_key_file_remove_group (key_file, groups[i], NULL);
|
|
||||||
|
|
||||||
g_free (uri);
|
|
||||||
}
|
|
||||||
g_strfreev (groups);
|
|
||||||
|
|
||||||
g_string_append_printf (markup,
|
|
||||||
"<div class=\"shortcut\" id=\"s%d\"><div class=\"preview new\">"
|
|
||||||
"<a class=\"add\" href=\"#\" onclick='return getAction(\"s%d\");'></a>"
|
|
||||||
"</div><div class=\"title\">%s</div></div>\n",
|
|
||||||
slot_count + 1, slot_count + 1, _("Click to add a shortcut"));
|
|
||||||
g_string_append_printf (markup,
|
|
||||||
"</div>\n</body>\n</html>\n");
|
|
||||||
|
|
||||||
return g_string_free (markup, FALSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -64,15 +64,18 @@ sokoke_js_script_eval (JSContextRef js_context,
|
||||||
const gchar* script,
|
const gchar* script,
|
||||||
gchar** exception)
|
gchar** exception)
|
||||||
{
|
{
|
||||||
|
JSGlobalContextRef temporary_context = NULL;
|
||||||
gchar* value;
|
gchar* value;
|
||||||
JSStringRef js_value_string;
|
JSStringRef js_value_string;
|
||||||
JSStringRef js_script;
|
JSStringRef js_script;
|
||||||
JSValueRef js_exception = NULL;
|
JSValueRef js_exception = NULL;
|
||||||
JSValueRef js_value;
|
JSValueRef js_value;
|
||||||
|
|
||||||
g_return_val_if_fail (js_context, FALSE);
|
|
||||||
g_return_val_if_fail (script, FALSE);
|
g_return_val_if_fail (script, FALSE);
|
||||||
|
|
||||||
|
if (!js_context)
|
||||||
|
js_context = temporary_context = JSGlobalContextCreateInGroup (NULL, NULL);
|
||||||
|
|
||||||
js_script = JSStringCreateWithUTF8CString (script);
|
js_script = JSStringCreateWithUTF8CString (script);
|
||||||
js_value = JSEvaluateScript (js_context, js_script,
|
js_value = JSEvaluateScript (js_context, js_script,
|
||||||
JSContextGetGlobalObject (js_context), NULL, 0, &js_exception);
|
JSContextGetGlobalObject (js_context), NULL, 0, &js_exception);
|
||||||
|
@ -91,12 +94,16 @@ sokoke_js_script_eval (JSContextRef js_context,
|
||||||
g_free (value);
|
g_free (value);
|
||||||
}
|
}
|
||||||
JSStringRelease (js_message);
|
JSStringRelease (js_message);
|
||||||
|
if (temporary_context)
|
||||||
|
JSGlobalContextRelease (temporary_context);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
js_value_string = JSValueToStringCopy (js_context, js_value, NULL);
|
js_value_string = JSValueToStringCopy (js_context, js_value, NULL);
|
||||||
value = sokoke_js_string_utf8 (js_value_string);
|
value = sokoke_js_string_utf8 (js_value_string);
|
||||||
JSStringRelease (js_value_string);
|
JSStringRelease (js_value_string);
|
||||||
|
if (temporary_context)
|
||||||
|
JSGlobalContextRelease (temporary_context);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
tests/speeddial.vala
Normal file
55
tests/speeddial.vala
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
string get_test_file (string contents) {
|
||||||
|
string file;
|
||||||
|
int fd = FileUtils.open_tmp ("speeddialXXXXXX", out file);
|
||||||
|
FileUtils.set_contents (file, contents, -1);
|
||||||
|
FileUtils.close (fd);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Katze {
|
||||||
|
extern static string assert_str_equal (string input, string result, string expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void speeddial_load () {
|
||||||
|
string data = get_test_file ("""
|
||||||
|
[Dial 1]
|
||||||
|
uri=http://example.com
|
||||||
|
title=Example
|
||||||
|
[settings]
|
||||||
|
columns=3
|
||||||
|
rows=3
|
||||||
|
""");
|
||||||
|
|
||||||
|
string json = get_test_file ("""
|
||||||
|
'{"shortcuts":[{"id":"s1","href":"http://example.com","title":"Example","img":"a2F0emU="}]}'
|
||||||
|
""");
|
||||||
|
|
||||||
|
var dial_data = new Midori.SpeedDial (data, "");
|
||||||
|
var dial_json = new Midori.SpeedDial ("", json);
|
||||||
|
FileUtils.remove (data);
|
||||||
|
FileUtils.remove (json);
|
||||||
|
|
||||||
|
Katze.assert_str_equal (json, dial_data.keyfile.to_data (), dial_json.keyfile.to_data ());
|
||||||
|
Katze.assert_str_equal (json, Midori.SpeedDial.get_next_free_slot_fk (dial_data.keyfile), "s2");
|
||||||
|
Katze.assert_str_equal (json, Midori.SpeedDial.get_next_free_Slot_fk (dial_json), "s2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void main (string[] args) {
|
||||||
|
string temporary_cache = DirUtils.make_tmp ("cacheXXXXXX");
|
||||||
|
Environment.set_variable ("XDG_CACHE_HOME", temporary_cache, true);
|
||||||
|
Test.init (ref args);
|
||||||
|
Test.add_func ("/speeddial/load", speeddial_load);
|
||||||
|
Test.run ();
|
||||||
|
DirUtils.remove (temporary_cache);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue