#!/usr/bin/python

'''GTK Apport user interface. 

Copyright (C) 2007 Canonical Ltd.
Author: Martin Pitt <martin.pitt@ubuntu.com>

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.  See http://www.gnu.org/copyleft/gpl.html for
the full text of the license.
'''

import os.path, sys, subprocess
from gettext import gettext as _
try:
    import gobject, gtk, gtk.glade
    import apport.ui
except ImportError, e:
    # this can happen while upgrading python packages
    print >> sys.stderr, 'Could not import module, is a package upgrade in progress? Error:', e
    sys.exit(1)

class GTKUserInterface(apport.ui.UserInterface):
    '''GTK UserInterface.'''

    def w(self, widget):
        '''Shortcut for getting a widget.'''

        return self.widgets.get_widget(widget)

    def __init__(self):
        apport.ui.UserInterface.__init__(self)

        # load UI
        gtk.window_set_default_icon_name("apport")
        gtk.glade.textdomain(self.gettext_domain)
        self.widgets = gtk.glade.XML(os.path.join(os.path.dirname(sys.argv[0]),
            'apport-gtk.glade'))
        self.widgets.signal_autoconnect(self)

        # 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 dialogs (those 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_heading_package_error = self.w('label_heading_package_error').get_label()
        self.str_radio_complete = self.w('radiobutton_complete').get_label()
        self.str_radio_reduced = self.w('radiobutton_reduced').get_label()

    #
    # ui_* implementation of abstract UserInterface classes
    #

    def ui_present_crash(self, desktop_entry): 
        # adapt dialog heading and label appropriately
        if desktop_entry:
            heading = _('Sorry, %s closed unexpectedly') % 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.cur_package
        if desktop_entry and self.report.has_key('ExecutablePath') and \
	    os.path.dirname(self.report['ExecutablePath']) in os.environ['PATH'].split(':') and \
	    subprocess.call(['pgrep', '-x',
		os.path.basename(self.report['ExecutablePath']), 
		'-u', str(os.geteuid())], stdout=subprocess.PIPE) != 0:
            self.w('label_heading_reopen').set_markup(self.str_heading % heading)
            d = self.w('dialog_crash_reopen')
	    bl_checkbox = self.w('checkbutton_blacklist_reopen')
        else:
            self.w('label_heading').set_markup(self.str_heading % heading)
            d = self.w('dialog_crash')
	    bl_checkbox = self.w('checkbutton_blacklist')

        # show crash notification dialog
        response = d.run()
        d.hide()
        while gtk.events_pending():
            gtk.main_iteration(False)
	blacklist = bl_checkbox.get_active()
        if response == gtk.RESPONSE_YES:
            return {'action': 'report', 'blacklist': blacklist}
        elif response == gtk.RESPONSE_OK:
            return {'action': 'restart', 'blacklist': blacklist}
        else:
            return {'action': 'cancel', 'blacklist': blacklist}

    def ui_present_package_error(self):
	self.w('label_heading_package_error').set_markup(
	    self.str_heading_package_error % self.report['Package'])
        response = self.w('dialog_package_error').run()
        self.w('dialog_package_error').hide()
        while gtk.events_pending():
            gtk.main_iteration(False)
        if response == gtk.RESPONSE_YES:
            return 'report'
        else:
            return 'cancel'

    def ui_present_kernel_error(self):
        response = self.w('dialog_kernel_error').run()
        self.w('dialog_kernel_error').hide()
        while gtk.events_pending():
            gtk.main_iteration(False)
        if response == gtk.RESPONSE_YES:
            return 'report'
        else:
            return 'cancel'

    def ui_present_report_details(self):
        # report contents in expander
        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 not hasattr(self.report[key], 'gzipvalue') and \
                hasattr(self.report[key], 'isspace') and \
                not self.report._is_binary(self.report[key]):
                self.tree_model.set_value(valiter, 0, self.report[key])
                # 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)
            else:
                self.tree_model.set_value(valiter, 0, _('(binary data)'))

            row += 1

        # complete/reduced radio buttons
        if self.report.has_key('CoreDump'):
            self.w('radiobutton_complete').set_label(self.str_radio_complete % 
                self.format_filesize(self.get_complete_size()))
            self.w('radiobutton_reduced').set_label(self.str_radio_reduced % 
                self.format_filesize(self.get_reduced_size()))
            if self.report.has_useful_stacktrace():
                self.w('radiobutton_complete').set_sensitive(True)
                self.w('radiobutton_complete').show()
                self.w('radiobutton_reduced').show()
            else:
                self.w('radiobutton_complete').set_sensitive(False)
                self.w('radiobutton_reduced').hide()
        else:
            self.w('radiobutton_complete').hide()
            self.w('radiobutton_reduced').hide()

        response = self.w('dialog_bugreport').run()
        self.w('dialog_bugreport').hide()
        while gtk.events_pending():
            gtk.main_iteration(False)

        if response == gtk.RESPONSE_OK:
            if self.w('radiobutton_complete').get_active():
                return 'full'
            else:
                return 'reduced'
        else:
            return 'cancel'

    def ui_info_message(self, title, text):
        md = gtk.MessageDialog(type=gtk.MESSAGE_INFO,
            buttons=gtk.BUTTONS_CLOSE, message_format=text)
        md.set_title(title)
        md.run()
        md.hide()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_error_message(self, title, text):
        md = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
            buttons=gtk.BUTTONS_CLOSE, message_format=text)
        md.set_title(title)
        md.run()
        md.hide()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_start_info_collection_progress(self):
        self.w('progressbar_information_collection').set_fraction(0)
        self.w('window_information_collection').show()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_pulse_info_collection_progress(self):
        self.w('progressbar_information_collection').pulse()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_stop_info_collection_progress(self):
        self.w('window_information_collection').hide()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_start_upload_progress(self):
        '''Open a window with an definite progress bar, telling the user to
        wait while debug information is being uploaded.'''

        self.w('progressbar_upload').set_fraction(0)
        self.w('window_report_upload').show()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_set_upload_progress(self, progress):
        '''Set the progress bar in the debug data upload progress
        window to the given ratio (between 0 and 1, or None for indefinite
        progress).
        
        This function is called every 100 ms.'''

        if progress:
            self.w('progressbar_upload').set_fraction(progress)
        else:
            self.w('progressbar_upload').set_pulse_step(0.1)
            self.w('progressbar_upload').pulse()
        while gtk.events_pending():
            gtk.main_iteration(False)

    def ui_stop_upload_progress(self):
        '''Close debug data upload progress window.'''

        self.w('window_report_upload').hide()
        while gtk.events_pending():
            gtk.main_iteration(False)

    #
    # Event handlers
    #

    def on_progress_window_close_event(self, widget, event=None):
        self.w('window_information_collection').hide()
        self.w('window_report_upload').hide()
        sys.exit(0)
        return True

    def on_expander_details_activate(self, widget):
        # signal is sent before actually expanding/collapsing, thus this
        # requires negation
        self.w('dialog_bugreport').set_resizable(not self.w('expander_details').get_expanded())
        while gtk.events_pending():
            gtk.main_iteration(False)
        return True

if __name__ == '__main__':
    app = GTKUserInterface()
    app.run_argv()
