403Webshell
Server IP : 104.21.25.180  /  Your IP : 162.159.115.41
Web Server : Apache/2.4.37
System : Linux almalinux.duckdns.org 4.18.0-553.111.1.el8_10.x86_64 #1 SMP Sun Mar 8 20:06:07 EDT 2026 x86_64
User : ricodeal ( 1046)
PHP Version : 7.4.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /lib64/python3.6/site-packages/gnome_abrt/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /lib64/python3.6/site-packages/gnome_abrt/views.py
# coding=UTF-8

## Copyright (C) 2012 ABRT team <[email protected]>
## Copyright (C) 2001-2005 Red Hat, Inc.

## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA

import os
import time
import logging
import traceback
import datetime

#pygobject
import gi
#pylint: disable=E0611
from gi.repository import Gtk
#pylint: disable=E0611
from gi.repository import Gdk
#pylint: disable=E0611
from gi.repository import GObject
#pylint: disable=E0611
from gi.repository import Gio
#pylint: disable=E0611
from gi.repository import Pango
#pylint: disable=E0611
from gi.repository import GLib

import humanize

from gnome_abrt import GNOME_ABRT_UI_DIR, problems, config, wrappers, errors, desktop
from gnome_abrt.tools import fancydate, smart_truncate, load_icon
from gnome_abrt.tools import set_icon_from_pixbuf_with_scale
from gnome_abrt.l10n import _, C_, GETTEXT_PROGNAME

gi.require_version('Gtk', '3.0')

def list_box_row_to_problem(list_box_row):
    return list_box_row.get_children()[0].get_problem()


def list_box_row_set_values(list_box_row, values):
    return list_box_row.get_children()[0].set_values(values)


class ProblemsFilter:

    def __init__(self, list_box, list_box_selection):
        self._pattern = ""
        self._list_box = list_box
        self._list_box.set_filter_func(lambda row, _: self.match(row), None)
        self._list_box_selection = list_box_selection

    def set_pattern(self, pattern):
        self._pattern = pattern
        self._list_box.invalidate_filter()

        i = 0
        problem_row = self._list_box.get_row_at_index(i)
        while problem_row is not None:
            if self.match(problem_row):
                self._list_box.select_row(problem_row)
                break

            i += 1
            problem_row = self._list_box.get_row_at_index(i)

        if problem_row is None:
            self._list_box_selection.unselect_all()

    def match(self, list_box_row):
        # None nevere matches the patter
        if list_box_row is None:
            return False

        # Empty string mathces everything
        if not self._pattern:
            return True

        problem = list_box_row_to_problem(list_box_row)

        for i in ['component', 'reason', 'executable', 'package']:
            if problem[i] is None:
                logging.debug("Problem '{0}' doesn't have '{1}"
                                .format(problem.problem_id, i))
                continue

            # _pattern is 'ascii' and problem[i] is 'dbus.String'
            val = str(problem[i])
            if val and self._pattern in val:
                return True

        # Check Bug tracker ID
        if problem['is_reported']:
            for sbm in problem['submission']:
                if problems.Problem.Submission.URL != sbm.rtype:
                    continue

                # _pattern is 'str' and sbm.data is 'dbus.String', so we need
                # to convert sbm.data to a regular 'str'
                rid = str(sbm.data)
                rid = rid.rstrip('/').split('/')[-1].split('=')[-1]
                if self._pattern in rid:
                    return True

        # This might be confusing as users can't see problem ID in UI but it
        # will come in handy when you want to see particular problem discovered
        # in the system logs.
        if self._pattern in problem.problem_id:
            return True

        app = problem['application']
        if app is None or app.name is None:
            return False

        return self._pattern in app.name


def problem_to_storage_values(problem):
    app = problem.get_application()

    if app.name:
        name = app.name
    else:
        name = problem['human_type']

    if name == "kernel" or name.startswith("kernel-"):
        # Translators: if the kernel crashed we display the word "System"
        # instead of "kernel". In this context "System" is like a proper
        # package name, probably a nominative noun.
        name = C_("package name", "System")

    problem_type = problem['type']
    if problem_type == "CCpp":
        # Translators: These are the problem types displayed in the problem
        # list under the application name
        problem_type = _("Application Crash")
    elif problem_type == "vmcore":
        problem_type = _("System Crash")
    elif problem_type == "Kerneloops":
        problem_type = _("System Failure")
    else:
        problem_type = _("Misbehavior")

    return (smart_truncate(name, length=40),
            fancydate(problem['date_last']),
            problem_type,
            problem['count'],
            problem)


#pylint: disable=W0613
def time_sort_func(first_row, second_row, trash):
    fst_problem = list_box_row_to_problem(first_row)
    scn_problem = list_box_row_to_problem(second_row)
    # skip invalid problems which were marked invalid while sorting
    if (fst_problem.problem_id in trash or
        scn_problem.problem_id in trash):
        return 0

    try:
        lhs = fst_problem['date_last'].timetuple()
        rhs = scn_problem['date_last'].timetuple()
        return time.mktime(rhs) - time.mktime(lhs)
    except errors.InvalidProblem as ex:
        trash.add(ex.problem_id)
        logging.debug(ex)
        return 0

def format_button_source_name(name, source):
    return "{0} ({1})".format(name, len(source.get_problems()))


def handle_problem_and_source_errors(func):
    """Wraps repetitive exception handling."""

    def wrapper_for_instance_function(oops_wnd, *args):
        try:
            return func(oops_wnd, *args)
        except errors.InvalidProblem as ex:
            logging.debug(traceback.format_exc())
            oops_wnd._remove_problem_from_storage(ex.problem_id)
        except errors.UnavailableSource as ex:
            logging.debug(traceback.format_exc())
            oops_wnd._disable_source(ex.source, ex.temporary)

        return None

    return wrapper_for_instance_function


class ListBoxSelection:

    def __init__(self, list_box, selection_changed):
        self._lb = list_box

        self._lb.connect('selected-rows-changed', self._on_row_selected)

        self._selection = []
        self._selection_changed = selection_changed

    def _on_row_selected(self, _):
        self._selection_changed(self)

    def unselect_all(self):
        if self._lb.get_selection_mode() == Gtk.SelectionMode.MULTIPLE:
            self._lb.unselect_all()
        else:
            selected_row = self._lb.get_selected_row()
            if selected_row is not None:
                self._lb.unselect_row(selected_row)

        self._lb.unselect_all()

    def get_selected_rows(self):
        return [list_box_row_to_problem(lbr)
                for lbr in self._lb.get_selected_rows()]


class ProblemListBoxCell(Gtk.Box):

    def __init__(self, problem_values):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL,
                spacing=0, homogeneous=False)

        self.get_style_context().add_class('problem-cell')

        self._problem = problem_values[4]

        self._hbox1 = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 12)
        self._hbox2 = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 12)

        self._lbl_app = Gtk.Label.new(problem_values[0])
        self._lbl_app.set_halign(Gtk.Align.START)
        self._lbl_app.set_alignment(0.0, 0.5)
        self._lbl_app.set_ellipsize(Pango.EllipsizeMode.END)
        self._lbl_app.set_width_chars(15)
        self._lbl_app.get_style_context().add_class('app-name-label')

        self._lbl_date = Gtk.Label.new(problem_values[1])
        self._lbl_date.set_halign(Gtk.Align.END)
        self._lbl_date.get_style_context().add_class('dim-label')

        self._lbl_type = Gtk.Label.new(problem_values[2])
        self._lbl_type.set_halign(Gtk.Align.START)
        self._lbl_type.set_alignment(0.0, 0.5)
        self._lbl_type.get_style_context().add_class('dim-label')

        self._lbl_count = Gtk.Label.new(problem_values[3])
        self._lbl_count.set_halign(Gtk.Align.END)
        self._lbl_count.get_style_context().add_class('dim-label')

        self._hbox1.pack_start(self._lbl_app, False, True, 0)
        self._hbox1.pack_end(self._lbl_date, False, True, 0)

        self._hbox2.pack_start(self._lbl_type, False, True, 0)
        self._hbox2.pack_end(self._lbl_count, False, True, 0)

        self.pack_start(self._hbox1, True, True, 0)
        self.pack_start(self._hbox2, True, True, 0)
        self.show_all()

    def set_values(self, problem_values):
        self._lbl_app.set_text(problem_values[0])
        self._lbl_date.set_text(problem_values[1])
        self._lbl_type.set_text(problem_values[2])
        self._lbl_count.set_text(problem_values[3])
        self._problem = problem_values[4]

    def get_problem(self):
        return self._problem


#pylint: disable=R0902
class OopsWindow(Gtk.ApplicationWindow):

    _TITLE = _("Problem Reporting")

    class OopsGtkBuilder:
        def __init__(self):
            builder = None
            # try to load the glade from git at first step
            ui_files = ['./src/gnome_abrt/oops.glade',
                        GNOME_ABRT_UI_DIR + '/oops.glade']
            for glade_file in ui_files:
                if os.path.exists(glade_file):
                    builder = Gtk.Builder()
                    builder.set_translation_domain(GETTEXT_PROGNAME)
                    try:
                        builder.add_from_file(filename=glade_file)
                    #pylint: disable=E0712
                    except GObject.GError as ex:
                        builder = None
                        logging.debug("Failed to load UI file: '{0}': {1}"
                                .format(glade_file, str(ex)))
                    else:
                        break
                else:
                    logging.debug("UI file does not exist: '{0}'"
                            .format(glade_file))

            if builder is None:
                raise RuntimeError(_("Failed to load UI definition"))

            self._builder = builder

            self.wnd_main = builder.get_object('wnd_main')
            self.box_window = builder.get_object('box_window')
            self.box_header_left = builder.get_object('box_header_left')
            self.box_sources_switcher = builder.get_object(
                    'box_sources_switcher')
            self.box_panel_left = builder.get_object('box_panel_left')
            self.lbl_reason = builder.get_object('lbl_reason')
            self.lbl_summary = builder.get_object('lbl_summary')
            self.lbl_app_name_value = builder.get_object('lbl_app_name_value')
            self.lbl_app_version_value = builder.get_object(
                    'lbl_app_version_value')
            self.lbl_detected_value = builder.get_object('lbl_detected_value')
            self.lbl_reported = builder.get_object('lbl_reported')
            self.lbl_reported_value = builder.get_object('lbl_reported_value')
            self.lbl_repots = builder.get_object('lbl_reports')
            self.lb_problems = builder.get_object('lb_problems')
            self.img_app_icon = builder.get_object('img_app_icon')
            self.nb_problem_layout = builder.get_object('nb_problem_layout')
            self.btn_delete = builder.get_object('btn_delete')
            self.btn_report = builder.get_object('btn_report')
            self.btn_detail = builder.get_object('btn_detail')
            self.se_problems = builder.get_object('se_problems')
            self.search_bar = builder.get_object('search_bar')
            self.chb_all_problems = builder.get_object('chb_all_problems')
            self.vbx_links = builder.get_object('vbx_links')
            self.vbx_problem_messages = builder.get_object(
                    'vbx_problem_messages')
            self.gac_report = builder.get_object('gac_report')
            self.gac_delete = builder.get_object('gac_delete')
            self.gac_open_directory = builder.get_object('gac_open_directory')
            self.gac_copy_id = builder.get_object('gac_copy_id')
            self.gac_search = builder.get_object('gac_search')
            self.tbtn_search = builder.get_object('tbtn_search')
            self.tbtn_multi_select = builder.get_object('tbtn_multi_select')

            GObject.Binding.bind_property(
                    self.tbtn_search, "active",
                    self.search_bar, "search-mode-enabled",
                    GObject.BindingFlags.BIDIRECTIONAL)

            label = Gtk.Label.new('')
            label.show()
            self.lb_problems.set_placeholder(label)
            label.connect('map', self.placeholder_mapped, self)
            label.connect('unmap', self.placeholder_unmapped, self)

            self.menu_problem_item = builder.get_object('menu_problem_item')
            self.menu_multiple_problems = builder.get_object(
                    'menu_multiple_problems')
            self.ag_accelerators = builder.get_object('ag_accelerators')
            self.header_bar = None

        def placeholder_mapped(self, label, data):
            self.tbtn_multi_select.set_sensitive(False)

        def placeholder_unmapped(self, label, data):
            self.tbtn_multi_select.set_sensitive(True)

        def connect_signals(self, implementor):
            self._builder.connect_signals(implementor)

            self.search_bar.connect_entry(self.se_problems)

        def reset_window(self, window, title):
            window.set_default_size(*self.wnd_main.get_size())
            self.wnd_main.remove(self.box_window)
            #pylint: disable=E1101
            window.add(self.box_window)

            if desktop.replace_window_header():
                self.box_header.foreach(lambda w, c: c.remove(w),
                        self.box_header)

                self.header_bar = Gtk.HeaderBar.new()
                self.header_bar.pack_start(self.box_header_left)
                self.header_bar.pack_end(self.btn_report)
                self.header_bar.pack_end(self.btn_delete)

                window.set_titlebar(self.header_bar)
                self.header_bar.set_show_close_button(True)
                # window.get_title() returns None
                self.header_bar.set_title(title)

            # move accelators group from the design window to this window
            window.add_accel_group(self.ag_accelerators)

        def __getattr__(self, name):
            obj = self._builder.get_object(name)

            if obj is None:
                raise AttributeError("Builder has not member '{0}'"
                        .format(name))

            return obj


    class SourceObserver:
        def __init__(self, wnd):
            self.wnd = wnd
            self._enabled = True

        def enable(self):
            self._enabled = True

        def disable(self):
            self._enabled = False

        def changed(self, source, change_type=None, problem=None):
            if not self._enabled:
                return

            try:
                if source == self.wnd._source:
                    if change_type is None:
                        self.wnd._reload_problems(source)
                    elif change_type == problems.ProblemSource.NEW_PROBLEM:
                        self.wnd._add_problem_to_storage(problem)
                    elif change_type == problems.ProblemSource.DELETED_PROBLEM:
                        self.wnd._remove_problem_from_storage(problem)
                    elif change_type == problems.ProblemSource.CHANGED_PROBLEM:
                        self.wnd._update_problem_in_storage(problem)

                self.wnd._update_source_button(source)
            except errors.UnavailableSource as ex:
                self.wnd._disable_source(ex.source, ex.temporary)


    class OptionsObserver:
        def __init__(self, wnd):
            self.wnd = wnd

        def option_updated(self, conf, option):
            if option == 'problemid' and conf[option]:
                self.wnd._select_problem_by_id(conf[option])
            if option == 'T_FMT' and conf[option]:
                self.wnd._reload_problems(self.wnd._source)
            if option == 'D_T_FMT' and conf[option]:
                self.wnd._set_problem(self.wnd.selected_problem)


    def __init__(self, application, sources, controller):
        Gtk.ApplicationWindow.__init__(self,
                            title=OopsWindow._TITLE,
                            application=application)

        if not sources:
            raise ValueError("The source list cannot be empty!")

        self._builder = OopsWindow.OopsGtkBuilder()
        self._builder.reset_window(self, OopsWindow._TITLE)

        #pylint: disable=E1120
        css_prv = Gtk.CssProvider.new()
        # "row" selector is valid and supported in GTK>=3.20 (Fedora 24).
        # "GtkListBoxRow" selector is no longer supported but required
        # for GTK<3.20 (Fedora 23). It can be removed if we decide to stop
        # supporting older systems.
        css_prv.load_from_data("GtkListBoxRow, row {\n"
                               "  padding          : 12px;\n"
                               "}\n"
                               ".app-name-label {\n"
                               "  font-weight      : bold;\n"
                               "}\n"
                               ".oops-reason {\n"
                               "  font-size        : 120%;\n"
                               "  font-weight      : bold;\n"
                               "}\n".encode()
                               )
        stl_ctx = self.get_style_context()
        stl_ctx.add_provider_for_screen(stl_ctx.get_screen(), css_prv, 6000)
        self._builder.connect_signals(self)

        self._source_observer = OopsWindow.SourceObserver(self)
        self._source_observer.disable()

        self._reloading = False
        self._controller = controller

        self.selected_problem = None
        self._all_sources = []
        self._source = None
        self._handling_source_click = False
        self._configure_sources(sources)
        self._set_button_toggled(self._source.button, True)

        # a set where invalid problems found while sorting of the problem list
        # are stored
        self._trash = set()
        self._builder.lb_problems.set_sort_func(time_sort_func, self._trash)
        self.lss_problems = ListBoxSelection(self._builder.lb_problems,
                self.on_tvs_problems_changed)
        self._filter = ProblemsFilter(self._builder.lb_problems,
                self.lss_problems)

        self._builder.lb_problems.grab_focus()
        try:
            self._reload_problems(self._source)
        except errors.UnavailableSource as ex:
            self._disable_source(ex.source, ex.temporary)

        self._options_observer = OopsWindow.OptionsObserver(self)
        conf = config.get_configuration()
        conf.set_watch('problemid', self._options_observer)
        conf.set_watch('T_FMT', self._options_observer)
        conf.set_watch('D_T_FMT', self._options_observer)
        self._options_observer.option_updated(conf, 'problemid')

        # enable observer
        self._source_observer.enable()

        self.connect("key-press-event", self._on_key_press_event)


    def _configure_sources(self, sources):
        for name, src in sources:
            self._all_sources.append(src)
            src.attach(self._source_observer)

            label = None
            try:
                label = format_button_source_name(name, src)
            except errors.UnavailableSource:
                logging.debug("Unavailable source: {0}".format(name))
                continue

            src_btn = Gtk.ToggleButton.new_with_label(label)
            src_btn.set_visible(True)
            # add an extra member source (I don't like it but it so easy)
            src_btn.source = src
            self._builder.box_sources_switcher.pack_start(
                    src_btn, False, True, 0)

            # add an extra member name (I don't like it but it so easy)
            src.name = name
            # add an extra member button (I don't like it but it so easy)
            src.button = src_btn
            src_btn.connect("clicked", self._on_source_btn_clicked, src)

        self._source = self._all_sources[0]

    def _update_source_button(self, source):
        name = format_button_source_name(source.name, source)
        source.button.set_label(name)

    def _set_button_toggled(self, button, state):
        # set_active() triggers the clicked signal
        # and if we set the active in program,
        # we don't want do any action in the clicked handler
        self._handling_source_click = True
        try:
            button.set_active(state)
        finally:
            self._handling_source_click = False

    def _on_source_btn_clicked(self, btn, args):
        # If True, then button's state was not changed by click
        # and we don't want to switch source
        if self._handling_source_click:
            return

        res, old_source = self._switch_source(btn.source)
        if not res:
            # switching sources failed and we have to untoggle clicked
            # source's button
            self._set_button_toggled(btn, False)
        else:
            if old_source is not None:
                # sources were switched and we have to untoggle old source's
                # button
                self._set_button_toggled(old_source.button, False)
            elif not btn.get_active():
                # source wasn't changed and we have to set toggled back if
                # someone clicked already selected button
                self._set_button_toggled(btn, True)

    def _switch_source(self, source):
        """Sets the passed source as the selected source."""

        result = True
        old_source = None
        if source != self._source:
            try:
                self._reload_problems(source)
                old_source = self._source
                self._source = source
            except errors.UnavailableSource as ex:
                self._disable_source(source, ex.temporary)
                result = False

        return (result, old_source)

    def _disable_source(self, source, temporary):
        if self._source is None or not self._all_sources:
            return

        # Some sources can be components of other sources.
        # Problems are connected directly to the component sources, therefore
        # exception's source is a component source, thus we have to find an
        # instance of composite source which the unavailable component source
        # belongs.
        source_index = self._all_sources.index(source)

        if source_index != -1:
            real_source = self._all_sources[source_index]
            self._set_button_toggled(real_source.button, False)
            if not temporary:
                logging.debug("Disabling source")
                real_source.button.set_sensitive(False)
                self._all_sources.pop(source_index)

        if source != self._source:
            return

        # We just disabled the currently selected source. So, we should select
        # some other source. The simplest way is to select the first source
        # but only if it is not the disabled source.
        # If the disabled source is completely unavailable (not temporary) we
        # can always select the source at index 0 because the disabled
        # source was removed from the _all_sources list.
        if (not temporary or source_index != 0) and self._all_sources:
            self._source = self._all_sources[0]
            self._set_button_toggled(self._source.button, True)
        else:
            self._source = None

        try:
            self._reload_problems(self._source)
        except errors.UnavailableSource as ex:
            self._disable_source(ex.source, ex.temporary)

    @handle_problem_and_source_errors
    def _find_problem_row_full(self, problem):
        i = 0
        lb_row = self._builder.lb_problems.get_row_at_index(i)
        while lb_row is not None:
            if problem == list_box_row_to_problem(lb_row):
                break

            i += 1
            lb_row = self._builder.lb_problems.get_row_at_index(i)

        return (i, lb_row)

    @handle_problem_and_source_errors
    def _find_problem_row(self, problem):
        return self._find_problem_row_full(problem)[1]

    def _add_problem_to_storage(self, problem):
        try:
            values = problem_to_storage_values(problem)
        except errors.InvalidProblem:
            logging.debug("Exception: {0}".format(traceback.format_exc()))
            return

        self._append_problem_values_to_storage(values)

    def _append_problem_values_to_storage(self, problem_values):
        problem_cell = ProblemListBoxCell(problem_values)
        self._builder.lb_problems.insert(problem_cell, -1)
        self._clear_invalid_problems_trash()

    def _clear_invalid_problems_trash(self):
        # append methods trigger time_sort_func() where InvalidProblem
        # exception can occur. In that case time_sort_func() pushes an invalid
        # problem to the trash set because the invalid problem cannot be
        # removed while executing the operation
        while self._trash:
            self._remove_problem_from_storage(self._trash.pop())

    def _remove_problem_from_storage(self, problem):
        if problem is None:
            return

        index, problem_row = self._find_problem_row_full(problem)
        if problem_row is None:
            return

        selected = problem in self._get_selected(self.lss_problems)

        problem_row.destroy()

        if selected:
            for i in range(index, -1, -1):
                problem_row = self._builder.lb_problems.get_row_at_index(i)
                if self._filter.match(problem_row):
                    break

            if problem_row is not None:
                self._builder.lb_problems.select_row(problem_row)
            else:
                self._set_problem(None)

    def _update_problem_in_storage(self, problem):
        problem_row = self._find_problem_row(problem)
        if problem_row is not None:
            try:
                values = problem_to_storage_values(problem)
            except errors.InvalidProblem as ex:
                logging.debug("Exception: {0}".format(traceback.format_exc()))
                self._remove_problem_from_storage(ex.problem_id)
                return

            list_box_row_set_values(problem_row, values)
            self._builder.lb_problems.invalidate_sort()
            self._clear_invalid_problems_trash()

        if problem in self._get_selected(self.lss_problems):
            self._set_problem(problem)

    def _reload_problems(self, source):
        # Try to load and prepare the list of selected problems before we
        # clear the view. So, we can gracefully handle UnavailableSource
        # exception. If the reloaded source is unavailable the old list
        # of problems remains untouched.
        storage_problems = []
        if source is not None:
            prblms = source.get_problems()
            for p in prblms:
                try:
                    storage_problems.append(problem_to_storage_values(p))
                except errors.InvalidProblem:
                    logging.debug("Exception: {0}"
                            .format(traceback.format_exc()))

        old_selection = self._get_selected(self.lss_problems)

        self._reloading = True
        try:
            self._builder.lb_problems.foreach(
                lambda w, u: w.destroy(), None)

            if storage_problems:
                for p in storage_problems:
                    self._append_problem_values_to_storage(p)
        finally:
            self._reloading = False

        if storage_problems:
            problem_row = None
            if old_selection:
                problem_row = self._find_problem_row(old_selection[0])

            i = 0
            if problem_row is None:
                problem_row = self._builder.lb_problems.get_row_at_index(i)
                i = 1

            while (problem_row is not None
                    and not self._filter.match(problem_row)):
                problem_row = self._builder.lb_problems.get_row_at_index(i)
                i += 1

            if problem_row is not None:
                self._builder.lb_problems.select_row(problem_row)
                return

        self._set_problem(None)

    def _select_problem_by_id(self, problem_id):
        # The problem could come from a different source than the currently
        # loaded source. If so, try to switch to problem's origin source and
        # select the problem after that.
        if (self._source is not None and
                problem_id not in self._source.get_problems()):
            for source in self._all_sources:
                if problem_id in source.get_problems():
                    res, old_source = self._switch_source(source)
                    if res:
                        self._set_button_toggled(old_source.button, False)
                        self._set_button_toggled(source.button, True)
                    break

        problem_row = self._find_problem_row(problem_id)

        if problem_row is not None:
            self._builder.lb_problems.select_row(problem_row)
        else:
            logging.debug("Can't select problem id '{0}' because the id was "
                    "not found".format(problem_id))

    def _show_problem_links(self, submissions):
        if not submissions:
            return False

        link_added = False
        for sbm in submissions:
            if problems.Problem.Submission.URL == sbm.rtype:
                lnk = Gtk.Label.new(sbm.title)
                lnk.set_use_markup(True)
                lnk.set_markup(
                    "<a href=\"{0}\">{1}</a>".format(sbm.data, sbm.title))
                lnk.set_halign(Gtk.Align.START)
                lnk.set_margin_top(5)
                lnk.set_margin_bottom(8)
                lnk.set_line_wrap(True)
                lnk.set_visible(True)

                self._builder.vbx_links.pack_start(lnk, False, True, 0)
                link_added = True

        return link_added

    def _show_problem_message(self, message):
        msg = Gtk.Label.new(message)
        msg.set_markup(message)
        msg.set_visible(True)
        msg.set_halign(Gtk.Align.START)
        msg.set_valign(Gtk.Align.START)
        msg.set_line_wrap(True)
        msg.set_selectable(True)
        msg.set_xalign(0)

        self._builder.vbx_problem_messages.pack_start(msg, False, True, 0)

    @handle_problem_and_source_errors
    def _set_problem(self, problem):
        def destroy_links(widget, _):
            if widget != self._builder.lbl_reported_value:
                widget.destroy()

        self.selected_problem = problem

        sensitive_btn = problem is not None
        self._builder.btn_delete.set_sensitive(sensitive_btn)
        self._builder.btn_report.set_sensitive(sensitive_btn)
        self._builder.vbx_links.foreach(
                destroy_links, None)
        self._builder.vbx_problem_messages.foreach(
                lambda w, u: w.destroy(), None)

        if problem:
            self._builder.nb_problem_layout.set_current_page(0)
            app = problem['application']
            if problem['type'] == 'Kerneloops':
                self._builder.lbl_reason.set_text(
            _("Unexpected system error"))
                self._builder.lbl_summary.set_text(
            _("The system has encountered a problem and recovered."))
            elif problem['type'] == 'vmcore':
                self._builder.lbl_reason.set_text(
            _("Fatal system failure"))
                self._builder.lbl_summary.set_text(
            _("The system has encountered a problem and could not continue."))
            else:
                if not app.name:
                    self._builder.lbl_reason.set_text(
                            # Translators: If Application's name is unknown,
                            # display neutral header
                            # "'Type' problem has been detected". Examples:
                            #  Kerneloops problem has been detected
                            #  C/C++ problem has been detected
                            #  Python problem has been detected
                            #  Ruby problem has been detected
                            #  VMCore problem has been detected
                            #  AVC problem has been detected
                            #  Java problem has been detected
                            _("{0} problem has been detected").format(
                                    problem['human_type']))
                else:
                    self._builder.lbl_reason.set_text(
                            _("{0} quit unexpectedly").format(app.name))

                self._builder.lbl_summary.set_text(
            _("The application encountered a problem and could not continue."))

            self._builder.lbl_app_name_value.set_text(
                        # Translators: package name not available
                        problem['package_name'] or _("N/A"))
            self._builder.lbl_app_version_value.set_text(
                        # Translators: package version not available
                        problem['package_version'] or _("N/A"))
            self._builder.lbl_detected_value.set_text(
                humanize.naturaltime(datetime.datetime.now()-problem['date']))
            self._builder.lbl_detected_value.set_tooltip_text(
                problem['date'].strftime(config.get_configuration()['D_T_FMT']))

            icon_buf = None
            scale = self._builder.img_app_icon.get_scale_factor()
            if app.icon:
                icon_buf = load_icon(gicon=app.icon, scale=scale)

            if icon_buf is None:
                icon_buf = load_icon(name="system-run-symbolic", scale=scale)
                self._builder.img_app_icon.get_style_context().add_class(
                                                                    'dim-label')
            else:
                self._builder.img_app_icon.get_style_context().remove_class(
                                                                    'dim-label')

            # icon_buf can be None and if it is None, no icon will be displayed
            set_icon_from_pixbuf_with_scale(self._builder.img_app_icon,
                                            icon_buf, scale)

            self._builder.lbl_reported_value.show()
            self._builder.lbl_reported.set_text(_("Reported"))
            if problem['not-reportable']:
                self._builder.lbl_reported_value.set_text(
                        _('cannot be reported'))
                self._show_problem_links(problem['submission'])
                self._show_problem_message(problem['not-reportable'])
            elif problem['is_reported']:
                if self._show_problem_links(problem['submission']):
                    self._builder.lbl_reported.set_text(_("Reports"))
                    self._builder.lbl_reported_value.hide()

                    if (not any((s.name == "Bugzilla"
                                for s in problem['submission']))):
                        self._show_problem_message(
_("This problem has been reported, but a <i>Bugzilla</i> ticket has not"
" been opened. Our developers may need more information to fix the problem.\n"
"Please consider also <b>reporting it</b> to Bugzilla in"
" order to provide that. Thank you."))
                else:
                    # Translators: Displayed after 'Reported' if a problem
                    # has been reported but we don't know where and when.
                    # Probably a rare situation, usually if a problem is
                    # reported we display a list of reports here.
                    self._builder.lbl_reported_value.set_text(_('yes'))
            else:
                # Translators: Displayed after 'Reported' if a problem
                # has not been reported.
                self._builder.lbl_reported_value.set_text(_('no'))

            style_context = self._builder.btn_report.get_style_context()

            if problem['not-reportable']:
                style_context.add_class('destructive-action')
            else:
                style_context.remove_class('destructive-action')
        else:
            if self._source is not None:
                self._builder.nb_problem_layout.set_current_page(1)
            else:
                self._builder.nb_problem_layout.set_current_page(2)

    def _get_selected(self, selection):
        return selection.get_selected_rows()

    def on_tbtn_multi_select_toggled(self, tbtn):
        if tbtn.get_active():
            self._builder.lb_problems.set_selection_mode(
                    Gtk.SelectionMode.MULTIPLE)
            if self._builder.header_bar is not None:
                self._builder.header_bar.get_style_context().add_class(
                                                               'selection-mode')
        else:
            row = self._builder.lb_problems.get_selected_row()
            if row is None:
                row = self._builder.lb_problems.get_row_at_index(0)

            self._builder.lb_problems.set_selection_mode(
                    Gtk.SelectionMode.BROWSE)

            if row is not None and self._filter.match(row):
                self._builder.lb_problems.select_row(row)

            if self._builder.header_bar is not None:
                self._builder.header_bar.get_style_context().remove_class(
                                                               'selection-mode')

    def on_tvs_problems_changed(self, selection):
        if not self._reloading:
            rows = self._get_selected(selection)
            if rows:
                self._set_problem(rows[0])
            else:
                # Clear window because of empty list of problems!
                self._set_problem(None)

    @handle_problem_and_source_errors
    def on_gac_delete_activate(self, action):
        for prblm in self._get_selected(self.lss_problems):
            try:
                self._controller.delete(prblm)
            except errors.InvalidProblem as ex:
                logging.debug(traceback.format_exc())
                self._remove_problem_from_storage(ex.problem_id)

    @handle_problem_and_source_errors
    def on_gac_detail_activate(self, action):
        selected = self._get_selected(self.lss_problems)
        if selected:
            wrappers.show_problem_details_for_dir(
                    selected[0].problem_id, self)

    @handle_problem_and_source_errors
    def on_gac_report_activate(self, action):
        selected = self._get_selected(self.lss_problems)
        if not selected:
            return

        unsafe = False

        if selected[0]['not-reportable']:
            dialog = Gtk.MessageDialog(self,
                Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
                Gtk.MessageType.QUESTION,
                Gtk.ButtonsType.NONE,
                _("This problem is marked as not reportable, thus reporting should only be forced if you know what it entails. Do you wish to continue?"))
            button = Gtk.Button.new_with_label(_("No"))

            button.get_style_context().add_class("suggested-action")
            button.show()

            dialog.add_action_widget(button, Gtk.ResponseType.NO)
            dialog.add_button(_("Yes"), Gtk.ResponseType.YES)

            response = dialog.run()

            dialog.destroy()

            if response != Gtk.ResponseType.YES:
                return

            unsafe = True

        self._controller.report(selected[0], unsafe)

    @handle_problem_and_source_errors
    def on_se_problems_search_changed(self, entry):
        self._filter.set_pattern(entry.get_text())

    def _on_key_press_event(self, sender, event):
        return self._builder.search_bar.handle_event(event)

    def _hide_problem_filter(self):
        self._builder.se_problems.set_text("")
        self._builder.search_bar.set_search_mode(False)

    def _show_problem_filter(self):
        self._builder.search_bar.set_search_mode(True)
        self._builder.se_problems.grab_focus()

    def on_se_problems_key_press_event(self, sender, data):
        if data.keyval == Gdk.KEY_Escape:
            self._hide_problem_filter()

        return False

    def on_gac_search_activate(self, action):
        if self._builder.search_bar.get_search_mode():
            self._hide_problem_filter()
        else:
            self._show_problem_filter()

    def on_gac_opt_all_problems_activate(self, action):
        conf = config.get_configuration()
        conf['all_problems'] = self._builder.chb_all_problems.get_active()

    def on_gac_control_preferences_activate(self, action):
        wrappers.show_events_list_dialog(self)

    def on_gac_open_directory_activate(self, action):
        selection = self._get_selected(self.lss_problems)
        if selection:
            Gio.app_info_launch_default_for_uri(
                                'file://' + selection[0].problem_id, None)
        self._builder.menu_problem_item.popdown()
        self._builder.menu_multiple_problems.popdown()

    def on_gac_copy_id_activate(self, action):
        selection = self._get_selected(self.lss_problems)
        if selection:
            #pylint: disable=E1101
            (Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
                .set_text(selection[0].problem_id, -1))
        self._builder.menu_problem_item.popdown()
        self._builder.menu_multiple_problems.popdown()

    def problems_button_press_event(self, sender, data):
        # getattribute() used because number as first character in name
        # is syntax error
        if (data.type == type.__getattribute__(Gdk.EventType, '2BUTTON_PRESS')
                and data.button == Gdk.BUTTON_PRIMARY):
            self._builder.gac_report.activate()
        elif (data.type == Gdk.EventType.BUTTON_PRESS
                and data.button == Gdk.BUTTON_SECONDARY):
            if len(self.lss_problems.get_selected_rows()) > 1:
                self._builder.menu_multiple_problems.popup(None, None,
                        None, None, data.button, data.time)
                return True
            else:
                problem_row = self._builder.lb_problems.get_row_at_y(data.y)
                if problem_row:
                    self._builder.lb_problems.select_row(problem_row)
                    self._builder.menu_problem_item.popup(None, None,
                            None, None, data.button, data.time)
        return None

    def get_box_header_left_offset(self):
        # Returns the offset of box_header_left relative to the main paned
        # widget: distance between the left edges of the widgets in LTR
        # locales or the right edges in RTL locales.
        box_header_left = self._builder.box_header_left
        box_panel_left = self._builder.box_panel_left
        paned = box_panel_left.get_parent()
        if paned is None:
            # Fatal error, we can't do anything so just return error.
            # See also: rhbz#1347951
            return None

        offset = box_header_left.translate_coordinates(paned, 0, 0)[0]
        # We don't know who is the parent of box_header_left, it may be
        # box_header or header_bar.
        parent = box_header_left.get_parent()
        if parent is not None:
            if parent.get_direction() == Gtk.TextDirection.RTL:
                offset = paned.get_allocation().width - offset - \
                         box_header_left.get_allocation().width

        return offset

    def do_box_header_left_size_allocate(self, sender):
        # When something changes in the left group of header widgets
        # (for example the number of "My" or "System" bugs is changed
        # and requires more or less space) get its new minimum width
        # and set it as the minimum width of the left panel.
        # Unfortunately, we can't just call sender.get_preferred_width()
        # because once we set the minimum width (set_size_request())
        # of this box that value may be returned rather than the real
        # minimum value required by the box. So here we repeat roughly
        # the same algorithm which is inside the GtkBox implementation:
        # calculate the sum of minimum widths required by the children.
        spacing = sender.get_spacing()
        sum_width = -spacing
        for child in sender.get_children():
            width = child.get_preferred_width()[0]  # child's minimum width
            sum_width += width
            sum_width += spacing

        # Calculate the position of the box relative to its parent
        padding = self.get_box_header_left_offset()
        if padding is None:
            return GLib.SOURCE_REMOVE  # Error, we won't retry

        # This assumes that the right padding is the same as the left padding
        self._builder.box_panel_left.set_size_request(
                sum_width + 2 * padding, -1)

        return GLib.SOURCE_REMOVE

    def on_box_header_left_size_allocate(self, sender, allocation):
        other = self._builder.box_panel_left

        # Sometimes this function is called too early. All widgets must
        # be realized in order to measure their relative position.
        if not sender.get_realized() or not other.get_realized():
            return

        # We can't set the new size request while a widget size is being
        # allocated because the widget must be fully measured and the new
        # size request clears the measured flag causing a warning. For the
        # same reason we can't set the new size request of another widget
        # sharing the same common toplevel because the new size requsest
        # causes resize of all its parents including the common parent which
        # is just being allocated. To avoid this we schedule this action
        # on idle.
        GLib.idle_add(self.do_box_header_left_size_allocate, sender)

    def update_box_header_left_size_from_paned(self, sender):
        # Sets the box_header_left width the same as the paned position
        # minus optional margins
        other = self._builder.box_header_left

        # Sometimes this function is called too early. All widgets must
        # be realized in order to measure their relative position.
        if not sender.get_realized() or not other.get_realized():
            return GLib.SOURCE_REMOVE

        padding = self.get_box_header_left_offset()
        if padding is None:
            return GLib.SOURCE_REMOVE  # Error, we won't retry

        self._builder.box_header_left.set_size_request(
                sender.get_position() - 2 * padding, -1)

        # Sometimes the new width request is accepted (get_size_request()
        # returns the new value correctly) but not applied (the actual widget
        # width is old and unnecessarily larger). Not sure whose bug this is
        # but to workaround let's force resize.
        self._builder.box_header_left.queue_resize()
        return GLib.SOURCE_REMOVE

    def on_paned_position_changed(self, sender, data):
        # Alternatively we could watch box_panel_left size-allocate signal
        # but that other method seemed to be delayed and not updated the
        # size correctly.
        self.update_box_header_left_size_from_paned(sender)

    def on_paned_size_allocate(self, sender, allocation):
        # Sometimes when the paned position is changed as a result of the
        # resize of whole window (for example unmaximization) the paned
        # position is not notified correctly. Again, not sure whose bug this
        # is but to workaround let's watch the size of the paned and update
        # the header box size. Same as previously, we should not resize
        # any widget even when another widget is being allocated so schedule
        # this action on idle.
        GLib.idle_add(self.update_box_header_left_size_from_paned, sender)

    def on_paned_map(self, sender):
        # Also on the first appearance force the paned position changed event
        # to update the box_header_left minimum width. Otherwise it is not
        # adjusted to the paned handle position until a user moves the handle
        # manually.
        self.on_paned_position_changed(sender, None)

Youez - 2016 - github.com/yon3zu
LinuXploit