/*
* about:profiles
*
* Copyright (C) 2017 Patrick McDermott
*
* This file is part of Marquee.
*
* Marquee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Marquee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Marquee. If not, see .
*/
#include
#include
#include
#include "../../application.h"
#include "../../config/profiles.h"
#include "../../i18n.h"
#include "../../utils/html.h"
#include "../../utils/profile-icon.h"
#include "../about.h"
#include "paths.h"
static const gchar *styles =
"div.profile > svg {\n"
" width: 64px;\n"
" height: 64px;\n"
" border: 1px solid transparent;\n"
" border-radius: 3px;\n"
" padding: 6px;\n"
" transition: all 250ms ease-in-out 0s;\n"
" -moz-transition: all 250ms ease-in-out 0s;\n"
" -wekbit-transition: all 250ms ease-in-out 0s;\n"
" -o-transition: all 250ms ease-in-out 0s;\n"
"}\n"
"div.profile > label {\n"
" display: inline;\n"
" border: 1px solid #9F9F9F;\n"
" border-radius: 3px;\n"
" background-color: #DFDFDF;\n"
" padding: 6px;\n"
" color: #000000;\n"
" transition: all 250ms ease-in-out 0s;\n"
" -moz-transition: all 250ms ease-in-out 0s;\n"
" -wekbit-transition: all 250ms ease-in-out 0s;\n"
" -o-transition: all 250ms ease-in-out 0s;\n"
"}\n"
"div.profile > label:hover {\n"
" border: 1px solid #4F8FCF;\n"
" outline: 0;\n"
" background-color: #EFEFEF;\n"
"}\n"
"div.profile {\n"
" background-color: #CFCFCF;\n"
" border: 1px solid #9F9F9F;\n"
" border-radius: 3px;\n"
"}\n"
"div.profile > input[type=radio] {\n"
" display: none;\n"
"}\n"
"div.profile > span {\n"
" display: inline;\n"
" position: absolute;\n"
"}\n"
"div.profile > span.current {\n"
" font-weight: bold;\n"
"}\n"
"div.profile > input[type=text] {\n"
" display: none;\n"
" position: absolute;\n"
"}\n"
"div.profile > label {\n"
" display: inline;\n"
"}\n"
"div.profile > label > span {\n"
" margin: 0;\n"
"}\n"
"div.profile > input[type=submit]:last-child {\n"
" display: none;\n"
"}\n"
"div.profile > input[type=radio]:checked ~ svg {\n"
" border-color: #9F9F9F;\n"
" background-color: #DFDFDF;\n"
"}\n"
"div.profile > input[type=radio]:checked ~ svg:hover {\n"
" border-color: #4F8FCF;\n"
" background-color: #EFEFEF;\n"
"}\n"
"div.profile > input[type=radio]:checked ~ span {\n"
" display: none;\n"
"}\n"
"div.profile > input[type=radio]:checked ~ input[type=text] {\n"
" display: inline;\n"
"}\n"
"div.profile > input[type=radio]:checked ~ label {\n"
" display: none;\n"
"}\n"
"div.profile > input[type=radio]:checked ~ "
" input[type=submit]:last-child {\n"
" display: inline;\n"
"}\n"
"div.profile > label, div.profile > input[type=submit] {\n"
" float: right;\n"
"}\n"
"div.color-picker {\n"
" display: none;\n"
" position: fixed;\n"
" top: 50%;\n"
" left: 50%;\n"
" margin-top: -127px;\n"
" margin-left: -127px;\n"
" border: 1px solid #9F9F9F;\n"
" border-radius: 3px;\n"
" box-shadow: 0 0 12px #000000;\n"
" background-color: #CFCFCF;\n"
"}\n"
"div.color-picker div.picker-wrapper,\n"
"div.color-picker div.slide-wrapper {\n"
" position: relative;\n"
" float: left;\n"
"}\n"
"div.color-picker div.picker {\n"
" float: left;\n"
" margin: 0;\n"
" width: 200px;\n"
" height: 200px;\n"
" cursor: crosshair;\n"
"}\n"
"div.color-picker div.slide {\n"
" float: left;\n"
" margin: 0;\n"
" width: 30px;\n"
" height: 200px;\n"
" cursor: crosshair;\n"
"}\n"
"div.color-picker svg {\n"
" margin: 0;\n"
"}\n"
"div.color-picker div.picker-indicator {\n"
" position: absolute;\n"
" top: -6px;\n"
" left: -6px;\n"
" margin: 0;\n"
" border: 3px solid #9F9F9F;\n"
" border-radius: 6px;\n"
" width: 6px;\n"
" height: 6px;\n"
" opacity: 0.5;\n"
" background-color: #FFFFFF;\n"
" pointer-events: none;\n"
"}\n"
"div.color-picker div.slide-indicator {\n"
" position: absolute;\n"
" top: -6px;\n"
" left: -3px;\n"
" margin: 0;\n"
" border: 3px solid #9F9F9F;\n"
" border-radius: 6px;\n"
" width: 100%;\n"
" height: 6px;\n"
" opacity: 0.5;\n"
" background-color: #FFFFFF;\n"
" pointer-events: none;\n"
"}\n"
"div.color-picker div.dialog-buttonbox {\n"
" clear: left;\n"
"}\n"
"div.color-picker div.dialog-buttonbox * {\n"
" line-height: 100%;\n"
"}\n"
;
static const gchar *cp_script =
"(function () {\n"
"var parent = document.currentScript.parentNode;\n"
"var radio = parent.querySelector(\"input[type=radio]\");\n"
"var svg = parent.querySelector(\"svg\");\n"
"var colorInput = parent.querySelector(\"input[type=hidden]\");\n"
"var nameInput = parent.querySelector(\"input[type=text]\");\n"
"var cpDiv = parent.querySelector(\"div.color-picker\");\n"
"var slide = cpDiv.querySelector(\"div.slide\");\n"
"var slideInd = cpDiv.querySelector(\"div.slide-indicator\");\n"
"var picker = cpDiv.querySelector(\"div.picker\");\n"
"var pickerInd = cpDiv.querySelector(\"div.picker-indicator\");\n"
"var cancelBtn = cpDiv.querySelector(\"input[type=reset]\");\n"
"var okBtn = cpDiv.querySelector(\"input[type=submit]\");\n"
"var colorHex;\n"
"var cp = ColorPicker(slide, picker,\n"
" function (hex, hsv, rgb, pickerCoord, sliderCoord) {\n"
" ColorPicker.positionIndicators(\n"
" slideInd, pickerInd,\n"
" sliderCoord, pickerCoord);\n"
" colorHex = hex;\n"
" });\n"
"radio.addEventListener(\"change\", function (event) {\n"
" var cps = document.querySelectorAll(\n"
" \"div.color-picker\");\n"
" for (var i = 0; i < cps.length; ++i) {\n"
" cps[i].style.display = \"none\";\n"
" }\n"
" nameInput.focus();\n"
"});\n"
"svg.addEventListener(\"click\", function (event) {\n"
" if (radio.checked) {\n"
" cp.setHex(colorInput.value);\n"
" cpDiv.style.display = \"block\";\n"
" }\n"
"});\n"
"cancelBtn.addEventListener(\"click\", function (event) {\n"
" cpDiv.style.display = \"none\";\n"
" event.preventDefault();\n"
" return false;\n"
"});\n"
"okBtn.addEventListener(\"click\", function (event) {\n"
" colorInput.value = colorHex;\n"
" for (var i = 0; i < svg.childNodes.length; ++i) {\n"
" if (svg.childNodes[i].nodeType != Node.ELEMENT_NODE)\n"
" {\n"
" continue;\n"
" }\n"
" svg.childNodes[i].setAttribute(\"fill\",\n"
" colorHex);\n"
" }\n"
" cpDiv.style.display = \"none\";\n"
" event.preventDefault();\n"
" return false;\n"
"});\n"
"})();\n"
;
static gchar *
generate_div(MqProfiles *profiles, gchar *id)
{
gchar *name;
gchar *color;
gchar *editing_str;
gchar *name_str;
gchar *color_str;
gchar *launch_str;
gchar *default_str;
gchar *delete_str;
gchar *save_str;
gchar *div;
gchar *current;
gboolean is_current;
/* name is freed by the mq_html_container() call to generate the name
* . */
if (id) {
name = mq_profiles_get_name(profiles, id);
color = mq_profiles_get_color(profiles, id);
} else {
name = g_strdup(_("New Profile"));
color = g_strdup("#00ff00");
}
editing_str = g_strconcat("editing_", id, NULL);
name_str = g_strconcat("name_", id, NULL);
color_str = g_strconcat("color_", id, NULL);
launch_str = g_strconcat("launch_", id, NULL);
default_str = g_strconcat("default_", id, NULL);
delete_str = g_strconcat("delete_", id, NULL);
save_str = g_strconcat("save_", id, NULL);
current = mq_profiles_get_current(profiles);
is_current = (g_strcmp0(id, current) == 0);
g_free(current);
div = mq_html_container("div", "profile",
mq_html_input_radio("editing", editing_str, id, NULL,
id == NULL),
mq_profile_icon_new(color),
mq_html_input_hidden(color_str, color),
mq_html_container("div", "color-picker",
mq_html_container("div", "picker-wrapper",
mq_html_container("div", "picker",
NULL),
mq_html_container("div", "picker-indicator",
NULL),
NULL),
mq_html_container("div", "slide-wrapper",
mq_html_container("div", "slide",
NULL),
mq_html_container("div", "slide-indicator",
NULL),
NULL),
mq_html_buttonbox(NULL, _("OK"), NULL, _("Cancel")),
NULL),
mq_html_container("span", is_current ? "current" : NULL, name,
NULL),
mq_html_input_text(name_str, NULL, name, id == NULL),
mq_html_container("script", NULL, g_strdup(cp_script), NULL),
/* TRANSLATORS: "Launch" meaning execute or run. */
mq_html_submit(launch_str, _("Launch"), !id),
mq_html_submit(default_str, _("Make Default"),
!id || mq_profiles_is_default(profiles, id)),
mq_html_submit(delete_str, _("Delete"), is_current),
mq_html_label(editing_str, _("Edit"), FALSE),
mq_html_submit(save_str, _("Save"), FALSE),
NULL);
g_free(editing_str);
g_free(name_str);
g_free(color_str);
g_free(launch_str);
g_free(default_str);
g_free(delete_str);
g_free(save_str);
g_free(color);
return div;
}
static gchar *
generate_document(MqProfiles *profiles, gboolean adding)
{
gchar **ids;
gsize length;
gchar **divs;
gsize i;
gchar *cp_res;
gchar *document;
ids = mq_profiles_get_profiles(profiles, &length);
divs = g_new(gchar *, length + (adding ? 1 : 0) + 1);
for (i = 0; i < length; ++i) {
divs[i] = generate_div(profiles, ids[i]);
}
if (adding) {
divs[i++] = generate_div(profiles, NULL);
}
divs[i] = NULL;
g_strfreev(ids);
cp_res = g_strdup(
"\n"
);
document = mq_html_document(_("Profiles"), styles, cp_res,
mq_html_form_v("add", _("Add Profile"), NULL, NULL, divs),
NULL);
g_free(cp_res);
return document;
}
static void
edit_profiles(MqProfiles *profiles, GHashTable *query)
{
GList *keys;
GList *key;
gchar *id;
gchar *name_key;
gchar *color_key;
keys = g_hash_table_get_keys(query);
for (key = keys; key; key = key->next) {
if (g_str_has_prefix(key->data, "save_")) {
id = (gchar *) key->data + strlen("save_");
name_key = g_strconcat("name_", id, NULL);
color_key = g_strconcat("color_", id, NULL);
if (id[0]) { /* Edit existing profile. */
mq_profiles_set_name(profiles, id,
g_hash_table_lookup(query, name_key));
mq_profiles_set_color(profiles, id,
g_hash_table_lookup(query, color_key));
} else {
mq_profiles_insert(profiles,
g_hash_table_lookup(query, name_key),
g_hash_table_lookup(query, color_key));
}
g_free(name_key);
g_free(color_key);
mq_profiles_save(profiles);
break;
} else if (g_str_has_prefix(key->data, "delete_")) {
id = (gchar *) key->data + strlen("delete_");
if (id[0]) { /* Don't remove new profile. */
mq_profiles_remove(profiles, id);
}
mq_profiles_save(profiles);
break;
} else if (g_str_has_prefix(key->data, "default_")) {
id = (gchar *) key->data + strlen("default_");
if (id[0]) { /* Don't set new profile as default. */
mq_profiles_set_default(profiles, id);
}
mq_profiles_save(profiles);
break;
} else if (g_str_has_prefix(key->data, "launch_")) {
id = (gchar *) key->data + strlen("launch_");
mq_profile_launch(id);
break;
}
}
g_list_free(keys);
}
void
mq_about_profiles_response(MqApplication *application,
GHashTable *query, WebKitURISchemeRequest *request)
{
MqProfiles *profiles;
profiles = mq_application_get_profiles(application);
if (!query) {
mq_about_response(request, generate_document(profiles, FALSE));
} else if (g_hash_table_lookup(query, "add")) {
mq_about_response(request, generate_document(profiles, TRUE));
} else {
edit_profiles(profiles, query);
mq_about_redirect(request, "mq-about:profiles");
}
}