56
58
def _init_text_tags(self):
57
59
"""Initialize tags for the text buffer."""
58
60
text_buffer = self._text_view.get_buffer()
59
text_buffer.create_tag("bold", weight=pango.WEIGHT_BOLD)
60
text_buffer.create_tag("large", scale=pango.SCALE_LARGE)
61
text_buffer.create_tag("bold", weight=Pango.Weight.BOLD)
62
text_buffer.create_tag("large", scale=1.2)
61
63
text_buffer.create_tag("monospace", family="monospace")
65
def _insert_environment(self):
66
"""Insert environment information."""
67
locale = aeidon.locales.get_system_code()
68
encoding = aeidon.encodings.get_locale_code()
69
ins = self._insert_text
70
ins("Platform: {}\n".format(platform.platform(True)))
71
ins("Locale: {}.{}\n\n".format(locale, encoding))
73
def _insert_library_versions(self):
74
"""Insert version numbers of libraries."""
75
dotjoin = lambda seq: ".".join(map(str, seq))
76
python_version = dotjoin(sys.version_info[:3])
77
gtk_version = dotjoin((Gtk.get_major_version(),
78
Gtk.get_minor_version(),
79
Gtk.get_micro_version()))
81
pygobject_version = dotjoin(GObject.pygobject_version)
82
gst_version = gaupol.util.get_gst_version()
83
ins = self._insert_text
84
ins("Python: {}\n".format(python_version))
85
ins("GTK+: {}\n".format(gtk_version))
86
ins("PyGObject: {}\n".format(pygobject_version))
87
ins("GStreamer: {}\n\n".format(gst_version))
63
89
def _insert_link(self, path, lineno, *tags):
64
90
"""Insert `path` as a link into the text view."""
65
91
text_buffer = self._text_view.get_buffer()
66
92
tag = text_buffer.create_tag(None, foreground="blue")
67
tag.props.underline = pango.UNDERLINE_SINGLE
93
tag.props.underline = Pango.Underline.SINGLE
68
94
tag.connect("event", self._on_text_view_link_tag_event)
69
95
path = os.path.abspath(path)
70
tag.set_data("path", path)
71
tag.set_data("lineno", lineno)
96
tag.gaupol_path = path
97
tag.gaupol_lineno = lineno
72
98
if path.startswith(os.getcwd()):
73
99
path = path.replace(os.getcwd(), "")
74
while path.startswith(os.sep):
75
path = path.replace(os.sep, "", 1)
100
while path.startswith(os.sep):
101
path = path.replace(os.sep, "", 1)
76
102
itr = text_buffer.get_end_iter()
77
103
tag_table = text_buffer.get_tag_table()
78
tags = map(tag_table.lookup, tags + ("monospace",))
104
tags = list(map(tag_table.lookup, tags + ("monospace",)))
79
105
text_buffer.insert_with_tags(itr, path, tag, *tags)
81
def _insert_platform(self):
82
"""Insert platform information."""
83
self._insert_text("%s\n\n" % platform.platform(True))
107
def _insert_python_package_versions(self):
108
"""Insert version numbers of Python packages."""
109
ins = self._insert_text
110
ins("aeidon: {}\n".format(aeidon.__version__))
111
ins("gaupol: {}\n".format(gaupol.__version__))
112
ins("enchant: {}\n".format(aeidon.util.get_enchant_version()))
113
ins("chardet: {}\n".format(aeidon.util.get_chardet_version()))
85
115
def _insert_text(self, text, *tags):
86
116
"""Insert `text` with `tags` to the text view."""
97
127
def _insert_traceback(self, exctype, value, tb, limit=100):
98
128
"""Insert up to `limit` stack trace entries from `tb`."""
129
# This function has been originally adapted from Gazpacho
130
# Copyright (C) 2005 by Async Open Source and Sicem S.L.
99
131
for i in range(limit):
100
132
if tb is None: break
103
line = linecache.getline(code.co_filename,
104
tb.tb_lineno).strip()
133
lineno = tb.tb_lineno
134
filename = tb.tb_frame.f_code.co_filename
135
name = tb.tb_frame.f_code.co_name
136
line = linecache.getline(filename, lineno)
106
138
self._insert_text("File: ")
107
self._insert_link(code.co_filename, tb.tb_lineno)
139
self._insert_link(filename, lineno)
108
140
self._insert_text("\n")
109
self._insert_text("Line: %s\n" % str(tb.tb_lineno))
110
self._insert_text("In: %s\n\n" % code.co_name)
141
self._insert_text("Line: {}\n".format(str(lineno)))
142
self._insert_text("In: {}\n\n".format(name))
112
self._insert_text(" %s\n\n" % line)
145
self._insert_text("{}{}\n\n".format(indent, line))
114
147
exception = traceback.format_exception_only(exctype, value)[0]
115
148
exception, space, message = exception.partition(" ")
116
149
self._insert_text(exception, "bold")
117
self._insert_text("%s%s\n" % (space, message))
119
def _insert_versions(self):
120
"""Insert version numbers of dependencies."""
121
self._insert_text("Aeidon: %s\n" % aeidon.__version__)
122
self._insert_text("Gaupol: %s\n" % gaupol.__version__)
123
self._insert_text("Python: %d.%d.%d\n" % sys.version_info[:3])
124
self._insert_text("GTK+: %d.%d.%d\n" % gtk.gtk_version)
125
self._insert_text("PyGTK: %d.%d.%d\n" % gtk.pygtk_version)
126
self._insert_text("PyEnchant: %s\n"
127
% aeidon.util.get_enchant_version() or "N/A")
129
self._insert_text("Universal Encoding Detector: %s\n"
130
% aeidon.util.get_chardet_version() or "N/A")
150
self._insert_text("{}{}\n".format(space, message))
132
152
def _on_editor_exit(self, pid, return_value, command):
133
153
"""Print an error message if editor process failed."""
134
154
if return_value == 0: return
135
print ("Command '%s' failed with return value %d"
136
% (command, return_value))
155
print(("Command {} failed with return value {}"
156
.format(repr(command), repr(return_value))),
138
159
def _on_response(self, dialog, response):
139
160
"""Do not send response if reporting bug."""
140
if response != gtk.RESPONSE_YES: return
161
if response != Gtk.ResponseType.YES: return
141
162
gaupol.util.show_uri(gaupol.BUG_REPORT_URL)
142
163
self.stop_emission("response")
144
165
def _on_text_view_link_tag_event(self, tag, text_view, event, itr):
145
166
"""Open linked file in editor."""
146
if event.type != gtk.gdk.BUTTON_RELEASE: return
147
if event.button != 1: return
167
if event.type != Gdk.EventType.BUTTON_RELEASE: return
148
168
text_buffer = self._text_view.get_buffer()
149
assert not text_buffer.get_selection_bounds()
169
if text_buffer.get_selection_bounds(): return
150
170
self._open_link(tag)
152
172
def _on_text_view_motion_notify_event(self, text_view, event):
153
173
"""Change mouse pointer when hovering over a link."""
156
window = gtk.TEXT_WINDOW_WIDGET
176
window = Gtk.TextWindowType.WIDGET
157
177
x, y = text_view.window_to_buffer_coords(window, x, y)
158
window = text_view.get_window(gtk.TEXT_WINDOW_TEXT)
178
window = text_view.get_window(Gtk.TextWindowType.TEXT)
159
179
for tag in text_view.get_iter_at_location(x, y).get_tags():
160
if tag.get_data("path") is not None:
161
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
162
return text_view.window.get_pointer()
163
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
164
text_view.window.get_pointer()
180
if hasattr(tag, "gaupol_path"):
181
window.set_cursor(Gdk.Cursor(cursor_type=Gdk.CursorType.HAND2))
183
window.set_cursor(Gdk.Cursor(cursor_type=Gdk.CursorType.XTERM))
166
186
def _open_link(self, tag):
167
187
"""Open linked file in editor."""
168
path = aeidon.util.shell_quote(tag.get_data("path"))
188
path = aeidon.util.shell_quote(tag.gaupol_path)
169
189
command = string.Template(gaupol.conf.debug.text_editor)
170
command = command.safe_substitute(LINENO=tag.get_data("lineno"),
190
command = command.safe_substitute(LINENO=tag.gaupol_lineno,
173
193
process = aeidon.util.start_process(command)
174
glib.child_watch_add(process.pid, self._on_editor_exit, command)
194
GLib.child_watch_add(process.pid, self._on_editor_exit, command)
175
195
tag.props.foreground = "purple"
177
197
def set_text(self, exctype, value, tb):
178
198
"""Set text from `tb` to the text view."""
179
199
self._insert_title("Traceback")
180
200
self._insert_traceback(exctype, value, tb)
181
self._insert_title("Platform")
182
self._insert_platform()
183
self._insert_title("Versions")
184
self._insert_versions()
201
self._insert_title("Environment")
202
self._insert_environment()
203
self._insert_title("Libraries")
204
self._insert_library_versions()
205
self._insert_title("Python Packages")
206
self._insert_python_package_versions()
185
207
gaupol.util.scale_to_content(self._text_view,
190
212
font="monospace")