1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3  * @file
4  * A dialog that displays log messages.
5  */
6 /* Authors:
7  *   Bob Jamison
8  *   Other dudes from The Inkscape Organization
9  *
10  * Copyright (C) 2004 The Inkscape Organization
11  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12  */
13 
14 #include <gtkmm/box.h>
15 #include <gtkmm/dialog.h>
16 #include <gtkmm/textview.h>
17 #include <gtkmm/menubar.h>
18 #include <gtkmm/scrolledwindow.h>
19 #include <glibmm/i18n.h>
20 
21 #include "debug.h"
22 
23 namespace Inkscape {
24 namespace UI {
25 namespace Dialog {
26 
27 /**
28  * A very simple dialog for displaying Inkscape messages - implementation.
29  */
30 class DebugDialogImpl : public DebugDialog, public Gtk::Dialog
31 {
32 public:
33     DebugDialogImpl();
34     ~DebugDialogImpl() override;
35 
36     void show() override;
37     void hide() override;
38     void clear() override;
39     void message(char const *msg) override;
40     void captureLogMessages() override;
41     void releaseLogMessages() override;
42 
43 private:
44     Gtk::MenuBar menuBar;
45     Gtk::Menu   fileMenu;
46     Gtk::ScrolledWindow textScroll;
47     Gtk::TextView messageText;
48 
49     //Handler ID's
50     guint handlerDefault;
51     guint handlerGlibmm;
52     guint handlerAtkmm;
53     guint handlerPangomm;
54     guint handlerGdkmm;
55     guint handlerGtkmm;
56 };
57 
clear()58 void DebugDialogImpl::clear()
59 {
60     Glib::RefPtr<Gtk::TextBuffer> buffer = messageText.get_buffer();
61     buffer->erase(buffer->begin(), buffer->end());
62 }
63 
DebugDialogImpl()64 DebugDialogImpl::DebugDialogImpl()
65 {
66     set_title(_("Messages"));
67     set_size_request(300, 400);
68     auto mainVBox = get_content_area();
69 
70     //## Add a menu for clear()
71     Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem(_("_File"), true));
72     item->set_submenu(fileMenu);
73     menuBar.append(*item);
74 
75     item = Gtk::manage(new Gtk::MenuItem(_("_Clear"), true));
76     item->signal_activate().connect(sigc::mem_fun(*this, &DebugDialogImpl::clear));
77     fileMenu.append(*item);
78 
79     item = Gtk::manage(new Gtk::MenuItem(_("Capture log messages")));
80     item->signal_activate().connect(sigc::mem_fun(*this, &DebugDialogImpl::captureLogMessages));
81     fileMenu.append(*item);
82 
83     item = Gtk::manage(new Gtk::MenuItem(_("Release log messages")));
84     item->signal_activate().connect(sigc::mem_fun(*this, &DebugDialogImpl::releaseLogMessages));
85     fileMenu.append(*item);
86 
87     mainVBox->pack_start(menuBar, Gtk::PACK_SHRINK);
88 
89 
90     //### Set up the text widget
91     messageText.set_editable(false);
92     textScroll.add(messageText);
93     textScroll.set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
94     mainVBox->pack_start(textScroll);
95 
96     show_all_children();
97 
98     message("ready.");
99     message("enable log display by setting ");
100     message("dialogs.debug 'redirect' attribute to 1 in preferences.xml");
101 
102     handlerDefault = 0;
103     handlerGlibmm  = 0;
104     handlerAtkmm   = 0;
105     handlerPangomm = 0;
106     handlerGdkmm   = 0;
107     handlerGtkmm   = 0;
108 }
109 
110 
create()111 DebugDialog *DebugDialog::create()
112 {
113     DebugDialog *dialog = new DebugDialogImpl();
114     return dialog;
115 }
116 
117 DebugDialogImpl::~DebugDialogImpl()
118 = default;
119 
show()120 void DebugDialogImpl::show()
121 {
122     //call super()
123     Gtk::Dialog::show();
124     //sp_transientize(GTK_WIDGET(gobj()));  //Make transient
125     raise();
126     Gtk::Dialog::present();
127 }
128 
hide()129 void DebugDialogImpl::hide()
130 {
131     // call super
132     Gtk::Dialog::hide();
133 }
134 
message(char const * msg)135 void DebugDialogImpl::message(char const *msg)
136 {
137     Glib::RefPtr<Gtk::TextBuffer> buffer = messageText.get_buffer();
138     Glib::ustring uMsg = msg;
139     if (uMsg[uMsg.length()-1] != '\n')
140         uMsg += '\n';
141     buffer->insert (buffer->end(), uMsg);
142 }
143 
144 /* static instance, to reduce dependencies */
145 static DebugDialog *debugDialogInstance = nullptr;
146 
getInstance()147 DebugDialog *DebugDialog::getInstance()
148 {
149     if (!debugDialogInstance) {
150         debugDialogInstance = new DebugDialogImpl();
151     }
152     return debugDialogInstance;
153 }
154 
155 
156 
showInstance()157 void DebugDialog::showInstance()
158 {
159     DebugDialog *debugDialog = getInstance();
160     debugDialog->show();
161     // this is not a real memleak because getInstance() only creates a debug dialog once, and returns that instance for all subsequent calls
162     // cppcheck-suppress memleak
163 }
164 
165 
166 
167 
168 /*##### THIS IS THE IMPORTANT PART ##### */
dialogLoggingFunction(const gchar *,GLogLevelFlags,const gchar * messageText,gpointer user_data)169 static void dialogLoggingFunction(const gchar */*log_domain*/,
170                                   GLogLevelFlags /*log_level*/,
171                                   const gchar *messageText,
172                                   gpointer user_data)
173 {
174     DebugDialogImpl *dlg = static_cast<DebugDialogImpl *>(user_data);
175     dlg->message(messageText);
176 }
177 
178 
captureLogMessages()179 void DebugDialogImpl::captureLogMessages()
180 {
181     /*
182     This might likely need more code, to capture Gtkmm
183     and Glibmm warnings, or maybe just simply grab stdout/stderr
184     */
185     GLogLevelFlags flags = (GLogLevelFlags) (G_LOG_LEVEL_ERROR   | G_LOG_LEVEL_CRITICAL |
186                              G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE  |
187                              G_LOG_LEVEL_INFO    | G_LOG_LEVEL_DEBUG);
188     if ( !handlerDefault ) {
189         handlerDefault = g_log_set_handler(nullptr, flags,
190               dialogLoggingFunction, (gpointer)this);
191     }
192     if ( !handlerGlibmm ) {
193         handlerGlibmm = g_log_set_handler("glibmm", flags,
194               dialogLoggingFunction, (gpointer)this);
195     }
196     if ( !handlerAtkmm ) {
197         handlerAtkmm = g_log_set_handler("atkmm", flags,
198               dialogLoggingFunction, (gpointer)this);
199     }
200     if ( !handlerPangomm ) {
201         handlerPangomm = g_log_set_handler("pangomm", flags,
202               dialogLoggingFunction, (gpointer)this);
203     }
204     if ( !handlerGdkmm ) {
205         handlerGdkmm = g_log_set_handler("gdkmm", flags,
206               dialogLoggingFunction, (gpointer)this);
207     }
208     if ( !handlerGtkmm ) {
209         handlerGtkmm = g_log_set_handler("gtkmm", flags,
210               dialogLoggingFunction, (gpointer)this);
211     }
212     message("log capture started");
213 }
214 
releaseLogMessages()215 void DebugDialogImpl::releaseLogMessages()
216 {
217     if ( handlerDefault ) {
218         g_log_remove_handler(nullptr, handlerDefault);
219         handlerDefault = 0;
220     }
221     if ( handlerGlibmm ) {
222         g_log_remove_handler("glibmm", handlerGlibmm);
223         handlerGlibmm = 0;
224     }
225     if ( handlerAtkmm ) {
226         g_log_remove_handler("atkmm", handlerAtkmm);
227         handlerAtkmm = 0;
228     }
229     if ( handlerPangomm ) {
230         g_log_remove_handler("pangomm", handlerPangomm);
231         handlerPangomm = 0;
232     }
233     if ( handlerGdkmm ) {
234         g_log_remove_handler("gdkmm", handlerGdkmm);
235         handlerGdkmm = 0;
236     }
237     if ( handlerGtkmm ) {
238         g_log_remove_handler("gtkmm", handlerGtkmm);
239         handlerGtkmm = 0;
240     }
241     message("log capture discontinued");
242 }
243 
244 
245 
246 } //namespace Dialogs
247 } //namespace UI
248 } //namespace Inkscape
249 
250 /*
251   Local Variables:
252   mode:c++
253   c-file-style:"stroustrup"
254   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
255   indent-tabs-mode:nil
256   fill-column:99
257   End:
258 */
259 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
260