snapshots: Split out vmmSnapshotNew class
Simplifies code org
This commit is contained in:
parent
318e0c0a39
commit
f2304664d6
294
ui/snapshots.ui
294
ui/snapshots.ui
|
@ -1,297 +1,14 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.20.2 -->
|
<!-- Generated with glade 3.22.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<object class="GtkImage" id="image3">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="stock">gtk-new</property>
|
|
||||||
</object>
|
|
||||||
<object class="GtkWindow" id="snapshot-new">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="title" translatable="yes">Create snapshot</property>
|
|
||||||
<property name="resizable">False</property>
|
|
||||||
<property name="type_hint">dialog</property>
|
|
||||||
<signal name="delete-event" handler="on_snapshot_new_delete_event" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box1">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkViewport" id="header">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="resize_mode">queue</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="hbox77">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="border_width">6</property>
|
|
||||||
<property name="spacing">10</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="image4">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="stock">gtk-new</property>
|
|
||||||
<property name="icon_size">6</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="hbox2">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label2">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes"><span size='large' color='white'>Create snapshot</span></property>
|
|
||||||
<property name="use_markup">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="box3">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="border_width">12</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">18</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkGrid" id="box4">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="row_spacing">6</property>
|
|
||||||
<property name="column_spacing">6</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label1">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes">_Name:</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="mnemonic_widget">snapshot-new-name</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="snapshot-new-name">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="invisible_char">●</property>
|
|
||||||
<signal name="activate" handler="on_snapshot_new_name_activate" swapped="no"/>
|
|
||||||
<signal name="changed" handler="on_snapshot_new_name_changed" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label3">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
<property name="label" translatable="yes">_Description:</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="mnemonic_widget">snapshot-new-description</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
|
||||||
<property name="width_request">300</property>
|
|
||||||
<property name="height_request">125</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkTextView" id="snapshot-new-description">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label4">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes">Status:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="hbox1">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="spacing">3</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="snapshot-new-status-icon">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="stock">gtk-cancel</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="snapshot-new-status-text">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label">Shut down</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label6">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
<property name="label" translatable="yes">Screenshot:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="snapshot-new-screenshot">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="stock">gtk-missing-image</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButtonBox" id="buttonbox2">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<property name="layout_style">end</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="snapshot-new-cancel">
|
|
||||||
<property name="label">gtk-cancel</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="use_stock">True</property>
|
|
||||||
<signal name="clicked" handler="on_snapshot_new_cancel_clicked" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="snapshot-new-ok">
|
|
||||||
<property name="label" translatable="yes">_Finish</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="image">image3</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<signal name="clicked" handler="on_snapshot_new_ok_clicked" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</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>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="titlebar">
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<object class="GtkWindow" id="snapshot-top-window">
|
<object class="GtkWindow" id="snapshot-top-window">
|
||||||
<property name="width_request">600</property>
|
<property name="width_request">600</property>
|
||||||
<property name="height_request">400</property>
|
<property name="height_request">400</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<child type="titlebar">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="snapshot-top-box">
|
<object class="GtkBox" id="snapshot-top-box">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -819,8 +536,5 @@
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="titlebar">
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkImage" id="image3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-new</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkWindow" id="snapshot-new">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Create snapshot</property>
|
||||||
|
<property name="type_hint">dialog</property>
|
||||||
|
<signal name="delete-event" handler="on_snapshot_new_delete_event" swapped="no"/>
|
||||||
|
<child type="titlebar">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport" id="header">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="resize_mode">queue</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="hbox77">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="spacing">10</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="image4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-new</property>
|
||||||
|
<property name="icon_size">6</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="hbox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes"><span size='large' color='white'>Create snapshot</span></property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="box3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="border_width">12</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">18</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkAlignment" id="snapshot-new-box-align">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid" id="snapshot-new-box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="row_spacing">6</property>
|
||||||
|
<property name="column_spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">_Name:</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="mnemonic_widget">snapshot-new-name</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="snapshot-new-name">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<signal name="activate" handler="on_snapshot_new_name_activate" swapped="no"/>
|
||||||
|
<signal name="changed" handler="on_snapshot_new_name_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="label" translatable="yes">_Description:</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="mnemonic_widget">snapshot-new-description</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||||
|
<property name="width_request">300</property>
|
||||||
|
<property name="height_request">125</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="snapshot-new-description">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Status:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="hbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="snapshot-new-status-icon">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-cancel</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="snapshot-new-status-text">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label">Shut down</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="label" translatable="yes">Screenshot:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="snapshot-new-screenshot">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="stock">gtk-missing-image</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButtonBox" id="buttonbox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<property name="layout_style">end</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="snapshot-new-cancel">
|
||||||
|
<property name="label">gtk-cancel</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="clicked" handler="on_snapshot_new_cancel_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="snapshot-new-ok">
|
||||||
|
<property name="label" translatable="yes">_Finish</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="image">image3</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="clicked" handler="on_snapshot_new_ok_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</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>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
|
@ -258,13 +258,6 @@ class vmmGObject(GObject.GObject):
|
||||||
|
|
||||||
|
|
||||||
class vmmGObjectUI(vmmGObject):
|
class vmmGObjectUI(vmmGObject):
|
||||||
@staticmethod
|
|
||||||
def bind_escape_key_close_helper(topwin, close_cb):
|
|
||||||
def close_on_escape(src_ignore, event):
|
|
||||||
if Gdk.keyval_name(event.keyval) == "Escape":
|
|
||||||
close_cb()
|
|
||||||
topwin.connect("key-press-event", close_on_escape)
|
|
||||||
|
|
||||||
def __init__(self, filename, windowname, builder=None, topwin=None):
|
def __init__(self, filename, windowname, builder=None, topwin=None):
|
||||||
vmmGObject.__init__(self)
|
vmmGObject.__init__(self)
|
||||||
self._external_topwin = bool(topwin)
|
self._external_topwin = bool(topwin)
|
||||||
|
@ -325,7 +318,10 @@ class vmmGObjectUI(vmmGObject):
|
||||||
return bool(self.topwin and self.topwin.get_visible())
|
return bool(self.topwin and self.topwin.get_visible())
|
||||||
|
|
||||||
def bind_escape_key_close(self):
|
def bind_escape_key_close(self):
|
||||||
self.bind_escape_key_close_helper(self.topwin, self.close)
|
def close_on_escape(src_ignore, event):
|
||||||
|
if Gdk.keyval_name(event.keyval) == "Escape":
|
||||||
|
self.close()
|
||||||
|
self.topwin.connect("key-press-event", close_on_escape)
|
||||||
|
|
||||||
def _set_cursor(self, cursor_type):
|
def _set_cursor(self, cursor_type):
|
||||||
gdk_window = self.topwin.get_window()
|
gdk_window = self.topwin.get_window()
|
||||||
|
|
|
@ -29,6 +29,30 @@ mimemap = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _make_screenshot_pixbuf(mime, sdata):
|
||||||
|
loader = GdkPixbuf.PixbufLoader.new_with_mime_type(mime)
|
||||||
|
loader.write(sdata)
|
||||||
|
pixbuf = loader.get_pixbuf()
|
||||||
|
loader.close()
|
||||||
|
|
||||||
|
maxsize = 450
|
||||||
|
def _scale(big, small, maxsize):
|
||||||
|
if big <= maxsize:
|
||||||
|
return big, small
|
||||||
|
factor = float(maxsize) / float(big)
|
||||||
|
return maxsize, int(factor * float(small))
|
||||||
|
|
||||||
|
width = pixbuf.get_width()
|
||||||
|
height = pixbuf.get_height()
|
||||||
|
if width > height:
|
||||||
|
width, height = _scale(width, height, maxsize)
|
||||||
|
else:
|
||||||
|
height, width = _scale(height, width, maxsize)
|
||||||
|
|
||||||
|
return pixbuf.scale_simple(width, height,
|
||||||
|
GdkPixbuf.InterpType.BILINEAR)
|
||||||
|
|
||||||
|
|
||||||
def _mime_to_ext(val, reverse=False):
|
def _mime_to_ext(val, reverse=False):
|
||||||
for m, e in mimemap.items():
|
for m, e in mimemap.items():
|
||||||
if val == m and not reverse:
|
if val == m and not reverse:
|
||||||
|
@ -40,6 +64,226 @@ def _mime_to_ext(val, reverse=False):
|
||||||
reverse and "mime" or "extension")
|
reverse and "mime" or "extension")
|
||||||
|
|
||||||
|
|
||||||
|
class vmmSnapshotNew(vmmGObjectUI):
|
||||||
|
__gsignals__ = {
|
||||||
|
"snapshot-created": (vmmGObjectUI.RUN_FIRST, None, [str]),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, vm):
|
||||||
|
vmmGObjectUI.__init__(self, "snapshotsnew.ui", "snapshot-new")
|
||||||
|
self.vm = vm
|
||||||
|
|
||||||
|
self._init_ui()
|
||||||
|
|
||||||
|
self.builder.connect_signals({
|
||||||
|
"on_snapshot_new_delete_event": self.close,
|
||||||
|
"on_snapshot_new_cancel_clicked": self.close,
|
||||||
|
"on_snapshot_new_name_changed": self._name_changed_cb,
|
||||||
|
"on_snapshot_new_name_activate": self._ok_clicked_cb,
|
||||||
|
"on_snapshot_new_ok_clicked": self._ok_clicked_cb,
|
||||||
|
})
|
||||||
|
self.bind_escape_key_close()
|
||||||
|
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Standard UI methods #
|
||||||
|
#######################
|
||||||
|
|
||||||
|
def show(self, parent):
|
||||||
|
logging.debug("Showing new snapshot wizard")
|
||||||
|
self._reset_state()
|
||||||
|
self.topwin.set_transient_for(parent)
|
||||||
|
self.topwin.present()
|
||||||
|
|
||||||
|
def close(self, ignore1=None, ignore2=None):
|
||||||
|
logging.debug("Closing new snapshot wizard")
|
||||||
|
self.topwin.hide()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def _cleanup(self):
|
||||||
|
self.vm = None
|
||||||
|
|
||||||
|
|
||||||
|
###########
|
||||||
|
# UI init #
|
||||||
|
###########
|
||||||
|
|
||||||
|
def _init_ui(self):
|
||||||
|
blue = Gdk.color_parse("#0072A8")
|
||||||
|
self.widget("header").modify_bg(Gtk.StateType.NORMAL, blue)
|
||||||
|
|
||||||
|
buf = Gtk.TextBuffer()
|
||||||
|
self.widget("snapshot-new-description").set_buffer(buf)
|
||||||
|
|
||||||
|
def _reset_state(self):
|
||||||
|
collidelist = [s.get_xmlobj().name for s in self.vm.list_snapshots()]
|
||||||
|
default_name = DomainSnapshot.find_free_name(
|
||||||
|
self.vm.get_backend(), collidelist)
|
||||||
|
|
||||||
|
self.widget("snapshot-new-name").set_text(default_name)
|
||||||
|
self.widget("snapshot-new-name").emit("changed")
|
||||||
|
self.widget("snapshot-new-description").get_buffer().set_text("")
|
||||||
|
self.widget("snapshot-new-ok").grab_focus()
|
||||||
|
self.widget("snapshot-new-status-text").set_text(self.vm.run_status())
|
||||||
|
self.widget("snapshot-new-status-icon").set_from_icon_name(
|
||||||
|
self.vm.run_status_icon_name(), Gtk.IconSize.BUTTON)
|
||||||
|
|
||||||
|
sn = self._get_screenshot()
|
||||||
|
uiutil.set_grid_row_visible(
|
||||||
|
self.widget("snapshot-new-screenshot"), bool(sn))
|
||||||
|
if sn:
|
||||||
|
self.widget("snapshot-new-screenshot").set_from_pixbuf(sn)
|
||||||
|
|
||||||
|
self.widget("snapshot-new-name").grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Create handling #
|
||||||
|
###################
|
||||||
|
|
||||||
|
def _take_screenshot(self):
|
||||||
|
stream = None
|
||||||
|
try:
|
||||||
|
stream = self.vm.conn.get_backend().newStream(0)
|
||||||
|
screen = 0
|
||||||
|
flags = 0
|
||||||
|
mime = self.vm.get_backend().screenshot(stream, screen, flags)
|
||||||
|
|
||||||
|
ret = io.BytesIO()
|
||||||
|
def _write_cb(_stream, data, userdata):
|
||||||
|
ignore = stream
|
||||||
|
ignore = userdata
|
||||||
|
ret.write(data)
|
||||||
|
|
||||||
|
stream.recvAll(_write_cb, None)
|
||||||
|
return mime, ret.getvalue()
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if stream:
|
||||||
|
stream.finish()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_screenshot(self):
|
||||||
|
if not self.vm.is_active():
|
||||||
|
logging.debug("Skipping screenshot since VM is not active")
|
||||||
|
return
|
||||||
|
if not self.vm.xmlobj.devices.graphics:
|
||||||
|
logging.debug("Skipping screenshot since VM has no graphics")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Perform two screenshots, because qemu + qxl has a bug where
|
||||||
|
# screenshot generally only shows the data from the previous
|
||||||
|
# screenshot request:
|
||||||
|
# https://bugs.launchpad.net/qemu/+bug/1314293
|
||||||
|
self._take_screenshot()
|
||||||
|
mime, sdata = self._take_screenshot()
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Error taking screenshot")
|
||||||
|
return
|
||||||
|
|
||||||
|
ext = _mime_to_ext(mime)
|
||||||
|
if not ext:
|
||||||
|
return
|
||||||
|
|
||||||
|
newpix = _make_screenshot_pixbuf(mime, sdata)
|
||||||
|
setattr(newpix, "vmm_mimetype", mime)
|
||||||
|
setattr(newpix, "vmm_sndata", sdata)
|
||||||
|
return newpix
|
||||||
|
|
||||||
|
def _new_finish_cb(self, error, details, newname):
|
||||||
|
self.reset_finish_cursor()
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
error = _("Error creating snapshot: %s") % error
|
||||||
|
self.err.show_err(error, details=details)
|
||||||
|
return
|
||||||
|
self.emit("snapshot-created", newname)
|
||||||
|
|
||||||
|
def _validate_new_snapshot(self):
|
||||||
|
name = self.widget("snapshot-new-name").get_text()
|
||||||
|
desc = self.widget("snapshot-new-description"
|
||||||
|
).get_buffer().get_property("text")
|
||||||
|
|
||||||
|
try:
|
||||||
|
newsnap = DomainSnapshot(self.vm.conn.get_backend())
|
||||||
|
newsnap.name = name
|
||||||
|
newsnap.description = desc or None
|
||||||
|
newsnap.validate()
|
||||||
|
newsnap.get_xml()
|
||||||
|
return newsnap
|
||||||
|
except Exception as e:
|
||||||
|
return self.err.val_err(_("Error validating snapshot: %s") % e)
|
||||||
|
|
||||||
|
def _get_screenshot_data_for_save(self):
|
||||||
|
snwidget = self.widget("snapshot-new-screenshot")
|
||||||
|
if not snwidget.is_visible():
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
sn = snwidget.get_pixbuf()
|
||||||
|
if not sn:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
mime = getattr(sn, "vmm_mimetype", None)
|
||||||
|
sndata = getattr(sn, "vmm_sndata", None)
|
||||||
|
return mime, sndata
|
||||||
|
|
||||||
|
def _do_create_snapshot(self, asyncjob, xml, name, mime, sndata):
|
||||||
|
ignore = asyncjob
|
||||||
|
|
||||||
|
self.vm.create_snapshot(xml)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cachedir = self.vm.get_cache_dir()
|
||||||
|
basesn = os.path.join(cachedir, "snap-screenshot-%s" % name)
|
||||||
|
|
||||||
|
# Remove any pre-existing screenshots so we don't show stale data
|
||||||
|
for ext in list(mimemap.values()):
|
||||||
|
p = basesn + "." + ext
|
||||||
|
if os.path.exists(basesn + "." + ext):
|
||||||
|
os.unlink(p)
|
||||||
|
|
||||||
|
if not mime or not sndata:
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = basesn + "." + _mime_to_ext(mime)
|
||||||
|
logging.debug("Writing screenshot to %s", filename)
|
||||||
|
open(filename, "wb").write(sndata)
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Error saving screenshot")
|
||||||
|
|
||||||
|
def _create_new_snapshot(self):
|
||||||
|
snap = self._validate_new_snapshot()
|
||||||
|
if not snap:
|
||||||
|
return
|
||||||
|
|
||||||
|
xml = snap.get_xml()
|
||||||
|
name = snap.name
|
||||||
|
mime, sndata = self._get_screenshot_data_for_save()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
self.set_finish_cursor()
|
||||||
|
progWin = vmmAsyncJob(
|
||||||
|
self._do_create_snapshot, [xml, name, mime, sndata],
|
||||||
|
self._new_finish_cb, [name],
|
||||||
|
_("Creating snapshot"),
|
||||||
|
_("Creating virtual machine snapshot"),
|
||||||
|
self.topwin)
|
||||||
|
progWin.run()
|
||||||
|
|
||||||
|
|
||||||
|
################
|
||||||
|
# UI listeners #
|
||||||
|
################
|
||||||
|
|
||||||
|
def _name_changed_cb(self, src):
|
||||||
|
self.widget("snapshot-new-ok").set_sensitive(bool(src.get_text()))
|
||||||
|
|
||||||
|
def _ok_clicked_cb(self, src):
|
||||||
|
return self._create_new_snapshot()
|
||||||
|
|
||||||
|
|
||||||
class vmmSnapshotPage(vmmGObjectUI):
|
class vmmSnapshotPage(vmmGObjectUI):
|
||||||
def __init__(self, vm, builder, topwin):
|
def __init__(self, vm, builder, topwin):
|
||||||
vmmGObjectUI.__init__(self, "snapshots.ui",
|
vmmGObjectUI.__init__(self, "snapshots.ui",
|
||||||
|
@ -49,15 +293,11 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
|
|
||||||
self._initial_populate = False
|
self._initial_populate = False
|
||||||
self._unapplied_changes = False
|
self._unapplied_changes = False
|
||||||
|
self._snapshot_new = None
|
||||||
|
|
||||||
self._snapmenu = None
|
self._snapmenu = None
|
||||||
self._init_ui()
|
self._init_ui()
|
||||||
|
|
||||||
self._snapshot_new = self.widget("snapshot-new")
|
|
||||||
self._snapshot_new.set_transient_for(self.topwin)
|
|
||||||
self.bind_escape_key_close_helper(self._snapshot_new,
|
|
||||||
self._snapshot_new_close)
|
|
||||||
|
|
||||||
self.builder.connect_signals({
|
self.builder.connect_signals({
|
||||||
"on_snapshot_add_clicked": self._on_add_clicked,
|
"on_snapshot_add_clicked": self._on_add_clicked,
|
||||||
"on_snapshot_delete_clicked": self._on_delete_clicked,
|
"on_snapshot_delete_clicked": self._on_delete_clicked,
|
||||||
|
@ -67,13 +307,6 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
"on_snapshot_list_button_press_event": self._popup_snapshot_menu,
|
"on_snapshot_list_button_press_event": self._popup_snapshot_menu,
|
||||||
"on_snapshot_refresh_clicked": self._on_refresh_clicked,
|
"on_snapshot_refresh_clicked": self._on_refresh_clicked,
|
||||||
"on_snapshot_list_row_activated": self._on_start_clicked,
|
"on_snapshot_list_row_activated": self._on_start_clicked,
|
||||||
|
|
||||||
# 'Create' dialog
|
|
||||||
"on_snapshot_new_delete_event": self._snapshot_new_close,
|
|
||||||
"on_snapshot_new_ok_clicked": self._on_new_ok_clicked,
|
|
||||||
"on_snapshot_new_cancel_clicked": self._snapshot_new_close,
|
|
||||||
"on_snapshot_new_name_changed": self._snapshot_new_name_changed,
|
|
||||||
"on_snapshot_new_name_activate": self._on_new_ok_clicked,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
self.top_box = self.widget("snapshot-top-box")
|
self.top_box = self.widget("snapshot-top-box")
|
||||||
|
@ -83,31 +316,27 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
||||||
selection.set_select_function(self._confirm_changes, None)
|
selection.set_select_function(self._confirm_changes, None)
|
||||||
|
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# Init stuff #
|
# Init stuff #
|
||||||
##############
|
##############
|
||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
self.vm = None
|
self.vm = None
|
||||||
|
|
||||||
self._snapshot_new.destroy()
|
|
||||||
self._snapshot_new = None
|
|
||||||
self._snapmenu = None
|
self._snapmenu = None
|
||||||
|
|
||||||
|
if self._snapshot_new:
|
||||||
|
self._snapshot_new.cleanup()
|
||||||
|
self._snapshot_new = None
|
||||||
|
|
||||||
def _init_ui(self):
|
def _init_ui(self):
|
||||||
# pylint: disable=redefined-variable-type
|
# pylint: disable=redefined-variable-type
|
||||||
blue = Gdk.color_parse("#0072A8")
|
|
||||||
self.widget("header").modify_bg(Gtk.StateType.NORMAL, blue)
|
|
||||||
|
|
||||||
self.widget("snapshot-notebook").set_show_tabs(False)
|
self.widget("snapshot-notebook").set_show_tabs(False)
|
||||||
|
|
||||||
buf = Gtk.TextBuffer()
|
buf = Gtk.TextBuffer()
|
||||||
buf.connect("changed", self._description_changed)
|
buf.connect("changed", self._description_changed)
|
||||||
self.widget("snapshot-description").set_buffer(buf)
|
self.widget("snapshot-description").set_buffer(buf)
|
||||||
|
|
||||||
buf = Gtk.TextBuffer()
|
|
||||||
self.widget("snapshot-new-description").set_buffer(buf)
|
|
||||||
|
|
||||||
# [name, row label, tooltip, icon name, sortname, current]
|
# [name, row label, tooltip, icon name, sortname, current]
|
||||||
model = Gtk.ListStore(str, str, str, str, str, bool)
|
model = Gtk.ListStore(str, str, str, str, str, bool)
|
||||||
model.set_sort_column_id(4, Gtk.SortType.ASCENDING)
|
model.set_sort_column_id(4, Gtk.SortType.ASCENDING)
|
||||||
|
@ -254,29 +483,6 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
|
|
||||||
self._initial_populate = True
|
self._initial_populate = True
|
||||||
|
|
||||||
def _make_screenshot_pixbuf(self, mime, sdata):
|
|
||||||
loader = GdkPixbuf.PixbufLoader.new_with_mime_type(mime)
|
|
||||||
loader.write(sdata)
|
|
||||||
pixbuf = loader.get_pixbuf()
|
|
||||||
loader.close()
|
|
||||||
|
|
||||||
maxsize = 450
|
|
||||||
def _scale(big, small, maxsize):
|
|
||||||
if big <= maxsize:
|
|
||||||
return big, small
|
|
||||||
factor = float(maxsize) / float(big)
|
|
||||||
return maxsize, int(factor * float(small))
|
|
||||||
|
|
||||||
width = pixbuf.get_width()
|
|
||||||
height = pixbuf.get_height()
|
|
||||||
if width > height:
|
|
||||||
width, height = _scale(width, height, maxsize)
|
|
||||||
else:
|
|
||||||
height, width = _scale(height, width, maxsize)
|
|
||||||
|
|
||||||
return pixbuf.scale_simple(width, height,
|
|
||||||
GdkPixbuf.InterpType.BILINEAR)
|
|
||||||
|
|
||||||
def _read_screenshot_file(self, name):
|
def _read_screenshot_file(self, name):
|
||||||
if not name:
|
if not name:
|
||||||
return
|
return
|
||||||
|
@ -291,7 +497,7 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
mime = _mime_to_ext(os.path.splitext(filename)[1][1:], reverse=True)
|
mime = _mime_to_ext(os.path.splitext(filename)[1][1:], reverse=True)
|
||||||
if not mime:
|
if not mime:
|
||||||
return
|
return
|
||||||
return self._make_screenshot_pixbuf(mime, open(filename, "rb").read())
|
return _make_screenshot_pixbuf(mime, open(filename, "rb").read())
|
||||||
|
|
||||||
def _set_snapshot_state(self, snap=None):
|
def _set_snapshot_state(self, snap=None):
|
||||||
self.widget("snapshot-notebook").set_current_page(0)
|
self.widget("snapshot-notebook").set_current_page(0)
|
||||||
|
@ -363,164 +569,6 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##################
|
|
||||||
# 'New' handling #
|
|
||||||
##################
|
|
||||||
|
|
||||||
def _take_screenshot(self):
|
|
||||||
stream = None
|
|
||||||
try:
|
|
||||||
stream = self.vm.conn.get_backend().newStream(0)
|
|
||||||
screen = 0
|
|
||||||
flags = 0
|
|
||||||
mime = self.vm.get_backend().screenshot(stream, screen, flags)
|
|
||||||
|
|
||||||
ret = io.BytesIO()
|
|
||||||
def _write_cb(_stream, data, userdata):
|
|
||||||
ignore = stream
|
|
||||||
ignore = userdata
|
|
||||||
ret.write(data)
|
|
||||||
|
|
||||||
stream.recvAll(_write_cb, None)
|
|
||||||
return mime, ret.getvalue()
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
if stream:
|
|
||||||
stream.finish()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _get_screenshot(self):
|
|
||||||
if not self.vm.is_active():
|
|
||||||
logging.debug("Skipping screenshot since VM is not active")
|
|
||||||
return
|
|
||||||
if not self.vm.xmlobj.devices.graphics:
|
|
||||||
logging.debug("Skipping screenshot since VM has no graphics")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Perform two screenshots, because qemu + qxl has a bug where
|
|
||||||
# screenshot generally only shows the data from the previous
|
|
||||||
# screenshot request:
|
|
||||||
# https://bugs.launchpad.net/qemu/+bug/1314293
|
|
||||||
self._take_screenshot()
|
|
||||||
mime, sdata = self._take_screenshot()
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Error taking screenshot")
|
|
||||||
return
|
|
||||||
|
|
||||||
ext = _mime_to_ext(mime)
|
|
||||||
if not ext:
|
|
||||||
return
|
|
||||||
|
|
||||||
newpix = self._make_screenshot_pixbuf(mime, sdata)
|
|
||||||
setattr(newpix, "vmm_mimetype", mime)
|
|
||||||
setattr(newpix, "vmm_sndata", sdata)
|
|
||||||
return newpix
|
|
||||||
|
|
||||||
def _reset_new_state(self):
|
|
||||||
collidelist = [s.get_xmlobj().name for s in self.vm.list_snapshots()]
|
|
||||||
default_name = DomainSnapshot.find_free_name(
|
|
||||||
self.vm.get_backend(), collidelist)
|
|
||||||
|
|
||||||
self.widget("snapshot-new-name").set_text(default_name)
|
|
||||||
self.widget("snapshot-new-name").emit("changed")
|
|
||||||
self.widget("snapshot-new-description").get_buffer().set_text("")
|
|
||||||
self.widget("snapshot-new-ok").grab_focus()
|
|
||||||
self.widget("snapshot-new-status-text").set_text(self.vm.run_status())
|
|
||||||
self.widget("snapshot-new-status-icon").set_from_icon_name(
|
|
||||||
self.vm.run_status_icon_name(), Gtk.IconSize.BUTTON)
|
|
||||||
|
|
||||||
sn = self._get_screenshot()
|
|
||||||
uiutil.set_grid_row_visible(
|
|
||||||
self.widget("snapshot-new-screenshot"), bool(sn))
|
|
||||||
if sn:
|
|
||||||
self.widget("snapshot-new-screenshot").set_from_pixbuf(sn)
|
|
||||||
|
|
||||||
|
|
||||||
def _snapshot_new_name_changed(self, src):
|
|
||||||
self.widget("snapshot-new-ok").set_sensitive(bool(src.get_text()))
|
|
||||||
|
|
||||||
def _new_finish_cb(self, error, details, newname):
|
|
||||||
self.reset_finish_cursor()
|
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
error = _("Error creating snapshot: %s") % error
|
|
||||||
self.err.show_err(error, details=details)
|
|
||||||
return
|
|
||||||
self._refresh_snapshots(newname)
|
|
||||||
|
|
||||||
def _validate_new_snapshot(self):
|
|
||||||
name = self.widget("snapshot-new-name").get_text()
|
|
||||||
desc = self.widget("snapshot-new-description"
|
|
||||||
).get_buffer().get_property("text")
|
|
||||||
|
|
||||||
try:
|
|
||||||
newsnap = DomainSnapshot(self.vm.conn.get_backend())
|
|
||||||
newsnap.name = name
|
|
||||||
newsnap.description = desc or None
|
|
||||||
newsnap.validate()
|
|
||||||
newsnap.get_xml()
|
|
||||||
return newsnap
|
|
||||||
except Exception as e:
|
|
||||||
return self.err.val_err(_("Error validating snapshot: %s") % e)
|
|
||||||
|
|
||||||
def _get_screenshot_data_for_save(self):
|
|
||||||
snwidget = self.widget("snapshot-new-screenshot")
|
|
||||||
if not snwidget.is_visible():
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
sn = snwidget.get_pixbuf()
|
|
||||||
if not sn:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
mime = getattr(sn, "vmm_mimetype", None)
|
|
||||||
sndata = getattr(sn, "vmm_sndata", None)
|
|
||||||
return mime, sndata
|
|
||||||
|
|
||||||
def _do_create_snapshot(self, asyncjob, xml, name, mime, sndata):
|
|
||||||
ignore = asyncjob
|
|
||||||
|
|
||||||
self.vm.create_snapshot(xml)
|
|
||||||
|
|
||||||
try:
|
|
||||||
cachedir = self.vm.get_cache_dir()
|
|
||||||
basesn = os.path.join(cachedir, "snap-screenshot-%s" % name)
|
|
||||||
|
|
||||||
# Remove any pre-existing screenshots so we don't show stale data
|
|
||||||
for ext in list(mimemap.values()):
|
|
||||||
p = basesn + "." + ext
|
|
||||||
if os.path.exists(basesn + "." + ext):
|
|
||||||
os.unlink(p)
|
|
||||||
|
|
||||||
if not mime or not sndata:
|
|
||||||
return
|
|
||||||
|
|
||||||
filename = basesn + "." + _mime_to_ext(mime)
|
|
||||||
logging.debug("Writing screenshot to %s", filename)
|
|
||||||
open(filename, "wb").write(sndata)
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Error saving screenshot")
|
|
||||||
|
|
||||||
def _create_new_snapshot(self):
|
|
||||||
snap = self._validate_new_snapshot()
|
|
||||||
if not snap:
|
|
||||||
return
|
|
||||||
|
|
||||||
xml = snap.get_xml()
|
|
||||||
name = snap.name
|
|
||||||
mime, sndata = self._get_screenshot_data_for_save()
|
|
||||||
self._snapshot_new_close()
|
|
||||||
|
|
||||||
self.set_finish_cursor()
|
|
||||||
progWin = vmmAsyncJob(
|
|
||||||
self._do_create_snapshot, [xml, name, mime, sndata],
|
|
||||||
self._new_finish_cb, [name],
|
|
||||||
_("Creating snapshot"),
|
|
||||||
_("Creating virtual machine snapshot"),
|
|
||||||
self.topwin)
|
|
||||||
progWin.run()
|
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(self):
|
||||||
snaps = self._get_selected_snapshots()
|
snaps = self._get_selected_snapshots()
|
||||||
if not snaps or len(snaps) > 1:
|
if not snaps or len(snaps) > 1:
|
||||||
|
@ -554,10 +602,9 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
return
|
return
|
||||||
self._snapmenu.popup_at_pointer(event)
|
self._snapmenu.popup_at_pointer(event)
|
||||||
|
|
||||||
def _snapshot_new_close(self, *args, **kwargs):
|
def close(self, ignore1=None, ignore2=None):
|
||||||
ignore = args
|
if self._snapshot_new:
|
||||||
ignore = kwargs
|
self._snapshot_new.close()
|
||||||
self._snapshot_new.hide()
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def _description_changed(self, ignore):
|
def _description_changed(self, ignore):
|
||||||
|
@ -574,15 +621,15 @@ class vmmSnapshotPage(vmmGObjectUI):
|
||||||
self._apply()
|
self._apply()
|
||||||
self._refresh_snapshots()
|
self._refresh_snapshots()
|
||||||
|
|
||||||
def _on_new_ok_clicked(self, ignore):
|
def _snapshot_created_cb(self, src, newname):
|
||||||
return self._create_new_snapshot()
|
self._refresh_snapshots(newname)
|
||||||
|
|
||||||
def _on_add_clicked(self, ignore):
|
def _on_add_clicked(self, ignore):
|
||||||
if self._snapshot_new.is_visible():
|
if not self._snapshot_new:
|
||||||
return
|
self._snapshot_new = vmmSnapshotNew(self.vm)
|
||||||
self._reset_new_state()
|
self._snapshot_new.connect("snapshot-created",
|
||||||
self._snapshot_new.show()
|
self._snapshot_created_cb)
|
||||||
self.widget("snapshot-new-name").grab_focus()
|
self._snapshot_new.show(self.topwin)
|
||||||
|
|
||||||
def _on_refresh_clicked(self, ignore):
|
def _on_refresh_clicked(self, ignore):
|
||||||
self._refresh_snapshots()
|
self._refresh_snapshots()
|
||||||
|
|
Loading…
Reference in New Issue