1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
3  *
4  * gimperrorconsole.c
5  * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
6  *
7  * partly based on errorconsole.c
8  * Copyright (C) 1998 Nick Fetchak <nuke@bayside.net>
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28 
29 #include "libgimpbase/gimpbase.h"
30 #include "libgimpwidgets/gimpwidgets.h"
31 
32 #include "widgets-types.h"
33 
34 #include "core/gimp.h"
35 
36 #include "gimpdocked.h"
37 #include "gimperrorconsole.h"
38 #include "gimpmenufactory.h"
39 #include "gimpsessioninfo-aux.h"
40 #include "gimptextbuffer.h"
41 #include "gimpwidgets-utils.h"
42 
43 #include "gimp-intl.h"
44 
45 
46 static const gboolean default_highlight[] =
47 {
48   [GIMP_MESSAGE_ERROR]   = TRUE,
49   [GIMP_MESSAGE_WARNING] = TRUE,
50   [GIMP_MESSAGE_INFO]    = FALSE
51 };
52 
53 
54 static void       gimp_error_console_docked_iface_init (GimpDockedInterface *iface);
55 
56 static void       gimp_error_console_constructed       (GObject          *object);
57 static void       gimp_error_console_dispose           (GObject          *object);
58 
59 static void       gimp_error_console_unmap             (GtkWidget        *widget);
60 
61 static gboolean   gimp_error_console_button_press      (GtkWidget        *widget,
62                                                         GdkEventButton   *event,
63                                                         GimpErrorConsole *console);
64 
65 static void       gimp_error_console_set_aux_info      (GimpDocked       *docked,
66                                                         GList            *aux_info);
67 static GList    * gimp_error_console_get_aux_info      (GimpDocked       *docked);
68 
69 
70 G_DEFINE_TYPE_WITH_CODE (GimpErrorConsole, gimp_error_console, GIMP_TYPE_EDITOR,
71                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
72                                                 gimp_error_console_docked_iface_init))
73 
74 #define parent_class gimp_error_console_parent_class
75 
76 static GimpDockedInterface *parent_docked_iface = NULL;
77 
78 
79 static void
gimp_error_console_class_init(GimpErrorConsoleClass * klass)80 gimp_error_console_class_init (GimpErrorConsoleClass *klass)
81 {
82   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
83   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
84 
85   object_class->constructed = gimp_error_console_constructed;
86   object_class->dispose     = gimp_error_console_dispose;
87 
88   widget_class->unmap       = gimp_error_console_unmap;
89 }
90 
91 static void
gimp_error_console_init(GimpErrorConsole * console)92 gimp_error_console_init (GimpErrorConsole *console)
93 {
94   GtkWidget *scrolled_window;
95 
96   console->text_buffer = GTK_TEXT_BUFFER (gimp_text_buffer_new ());
97 
98   gtk_text_buffer_create_tag (console->text_buffer, "title",
99                               "scale",  PANGO_SCALE_LARGE,
100                               "weight", PANGO_WEIGHT_BOLD,
101                               NULL);
102   gtk_text_buffer_create_tag (console->text_buffer, "message",
103                               NULL);
104 
105   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
106   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
107                                   GTK_POLICY_AUTOMATIC,
108                                   GTK_POLICY_AUTOMATIC);
109   gtk_box_pack_start (GTK_BOX (console), scrolled_window, TRUE, TRUE, 0);
110   gtk_widget_show (scrolled_window);
111 
112   console->text_view = gtk_text_view_new_with_buffer (console->text_buffer);
113   g_object_unref (console->text_buffer);
114 
115   gtk_text_view_set_editable (GTK_TEXT_VIEW (console->text_view), FALSE);
116   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (console->text_view),
117                                GTK_WRAP_WORD);
118   gtk_container_add (GTK_CONTAINER (scrolled_window), console->text_view);
119   gtk_widget_show (console->text_view);
120 
121   g_signal_connect (console->text_view, "button-press-event",
122                     G_CALLBACK (gimp_error_console_button_press),
123                     console);
124 
125   console->file_dialog = NULL;
126 
127   memcpy (console->highlight, default_highlight, sizeof (default_highlight));
128 }
129 
130 static void
gimp_error_console_docked_iface_init(GimpDockedInterface * iface)131 gimp_error_console_docked_iface_init (GimpDockedInterface *iface)
132 {
133   parent_docked_iface = g_type_interface_peek_parent (iface);
134 
135   if (! parent_docked_iface)
136     parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
137 
138   iface->set_aux_info = gimp_error_console_set_aux_info;
139   iface->get_aux_info = gimp_error_console_get_aux_info;
140 }
141 
142 static void
gimp_error_console_constructed(GObject * object)143 gimp_error_console_constructed (GObject *object)
144 {
145   GimpErrorConsole *console = GIMP_ERROR_CONSOLE (object);
146 
147   G_OBJECT_CLASS (parent_class)->constructed (object);
148 
149   console->clear_button =
150     gimp_editor_add_action_button (GIMP_EDITOR (console), "error-console",
151                                    "error-console-clear", NULL);
152 
153   console->save_button =
154     gimp_editor_add_action_button (GIMP_EDITOR (console), "error-console",
155                                    "error-console-save-all",
156                                    "error-console-save-selection",
157                                    GDK_SHIFT_MASK,
158                                    NULL);
159 }
160 
161 static void
gimp_error_console_dispose(GObject * object)162 gimp_error_console_dispose (GObject *object)
163 {
164   GimpErrorConsole *console = GIMP_ERROR_CONSOLE (object);
165 
166   if (console->file_dialog)
167     gtk_widget_destroy (console->file_dialog);
168 
169   console->gimp->message_handler = GIMP_MESSAGE_BOX;
170 
171   G_OBJECT_CLASS (parent_class)->dispose (object);
172 }
173 
174 static void
gimp_error_console_unmap(GtkWidget * widget)175 gimp_error_console_unmap (GtkWidget *widget)
176 {
177   GimpErrorConsole *console = GIMP_ERROR_CONSOLE (widget);
178 
179   if (console->file_dialog)
180     gtk_widget_destroy (console->file_dialog);
181 
182   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
183 }
184 
185 GtkWidget *
gimp_error_console_new(Gimp * gimp,GimpMenuFactory * menu_factory)186 gimp_error_console_new (Gimp            *gimp,
187                         GimpMenuFactory *menu_factory)
188 {
189   GimpErrorConsole *console;
190 
191   g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
192   g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
193 
194   console = g_object_new (GIMP_TYPE_ERROR_CONSOLE,
195                           "menu-factory",   menu_factory,
196                           "menu-identifier", "<ErrorConsole>",
197                           "ui-path",        "/error-console-popup",
198                           NULL);
199 
200   console->gimp = gimp;
201 
202   console->gimp->message_handler = GIMP_ERROR_CONSOLE;
203 
204   return GTK_WIDGET (console);
205 }
206 
207 void
gimp_error_console_add(GimpErrorConsole * console,GimpMessageSeverity severity,const gchar * domain,const gchar * message)208 gimp_error_console_add (GimpErrorConsole    *console,
209                         GimpMessageSeverity  severity,
210                         const gchar         *domain,
211                         const gchar         *message)
212 {
213   const gchar *desc;
214   GtkTextIter  end;
215   GtkTextMark *end_mark;
216   GdkPixbuf   *pixbuf;
217   gchar       *str;
218 
219   g_return_if_fail (GIMP_IS_ERROR_CONSOLE (console));
220   g_return_if_fail (domain != NULL);
221   g_return_if_fail (message != NULL);
222 
223   gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
224                        NULL, NULL, &desc, NULL);
225 
226   gtk_text_buffer_get_end_iter (console->text_buffer, &end);
227 
228   pixbuf = gimp_widget_load_icon (GTK_WIDGET (console),
229                                   gimp_get_message_icon_name (severity), 20);
230   gtk_text_buffer_insert_pixbuf (console->text_buffer, &end, pixbuf);
231   g_object_unref (pixbuf);
232 
233   gtk_text_buffer_insert (console->text_buffer, &end, "  ", -1);
234 
235   str = g_strdup_printf ("%s %s", domain, desc);
236   gtk_text_buffer_insert_with_tags_by_name (console->text_buffer, &end,
237                                             str, -1,
238                                             "title",
239                                             NULL);
240   g_free (str);
241 
242   gtk_text_buffer_insert (console->text_buffer, &end, "\n", -1);
243 
244   gtk_text_buffer_insert_with_tags_by_name (console->text_buffer, &end,
245                                             message, -1,
246                                             "message",
247                                             NULL);
248 
249   gtk_text_buffer_insert (console->text_buffer, &end, "\n\n", -1);
250 
251   end_mark = gtk_text_buffer_create_mark (console->text_buffer,
252                                           NULL, &end, TRUE);
253   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (console->text_view), end_mark,
254                                 FALSE, TRUE, 1.0, 0.0);
255   gtk_text_buffer_delete_mark (console->text_buffer, end_mark);
256 }
257 
258 
259 /*  private functions  */
260 
261 static gboolean
gimp_error_console_button_press(GtkWidget * widget,GdkEventButton * bevent,GimpErrorConsole * console)262 gimp_error_console_button_press (GtkWidget        *widget,
263                                  GdkEventButton   *bevent,
264                                  GimpErrorConsole *console)
265 {
266   if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
267     {
268       return gimp_editor_popup_menu (GIMP_EDITOR (console), NULL, NULL);
269     }
270 
271   return FALSE;
272 }
273 
274 static const gchar * const aux_info_highlight[] =
275 {
276   [GIMP_MESSAGE_ERROR]   = "highlight-error",
277   [GIMP_MESSAGE_WARNING] = "highlight-warning",
278   [GIMP_MESSAGE_INFO]    = "highlight-info"
279 };
280 
281 static void
gimp_error_console_set_aux_info(GimpDocked * docked,GList * aux_info)282 gimp_error_console_set_aux_info (GimpDocked *docked,
283                                  GList      *aux_info)
284 {
285   GimpErrorConsole *console = GIMP_ERROR_CONSOLE (docked);
286   GList            *list;
287 
288   parent_docked_iface->set_aux_info (docked, aux_info);
289 
290   for (list = aux_info; list; list = g_list_next (list))
291     {
292       GimpSessionInfoAux *aux = list->data;
293       gint                i;
294 
295       for (i = 0; i < G_N_ELEMENTS (aux_info_highlight); i++)
296         {
297           if (! strcmp (aux->name, aux_info_highlight[i]))
298             {
299               console->highlight[i] = ! strcmp (aux->value, "yes");
300               break;
301             }
302         }
303     }
304 }
305 
306 static GList *
gimp_error_console_get_aux_info(GimpDocked * docked)307 gimp_error_console_get_aux_info (GimpDocked *docked)
308 {
309   GimpErrorConsole   *console = GIMP_ERROR_CONSOLE (docked);
310   GList              *aux_info;
311   gint                i;
312 
313   aux_info = parent_docked_iface->get_aux_info (docked);
314 
315   for (i = 0; i < G_N_ELEMENTS (aux_info_highlight); i++)
316     {
317       GimpSessionInfoAux *aux;
318 
319       aux = gimp_session_info_aux_new (aux_info_highlight[i],
320                                        console->highlight[i] ? "yes" : "no");
321 
322       aux_info = g_list_append (aux_info, aux);
323     }
324 
325   return aux_info;
326 }
327