mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-03-01 04:58:27 +03:00
details: split out a top level vmwindow
vmmVMWindow handles all the menuing, and coordinating between the console, snapshots, and details panel. Simplifies the details code a bit which will help when we add xmlediting
This commit is contained in:
parent
ad4f869405
commit
f947afc3df
11008
ui/details.ui
11008
ui/details.ui
File diff suppressed because it is too large
Load Diff
825
ui/vmwindow.ui
Normal file
825
ui/vmwindow.ui
Normal file
@ -0,0 +1,825 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<object class="GtkAccelGroup" id="accelgroup1"/>
|
||||
<object class="GtkImage" id="image70">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-apply</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="vmm-vmwindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Virtual Machine</property>
|
||||
<property name="default_width">800</property>
|
||||
<property name="default_height">600</property>
|
||||
<accel-groups>
|
||||
<group name="accelgroup1"/>
|
||||
</accel-groups>
|
||||
<signal name="configure-event" handler="on_vmm_details_configure_event" swapped="no"/>
|
||||
<signal name="delete-event" handler="on_vmm_details_delete_event" swapped="no"/>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkMenuBar" id="details-menubar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="file1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="file1_menu">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="view_manager">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_View Manager</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_view_manager_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="separator15">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="close4">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="accel_group">accelgroup1</property>
|
||||
<signal name="activate" handler="on_details_menu_close_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="quit3">
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="accel_group">accelgroup1</property>
|
||||
<signal name="activate" handler="on_details_menu_quit_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="details-vm-menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Virtual _Machine</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_virtual_manager_activate" swapped="no"/>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="virtual_machine1_menu">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="separator2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="details-menu-vm-screenshot">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Take Screenshot</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_screenshot_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="details-menu-usb-redirection">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Redirect host USB device to virtual machine with SPICE graphics.</property>
|
||||
<property name="label" translatable="yes">_Redirect USB device</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_usb_redirection" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="view2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="view2_menu">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkRadioMenuItem" id="details-menu-view-console">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Console</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_as_radio">True</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_console_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioMenuItem" id="details-menu-view-details">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Details</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_as_radio">True</property>
|
||||
<property name="group">details-menu-view-console</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_details_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioMenuItem" id="details-menu-view-snapshots">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Sna_pshots</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_as_radio">True</property>
|
||||
<property name="group">details-menu-view-console</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_snapshots_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="menuitem2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckMenuItem" id="details-menu-view-fullscreen">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Fullscreen</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_view_fullscreen_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="detains-menu-view-size-to-vm">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Resize to VM</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_view_size_to_vm_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Scale Display</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="menu1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkRadioMenuItem" id="details-menu-view-scale-always">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Always</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_scale_always_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioMenuItem" id="details-menu-view-scale-fullscreen">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Only when Fullscreen</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_as_radio">True</property>
|
||||
<property name="group">details-menu-view-scale-always</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_scale_fullscreen_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioMenuItem" id="details-menu-view-scale-never">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Never</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_as_radio">True</property>
|
||||
<property name="group">details-menu-view-scale-always</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_scale_never_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="menuitem3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckMenuItem" id="details-menu-view-resizeguest">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Auto _resize VM with window</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="toggled" handler="on_details_menu_view_resizeguest_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="details-menu-view-serial-list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Text Consoles</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="separator7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckMenuItem" id="details-menu-view-toolbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">T_oolbar</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<signal name="activate" handler="on_details_menu_view_toolbar_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="details-menu-send-key">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Send _Key</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="toolbar-box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkToolbar" id="details-toolbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="control-vm-console">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Show the graphical console</property>
|
||||
<property name="label" translatable="yes">Console</property>
|
||||
<property name="icon_name">icon_console</property>
|
||||
<property name="active">True</property>
|
||||
<signal name="toggled" handler="on_control_vm_console_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="control-vm-details">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Show virtual hardware details</property>
|
||||
<property name="label" translatable="yes">Details</property>
|
||||
<property name="stock_id">gtk-info</property>
|
||||
<property name="group">control-vm-console</property>
|
||||
<signal name="toggled" handler="on_control_vm_details_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="toolbutton3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="control-run">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Power on the virtual machine</property>
|
||||
<property name="label" translatable="yes">Run</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-media-play</property>
|
||||
<signal name="clicked" handler="on_control_run_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="control-pause">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Pause the virtual machine</property>
|
||||
<property name="label" translatable="yes">Pause</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-media-pause</property>
|
||||
<signal name="toggled" handler="on_control_pause_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuToolButton" id="control-shutdown">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Shut down the virtual machine</property>
|
||||
<property name="label" translatable="yes">_Shut Down</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_control_shutdown_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="toolbutton7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="control-snapshots">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Snapshots</property>
|
||||
<property name="icon_name">vm_clone_wizard</property>
|
||||
<property name="group">control-vm-console</property>
|
||||
<signal name="toggled" handler="on_control_snapshots_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="control-fullscreen">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Switch to fullscreen view</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="stock_id">gtk-fullscreen</property>
|
||||
<signal name="toggled" handler="on_control_fullscreen_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolbar" id="customize-toolbar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="toolbar_style">both-horiz</property>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="details-finish-customize">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Begin Installation</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">_Begin Installation</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-apply</property>
|
||||
<signal name="clicked" handler="on_details_customize_finish_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="details-cancel-customize">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">_Cancel Installation</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-cancel</property>
|
||||
<signal name="clicked" handler="on_details_cancel_customize_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="details-pages">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="show_border">False</property>
|
||||
<signal name="switch-page" handler="on_details_pages_switch_page" after="yes" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="details-placeholder">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label77">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Details</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="console-pages">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="show_border">False</property>
|
||||
<signal name="switch-page" handler="on_console_pages_switch_page" after="yes" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkLabel" id="console-unavailable">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes"><b>The console is currently unavailable</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="selectable">True</property>
|
||||
<property name="width_chars">60</property>
|
||||
<property name="max_width_chars">60</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_expand">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label434">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Unavailable</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTable" id="console-auth">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">3</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_columns">3</property>
|
||||
<property name="column_spacing">3</property>
|
||||
<property name="row_spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label-auth-password">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Password:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">console-auth-password</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="console-auth-password">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="visibility">False</property>
|
||||
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="console-auth-remember">
|
||||
<property name="label" translatable="yes">_Save this password in your keyring</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Check to save password, uncheck to forget password.</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label-auth-username">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Username:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">console-auth-username</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="console-auth-username">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="console-auth-login">
|
||||
<property name="label" translatable="yes">_Login</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="image">image70</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_console_auth_login_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"/>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment17">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment18">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment19">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label438">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Auth</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="serial-pages">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="serial-pages-atkobject">
|
||||
<property name="AtkObject::accessible-name">serial-pages</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label82">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Serial</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkOverlay" id="console-overlay">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="console-gfx-scroll">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkViewport" id="console-gfx-viewport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resize_mode">queue</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="console-gfx-viewport-atkobject">
|
||||
<property name="AtkObject::accessible-name">console-gfx-viewport</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="index">-1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label439">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Graphics</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label420">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Console</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="snapshot-placeholder">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label88">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Snapshots</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
@ -26,7 +26,7 @@ class vmmGObject(GObject.GObject):
|
||||
# Singleton reference, if applicable (vmmSystray, vmmInspection, ...)
|
||||
_instance = None
|
||||
|
||||
# windowlist mapping, if applicable (vmmDetails, vmmHost, ...)
|
||||
# windowlist mapping, if applicable (vmmVMWindow, vmmHost, ...)
|
||||
_instances = None
|
||||
|
||||
# This saves a bunch of imports and typing
|
||||
|
@ -11,10 +11,10 @@ from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
from .baseclass import vmmGObject, vmmGObjectUI
|
||||
from .details import DETAILS_PAGE_CONSOLE
|
||||
from .serialcon import vmmSerialConsole
|
||||
from .sshtunnels import ConnectionInfo
|
||||
from .viewers import SpiceViewer, VNCViewer, have_spice_gtk
|
||||
from .vmwindow import DETAILS_PAGE_CONSOLE
|
||||
|
||||
|
||||
# console-pages IDs
|
||||
@ -223,7 +223,7 @@ class vmmConsolePages(vmmGObjectUI):
|
||||
self._serial_consoles = []
|
||||
self._init_menus()
|
||||
|
||||
# Signals are added by vmmDetails. Don't use connect_signals here
|
||||
# Signals are added by vmmVMWindow. Don't use connect_signals here
|
||||
# or it changes will be overwritten
|
||||
|
||||
self.widget("console-gfx-scroll").connect("size-allocate",
|
||||
@ -996,9 +996,9 @@ class vmmConsolePages(vmmGObjectUI):
|
||||
src.show_all()
|
||||
|
||||
|
||||
##########################
|
||||
# API used by vmmDetails #
|
||||
##########################
|
||||
###########################
|
||||
# API used by vmmVMWindow #
|
||||
###########################
|
||||
|
||||
def details_viewer_is_visible(self):
|
||||
return bool(self._viewer and self._viewer.console_get_visible())
|
||||
|
@ -21,15 +21,15 @@ from virtinst import util
|
||||
from . import uiutil
|
||||
from .addstorage import vmmAddStorage
|
||||
from .asyncjob import vmmAsyncJob
|
||||
from .connmanager import vmmConnectionManager
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .details import vmmDetails
|
||||
from .connmanager import vmmConnectionManager
|
||||
from .domain import vmmDomainVirtinst
|
||||
from .engine import vmmEngine
|
||||
from .mediacombo import vmmMediaCombo
|
||||
from .netlist import vmmNetworkList
|
||||
from .oslist import vmmOSList
|
||||
from .storagebrowse import vmmStorageBrowser
|
||||
from .vmwindow import vmmVMWindow
|
||||
|
||||
# Number of seconds to wait for media detection
|
||||
DETECT_TIMEOUT = 20
|
||||
@ -2018,10 +2018,10 @@ class vmmCreate(vmmGObjectUI):
|
||||
logging.debug("User closed customize window, closing wizard")
|
||||
self._close_requested()
|
||||
|
||||
# We specifically don't use vmmDetails.get_instance here since
|
||||
# it's not a top level Details window
|
||||
# We specifically don't use vmmVMWindow.get_instance here since
|
||||
# it's not a top level VM window
|
||||
self._cleanup_customize_window()
|
||||
self._customize_window = vmmDetails(virtinst_guest, self.topwin)
|
||||
self._customize_window = vmmVMWindow(virtinst_guest, self.topwin)
|
||||
self._customize_window.connect(
|
||||
"customize-finished", start_install_wrapper, guest)
|
||||
self._customize_window.connect("closed", config_canceled)
|
||||
@ -2045,7 +2045,7 @@ class vmmCreate(vmmGObjectUI):
|
||||
self._close()
|
||||
|
||||
# Launch details dialog for new VM
|
||||
vmmDetails.get_instance(self, foundvm).show()
|
||||
vmmVMWindow.get_instance(self, foundvm).show()
|
||||
|
||||
|
||||
def _start_install(self, guest):
|
||||
|
@ -7,7 +7,6 @@
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
|
||||
import libvirt
|
||||
@ -15,19 +14,16 @@ import libvirt
|
||||
import virtinst
|
||||
from virtinst import util
|
||||
|
||||
from . import vmmenu
|
||||
from . import uiutil
|
||||
from .addhardware import vmmAddHardware
|
||||
from .addstorage import vmmAddStorage
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .engine import vmmEngine
|
||||
from .fsdetails import vmmFSDetails
|
||||
from .gfxdetails import vmmGraphicsDetails
|
||||
from .graphwidgets import Sparkline
|
||||
from .mediacombo import vmmMediaCombo
|
||||
from .netlist import vmmNetworkList
|
||||
from .oslist import vmmOSList
|
||||
from .snapshots import vmmSnapshotPage
|
||||
from .storagebrowse import vmmStorageBrowser
|
||||
from .vsockdetails import vmmVsockDetails
|
||||
|
||||
@ -155,12 +151,6 @@ remove_pages = [HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT,
|
||||
BOOT_ACTIVE,
|
||||
BOOT_CAN_SELECT) = range(5)
|
||||
|
||||
# Main tab pages
|
||||
(DETAILS_PAGE_DETAILS,
|
||||
DETAILS_PAGE_CONSOLE,
|
||||
DETAILS_PAGE_SNAPSHOTS) = range(3)
|
||||
|
||||
|
||||
|
||||
def _calculate_disk_bus_index(disklist):
|
||||
# Iterate through all disks and calculate what number they are
|
||||
@ -342,78 +332,19 @@ def _warn_cpu_thread_topo(threads, cpu_model):
|
||||
return False
|
||||
|
||||
|
||||
def _label_for_os_type(os_type):
|
||||
typemap = {
|
||||
"dos": _("MS-DOS/FreeDOS"),
|
||||
"freebsd": _("FreeBSD"),
|
||||
"hurd": _("GNU/Hurd"),
|
||||
"linux": _("Linux"),
|
||||
"minix": _("MINIX"),
|
||||
"netbsd": _("NetBSD"),
|
||||
"openbsd": _("OpenBSD"),
|
||||
"windows": _("Microsoft Windows"),
|
||||
}
|
||||
try:
|
||||
return typemap[os_type]
|
||||
except KeyError:
|
||||
return _("unknown")
|
||||
|
||||
|
||||
class vmmDetails(vmmGObjectUI):
|
||||
__gsignals__ = {
|
||||
"customize-finished": (vmmGObjectUI.RUN_FIRST, None, []),
|
||||
"closed": (vmmGObjectUI.RUN_FIRST, None, []),
|
||||
}
|
||||
def __init__(self, vm, builder, topwin, is_customize_dialog):
|
||||
vmmGObjectUI.__init__(self, "details.ui",
|
||||
None, builder=builder, topwin=topwin)
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, parentobj, vm):
|
||||
try:
|
||||
# Maintain one dialog per VM
|
||||
key = "%s+%s" % (vm.conn.get_uri(), vm.get_uuid())
|
||||
if cls._instances is None:
|
||||
cls._instances = {}
|
||||
if key not in cls._instances:
|
||||
cls._instances[key] = vmmDetails(vm)
|
||||
return cls._instances[key]
|
||||
except Exception as e:
|
||||
if not parentobj:
|
||||
raise
|
||||
parentobj.err.show_err(
|
||||
_("Error launching details: %s") % str(e))
|
||||
|
||||
def __init__(self, vm, parent=None):
|
||||
vmmGObjectUI.__init__(self, "details.ui", "vmm-details")
|
||||
self.vm = vm
|
||||
|
||||
self.is_customize_dialog = False
|
||||
if parent:
|
||||
# Details window is being abused as a 'configure before install'
|
||||
# dialog, set things as appropriate
|
||||
self.is_customize_dialog = True
|
||||
self.topwin.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
||||
self.topwin.set_transient_for(parent)
|
||||
|
||||
self.widget("toolbar-box").show()
|
||||
self.widget("customize-toolbar").show()
|
||||
self.widget("details-toolbar").hide()
|
||||
self.widget("details-menubar").hide()
|
||||
pages = self.widget("details-pages")
|
||||
pages.set_current_page(DETAILS_PAGE_DETAILS)
|
||||
else:
|
||||
self.conn.connect("vm-removed", self._vm_removed)
|
||||
|
||||
self.active_edits = []
|
||||
self.top_box = self.widget("details-top-box")
|
||||
|
||||
self.addhw = None
|
||||
self.storage_browser = None
|
||||
self._mediacombo = None
|
||||
|
||||
self.ignoreDetails = False
|
||||
|
||||
from .console import vmmConsolePages
|
||||
self.console = vmmConsolePages(self.vm, self.builder, self.topwin)
|
||||
self.snapshots = vmmSnapshotPage(self.vm, self.builder, self.topwin)
|
||||
self.widget("snapshot-placeholder").add(self.snapshots.top_box)
|
||||
self.is_customize_dialog = is_customize_dialog
|
||||
|
||||
self._mediacombo = vmmMediaCombo(self.conn, self.builder, self.topwin)
|
||||
self.widget("disk-source-align").add(self._mediacombo.top_box)
|
||||
@ -466,20 +397,9 @@ class vmmDetails(vmmGObjectUI):
|
||||
self.vsockdetails.connect("changed-cid",
|
||||
lambda *x: self.enable_apply(x, EDIT_VSOCK_CID))
|
||||
|
||||
# Set default window size
|
||||
w, h = self.vm.get_details_window_size()
|
||||
if w <= 0:
|
||||
w = 800
|
||||
if h <= 0:
|
||||
h = 600
|
||||
self.topwin.set_default_size(w, h)
|
||||
self._window_size = None
|
||||
|
||||
self.oldhwkey = None
|
||||
self.addhwmenu = None
|
||||
self._addhwmenuitems = None
|
||||
self._shutdownmenu = None
|
||||
self._vmmenu = None
|
||||
self._os_list = None
|
||||
self.init_menus()
|
||||
self.init_details()
|
||||
@ -491,35 +411,8 @@ class vmmDetails(vmmGObjectUI):
|
||||
self.init_graphs()
|
||||
|
||||
self.builder.connect_signals({
|
||||
"on_close_details_clicked": self.close,
|
||||
"on_details_menu_close_activate": self.close,
|
||||
"on_vmm_details_delete_event": self._window_delete_event,
|
||||
"on_vmm_details_configure_event": self.window_resized,
|
||||
"on_details_menu_quit_activate": self.exit_app,
|
||||
"on_hw_list_changed": self.hw_changed,
|
||||
|
||||
"on_control_vm_details_toggled": self.details_console_changed,
|
||||
"on_control_vm_console_toggled": self.details_console_changed,
|
||||
"on_control_snapshots_toggled": self.details_console_changed,
|
||||
"on_control_run_clicked": self.control_vm_run,
|
||||
"on_control_shutdown_clicked": self.control_vm_shutdown,
|
||||
"on_control_pause_toggled": self.control_vm_pause,
|
||||
"on_control_fullscreen_toggled": self.control_fullscreen,
|
||||
|
||||
"on_details_customize_finish_clicked": self.customize_finish,
|
||||
"on_details_cancel_customize_clicked": self._customize_cancel_clicked,
|
||||
|
||||
"on_details_menu_virtual_manager_activate": self.control_vm_menu,
|
||||
"on_details_menu_screenshot_activate": self.control_vm_screenshot,
|
||||
"on_details_menu_usb_redirection": self.control_vm_usb_redirection,
|
||||
"on_details_menu_view_toolbar_activate": self.toggle_toolbar,
|
||||
"on_details_menu_view_manager_activate": self.view_manager,
|
||||
"on_details_menu_view_details_toggled": self.details_console_changed,
|
||||
"on_details_menu_view_console_toggled": self.details_console_changed,
|
||||
"on_details_menu_view_snapshots_toggled": self.details_console_changed,
|
||||
|
||||
"on_details_pages_switch_page": self.switch_page,
|
||||
|
||||
"on_overview_name_changed": lambda *x: self.enable_apply(x, EDIT_NAME),
|
||||
"on_overview_title_changed": lambda *x: self.enable_apply(x, EDIT_TITLE),
|
||||
"on_machine_type_changed": lambda *x: self.enable_apply(x, EDIT_MACHTYPE),
|
||||
@ -613,40 +506,10 @@ class vmmDetails(vmmGObjectUI):
|
||||
"on_hw_list_button_press_event": self.popup_addhw_menu,
|
||||
|
||||
"on_tpm_model_combo_changed": lambda *x: self.enable_apply(x, EDIT_TPM_MODEL),
|
||||
|
||||
# Listeners stored in vmmConsolePages
|
||||
"on_details_menu_view_fullscreen_activate": (
|
||||
self.console.details_toggle_fullscreen),
|
||||
"on_details_menu_view_size_to_vm_activate": (
|
||||
self.console.details_size_to_vm),
|
||||
"on_details_menu_view_scale_always_toggled": (
|
||||
self.console.details_scaling_ui_changed_cb),
|
||||
"on_details_menu_view_scale_fullscreen_toggled": (
|
||||
self.console.details_scaling_ui_changed_cb),
|
||||
"on_details_menu_view_scale_never_toggled": (
|
||||
self.console.details_scaling_ui_changed_cb),
|
||||
"on_details_menu_view_resizeguest_toggled": (
|
||||
self.console.details_resizeguest_ui_changed_cb),
|
||||
|
||||
"on_console_pages_switch_page": (
|
||||
self.console.details_page_changed),
|
||||
"on_console_auth_password_activate": (
|
||||
self.console.details_auth_login),
|
||||
"on_console_auth_login_clicked": (
|
||||
self.console.details_auth_login),
|
||||
})
|
||||
|
||||
# Deliberately keep all this after signal connection
|
||||
self.vm.connect("state-changed", self.refresh_vm_state)
|
||||
self.vm.connect("resources-sampled", self.refresh_resources)
|
||||
self.vm.connect("inspection-changed",
|
||||
lambda *x: self.refresh_os_page())
|
||||
|
||||
self.populate_hw_list()
|
||||
|
||||
self.hw_selected()
|
||||
self.refresh_vm_state()
|
||||
self.activate_default_page()
|
||||
|
||||
|
||||
@property
|
||||
@ -665,17 +528,6 @@ class vmmDetails(vmmGObjectUI):
|
||||
|
||||
self._mediacombo.cleanup()
|
||||
self._mediacombo = None
|
||||
self.console.cleanup()
|
||||
self.console = None
|
||||
self.snapshots.cleanup()
|
||||
self.snapshots = None
|
||||
self._shutdownmenu.destroy()
|
||||
self._shutdownmenu = None
|
||||
self._vmmenu.destroy()
|
||||
self._vmmenu = None
|
||||
|
||||
if self._window_size:
|
||||
self.vm.set_details_window_size(*self._window_size)
|
||||
|
||||
self.conn.disconnect_by_obj(self)
|
||||
self.vm = None
|
||||
@ -691,90 +543,12 @@ class vmmDetails(vmmGObjectUI):
|
||||
self.vsockdetails.cleanup()
|
||||
self.vsockdetails = None
|
||||
|
||||
def show(self):
|
||||
logging.debug("Showing VM details: %s", self.vm)
|
||||
vis = self.is_visible()
|
||||
self.topwin.present()
|
||||
if vis:
|
||||
return
|
||||
|
||||
vmmEngine.get_instance().increment_window_counter()
|
||||
self.refresh_vm_state()
|
||||
|
||||
def customize_finish(self, src):
|
||||
ignore = src
|
||||
if self.has_unapplied_changes(self.get_hw_row()):
|
||||
return
|
||||
self.emit("customize-finished")
|
||||
|
||||
def _vm_removed(self, _conn, connkey):
|
||||
if self.vm.get_connkey() == connkey:
|
||||
self.cleanup()
|
||||
|
||||
def _customize_cancel(self):
|
||||
logging.debug("Asking to cancel customization")
|
||||
|
||||
result = self.err.yes_no(
|
||||
_("This will abort the installation. Are you sure?"))
|
||||
if not result:
|
||||
logging.debug("Customize cancel aborted")
|
||||
return
|
||||
|
||||
logging.debug("Canceling customization")
|
||||
return self._close()
|
||||
|
||||
def _customize_cancel_clicked(self, src):
|
||||
ignore = src
|
||||
return self._customize_cancel()
|
||||
|
||||
def _window_delete_event(self, ignore1=None, ignore2=None):
|
||||
return self.close()
|
||||
|
||||
def close(self, ignore1=None, ignore2=None):
|
||||
if self.is_visible():
|
||||
logging.debug("Closing VM details: %s", self.vm)
|
||||
return self._close()
|
||||
|
||||
def _close(self):
|
||||
fs = self.widget("details-menu-view-fullscreen")
|
||||
if fs.get_active():
|
||||
fs.set_active(False)
|
||||
|
||||
if not self.is_visible():
|
||||
return
|
||||
|
||||
self.topwin.hide()
|
||||
if self.console.details_viewer_is_visible():
|
||||
try:
|
||||
self.console.details_close_viewer()
|
||||
except Exception:
|
||||
logging.error("Failure when disconnecting from desktop server")
|
||||
|
||||
self.emit("closed")
|
||||
vmmEngine.get_instance().decrement_window_counter()
|
||||
return 1
|
||||
|
||||
|
||||
##########################
|
||||
# Initialization helpers #
|
||||
##########################
|
||||
|
||||
def init_menus(self):
|
||||
# Virtual Machine menu
|
||||
self._shutdownmenu = vmmenu.VMShutdownMenu(self, lambda: self.vm)
|
||||
self.widget("control-shutdown").set_menu(self._shutdownmenu)
|
||||
self.widget("control-shutdown").set_icon_name("system-shutdown")
|
||||
|
||||
topmenu = self.widget("details-vm-menu")
|
||||
submenu = topmenu.get_submenu()
|
||||
self._vmmenu = vmmenu.VMActionMenu(
|
||||
self, lambda: self.vm, show_open=False)
|
||||
for child in submenu.get_children():
|
||||
submenu.remove(child)
|
||||
self._vmmenu.add(child)
|
||||
topmenu.set_submenu(self._vmmenu)
|
||||
topmenu.show_all()
|
||||
|
||||
# Add HW popup menu
|
||||
self.addhwmenu = Gtk.Menu()
|
||||
|
||||
@ -799,9 +573,6 @@ class vmmDetails(vmmGObjectUI):
|
||||
self.addhwmenu.add(i)
|
||||
|
||||
self.widget("hw-panel").set_show_tabs(False)
|
||||
self.widget("details-pages").set_show_tabs(False)
|
||||
self.widget("details-menu-view-toolbar").set_active(
|
||||
self.config.get_details_show_toolbar())
|
||||
|
||||
|
||||
def init_graphs(self):
|
||||
@ -1114,11 +885,6 @@ class vmmDetails(vmmGObjectUI):
|
||||
# Window state listeners #
|
||||
##########################
|
||||
|
||||
def window_resized(self, ignore, ignore2):
|
||||
if not self.is_visible():
|
||||
return
|
||||
self._window_size = self.topwin.get_size()
|
||||
|
||||
def popup_addhw_menu(self, widget, event):
|
||||
ignore = widget
|
||||
if event.button != 3:
|
||||
@ -1142,24 +908,6 @@ class vmmDetails(vmmGObjectUI):
|
||||
|
||||
self.addhwmenu.popup_at_pointer(event)
|
||||
|
||||
def control_fullscreen(self, src):
|
||||
menu = self.widget("details-menu-view-fullscreen")
|
||||
if src.get_active() != menu.get_active():
|
||||
menu.set_active(src.get_active())
|
||||
|
||||
def toggle_toolbar(self, src):
|
||||
if self.is_customize_dialog:
|
||||
return
|
||||
|
||||
active = src.get_active()
|
||||
self.config.set_details_show_toolbar(active)
|
||||
|
||||
if (active and not
|
||||
self.widget("details-menu-view-fullscreen").get_active()):
|
||||
self.widget("toolbar-box").show()
|
||||
else:
|
||||
self.widget("toolbar-box").hide()
|
||||
|
||||
def get_boot_selection(self):
|
||||
return uiutil.get_list_selected_row(self.widget("boot-list"))
|
||||
|
||||
@ -1281,113 +1029,10 @@ class vmmDetails(vmmGObjectUI):
|
||||
|
||||
self.widget("hw-panel").set_current_page(pagetype)
|
||||
|
||||
def details_console_changed(self, src):
|
||||
if self.ignoreDetails:
|
||||
return
|
||||
|
||||
if not src.get_active():
|
||||
return
|
||||
|
||||
is_details = (src == self.widget("control-vm-details") or
|
||||
src == self.widget("details-menu-view-details"))
|
||||
is_snapshot = (src == self.widget("control-snapshots") or
|
||||
src == self.widget("details-menu-view-snapshots"))
|
||||
|
||||
pages = self.widget("details-pages")
|
||||
if pages.get_current_page() == DETAILS_PAGE_DETAILS:
|
||||
if self.has_unapplied_changes(self.get_hw_row()):
|
||||
self.sync_details_console_view(True)
|
||||
return
|
||||
self.disable_apply()
|
||||
|
||||
if is_details:
|
||||
pages.set_current_page(DETAILS_PAGE_DETAILS)
|
||||
elif is_snapshot:
|
||||
self.snapshots.show_page()
|
||||
pages.set_current_page(DETAILS_PAGE_SNAPSHOTS)
|
||||
else:
|
||||
pages.set_current_page(DETAILS_PAGE_CONSOLE)
|
||||
|
||||
def sync_details_console_view(self, newpage):
|
||||
details = self.widget("control-vm-details")
|
||||
details_menu = self.widget("details-menu-view-details")
|
||||
console = self.widget("control-vm-console")
|
||||
console_menu = self.widget("details-menu-view-console")
|
||||
snapshot = self.widget("control-snapshots")
|
||||
snapshot_menu = self.widget("details-menu-view-snapshots")
|
||||
|
||||
is_details = newpage == DETAILS_PAGE_DETAILS
|
||||
is_snapshot = newpage == DETAILS_PAGE_SNAPSHOTS
|
||||
is_console = not is_details and not is_snapshot
|
||||
|
||||
try:
|
||||
self.ignoreDetails = True
|
||||
|
||||
details.set_active(is_details)
|
||||
details_menu.set_active(is_details)
|
||||
snapshot.set_active(is_snapshot)
|
||||
snapshot_menu.set_active(is_snapshot)
|
||||
console.set_active(is_console)
|
||||
console_menu.set_active(is_console)
|
||||
finally:
|
||||
self.ignoreDetails = False
|
||||
|
||||
def switch_page(self, notebook=None, ignore2=None, newpage=None):
|
||||
for i in range(notebook.get_n_pages()):
|
||||
w = notebook.get_nth_page(i)
|
||||
w.set_visible(i == newpage)
|
||||
|
||||
self.page_refresh(newpage)
|
||||
|
||||
self.sync_details_console_view(newpage)
|
||||
self.console.details_refresh_can_fullscreen()
|
||||
|
||||
def change_run_text(self, can_restore):
|
||||
if can_restore:
|
||||
text = _("_Restore")
|
||||
else:
|
||||
text = _("_Run")
|
||||
strip_text = text.replace("_", "")
|
||||
|
||||
self.widget("details-vm-menu").get_submenu().change_run_text(text)
|
||||
self.widget("control-run").set_label(strip_text)
|
||||
|
||||
def refresh_vm_state(self, ignore=None):
|
||||
vm = self.vm
|
||||
status = self.vm.status()
|
||||
|
||||
self.widget("details-menu-view-toolbar").set_active(
|
||||
self.config.get_details_show_toolbar())
|
||||
self.toggle_toolbar(self.widget("details-menu-view-toolbar"))
|
||||
|
||||
active = vm.is_active()
|
||||
run = vm.is_runable()
|
||||
stop = vm.is_stoppable()
|
||||
paused = vm.is_paused()
|
||||
|
||||
if vm.managedsave_supported:
|
||||
self.change_run_text(vm.has_managed_save())
|
||||
|
||||
self.widget("control-run").set_sensitive(run)
|
||||
self.widget("control-shutdown").set_sensitive(stop)
|
||||
self.widget("control-shutdown").get_menu().update_widget_states(vm)
|
||||
self.widget("control-pause").set_sensitive(stop)
|
||||
|
||||
if paused:
|
||||
pauseTooltip = _("Resume the virtual machine")
|
||||
else:
|
||||
pauseTooltip = _("Pause the virtual machine")
|
||||
self.widget("control-pause").set_tooltip_text(pauseTooltip)
|
||||
|
||||
self.widget("details-vm-menu").get_submenu().update_widget_states(vm)
|
||||
self.set_pause_state(paused)
|
||||
|
||||
def vmwindow_refresh_vm_state(self):
|
||||
active = self.vm.is_active()
|
||||
self.widget("overview-name").set_editable(not active)
|
||||
|
||||
self.console.details_update_widget_states()
|
||||
if not run:
|
||||
self.activate_default_console_page()
|
||||
|
||||
reason = self.vm.run_status_reason()
|
||||
if reason:
|
||||
status = "%s (%s)" % (self.vm.run_status(), reason)
|
||||
@ -1395,68 +1040,14 @@ class vmmDetails(vmmGObjectUI):
|
||||
status = self.vm.run_status()
|
||||
self.widget("overview-status-text").set_text(status)
|
||||
self.widget("overview-status-icon").set_from_icon_name(
|
||||
self.vm.run_status_icon_name(), Gtk.IconSize.BUTTON)
|
||||
|
||||
details = self.widget("details-pages")
|
||||
self.page_refresh(details.get_current_page())
|
||||
|
||||
errmsg = self.vm.snapshots_supported()
|
||||
cansnap = not bool(errmsg)
|
||||
self.widget("control-snapshots").set_sensitive(cansnap)
|
||||
self.widget("details-menu-view-snapshots").set_sensitive(cansnap)
|
||||
tooltip = _("Manage VM snapshots")
|
||||
if not cansnap:
|
||||
tooltip += "\n" + errmsg
|
||||
self.widget("control-snapshots").set_tooltip_text(tooltip)
|
||||
self.vm.run_status_icon_name(),
|
||||
Gtk.IconSize.BUTTON)
|
||||
|
||||
|
||||
#############################
|
||||
# External action listeners #
|
||||
#############################
|
||||
|
||||
def view_manager(self, _src):
|
||||
from .manager import vmmManager
|
||||
vmmManager.get_instance(self).show()
|
||||
|
||||
def exit_app(self, _src):
|
||||
vmmEngine.get_instance().exit_app()
|
||||
|
||||
def activate_default_console_page(self):
|
||||
pages = self.widget("details-pages")
|
||||
|
||||
# console.activate_default_console_page() will as a side effect
|
||||
# switch to DETAILS_PAGE_CONSOLE. However this code path is triggered
|
||||
# when the user runs a VM while they are focused on the details page,
|
||||
# and we don't want to switch pages out from under them.
|
||||
origpage = pages.get_current_page()
|
||||
self.console.details_activate_default_console_page()
|
||||
pages.set_current_page(origpage)
|
||||
|
||||
# activate_* are called from engine.py via CLI options
|
||||
def activate_default_page(self):
|
||||
if self.is_customize_dialog:
|
||||
return
|
||||
pages = self.widget("details-pages")
|
||||
pages.set_current_page(DETAILS_PAGE_CONSOLE)
|
||||
self.activate_default_console_page()
|
||||
|
||||
def activate_console_page(self):
|
||||
pages = self.widget("details-pages")
|
||||
pages.set_current_page(DETAILS_PAGE_CONSOLE)
|
||||
|
||||
def activate_performance_page(self):
|
||||
self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS)
|
||||
index = 0
|
||||
model = self.widget("hw-list").get_model()
|
||||
for idx, row in enumerate(model):
|
||||
if row[HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS:
|
||||
index = idx
|
||||
break
|
||||
self.set_hw_selection(index)
|
||||
|
||||
def activate_config_page(self):
|
||||
self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS)
|
||||
|
||||
def add_hardware(self, src_ignore):
|
||||
try:
|
||||
if self.addhw is None:
|
||||
@ -1471,102 +1062,6 @@ class vmmDetails(vmmGObjectUI):
|
||||
devobj = self.get_hw_row()[HW_LIST_COL_DEVICE]
|
||||
self.remove_device(devobj)
|
||||
|
||||
def set_pause_state(self, state):
|
||||
src = self.widget("control-pause")
|
||||
try:
|
||||
src.handler_block_by_func(self.control_vm_pause)
|
||||
src.set_active(state)
|
||||
finally:
|
||||
src.handler_unblock_by_func(self.control_vm_pause)
|
||||
|
||||
def control_vm_pause(self, src):
|
||||
do_pause = src.get_active()
|
||||
|
||||
# Set button state back to original value: just let the status
|
||||
# update function fix things for us
|
||||
self.set_pause_state(not do_pause)
|
||||
|
||||
if do_pause:
|
||||
vmmenu.VMActionUI.suspend(self, self.vm)
|
||||
else:
|
||||
vmmenu.VMActionUI.resume(self, self.vm)
|
||||
|
||||
def control_vm_menu(self, src_ignore):
|
||||
can_usb = bool(self.console.details_viewer_has_usb_redirection() and
|
||||
self.vm.has_spicevmc_type_redirdev())
|
||||
self.widget("details-menu-usb-redirection").set_sensitive(can_usb)
|
||||
|
||||
def control_vm_run(self, src_ignore):
|
||||
if self.has_unapplied_changes(self.get_hw_row()):
|
||||
return
|
||||
vmmenu.VMActionUI.run(self, self.vm)
|
||||
|
||||
def control_vm_shutdown(self, src_ignore):
|
||||
vmmenu.VMActionUI.shutdown(self, self.vm)
|
||||
|
||||
def control_vm_screenshot(self, src):
|
||||
ignore = src
|
||||
try:
|
||||
return self._take_screenshot()
|
||||
except Exception as e:
|
||||
self.err.show_err(_("Error taking screenshot: %s") % str(e))
|
||||
|
||||
def control_vm_usb_redirection(self, src):
|
||||
ignore = src
|
||||
spice_usbdev_dialog = self.err
|
||||
|
||||
spice_usbdev_widget = self.console.details_viewer_get_usb_widget()
|
||||
if not spice_usbdev_widget:
|
||||
self.err.show_err(_("Error initializing spice USB device widget"))
|
||||
return
|
||||
|
||||
spice_usbdev_widget.show()
|
||||
spice_usbdev_dialog.show_info(_("Select USB devices for redirection"),
|
||||
widget=spice_usbdev_widget,
|
||||
buttons=Gtk.ButtonsType.CLOSE)
|
||||
|
||||
def _take_screenshot(self):
|
||||
image = self.console.details_viewer_get_pixbuf()
|
||||
|
||||
metadata = {
|
||||
'tEXt::Hypervisor URI': self.vm.conn.get_uri(),
|
||||
'tEXt::Domain Name': self.vm.get_name(),
|
||||
'tEXt::Domain UUID': self.vm.get_uuid(),
|
||||
'tEXt::Generator App': self.config.get_appname(),
|
||||
'tEXt::Generator Version': self.config.get_appversion(),
|
||||
}
|
||||
|
||||
ret = image.save_to_bufferv(
|
||||
'png', list(metadata.keys()), list(metadata.values())
|
||||
)
|
||||
# On Fedora 19, ret is (bool, str)
|
||||
# Someday the bindings might be fixed to just return the str, try
|
||||
# and future proof it a bit
|
||||
if isinstance(ret, tuple) and len(ret) >= 2:
|
||||
ret = ret[1]
|
||||
# F24 rawhide, ret[1] is a named tuple with a 'buffer' element...
|
||||
if hasattr(ret, "buffer"):
|
||||
ret = ret.buffer
|
||||
|
||||
import datetime
|
||||
now = str(datetime.datetime.now()).split(".")[0].replace(" ", "_")
|
||||
default = "Screenshot_%s_%s.png" % (self.vm.get_name(), now)
|
||||
|
||||
path = self.err.browse_local(
|
||||
self.vm.conn, _("Save Virtual Machine Screenshot"),
|
||||
_type=("png", _("PNG files")),
|
||||
dialog_type=Gtk.FileChooserAction.SAVE,
|
||||
browse_reason=self.config.CONFIG_DIR_SCREENSHOT,
|
||||
default_name=default)
|
||||
if not path:
|
||||
logging.debug("No screenshot path given, skipping save.")
|
||||
return
|
||||
|
||||
filename = path
|
||||
if not filename.endswith(".png"):
|
||||
filename += ".png"
|
||||
open(filename, "wb").write(ret)
|
||||
|
||||
|
||||
############################
|
||||
# Details/Hardware getters #
|
||||
@ -2393,40 +1888,15 @@ class vmmDetails(vmmGObjectUI):
|
||||
buttons=Gtk.ButtonsType.OK,
|
||||
dialog_type=Gtk.MessageType.INFO)
|
||||
|
||||
#######################
|
||||
# vmwindow Public API #
|
||||
#######################
|
||||
|
||||
########################
|
||||
# Details page refresh #
|
||||
########################
|
||||
|
||||
def refresh_resources(self, ignore):
|
||||
details = self.widget("details-pages")
|
||||
page = details.get_current_page()
|
||||
|
||||
# If the dialog is visible, we want to make sure the XML is always
|
||||
# up to date
|
||||
try:
|
||||
if self.is_visible():
|
||||
self.vm.ensure_latest_xml()
|
||||
except libvirt.libvirtError as e:
|
||||
if util.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"):
|
||||
self.close()
|
||||
return
|
||||
raise
|
||||
|
||||
# Stats page needs to be refreshed every tick
|
||||
if (page == DETAILS_PAGE_DETAILS and
|
||||
self.get_hw_row()[HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS):
|
||||
def vmwindow_resources_refreshed(self):
|
||||
if self.get_hw_row()[HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS:
|
||||
self.refresh_stats_page()
|
||||
|
||||
def page_refresh(self, page):
|
||||
if page != DETAILS_PAGE_DETAILS:
|
||||
return
|
||||
|
||||
# This function should only be called when the VM xml actually
|
||||
# changes (not every time it is refreshed). This saves us from blindly
|
||||
# parsing the xml every tick
|
||||
|
||||
# Add / remove new devices
|
||||
def vmwindow_page_refresh(self):
|
||||
self.repopulate_hw_list()
|
||||
|
||||
pagetype = self.get_hw_row()[HW_LIST_COL_TYPE]
|
||||
@ -2440,6 +1910,23 @@ class vmmDetails(vmmGObjectUI):
|
||||
|
||||
self.hw_selected(pagetype=pagetype)
|
||||
|
||||
def vmwindow_activate_performance_page(self):
|
||||
index = 0
|
||||
model = self.widget("hw-list").get_model()
|
||||
for idx, row in enumerate(model):
|
||||
if row[HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS:
|
||||
index = idx
|
||||
break
|
||||
self.set_hw_selection(index)
|
||||
|
||||
def vmwindow_has_unapplied_changes(self):
|
||||
return self.has_unapplied_changes(self.get_hw_row())
|
||||
|
||||
|
||||
########################
|
||||
# Details page refresh #
|
||||
########################
|
||||
|
||||
def refresh_overview_page(self):
|
||||
# Basic details
|
||||
self.widget("overview-name").set_text(self.vm.get_name())
|
||||
|
@ -476,8 +476,8 @@ class vmmEngine(vmmGObject):
|
||||
raise RuntimeError("%s does not have VM '%s'" %
|
||||
(uri, clistr))
|
||||
|
||||
from .details import vmmDetails
|
||||
details = vmmDetails.get_instance(None, vm)
|
||||
from .vmwindow import vmmVMWindow
|
||||
details = vmmVMWindow.get_instance(None, vm)
|
||||
|
||||
if page == self.CLI_SHOW_DOMAIN_PERFORMANCE:
|
||||
details.activate_performance_page()
|
||||
|
@ -327,5 +327,5 @@ class VMActionUI(object):
|
||||
|
||||
@staticmethod
|
||||
def show(src, vm):
|
||||
from .details import vmmDetails
|
||||
vmmDetails.get_instance(src, vm).show()
|
||||
from .vmwindow import vmmVMWindow
|
||||
vmmVMWindow.get_instance(src, vm).show()
|
||||
|
573
virtManager/vmwindow.py
Normal file
573
virtManager/vmwindow.py
Normal file
@ -0,0 +1,573 @@
|
||||
# Copyright (C) 2006-2008, 2013, 2014 Red Hat, Inc.
|
||||
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
|
||||
#
|
||||
# This work is licensed under the GNU GPLv2 or later.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
import logging
|
||||
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
|
||||
import libvirt
|
||||
|
||||
from virtinst import util
|
||||
|
||||
from . import vmmenu
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .details import vmmDetails
|
||||
from .engine import vmmEngine
|
||||
from .snapshots import vmmSnapshotPage
|
||||
|
||||
|
||||
# Main tab pages
|
||||
(DETAILS_PAGE_DETAILS,
|
||||
DETAILS_PAGE_CONSOLE,
|
||||
DETAILS_PAGE_SNAPSHOTS) = range(3)
|
||||
|
||||
|
||||
class vmmVMWindow(vmmGObjectUI):
|
||||
__gsignals__ = {
|
||||
"customize-finished": (vmmGObjectUI.RUN_FIRST, None, []),
|
||||
"closed": (vmmGObjectUI.RUN_FIRST, None, []),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, parentobj, vm):
|
||||
try:
|
||||
# Maintain one dialog per VM
|
||||
key = "%s+%s" % (vm.conn.get_uri(), vm.get_uuid())
|
||||
if cls._instances is None:
|
||||
cls._instances = {}
|
||||
if key not in cls._instances:
|
||||
cls._instances[key] = vmmVMWindow(vm)
|
||||
return cls._instances[key]
|
||||
except Exception as e:
|
||||
if not parentobj:
|
||||
raise
|
||||
parentobj.err.show_err(
|
||||
_("Error launching details: %s") % str(e))
|
||||
|
||||
def __init__(self, vm, parent=None):
|
||||
vmmGObjectUI.__init__(self, "vmwindow.ui", "vmm-vmwindow")
|
||||
self.vm = vm
|
||||
|
||||
self.is_customize_dialog = False
|
||||
if parent:
|
||||
# Details window is being abused as a 'configure before install'
|
||||
# dialog, set things as appropriate
|
||||
self.is_customize_dialog = True
|
||||
self.topwin.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
||||
self.topwin.set_transient_for(parent)
|
||||
|
||||
self.widget("toolbar-box").show()
|
||||
self.widget("customize-toolbar").show()
|
||||
self.widget("details-toolbar").hide()
|
||||
self.widget("details-menubar").hide()
|
||||
pages = self.widget("details-pages")
|
||||
pages.set_current_page(DETAILS_PAGE_DETAILS)
|
||||
else:
|
||||
self.conn.connect("vm-removed", self._vm_removed)
|
||||
|
||||
self._mediacombo = None
|
||||
|
||||
self.ignoreDetails = False
|
||||
|
||||
from .console import vmmConsolePages
|
||||
self.console = vmmConsolePages(self.vm, self.builder, self.topwin)
|
||||
self.snapshots = vmmSnapshotPage(self.vm, self.builder, self.topwin)
|
||||
self.widget("snapshot-placeholder").add(self.snapshots.top_box)
|
||||
self._details = vmmDetails(self.vm, self.builder, self.topwin,
|
||||
self.is_customize_dialog)
|
||||
self.widget("details-placeholder").add(self._details.top_box)
|
||||
|
||||
# Set default window size
|
||||
w, h = self.vm.get_details_window_size()
|
||||
if w <= 0:
|
||||
w = 800
|
||||
if h <= 0:
|
||||
h = 600
|
||||
self.topwin.set_default_size(w, h)
|
||||
self._window_size = None
|
||||
|
||||
self._shutdownmenu = None
|
||||
self._vmmenu = None
|
||||
self.init_menus()
|
||||
|
||||
self.builder.connect_signals({
|
||||
"on_close_details_clicked": self.close,
|
||||
"on_details_menu_close_activate": self.close,
|
||||
"on_vmm_details_delete_event": self._window_delete_event,
|
||||
"on_vmm_details_configure_event": self.window_resized,
|
||||
"on_details_menu_quit_activate": self.exit_app,
|
||||
|
||||
"on_control_vm_details_toggled": self.details_console_changed,
|
||||
"on_control_vm_console_toggled": self.details_console_changed,
|
||||
"on_control_snapshots_toggled": self.details_console_changed,
|
||||
"on_control_run_clicked": self.control_vm_run,
|
||||
"on_control_shutdown_clicked": self.control_vm_shutdown,
|
||||
"on_control_pause_toggled": self.control_vm_pause,
|
||||
"on_control_fullscreen_toggled": self.control_fullscreen,
|
||||
|
||||
"on_details_customize_finish_clicked": self.customize_finish,
|
||||
"on_details_cancel_customize_clicked": self._customize_cancel_clicked,
|
||||
|
||||
"on_details_menu_virtual_manager_activate": self.control_vm_menu,
|
||||
"on_details_menu_screenshot_activate": self.control_vm_screenshot,
|
||||
"on_details_menu_usb_redirection": self.control_vm_usb_redirection,
|
||||
"on_details_menu_view_toolbar_activate": self.toggle_toolbar,
|
||||
"on_details_menu_view_manager_activate": self.view_manager,
|
||||
"on_details_menu_view_details_toggled": self.details_console_changed,
|
||||
"on_details_menu_view_console_toggled": self.details_console_changed,
|
||||
"on_details_menu_view_snapshots_toggled": self.details_console_changed,
|
||||
|
||||
"on_details_pages_switch_page": self.switch_page,
|
||||
|
||||
# Listeners stored in vmmConsolePages
|
||||
"on_details_menu_view_fullscreen_activate": (
|
||||
self.console.details_toggle_fullscreen),
|
||||
"on_details_menu_view_size_to_vm_activate": (
|
||||
self.console.details_size_to_vm),
|
||||
"on_details_menu_view_scale_always_toggled": (
|
||||
self.console.details_scaling_ui_changed_cb),
|
||||
"on_details_menu_view_scale_fullscreen_toggled": (
|
||||
self.console.details_scaling_ui_changed_cb),
|
||||
"on_details_menu_view_scale_never_toggled": (
|
||||
self.console.details_scaling_ui_changed_cb),
|
||||
"on_details_menu_view_resizeguest_toggled": (
|
||||
self.console.details_resizeguest_ui_changed_cb),
|
||||
|
||||
"on_console_pages_switch_page": (
|
||||
self.console.details_page_changed),
|
||||
"on_console_auth_password_activate": (
|
||||
self.console.details_auth_login),
|
||||
"on_console_auth_login_clicked": (
|
||||
self.console.details_auth_login),
|
||||
})
|
||||
|
||||
# Deliberately keep all this after signal connection
|
||||
self.vm.connect("state-changed", self.refresh_vm_state)
|
||||
self.vm.connect("resources-sampled", self.refresh_resources)
|
||||
self.vm.connect("inspection-changed",
|
||||
lambda *x: self._details.refresh_os_page())
|
||||
|
||||
self.refresh_vm_state()
|
||||
self.activate_default_page()
|
||||
|
||||
|
||||
@property
|
||||
def conn(self):
|
||||
return self.vm.conn
|
||||
|
||||
def _cleanup(self):
|
||||
self.console.cleanup()
|
||||
self.console = None
|
||||
self.snapshots.cleanup()
|
||||
self.snapshots = None
|
||||
self._shutdownmenu.destroy()
|
||||
self._shutdownmenu = None
|
||||
self._vmmenu.destroy()
|
||||
self._vmmenu = None
|
||||
|
||||
if self._window_size:
|
||||
self.vm.set_details_window_size(*self._window_size)
|
||||
|
||||
self.conn.disconnect_by_obj(self)
|
||||
self.vm = None
|
||||
|
||||
def show(self):
|
||||
logging.debug("Showing VM details: %s", self.vm)
|
||||
vis = self.is_visible()
|
||||
self.topwin.present()
|
||||
if vis:
|
||||
return
|
||||
|
||||
vmmEngine.get_instance().increment_window_counter()
|
||||
self.refresh_vm_state()
|
||||
|
||||
def customize_finish(self, src):
|
||||
ignore = src
|
||||
if self._details.vmwindow_has_unapplied_changes():
|
||||
return
|
||||
self.emit("customize-finished")
|
||||
|
||||
def _vm_removed(self, _conn, connkey):
|
||||
if self.vm.get_connkey() == connkey:
|
||||
self.cleanup()
|
||||
|
||||
def _customize_cancel(self):
|
||||
logging.debug("Asking to cancel customization")
|
||||
|
||||
result = self.err.yes_no(
|
||||
_("This will abort the installation. Are you sure?"))
|
||||
if not result:
|
||||
logging.debug("Customize cancel aborted")
|
||||
return
|
||||
|
||||
logging.debug("Canceling customization")
|
||||
return self._close()
|
||||
|
||||
def _customize_cancel_clicked(self, src):
|
||||
ignore = src
|
||||
return self._customize_cancel()
|
||||
|
||||
def _window_delete_event(self, ignore1=None, ignore2=None):
|
||||
return self.close()
|
||||
|
||||
def close(self, ignore1=None, ignore2=None):
|
||||
if self.is_visible():
|
||||
logging.debug("Closing VM details: %s", self.vm)
|
||||
return self._close()
|
||||
|
||||
def _close(self):
|
||||
fs = self.widget("details-menu-view-fullscreen")
|
||||
if fs.get_active():
|
||||
fs.set_active(False)
|
||||
|
||||
if not self.is_visible():
|
||||
return
|
||||
|
||||
self.topwin.hide()
|
||||
if self.console.details_viewer_is_visible():
|
||||
try:
|
||||
self.console.details_close_viewer()
|
||||
except Exception:
|
||||
logging.error("Failure when disconnecting from desktop server")
|
||||
|
||||
self.emit("closed")
|
||||
vmmEngine.get_instance().decrement_window_counter()
|
||||
return 1
|
||||
|
||||
|
||||
##########################
|
||||
# Initialization helpers #
|
||||
##########################
|
||||
|
||||
def init_menus(self):
|
||||
# Virtual Machine menu
|
||||
self._shutdownmenu = vmmenu.VMShutdownMenu(self, lambda: self.vm)
|
||||
self.widget("control-shutdown").set_menu(self._shutdownmenu)
|
||||
self.widget("control-shutdown").set_icon_name("system-shutdown")
|
||||
|
||||
topmenu = self.widget("details-vm-menu")
|
||||
submenu = topmenu.get_submenu()
|
||||
self._vmmenu = vmmenu.VMActionMenu(
|
||||
self, lambda: self.vm, show_open=False)
|
||||
for child in submenu.get_children():
|
||||
submenu.remove(child)
|
||||
self._vmmenu.add(child)
|
||||
topmenu.set_submenu(self._vmmenu)
|
||||
topmenu.show_all()
|
||||
|
||||
self.widget("details-pages").set_show_tabs(False)
|
||||
self.widget("details-menu-view-toolbar").set_active(
|
||||
self.config.get_details_show_toolbar())
|
||||
|
||||
|
||||
##########################
|
||||
# Window state listeners #
|
||||
##########################
|
||||
|
||||
def window_resized(self, ignore, ignore2):
|
||||
if not self.is_visible():
|
||||
return
|
||||
self._window_size = self.topwin.get_size()
|
||||
|
||||
def control_fullscreen(self, src):
|
||||
menu = self.widget("details-menu-view-fullscreen")
|
||||
if src.get_active() != menu.get_active():
|
||||
menu.set_active(src.get_active())
|
||||
|
||||
def toggle_toolbar(self, src):
|
||||
if self.is_customize_dialog:
|
||||
return
|
||||
|
||||
active = src.get_active()
|
||||
self.config.set_details_show_toolbar(active)
|
||||
|
||||
if (active and not
|
||||
self.widget("details-menu-view-fullscreen").get_active()):
|
||||
self.widget("toolbar-box").show()
|
||||
else:
|
||||
self.widget("toolbar-box").hide()
|
||||
|
||||
def details_console_changed(self, src):
|
||||
if self.ignoreDetails:
|
||||
return
|
||||
|
||||
if not src.get_active():
|
||||
return
|
||||
|
||||
is_details = (src == self.widget("control-vm-details") or
|
||||
src == self.widget("details-menu-view-details"))
|
||||
is_snapshot = (src == self.widget("control-snapshots") or
|
||||
src == self.widget("details-menu-view-snapshots"))
|
||||
|
||||
pages = self.widget("details-pages")
|
||||
if pages.get_current_page() == DETAILS_PAGE_DETAILS:
|
||||
if self._details.vmwindow_has_unapplied_changes():
|
||||
self.sync_details_console_view(True)
|
||||
return
|
||||
self._details.disable_apply()
|
||||
|
||||
if is_details:
|
||||
pages.set_current_page(DETAILS_PAGE_DETAILS)
|
||||
elif is_snapshot:
|
||||
self.snapshots.show_page()
|
||||
pages.set_current_page(DETAILS_PAGE_SNAPSHOTS)
|
||||
else:
|
||||
pages.set_current_page(DETAILS_PAGE_CONSOLE)
|
||||
|
||||
def sync_details_console_view(self, newpage):
|
||||
details = self.widget("control-vm-details")
|
||||
details_menu = self.widget("details-menu-view-details")
|
||||
console = self.widget("control-vm-console")
|
||||
console_menu = self.widget("details-menu-view-console")
|
||||
snapshot = self.widget("control-snapshots")
|
||||
snapshot_menu = self.widget("details-menu-view-snapshots")
|
||||
|
||||
is_details = newpage == DETAILS_PAGE_DETAILS
|
||||
is_snapshot = newpage == DETAILS_PAGE_SNAPSHOTS
|
||||
is_console = not is_details and not is_snapshot
|
||||
|
||||
try:
|
||||
self.ignoreDetails = True
|
||||
|
||||
details.set_active(is_details)
|
||||
details_menu.set_active(is_details)
|
||||
snapshot.set_active(is_snapshot)
|
||||
snapshot_menu.set_active(is_snapshot)
|
||||
console.set_active(is_console)
|
||||
console_menu.set_active(is_console)
|
||||
finally:
|
||||
self.ignoreDetails = False
|
||||
|
||||
def switch_page(self, notebook=None, ignore2=None, newpage=None):
|
||||
for i in range(notebook.get_n_pages()):
|
||||
w = notebook.get_nth_page(i)
|
||||
w.set_visible(i == newpage)
|
||||
|
||||
self.page_refresh(newpage)
|
||||
|
||||
self.sync_details_console_view(newpage)
|
||||
self.console.details_refresh_can_fullscreen()
|
||||
|
||||
def change_run_text(self, can_restore):
|
||||
if can_restore:
|
||||
text = _("_Restore")
|
||||
else:
|
||||
text = _("_Run")
|
||||
strip_text = text.replace("_", "")
|
||||
|
||||
self.widget("details-vm-menu").get_submenu().change_run_text(text)
|
||||
self.widget("control-run").set_label(strip_text)
|
||||
|
||||
def refresh_vm_state(self, ignore=None):
|
||||
vm = self.vm
|
||||
|
||||
self.widget("details-menu-view-toolbar").set_active(
|
||||
self.config.get_details_show_toolbar())
|
||||
self.toggle_toolbar(self.widget("details-menu-view-toolbar"))
|
||||
|
||||
run = vm.is_runable()
|
||||
stop = vm.is_stoppable()
|
||||
paused = vm.is_paused()
|
||||
|
||||
if vm.managedsave_supported:
|
||||
self.change_run_text(vm.has_managed_save())
|
||||
|
||||
self.widget("control-run").set_sensitive(run)
|
||||
self.widget("control-shutdown").set_sensitive(stop)
|
||||
self.widget("control-shutdown").get_menu().update_widget_states(vm)
|
||||
self.widget("control-pause").set_sensitive(stop)
|
||||
|
||||
if paused:
|
||||
pauseTooltip = _("Resume the virtual machine")
|
||||
else:
|
||||
pauseTooltip = _("Pause the virtual machine")
|
||||
self.widget("control-pause").set_tooltip_text(pauseTooltip)
|
||||
|
||||
self.widget("details-vm-menu").get_submenu().update_widget_states(vm)
|
||||
self.set_pause_state(paused)
|
||||
|
||||
errmsg = self.vm.snapshots_supported()
|
||||
cansnap = not bool(errmsg)
|
||||
self.widget("control-snapshots").set_sensitive(cansnap)
|
||||
self.widget("details-menu-view-snapshots").set_sensitive(cansnap)
|
||||
tooltip = _("Manage VM snapshots")
|
||||
if not cansnap:
|
||||
tooltip += "\n" + errmsg
|
||||
self.widget("control-snapshots").set_tooltip_text(tooltip)
|
||||
|
||||
details = self.widget("details-pages")
|
||||
self.page_refresh(details.get_current_page())
|
||||
|
||||
self._details.vmwindow_refresh_vm_state()
|
||||
self.console.details_update_widget_states()
|
||||
if not run:
|
||||
self.activate_default_console_page()
|
||||
|
||||
|
||||
#############################
|
||||
# External action listeners #
|
||||
#############################
|
||||
|
||||
def view_manager(self, _src):
|
||||
from .manager import vmmManager
|
||||
vmmManager.get_instance(self).show()
|
||||
|
||||
def exit_app(self, _src):
|
||||
vmmEngine.get_instance().exit_app()
|
||||
|
||||
def activate_default_console_page(self):
|
||||
pages = self.widget("details-pages")
|
||||
|
||||
# console.activate_default_console_page() will as a side effect
|
||||
# switch to DETAILS_PAGE_CONSOLE. However this code path is triggered
|
||||
# when the user runs a VM while they are focused on the details page,
|
||||
# and we don't want to switch pages out from under them.
|
||||
origpage = pages.get_current_page()
|
||||
self.console.details_activate_default_console_page()
|
||||
pages.set_current_page(origpage)
|
||||
|
||||
# activate_* are called from engine.py via CLI options
|
||||
def activate_default_page(self):
|
||||
if self.is_customize_dialog:
|
||||
return
|
||||
pages = self.widget("details-pages")
|
||||
pages.set_current_page(DETAILS_PAGE_CONSOLE)
|
||||
self.activate_default_console_page()
|
||||
|
||||
def activate_console_page(self):
|
||||
pages = self.widget("details-pages")
|
||||
pages.set_current_page(DETAILS_PAGE_CONSOLE)
|
||||
|
||||
def activate_performance_page(self):
|
||||
self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS)
|
||||
self._details.vmwindow_activate_performance_page()
|
||||
|
||||
def activate_config_page(self):
|
||||
self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS)
|
||||
|
||||
def set_pause_state(self, state):
|
||||
src = self.widget("control-pause")
|
||||
try:
|
||||
src.handler_block_by_func(self.control_vm_pause)
|
||||
src.set_active(state)
|
||||
finally:
|
||||
src.handler_unblock_by_func(self.control_vm_pause)
|
||||
|
||||
def control_vm_pause(self, src):
|
||||
do_pause = src.get_active()
|
||||
|
||||
# Set button state back to original value: just let the status
|
||||
# update function fix things for us
|
||||
self.set_pause_state(not do_pause)
|
||||
|
||||
if do_pause:
|
||||
vmmenu.VMActionUI.suspend(self, self.vm)
|
||||
else:
|
||||
vmmenu.VMActionUI.resume(self, self.vm)
|
||||
|
||||
def control_vm_menu(self, src_ignore):
|
||||
can_usb = bool(self.console.details_viewer_has_usb_redirection() and
|
||||
self.vm.has_spicevmc_type_redirdev())
|
||||
self.widget("details-menu-usb-redirection").set_sensitive(can_usb)
|
||||
|
||||
def control_vm_run(self, src_ignore):
|
||||
if self._details.vmwindow_has_unapplied_changes():
|
||||
return
|
||||
vmmenu.VMActionUI.run(self, self.vm)
|
||||
|
||||
def control_vm_shutdown(self, src_ignore):
|
||||
vmmenu.VMActionUI.shutdown(self, self.vm)
|
||||
|
||||
def control_vm_screenshot(self, src):
|
||||
ignore = src
|
||||
try:
|
||||
return self._take_screenshot()
|
||||
except Exception as e:
|
||||
self.err.show_err(_("Error taking screenshot: %s") % str(e))
|
||||
|
||||
def control_vm_usb_redirection(self, src):
|
||||
ignore = src
|
||||
spice_usbdev_dialog = self.err
|
||||
|
||||
spice_usbdev_widget = self.console.details_viewer_get_usb_widget()
|
||||
if not spice_usbdev_widget:
|
||||
self.err.show_err(_("Error initializing spice USB device widget"))
|
||||
return
|
||||
|
||||
spice_usbdev_widget.show()
|
||||
spice_usbdev_dialog.show_info(_("Select USB devices for redirection"),
|
||||
widget=spice_usbdev_widget,
|
||||
buttons=Gtk.ButtonsType.CLOSE)
|
||||
|
||||
def _take_screenshot(self):
|
||||
image = self.console.details_viewer_get_pixbuf()
|
||||
|
||||
metadata = {
|
||||
'tEXt::Hypervisor URI': self.vm.conn.get_uri(),
|
||||
'tEXt::Domain Name': self.vm.get_name(),
|
||||
'tEXt::Domain UUID': self.vm.get_uuid(),
|
||||
'tEXt::Generator App': self.config.get_appname(),
|
||||
'tEXt::Generator Version': self.config.get_appversion(),
|
||||
}
|
||||
|
||||
ret = image.save_to_bufferv(
|
||||
'png', list(metadata.keys()), list(metadata.values())
|
||||
)
|
||||
# On Fedora 19, ret is (bool, str)
|
||||
# Someday the bindings might be fixed to just return the str, try
|
||||
# and future proof it a bit
|
||||
if isinstance(ret, tuple) and len(ret) >= 2:
|
||||
ret = ret[1]
|
||||
# F24 rawhide, ret[1] is a named tuple with a 'buffer' element...
|
||||
if hasattr(ret, "buffer"):
|
||||
ret = ret.buffer
|
||||
|
||||
import datetime
|
||||
now = str(datetime.datetime.now()).split(".")[0].replace(" ", "_")
|
||||
default = "Screenshot_%s_%s.png" % (self.vm.get_name(), now)
|
||||
|
||||
path = self.err.browse_local(
|
||||
self.vm.conn, _("Save Virtual Machine Screenshot"),
|
||||
_type=("png", _("PNG files")),
|
||||
dialog_type=Gtk.FileChooserAction.SAVE,
|
||||
browse_reason=self.config.CONFIG_DIR_SCREENSHOT,
|
||||
default_name=default)
|
||||
if not path:
|
||||
logging.debug("No screenshot path given, skipping save.")
|
||||
return
|
||||
|
||||
filename = path
|
||||
if not filename.endswith(".png"):
|
||||
filename += ".png"
|
||||
open(filename, "wb").write(ret)
|
||||
|
||||
|
||||
########################
|
||||
# Details page refresh #
|
||||
########################
|
||||
|
||||
def refresh_resources(self, ignore):
|
||||
details = self.widget("details-pages")
|
||||
page = details.get_current_page()
|
||||
|
||||
# If the dialog is visible, we want to make sure the XML is always
|
||||
# up to date
|
||||
try:
|
||||
if self.is_visible():
|
||||
self.vm.ensure_latest_xml()
|
||||
except libvirt.libvirtError as e:
|
||||
if util.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"):
|
||||
self.close()
|
||||
return
|
||||
raise
|
||||
|
||||
if page == DETAILS_PAGE_DETAILS:
|
||||
self._details.vmwindow_resources_refreshed()
|
||||
|
||||
def page_refresh(self, page):
|
||||
if page == DETAILS_PAGE_DETAILS:
|
||||
self._details.vmwindow_page_refresh()
|
Loading…
x
Reference in New Issue
Block a user