1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3 * Pan - A Newsreader for Gtk+
4 * Copyright (C) 2002-2006 Charles Kerr <charles@rebelbase.com>
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; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include <config.h>
21 #include <ostream>
22 #include <fstream>
23 #include <iostream>
24 extern "C" {
25 #include <glib/gi18n.h>
26 #include "gtk-compat.h"
27 }
28 #include <pan/general/log.h>
29 #include <pan/general/macros.h>
30 #include <pan/general/string-view.h>
31 #include "log-ui.h"
32 #include "pad.h"
33
34 using namespace pan;
35
36 namespace
37 {
38 enum { COL_HIDDEN, COL_SEVERITY, COL_DATE, COL_MESSAGE, N_COLS };
39
40 struct MyLogListener: private Log::Listener
41 {
42 GtkTreeStore * myStore;
43
MyLogListener__anonfe89997a0111::MyLogListener44 MyLogListener (GtkTreeStore * store): myStore(store) {
45 Log::get().add_listener (this);
46 }
47
~MyLogListener__anonfe89997a0111::MyLogListener48 ~MyLogListener () {
49 Log::get().remove_listener (this);
50 }
51
on_log_entry_added__anonfe89997a0111::MyLogListener52 virtual void on_log_entry_added (const Log::Entry& e) {
53 GtkTreeIter iter;
54 gtk_tree_store_prepend (myStore, &iter, NULL);
55 gtk_tree_store_set (myStore, &iter,
56 COL_HIDDEN, "",
57 COL_SEVERITY, (e.severity & Log::PAN_SEVERITY_ERROR),
58 COL_DATE, (unsigned long)e.date,
59 COL_MESSAGE, &e, -1);
60 if (!e.messages.empty())
61 {
62 GtkTreeIter child;
63
64 foreach_const (Log::entries_p, e.messages, lit)
65 {
66 Log::Entry entry(**lit);
67 gtk_tree_store_prepend (myStore, &child, &iter );
68 gtk_tree_store_set (myStore, &child,
69 COL_HIDDEN, "",
70 COL_SEVERITY, (entry.severity & Log::PAN_SEVERITY_ERROR),
71 COL_DATE, (unsigned long)entry.date,
72 COL_MESSAGE, &*lit, -1);
73 }
74 }
75 }
76
on_log_cleared__anonfe89997a0111::MyLogListener77 virtual void on_log_cleared () {
78 gtk_tree_store_clear (myStore);
79 }
80 };
81
delete_my_log_listener(gpointer object)82 void delete_my_log_listener (gpointer object)
83 {
84 delete (MyLogListener*) object;
85 }
86 }
87
88 namespace
89 {
90 void
log_response_cb(GtkDialog * dialog,int response,gpointer)91 log_response_cb (GtkDialog * dialog, int response, gpointer )
92 {
93 if (response == GTK_RESPONSE_NO)
94 {
95 Log::get().clear ();
96 }
97 else if (response == GTK_RESPONSE_CLOSE)
98 {
99 gtk_widget_destroy (GTK_WIDGET(dialog));
100 }
101 else if (response == GTK_RESPONSE_APPLY)
102 {
103 GtkWidget * d = gtk_file_chooser_dialog_new (
104 _("Save Event List"),
105 GTK_WINDOW(dialog),
106 GTK_FILE_CHOOSER_ACTION_SAVE,
107 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
108 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
109 NULL);
110 if (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(d)))
111 {
112 char * fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (d));
113 const Log::entries_t& entries (Log::get().get_entries());
114 std::ofstream out (fname, std::ios_base::out|std::ios_base::trunc);
115 foreach_const (Log::entries_t, entries, it) {
116 StringView date (ctime (&it->date));
117 --date.len; // trim off the \n
118 out << date << " - " << it->message << '\n';
119 }
120 out.close ();
121 g_free (fname);
122 }
123 gtk_widget_destroy (d);
124 }
125 }
126 }
127
128 namespace
129 {
to_string(std::deque<Log::Entry> d)130 std::string to_string(std::deque<Log::Entry> d)
131 {
132 std::string tmp;
133 foreach_const(std::deque<Log::Entry>, d, it)
134 tmp += it->message + "\n";
135 return tmp;
136 }
137 }
138
139 namespace
140 {
141 GtkTreeStore*
create_model()142 create_model ()
143 {
144 GtkTreeStore * store = gtk_tree_store_new (N_COLS,
145 G_TYPE_STRING,
146 G_TYPE_BOOLEAN, // true==error, false==info
147 G_TYPE_ULONG, // date
148 G_TYPE_POINTER); // message
149
150 const Log::entries_t& entries (Log::get().get_entries());
151 foreach_const (Log::entries_t, entries, it) {
152 GtkTreeIter top, child, tmp;
153 gtk_tree_store_prepend (store, &top, 0);
154 gtk_tree_store_set (store, &top,
155 COL_HIDDEN, "",
156 COL_SEVERITY, (it->severity & Log::PAN_SEVERITY_ERROR),
157 COL_DATE, (unsigned long)it->date,
158 COL_MESSAGE, &*it, -1);
159 if (!it->messages.empty())
160 {
161 foreach_const (Log::entries_p, it->messages, lit)
162 {
163 Log::Entry entry (**lit);
164 gtk_tree_store_prepend (store, &child, &top );
165 gtk_tree_store_set (store, &child,
166 COL_HIDDEN, "",
167 COL_SEVERITY, (entry.severity & Log::PAN_SEVERITY_ERROR),
168 COL_DATE, (unsigned long)entry.date,
169 COL_MESSAGE, &*lit, -1);
170 }
171 }
172 }
173 return store;
174 }
175 }
176
177 namespace
178 {
179 void
render_severity(GtkTreeViewColumn *,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer dialog)180 render_severity (GtkTreeViewColumn * ,
181 GtkCellRenderer * renderer,
182 GtkTreeModel * model,
183 GtkTreeIter * iter,
184 gpointer dialog)
185 {
186 gboolean severe (false);
187 gtk_tree_model_get (model, iter, COL_SEVERITY, &severe, -1);
188 const char * key (severe ? "pixbuf-error" : "pixbuf-info");
189 g_object_set (renderer, "pixbuf", g_object_get_data(G_OBJECT(dialog),key), NULL);
190 }
191
192 void
render_date(GtkTreeViewColumn *,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer)193 render_date (GtkTreeViewColumn * ,
194 GtkCellRenderer * renderer,
195 GtkTreeModel * model,
196 GtkTreeIter * iter,
197 gpointer )
198 {
199 unsigned long date_ul;
200 gtk_tree_model_get (model, iter, COL_DATE, &date_ul, -1);
201 time_t date_t (date_ul);
202 std::string s = ctime (&date_t);
203 s.resize (s.size()-1); // remove \n
204 g_object_set (renderer, "text", s.c_str(), NULL);
205 }
206
207 void
render_message(GtkTreeViewColumn *,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer)208 render_message (GtkTreeViewColumn * ,
209 GtkCellRenderer * renderer,
210 GtkTreeModel * model,
211 GtkTreeIter * iter,
212 gpointer )
213 {
214 Log::Entry* log_entry(0);
215 gtk_tree_model_get (model, iter, COL_MESSAGE, &log_entry, -1);
216 bool bold (log_entry->is_child);
217 g_object_set (renderer,
218 "text", log_entry ? log_entry->message.c_str() : "",
219 "weight", bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
220 NULL);
221 }
222
223
224 }
225
226 gboolean
on_button_pressed(GtkWidget * treeview,GdkEventButton * event,gpointer userdata)227 pan :: on_button_pressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
228 {
229 // single click with the right mouse button?
230 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
231 {
232 GtkTreeSelection * selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
233 GtkTreePath * path;
234 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(treeview),
235 (gint)event->x, (gint)event->y,
236 &path, NULL, NULL, NULL))
237 {
238 if (!gtk_tree_selection_path_is_selected (selection, path))
239 {
240 gtk_tree_selection_unselect_all (selection);
241 gtk_tree_selection_select_path (selection, path);
242 }
243 }
244 const bool expanded (gtk_tree_view_row_expanded (GTK_TREE_VIEW(treeview), path));
245 if (expanded)
246 gtk_tree_view_collapse_row(GTK_TREE_VIEW(treeview),path);
247 else
248 gtk_tree_view_expand_row (GTK_TREE_VIEW(treeview),path,false);
249 gtk_tree_path_free (path);
250 return true;
251 }
252 return false;
253 }
254
255 GtkWidget*
log_dialog_new(Prefs & prefs,GtkWindow * window)256 pan :: log_dialog_new (Prefs& prefs, GtkWindow* window)
257 {
258 GtkWidget * dialog = gtk_dialog_new_with_buttons (_("Pan: Events"),
259 window,
260 GTK_DIALOG_DESTROY_WITH_PARENT,
261 GTK_STOCK_CLEAR, GTK_RESPONSE_NO,
262 GTK_STOCK_SAVE, GTK_RESPONSE_APPLY,
263 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
264 NULL);
265 g_signal_connect (dialog, "response", G_CALLBACK(log_response_cb), NULL);
266
267 GtkIconTheme * theme = gtk_icon_theme_get_default ();
268 GdkPixbuf * err_pixbuf = gtk_icon_theme_load_icon (theme, GTK_STOCK_DIALOG_ERROR, 20, (GtkIconLookupFlags)0, NULL);
269 g_object_set_data_full (G_OBJECT(dialog), "pixbuf-error", err_pixbuf, g_object_unref);
270 GdkPixbuf * info_pixbuf = gtk_icon_theme_load_icon (theme, GTK_STOCK_DIALOG_INFO, 20, (GtkIconLookupFlags)0, NULL);
271 g_object_set_data_full (G_OBJECT(dialog), "pixbuf-info", info_pixbuf, g_object_unref);
272
273 GtkTreeStore * store = create_model ();
274 GtkWidget * view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
275
276 gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(view),false);
277
278 g_object_set_data_full (G_OBJECT(view), "listener", new MyLogListener(store), delete_my_log_listener);
279 GtkWidget * scroll = gtk_scrolled_window_new (0, 0);
280 gtk_container_set_border_width (GTK_CONTAINER(scroll), PAD_BIG);
281 gtk_container_add (GTK_CONTAINER(scroll), view);
282
283 GtkCellRenderer * pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
284 GtkCellRenderer * text_renderer = gtk_cell_renderer_text_new ();
285
286 /* placeholder for expander */
287 GtkTreeViewColumn * col = gtk_tree_view_column_new ();
288 gtk_tree_view_column_set_resizable (col, false);
289 gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
290 gtk_tree_view_column_set_visible(col,false);
291 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), col);
292
293 // severity
294 col = gtk_tree_view_column_new ();
295 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
296 gtk_tree_view_column_set_fixed_width (col, 35);
297 gtk_tree_view_column_set_resizable (col, false);
298 gtk_tree_view_column_pack_start (col, pixbuf_renderer, false);
299 gtk_tree_view_column_set_cell_data_func (col, pixbuf_renderer, render_severity, dialog, 0);
300 gtk_tree_view_column_set_sort_column_id (col, COL_SEVERITY);
301 gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
302
303 // date
304 col = gtk_tree_view_column_new ();
305 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
306 gtk_tree_view_column_set_sort_column_id (col, COL_DATE);
307 gtk_tree_view_column_set_title (col, _("Date"));
308 gtk_tree_view_column_pack_start (col, text_renderer, false);
309 gtk_tree_view_column_set_cell_data_func (col, text_renderer, render_date, 0, 0);
310 gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
311
312 // message
313 text_renderer = gtk_cell_renderer_text_new ();
314 col = gtk_tree_view_column_new ();
315 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
316 gtk_tree_view_column_set_sort_column_id (col, COL_MESSAGE);
317 gtk_tree_view_column_set_title (col, _("Message"));
318 gtk_tree_view_column_pack_start (col, text_renderer, true);
319 gtk_tree_view_column_set_cell_data_func (col, text_renderer, render_message, 0, 0);
320 gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
321 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), col);
322
323 gtk_widget_show (view);
324 gtk_widget_show (scroll);
325 pan_box_pack_start_defaults (GTK_BOX(gtk_dialog_get_content_area( GTK_DIALOG(dialog))), scroll);
326
327 gtk_window_set_role (GTK_WINDOW(dialog), "pan-events-dialog");
328 prefs.set_window ("events-window", GTK_WINDOW(dialog), 150, 150, 600, 300);
329 gtk_window_set_resizable (GTK_WINDOW(dialog), true);
330 if (window != 0)
331 gtk_window_set_transient_for (GTK_WINDOW(dialog), window);
332
333 g_signal_connect (view, "button-press-event", G_CALLBACK(on_button_pressed), view);
334
335 return dialog;
336 }
337