diff options
Diffstat (limited to 'endless/eosapplication.c')
-rw-r--r-- | endless/eosapplication.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/endless/eosapplication.c b/endless/eosapplication.c index 72016c1..510ff3b 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,65 @@ 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_sync (priv->image_attribution_file, NULL, + &error); + if (attribution == NULL) + { + g_warning ("Error loading image attribution file: %s", error->message); + 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 +312,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 +347,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 +429,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 +528,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,16 +549,92 @@ 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); } static void +set_image_credits_action_enabled (EosApplication *self, + gboolean enabled) +{ + GAction *action = g_action_map_lookup_action (G_ACTION_MAP (self), + "image-credits"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); + /* action map owns action */ +} + +static void 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); + set_image_credits_action_enabled (self, FALSE); + g_signal_connect (self, "notify::application-id", G_CALLBACK (on_app_id_set), self); } @@ -353,3 +693,67 @@ 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) + { + gboolean enabled = (file != NULL); + set_image_credits_action_enabled (self, enabled); + } + + 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]); +} |