summaryrefslogtreecommitdiff
path: root/endless/eosapplication.c
diff options
context:
space:
mode:
authorPhilip Chimento <philip@endlessm.com>2014-01-08 16:51:39 -0500
committerPhilip Chimento <philip@endlessm.com>2015-03-25 17:42:34 -0700
commit67c212e47a1db5f3e3dd1abaf552052e46512c93 (patch)
tree84d7d7906b033e502561c4136efeda68312cc6bc /endless/eosapplication.c
parent88a142a202a3ae5e82f222cbb5ce75372d66e719 (diff)
Initial implementation of image credits
This is a rough implementation of an "About"-like dialog for image attribution in SDK applications. You can press the 'secret' hotkey Shift+Ctrl+A to get a dialog with thumbnails and attribution information for the images in the app's GResource. Clicking on the thumbnail opens the image's original URI in the browser, if known. Clicking on the text opens the image's license text in the browser, if known. For this, you need to add a JSON file to the app's GResource and pass its GResource path to the EosApplication:image-attribution-file property. The format of this JSON file is described in the documentation for EosApplication. The dialog uses GtkTreeView because we didn't have GtkListBox when I started implementing it over a year ago. This places some limitations on the UI; the links behave weirdly and the mouse pointer doesn't change to a hand when hovering over the links. [endlessm/eos-sdk#2934]
Diffstat (limited to 'endless/eosapplication.c')
-rw-r--r--endless/eosapplication.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/endless/eosapplication.c b/endless/eosapplication.c
index 72016c1..9ca109b 100644
--- a/endless/eosapplication.c
+++ b/endless/eosapplication.c
@@ -2,12 +2,16 @@
#include "config.h"
#include "eosapplication.h"
+#include "eosattribution-private.h"
+#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include "eoswindow.h"
#define CSS_THEME_URI "resource:///com/endlessm/sdk/css/endless-widgets.css"
+#define _CREDITS_DIALOG_DEFAULT_HEIGHT 450
+#define _CREDITS_DIALOG_DEFAULT_WIDTH 750
/**
* SECTION:application
@@ -46,12 +50,186 @@
* flags: 0 });
* app.run(ARGV);
* ]|
+ *
+ * You can specify attribution for images used in your application.
+ * This is important if you use images that require you to credit the original
+ * author, or Creative Commons-licenses.
+ * The attribution takes the form of a JSON file, an array of objects with the
+ * properties listed in <xref linkend="image-attribution-properties"/>.
+ * See #EosApplication:image-attribution-file.
+ *
+ * <table id="image-attribution-properties">
+ * <thead>
+ * <tr>
+ * <th align="left">Property</th>
+ * <th align="left">Required?</th>
+ * <th align="left">Type</th>
+ * <th align="left">Description</th>
+ * </tr>
+ * </thead>
+ * <tr>
+ * <td><code>resource_path</code></td>
+ * <td>Yes</td>
+ * <td>string</td>
+ * <td>
+ * Resource path to the image (e.g. <code>/com/example/...</code>)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>uri</code></td>
+ * <td>No</td>
+ * <td>string</td>
+ * <td>
+ * URI where the original image is to be found: e.g., a Flickr link.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>license</code></td>
+ * <td>*</td>
+ * <td>string</td>
+ * <td>
+ * Text identifying the license under which you are using this image.
+ * This field is not free-form; the allowed values are listed in <xref
+ * linkend="image-attribution-licenses"/>.
+ * If the license is not listed there, leave this field blank and clarify
+ * the license in the <code>comment</code> field.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>license_uri</code></td>
+ * <td>*</td>
+ * <td>string</td>
+ * <td>
+ * URI linking to the text of the image license.
+ * If you use the <code>license</code> field, this field may be
+ * automatically filled in, so you can leave it blank.
+ * If you do specify a value, then your value will override any automatic
+ * value.
+ * Note that you will then lose any localization from the automatic value;
+ * localization of this field is planned for later.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>credit</code></td>
+ * <td>*</td>
+ * <td>string</td>
+ * <td>
+ * The name or username of the author of the image.
+ * This is appropriate when the terms of use specify that the author is to
+ * be credited when the image is used.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>credit_contact</code></td>
+ * <td>No</td>
+ * <td>string</td>
+ * <td>
+ * URI at which the author can be contacted.
+ * (If this is an e-mail address, prefix it with <code>mailto:</code> so
+ * that it is a valid URI.)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>copyright_holder</code></td>
+ * <td>*</td>
+ * <td>string</td>
+ * <td>
+ * Copyright holder of the image.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>copyright_year</code></td>
+ * <td>No</td>
+ * <td>integer</td>
+ * <td>
+ * Copyright year of the image.
+ * This will be displayed along with the copyright holder.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>permission</code></td>
+ * <td>No</td>
+ * <td>boolean</td>
+ * <td>
+ * Whether the image is used with permission.
+ * If this is specified, a string such as <quote>Used with
+ * permission</quote> may be displayed.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><code>comment</code></td>
+ * <td>No</td>
+ * <td>string</td>
+ * <td>
+ * Any other comments about the image license, terms of use, or source.
+ * </td>
+ * </tr>
+ * <tfoot>
+ * <tr>
+ * <td colspan="4">*At least one of these properties is required.</td>
+ * </tr>
+ * </tfoot>
+ * <caption>Allowed properties of the objects in the image attribution JSON
+ * file</caption>
+ * </table>
+ * <para></para>
+ * <table id="image-attribution-licenses">
+ * <thead>
+ * <tr>
+ * <th align="left">String</th>
+ * <th align="left">Description</th>
+ * </tr>
+ * </thead>
+ * <tr>
+ * <td>Public domain</td>
+ * <td>Public domain</td>
+ * </tr>
+ * <tr>
+ * <td>CC0 1.0</td>
+ * <td><ulink url="http://creativecommons.org/publicdomain/zero/1.0/">CC0
+ * 1.0 Universal (Public domain)</ulink></td>
+ * </tr>
+ * <tr>
+ * <td>CC BY 2.0</td>
+ * <td><ulink url="http://creativecommons.org/licenses/by/2.0/">Creative
+ * Commons Attribution 2.0</ulink></td>
+ * </tr>
+ * <tr>
+ * <td>CC BY 3.0</td>
+ * <td><ulink url="http://creativecommons.org/licenses/by/3.0/">Creative
+ * Commons Attribution 3.0</ulink></td>
+ * </tr>
+ * <tr>
+ * <td>CC BY-SA 2.0</td>
+ * <td><ulink url="http://creativecommons.org/licenses/by-sa/2.0/">Creative
+ * Commons Attribution-ShareAlike 2.0</ulink></td>
+ * </tr>
+ * <tr>
+ * <td>CC BY-SA 3.0</td>
+ * <td><ulink url="http://creativecommons.org/licenses/by-sa/3.0/">Creative
+ * Commons Attribution-ShareAlike 3.0</ulink></td>
+ * </tr>
+ * <tr>
+ * <td>CC BY-ND 2.0</td>
+ * <td><ulink url="http://creativecommons.org/licenses/by-nd/2.0/">Creative
+ * Commons Attribution-NoDerivs 2.0</ulink></td>
+ * </tr>
+ * <tr>
+ * <td>CC BY-ND 3.0</td>
+ * <td><ulink url="http://creativecommons.org/licenses/by-nd/3.0/">Creative
+ * Commons Attribution-NoDerivs 3.0</ulink></td>
+ * </tr>
+ * <caption>Allowed values for the <code>license</code> property in the image
+ * attribution JSON file</caption>
+ * </table>
*/
typedef struct {
GOnce init_config_dir_once;
GFile *config_dir;
+ GFile *image_attribution_file;
+
EosWindow *main_application_window;
} EosApplicationPrivate;
@@ -61,11 +239,67 @@ enum
{
PROP_0,
PROP_CONFIG_DIR,
+ PROP_IMAGE_ATTRIBUTION_FILE,
NPROPS
};
static GParamSpec *eos_application_props[NPROPS] = { NULL, };
+/* Signal handler for attribution widget requesting to show uri */
+static void
+on_attribution_show_uri (EosAttribution *attribution,
+ const gchar *uri,
+ EosApplication *self)
+{
+ GError *error = NULL;
+ if (!gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error))
+ {
+ g_critical ("Error showing URI %s: %s", uri, error->message);
+ g_error_free (error);
+ }
+}
+
+/* Signal handler for app.image-credits::activate action */
+static void
+on_image_credits_activate (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ EosApplication *self = EOS_APPLICATION (data);
+ EosApplicationPrivate *priv = eos_application_get_instance_private (self);
+ GtkWidget *dialog, *attribution, *content;
+ GError *error = NULL;
+
+ attribution = eos_attribution_new ();
+ if (!eos_attribution_populate_from_json_file (EOS_ATTRIBUTION (attribution),
+ priv->image_attribution_file,
+ &error))
+ {
+ g_warning ("Error loading image attribution file: %s", error->message);
+ gtk_widget_destroy (attribution);
+ return;
+ }
+ gtk_widget_set_hexpand (attribution, TRUE);
+ gtk_widget_set_vexpand (attribution, TRUE);
+ g_signal_connect (attribution, "show-uri",
+ G_CALLBACK (on_attribution_show_uri), self);
+ gtk_widget_show_all (attribution);
+
+ dialog = g_object_new (GTK_TYPE_DIALOG,
+ "default-height", _CREDITS_DIALOG_DEFAULT_HEIGHT,
+ "default-width", _CREDITS_DIALOG_DEFAULT_WIDTH,
+ "destroy-with-parent", TRUE,
+ "modal", TRUE,
+ "title", _("Image credits"),
+ "transient-for", GTK_WINDOW (priv->main_application_window),
+ "use-header-bar", TRUE,
+ NULL);
+ content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_container_add (GTK_CONTAINER (content), attribution);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
static void
eos_application_get_property (GObject *object,
guint property_id,
@@ -80,6 +314,30 @@ eos_application_get_property (GObject *object,
g_value_set_object (value, eos_application_get_config_dir (self));
break;
+ case PROP_IMAGE_ATTRIBUTION_FILE:
+ g_value_set_object (value, eos_application_get_image_attribution_file (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+eos_application_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EosApplication *self = EOS_APPLICATION (object);
+
+ switch (property_id)
+ {
+ case PROP_IMAGE_ATTRIBUTION_FILE:
+ eos_application_set_image_attribution_file (self,
+ g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -91,6 +349,7 @@ eos_application_finalize (GObject *object)
EosApplication *self = EOS_APPLICATION (object);
EosApplicationPrivate *priv = eos_application_get_instance_private (self);
g_clear_object (&priv->config_dir);
+ g_clear_object (&priv->image_attribution_file);
G_OBJECT_CLASS (eos_application_parent_class)->finalize (object);
}
@@ -172,6 +431,12 @@ eos_application_startup (GApplication *application)
{
G_APPLICATION_CLASS (eos_application_parent_class)->startup (application);
+ /* Set up the hotkey for the image credit dialog */
+ static const gchar * const accelerators[] = { "<Primary><Shift>a", NULL };
+ gtk_application_set_accels_for_action (GTK_APPLICATION (application),
+ "app.image-credits",
+ accelerators);
+
GtkCssProvider *provider = gtk_css_provider_new ();
/* Reset CSS for SDK applications and apply our own theme on top of it. This
@@ -265,6 +530,7 @@ eos_application_class_init (EosApplicationClass *klass)
GtkApplicationClass *gtk_application_class = GTK_APPLICATION_CLASS (klass);
object_class->get_property = eos_application_get_property;
+ object_class->set_property = eos_application_set_property;
object_class->finalize = eos_application_finalize;
g_application_class->activate = eos_application_activate;
g_application_class->startup = eos_application_startup;
@@ -285,6 +551,63 @@ eos_application_class_init (EosApplicationClass *klass)
"User configuration directory for this application",
G_TYPE_FILE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ /**
+ * EosApplication:image-attribution-file:
+ *
+ * A #GFile handle to a file for storing attribution information for the
+ * images included in this application's resource file.
+ *
+ * This attribution file must be a JSON file.
+ * Here is an example of the required format:
+ * |[
+ * [
+ * {
+ * "resource_path": "/com/example/smokegrinder/image1.jpg",
+ * "license": "Public domain",
+ * "uri": "http://www.photos.com/photos/12345",
+ * "comment": "No known copyright restrictions"
+ * },
+ * {
+ * "resource_path": "/com/example/smokegrinder/image2.jpg",
+ * "license_uri": "http://example.com/image-license",
+ * "uri": "http://www.photos.com/photos/54321",
+ * "credit": "Edward X. Ample",
+ * "credit_contact": "http://www.photos.com/users/example"
+ * },
+ * {
+ * "resource_path": "/com/example/smokegrinder/image3.jpg",
+ * "copyright_holder": "Jane Q. Hacker",
+ * "copyright_year": 2014,
+ * "permission": true
+ * }
+ * ]
+ * ]|
+ *
+ * The JSON object is an array of objects that each contain information about
+ * one image.
+ * The only required property is <code>resource_path</code>, which is the path
+ * to the image in the resource file.
+ *
+ * The recognized properties are shown in <xref
+ * linkend="image-attribution-properties"/>.
+ *
+ * Nothing is guaranteed about how the application uses this information.
+ * It can display it to the user or make it available to other programs.
+ *
+ * <note><para>
+ * Currently, pressing <keycombo><keycap>Control</keycap>
+ * <keycap>Shift</keycap><keycap>A</keycap></keycombo> brings up a credits
+ * dialog.
+ * This is liable to change in future versions.
+ * </para></note>
+ *
+ * Since: 0.2
+ */
+ eos_application_props[PROP_IMAGE_ATTRIBUTION_FILE] =
+ g_param_spec_object ("image-attribution-file", "Image attribution file",
+ "File with attribution information for images in this application",
+ G_TYPE_FILE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, NPROPS,
eos_application_props);
@@ -295,6 +618,14 @@ eos_application_init (EosApplication *self)
{
EosApplicationPrivate *priv = eos_application_get_instance_private (self);
priv->init_config_dir_once = (GOnce)G_ONCE_INIT;
+
+ /* Set up app actions */
+ static const GActionEntry actions[] = {
+ { "image-credits", on_image_credits_activate },
+ };
+ g_action_map_add_action_entries (G_ACTION_MAP (self), actions,
+ G_N_ELEMENTS (actions), self);
+
g_signal_connect (self, "notify::application-id",
G_CALLBACK (on_app_id_set), self);
}
@@ -353,3 +684,70 @@ eos_application_get_config_dir (EosApplication *self)
(GThreadFunc)ensure_config_dir_exists_and_is_writable, self);
return priv->config_dir;
}
+
+/**
+ * eos_application_get_image_attribution_file:
+ * @self: the application
+ *
+ * Gets a #GFile pointing to a JSON file containing credits for images included
+ * in the app's resources.
+ * See #EosApplication:image-attribution-file.
+ *
+ * Returns: (transfer none) (allow-none): A #GFile pointing to the image
+ * attribution file, or %NULL if one has not been set.
+ *
+ * Since: 0.2
+ */
+GFile *
+eos_application_get_image_attribution_file (EosApplication *self)
+{
+ g_return_val_if_fail (self != NULL && EOS_IS_APPLICATION (self), NULL);
+
+ EosApplicationPrivate *priv = eos_application_get_instance_private (self);
+ return priv->image_attribution_file;
+}
+
+/**
+ * eos_application_set_image_attribution_file:
+ * @self: the application
+ * @file: (allow-none): a #GFile pointing to a file in the proper format, or
+ * %NULL to unset.
+ *
+ * You can provide attribution and credit for images included in the application
+ * by giving this function a JSON file with image credits.
+ * See #EosApplication:image-attribution-file for the JSON file's required
+ * format.
+ *
+ * Since: 0.2
+ */
+void
+eos_application_set_image_attribution_file (EosApplication *self,
+ GFile *file)
+{
+ g_return_if_fail (self != NULL && EOS_IS_APPLICATION (self));
+ g_return_if_fail (file == NULL || G_IS_FILE (file));
+
+ EosApplicationPrivate *priv = eos_application_get_instance_private (self);
+
+ if (priv->image_attribution_file == file ||
+ (priv->image_attribution_file != NULL && file != NULL &&
+ g_file_equal (file, priv->image_attribution_file)))
+ return;
+
+ if (priv->image_attribution_file == NULL || file == NULL)
+ {
+ GAction *action = g_action_map_lookup_action (G_ACTION_MAP (self),
+ "image-credits");
+ gboolean enabled = (file == NULL);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (self), enabled);
+ /* action map owns action */
+ }
+
+ g_clear_object (&priv->image_attribution_file);
+ if (file != NULL)
+ g_object_ref (file);
+ priv->image_attribution_file = file;
+
+ g_object_notify_by_pspec (G_OBJECT (self),
+ eos_application_props[PROP_IMAGE_ATTRIBUTION_FILE]);
+}