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