00001
00002
00003
00004
00005
00006
00007
00008 import os
00009 import re
00010 import gobject
00011 import gtk
00012 import gtk.glade
00013 from gettext import gettext
00014
00015 import view
00016 import extension
00017 import execution
00018 import config
00019 import environment
00020 import settings
00021
00022 datadir = None
00023 views = []
00024
00025 def init_extension(d):
00026 global datadir
00027 datadir = d
00028
00029 def get_views():
00030 global views
00031 return [i for i in views if not i.is_busy()]
00032
00033
00034
00035
00036 class Output (environment.OutputExtension):
00037 def __init__(self):
00038 environment.OutputExtension.__init__(self)
00039
00040 def get_view(self):
00041 views = get_views()
00042 if views:
00043 return views[0]
00044 else:
00045 view = View()
00046 environment.add_view(view, 'bottom')
00047 return view
00048
00049 def set_batch(self, batch):
00050 view = self.get_view()
00051 view.set_batch(batch)
00052 environment.show_view(view, focus=False)
00053
00054 gobject.type_register(Output)
00055
00056
00057
00058
00059 class Launcher (environment.LauncherExtension):
00060 def __init__(self):
00061 environment.LauncherExtension.__init__(self)
00062
00063 label = gettext('Output window')
00064 self.action = gtk.Action(None, label, None, None)
00065 self.action.set_visible(True)
00066
00067 def get_action(self):
00068 return self.action
00069
00070 def create(self):
00071 return View()
00072
00073 gobject.type_register(Launcher)
00074
00075
00076
00077
00078 class View (view.AbstractView):
00079 def __init__(self):
00080 view.AbstractView.__init__(self)
00081
00082 path = os.path.join(datadir, 'encode', 'output-view.glade')
00083 self.glade = gtk.glade.XML(path)
00084 self.glade.signal_autoconnect(self)
00085
00086 self._set_title('idle')
00087
00088 buffer = self._get_buffer()
00089 buffer.connect_after('insert-text', self.on_buffer_insert)
00090
00091 self.tag_common = buffer.create_tag()
00092
00093 self.tag_execute = buffer.create_tag()
00094 self.tag_execute.set_property('foreground', 'blue')
00095
00096 self.tag_error = buffer.create_tag()
00097 self.tag_error.set_property('foreground', 'red3')
00098
00099 self.tag_success = buffer.create_tag()
00100 self.tag_success.set_property('foreground', 'green4')
00101
00102 self.tag_failure = buffer.create_tag()
00103 self.tag_failure.set_property('foreground', 'red')
00104
00105 self.conf = config.Directory('encode/output')
00106 self.conf.add_string('font', lambda font: self.tag_common.set_property('font', font))
00107
00108 self.formats = []
00109 self.conf.add_strings('formats', self.set_formats)
00110
00111 self.batch = None
00112 self.handlers = None
00113 self.jobs_by_line = []
00114
00115 def set_formats(self, strings):
00116 self.formats = [re.compile(s) for s in strings]
00117
00118 def _get_job(self, line):
00119 for start_line, job in self.jobs_by_line:
00120 if start_line <= line:
00121 return job
00122 return None
00123
00124 def _get_buffer(self):
00125 textview = self.glade.get_widget('textview')
00126 return textview.get_buffer()
00127
00128 def _set_title(self, name):
00129 data_label = self.glade.get_widget('title_%s' % name)
00130 text = data_label.get_label()
00131
00132 display_label = self.glade.get_widget('title')
00133 display_label.set_label(text)
00134
00135 def _ensure_newline(self):
00136 buffer = self._get_buffer()
00137 end = buffer.get_end_iter()
00138
00139 if not end.is_start():
00140 last = end.copy()
00141 last.backward_char()
00142
00143 if last.get_char() != '\n':
00144 buffer.insert(end, '\n')
00145
00146 def _append(self, text, tags=[]):
00147 buffer = self._get_buffer()
00148
00149 end = buffer.get_end_iter()
00150 buffer.insert(end, text)
00151
00152 start = buffer.get_iter_at_mark(self.mark)
00153
00154 for tag in [self.tag_common] + tags:
00155 buffer.apply_tag(tag, start, end)
00156
00157 buffer.move_mark(self.mark, end)
00158
00159 def _append_message(self, tag, name, params=()):
00160 label = self.glade.get_widget('message_%s' % name)
00161 format = label.get_label()
00162
00163 text = format % params
00164
00165 self._ensure_newline()
00166 self._append(text, [tag])
00167
00168 def on_textview_popup(self, textview, menu):
00169 item = self.glade.get_widget('stop_item')
00170 menu.insert(item, 0)
00171 menu.connect('destroy', lambda menu: menu.remove(item))
00172
00173 separator = gtk.SeparatorMenuItem()
00174 separator.show()
00175 menu.insert(separator, 1)
00176
00177 def on_textview_release(self, textview, event):
00178 if event.button == 1:
00179 buffer = self._get_buffer()
00180 if not buffer.get_selection_bounds():
00181 buf_x, buf_y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y))
00182 line_iter, line_y = textview.get_line_at_y(buf_y)
00183
00184 self._edit_source(line_iter)
00185
00186 def on_scrolledwindow_parent_set(self, scrolledwindow, old_parent):
00187 global views
00188 if scrolledwindow.get_parent():
00189 if not old_parent:
00190 views.append(self)
00191 else:
00192 if old_parent:
00193 views.remove(self)
00194
00195 def on_scrolledwindow_destroy(self, scrolledwindow):
00196 self.conf.remove_all()
00197 self.conf = None
00198
00199 if self.batch:
00200 self.batch.stop()
00201 self.batch = None
00202
00203
00204
00205 def set_batch(self, batch):
00206 if self.batch:
00207 for id in self.handlers:
00208 self.batch.disconnect(id)
00209
00210 self.batch = batch
00211 self.handlers = (
00212 self.batch.connect('job-started', self.on_batch_started),
00213 self.batch.connect('finished', self.on_batch_finished)
00214 )
00215
00216 self.batch.get_output().consumers.add(self._consume_output)
00217 self.batch.get_error().consumers.add(self._consume_error)
00218
00219 self.jobs_by_line = []
00220
00221 self._set_title('busy')
00222
00223 buffer = self._get_buffer()
00224 buffer.set_text('')
00225
00226 start = buffer.get_start_iter()
00227 self.mark = buffer.create_mark(None, start, left_gravity=True)
00228
00229 def is_busy(self):
00230 if not self.get_widget():
00231 return True
00232
00233 if self.batch:
00234 return self.batch.is_alive()
00235 else:
00236 return False
00237
00238 def _consume_output(self, text, tags=[]):
00239 self._append(text, tags)
00240
00241 def _consume_error(self, text):
00242 self._consume_output(text, [self.tag_error])
00243
00244 def on_batch_started(self, batch, job):
00245
00246
00247 self._ensure_newline()
00248
00249 buffer = self._get_buffer()
00250 iter = buffer.get_end_iter()
00251 line = iter.get_line()
00252
00253 self.jobs_by_line.append((line, job))
00254
00255
00256
00257 info = ''
00258
00259 if isinstance(job, execution.Job):
00260 if job.vars:
00261 for key in job.vars:
00262 info += 'Environ: %s=%s\n' % (key, job.vars[key])
00263
00264 if job.workdir:
00265 info += 'Workdir: %s\n' % job.workdir
00266
00267 info += 'Command:'
00268 for arg in job.args:
00269 arg = arg.replace(' ', '\\ ')
00270 info = '%s %s' % (info, arg)
00271 else:
00272 info += 'Hook: %s' % job
00273
00274 info += '\n'
00275
00276 self._append_message(self.tag_execute, 'execute', (info))
00277 self._ensure_newline()
00278
00279 item = self.glade.get_widget('stop_item')
00280 item.set_sensitive(True)
00281
00282 def on_batch_finished(self, batch, status):
00283 self._set_title('idle')
00284
00285 if status == 0:
00286 self._append_message(self.tag_success, 'success')
00287 else:
00288 signum = status & 0xff
00289 retval = status >> 8
00290
00291 if signum:
00292 self._append_message(self.tag_failure, 'signal', (signum))
00293 else:
00294 self._append_message(self.tag_failure, 'error', (retval))
00295
00296 item = self.glade.get_widget('stop_item')
00297 item.set_sensitive(False)
00298
00299 def on_buffer_insert(self, buffer, iter, text, length):
00300 textview = self.glade.get_widget('textview')
00301 textview.scroll_to_mark(self.mark, within_margin=0.0, use_align=True, xalign=0.0, yalign=0.0)
00302
00303 def on_stop_activate(self, item):
00304 self.batch.stop()
00305
00306 self._set_title('idle')
00307 self._append_message(self.tag_failure, 'stop')
00308
00309 item.set_sensitive(False)
00310
00311
00312
00313 def _edit_source(self, iter):
00314 line = iter.get_line()
00315
00316 job = self._get_job(line)
00317 if job:
00318 buffer = self._get_buffer()
00319
00320 end_iter = buffer.get_iter_at_line(line + 1)
00321 if iter == end_iter:
00322 end_iter = buffer.get_end_iter()
00323
00324 text = buffer.get_text(iter, end_iter)
00325
00326 location = self._match(text)
00327 if location:
00328 filename, line = location
00329
00330 path = os.path.join(job.workdir, filename)
00331 environment.edit(path, job.workdir, line)
00332
00333 def _match(self, text):
00334 for regex in self.formats:
00335 match = regex.match(text)
00336 if match:
00337 filename, line = match.group(1, 2)
00338 return filename, int(line)
00339 return None
00340
00341
00342
00343 def get_label(self):
00344 return self.glade.get_widget('title')
00345
00346 def get_widget(self):
00347 return self.glade.get_widget('scrolledwindow')
00348
00349 gobject.type_register(View)
00350
00351
00352
00353
00354 if 'set' not in __builtins__:
00355 import sets
00356 set = sets.Set
00357
00358 class Settings (settings.SettingsExtension):
00359 def __init__(self):
00360 settings.SettingsExtension.__init__(self)
00361
00362 path = os.path.join(datadir, 'encode', 'output-settings.glade')
00363 self.glade = gtk.glade.XML(path)
00364 self.glade.signal_autoconnect(self)
00365
00366 self.conf = config.Directory('encode/output')
00367
00368 self.dirty = set((
00369 'font',
00370 ))
00371 self.revert()
00372
00373 def get_name(self):
00374 return gettext('Output')
00375
00376 def get_title(self):
00377 return gettext('Output window')
00378
00379 def get_widget(self):
00380 return self.glade.get_widget('table')
00381
00382 def apply(self):
00383 if 'font' in self.dirty:
00384 fontbutton = self.glade.get_widget('font_fontbutton')
00385 font = fontbutton.get_font_name()
00386
00387 self.conf.set_string('font', font)
00388
00389 self.dirty.clear()
00390
00391 def revert(self):
00392 if 'font' in self.dirty:
00393 font = self.conf.get_string('font')
00394
00395 fontbutton = self.glade.get_widget('font_fontbutton')
00396 fontbutton.set_font_name(font)
00397
00398 self.dirty.clear()
00399
00400 def on_font_set(self, fontbutton):
00401 self.dirty.add('font')
00402 self.modified()
00403
00404 gobject.type_register(Settings)