2014-07-24 19:41:11 +04:00
# Copyright (C) 2007, 2013-2014 Red Hat, Inc.
2007-03-23 03:59:10 +03:00
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2007-03-23 03:59:10 +03:00
2008-09-06 21:24:47 +04:00
import logging
2018-09-07 01:37:18 +03:00
import sys
2011-04-06 19:22:03 +04:00
import traceback
2008-09-06 21:24:47 +04:00
2016-06-07 18:33:21 +03:00
from gi . repository import Gtk
2014-09-13 00:10:45 +04:00
from . baseclass import vmmGObject
2010-01-14 18:09:52 +03:00
2013-04-13 22:34:52 +04:00
2010-12-02 01:48:07 +03:00
def _launch_dialog ( dialog , primary_text , secondary_text , title ,
2013-09-07 04:16:37 +04:00
widget = None , modal = True ) :
2012-05-14 17:24:56 +04:00
dialog . set_property ( " text " , primary_text )
2010-12-02 01:48:07 +03:00
dialog . format_secondary_text ( secondary_text or None )
dialog . set_title ( title )
2013-07-01 22:33:06 +04:00
if widget :
dialog . get_content_area ( ) . add ( widget )
2010-12-02 01:48:07 +03:00
res = False
2013-09-07 04:16:37 +04:00
if modal :
2010-12-02 01:48:07 +03:00
res = dialog . run ( )
2012-05-14 17:24:56 +04:00
res = bool ( res in [ Gtk . ResponseType . YES , Gtk . ResponseType . OK ] )
2010-12-02 01:48:07 +03:00
dialog . destroy ( )
else :
def response_destroy ( src , ignore ) :
src . destroy ( )
dialog . connect ( " response " , response_destroy )
dialog . show ( )
2010-01-19 22:09:43 +03:00
2010-12-02 01:48:07 +03:00
return res
2009-01-22 00:28:54 +03:00
2013-04-12 16:26:21 +04:00
2011-04-12 00:26:41 +04:00
class vmmErrorDialog ( vmmGObject ) :
2018-03-16 04:22:14 +03:00
# singleton instance for non-UI classes
@classmethod
def get_instance ( cls ) :
if not cls . _instance :
2018-03-17 23:08:20 +03:00
cls . _instance = vmmErrorDialog ( None )
2018-03-16 04:22:14 +03:00
return cls . _instance
def __init__ ( self , parent ) :
2011-04-12 00:26:41 +04:00
vmmGObject . __init__ ( self )
2010-12-02 01:48:07 +03:00
self . _parent = parent
2010-12-07 23:27:10 +03:00
self . _simple = None
2008-03-14 20:18:44 +03:00
2015-04-09 01:29:48 +03:00
self . _modal_default = False
2011-07-24 05:16:54 +04:00
def _cleanup ( self ) :
2018-03-18 01:49:55 +03:00
pass
2011-07-24 05:16:54 +04:00
2015-04-09 01:29:48 +03:00
def set_modal_default ( self , val ) :
self . _modal_default = val
2010-12-02 01:48:07 +03:00
def get_parent ( self ) :
2018-03-18 01:49:55 +03:00
return self . _parent
2010-12-02 01:48:07 +03:00
2011-04-06 19:22:03 +04:00
def show_err ( self , summary , details = None , title = " " ,
2015-04-09 01:29:48 +03:00
modal = None , debug = True ,
2012-05-14 17:24:56 +04:00
dialog_type = Gtk . MessageType . ERROR ,
buttons = Gtk . ButtonsType . CLOSE ,
2010-12-02 01:48:07 +03:00
text2 = None ) :
2015-04-09 01:29:48 +03:00
if modal is None :
modal = self . _modal_default
2011-04-06 19:22:03 +04:00
if details is None :
2013-06-30 20:42:32 +04:00
details = summary
2018-09-07 01:37:18 +03:00
if sys . exc_info ( ) [ 0 ] is not None :
details + = " \n \n " + " " . join ( traceback . format_exc ( ) ) . strip ( )
2015-08-10 19:56:11 +03:00
else :
details = str ( details )
2011-04-06 19:52:26 +04:00
2014-04-16 20:12:07 +04:00
if debug :
debugmsg = " error dialog message: \n summary= %s " % summary
if details and details != summary :
2018-03-17 00:30:39 +03:00
det = details
if details . startswith ( summary ) :
det = details [ len ( summary ) : ] . strip ( )
debugmsg + = " \n details= %s " % det
2014-04-16 20:12:07 +04:00
logging . debug ( debugmsg )
2011-04-06 19:52:26 +04:00
# Make sure we have consistent details for error dialogs
2014-05-02 18:20:59 +04:00
if ( dialog_type == Gtk . MessageType . ERROR and summary not in details ) :
2011-04-06 19:52:26 +04:00
details = summary + " \n \n " + details
2011-04-06 19:22:03 +04:00
2010-12-02 01:48:07 +03:00
dialog = _errorDialog ( parent = self . get_parent ( ) ,
2012-05-14 17:24:56 +04:00
flags = 0 ,
message_type = dialog_type ,
buttons = buttons )
2010-12-02 01:48:07 +03:00
return dialog . show_dialog ( primary_text = summary ,
secondary_text = text2 ,
details = details , title = title ,
2013-09-07 04:16:37 +04:00
modal = modal )
2008-03-14 20:18:44 +03:00
2015-04-09 01:29:48 +03:00
2010-11-30 23:37:54 +03:00
###################################
# Simple one shot message dialogs #
###################################
def _simple_dialog ( self , dialog_type , buttons , text1 ,
2013-09-07 04:16:37 +04:00
text2 , title , widget = None , modal = True ) :
2010-11-30 23:37:54 +03:00
2012-05-14 17:24:56 +04:00
dialog = Gtk . MessageDialog ( self . get_parent ( ) ,
flags = Gtk . DialogFlags . DESTROY_WITH_PARENT ,
message_type = dialog_type ,
buttons = buttons )
2010-12-07 23:27:10 +03:00
if self . _simple :
self . _simple . destroy ( )
self . _simple = dialog
2018-01-11 23:32:18 +03:00
self . _simple . get_accessible ( ) . set_name ( " vmm dialog " )
2010-11-30 23:37:54 +03:00
2010-12-07 23:27:10 +03:00
return _launch_dialog ( self . _simple ,
2010-12-02 01:48:07 +03:00
text1 , text2 or " " , title or " " ,
2013-07-01 22:33:06 +04:00
widget = widget ,
2013-09-07 04:16:37 +04:00
modal = modal )
2008-03-14 20:18:44 +03:00
2013-09-07 04:16:37 +04:00
def val_err ( self , text1 , text2 = None , title = _ ( " Input Error " ) , modal = True ) :
2016-02-05 18:18:16 +03:00
logtext = _ ( " Validation Error: %s " ) % text1
2011-08-30 22:50:50 +04:00
if text2 :
logtext + = " %s " % text2
if isinstance ( text1 , Exception ) or isinstance ( text2 , Exception ) :
logging . exception ( logtext )
else :
self . _logtrace ( logtext )
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . ERROR
buttons = Gtk . ButtonsType . OK
2012-01-17 19:29:16 +04:00
self . _simple_dialog ( dtype , buttons ,
str ( text1 ) ,
text2 and str ( text2 ) or " " ,
2013-09-07 04:16:37 +04:00
str ( title ) , None , modal )
2010-11-30 23:37:54 +03:00
return False
2009-07-02 20:45:48 +04:00
2017-07-17 22:46:12 +03:00
def show_info ( self , text1 , text2 = None , title = " " , widget = None , modal = True ,
buttons = Gtk . ButtonsType . OK ) :
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . INFO
2013-09-07 04:16:37 +04:00
self . _simple_dialog ( dtype , buttons , text1 , text2 , title , widget , modal )
2010-11-30 23:37:54 +03:00
return False
2009-02-16 05:20:14 +03:00
2010-11-30 23:37:54 +03:00
def yes_no ( self , text1 , text2 = None , title = None ) :
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . WARNING
buttons = Gtk . ButtonsType . YES_NO
2010-11-30 23:37:54 +03:00
return self . _simple_dialog ( dtype , buttons , text1 , text2 , title )
2008-03-14 20:18:44 +03:00
2010-11-30 23:37:54 +03:00
def ok_cancel ( self , text1 , text2 = None , title = None ) :
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . WARNING
buttons = Gtk . ButtonsType . OK_CANCEL
2010-11-30 23:37:54 +03:00
return self . _simple_dialog ( dtype , buttons , text1 , text2 , title )
2009-02-16 05:20:14 +03:00
2010-11-30 23:37:54 +03:00
def ok ( self , text1 , text2 = None , title = None ) :
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . WARNING
buttons = Gtk . ButtonsType . OK
2010-11-30 23:37:54 +03:00
return self . _simple_dialog ( dtype , buttons , text1 , text2 , title )
2009-02-16 05:20:14 +03:00
2019-04-14 23:19:56 +03:00
def confirm_unapplied_changes ( self ) :
"""
Helper function for confirming whether to apply unapplied changes
"""
return self . chkbox_helper (
self . config . get_confirm_unapplied ,
self . config . set_confirm_unapplied ,
text1 = ( _ ( " There are unapplied changes. "
" Would you like to apply them now? " ) ) ,
chktext = _ ( " Don ' t warn me again. " ) ,
default = False )
2010-11-30 23:37:54 +03:00
##########################################
# One shot dialog with a checkbox prompt #
##########################################
2010-03-04 00:58:50 +03:00
2009-11-19 00:11:17 +03:00
def warn_chkbox ( self , text1 , text2 = None , chktext = None , buttons = None ) :
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . WARNING
buttons = buttons or Gtk . ButtonsType . OK_CANCEL
2010-12-02 01:48:07 +03:00
chkbox = _errorDialog ( parent = self . get_parent ( ) ,
2012-05-14 17:24:56 +04:00
flags = 0 ,
message_type = dtype ,
2010-12-02 01:48:07 +03:00
buttons = buttons )
return chkbox . show_dialog ( primary_text = text1 ,
secondary_text = text2 ,
chktext = chktext )
2009-09-24 00:42:56 +04:00
2009-11-19 00:11:17 +03:00
def err_chkbox ( self , text1 , text2 = None , chktext = None , buttons = None ) :
2012-05-14 17:24:56 +04:00
dtype = Gtk . MessageType . ERROR
buttons = buttons or Gtk . ButtonsType . OK
2010-12-02 01:48:07 +03:00
chkbox = _errorDialog ( parent = self . get_parent ( ) ,
2012-05-14 17:24:56 +04:00
flags = 0 ,
message_type = dtype ,
2010-12-02 01:48:07 +03:00
buttons = buttons )
return chkbox . show_dialog ( primary_text = text1 ,
secondary_text = text2 ,
chktext = chktext )
2014-01-27 02:42:24 +04:00
def chkbox_helper ( self , getcb , setcb , text1 , text2 = None ,
default = True ,
chktext = _ ( " Don ' t ask me again " ) ) :
"""
Helper to prompt user about proceeding with an operation
Returns True if the ' yes ' or ' ok ' button was selected , False otherwise
@default : What value to return if getcb tells us not to prompt
"""
do_prompt = getcb ( )
if not do_prompt :
return default
2014-03-22 19:21:19 +04:00
# pylint: disable=unpacking-non-sequence
2014-01-27 02:42:24 +04:00
res = self . warn_chkbox ( text1 = text1 , text2 = text2 ,
chktext = chktext ,
buttons = Gtk . ButtonsType . YES_NO )
response , skip_prompt = res
2014-07-24 19:41:11 +04:00
setcb ( not skip_prompt )
2014-01-27 02:42:24 +04:00
return response
2014-01-27 02:59:09 +04:00
def browse_local ( self , conn , dialog_name , start_folder = None ,
_type = None , dialog_type = None ,
confirm_func = None , browse_reason = None ,
choose_button = None , default_name = None ) :
"""
Helper function for launching a filechooser
@dialog_name : String to use in the title bar of the filechooser .
@conn : vmmConnection used by calling class
@start_folder : Folder the filechooser is viewing at startup
@_type : File extension to filter by ( e . g . " iso " , " png " )
@dialog_type : Maps to FileChooserDialog ' action '
@confirm_func : Optional callback function if file is chosen .
@browse_reason : The vmmConfig . CONFIG_DIR * reason we are browsing .
2014-09-28 15:37:16 +04:00
If set , this will override the ' folder ' parameter with the gsettings
2014-01-27 02:59:09 +04:00
value , and store the user chosen path .
"""
import os
# Initial setup
overwrite_confirm = False
if dialog_type is None :
dialog_type = Gtk . FileChooserAction . OPEN
if dialog_type == Gtk . FileChooserAction . SAVE :
if choose_button is None :
choose_button = Gtk . STOCK_SAVE
overwrite_confirm = True
if choose_button is None :
choose_button = Gtk . STOCK_OPEN
fcdialog = Gtk . FileChooserDialog ( title = dialog_name ,
2015-04-11 21:34:38 +03:00
parent = self . get_parent ( ) ,
2014-01-27 02:59:09 +04:00
action = dialog_type ,
buttons = ( Gtk . STOCK_CANCEL ,
Gtk . ResponseType . CANCEL ,
choose_button ,
Gtk . ResponseType . ACCEPT ) )
fcdialog . set_default_response ( Gtk . ResponseType . ACCEPT )
if default_name :
fcdialog . set_current_name ( default_name )
# If confirm is set, warn about a file overwrite
if confirm_func :
overwrite_confirm = True
fcdialog . connect ( " confirm-overwrite " , confirm_func )
fcdialog . set_do_overwrite_confirmation ( overwrite_confirm )
# Set file match pattern (ex. *.png)
if _type is not None :
pattern = _type
name = None
2017-10-11 14:35:41 +03:00
if isinstance ( _type , tuple ) :
2014-01-27 02:59:09 +04:00
pattern = _type [ 0 ]
name = _type [ 1 ]
f = Gtk . FileFilter ( )
f . add_pattern ( " *. " + pattern )
if name :
f . set_name ( name )
fcdialog . set_filter ( f )
# Set initial dialog folder
if browse_reason :
start_folder = self . config . get_default_directory (
conn , browse_reason )
if start_folder is not None :
if os . access ( start_folder , os . R_OK ) :
fcdialog . set_current_folder ( start_folder )
# Run the dialog and parse the response
ret = None
if fcdialog . run ( ) == Gtk . ResponseType . ACCEPT :
ret = fcdialog . get_filename ( )
fcdialog . destroy ( )
2014-09-28 15:37:16 +04:00
# Store the chosen directory in gsettings if necessary
2014-01-27 02:59:09 +04:00
if ret and browse_reason and not ret . startswith ( " /dev " ) :
self . config . set_default_directory (
os . path . dirname ( ret ) , browse_reason )
return ret
2010-12-02 01:48:07 +03:00
2012-05-14 17:24:56 +04:00
class _errorDialog ( Gtk . MessageDialog ) :
2010-12-02 01:48:07 +03:00
"""
Custom error dialog with optional check boxes or details drop down
"""
2010-12-10 19:47:07 +03:00
def __init__ ( self , * args , * * kwargs ) :
2012-05-14 17:24:56 +04:00
Gtk . MessageDialog . __init__ ( self , * args , * * kwargs )
2013-12-19 00:42:02 +04:00
2010-12-02 01:48:07 +03:00
self . set_title ( " " )
2013-12-19 00:42:02 +04:00
for child in self . get_message_area ( ) . get_children ( ) :
if hasattr ( child , " set_max_width_chars " ) :
child . set_max_width_chars ( 40 )
2009-09-24 00:42:56 +04:00
2018-01-11 23:32:18 +03:00
self . get_accessible ( ) . set_name ( " vmm dialog " )
2018-01-10 00:44:09 +03:00
2010-12-02 01:48:07 +03:00
self . chk_vbox = None
self . chk_align = None
self . init_chkbox ( )
2009-09-24 00:42:56 +04:00
2010-12-02 01:48:07 +03:00
self . buffer = None
self . buf_expander = None
self . init_details ( )
2009-09-24 00:42:56 +04:00
2010-12-02 01:48:07 +03:00
def init_chkbox ( self ) :
# Init check items
2012-05-14 17:24:56 +04:00
self . chk_vbox = Gtk . VBox ( False , False )
2009-09-24 00:42:56 +04:00
self . chk_vbox . set_spacing ( 0 )
2012-05-14 17:24:56 +04:00
self . chk_align = Gtk . Alignment ( )
2013-09-27 21:43:43 +04:00
self . chk_align . set_padding ( 0 , 0 , 0 , 0 )
2009-09-24 00:42:56 +04:00
self . chk_align . add ( self . chk_vbox )
self . chk_align . show_all ( )
2014-04-03 02:39:43 +04:00
self . vbox . pack_start ( # pylint: disable=no-member
self . chk_align , False , False , 0 )
2009-09-24 00:42:56 +04:00
2010-12-02 01:48:07 +03:00
def init_details ( self ) :
# Init details buffer
2012-05-14 17:24:56 +04:00
self . buffer = Gtk . TextBuffer ( )
self . buf_expander = Gtk . Expander . new ( _ ( " Details " ) )
sw = Gtk . ScrolledWindow ( )
sw . set_shadow_type ( Gtk . ShadowType . IN )
2010-12-10 19:47:07 +03:00
sw . set_size_request ( 400 , 240 )
2012-05-14 17:24:56 +04:00
sw . set_policy ( Gtk . PolicyType . AUTOMATIC , Gtk . PolicyType . AUTOMATIC )
details = Gtk . TextView . new_with_buffer ( self . buffer )
2010-12-10 19:47:07 +03:00
details . set_editable ( False )
details . set_overwrite ( False )
details . set_cursor_visible ( False )
2012-05-14 17:24:56 +04:00
details . set_wrap_mode ( Gtk . WrapMode . WORD )
2013-01-13 01:13:53 +04:00
details . set_border_width ( 6 )
2010-12-02 01:48:07 +03:00
sw . add ( details )
self . buf_expander . add ( sw )
2014-04-03 02:39:43 +04:00
self . vbox . pack_start ( # pylint: disable=no-member
self . buf_expander , False , False , 0 )
2010-12-02 01:48:07 +03:00
self . buf_expander . show_all ( )
def show_dialog ( self , primary_text , secondary_text = " " ,
title = " " , details = " " , chktext = " " ,
2013-09-07 04:16:37 +04:00
modal = True ) :
2009-09-24 00:42:56 +04:00
chkbox = None
res = None
2010-12-02 01:48:07 +03:00
# Hide starting widgets
2009-09-24 00:42:56 +04:00
self . hide ( )
2010-12-02 01:48:07 +03:00
self . buf_expander . hide ( )
2009-09-24 00:42:56 +04:00
for c in self . chk_vbox . get_children ( ) :
self . chk_vbox . remove ( c )
2010-12-02 01:48:07 +03:00
if details :
self . buffer . set_text ( details )
2010-12-08 21:05:38 +03:00
title = title or " "
2010-12-02 01:48:07 +03:00
self . buf_expander . show ( )
2009-09-24 00:42:56 +04:00
if chktext :
2012-05-14 17:24:56 +04:00
chkbox = Gtk . CheckButton ( chktext )
2009-09-24 00:42:56 +04:00
self . chk_vbox . add ( chkbox )
chkbox . show ( )
2010-12-02 01:48:07 +03:00
res = _launch_dialog ( self ,
primary_text , secondary_text or " " ,
title ,
2013-09-07 04:16:37 +04:00
modal = modal )
2010-12-02 01:48:07 +03:00
2009-09-24 00:42:56 +04:00
if chktext :
res = [ res , chkbox . get_active ( ) ]
return res