diff --git a/src/virt-manager.schemas.in b/src/virt-manager.schemas.in
index 987528954..5c646186a 100644
--- a/src/virt-manager.schemas.in
+++ b/src/virt-manager.schemas.in
@@ -351,6 +351,19 @@
+
+ /schemas/apps/::PACKAGE::/confirm/unapplied_dev
+ /apps/::PACKAGE::/confirm/unapplied_dev
+ ::PACKAGE::
+ bool
+ 1
+
+
+ Confirm about unapplied device changes
+ Whether we ask the user to apply or discard unapplied device changes
+
+
+
/schemas/apps/::PACKAGE::/manager_window_height
/apps/::PACKAGE::/manager_window_height
diff --git a/src/virtManager/config.py b/src/virtManager/config.py
index d7da2369e..a0ae3fd5c 100644
--- a/src/virtManager/config.py
+++ b/src/virtManager/config.py
@@ -386,6 +386,8 @@ class vmmConfig(object):
return self.conf.get_bool(self.conf_dir + "/confirm/removedev")
def get_confirm_interface(self):
return self.conf.get_bool(self.conf_dir + "/confirm/interface_power")
+ def get_confirm_unapplied(self):
+ return self.conf.get_bool(self.conf_dir + "/confirm/unapplied_dev")
def set_confirm_forcepoweroff(self, val):
@@ -398,6 +400,8 @@ class vmmConfig(object):
self.conf.set_bool(self.conf_dir + "/confirm/removedev", val)
def set_confirm_interface(self, val):
self.conf.set_bool(self.conf_dir + "/confirm/interface_power", val)
+ def set_confirm_unapplied(self, val):
+ self.conf.set_bool(self.conf_dir + "/confirm/unapplied_dev", val)
def on_confirm_forcepoweroff_changed(self, cb):
return self.conf.notify_add(self.conf_dir + "/confirm/forcepoweroff", cb)
@@ -409,6 +413,8 @@ class vmmConfig(object):
return self.conf.notify_add(self.conf_dir + "/confirm/removedev", cb)
def on_confirm_interface_changed(self, cb):
return self.conf.notify_add(self.conf_dir + "/confirm/interface_power", cb)
+ def on_confirm_unapplied_changed(self, cb):
+ return self.conf.notify_add(self.conf_dir + "/confirm/unapplied_dev", cb)
# System tray visibility
diff --git a/src/virtManager/details.py b/src/virtManager/details.py
index cef7fb5a5..74d5f752b 100644
--- a/src/virtManager/details.py
+++ b/src/virtManager/details.py
@@ -307,6 +307,7 @@ class vmmDetails(vmmGObjectUI):
w, h = self.vm.get_details_window_size()
self.topwin.set_default_size(w or 800, h or 600)
+ self.oldhwrow = None
self.addhwmenu = None
self.keycombo_menu = None
self.init_menus()
@@ -458,7 +459,7 @@ class vmmDetails(vmmGObjectUI):
self.vm.connect("config-changed", self.refresh_vm_state)
self.vm.connect("resources-sampled", self.refresh_resources)
self.widget("hw-list").get_selection().connect("changed",
- self.hw_selected)
+ self.hw_changed)
self.widget("config-boot-list").get_selection().connect(
"changed",
self.config_bootdev_selected)
@@ -477,6 +478,8 @@ class vmmDetails(vmmGObjectUI):
self.close()
try:
+ self.oldhwrow = None
+
if self.addhw:
self.addhw.cleanup()
self.addhw = None
@@ -1084,26 +1087,29 @@ class vmmDetails(vmmGObjectUI):
else:
self.widget("toolbar-box").hide()
- def get_boot_selection(self):
- widget = self.widget("config-boot-list")
+ def get_selected_row(self, widget):
selection = widget.get_selection()
model, treepath = selection.get_selected()
if treepath == None:
return None
return model[treepath]
+ def get_boot_selection(self):
+ return self.get_selected_row(self.widget("config-boot-list"))
+
def set_hw_selection(self, page):
hwlist = self.widget("hw-list")
selection = hwlist.get_selection()
selection.select_path(str(page))
+ def get_hw_row(self):
+ return self.get_selected_row(self.widget("hw-list"))
+
def get_hw_selection(self, field):
- vmlist = self.widget("hw-list")
- selection = vmlist.get_selection()
- active = selection.get_selected()
- if active[1] == None:
+ row = self.get_hw_row()
+ if not row:
return None
- return active[0].get_value(active[1], field)
+ return row[field]
def force_get_hw_pagetype(self, page=None):
if page:
@@ -1116,7 +1122,56 @@ class vmmDetails(vmmGObjectUI):
return page
- def hw_selected(self, ignore1=None, page=None, selected=True):
+ def compare_hw_rows(self, row1, row2):
+ if row1 == row2:
+ return True
+ if not row1 or not row2:
+ return False
+
+ for idx in range(len(row1)):
+ if row1[idx] != row2[idx]:
+ return False
+ return True
+
+ def has_unapplied_changes(self, row):
+ if not row:
+ return False
+
+ if not self.widget("config-apply").get_property("sensitive"):
+ return False
+
+ if util.chkbox_helper(self,
+ 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."),
+ alwaysrecord=True):
+ return False
+
+ return not self.config_apply(row=row)
+
+ def hw_changed(self, ignore):
+ newrow = self.get_hw_row()
+ oldrow = self.oldhwrow
+ model = self.widget("hw-list").get_model()
+
+ if self.compare_hw_rows(newrow, oldrow):
+ return
+
+ if self.has_unapplied_changes(oldrow):
+ # Unapplied changes, and syncing them failed
+ pageidx = 0
+ for idx in range(len(model)):
+ if self.compare_hw_rows(model[idx], oldrow):
+ pageidx = idx
+ break
+ self.set_hw_selection(pageidx)
+ else:
+ self.oldhwrow = newrow
+ self.hw_selected()
+
+ def hw_selected(self, page=None):
pagetype = self.force_get_hw_pagetype(page)
self.widget("config-remove").set_sensitive(True)
@@ -1165,8 +1220,7 @@ class vmmDetails(vmmGObjectUI):
return
rem = pagetype in remove_pages
- if selected:
- self.disable_apply()
+ self.disable_apply()
self.widget("config-remove").set_property("visible", rem)
self.widget("hw-panel").set_current_page(pagetype)
@@ -1184,6 +1238,11 @@ class vmmDetails(vmmGObjectUI):
is_details = True
pages = self.widget("details-pages")
+ if pages.get_current_page() == PAGE_DETAILS:
+ if self.has_unapplied_changes(self.get_hw_row()):
+ self.sync_details_console_view(True)
+ return
+
if is_details:
pages.set_current_page(PAGE_DETAILS)
else:
@@ -1732,13 +1791,20 @@ class vmmDetails(vmmGObjectUI):
# Details/Hardware config changes (apply button) #
##################################################
- def config_cancel(self, ignore):
+ def config_cancel(self, ignore=None):
# Remove current changes and deactive 'apply' button
self.hw_selected()
- def config_apply(self, ignore):
- pagetype = self.get_hw_selection(HW_LIST_COL_TYPE)
- devobj = self.get_hw_selection(HW_LIST_COL_DEVICE)
+ def config_apply(self, ignore=None, row=None):
+ pagetype = None
+ devobj = None
+
+ if not row:
+ row = self.get_hw_row()
+ if row:
+ pagetype = row[HW_LIST_COL_TYPE]
+ devobj = row[HW_LIST_COL_DEVICE]
+
key = devobj
ret = False
@@ -1768,10 +1834,11 @@ class vmmDetails(vmmGObjectUI):
else:
ret = False
except Exception, e:
- self.err.show_err(_("Error apply changes: %s") % e)
+ return self.err.show_err(_("Error apply changes: %s") % e)
if ret is not False:
self.disable_apply()
+ return True
def get_text(self, widgetname, strip=True):
ret = self.widget(widgetname).get_text()
diff --git a/src/virtManager/preferences.py b/src/virtManager/preferences.py
index 2d9ea003a..43bf08869 100644
--- a/src/virtManager/preferences.py
+++ b/src/virtManager/preferences.py
@@ -46,6 +46,7 @@ class vmmPreferences(vmmGObjectUI):
self.add_gconf_handle(self.config.on_confirm_pause_changed(self.refresh_confirm_pause))
self.add_gconf_handle(self.config.on_confirm_removedev_changed(self.refresh_confirm_removedev))
self.add_gconf_handle(self.config.on_confirm_interface_changed(self.refresh_confirm_interface))
+ self.add_gconf_handle(self.config.on_confirm_unapplied_changed(self.refresh_confirm_unapplied))
self.refresh_view_system_tray()
self.refresh_update_interval()
@@ -63,6 +64,7 @@ class vmmPreferences(vmmGObjectUI):
self.refresh_confirm_pause()
self.refresh_confirm_removedev()
self.refresh_confirm_interface()
+ self.refresh_confirm_unapplied()
self.window.signal_autoconnect({
"on_prefs_system_tray_toggled" : self.change_view_system_tray,
@@ -82,6 +84,7 @@ class vmmPreferences(vmmGObjectUI):
"on_prefs_confirm_pause_toggled": self.change_confirm_pause,
"on_prefs_confirm_removedev_toggled": self.change_confirm_removedev,
"on_prefs_confirm_interface_toggled": self.change_confirm_interface,
+ "on_prefs_confirm_unapplied_toggled": self.change_confirm_unapplied,
"on_prefs_btn_keys_define_clicked": self.change_grab_keys,
"on_prefs_graphics_type_changed": self.change_graphics_type,
})
@@ -192,6 +195,10 @@ class vmmPreferences(vmmGObjectUI):
ignore3=None, ignore4=None):
self.widget("prefs-confirm-interface").set_active(
self.config.get_confirm_interface())
+ def refresh_confirm_unapplied(self, ignore1=None, ignore2=None,
+ ignore3=None, ignore4=None):
+ self.widget("prefs-confirm-unapplied").set_active(
+ self.config.get_confirm_unapplied())
def grabkeys_get_string(self, keysyms):
keystr = None
@@ -286,6 +293,8 @@ class vmmPreferences(vmmGObjectUI):
self.config.set_confirm_removedev(src.get_active())
def change_confirm_interface(self, src):
self.config.set_confirm_interface(src.get_active())
+ def change_confirm_unapplied(self, src):
+ self.config.set_confirm_unapplied(src.get_active())
def change_graphics_type(self, src):
gtype = 'vnc'
diff --git a/src/virtManager/util.py b/src/virtManager/util.py
index 729ed067d..b1d4260c9 100644
--- a/src/virtManager/util.py
+++ b/src/virtManager/util.py
@@ -361,7 +361,8 @@ def pretty_bytes(val):
xpath = virtinst.util.get_xml_path
-def chkbox_helper(src, getcb, setcb, text1, text2=None):
+def chkbox_helper(src, getcb, setcb, text1, text2=None, alwaysrecord=False,
+ chktext=_("Don't ask me again")):
"""
Helper to prompt user about proceeding with an operation
Returns True if operation should be cancelled
@@ -373,14 +374,13 @@ def chkbox_helper(src, getcb, setcb, text1, text2=None):
return False
res = src.err.warn_chkbox(text1=text1, text2=text2,
- chktext=_("Don't ask me again."),
+ chktext=chktext,
buttons=gtk.BUTTONS_YES_NO)
response, skip_prompt = res
- if not response:
- return True
+ if alwaysrecord or response:
+ setcb(not skip_prompt)
- setcb(not skip_prompt)
- return False
+ return not response
def get_list_selection(widget):
selection = widget.get_selection()
diff --git a/src/vmm-preferences.glade b/src/vmm-preferences.glade
index bfd319d63..dc85526ac 100644
--- a/src/vmm-preferences.glade
+++ b/src/vmm-preferences.glade
@@ -589,7 +589,7 @@ Spice
True
- 5
+ 6
2
12
6
@@ -741,6 +741,33 @@ Spice
GTK_FILL
+
+
+ True
+ 0
+ Unapplied changes:
+
+
+ 5
+ 6
+ GTK_FILL
+
+
+
+
+ True
+ True
+ False
+ True
+
+
+
+ 1
+ 2
+ 5
+ 6
+
+