# -*- coding: utf-8 -*- import gtk from PluginList import getPluginInfoList from abrt_utils import _, log, log1, log2, get_verbose_level, g_verbose, warn from CCDump import * # FILENAME_xxx, CD_xxx from PluginSettingsUI import PluginSettingsUI import sys import gobject from CC_gui_functions import * import pango # assistant pages PAGE_REPORTER_SELECTOR = 0 PAGE_BACKTRACE_APPROVAL = 1 PAGE_EXTRA_INFO = 2 PAGE_CONFIRM = 3 PAGE_REPORT_DONE = 4 NO_PROBLEMS_DETECTED = -50 HOW_TO_HINT_TEXT = "1.\n2.\n3.\n" COMMENT_HINT_TEXT = _("Brief description of how to reproduce this or what you did...") MISSING_BACKTRACE_TEXT = _("Crash info doesn't contain a backtrace") DEFAULT_WIDTH = 800 DEFAULT_HEIGHT = 500 class Log(gtk.ScrolledWindow): def __init__(self, log=""): gtk.ScrolledWindow.__init__(self) self.tv = gtk.TextView() self.tv.set_editable(False) self.add(self.tv) # FIXME: log shouldn't be None self.buff = gtk.TextBuffer() self.buff.set_text(log) self.tv.set_buffer(self.buff) self.scroll = False def append_line(self, text): end_iter = self.buff.get_end_iter() self.buff.insert(end_iter, "%s\n" % text) self.scroll_to_end() def append_warning(self, text): """ stub """ pass def append_error(self, text): """ do we need this? shouldn't error be an exception? """ pass def set_scroll(self, scroll): """ enables/disables scrolling to end, when new text is added """ self.scroll = scroll def scroll_to_end(self): if self.scroll: mark = self.buff.get_insert() self.tv.scroll_mark_onscreen(mark) class ReporterAssistant(): def __init__(self, report, daemon, log=None, parent=None): self.connected_signals = [] self.plugins_cb = [] self.daemon = daemon self.updates = [] self.pdict = {} self.report = report self.parent = parent self.comment_changed = False self.howto_changed = False self.report_has_bt = False self.selected_report_events = [] self.ev_warning = None self.log = Log() self.log.set_scroll(True) """ create the assistant """ self.assistant = gtk.Assistant() self.assistant.set_icon_name("abrt") self.assistant.set_default_size(DEFAULT_WIDTH,DEFAULT_HEIGHT) self.assistant.set_size_request(DEFAULT_WIDTH,DEFAULT_HEIGHT) self.connect_signal(self.assistant, "prepare",self.on_page_prepare) self.connect_signal(self.assistant, "cancel",self.on_cancel_clicked) self.connect_signal(self.assistant, "close",self.on_close_clicked) self.connect_signal(self.assistant, "apply",self.on_apply_clicked) self.parent = parent if parent: self.assistant.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.assistant.set_transient_for(parent) else: # if we don't have parent we want to quit the mainloop on close self.assistant.set_position(gtk.WIN_POS_CENTER) ### progress bar window self.builder = gtk.Builder() builderfile = "%s/progress_window.glade" % sys.path[0] self.builder.add_from_file(builderfile) self.pBarWindow = self.builder.get_object("pBarWindow") if self.pBarWindow: self.connect_signal(self.pBarWindow, "delete_event", self.sw_delete_event_cb) pb_log_expander = self.builder.get_object("pb_log_expander") pb_log_expander.add(self.log) self.connect_signal(daemon, "analyze-complete", self.on_analyze_complete_cb, self.pBarWindow) self.connect_signal(daemon, "report-done", self.on_report_done_cb) self.connect_signal(daemon, "update", self.update_cb) # add view log button, when in verbose mode if get_verbose_level(): b_view_log = gtk.Button(_("View log")) b_view_log.connect("clicked", self.on_show_log_cb) self.assistant.add_action_widget(b_view_log) b_view_log.show() def on_show_log_cb(self, button): viewer = gtk.Window() viewer.set_icon_name("abrt") viewer.set_default_size(600,500) viewer.set_position(gtk.WIN_POS_CENTER_ON_PARENT) viewer.set_transient_for(self.assistant) vbox = gtk.VBox() viewer.add(vbox) log = Log() for line in self.updates: log.append_line(line) vbox.pack_start(log) b_close = gtk.Button(stock=gtk.STOCK_CLOSE) b_close.connect("clicked",lambda *w: viewer.destroy()) vbox.pack_start(b_close, False) viewer.show_all() # call to update the progressbar def progress_update_cb(self, *args): self.pBar.pulse() return True def on_show_bt_clicked(self, button): viewer = gtk.Window() viewer.set_icon_name("abrt") viewer.set_default_size(600,500) viewer.set_position(gtk.WIN_POS_CENTER_ON_PARENT) viewer.set_transient_for(self.assistant) vbox = gtk.VBox() viewer.add(vbox) bt_tev = gtk.TextView() # rhbz#621871 bt_tev.set_editable(False) backtrace_scroll_w = gtk.ScrolledWindow() backtrace_scroll_w.add(bt_tev) backtrace_scroll_w.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) bt_tev.set_buffer(self.backtrace_buff) vbox.pack_start(backtrace_scroll_w) b_close = gtk.Button(stock=gtk.STOCK_CLOSE) b_close.connect("clicked",lambda *w: viewer.destroy()) vbox.pack_start(b_close, False) viewer.show_all() def on_report_done_cb(self, daemon, result): self.hide_progress() STATUS = 0 MESSAGE = 1 # 0 means not succesfull #if report_status_dict[plugin][STATUS] == '0': # this first one is actually a fallback to set at least # a raw text in case when set_markup() fails for plugin, res in result.iteritems(): bug_report = gtk.Label() bug_report.set_selectable(True) bug_report.set_line_wrap(True) bug_report.set_alignment(0.0, 0.0) bug_report.set_justify(gtk.JUSTIFY_LEFT) bug_report.set_size_request(DEFAULT_WIDTH-50, -1) bug_report.set_text(result[plugin][MESSAGE]) bug_report.set_markup("%s" % markup_escape_text(result[plugin][MESSAGE])) # if the report was not succesful then this won't pass so this runs only # if report succeds and gets overwriten by the status message if result[plugin][STATUS] == '1': bug_report.set_markup(tag_urls_in_text(result[plugin][MESSAGE])) self.bug_reports_vbox.pack_start(bug_report, expand=False) bug_report.show() #if len(result[plugin][1]) > MAX_WIDTH: # self.bug_reports.set_tooltip_text(result[plugin][1]) #gui_report_dialog(result, self.parent) def cleanup_and_exit(self): self.disconnect_signals() self.assistant.destroy() if not self.parent: gtk.main_quit() def update_cb(self, daemon, message): self.updates.append(message) message = message.replace('\n',' ') self.builder.get_object("lStatus").set_text(message) self.log.append_line(message) def sw_delete_event_cb(self, widget, event, data=None): if self.timer: gobject.source_remove(self.timer) widget.hide() return True def connect_signal(self, obj, signal, callback, data=None): if data: signal_id = obj.connect(signal, callback, data) else: signal_id = obj.connect(signal, callback) log1("connected signal %s:%s" % (signal, signal_id)) self.connected_signals.append((obj, signal_id)) def disconnect_signals(self): # we need to disconnect all signals in order to break all references # to this object, otherwise python won't destroy this object and the # signals emmited by daemon will get caught by multiple instances of # this class for obj, signal_id in self.connected_signals: log1("disconnect %s:%s" % (obj, signal_id)) obj.disconnect(signal_id) def on_cancel_clicked(self, assistant, user_data=None): self.cleanup_and_exit() def on_close_clicked(self, assistant, user_data=None): self.cleanup_and_exit() def on_apply_clicked(self, assistant, user_data=None): self.send_report(self.result) def show_progress(self): if not self.pBarWindow: log1("couldn't create the progressbar window") return if self.assistant.get_property("visible"): parent = self.assistant log1("progress bar parent is self.assistant") else: log1("progress bar parent is main window") parent = self.parent if parent: self.pBarWindow.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.pBarWindow.set_transient_for(parent) else: self.pBarWindow.set_position(gtk.WIN_POS_CENTER) self.pBar = self.builder.get_object("pBar") self.pBarWindow.show_all() def hide_progress(self): try: gobject.source_remove(self.timer) except: pass self.pBarWindow.hide() def on_config_plugin_clicked(self, button, parent, plugin, image): try: ui = PluginSettingsUI(plugin, parent=parent) except Exception, ex: gui_error_message(str(ex)) return ui.hydrate() response = ui.run() if response == gtk.RESPONSE_APPLY: ui.dehydrate() if plugin.Settings.check(): try: plugin.save_settings_on_client_side() except Exception, e: gui_error_message(_("Cannot save plugin settings:\n %s" % e)) box = image.get_parent() im = gtk.Image() im.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_MENU) box.remove(image) box.pack_start(im, expand = False, fill = False) im.show() image.destroy() button.set_sensitive(False) elif response == gtk.RESPONSE_CANCEL: log1("cancel") ui.destroy() def check_settings(self, reporters): wrong_conf_plugs = [] for reporter in reporters: if reporter.Settings.check() == False: wrong_conf_plugs.append(reporter) if wrong_conf_plugs: gladefile = "%s%ssettings_wizard.glade" % (sys.path[0],"/") builder = gtk.Builder() builder.add_from_file(gladefile) dialog = builder.get_object("WrongSettings") vbWrongSettings = builder.get_object("vbWrongSettings") for plugin in wrong_conf_plugs: hbox = gtk.HBox() hbox.set_spacing(6) image = gtk.Image() image.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_MENU) button = gtk.Button(_("Configure %s options" % plugin.getName())) button.connect("clicked", self.on_config_plugin_clicked, dialog, plugin, image) hbox.pack_start(button) hbox.pack_start(image, expand = False, fill = False) vbWrongSettings.pack_start(hbox) vbWrongSettings.show_all() dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT) dialog.set_transient_for(self.assistant) dialog.set_modal(True) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: return True else: # user cancelled reporting return False else: return NO_PROBLEMS_DETECTED def warn_user(self, warnings): # FIXME: show in lError #self.lErrors = self.builder.get_object("lErrors") warning_lbl = None for warning in warnings: if warning_lbl: warning_lbl += "\n• %s" % warning else: warning_lbl = "• %s" % warning # fallback self.lbl_errors.set_label(warning_lbl) self.lbl_errors.set_markup(warning_lbl) self.errors_hbox.show_all() #fErrors.show_all() def hide_warning(self): self.errors_hbox.hide() def allow_send(self, send_toggle): self.hide_warning() #bSend = self.builder.get_object("bSend") SendBacktrace = send_toggle.get_active() send = True error_msgs = [] rating_required = False ###for reporter in self.selected_report_events: ### if "RatingRequired" in reporter.Settings.keys(): ### if reporter.Settings["RatingRequired"] == "yes": ### rating_required = True ### log1(_("Rating is required by the %s plugin") % reporter) ###if self.selected_report_events and not rating_required: ### log1(_("Rating is not required by any plugin, skipping the check...")) try: rating = int(self.result[FILENAME_RATING][CD_CONTENT]) log1(_("Rating is %s" % rating)) except Exception, ex: rating = None log1(_("Crashdump doesn't have rating => we suppose it's not required")) # FIXME: temporary warning about low rating because of # removal the code that checks if reporter requires rating if rating != None and rating < 4: error_msgs.append(_("The backtrace is incomplete, please make sure" " you provide the steps to reproduce.")) # active buttons acording to required fields # if an backtrace has rating use it if not SendBacktrace: send = False error_msgs.append(_("You should check the backtrace for sensitive data.")) error_msgs.append(_("You must agree with sending the backtrace.")) # we have both SendBacktrace and rating # if analyzer doesn't provide the rating, then we suppose that it's # not required e.g.: kerneloops, python if rating_required and rating != None: try: package = self.result[FILENAME_PACKAGE][CD_CONTENT] # if we don't have package for some reason except: package = None # not usable report if rating < 3: if package: error_msgs.append(_("Reporting disabled because the backtrace is unusable.\nPlease try to install debuginfo manually using the command: debuginfo-install %s \nthen use the Refresh button to regenerate the backtrace." % self.report.getPackageName())) else: error_msgs.append(_("Reporting disabled because the backtrace is unusable.")) send = False # probably usable 3 elif rating < 4: error_msgs.append(_("The backtrace is incomplete, please make sure you provide the steps to reproduce.")) if error_msgs: self.warn_user(error_msgs) #bSend.set_sensitive(send) self.assistant.set_page_complete(self.pdict_get_page(PAGE_BACKTRACE_APPROVAL), send) def on_page_prepare(self, assistant, page): if page == self.pdict_get_page(PAGE_REPORTER_SELECTOR): pass # this is where dehydrate happens elif page == self.pdict_get_page(PAGE_EXTRA_INFO): if not self.howto_changed: # howto buff = gtk.TextBuffer() try: buff.set_text(self.result[FILENAME_REPRODUCE][CD_CONTENT]) self.howto_changed = True except KeyError: buff.set_text(HOW_TO_HINT_TEXT) self.howto_tev.set_buffer(buff) # don't refresh the comment if user changed it if not self.comment_changed: # comment buff = gtk.TextBuffer() try: buff.set_text(self.result[FILENAME_COMMENT][CD_CONTENT]) self.comment_changed = True except KeyError: buff.set_text(COMMENT_HINT_TEXT) self.comment_tev.set_buffer(buff) self.on_howto_changed(self.howto_tev, None, self.howto_vbox, page) elif page == self.pdict_get_page(PAGE_CONFIRM): # howto if self.howto_changed: howto_buff = self.howto_tev.get_buffer() howto_text = howto_buff.get_text(howto_buff.get_start_iter(), howto_buff.get_end_iter()) # user has changed the steps to reproduce self.steps.set_text(howto_text) self.result[FILENAME_REPRODUCE] = [CD_TXT, 'y', howto_text] else: self.steps.set_text(_("You did not provide any steps to reproduce.")) try: del self.result[FILENAME_REPRODUCE] except KeyError: # if this is a first time, then we don't have key FILENAME_REPRODUCE pass #comment if self.comment_changed: comment_buff = self.comment_tev.get_buffer() comment_text = comment_buff.get_text(comment_buff.get_start_iter(), comment_buff.get_end_iter()) # user has changed the comment self.comments.set_text(comment_text) self.result[FILENAME_COMMENT] = [CD_TXT, 'y', comment_text] else: self.comments.set_text(_("You did not provide any comments.")) try: del self.result[FILENAME_COMMENT] except KeyError: # if this is a first time, then we don't have key FILENAME_COMMENT pass # backtrace backtrace_text = self.backtrace_buff.get_text(self.backtrace_buff.get_start_iter(), self.backtrace_buff.get_end_iter()) if self.report_has_bt: self.result[FILENAME_BACKTRACE] = [CD_TXT, 'y', backtrace_text] if page == self.pdict_get_page(PAGE_BACKTRACE_APPROVAL): self.allow_send(self.backtrace_cb) def send_report(self, report): try: self.show_progress() self.timer = gobject.timeout_add(100, self.progress_update_cb) pluginlist = getPluginInfoList(self.daemon) reporters_settings = pluginlist.getReporterPluginsSettings() log2("Report(report, reporters, settings):") log2(" result:%s", str(report)) # Careful, this will print reporters_settings["Password"] too log2(" settings:%s", str(reporters_settings)) self.daemon.Report(report, self.selected_report_events, reporters_settings) log2("Report() returned") #self.hydrate() except Exception, ex: self.hide_progress() gui_error_message(_("Reporting failed!\n%s" % ex)) def on_plugin_toggled(self, plugin, plugins, event_name, page): complete = False if plugin.get_active(): log1("Plugin >>%s<< activated" % event_name) self.selected_report_events.append(event_name) ###check_result = self.check_settings([reporter]) ###if check_result == NO_PROBLEMS_DETECTED: ### pass ###elif check_result: ### page_n = self.assistant.get_current_page() ### self.assistant.set_page_complete(page, True) ### self.assistant.set_current_page(page_n+1) ###else: ### plugin.set_active(False) else: self.selected_report_events.remove(event_name) log1("Plugin >>%s<< de-activated" % event_name) if self.selected_report_events: complete = True log1("Selected reporters: %s" % [x for x in self.selected_report_events]) self.assistant.set_page_complete(page, complete) def on_bt_toggled(self, togglebutton, page): self.allow_send(togglebutton) def pdict_add_page(self, page, name): # FIXME try, except?? if name not in self.pdict: self.pdict[name] = page else: warn("The page %s is already in the dictionary" % name) #raise Exception("The page %s is already in the dictionary" % name) def pdict_get_page(self, name): try: return self.pdict[name] except Exception, e: log2(e) return None def prepare_page_1(self): plugins_cb = [] page = gtk.VBox(spacing=10) page.set_border_width(10) self.assistant.insert_page(page, PAGE_REPORTER_SELECTOR) lbl_default_info = gtk.Label() lbl_default_info.set_line_wrap(True) lbl_default_info.set_alignment(0.0, 0.0) lbl_default_info.set_justify(gtk.JUSTIFY_LEFT) lbl_default_info.set_size_request(DEFAULT_WIDTH-50, -1) lbl_default_info.set_markup(_("It looks like an application from the " "package %s has crashed " "on your system. It is a good idea to send " "a bug report about this issue. The report " "will provide software maintainers with " "information essential in figuring out how " "to provide a bug fix for you.\n\n" "Please review the information that follows " "and modify it as needed to ensure your bug " "report does not contain any sensitive data " "you would rather not share.\n\n" "Select where you would like to report the " "bug, and press 'Forward' to continue.") % self.report.getPackageName()) page.pack_start(lbl_default_info, expand=True, fill=True) vbox_plugins = gtk.VBox() page.pack_start(vbox_plugins) # add checkboxes for enabled reporters # the logic for package specific reporters has been moved # to abrt-handle-crashdump with configuration in abrt_events.conf pluginlist = getPluginInfoList(self.daemon) self.selected_report_events = [] self.possible_report_events = [] for event_name in self.report.get_report_event_names(): log1("Adding >>%s<< to report events", event_name) self.possible_report_events.append(event_name) if len(self.possible_report_events) == 0: # WTF?? Try running "report" event anyway... self.possible_report_events.append("report") for event_name in self.possible_report_events: display_name = "Report" if event_name != "report": # then it's "report_foo" display_name = event_name[7:] cb = gtk.CheckButton(display_name) cb.connect("toggled", self.on_plugin_toggled, plugins_cb, event_name, page) plugins_cb.append(cb) vbox_plugins.pack_start(cb, fill=True, expand=False) # automatically select the report event if we have only one if len(self.possible_report_events) == 1: self.assistant.set_page_complete(page, True) log1(_("Only one reporter plugin is configured.")) # this is safe, because in python the variable is visible even # outside the for loop it was created (in this case, cb) cb.set_active(True) self.pdict_add_page(page, PAGE_REPORTER_SELECTOR) self.assistant.set_page_type(page, gtk.ASSISTANT_PAGE_INTRO) self.assistant.set_page_title(page, _("Send a bug report")) page.show_all() def on_bt_copy(self, button, bt_text_view): buff = bt_text_view.get_buffer() bt_text = buff.get_text(buff.get_start_iter(), buff.get_end_iter()) clipboard = gtk.clipboard_get() clipboard.set_text(bt_text) def tv_text_changed(self, textview, default_text): buff = textview.get_buffer() text = buff.get_text(buff.get_start_iter(), buff.get_end_iter()) if text: if text and text != default_text: return True return False else: buff.set_text(default_text) def on_howto_focusout_cb(self, textview, event): self.howto_changed = self.tv_text_changed(textview, HOW_TO_HINT_TEXT) def on_comment_focusin_cb(self, textview, event): if not self.comment_changed: # clear "hint" text by supplying a fresh, empty TextBuffer textview.set_buffer(gtk.TextBuffer()) def on_comment_focusout_cb(self, textview, event): self.comment_changed = self.tv_text_changed(textview, COMMENT_HINT_TEXT) def prepare_page_2(self): page = gtk.VBox(spacing=10) page.set_border_width(10) lbl_default_info = gtk.Label() lbl_default_info.set_line_wrap(True) lbl_default_info.set_alignment(0.0, 0.0) lbl_default_info.set_justify(gtk.JUSTIFY_FILL) lbl_default_info.set_size_request(DEFAULT_WIDTH-50, -1) lbl_default_info.set_text(_("Below is the backtrace associated with your " "crash. A crash backtrace provides developers with details about " "how the crash happened, helping them track down the source of the " "problem.\n\n" "Please review the backtrace below and modify it as needed to " "ensure your bug report does not contain any sensitive data you would " "rather not share:") ) page.pack_start(lbl_default_info, expand=False, fill=True) self.backtrace_tev = gtk.TextView() self.backtrace_tev.set_wrap_mode(gtk.WRAP_WORD) # global? self.backtrace_buff = gtk.TextBuffer() # a tag to highlight searched word # OPTION search background self.backtrace_buff.create_tag("search_result_bg", background="red") # OPTION current possiotion bg self.backtrace_buff.create_tag("current_pos_bg", background="yellow") #self.backtrace_buff.set_text(self.report[FILENAME_BACKTRACE][CD_CONTENT]) self.backtrace_tev.set_buffer(self.backtrace_buff) backtrace_scroll_w = gtk.ScrolledWindow() backtrace_scroll_w.add(self.backtrace_tev) backtrace_scroll_w.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) # backtrace hbox_bt = gtk.HBox() vbox_bt = gtk.VBox(homogeneous=False, spacing=5) hbox_bt.pack_start(vbox_bt) backtrace_alignment = gtk.Alignment() hbox_bt.pack_start(backtrace_alignment, expand=False, padding=10) vbox_bt.pack_start(backtrace_scroll_w) self.last_highlighted_text = [] self.last_search = 0 # highlights the arrow (or any other widget) def highlight(widget, event): widget.drag_highlight() def unhighlight(widget, event): widget.drag_unhighlight() # highlights the current possition with yellow def highlight_current(): start_it = self.backtrace_buff.get_iter_at_mark(self.last_highlighted_text[self.last_search][0]) end_it = self.backtrace_buff.get_iter_at_mark(self.last_highlighted_text[self.last_search][1]) self.backtrace_buff.apply_tag_by_name("current_pos_bg", start_it, end_it) self.backtrace_buff.place_cursor(start_it) self.backtrace_tev.scroll_to_iter( start_it, 0, use_align=False, xalign=0.5, yalign=0.5) # remove the yellow highlighting from last position def unhighlight_last(): start_it = self.backtrace_buff.get_iter_at_mark(self.last_highlighted_text[self.last_search][0]) end_it = self.backtrace_buff.get_iter_at_mark(self.last_highlighted_text[self.last_search][1]) self.backtrace_buff.remove_tag_by_name("current_pos_bg", start_it, end_it) def highlight_result(position_mark): start_it = self.backtrace_buff.get_iter_at_mark(position_mark[0]) end_it = self.backtrace_buff.get_iter_at_mark(position_mark[1]) self.backtrace_buff.apply_tag_by_name("search_result_bg", start_it, end_it) def search_down(widget, event): if event.button == 1: if self.last_search+1 < len(self.last_highlighted_text): unhighlight_last() self.last_search += 1 # mark current possition yellow highlight_current() self.lbl_occur.set_text(_("Found %i occurence(s) [at: %i of %i]" % (len(self.last_highlighted_text), self.last_search+1, len(self.last_highlighted_text) ))) def search_up(widget, event): if event.button == 1: if self.last_highlighted_text and (self.last_search-1 >= 0): unhighlight_last() self.last_search -= 1 # mark current possition yellow highlight_current() self.lbl_occur.set_text(_("Found %i occurence(s) [at: %i of %i]" % (len(self.last_highlighted_text), self.last_search+1, len(self.last_highlighted_text) ))) def highlight_search(entry): if self.last_highlighted_text: # clear the search self.last_highlighted_text = [] self.last_search = 0 # clears everything # this will remove even tags, that might be added from a different # place start_iter = self.backtrace_buff.get_start_iter() end_iter = self.backtrace_buff.get_end_iter() self.backtrace_buff.remove_all_tags(start_iter, end_iter) search_start_iter = start_iter search_text = entry.get_text() self.lbl_occur.set_text("") if search_text: while True: match = search_start_iter.forward_search(search_text, gtk.TEXT_SEARCH_TEXT_ONLY, limit=None) if not match: break start_mark = self.backtrace_buff.create_mark(None, match[0]) end_mark = self.backtrace_buff.create_mark(None, match[1]) self.last_highlighted_text.append((start_mark, end_mark)) highlight_result(self.last_highlighted_text[-1]) search_start_iter = match[1] if self.last_highlighted_text: highlight_current() self.lbl_occur.set_text(_("Found %i occurence(s) [at: %i of %i]" % (len(self.last_highlighted_text), self.last_search+1, len(self.last_highlighted_text) ))) self.s_timeout = 0 def search_timeout(entry): if self.s_timeout: gobject.source_remove(self.s_timeout) self.s_timeout = gobject.timeout_add(500, highlight_search, entry) #arrows vbox vbox_arrows = gtk.VBox() # up arrow arrow_up = gtk.Arrow(gtk.ARROW_UP, gtk.SHADOW_NONE) event_up = gtk.EventBox() event_up.add(arrow_up) event_up.connect("enter-notify-event", highlight) event_up.connect("leave-notify-event", unhighlight) event_up.connect("button-press-event", search_up) # down arrow event_down = gtk.EventBox() arrow_down = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE) event_down.add(arrow_down) event_down.connect("enter-notify-event", highlight) event_down.connect("leave-notify-event", unhighlight) event_down.connect("button-press-event", search_down) vbox_arrows.pack_start(event_up) vbox_arrows.pack_start(event_down) # search box hbox_search_wrap = gtk.HBox() search_entry = gtk.Entry() search_entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_FIND) search_entry.set_icon_activatable(gtk.ENTRY_ICON_SECONDARY, False) search_entry.connect("changed", search_timeout) lbl_search = gtk.Label(_("Search:")) hbox_search_wrap.pack_start(lbl_search, False, False) hbox_search_wrap.pack_start(search_entry) hbox_search_wrap.pack_start(vbox_arrows, False, False) # the big hbox align_search = gtk.Alignment() hbox_search = gtk.HBox(homogeneous=True) hbox_search.pack_start(align_search) vb = gtk.VBox() self.lbl_occur = gtk.Label() vb.pack_start(hbox_search_wrap) vb.pack_start(self.lbl_occur) hbox_search.pack_start(vb) vbox_bt.pack_start(hbox_search, False, False) # bad backtrace, reporting disabled # warnings about wrong bt self.errors_hbox = gtk.HBox() self.warning_image = gtk.Image() self.warning_image.set_from_stock(gtk.STOCK_DIALOG_WARNING,gtk.ICON_SIZE_DIALOG) self.lbl_errors = gtk.Label() self.lbl_errors.set_line_wrap(True) #self.lbl_errors.set_alignment(0.0, 0.0) self.lbl_errors.set_justify(gtk.JUSTIFY_FILL) self.lbl_errors.set_size_request(DEFAULT_WIDTH-50, -1) self.lbl_errors.set_selectable(True) self.errors_hbox.pack_start(self.warning_image, False, False) self.errors_hbox.pack_start(self.lbl_errors) ### vbox_bt.pack_start(self.errors_hbox, False, False) hbox_buttons = gtk.HBox(homogeneous=True) button_alignment = gtk.Alignment() b_refresh = gtk.Button(_("Refresh")) b_refresh.connect("clicked", self.hydrate, 1) b_copy = gtk.Button(_("Copy")) b_copy.connect("clicked", self.on_bt_copy, self.backtrace_tev) hbox_buttons.pack_start(button_alignment) hbox_buttons.pack_start(b_refresh, expand=False, fill=True) hbox_buttons.pack_start(b_copy, expand=False, fill=True) vbox_bt.pack_start(hbox_buttons, expand=False, fill=False) self.backtrace_cb = gtk.CheckButton(_("I agree with submitting the backtrace")) self.backtrace_cb.connect("toggled", self.on_bt_toggled, page) self.assistant.insert_page(page, PAGE_BACKTRACE_APPROVAL) self.pdict_add_page(page, PAGE_BACKTRACE_APPROVAL) self.assistant.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT) self.assistant.set_page_title(page, _("Approve the backtrace")) page.pack_start(hbox_bt) page.pack_start(self.backtrace_cb, expand=False, fill=False) page.show_all() def show_warning_for_tev(self, vbox, message): if type(vbox) == gtk.VBox: if not self.ev_warning: lbl_warning = gtk.Label(message) self.ev_warning = gtk.EventBox() self.ev_warning.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#CC3333')) self.ev_warning.add(lbl_warning) vbox.pack_start(self.ev_warning, False, False) self.ev_warning.show_all() else: log("Can't show warning because page is not a VBox") # TODO: per page warning def hide_warning_for_tev(self, vbox=None): if self.ev_warning: self.ev_warning.hide() def is_howto_filled(self, howto_buff): text = "" start_it = howto_buff.get_start_iter() end_it = howto_buff.get_end_iter() text = howto_buff.get_text(start_it, end_it) # ingore new lines and spaces in the text clean_text = text.strip().replace("\n","").replace(" ","") clean_howto_hint = HOW_TO_HINT_TEXT.strip().replace("\n","").replace(" ","") # howto has to be longer then the hint # and must not be same as the hint if ( len(clean_text) >= len(clean_howto_hint) and clean_text != clean_howto_hint ): return True else: return False def on_howto_changed(self, textview, event, vbox, page): textbuffer = textview.get_buffer() howto_filled = self.is_howto_filled(textbuffer) if not howto_filled: self.show_warning_for_tev(vbox, _("You need to fill the how to before you can proceed...")) else: self.hide_warning_for_tev() self.assistant.set_page_complete(page, self.is_howto_filled(textbuffer)) def prepare_page_3(self): page = gtk.VBox(spacing=10) page.set_border_width(10) #lbl_default_info = gtk.Label() #lbl_default_info.set_line_wrap(True) #lbl_default_info.set_alignment(0.0, 0.0) #lbl_default_info.set_justify(gtk.JUSTIFY_FILL) #lbl_default_info.set_size_request(600, -1) #page.pack_start(lbl_default_info, expand=False, fill=True) details_hbox = gtk.HBox() details_hbox.set_border_width(10) details_alignment = gtk.Alignment() details_vbox = gtk.VBox(spacing=10) details_hbox.pack_start(details_vbox) details_hbox.pack_start(details_alignment, expand=False, padding=30) # how to reproduce self.howto_vbox = gtk.VBox(spacing=0) howto_lbl = gtk.Label(_("How did this crash happen (step-by-step)? " "How would you reproduce it?")) howto_lbl.set_alignment(0.0, 0.0) howto_lbl.set_justify(gtk.JUSTIFY_FILL) self.howto_tev = gtk.TextView() self.howto_tev.set_wrap_mode(gtk.WRAP_WORD) self.howto_tev.set_accepts_tab(False) self.howto_tev.connect("focus-out-event", self.on_howto_focusout_cb) self.howto_tev.connect("key-release-event", self.on_howto_changed, self.howto_vbox, page) howto_buff = gtk.TextBuffer() howto_buff.set_text(HOW_TO_HINT_TEXT) self.howto_tev.set_buffer(howto_buff) howto_scroll_w = gtk.ScrolledWindow() howto_scroll_w.add(self.howto_tev) howto_scroll_w.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.howto_vbox.pack_start(howto_lbl, expand=False, fill=True) self.howto_vbox.pack_start(howto_scroll_w) # comment comment_vbox = gtk.VBox(spacing=5) comment_lbl = gtk.Label(_("Are there any comments you would like to share " "with the software maintainers?")) comment_lbl.set_alignment(0.0, 0.0) comment_lbl.set_justify(gtk.JUSTIFY_FILL) self.comment_tev = gtk.TextView() self.comment_tev.set_wrap_mode(gtk.WRAP_WORD) self.comment_tev.set_accepts_tab(False) self.comment_tev.connect("focus-in-event", self.on_comment_focusin_cb) self.comment_tev.connect("focus-out-event", self.on_comment_focusout_cb) comment_scroll_w = gtk.ScrolledWindow() comment_scroll_w.add(self.comment_tev) comment_scroll_w.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) comment_vbox.pack_start(comment_lbl, expand=False, fill=True) comment_vbox.pack_start(comment_scroll_w) details_vbox.pack_start(self.howto_vbox) details_vbox.pack_start(comment_vbox) self.assistant.insert_page(page, PAGE_EXTRA_INFO) self.pdict_add_page(page, PAGE_EXTRA_INFO) self.assistant.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT) self.assistant.set_page_title(page, _("Provide additional details")) self.assistant.set_page_complete(page, True) tip_hbox = gtk.HBox() tip_image = gtk.Image() tip_lbl = gtk.Label("") tip_lbl.set_alignment(0.0, 0.0) tip_lbl.set_justify(gtk.JUSTIFY_FILL) tip_lbl.set_markup(_("Tip: Your comments are not private. " "Please watch what you say accordingly.")) #tip_hbox.pack_start(tip_image) tip_hbox.pack_start(tip_lbl, expand=False) page.pack_start(details_hbox) tip_alignment = gtk.Alignment() #page.pack_start(tip_alignment, padding=10) page.pack_start(tip_hbox, expand=False) page.show_all() def prepare_page_4(self): lines_in_table = {} width, height = self.assistant.get_size() def add_info_to_table(table, heading, text): line = 0 if table in lines_in_table: line = lines_in_table[table] heading_lbl = gtk.Label() heading_lbl.set_alignment(0.0, 0.0) heading_lbl.set_justify(gtk.JUSTIFY_LEFT) heading_lbl.set_markup("%s:" % heading) table.attach(heading_lbl, 0, 1, line, line+1, xoptions=gtk.FILL, yoptions=gtk.EXPAND|gtk.FILL, xpadding=5, ypadding=5) lbl = gtk.Label(text) lbl.set_line_wrap(True) lbl.set_size_request(width/4, -1) lbl.set_alignment(0.0, 0.0) lbl.set_justify(gtk.JUSTIFY_LEFT) table.attach(lbl, 1, 2, line, line+1, xoptions=gtk.FILL, yoptions=gtk.EXPAND|gtk.FILL, xpadding=5, ypadding=5) lines_in_table[table] = line+1 page = gtk.VBox(spacing=20) page.set_border_width(10) self.assistant.insert_page(page, PAGE_CONFIRM) self.pdict_add_page(page, PAGE_CONFIRM) self.assistant.set_page_type(page, gtk.ASSISTANT_PAGE_CONFIRM) self.assistant.set_page_title(page, _("Confirm and send the report")) self.assistant.set_page_complete(page, True) summary_lbl = gtk.Label(_("Below is a summary of your bug report. " "Please click 'Apply' to submit it.")) summary_lbl.set_alignment(0.0, 0.0) summary_lbl.set_justify(gtk.JUSTIFY_FILL) basic_details_lbl = gtk.Label() basic_details_lbl.set_markup(_("Basic details")) basic_details_lbl.set_alignment(0.0, 0.0) basic_details_lbl.set_justify(gtk.JUSTIFY_FILL) summary_table_left = gtk.Table(rows=4, columns=2) summary_table_right = gtk.Table(rows=4, columns=2) # left table add_info_to_table(summary_table_left, _("Component"), "%s" % self.report.get_component()) add_info_to_table(summary_table_left, _("Package"), "%s" % self.report.getPackageName()) add_info_to_table(summary_table_left, _("Executable"), "%s" % self.report.getExecutable()) add_info_to_table(summary_table_left, _("Cmdline"), "%s" % self.report.get_cmdline()) #right table add_info_to_table(summary_table_right, _("Architecture"), "%s" % self.report.get_arch()) add_info_to_table(summary_table_right, _("Kernel"), "%s" % self.report.get_kernel()) add_info_to_table(summary_table_right, _("Release"),"%s" % self.report.get_release()) add_info_to_table(summary_table_right, _("Reason"), "%s" % self.report.get_reason()) summary_hbox = gtk.HBox(spacing=5, homogeneous=True) left_table_vbox = gtk.VBox() left_table_vbox.pack_start(summary_table_left, expand=False, fill=False) left_table_vbox.pack_start(gtk.Alignment()) summary_hbox.pack_start(left_table_vbox, expand=False, fill=True) summary_hbox.pack_start(summary_table_right, expand=False, fill=True) # backtrace backtrace_lbl = gtk.Label() backtrace_lbl.set_markup(_("Backtrace")) backtrace_lbl.set_alignment(0.0, 0.5) backtrace_lbl.set_justify(gtk.JUSTIFY_LEFT) backtrace_show_btn = gtk.Button(_("Click to view...")) backtrace_show_btn.connect("clicked", self.on_show_bt_clicked) backtrace_hbox = gtk.HBox(homogeneous=True) hb = gtk.HBox() hb.pack_start(backtrace_lbl) hb.pack_start(backtrace_show_btn, expand=False) backtrace_hbox.pack_start(hb) alignment = gtk.Alignment() backtrace_hbox.pack_start(alignment) # steps to reporoduce reproduce_lbl = gtk.Label() reproduce_lbl.set_markup(_("Steps to reproduce:")) reproduce_lbl.set_alignment(0.0, 0.0) reproduce_lbl.set_justify(gtk.JUSTIFY_LEFT) self.steps = gtk.Label() self.steps.set_alignment(0.0, 0.0) self.steps.set_justify(gtk.JUSTIFY_LEFT) self.steps.set_line_wrap(True) self.steps.set_line_wrap_mode(pango.WRAP_CHAR) self.steps.set_size_request(int(DEFAULT_WIDTH*0.8), -1) #self.steps_lbl.set_text("1. Fill in information about step 1.\n" # "2. Fill in information about step 2.\n" # "3. Fill in information about step 3.\n") steps_aligned_hbox = gtk.HBox() self.steps_hbox = gtk.HBox(spacing=10) self.steps_hbox.pack_start(reproduce_lbl) self.steps_hbox.pack_start(self.steps) steps_aligned_hbox.pack_start(self.steps_hbox, expand=False) steps_aligned_hbox.pack_start(gtk.Alignment()) # comments comments_lbl = gtk.Label() comments_lbl.set_markup(_("Comments:")) comments_lbl.set_alignment(0.0, 0.0) comments_lbl.set_justify(gtk.JUSTIFY_LEFT) self.comments = gtk.Label(_("No comment provided!")) self.comments.set_line_wrap(True) self.comments.set_line_wrap_mode(pango.WRAP_CHAR) self.comments.set_size_request(int(DEFAULT_WIDTH*0.8), -1) comments_hbox = gtk.HBox(spacing=10) comments_hbox.pack_start(comments_lbl) comments_hbox.pack_start(self.comments) comments_aligned_hbox = gtk.HBox() comments_aligned_hbox.pack_start(comments_hbox, expand=False) comments_aligned_hbox.pack_start(gtk.Alignment()) # pack all into the page summary_vbox = gtk.VBox(spacing=20) summary_vbox.pack_start(summary_hbox, expand=False) summary_vbox.pack_start(backtrace_hbox, expand=False) summary_vbox.pack_start(steps_aligned_hbox, expand=False) summary_vbox.pack_start(comments_aligned_hbox, expand=False) summary_scroll = gtk.ScrolledWindow() summary_scroll.set_shadow_type(gtk.SHADOW_NONE) summary_scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) scroll_viewport = gtk.Viewport() scroll_viewport.set_shadow_type(gtk.SHADOW_NONE) scroll_viewport.add(summary_vbox) summary_scroll.add(scroll_viewport) page.pack_start(summary_lbl, expand=False) page.pack_start(basic_details_lbl, expand=False) page.pack_start(summary_scroll) page.show_all() def prepare_page_5(self): page = gtk.VBox(spacing=20) page.set_border_width(10) self.assistant.insert_page(page, PAGE_REPORT_DONE) self.pdict_add_page(page, PAGE_REPORT_DONE) self.assistant.set_page_type(page, gtk.ASSISTANT_PAGE_SUMMARY) self.assistant.set_page_title(page, _("Finished sending the bug report")) bug_reports_lbl = gtk.Label() bug_reports_lbl.set_alignment(0.0, 0.0) bug_reports_lbl.set_justify(gtk.JUSTIFY_LEFT) bug_reports_lbl.set_markup(_("Bug reports:")) width, height = self.assistant.get_size() self.bug_reports_vbox = gtk.VBox(spacing=5) self.bug_reports_vbox.pack_start(bug_reports_lbl, expand=False) page.pack_start(self.bug_reports_vbox) page.show_all() def __del__(self): log1("wizard: about to be deleted") def on_analyze_complete_cb(self, daemon, result, pBarWindow): try: gobject.source_remove(self.timer) except: pass self.pBarWindow.hide() if not result: gui_error_message(_("Unable to get report!\nIs debuginfo missing?")) return self.result = result # set the backtrace text try: self.backtrace_buff.set_text(self.result[FILENAME_BACKTRACE][CD_CONTENT]) self.report_has_bt = True except: self.backtrace_buff.set_text(MISSING_BACKTRACE_TEXT) self.backtrace_cb.set_active(True) log1("Crash info doesn't contain a backtrace, is it disabled?") self.allow_send(self.backtrace_cb) self.show() def hydrate(self, button=None, force=0): if not force: self.prepare_page_1() self.prepare_page_2() self.prepare_page_3() self.prepare_page_4() self.prepare_page_5() # FIXME don't duplicate the code, move to function self.show_progress() self.timer = gobject.timeout_add(100, self.progress_update_cb) # show the report window with selected report # when getReport is done it emits "analyze-complete" and on_analyze_complete_cb is called # FIXME: does it make sense to change it to use callback rather then signal emitting? try: self.daemon.start_job(self.report.getDumpDir(), force) except Exception, ex: # FIXME #3 dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not receive a reply # do this async and wait for yum to end with debuginfoinstal if self.timer: gobject.source_remove(self.timer) self.pBarWindow.hide() gui_error_message(_("Error acquiring the report: %s" % ex)) return def show(self): self.assistant.show() if __name__ == "__main__": wiz = ReporterAssistant() wiz.show() gtk.main()