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