/* * 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"); } }