home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/python
- import gobject, gtk, gtk.glade
- import glob, sys, gettext, os.path, locale, tempfile
- from gettext import gettext as _
- import webbrowser, subprocess
- import xdg.DesktopEntry
-
- from problem_report import ProblemReport
- import apport_utils
-
- gettext_domain = 'apport'
-
- def format_filesize(size):
- '''Format the given integer as humanly readable and i18n'ed file size.'''
-
- if size < 1048576:
- return locale.format('%.1f KB', size/1024.)
- if size < 1024 * 1048576:
- return locale.format('%.1f MB', size / 1048576.)
- return locale.format('%.1f GB', size / float(1024 * 1048576))
-
- class ApportGTK:
- def __init__(self):
- self.widgets = gtk.glade.XML(os.path.join(os.path.dirname(sys.argv[0]),
- 'apport-gtk.glade'))
- self.widgets.signal_autoconnect(self)
-
- self.reports = apport_utils.get_new_reports()
- if len(self.reports) == 0:
- print >> sys.stderr, 'No crash reports found, aborting'
- sys.exit(1)
-
- # initialize tree model and view
- self.tree_model = gtk.TreeStore(gobject.TYPE_STRING)
- self.w('treeview_reportdetails').set_model(self.tree_model)
-
- column = gtk.TreeViewColumn("Report", gtk.CellRendererText(), text=0)
- self.w('treeview_reportdetails').append_column(column)
-
- # save the original strings of the dialog headings (which contain %s,
- # which we replace later)
- self.str_heading = self.w('label_heading').get_label()
- self.str_heading_reopen = self.w('label_heading_reopen').get_label()
- self.str_bugreport_title = self.w('window_bugreport').get_title()
-
- self.cur_source = None
- self.report = None
- self.update_report()
-
- def ignore(self, widget, *auxargs):
- '''Do nothing with the current report.'''
-
- self.w('dialog_crash').hide()
- self.w('dialog_crash_reopen').hide()
- self.next()
- return True
-
- def next(self):
- '''Move on to the next report.'''
-
- apport_utils.mark_report_seen(self.reports.pop(0))
- self.report = None
-
- if len(self.reports) > 0:
- self.update_report()
- else:
- gtk.main_quit()
-
- def w(self, widget):
- '''Shortcut for getting a widget.'''
-
- return self.widgets.get_widget(widget)
-
- def update_report(self):
- '''Load currently selected report and update UI accordingly.'''
-
- # load problem report
- self.report = ProblemReport()
- self.report.load(open(self.reports[0]), binary=False)
-
- subject = self.report.get('ExecutablePath', None)
- if not subject:
- subject = self.report['Package'].split()[0]
-
- # create reduced report if there are any removed fields
- if self.report.has_removed_fields():
- (fd, self.reduced_file) = tempfile.mkstemp('.crash',
- subject.replace('/', '_') + '.')
- os.close(fd)
- self.report.write(open(self.reduced_file, 'w'))
- else:
- self.reduced_file = None
-
- package = self.report['Package'].split()[0]
- try:
- self.cur_source = self.report['SourcePackage']
- except KeyError:
- self.cur_source = package
-
- # try to get a matching .desktop file
- if self.report.has_key('DesktopFile') and os.path.exists(self.report['DesktopFile']):
- desktop_file = self.report['DesktopFile']
- else:
- desktop_file = apport_utils.find_package_desktopfile(package)
- self.desktop_entry = None
- if desktop_file:
- try:
- self.desktop_entry = xdg.DesktopEntry.DesktopEntry(desktop_file)
- except: pass
-
- # update dialog
- if self.desktop_entry:
- heading = _('Sorry, %s closed unexpectedly.') % self.desktop_entry.getName()
- elif self.report.has_key('ExecutablePath'):
- heading = _('Sorry, the program "%s" closed unexpectedly.') % os.path.basename(self.report['ExecutablePath'])
- else:
- heading = _('Sorry, %s closed unexpectedly.') % self.report['Package'].split()[0]
- if self.desktop_entry and self.report.has_key('ExecutablePath') and \
- subprocess.call(['pidof', '-x', self.report['ExecutablePath']], stdout=subprocess.PIPE) != 0:
- label = self.w('label_heading_reopen')
- label.set_markup(self.str_heading % heading)
- self.w('dialog_crash_reopen').show_all()
- else:
- label = self.w('label_heading')
- label.set_markup(self.str_heading % heading)
- self.w('dialog_crash').show_all()
-
- def on_button_reportbug_clicked(self, widget):
- '''Open bug page in browser and ask to file a bug.'''
-
- assert self.report
-
- self.w('dialog_crash').hide()
- self.w('dialog_crash_reopen').hide()
-
- mode = self.report.get('BugDisplayMode', 'list')
-
- if mode == 'file':
- url = 'https://launchpad.net/distros/ubuntu/+source/%s/+filebug' % self.cur_source
- self.w('label_bug_instructions').set_text(_('Please fill out the bug \
- report form in your web browser and give some details about what you did just \
- before the crash.'))
- else:
- url = 'https://launchpad.net/distros/ubuntu/+source/%s/+bugs' % self.cur_source
- self.w('label_bug_instructions').set_text(_('Please check the list \
- in your web browser for an already existing bug report about this problem. If \
- there is none, or you are in doubt, please create a new report.'))
-
- # update details
- self.w('window_bugreport').set_title(self.str_bugreport_title %
- self.report['Package'].split()[0])
-
- self.w('label_reportfile').set_selectable(True)
- self.update_reportfile_size()
-
- self.tree_model.clear()
- row = 0
- for key in self.report:
- keyiter = self.tree_model.insert_before(None, None)
- self.tree_model.set_value(keyiter, 0, key)
-
- valiter = self.tree_model.insert_before(keyiter, None)
- if self.report[key]:
- self.tree_model.set_value(valiter, 0, self.report[key])
- else:
- self.tree_model.set_value(valiter, 0, '(binary data)')
-
- # expand the row if the value has less than 5 lines
- if len(filter(lambda c: c == '\n', self.report[key])) < 4:
- self.w('treeview_reportdetails').expand_row(row, False)
- row += 1
-
- if self.reduced_file:
- self.w('radiobutton_complete').show()
- self.w('radiobutton_reduced').show()
- else:
- self.w('radiobutton_complete').hide()
- self.w('radiobutton_reduced').hide()
-
- self.w('window_bugreport').show()
-
- # prefer gnome-open if it exists
- try:
- if subprocess.call(['gnome-open', url]) == 0:
- return
- except OSError:
- pass
-
- webbrowser.open(url)
- return True
-
- def on_window_bugreport_delete_event(self, widget, event):
- self.w('window_bugreport').hide()
- if self.reduced_file:
- os.unlink(self.reduced_file)
- self.next()
- return True
-
- def on_button_reopen_clicked(self, widget):
- '''Reopen the crashed application.'''
-
- self.w('dialog_crash').hide()
- self.w('dialog_crash_reopen').hide()
-
- assert self.report.has_key('ProcCmdline')
-
- if os.fork() == 0:
- os.setsid()
- os.execlp('sh', 'sh', '-c', self.report.get('RespawnCommand', self.report['ProcCmdline']))
- sys.exit(1)
-
- self.next()
- return True
-
- def on_expander_details_activate(self, widget):
- # signal is sent before actually expanding/collapsing, thus this
- # requires negation
- self.w('window_bugreport').set_resizable(not self.w('expander_details').get_expanded())
-
- def update_reportfile_size(self):
- if not self.reduced_file or self.w('radiobutton_complete').get_active():
- f = self.reports[0]
- else:
- f = self.reduced_file
- self.w('label_reportfile').set_text(f)
- self.w('label_reportsize').set_text('(%s)' %
- format_filesize(os.path.getsize(f)))
-
- def on_radiobutton_complete_toggled(self, widget):
- self.update_reportfile_size()
-
- if __name__ == '__main__':
- gettext.textdomain(gettext_domain)
- gtk.glade.textdomain(gettext_domain)
- app = ApportGTK()
- gtk.main()
-
-