1 /* gEDA - GPL Electronic Design Automation
2  * gschem - gEDA Schematic Capture
3  * Copyright (C) 1998-2010 Ales Hvezda
4  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * \file x_log.c
23  * \brief GType class and functions to support the gschem log window.
24  */
25 
26 #include <config.h>
27 
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef HAVE_FCNTL_H
38 #include <fcntl.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 
44 #include "gschem.h"
45 
46 #ifdef HAVE_LIBDMALLOC
47 #include <dmalloc.h>
48 #endif
49 
50 static void x_log_callback_response (GtkDialog *dialog,
51                                      gint arg1,
52                                      gpointer user_data);
53 static void log_message (Log *log,
54                          const gchar *message,
55                          const gchar *style);
56 
57 static void log_class_init (LogClass *class);
58 static void log_init       (Log *log);
59 
60 static GtkWidget *log_dialog = NULL;
61 
62 /*!
63  *  \brief Open the Log window
64  *
65  *  \par Function Description
66  *  If the log dialog instance doesn't exist, create it, and read
67  *  the current log file contents (if they exist) and insert
68  *  them into the log dialog.
69  *
70  *  If the log dialog instance does exist, present it to the user.
71  */
x_log_open()72 void x_log_open ()
73 {
74   if (log_dialog == NULL) {
75     gchar *contents;
76 
77     log_dialog = GTK_WIDGET (g_object_new (TYPE_LOG,
78                                            /* GtkWindow */
79                                            "type", GTK_WINDOW_TOPLEVEL,
80                                            /* GschemDialog */
81                                            "settings-name", "log",
82                                            /* "toplevel", TOPEVEL * */
83                                            NULL));
84 
85     g_signal_connect (log_dialog,
86                       "response",
87                       G_CALLBACK (x_log_callback_response),
88                       NULL);
89 
90     /* make it read the content of the current log file */
91     /* and add its contents to the dialog */
92     contents = s_log_read ();
93 
94     /* s_log_read can return NULL if the log file cannot be written to */
95     if (contents == NULL)
96     {
97       return;
98     }
99 
100     log_message (LOG (log_dialog), contents, "old");
101     g_free (contents);
102 
103     x_log_update_func = x_log_message;
104 
105     if( auto_place_mode )
106 	gtk_widget_set_uposition( log_dialog, 10, 10);
107     gtk_widget_show (log_dialog);
108   } else {
109     g_assert (IS_LOG (log_dialog));
110     gtk_window_present ((GtkWindow*)log_dialog);
111   }
112 
113 }
114 
115 /*!
116  *  \brief Close the Log window
117  *  \par Function Description
118  *  If the log window exists, destroy it.
119  */
x_log_close()120 void x_log_close ()
121 {
122   if (log_dialog) {
123     g_assert (IS_LOG (log_dialog));
124     gtk_widget_destroy (log_dialog);
125     x_log_update_func = NULL;
126     log_dialog = NULL;
127   }
128 
129 }
130 
131 /*!
132  *  \brief Add a message to the Log window
133  *  \par Function Description
134  *  Add a message to the Log window.
135  *  Calls log_message() to do the actual logging.
136  *  \param [in] log_domain
137  *  \param [in] log_level The severity of the message
138  *  \param [in] message   The message to be displayed
139  */
x_log_message(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message)140 void x_log_message (const gchar *log_domain, GLogLevelFlags log_level,
141                     const gchar *message)
142 {
143   gchar *style;
144   g_return_if_fail (log_dialog != NULL);
145 
146   if (log_level & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR)) {
147     style = "critical";
148   } else if (log_level & G_LOG_LEVEL_WARNING) {
149     style = "warning";
150   } else {
151     style = "message";
152   }
153 
154   log_message (LOG(log_dialog), message, style);
155 }
156 
157 /*!
158  *  \brief Log window callback function
159  *
160  *  \par Function Description
161  *  Callback function for the Log window. Only used to close the window.
162  */
x_log_callback_response(GtkDialog * dialog,gint arg1,gpointer user_data)163 static void x_log_callback_response (GtkDialog *dialog,
164 				     gint arg1,
165 				     gpointer user_data)
166 {
167   switch (arg1) {
168     case GTK_RESPONSE_DELETE_EVENT:
169     case LOG_RESPONSE_CLOSE:
170     g_assert (GTK_WIDGET (dialog) == log_dialog);
171     x_log_close ();
172     break;
173     default:
174     g_assert_not_reached ();
175   }
176 
177 }
178 
179 /*!
180  *  \brief Add a message to the log window
181  *
182  *  \par Function Description
183  *  \param [in] log The log instance
184  *  \param [in] message The message to be logged
185  *  \param [in] style   The style to use in the text rendering
186  */
log_message(Log * log,const gchar * message,const gchar * style)187 static void log_message (Log *log, const gchar *message,
188                          const gchar *style)
189 {
190   GtkTextBuffer *buffer;
191   GtkTextIter iter;
192   GtkTextMark *mark;
193 
194   g_return_if_fail (IS_LOG (log));
195 
196   buffer = gtk_text_view_get_buffer (log->textview);
197   gtk_text_buffer_get_end_iter (buffer, &iter);
198   /* Apply the "plain" tag before the level-specific tag in order to
199    * reset the formatting */
200 
201   if (g_utf8_validate (message, -1, NULL)) {
202     gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, message, -1,
203                                               "plain", style, NULL);
204   } else {
205     /* If UTF-8 wasn't valid (due to a system locale encoded filename or
206      * other string being included by mistake), log a warning, and print
207      * the original message to stderr, where it may be partly intelligible */
208     gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
209       _("** Invalid UTF-8 in log message. See stderr or gschem.log.\n"),
210                                               -1, "plain", style, NULL);
211     fprintf (stderr, "%s", message);
212   }
213 
214   mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
215   gtk_text_view_scroll_to_mark (log->textview, mark, 0, TRUE, 0, 1);
216   gtk_text_buffer_delete_mark (buffer, mark);
217 }
218 
219 /*!
220  *  \brief Get the Log class type
221  *
222  *  \par Function Description
223  *
224  * On first call, registers the Log class with the GType dynamic type system.
225  * On subsequent calls, returns the saved value from first execution.
226  * \returns the type identifier for the Log class
227  */
log_get_type()228 GType log_get_type ()
229 {
230   static GType log_type = 0;
231 
232   if (!log_type) {
233     static const GTypeInfo log_info = {
234       sizeof(LogClass),
235       NULL, /* base_init */
236       NULL, /* base_finalize */
237       (GClassInitFunc) log_class_init,
238       NULL, /* class_finalize */
239       NULL, /* class_data */
240       sizeof(Log),
241       0,    /* n_preallocs */
242       (GInstanceInitFunc) log_init,
243     };
244 
245     log_type = g_type_register_static (GSCHEM_TYPE_DIALOG,
246                                        "Log",
247                                        &log_info, 0);
248   }
249 
250   return log_type;
251 }
252 
253 /*!
254  *  \brief Log class initialization function
255  *
256  *  \par Function Description
257  *
258  * Class initialization function for the Log class. Currently
259  * does nothing.
260  */
log_class_init(LogClass * klass)261 static void log_class_init (LogClass *klass)
262 {
263 /*   GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
264 
265 }
266 
267 /*!
268  *  \brief Log class instance initialization function
269  *
270  *  \par Function Description
271  *
272  * Instance initialization for the Log class. Sets up the log parameters,
273  * creates a scrollable text view with highlighting and a Close button.
274  *
275  * \param log the instance of the class to initialize
276  */
log_init(Log * log)277 static void log_init (Log *log)
278 {
279   GtkWidget *scrolled_win, *text_view;
280   GtkTextBuffer *text_buffer;
281   GtkTextMark *mark;
282 
283   /* dialog initialization */
284   g_object_set (G_OBJECT (log),
285                 /* GtkContainer */
286                 "border-width",    0,
287                 /* GtkWindow */
288                 "title",           _("Status"),
289                 "default-width",   600,
290                 "default-height",  200,
291                 "modal",           FALSE,
292                 "window-position", GTK_WIN_POS_NONE,
293                 "type-hint",       GDK_WINDOW_TYPE_HINT_NORMAL,
294                 /* GtkDialog */
295                 "has-separator",   TRUE,
296                 NULL);
297 
298   /* create a scrolled window for the textview */
299   scrolled_win = GTK_WIDGET (
300     g_object_new (GTK_TYPE_SCROLLED_WINDOW,
301                   /* GtkContainer */
302                   "border-width",      5,
303                   /* GtkScrolledWindow */
304                   "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
305                   "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
306                   "shadow-type",       GTK_SHADOW_ETCHED_IN,
307                   NULL));
308   /* create the text buffer */
309   text_buffer = GTK_TEXT_BUFFER (g_object_new (GTK_TYPE_TEXT_BUFFER,
310                                                NULL));
311 
312   /* Add some tags for highlighting log messages to the buffer */
313   gtk_text_buffer_create_tag (text_buffer, "plain",
314                               "foreground", "black",
315                               "foreground-set", TRUE,
316                               "weight", PANGO_WEIGHT_NORMAL,
317                               "weight-set", TRUE,
318                               NULL);
319   /* The default "message" style is plain */
320   gtk_text_buffer_create_tag (text_buffer, "message", NULL);
321   /* "old" messages are in dark grey */
322   gtk_text_buffer_create_tag (text_buffer, "old",
323                               "foreground", "#404040",
324                               "foreground-set", TRUE,
325 			      NULL);
326   /* "warning" messages are printed in red */
327   gtk_text_buffer_create_tag (text_buffer, "warning",
328                               "foreground", "red",
329                               "foreground-set", TRUE,
330                               NULL);
331   /* "critical" messages are bold red */
332   gtk_text_buffer_create_tag (text_buffer, "critical",
333                               "foreground", "red",
334                               "foreground-set", TRUE,
335                               "weight", PANGO_WEIGHT_BOLD,
336                               "weight-set", TRUE,
337                               NULL);
338 
339   /* create the text view and attach the buffer to it */
340   text_view = GTK_WIDGET (g_object_new (GTK_TYPE_TEXT_VIEW,
341                                         /* GtkTextView */
342 /* unknown property in GTK 2.2, use gtk_text_view_set_buffer() instead */
343 /*                                         "buffer",   text_buffer, */
344                                         "editable", FALSE,
345                                         NULL));
346   gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), text_buffer);
347 
348   /* add the text view to the scrolled window */
349   gtk_container_add (GTK_CONTAINER (scrolled_win), text_view);
350   /* set textview of log */
351   log->textview = GTK_TEXT_VIEW (text_view);
352 
353   /* add the scrolled window to the dialog vbox */
354   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (log)->vbox), scrolled_win,
355                       TRUE, TRUE, 0);
356   gtk_widget_show_all (scrolled_win);
357 
358   /* now add the close button to the action area */
359   gtk_dialog_add_button (GTK_DIALOG (log),
360                          GTK_STOCK_CLOSE, LOG_RESPONSE_CLOSE);
361 
362   /* scroll to the end of the buffer */
363   mark = gtk_text_buffer_get_insert (text_buffer);
364   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), mark, 0.0, TRUE, 0.0, 1.0);
365 }
366