diff --git a/export/securedrop_export/print/print_dialog.py b/export/securedrop_export/print/print_dialog.py index f6d970417..cb1191a28 100644 --- a/export/securedrop_export/print/print_dialog.py +++ b/export/securedrop_export/print/print_dialog.py @@ -8,23 +8,60 @@ logger = logging.getLogger(__name__) -def open_print_dialog(file_to_print): - app = PrintDialog(file_to_print) - app.run() + +class GtkExceptionRaiser: + """ + Context manager to keep track of exceptions to be raised after GTK exits + + This is a workaround for the fact that GTK does not behave like regular + libraries. Exceptions raised by the GUI code are always caught within GTK. + The context manager provides a way to store these exceptions. + + Usage: + + class SomeApplication(Gtk.Application): + def __init__(self, raise_later_func): + super().__init__() + self.raise_later_func = raise_later_func + + [...] + + def on_something_bad_happening(self): + self.raise_later_func(Exception("something happned")) + self.quit() + + with GtkExceptionRaiser() as raise_later_func: + app = SomeApplication(raise_later_func) + app.run() + """ + def __init__(self): + self.exception_to_raise = None + + def raise_later_func(self, exception): + self.exception_to_raise = exception + + def __enter__(self): + return self.raise_later_func + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.exception_to_raise: + raise self.exception_to_raise class PrintDialog(Gtk.Application): - def __init__(self, file_to_print): + def __init__(self, file_to_print, raise_later_func): super().__init__(application_id="org.securedrop.PrintDialog") self.file_to_print = file_to_print + self.raise_later_func = raise_later_func self.connect("activate", self.on_activate) def on_activate(self, app): window = Gtk.Window(application=app) + window.hide() self.dialog = Gtk.PrintUnixDialog.new("Print Document", window) self.dialog.connect("response", self.on_response) + self.dialog.connect("close", self.quit) self.dialog.show() - window.hide() def on_response(self, parent_widget, response_id): if response_id == Gtk.ResponseType.OK: @@ -36,13 +73,23 @@ def on_response(self, parent_widget, response_id): job = Gtk.PrintJob.new("print job", printer, settings, page_setup) job.set_source_file(self.file_to_print) job.send(self.on_job_complete, user_data=None) - elif response_id == Gtk.ResponseType.APPLY: # Preview (if available) - pass elif response_id == Gtk.ResponseType.CANCEL: # FIXME should this exist or should it simply cancel and not report errors - raise ExportException(sdstatus=Status.ERROR_PRINT, sderror="User canceled dialog") + self.raise_later_func( + ExportException(sdstatus=Status.ERROR_PRINT, sderror="User canceled dialog") + ) + self.quit() + elif response_id == Gtk.ResponseType.DELETE_EVENT: + self.quit() def on_job_complete(self, print_job, user_data, error): if error: - self.quit() - raise ExportException(sdstatus=Status.ERROR_PRINT, sderror=error.message) + self.raise_later_func( + ExportException(sdstatus=Status.ERROR_PRINT, sderror=error.message) + ) + self.quit() + +def open_print_dialog(file_to_print): + with GtkExceptionRaiser() as raise_later_func: + app = PrintDialog(file_to_print, raise_later_func) + app.run()