/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis * * pikadevicestatus.c * Copyright (C) 2003 Michael Natterer * * This program 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. * * This program 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 this program. If not, see . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "config/pikaguiconfig.h" #include "core/pika.h" #include "core/pikabrush.h" #include "core/pikacontext.h" #include "core/pikadatafactory.h" #include "core/pikagradient.h" #include "core/pikalist.h" #include "core/pikapattern.h" #include "core/pikatoolinfo.h" #include "pikadnd.h" #include "pikadeviceinfo.h" #include "pikadevicemanager.h" #include "pikadevices.h" #include "pikadevicestatus.h" #include "pikadialogfactory.h" #include "pikapropwidgets.h" #include "pikaview.h" #include "pikawidgets-utils.h" #include "pikawindowstrategy.h" #include "pika-intl.h" #define CELL_SIZE 20 /* The size of the view cells */ enum { PROP_0, PROP_PIKA }; struct _PikaDeviceStatusEntry { PikaDeviceInfo *device_info; PikaContext *context; PikaToolOptions *tool_options; GtkWidget *ebox; GtkWidget *options_hbox; GtkWidget *tool; GtkWidget *foreground; GtkWidget *foreground_none; GtkWidget *background; GtkWidget *background_none; GtkWidget *brush; GtkWidget *brush_none; GtkWidget *pattern; GtkWidget *pattern_none; GtkWidget *gradient; GtkWidget *gradient_none; }; static void pika_device_status_constructed (GObject *object); static void pika_device_status_dispose (GObject *object); static void pika_device_status_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_device_status_device_add (PikaContainer *devices, PikaDeviceInfo *device_info, PikaDeviceStatus *status); static void pika_device_status_device_remove (PikaContainer *devices, PikaDeviceInfo *device_info, PikaDeviceStatus *status); static void pika_device_status_notify_device (PikaDeviceManager *manager, const GParamSpec *pspec, PikaDeviceStatus *status); static void pika_device_status_config_notify (PikaGuiConfig *config, const GParamSpec *pspec, PikaDeviceStatus *status); static void pika_device_status_notify_info (PikaDeviceInfo *device_info, const GParamSpec *pspec, PikaDeviceStatusEntry *entry); static void pika_device_status_save_clicked (GtkWidget *button, PikaDeviceStatus *status); static void pika_device_status_view_clicked (GtkWidget *widget, GdkModifierType state, const gchar *identifier); G_DEFINE_TYPE (PikaDeviceStatus, pika_device_status, PIKA_TYPE_EDITOR) #define parent_class pika_device_status_parent_class static void pika_device_status_class_init (PikaDeviceStatusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = pika_device_status_constructed; object_class->dispose = pika_device_status_dispose; object_class->set_property = pika_device_status_set_property; g_object_class_install_property (object_class, PROP_PIKA, g_param_spec_object ("pika", NULL, NULL, PIKA_TYPE_PIKA, PIKA_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } static void pika_device_status_init (PikaDeviceStatus *status) { status->pika = NULL; status->current_device = NULL; status->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_set_border_width (GTK_CONTAINER (status->vbox), 2); gtk_box_pack_start (GTK_BOX (status), status->vbox, TRUE, TRUE, 0); gtk_widget_show (status->vbox); status->save_button = pika_editor_add_button (PIKA_EDITOR (status), PIKA_ICON_DOCUMENT_SAVE, _("Save device status"), NULL, G_CALLBACK (pika_device_status_save_clicked), NULL, G_OBJECT (status)); } static void pika_device_status_constructed (GObject *object) { PikaDeviceStatus *status = PIKA_DEVICE_STATUS (object); PikaContainer *devices; GList *list; G_OBJECT_CLASS (parent_class)->constructed (object); pika_assert (PIKA_IS_PIKA (status->pika)); devices = PIKA_CONTAINER (pika_devices_get_manager (status->pika)); for (list = PIKA_LIST (devices)->queue->head; list; list = list->next) pika_device_status_device_add (devices, list->data, status); g_signal_connect_object (devices, "add", G_CALLBACK (pika_device_status_device_add), status, 0); g_signal_connect_object (devices, "remove", G_CALLBACK (pika_device_status_device_remove), status, 0); g_signal_connect (devices, "notify::current-device", G_CALLBACK (pika_device_status_notify_device), status); pika_device_status_notify_device (PIKA_DEVICE_MANAGER (devices), NULL, status); g_signal_connect_object (status->pika->config, "notify::devices-share-tool", G_CALLBACK (pika_device_status_config_notify), status, 0); pika_device_status_config_notify (PIKA_GUI_CONFIG (status->pika->config), NULL, status); } static void pika_device_status_dispose (GObject *object) { PikaDeviceStatus *status = PIKA_DEVICE_STATUS (object); if (status->devices) { GList *list; for (list = status->devices; list; list = list->next) { PikaDeviceStatusEntry *entry = list->data; g_signal_handlers_disconnect_by_func (entry->device_info, pika_device_status_notify_info, entry); g_object_unref (entry->context); g_slice_free (PikaDeviceStatusEntry, entry); } g_list_free (status->devices); status->devices = NULL; g_signal_handlers_disconnect_by_func (pika_devices_get_manager (status->pika), pika_device_status_notify_device, status); } G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_device_status_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaDeviceStatus *status = PIKA_DEVICE_STATUS (object); switch (property_id) { case PROP_PIKA: status->pika = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pack_prop_widget (GtkBox *hbox, GtkWidget *widget, GtkWidget **none_widget) { GtkSizeGroup *size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, widget); gtk_widget_show (widget); *none_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (hbox), *none_widget, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, *none_widget); g_object_unref (size_group); } static void pika_device_status_device_add (PikaContainer *devices, PikaDeviceInfo *device_info, PikaDeviceStatus *status) { PikaDeviceStatusEntry *entry; GdkDisplay *display; GClosure *closure; GParamSpec *pspec; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; gchar *name; entry = g_slice_new0 (PikaDeviceStatusEntry); status->devices = g_list_prepend (status->devices, entry); entry->device_info = device_info; entry->context = pika_context_new (PIKA_TOOL_PRESET (device_info)->pika, pika_object_get_name (device_info), NULL); pika_context_define_properties (entry->context, PIKA_CONTEXT_PROP_MASK_TOOL | PIKA_CONTEXT_PROP_MASK_FOREGROUND | PIKA_CONTEXT_PROP_MASK_BACKGROUND | PIKA_CONTEXT_PROP_MASK_BRUSH | PIKA_CONTEXT_PROP_MASK_PATTERN | PIKA_CONTEXT_PROP_MASK_GRADIENT, FALSE); closure = g_cclosure_new (G_CALLBACK (pika_device_status_notify_info), entry, NULL); g_object_watch_closure (G_OBJECT (status), closure); g_signal_connect_closure (device_info, "notify", closure, FALSE); entry->ebox = gtk_event_box_new (); gtk_box_pack_start (GTK_BOX (status->vbox), entry->ebox, FALSE, FALSE, 0); gtk_widget_show (entry->ebox); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); gtk_container_add (GTK_CONTAINER (entry->ebox), vbox); gtk_widget_show (vbox); /* the device name */ pika_device_info_get_device (device_info, &display); if (display == NULL || display == gdk_display_get_default ()) name = g_strdup (pika_object_get_name (device_info)); else name = g_strdup_printf ("%s (%s)", pika_object_get_name (device_info), gdk_display_get_name (display)); label = gtk_label_new (name); g_free (name); gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); pika_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1); gtk_label_set_xalign (GTK_LABEL (label), 0.0); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); gtk_widget_show (label); /* the row of properties */ hbox = entry->options_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); /* the tool */ entry->tool = pika_prop_view_new (G_OBJECT (entry->context), "tool", entry->context, CELL_SIZE); gtk_box_pack_start (GTK_BOX (hbox), entry->tool, FALSE, FALSE, 0); /* the foreground color */ entry->foreground = pika_prop_color_area_new (G_OBJECT (entry->context), "foreground", CELL_SIZE, CELL_SIZE, PIKA_COLOR_AREA_FLAT); gtk_widget_add_events (entry->foreground, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); pack_prop_widget (GTK_BOX (hbox), entry->foreground, &entry->foreground_none); /* the background color */ entry->background = pika_prop_color_area_new (G_OBJECT (entry->context), "background", CELL_SIZE, CELL_SIZE, PIKA_COLOR_AREA_FLAT); gtk_widget_add_events (entry->background, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); pack_prop_widget (GTK_BOX (hbox), entry->background, &entry->background_none); /* the brush */ entry->brush = pika_prop_view_new (G_OBJECT (entry->context), "brush", entry->context, CELL_SIZE); PIKA_VIEW (entry->brush)->clickable = TRUE; PIKA_VIEW (entry->brush)->show_popup = TRUE; pack_prop_widget (GTK_BOX (hbox), entry->brush, &entry->brush_none); g_signal_connect (entry->brush, "clicked", G_CALLBACK (pika_device_status_view_clicked), "pika-brush-grid|pika-brush-list"); /* the pattern */ entry->pattern = pika_prop_view_new (G_OBJECT (entry->context), "pattern", entry->context, CELL_SIZE); PIKA_VIEW (entry->pattern)->clickable = TRUE; PIKA_VIEW (entry->pattern)->show_popup = TRUE; pack_prop_widget (GTK_BOX (hbox), entry->pattern, &entry->pattern_none); g_signal_connect (entry->pattern, "clicked", G_CALLBACK (pika_device_status_view_clicked), "pika-pattern-grid|pika-pattern-list"); /* the gradient */ entry->gradient = pika_prop_view_new (G_OBJECT (entry->context), "gradient", entry->context, 2 * CELL_SIZE); PIKA_VIEW (entry->gradient)->clickable = TRUE; PIKA_VIEW (entry->gradient)->show_popup = TRUE; pack_prop_widget (GTK_BOX (hbox), entry->gradient, &entry->gradient_none); g_signal_connect (entry->gradient, "clicked", G_CALLBACK (pika_device_status_view_clicked), "pika-gradient-list|pika-gradient-grid"); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (device_info), "tool-options"); pika_device_status_notify_info (device_info, pspec, entry); } static void pika_device_status_device_remove (PikaContainer *devices, PikaDeviceInfo *device_info, PikaDeviceStatus *status) { GList *list; for (list = status->devices; list; list = list->next) { PikaDeviceStatusEntry *entry = list->data; if (entry->device_info == device_info) { status->devices = g_list_remove (status->devices, entry); g_signal_handlers_disconnect_by_func (entry->device_info, pika_device_status_notify_info, entry); g_object_unref (entry->context); g_slice_free (PikaDeviceStatusEntry, entry); return; } } } GtkWidget * pika_device_status_new (Pika *pika) { g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); return g_object_new (PIKA_TYPE_DEVICE_STATUS, "pika", pika, NULL); } /* private functions */ static void pika_device_status_notify_device (PikaDeviceManager *manager, const GParamSpec *pspec, PikaDeviceStatus *status) { GList *list; status->current_device = pika_device_manager_get_current_device (manager); for (list = status->devices; list; list = list->next) { PikaDeviceStatusEntry *entry = list->data; GtkWidget *widget = entry->ebox; GtkStyleContext *style = gtk_widget_get_style_context (widget); if (entry->device_info == status->current_device) { gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, TRUE); gtk_style_context_add_class (style, GTK_STYLE_CLASS_VIEW); } else { gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_NORMAL, TRUE); gtk_style_context_remove_class (style, GTK_STYLE_CLASS_VIEW); } } } static void pika_device_status_config_notify (PikaGuiConfig *config, const GParamSpec *pspec, PikaDeviceStatus *status) { gboolean show_options; GList *list; show_options = ! PIKA_GUI_CONFIG (status->pika->config)->devices_share_tool; for (list = status->devices; list; list = list->next) { PikaDeviceStatusEntry *entry = list->data; gtk_widget_set_visible (entry->options_hbox, show_options); } } static void toggle_prop_visible (GtkWidget *widget, GtkWidget *widget_none, gboolean available) { gtk_widget_set_visible (widget, available); gtk_widget_set_visible (widget_none, ! available); } static void pika_device_status_notify_info (PikaDeviceInfo *device_info, const GParamSpec *pspec, PikaDeviceStatusEntry *entry) { PikaToolOptions *tool_options = PIKA_TOOL_PRESET (device_info)->tool_options; if (tool_options != entry->tool_options) { PikaContextPropMask serialize_props; entry->tool_options = tool_options; pika_context_set_parent (entry->context, PIKA_CONTEXT (tool_options)); serialize_props = pika_context_get_serialize_properties (PIKA_CONTEXT (tool_options)); toggle_prop_visible (entry->foreground, entry->foreground_none, serialize_props & PIKA_CONTEXT_PROP_MASK_FOREGROUND); toggle_prop_visible (entry->background, entry->background_none, serialize_props & PIKA_CONTEXT_PROP_MASK_BACKGROUND); toggle_prop_visible (entry->brush, entry->brush_none, serialize_props & PIKA_CONTEXT_PROP_MASK_BRUSH); toggle_prop_visible (entry->pattern, entry->pattern_none, serialize_props & PIKA_CONTEXT_PROP_MASK_PATTERN); toggle_prop_visible (entry->gradient, entry->gradient_none, serialize_props & PIKA_CONTEXT_PROP_MASK_GRADIENT); } if (! pika_device_info_get_device (device_info, NULL) || pika_device_info_get_mode (device_info) == GDK_MODE_DISABLED) { gtk_widget_hide (entry->ebox); } else { gtk_widget_show (entry->ebox); } if (! strcmp (pspec->name, "tool-options")) { PikaRGB color; guchar r, g, b; gchar buf[64]; pika_context_get_foreground (entry->context, &color); pika_rgb_get_uchar (&color, &r, &g, &b); g_snprintf (buf, sizeof (buf), _("Foreground: %d, %d, %d"), r, g, b); pika_help_set_help_data (entry->foreground, buf, NULL); pika_context_get_background (entry->context, &color); pika_rgb_get_uchar (&color, &r, &g, &b); g_snprintf (buf, sizeof (buf), _("Background: %d, %d, %d"), r, g, b); pika_help_set_help_data (entry->background, buf, NULL); } } static void pika_device_status_save_clicked (GtkWidget *button, PikaDeviceStatus *status) { pika_devices_save (status->pika, TRUE); } static void pika_device_status_view_clicked (GtkWidget *widget, GdkModifierType state, const gchar *identifier) { PikaDeviceStatus *status; PikaDialogFactory *dialog_factory; status = PIKA_DEVICE_STATUS (gtk_widget_get_ancestor (widget, PIKA_TYPE_DEVICE_STATUS)); dialog_factory = pika_dialog_factory_get_singleton (); pika_window_strategy_show_dockable_dialog (PIKA_WINDOW_STRATEGY (pika_get_window_strategy (status->pika)), status->pika, dialog_factory, pika_widget_get_monitor (widget), identifier); }