2019-04-15 00:31:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import gi
gi . require_version ( ' GtkSource ' , ' 4 ' )
from gi . repository import GtkSource
2019-06-06 00:40:54 +03:00
from . import uiutil
2019-04-15 00:31:41 +03:00
from . baseclass import vmmGObjectUI
_PAGE_DETAILS = 0
_PAGE_XML = 1
class vmmXMLEditor ( vmmGObjectUI ) :
__gsignals__ = {
" changed " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
" xml-requested " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
" xml-reset " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
}
def __init__ ( self , builder , topwin , parent_container , details_widget ) :
super ( ) . __init__ ( " xmleditor.ui " , None ,
builder = builder , topwin = topwin )
parent_container . remove ( details_widget )
parent_container . add ( self . widget ( " xml-notebook " ) )
self . widget ( " xml-details-box " ) . add ( details_widget )
self . _curpage = _PAGE_DETAILS
self . _srcxml = " "
self . _srcview = None
self . _srcbuf = None
self . _init_ui ( )
2019-06-15 12:58:43 +03:00
self . details_changed = False
2019-06-06 00:40:54 +03:00
self . add_gsettings_handle (
self . config . on_xmleditor_enabled_changed (
self . _xmleditor_enabled_changed_cb ) )
2019-04-15 00:31:41 +03:00
def _cleanup ( self ) :
pass
###########
# UI init #
###########
2019-06-06 00:40:54 +03:00
def _set_xmleditor_enabled_from_config ( self ) :
enabled = self . config . get_xmleditor_enabled ( )
self . _srcview . set_editable ( enabled )
uiutil . set_grid_row_visible ( self . widget ( " xml-warning-box " ) ,
not enabled )
2019-04-15 00:31:41 +03:00
def _init_ui ( self ) :
self . _srcview = GtkSource . View ( )
self . _srcbuff = self . _srcview . get_buffer ( )
lang = GtkSource . LanguageManager . get_default ( ) . get_language ( " xml " )
self . _srcbuff . set_language ( lang )
self . _srcview . set_monospace ( True )
self . _srcview . set_auto_indent ( True )
self . _srcview . get_accessible ( ) . set_name ( " XML editor " )
self . _srcbuff . set_highlight_syntax ( True )
self . _srcbuff . connect ( " changed " , self . _buffer_changed_cb )
self . widget ( " xml-notebook " ) . connect ( " switch-page " ,
self . _before_page_changed_cb )
self . widget ( " xml-notebook " ) . connect ( " notify::page " ,
self . _after_page_changed_cb )
self . _srcview . show_all ( )
self . widget ( " xml-scroll " ) . add ( self . _srcview )
2019-06-06 00:40:54 +03:00
self . _set_xmleditor_enabled_from_config ( )
2019-04-15 00:31:41 +03:00
####################
# Internal helpers #
####################
2019-06-15 12:58:43 +03:00
def _reselect_page ( self , pagenum ) :
2019-04-15 00:31:41 +03:00
# Setting _curpage first will shortcircuit our page changed callback
2019-06-15 12:58:43 +03:00
self . _curpage = pagenum
self . widget ( " xml-notebook " ) . set_current_page ( pagenum )
2019-04-15 00:31:41 +03:00
def _reset_xml ( self ) :
self . set_xml ( " " )
self . emit ( " xml-reset " )
def _reset_cursor ( self ) :
# Put cursor at the start of the second line. Starting on the
# first means XML open/close tags are highlighted which is weird
# starting visual
startiter = self . _srcbuff . get_start_iter ( )
startiter . forward_line ( )
self . _srcbuff . place_cursor ( startiter )
2019-06-15 12:58:43 +03:00
def _detials_unapplied_changes ( self ) :
if not self . details_changed :
return False
ret = self . err . yes_no (
_ ( " There are unapplied changes. " ) ,
_ ( " Your changes will be lost if you leave this tab. "
" Really leave this tab? " ) )
if ret :
self . details_changed = False
return not ret
def _xml_unapplied_changes ( self ) :
if self . _srcxml == self . get_xml ( ) :
return False
ret = self . err . yes_no (
_ ( " There are unapplied changes. " ) ,
_ ( " Your XML changes will be lost if you leave this tab. "
" Really leave this tab? " ) )
return not ret
2019-04-15 00:31:41 +03:00
##############
# Public API #
##############
def reset_state ( self ) :
"""
Clear XML and select the details page . Used when callers do
their own reset_state
"""
self . _reset_xml ( )
return self . widget ( " xml-notebook " ) . set_current_page ( _PAGE_DETAILS )
def get_xml ( self ) :
"""
Return the XML from the editor UI
"""
return self . _srcbuff . get_property ( " text " )
def set_xml ( self , xml ) :
"""
Set the editor UI XML to the passed string
"""
try :
self . _srcbuff . disconnect_by_func ( self . _buffer_changed_cb )
self . _srcxml = xml or " "
self . _srcbuff . set_text ( self . _srcxml )
self . _reset_cursor ( )
finally :
self . _srcbuff . connect ( " changed " , self . _buffer_changed_cb )
def set_xml_from_libvirtobject ( self , libvirtobject ) :
"""
Set the editor UI XML to the inactive XML from the passed
vmmLibvirtObject . If the XML UI isn ' t visible, we don ' t set
anything , which lets callers use this on every page refresh
"""
if not self . is_xml_selected ( ) :
return
xml = " "
if libvirtobject :
xml = libvirtobject . get_xml_to_define ( )
self . set_xml ( xml )
def is_xml_selected ( self ) :
"""
Return True if the XML page is selected
"""
return self . _curpage == _PAGE_XML
#############
# Listeners #
#############
def _buffer_changed_cb ( self , buf ) :
self . emit ( " changed " )
def _before_page_changed_cb ( self , notebook , widget , pagenum ) :
if self . _curpage == pagenum :
return
2019-06-15 12:58:43 +03:00
prevpage = self . _curpage
2019-04-15 00:31:41 +03:00
self . _curpage = pagenum
if pagenum == _PAGE_XML :
2019-06-15 12:58:43 +03:00
if not self . _detials_unapplied_changes ( ) :
# If the XML page is clicked, emit xml-requested signal which
# expects the user to call set_xml/set_libvirtobject. This saves
# having to fetch inactive XML up front, and gives users like
# a hook to actually serialize the final XML to return
self . emit ( " xml-requested " )
return
else :
if not self . _xml_unapplied_changes ( ) :
self . _reset_xml ( )
return
2019-04-15 00:31:41 +03:00
# I can't find anyway to make the notebook stay on the current page
# So set an idle callback to switch back to the XML page. It causes
# a visual UI blip unfortunately
2019-06-15 12:58:43 +03:00
self . idle_add ( self . _reselect_page , prevpage )
2019-04-15 00:31:41 +03:00
def _after_page_changed_cb ( self , notebook , gparam ) :
self . _curpage = notebook . get_current_page ( )
2019-06-06 00:40:54 +03:00
def _xmleditor_enabled_changed_cb ( self ) :
self . _set_xmleditor_enabled_from_config ( )