[gnome-disk-utility] Add a settings dialog for power management and acoustic levels



commit 1622871582f59bd89b15a6c3dc11db3dfd4c3638
Author: David Zeuthen <zeuthen gmail com>
Date:   Sat Jun 23 10:40:20 2012 -0400

    Add a settings dialog for power management and acoustic levels
    
    This feature is convenient if for always-on systems (for example
    serving media files to a television or set-top-box) that are idle most
    of the time. Reduces power usage and noise levels - save the planet!
    
    We also show a ZZZ icon next to a drive if it is currently in standby
    mode. We do this by polling all drives every few seconds - this is not
    a problem as people don't generally have the Disks application running.
    
     http://people.freedesktop.org/~david/gnome-disks-settings-item-in-drive-menu.png
     http://people.freedesktop.org/~david/gnome-disks-settings-power-management.png
     http://people.freedesktop.org/~david/gnome-disks-settings-acoustic.png
     http://people.freedesktop.org/~david/gnome-disks-zzz-icon-for-sleeping-disks.png
    
    Signed-off-by: David Zeuthen <zeuthen gmail com>

 data/icons/scalable/Makefile.am                    |    3 +-
 .../gnome-disks-state-standby-symbolic.svg         |  158 +++++
 data/ui/Makefile.am                                |    1 +
 data/ui/disk-settings-dialog.ui                    |  528 ++++++++++++++++
 data/ui/disks.ui                                   |   39 ++-
 src/disks/Makefile.am                              |    1 +
 src/disks/gduatasmartdialog.c                      |   48 +--
 src/disks/gdudevicetreemodel.c                     |   89 +++-
 src/disks/gdudevicetreemodel.h                     |    3 +-
 src/disks/gdudisksettingsdialog.c                  |  657 ++++++++++++++++++++
 src/disks/gdudisksettingsdialog.h                  |   38 ++
 src/disks/gduutils.c                               |  155 +++++
 src/disks/gduutils.h                               |    2 +
 src/disks/gduwindow.c                              |  114 +++-
 14 files changed, 1774 insertions(+), 62 deletions(-)
---
diff --git a/data/icons/scalable/Makefile.am b/data/icons/scalable/Makefile.am
index 3bbcecd..1c15678 100644
--- a/data/icons/scalable/Makefile.am
+++ b/data/icons/scalable/Makefile.am
@@ -1,7 +1,8 @@
 NULL =
 
 icondir = $(datadir)/icons/hicolor/scalable/apps
-icon_DATA =				\
+icon_DATA =									\
+	gnome-disks-state-standby-symbolic.svg	\
 	$(NULL)
 
 EXTRA_DIST = \
diff --git a/data/icons/scalable/gnome-disks-state-standby-symbolic.svg b/data/icons/scalable/gnome-disks-state-standby-symbolic.svg
new file mode 100644
index 0000000..ab7e595
--- /dev/null
+++ b/data/icons/scalable/gnome-disks-state-standby-symbolic.svg
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   id="svg7384"
+   sodipodi:docname="gnome-disks-state-standby-symbolic.svg"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   height="16"
+   width="16">
+  <metadata
+     id="metadata90">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     inkscape:cy="9.416826"
+     pagecolor="#555753"
+     borderopacity="1"
+     showborder="false"
+     inkscape:bbox-paths="false"
+     guidetolerance="10"
+     inkscape:object-paths="true"
+     inkscape:window-width="1296"
+     showguides="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-bbox="true"
+     inkscape:pageshadow="2"
+     inkscape:guide-bbox="true"
+     inkscape:snap-nodes="false"
+     bordercolor="#666666"
+     objecttolerance="10"
+     id="namedview88"
+     showgrid="false"
+     inkscape:window-maximized="0"
+     inkscape:window-x="265"
+     inkscape:snap-global="true"
+     inkscape:window-y="27"
+     gridtolerance="10"
+     inkscape:window-height="840"
+     inkscape:snap-to-guides="true"
+     inkscape:current-layer="layer9"
+     inkscape:snap-bbox-midpoints="false"
+     inkscape:zoom="8"
+     inkscape:cx="9.313336"
+     inkscape:snap-grids="true"
+     inkscape:pageopacity="1">
+    <inkscape:grid
+       spacingx="1px"
+       spacingy="1px"
+       id="grid4866"
+       empspacing="2"
+       enabled="true"
+       type="xygrid"
+       snapvisiblegridlinesonly="true"
+       visible="true" />
+  </sodipodi:namedview>
+  <title
+     id="title9167">Gnome Symbolic Icon Theme</title>
+  <defs
+     id="defs7386" />
+  <g
+     inkscape:label="status"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer9"
+     style="display:inline">
+    <g
+       inkscape:label="lock"
+       transform="translate(161.0002,-39)"
+       id="g4053"
+       style="fill:#bebebe;fill-opacity:1" />
+    <text
+       xml:space="preserve"
+       style="font-size:7.99732065px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#bebebe;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+       x="190.88081"
+       y="252"
+       id="text3032-94"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3034-5"
+         x="190.88081"
+         y="252">Z</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:7.99732065px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#bebebe;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+       x="183.13588"
+       y="245.66504"
+       id="text3032-94-6"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3034-5-8"
+         x="183.13588"
+         y="245.66504">Z</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:7.99732065px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#bebebe;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+       x="187.13588"
+       y="248.66504"
+       id="text3032-94-6-6"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3034-5-8-1"
+         x="187.13588"
+         y="248.66504">Z</tspan></text>
+  </g>
+  <g
+     inkscape:label="devices"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer10" />
+  <g
+     inkscape:label="apps"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer11" />
+  <g
+     inkscape:label="actions"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer12" />
+  <g
+     inkscape:label="places"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer13" />
+  <g
+     inkscape:label="mimetypes"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer14" />
+  <g
+     inkscape:label="emblems"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="layer15"
+     style="display:inline" />
+  <g
+     inkscape:label="categories"
+     transform="translate(-181.0002,-237)"
+     inkscape:groupmode="layer"
+     id="g4953"
+     style="display:inline" />
+</svg>
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
index 8c5b800..9e7d7c4 100644
--- a/data/ui/Makefile.am
+++ b/data/ui/Makefile.am
@@ -23,6 +23,7 @@ ui_DATA = 				\
 	about-dialog.ui			\
 	app-menu.ui			\
 	benchmark-dialog.ui		\
+	disk-settings-dialog.ui		\
 	$(NULL)
 
 EXTRA_DIST = 				\
diff --git a/data/ui/disk-settings-dialog.ui b/data/ui/disk-settings-dialog.ui
new file mode 100644
index 0000000..391af4a
--- /dev/null
+++ b/data/ui/disk-settings-dialog.ui
@@ -0,0 +1,528 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkAdjustment" id="aam-adjustment">
+    <property name="lower">128</property>
+    <property name="upper">254</property>
+    <property name="value">128</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="apm-adjustment">
+    <property name="lower">1</property>
+    <property name="upper">254</property>
+    <property name="value">127</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="dialog1">
+    <property name="width_request">600</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="default_width">600</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel-button">
+                <property name="label">gtk-cancel</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="ok-button">
+                <property name="label">gtk-ok</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </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="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkNotebook" id="settings-notebook">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <object class="GtkBox" id="power-management-page">
+                <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">6</property>
+                <child>
+                  <object class="GtkBox" id="box3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="label3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Apply Power Management _Settings When Drive Is Connected</property>
+                        <property name="use_underline">True</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                          <attribute name="scale" value="1"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="pm-apply-settings-switch">
+                        <property name="use_action_appearance">False</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="has_tooltip">True</property>
+                        <property name="use_action_appearance">False</property>
+                      </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">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox" id="pm-settings-box">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_left">24</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkBox" id="standby-box">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label5">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">&lt;b&gt;Standby _Timeout&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">standby-scale</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="box6">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_left">24</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label6">
+                                <property name="width_request">500</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">This timeout value is used by the drive to determine how long to wait (with no disk activity) before turning off the spindle motor to save power. Under such circumstances, the drive may take as long as 30 seconds to respond to a subsequent disk access, though most drives are much quicker</property>
+                                <property name="wrap">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScale" id="standby-scale">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="adjustment">standby-adjustment</property>
+                                <property name="round_digits">1</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="standby-disable-checkbutton">
+                                <property name="label" translatable="yes">_Disable Standby Timeout</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</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">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkBox" id="apm-box">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label7">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes" comments="Don't translate &quot;APM&quot; - it's an acronym">&lt;b&gt;Advanced Power _Management (APM)&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">apm-scale</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="box7">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_left">24</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label8">
+                                <property name="width_request">500</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">A low value means aggressive power management and a high value means better performance. If you enable this feature, please pay attention to SMART attributes such as  âStart/Stop Countâ as an aggressive spin-down policy may wear out the drive faster than anticipated</property>
+                                <property name="wrap">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScale" id="apm-scale">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="adjustment">apm-adjustment</property>
+                                <property name="round_digits">1</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="apm-disable-checkbutton">
+                                <property name="label" translatable="yes">D_isable Advanced Power Management</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</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>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">_Power Management</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="acoustic-page">
+                <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">6</property>
+                <child>
+                  <object class="GtkBox" id="box5">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Apply Acoustic _Settings When Drive Is Connected</property>
+                        <property name="use_underline">True</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                          <attribute name="scale" value="1"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="acoustic-apply-settings-switch">
+                        <property name="use_action_appearance">False</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="has_tooltip">True</property>
+                        <property name="use_action_appearance">False</property>
+                      </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">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox" id="acoustic-settings-box">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_left">24</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkBox" id="aam-box">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label9">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes" comments="Don't translate &quot;AAM&quot; - it's an acronym">&lt;b&gt;Automatic Acoustic _Management (AAM)&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">aam-scale</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="box4">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_left">24</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label10">
+                                <property name="width_request">500</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Most modern hard disks have the ability to speed down the head movements to reduce their noise output. The possible values are between 128 and 254 with 128 being the most quiet (and therefore slowest) setting and 254 the fastest (and loudest)</property>
+                                <property name="wrap">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScale" id="aam-scale">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="adjustment">aam-adjustment</property>
+                                <property name="round_digits">1</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="aam-disable-checkbutton">
+                                <property name="label" translatable="yes">_Disable Acoustic Management</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</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">0</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="position">1</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">_Acoustic Levels</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel-button</action-widget>
+      <action-widget response="-5">ok-button</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkAdjustment" id="standby-adjustment">
+    <property name="lower">1</property>
+    <property name="upper">255</property>
+    <property name="value">60</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+</interface>
diff --git a/data/ui/disks.ui b/data/ui/disks.ui
index 065fd1c..ced7c87 100644
--- a/data/ui/disks.ui
+++ b/data/ui/disks.ui
@@ -81,6 +81,13 @@
       </object>
     </child>
     <child>
+      <object class="GtkSeparatorMenuItem" id="menuitem4">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+      </object>
+    </child>
+    <child>
       <object class="GtkMenuItem" id="generic-drive-menu-item-create-disk-image">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
@@ -105,11 +112,41 @@
       </object>
     </child>
     <child>
+      <object class="GtkSeparatorMenuItem" id="menuitem5">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+      </object>
+    </child>
+    <child>
       <object class="GtkMenuItem" id="generic-drive-menu-item-view-smart">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="use_action_appearance">False</property>
-        <property name="label" translatable="yes">View SMART Data...</property>
+        <property name="label" translatable="yes">SMART Data and Tests...</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="generic-drive-menu-item-disk-settings">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+        <property name="label" translatable="yes">Drive Settings...</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkSeparatorMenuItem" id="menuitem6">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="generic-drive-menu-item-standby-now">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+        <property name="label" translatable="yes">Standby Now</property>
       </object>
     </child>
   </object>
diff --git a/src/disks/Makefile.am b/src/disks/Makefile.am
index 5d1ac5d..c3914a0 100644
--- a/src/disks/Makefile.am
+++ b/src/disks/Makefile.am
@@ -48,6 +48,7 @@ gnome_disks_SOURCES = 							\
 	gdupasswordstrengthwidget.h	gdupasswordstrengthwidget.c	\
 	gduestimator.h			gduestimator.c			\
 	gduchangepassphrasedialog.h	gduchangepassphrasedialog.c	\
+	gdudisksettingsdialog.h		gdudisksettingsdialog.c		\
 	$(enum_built_sources)						\
 	$(NULL)
 
diff --git a/src/disks/gduatasmartdialog.c b/src/disks/gduatasmartdialog.c
index 569af90..9d87c96 100644
--- a/src/disks/gduatasmartdialog.c
+++ b/src/disks/gduatasmartdialog.c
@@ -698,50 +698,6 @@ attr_format_assessment (gint     current,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gchar *
-format_duration_msec (guint64 msec)
-{
-  gchar *ret;
-  gdouble val;
-  if (msec > 1000 * 60 * 60 * 24 * 365.25)
-    {
-      val = msec / 1000.0 / 60.0 / 60.0 / 24.0 / 365.25;
-      /* Translators: Used for a time-based unit that exceed one year */
-      ret = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%.1f year", "%.1f years", val), val);
-    }
-  else if (msec > 1000 * 60 * 60 * 24)
-    {
-      val = msec / 1000.0 / 60.0 / 60.0 / 24.0;
-      /* Translators: Used for a time-based unit that exceed one day but not one year */
-      ret = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%.1f day", "%.1f days", val), val);
-    }
-  else if (msec > 1000 * 60 * 60)
-    {
-      val = msec / 1000.0 / 60.0 / 60.0;
-      /* Translators: Used for a time-based unit that exceed one hour but not one day */
-      ret = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%.1f hour", "%.1f hours", val), val);
-    }
-  else if (msec > 1000 * 60)
-    {
-      val = msec / 1000.0 / 60.0;
-      /* Translators: Used for a time-based unit that exceed one minute but not one hour */
-      ret = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%.1f minute", "%.1f minutes", val), val);
-    }
-  else if (msec > 1000)
-    {
-      val = msec / 1000.0;
-      /* Translators: Used for a time-based unit that exceed one second but not one minute */
-      ret = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%.1f second", "%.1f seconds", val), val);
-    }
-  else
-    {
-      val = msec;
-      /* Translators: Used for a time-based unit that is counted in milliseconds and doesn't exceed one second */
-      ret = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%.1f msec", "%.1f msecs", val), val);
-    }
-  return ret;
-}
-
-static gchar *
 pretty_to_string (guint64 pretty,
                   gint    pretty_unit)
 {
@@ -752,7 +708,7 @@ pretty_to_string (guint64 pretty,
   switch (pretty_unit)
     {
     case 2: /* SK_SMART_ATTRIBUTE_UNIT_MSECONDS */
-      ret = format_duration_msec (pretty);
+      ret = gdu_utils_format_duration_msec (pretty);
       break;
 
     case 3: /* SK_SMART_ATTRIBUTE_UNIT_SECTORS */
@@ -995,7 +951,7 @@ format_powered_on (UDisksDriveAta *ata)
 
   secs = udisks_drive_ata_get_smart_power_on_seconds (ata);
   if (secs > 0)
-    ret = format_duration_msec (secs * 1000);
+    ret = gdu_utils_format_duration_msec (secs * 1000);
   return ret;
 }
 
diff --git a/src/disks/gdudevicetreemodel.c b/src/disks/gdudevicetreemodel.c
index f84f26b..6784050 100644
--- a/src/disks/gdudevicetreemodel.c
+++ b/src/disks/gdudevicetreemodel.c
@@ -44,6 +44,9 @@ struct _GduDeviceTreeModel
   gboolean block_iter_valid;
 
   guint spinner_timeout;
+
+  /* "Polling Every Few Seconds" ... e.g. power state */
+  guint pefs_timeout_id;
 };
 
 typedef struct
@@ -79,6 +82,8 @@ gdu_device_tree_model_finalize (GObject *object)
 {
   GduDeviceTreeModel *model = GDU_DEVICE_TREE_MODEL (object);
 
+  g_source_remove (model->pefs_timeout_id);
+
   if (model->spinner_timeout != 0)
     g_source_remove (model->spinner_timeout);
 
@@ -301,6 +306,84 @@ _g_dbus_object_compare (GDBusObject *a,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+pm_get_state_cb (GObject       *source_object,
+                 GAsyncResult  *res,
+                 gpointer       user_data)
+{
+  GduDeviceTreeModel *model = GDU_DEVICE_TREE_MODEL (user_data);
+  GDBusObject *object;
+  gboolean sleeping;
+  guchar state;
+  GError *error = NULL;
+
+  if (!udisks_drive_ata_call_pm_get_state_finish (UDISKS_DRIVE_ATA (source_object),
+                                                  &state,
+                                                  res,
+                                                  &error))
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        {
+          g_printerr ("Error calling Drive.Ata.PmGetState: %s (%s, %d)\n",
+                      error->message, g_quark_to_string (error->domain), error->code);
+        }
+      g_clear_error (&error);
+      goto out;
+    }
+
+  sleeping = TRUE;
+  if (state == 0x80 || state == 0xff)
+    sleeping = FALSE;
+
+  object = g_dbus_interface_get_object (G_DBUS_INTERFACE (source_object));
+  if (object != NULL)
+    {
+      GtkTreeIter iter;
+      if (gdu_device_tree_model_get_iter_for_object (model, UDISKS_OBJECT (object), &iter))
+        {
+          gtk_tree_store_set (GTK_TREE_STORE (model),
+                              &iter,
+                              GDU_DEVICE_TREE_MODEL_COLUMN_SLEEPING, sleeping,
+                              -1);
+        }
+    }
+
+ out:
+  g_object_unref (model);
+}
+
+static gboolean
+on_pefs_timeout (gpointer user_data)
+{
+  GduDeviceTreeModel *model = GDU_DEVICE_TREE_MODEL (user_data);
+  GList *l;
+
+  for (l = model->current_drives; l != NULL; l = l->next)
+    {
+      UDisksObject *object = UDISKS_OBJECT (l->data);
+      UDisksDriveAta *ata;
+
+      ata = udisks_object_peek_drive_ata (object);
+      if (ata != NULL &&
+          udisks_drive_ata_get_pm_supported (ata) &&
+          udisks_drive_ata_get_pm_enabled (ata))
+        {
+          GVariantBuilder options_builder;
+          g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
+          g_variant_builder_add (&options_builder, "{sv}", "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+          udisks_drive_ata_call_pm_get_state (ata,
+                                              g_variant_builder_end (&options_builder),
+                                              NULL, /* GCancellable */
+                                              pm_get_state_cb,
+                                              g_object_ref (model));
+        }
+    }
+
+  return TRUE; /* keep timeout around */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 gdu_device_tree_model_constructed (GObject *object)
 {
   GduDeviceTreeModel *model = GDU_DEVICE_TREE_MODEL (object);
@@ -315,7 +398,8 @@ gdu_device_tree_model_constructed (GObject *object)
   types[6] = G_TYPE_BOOLEAN;
   types[7] = G_TYPE_BOOLEAN;
   types[8] = G_TYPE_UINT;
-  G_STATIC_ASSERT (9 == GDU_DEVICE_TREE_MODEL_N_COLUMNS);
+  types[9] = G_TYPE_BOOLEAN;
+  G_STATIC_ASSERT (10 == GDU_DEVICE_TREE_MODEL_N_COLUMNS);
   gtk_tree_store_set_column_types (GTK_TREE_STORE (model),
                                    GDU_DEVICE_TREE_MODEL_N_COLUMNS,
                                    types);
@@ -328,6 +412,9 @@ gdu_device_tree_model_constructed (GObject *object)
                     model);
   coldplug (model);
 
+  model->pefs_timeout_id = g_timeout_add_seconds (5, on_pefs_timeout, model);
+  on_pefs_timeout (model);
+
   if (G_OBJECT_CLASS (gdu_device_tree_model_parent_class)->constructed != NULL)
     G_OBJECT_CLASS (gdu_device_tree_model_parent_class)->constructed (object);
 }
diff --git a/src/disks/gdudevicetreemodel.h b/src/disks/gdudevicetreemodel.h
index abb0d33..de100b8 100644
--- a/src/disks/gdudevicetreemodel.h
+++ b/src/disks/gdudevicetreemodel.h
@@ -41,8 +41,9 @@ enum
   GDU_DEVICE_TREE_MODEL_COLUMN_NAME,
   GDU_DEVICE_TREE_MODEL_COLUMN_OBJECT,
   GDU_DEVICE_TREE_MODEL_COLUMN_WARNING,
-  GDU_DEVICE_TREE_MODEL_COLUMN_JOBS_RUNNING,
   GDU_DEVICE_TREE_MODEL_COLUMN_PULSE,
+  GDU_DEVICE_TREE_MODEL_COLUMN_JOBS_RUNNING,
+  GDU_DEVICE_TREE_MODEL_COLUMN_SLEEPING,
   GDU_DEVICE_TREE_MODEL_N_COLUMNS
 };
 
diff --git a/src/disks/gdudisksettingsdialog.c b/src/disks/gdudisksettingsdialog.c
new file mode 100644
index 0000000..16c2af7
--- /dev/null
+++ b/src/disks/gdudisksettingsdialog.c
@@ -0,0 +1,657 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <math.h>
+
+#include "gduapplication.h"
+#include "gduwindow.h"
+#include "gdudisksettingsdialog.h"
+#include "gduutils.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  volatile gint ref_count;
+
+  UDisksObject *object;
+  UDisksDrive *drive;
+  UDisksDriveAta *ata;
+
+  GCancellable *cancellable;
+
+  GduWindow *window;
+  GtkBuilder *builder;
+
+  GtkWidget *dialog;
+
+  GVariant *orig_drive_configuration;
+
+  /* Power Management page */
+  GtkWidget *pm_page;
+  GtkWidget *pm_apply_settings_switch;
+  GtkWidget *pm_settings_box;
+
+  GtkWidget *standby_box;
+  GtkAdjustment *standby_adjustment;
+  GtkWidget *standby_scale;
+  GtkWidget *standby_disable_checkbutton;
+
+  GtkWidget *apm_box;
+  GtkAdjustment *apm_adjustment;
+  GtkWidget *apm_scale;
+  GtkWidget *apm_disable_checkbutton;
+
+  /* Acoustic page */
+  GtkWidget *acoustic_page;
+  GtkWidget *acoustic_apply_settings_switch;
+  GtkWidget *acoustic_settings_box;
+
+  GtkWidget *aam_box;
+  GtkAdjustment *aam_adjustment;
+  GtkWidget *aam_scale;
+  GtkWidget *aam_disable_checkbutton;
+} DialogData;
+
+G_LOCK_DEFINE (bm_lock);
+
+static const struct {
+  goffset offset;
+  const gchar *name;
+} widget_mapping[] = {
+  /* Power Management page */
+  {G_STRUCT_OFFSET (DialogData, pm_page), "power-management-page"},
+  {G_STRUCT_OFFSET (DialogData, pm_apply_settings_switch), "pm-apply-settings-switch"},
+  {G_STRUCT_OFFSET (DialogData, pm_settings_box), "pm-settings-box"},
+  {G_STRUCT_OFFSET (DialogData, standby_box), "standby-box"},
+  {G_STRUCT_OFFSET (DialogData, standby_adjustment), "standby-adjustment"},
+  {G_STRUCT_OFFSET (DialogData, standby_scale), "standby-scale"},
+  {G_STRUCT_OFFSET (DialogData, standby_disable_checkbutton), "standby-disable-checkbutton"},
+  {G_STRUCT_OFFSET (DialogData, apm_box), "apm-box"},
+  {G_STRUCT_OFFSET (DialogData, apm_adjustment), "apm-adjustment"},
+  {G_STRUCT_OFFSET (DialogData, apm_scale), "apm-scale"},
+  {G_STRUCT_OFFSET (DialogData, apm_disable_checkbutton), "apm-disable-checkbutton"},
+
+  /* Acoustic page */
+  {G_STRUCT_OFFSET (DialogData, acoustic_page), "acoustic-page"},
+  {G_STRUCT_OFFSET (DialogData, acoustic_apply_settings_switch), "acoustic-apply-settings-switch"},
+  {G_STRUCT_OFFSET (DialogData, acoustic_settings_box), "acoustic-settings-box"},
+  {G_STRUCT_OFFSET (DialogData, aam_box), "aam-box"},
+  {G_STRUCT_OFFSET (DialogData, aam_adjustment), "aam-adjustment"},
+  {G_STRUCT_OFFSET (DialogData, aam_scale), "aam-scale"},
+  {G_STRUCT_OFFSET (DialogData, aam_disable_checkbutton), "aam-disable-checkbutton"},
+
+  {0, NULL}
+};
+
+static void update_dialog (DialogData *data);
+
+static void disable_unused_pages (DialogData *data);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static DialogData *
+dialog_data_ref (DialogData *data)
+{
+  g_atomic_int_inc (&data->ref_count);
+  return data;
+}
+
+static void
+dialog_data_unref (DialogData *data)
+{
+  if (g_atomic_int_dec_and_test (&data->ref_count))
+    {
+      if (data->dialog != NULL)
+        {
+          gtk_widget_hide (data->dialog);
+          gtk_widget_destroy (data->dialog);
+          data->dialog = NULL;
+        }
+
+      g_clear_object (&data->object);
+      g_clear_object (&data->window);
+      g_clear_object (&data->builder);
+
+      if (data->orig_drive_configuration != NULL)
+        g_variant_unref (data->orig_drive_configuration);
+
+      g_free (data);
+    }
+}
+
+static void
+dialog_data_close (DialogData *data)
+{
+  gtk_dialog_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_CANCEL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVariant *
+compute_configuration (DialogData *data)
+{
+  GVariantBuilder builder;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (data->orig_drive_configuration != NULL)
+    {
+      GVariantIter iter;
+      const gchar *key;
+      GVariant *value;
+      g_variant_iter_init (&iter, data->orig_drive_configuration);
+      while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
+        {
+          if (g_strcmp0 (key, "ata-pm-standby") == 0 ||
+              g_strcmp0 (key, "ata-apm-level") == 0 ||
+              g_strcmp0 (key, "ata-aam-level") == 0)
+            {
+              /* handled by us, skip */
+            }
+          else
+            {
+              g_variant_builder_add (&builder, "{sv}", key, value);
+            }
+          g_variant_unref (value);
+        }
+    }
+
+  /* Power Management page */
+  if (gtk_switch_get_active (GTK_SWITCH (data->pm_apply_settings_switch)))
+    {
+      gint ata_pm_standby = 0;
+      gint ata_apm_level = 255;
+
+      if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->standby_disable_checkbutton)))
+        ata_pm_standby = (gint) gtk_adjustment_get_value (data->standby_adjustment);
+
+      if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->apm_disable_checkbutton)))
+        ata_apm_level = (gint) gtk_adjustment_get_value (data->apm_adjustment);
+
+      if (udisks_drive_ata_get_pm_supported (data->ata))
+        g_variant_builder_add (&builder, "{sv}", "ata-pm-standby", g_variant_new_int32 (ata_pm_standby));
+      if (udisks_drive_ata_get_apm_supported (data->ata))
+        g_variant_builder_add (&builder, "{sv}", "ata-apm-level", g_variant_new_int32 (ata_apm_level));
+    }
+
+  /* Acoustic page */
+  if (gtk_switch_get_active (GTK_SWITCH (data->acoustic_apply_settings_switch)))
+    {
+      gint ata_aam_level = 0;
+
+      if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->aam_disable_checkbutton)))
+        ata_aam_level = (gint) gtk_adjustment_get_value (data->aam_adjustment);
+
+      if (udisks_drive_ata_get_aam_supported (data->ata))
+        g_variant_builder_add (&builder, "{sv}", "ata-aam-level", g_variant_new_int32 (ata_aam_level));
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+static gboolean
+_g_variant_equal0 (GVariant *a, GVariant *b)
+{
+  gboolean ret = FALSE;
+  if (a == NULL && b == NULL)
+    {
+      ret = TRUE;
+      goto out;
+    }
+  if (a == NULL || b == NULL)
+    goto out;
+  ret = g_variant_equal (a, b);
+out:
+  return ret;
+}
+
+static void
+update_dialog (DialogData *data)
+{
+  GVariant *new_drive_configuration;
+  gboolean changed = FALSE;
+
+  /* figure out if things has changed */
+  new_drive_configuration = compute_configuration (data);
+  if (!_g_variant_equal0 (new_drive_configuration, data->orig_drive_configuration))
+    changed = TRUE;
+  g_variant_unref (new_drive_configuration);
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, changed);
+}
+
+static void
+on_property_changed (GObject     *object,
+                     GParamSpec  *pspec,
+                     gpointer     user_data)
+{
+  DialogData *data = user_data;
+  update_dialog (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+on_apm_scale_format_value (GtkScale *scale,
+                           gdouble   value,
+                           gpointer  user_data)
+{
+  DialogData *data = user_data;
+  gchar *ret = NULL;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->apm_disable_checkbutton)))
+    {
+      ret = g_strdup (C_("apm-level", "Disabled"));
+    }
+  else if (value <= 127)
+    {
+      ret = g_strdup_printf (C_("apm-level", "%d (Spin-down permitted)"), (gint) value);
+    }
+  else
+    {
+      ret = g_strdup_printf (C_("apm-level", "%d (Spin-down not permitted)"), (gint) value);
+    }
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+on_aam_scale_format_value (GtkScale *scale,
+                           gdouble   value,
+                           gpointer  user_data)
+{
+  DialogData *data = user_data;
+  gchar *ret = NULL;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->aam_disable_checkbutton)))
+    {
+      ret = g_strdup (C_("aam-level", "Disabled"));
+    }
+  else
+    {
+      ret = g_strdup_printf ("%d", (gint) value);
+    }
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+on_standby_scale_format_value (GtkScale *scale,
+                               gdouble   value,
+                               gpointer  user_data)
+{
+  DialogData *data = user_data;
+  gchar *ret = NULL;
+
+  value = floor (value);
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->standby_disable_checkbutton)))
+    {
+      ret = g_strdup (C_("standby-value", "Disabled"));
+    }
+  else if (value < 241)
+    {
+      ret = gdu_utils_format_duration_msec (value * 5 * 1000);
+    }
+  else if (value < 252)
+    {
+      ret = gdu_utils_format_duration_msec ((value - 240) * 30 * 60 * 1000);
+    }
+  else if (value == 252)
+    {
+      ret = gdu_utils_format_duration_msec (21 * 60 * 1000);
+    }
+  else if (value == 253)
+    {
+      ret = g_strdup (C_("standby-value", "Vendor-defined"));
+    }
+  else if (value == 254)
+    {
+      ret = g_strdup (C_("standby-value", "Reserved"));
+    }
+  else if (value == 255)
+    {
+      ret = gdu_utils_format_duration_msec ((21 * 60 + 15) * 1000);
+    }
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_set_configuration_cb (GObject      *source_object,
+                         GAsyncResult *res,
+                         gpointer      user_data)
+{
+  DialogData *data = user_data;
+  GError *error = NULL;
+
+  if (!udisks_drive_call_set_configuration_finish (UDISKS_DRIVE (source_object),
+                                                   res,
+                                                   &error))
+    {
+      gdu_window_show_error (data->window,
+                             _("Error setting configuration"),
+                             error);
+      g_clear_error (&error);
+      goto out;
+    }
+  else
+    {
+      dialog_data_close (data);
+    }
+
+ out:
+  dialog_data_unref (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  gdouble pos;
+  const gchar *str;
+} Mark;
+
+void
+gdu_disk_settings_dialog_show (GduWindow    *window,
+                               UDisksObject *object)
+{
+  DialogData *data;
+  guint n;
+  Mark standby_marks[5] = {
+    /* Translators: This is a mark on the Standby scale. The string should be as short as possible */
+    { 12.0, N_("1 minute")},
+    /* Translators: This is a mark on the Standby scale. The string should be as short as possible */
+    { 60.0, N_("5 minutes")},
+    /* Translators: This is a mark on the Standby scale. The string should be as short as possible */
+    {120.0, N_("10 minutes")},
+    /* Translators: This is a mark on the Standby scale. The string should be as short as possible */
+    {180.0, N_("15 minutes")},
+    /* Translators: This is a mark on the Standby scale. The string should be as short as possible */
+    {246.0, N_("3 hours")},
+  };
+  Mark apm_marks[3] = {
+    /* Translators: This is a mark on the APM scale. The string should be as short as possible */
+    {  1.0, N_("Power Savings")},
+    /* Translators: This is a mark on the APM scale. The string should be as short as possible. The left arrow ("â") is to signify that the left part of the scale offers spindown. In RTL locales, please use a right arrow ("â") instead. */
+    {127.0, N_("â Spindown")},
+    /* Translators: This is a mark on the APM scale. The string should be as short as possible */
+    {254.0, N_("Performance")}
+  };
+  Mark aam_marks[2] = {
+    /* Translators: This is a mark on the AAM scale. The string should be as short as possible */
+    {128.0, N_("Quiet")},
+    /* Translators: This is a mark on the AAM scale. The string should be as short as possible */
+    {254.0, N_("Loud")}
+  };
+
+  data = g_new0 (DialogData, 1);
+  data->ref_count = 1;
+  data->object = g_object_ref (object);
+  data->drive = udisks_object_peek_drive (data->object);
+  data->ata = udisks_object_peek_drive_ata (data->object);
+  data->window = g_object_ref (window);
+  data->orig_drive_configuration = udisks_drive_dup_configuration (data->drive);
+
+  data->dialog = GTK_WIDGET (gdu_application_new_widget (gdu_window_get_application (window),
+                                                         "disk-settings-dialog.ui",
+                                                         "dialog1",
+                                                         &data->builder));
+  for (n = 0; widget_mapping[n].name != NULL; n++)
+    {
+      gpointer *p = (gpointer *) ((char *) data + widget_mapping[n].offset);
+      *p = gtk_builder_get_object (data->builder, widget_mapping[n].name);
+    }
+
+  /* add marks on Standby, APM and AAM scales */
+  for (n = 0; n < G_N_ELEMENTS (standby_marks); n++)
+    {
+      Mark *mark = &standby_marks[n];
+      gchar *s = g_strdup_printf ("<small>%s</small>", _(mark->str));
+      gtk_scale_add_mark (GTK_SCALE (data->standby_scale), mark->pos, GTK_POS_BOTTOM, s);
+      g_free (s);
+    }
+  for (n = 0; n < G_N_ELEMENTS (apm_marks); n++)
+    {
+      Mark *mark = &apm_marks[n];
+      gchar *s = g_strdup_printf ("<small>%s</small>", _(mark->str));
+      gtk_scale_add_mark (GTK_SCALE (data->apm_scale), mark->pos, GTK_POS_BOTTOM, s);
+      g_free (s);
+    }
+  for (n = 0; n < G_N_ELEMENTS (aam_marks); n++)
+    {
+      Mark *mark = &aam_marks[n];
+      gchar *s = g_strdup_printf ("<small>%s</small>", _(mark->str));
+      gtk_scale_add_mark (GTK_SCALE (data->aam_scale), mark->pos, GTK_POS_BOTTOM, s);
+      g_free (s);
+    }
+
+  g_signal_connect (data->standby_scale, "format-value", G_CALLBACK (on_standby_scale_format_value), data);
+  g_signal_connect (data->apm_scale, "format-value", G_CALLBACK (on_apm_scale_format_value), data);
+  g_signal_connect (data->aam_scale, "format-value", G_CALLBACK (on_aam_scale_format_value), data);
+
+  gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (window));
+
+  disable_unused_pages (data);
+
+  /* initialize dialog with values from current configuration */
+  if (data->orig_drive_configuration != NULL)
+    {
+      gint ata_pm_standby = -1;
+      gint ata_apm_level = -1;
+      gint ata_aam_level = -1;
+
+      /* Power Management page */
+      g_variant_lookup (data->orig_drive_configuration, "ata-pm-standby", "i", &ata_pm_standby);
+      g_variant_lookup (data->orig_drive_configuration, "ata-apm-level", "i", &ata_apm_level);
+      if (ata_pm_standby == -1 && ata_apm_level == -1)
+        {
+          /* No settings at all, set Switch to OFF and chose some good defaults */
+          gtk_switch_set_active (GTK_SWITCH (data->pm_apply_settings_switch), FALSE);
+          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->standby_disable_checkbutton), FALSE);
+          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->apm_disable_checkbutton), FALSE);
+          gtk_adjustment_set_value (data->standby_adjustment, 180);
+          gtk_adjustment_set_value (data->apm_adjustment, 127);
+        }
+      else
+        {
+          /* Set "Disable" buttons as appropriate and set slider values to something reasonable */
+          if (ata_pm_standby == 0 || ata_pm_standby == -1)
+            {
+              ata_pm_standby = 180;
+              gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->standby_disable_checkbutton), TRUE);
+            }
+          if (ata_apm_level == 255 || ata_apm_level == -1)
+            {
+              ata_apm_level = 127;
+              gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->apm_disable_checkbutton), TRUE);
+            }
+          gtk_adjustment_set_value (data->standby_adjustment, ata_pm_standby);
+          gtk_adjustment_set_value (data->apm_adjustment, ata_apm_level);
+          gtk_switch_set_active (GTK_SWITCH (data->pm_apply_settings_switch), TRUE);
+        }
+
+      /* Acoustic page */
+      g_variant_lookup (data->orig_drive_configuration, "ata-aam-level", "i", &ata_aam_level);
+      if (ata_aam_level == -1)
+        {
+          /* No settings at all, set Switch to OFF and chose some good defaults */
+          gtk_switch_set_active (GTK_SWITCH (data->acoustic_apply_settings_switch), FALSE);
+          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->aam_disable_checkbutton), FALSE);
+          gtk_adjustment_set_value (data->aam_adjustment, 128);
+        }
+      else
+        {
+          /* Set "Disable" buttons as appropriate and set slider values to something reasonable */
+          if (ata_aam_level == 0 || ata_aam_level == -1)
+            {
+              ata_aam_level = 128;
+              gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->aam_disable_checkbutton), TRUE);
+            }
+          gtk_adjustment_set_value (data->aam_adjustment, ata_aam_level);
+          gtk_switch_set_active (GTK_SWITCH (data->acoustic_apply_settings_switch), TRUE);
+        }
+    }
+
+  g_signal_connect (data->pm_apply_settings_switch,
+                    "notify::active", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->acoustic_apply_settings_switch,
+                    "notify::active", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->standby_adjustment,
+                    "notify::value", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->standby_disable_checkbutton,
+                    "notify::active", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->apm_adjustment,
+                    "notify::value", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->apm_disable_checkbutton,
+                    "notify::active", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->aam_adjustment,
+                    "notify::value", G_CALLBACK (on_property_changed), data);
+  g_signal_connect (data->aam_disable_checkbutton,
+                    "notify::active", G_CALLBACK (on_property_changed), data);
+
+  g_object_bind_property (data->pm_apply_settings_switch,
+                          "active",
+                          data->pm_settings_box,
+                          "sensitive",
+                          G_BINDING_SYNC_CREATE);
+
+  g_object_bind_property (data->acoustic_apply_settings_switch,
+                          "active",
+                          data->acoustic_settings_box,
+                          "sensitive",
+                          G_BINDING_SYNC_CREATE);
+
+  g_object_bind_property (data->standby_disable_checkbutton,
+                          "active",
+                          data->standby_scale,
+                          "sensitive",
+                          G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN);
+
+  g_object_bind_property (data->apm_disable_checkbutton,
+                          "active",
+                          data->apm_scale,
+                          "sensitive",
+                          G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN);
+
+  g_object_bind_property (data->aam_disable_checkbutton,
+                          "active",
+                          data->aam_scale,
+                          "sensitive",
+                          G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN);
+
+  update_dialog (data);
+
+  while (TRUE)
+    {
+      gint response;
+      response = gtk_dialog_run (GTK_DIALOG (data->dialog));
+      /* Keep in sync with .ui file */
+      switch (response)
+        {
+        case GTK_RESPONSE_OK: /* OK */
+          udisks_drive_call_set_configuration (data->drive,
+                                               compute_configuration (data),  /* consumes floating */
+                                               g_variant_new ("a{sv}", NULL), /* options */
+                                               NULL, /* cancellable */
+                                               on_set_configuration_cb,
+                                               dialog_data_ref (data));
+          break;
+
+        default:
+          goto out;
+        }
+    }
+ out:
+  dialog_data_close (data);
+  dialog_data_unref (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+hide_forever (GtkWidget *widget)
+{
+  gtk_widget_set_no_show_all (widget, TRUE);
+  gtk_widget_set_visible (widget, FALSE);
+}
+
+static void
+disable_unused_pages (DialogData *data)
+{
+  /* Disable pages (and parts of pages) not relevant for a drive - see also gdu_disk_settings_dialog_should_show() */
+
+  if (!(udisks_drive_ata_get_pm_supported (data->ata) || udisks_drive_ata_get_apm_supported (data->ata)))
+    {
+      hide_forever (data->pm_page);
+    }
+  else
+    {
+      if (!udisks_drive_ata_get_pm_supported (data->ata))
+        hide_forever (data->standby_box);
+      if (!udisks_drive_ata_get_apm_supported (data->ata))
+        hide_forever (data->apm_box);
+    }
+
+  if (!udisks_drive_ata_get_aam_supported (data->ata))
+    hide_forever (data->acoustic_page);
+}
+
+gboolean
+gdu_disk_settings_dialog_should_show (UDisksObject *object)
+{
+  gboolean ret = FALSE;
+  UDisksDrive *drive;
+  UDisksDriveAta *ata;
+
+  g_return_val_if_fail (UDISKS_IS_OBJECT (object), FALSE);
+
+  /* see also disabled_unused_pages() above */
+
+  drive = udisks_object_peek_drive (object);
+  if (drive == NULL)
+    goto out;
+
+  ata = udisks_object_peek_drive_ata (object);
+  if (ata == NULL)
+    goto out;
+
+  if (udisks_drive_ata_get_pm_supported (ata) ||
+      udisks_drive_ata_get_apm_supported (ata) ||
+      udisks_drive_ata_get_aam_supported (ata))
+    {
+      ret = TRUE;
+    }
+
+ out:
+  return ret;
+}
+
diff --git a/src/disks/gdudisksettingsdialog.h b/src/disks/gdudisksettingsdialog.h
new file mode 100644
index 0000000..c8bb5b7
--- /dev/null
+++ b/src/disks/gdudisksettingsdialog.h
@@ -0,0 +1,38 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GDU_DISK_SETTINGS_DIALOG_H__
+#define __GDU_DISK_SETTINGS_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include "gdutypes.h"
+
+G_BEGIN_DECLS
+
+void   gdu_disk_settings_dialog_show (GduWindow    *window,
+                                      UDisksObject *object);
+
+gboolean gdu_disk_settings_dialog_should_show (UDisksObject *object);
+
+G_END_DECLS
+
+#endif /* __GDU_DISK_SETTINGS_DIALOG_H__ */
diff --git a/src/disks/gduutils.c b/src/disks/gduutils.c
index 807b15e..6d85482 100644
--- a/src/disks/gduutils.c
+++ b/src/disks/gduutils.c
@@ -23,6 +23,8 @@
 #include "config.h"
 #include <glib/gi18n.h>
 
+#include <math.h>
+
 #include "gduutils.h"
 
 #if defined(HAVE_LIBSYSTEMD_LOGIN)
@@ -418,3 +420,156 @@ gdu_utils_get_seat (void)
 }
 
 #endif
+
+static gchar *
+years_to_string (gint value)
+{
+  /* Translators: Used for number of years */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d year", "%d years", value), value);
+}
+
+static gchar *
+months_to_string (gint value)
+{
+  /* Translators: Used for number of months */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d month", "%d months", value), value);
+}
+
+static gchar *
+days_to_string (gint value)
+{
+  /* Translators: Used for number of days */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d day", "%d days", value), value);
+}
+
+static gchar *
+hours_to_string (gint value)
+{
+  /* Translators: Used for number of hours */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d hour", "%d hours", value), value);
+}
+
+static gchar *
+minutes_to_string (gint value)
+{
+  /* Translators: Used for number of minutes */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d minute", "%d minutes", value), value);
+}
+
+static gchar *
+seconds_to_string (gint value)
+{
+  /* Translators: Used for number of seconds */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d second", "%d seconds", value), value);
+}
+
+static gchar *
+milliseconds_to_string (gint value)
+{
+  /* Translators: Used for number of milli-seconds */
+  return g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d milli-second", "%d milli-seconds", value), value);
+}
+
+#define MSEC_PER_YEAR   (1000.0 * 60 * 60 * 24 * 365.25)
+#define MSEC_PER_MONTH  (1000.0 * 60 * 60 * 24 * 365.25 / 12.0)
+#define MSEC_PER_DAY    (1000.0 * 60 * 60 * 24)
+#define MSEC_PER_HOUR   (1000.0 * 60 * 60)
+#define MSEC_PER_MINUTE (1000.0 * 60)
+#define MSEC_PER_SECOND (1000.0)
+
+gchar *
+gdu_utils_format_duration_msec (guint64 msec)
+{
+  gchar *ret;
+  guint years = 0;
+  guint months = 0;
+  guint days = 0;
+  guint hours = 0;
+  guint minutes = 0;
+  guint seconds = 0;
+  guint milliseconds = 0;
+  gchar *years_str = NULL;
+  gchar *months_str = NULL;
+  gchar *days_str = NULL;
+  gchar *hours_str = NULL;
+  gchar *minutes_str = NULL;
+  gchar *seconds_str = NULL;
+  gchar *milliseconds_str = NULL;
+  guint64 t;
+
+  t = msec;
+
+  years  = floor (t / MSEC_PER_YEAR);
+  t -= years * MSEC_PER_YEAR;
+
+  months = floor (t / MSEC_PER_MONTH);
+  t -= months * MSEC_PER_MONTH;
+
+  days = floor (t / MSEC_PER_DAY);
+  t -= days * MSEC_PER_DAY;
+
+  hours = floor (t / MSEC_PER_HOUR);
+  t -= hours * MSEC_PER_HOUR;
+
+  minutes = floor (t / MSEC_PER_MINUTE);
+  t -= minutes * MSEC_PER_MINUTE;
+
+  seconds = floor (t / MSEC_PER_SECOND);
+  t -= seconds * MSEC_PER_SECOND;
+
+  milliseconds = floor (t);
+
+  years_str = years_to_string (years);
+  months_str = months_to_string (months);
+  days_str = days_to_string (days);
+  hours_str = hours_to_string (hours);
+  minutes_str = minutes_to_string (minutes);
+  seconds_str = seconds_to_string (seconds);
+  milliseconds_str = milliseconds_to_string (milliseconds);
+
+  if (years > 0)
+    {
+      /* Translators: Used for duration greater than one year. First %s is number of years, second %s is months, third %s is days */
+      ret = g_strdup_printf (C_("duration-year-to-inf", "%s, %s and %s"), years_str, months_str, days_str);
+    }
+  else if (months > 0)
+    {
+      /* Translators: Used for durations less than one year but greater than one month. First %s is number of months, second %s is days */
+      ret = g_strdup_printf (C_("duration-months-to-year", "%s and %s"), months_str, days_str);
+    }
+  else if (days > 0)
+    {
+      /* Translators: Used for durations less than one month but greater than one day. First %s is number of days, second %s is hours */
+      ret = g_strdup_printf (C_("duration-day-to-month", "%s and %s"), days_str, hours_str);
+    }
+  else if (hours > 0)
+    {
+      /* Translators: Used for durations less than one day but greater than one hour. First %s is number of hours, second %s is minutes */
+      ret = g_strdup_printf (C_("duration-hour-to-day", "%s and %s"), hours_str, minutes_str);
+    }
+  else if (minutes > 0)
+    {
+      /* Translators: Used for durations less than one hour but greater than one minute. First %s is number of minutes, second %s is seconds */
+      ret = g_strdup_printf (C_("duration-minute-to-hour", "%s and %s"), minutes_str, seconds_str);
+    }
+  else if (seconds > 0)
+    {
+      /* Translators: Used for durations less than one minute byte greater than one second. First %s is number of seconds */
+      ret = g_strdup_printf (C_("duration-second-to-minute", "%s"), seconds_str);
+    }
+  else
+    {
+      /* Translators: Used for durations less than one second. First %s is number of milli-seconds */
+      ret = g_strdup_printf (C_("duration-zero-to-second", "%s"), milliseconds_str);
+    }
+
+  g_free (years_str);
+  g_free (months_str);
+  g_free (days_str);
+  g_free (hours_str);
+  g_free (minutes_str);
+  g_free (seconds_str);
+  g_free (milliseconds_str);
+
+  return ret;
+}
diff --git a/src/disks/gduutils.h b/src/disks/gduutils.h
index 65560ce..fe85949 100644
--- a/src/disks/gduutils.h
+++ b/src/disks/gduutils.h
@@ -57,6 +57,8 @@ void gdu_options_update_entry_option (GtkWidget       *options_entry,
 
 const gchar *gdu_utils_get_seat (void);
 
+gchar *gdu_utils_format_duration_msec (guint64 msec);
+
 G_END_DECLS
 
 #endif /* __GDU_UTILS_H__ */
diff --git a/src/disks/gduwindow.c b/src/disks/gduwindow.c
index 525179d..932e265 100644
--- a/src/disks/gduwindow.c
+++ b/src/disks/gduwindow.c
@@ -50,6 +50,7 @@
 #include "gducreatediskimagedialog.h"
 #include "gdurestorediskimagedialog.h"
 #include "gduchangepassphrasedialog.h"
+#include "gdudisksettingsdialog.h"
 
 /* Keep in sync with tabs in disks.ui file */
 typedef enum
@@ -109,6 +110,8 @@ struct _GduWindow
 
   GtkWidget *generic_drive_menu;
   GtkWidget *generic_drive_menu_item_view_smart;
+  GtkWidget *generic_drive_menu_item_disk_settings;
+  GtkWidget *generic_drive_menu_item_standby_now;
   GtkWidget *generic_drive_menu_item_format_disk;
   GtkWidget *generic_drive_menu_item_create_disk_image;
   GtkWidget *generic_drive_menu_item_restore_disk_image;
@@ -126,8 +129,6 @@ struct _GduWindow
   GtkWidget *generic_menu_item_benchmark;
 
   GtkWidget *devtab_loop_autoclear_switch;
-
-  GHashTable *label_connections;
 };
 
 static const struct {
@@ -169,11 +170,13 @@ static const struct {
   {G_STRUCT_OFFSET (GduWindow, devtab_loop_autoclear_switch), "devtab-loop-autoclear-switch"},
 
   {G_STRUCT_OFFSET (GduWindow, generic_drive_menu), "generic-drive-menu"},
+  {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_format_disk), "generic-drive-menu-item-format-disk"},
   {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_create_disk_image), "generic-drive-menu-item-create-disk-image"},
   {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_restore_disk_image), "generic-drive-menu-item-restore-disk-image"},
   {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_benchmark), "generic-drive-menu-item-benchmark"},
   {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_view_smart), "generic-drive-menu-item-view-smart"},
-  {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_format_disk), "generic-drive-menu-item-format-disk"},
+  {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_disk_settings), "generic-drive-menu-item-disk-settings"},
+  {G_STRUCT_OFFSET (GduWindow, generic_drive_menu_item_standby_now), "generic-drive-menu-item-standby-now"},
 
   {G_STRUCT_OFFSET (GduWindow, generic_menu), "generic-menu"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_configure_fstab), "generic-menu-item-configure-fstab"},
@@ -222,11 +225,13 @@ typedef enum
   SHOW_FLAGS_ENCRYPTED_LOCK_BUTTON   = (1<<9),
 
   /* generic drive menu */
-  SHOW_FLAGS_DISK_POPUP_MENU_CREATE_DISK_IMAGE     = (1<<10),
-  SHOW_FLAGS_DISK_POPUP_MENU_RESTORE_DISK_IMAGE    = (1<<11),
-  SHOW_FLAGS_DISK_POPUP_MENU_BENCHMARK             = (1<<12),
-  SHOW_FLAGS_DISK_POPUP_MENU_VIEW_SMART            = (1<<13),
-  SHOW_FLAGS_DISK_POPUP_MENU_FORMAT_DISK           = (1<<14),
+  SHOW_FLAGS_DISK_POPUP_MENU_FORMAT_DISK           = (1<<10),
+  SHOW_FLAGS_DISK_POPUP_MENU_CREATE_DISK_IMAGE     = (1<<11),
+  SHOW_FLAGS_DISK_POPUP_MENU_RESTORE_DISK_IMAGE    = (1<<12),
+  SHOW_FLAGS_DISK_POPUP_MENU_BENCHMARK             = (1<<13),
+  SHOW_FLAGS_DISK_POPUP_MENU_VIEW_SMART            = (1<<14),
+  SHOW_FLAGS_DISK_POPUP_MENU_DISK_SETTINGS         = (1<<15),
+  SHOW_FLAGS_DISK_POPUP_MENU_STANDBY_NOW           = (1<<16),
 
   /* generic volume menu */
   SHOW_FLAGS_POPUP_MENU_CONFIGURE_FSTAB       = (1<<20),
@@ -240,6 +245,7 @@ typedef enum
   SHOW_FLAGS_POPUP_MENU_BENCHMARK             = (1<<28),
 } ShowFlags;
 
+
 static void setup_device_page (GduWindow *window, UDisksObject *object);
 static void update_device_page (GduWindow *window, ShowFlags *show_flags);
 static void teardown_device_page (GduWindow *window);
@@ -261,6 +267,10 @@ static void on_devtab_action_generic_drive_activated (GtkAction *action, gpointe
 
 static void on_generic_drive_menu_item_view_smart (GtkMenuItem *menu_item,
                                              gpointer   user_data);
+static void on_generic_drive_menu_item_disk_settings (GtkMenuItem *menu_item,
+                                                      gpointer   user_data);
+static void on_generic_drive_menu_item_standby_now (GtkMenuItem *menu_item,
+                                                    gpointer   user_data);
 static void on_generic_drive_menu_item_format_disk (GtkMenuItem *menu_item,
                                               gpointer   user_data);
 static void on_generic_drive_menu_item_create_disk_image (GtkMenuItem *menu_item,
@@ -298,10 +308,6 @@ G_DEFINE_TYPE (GduWindow, gdu_window, GTK_TYPE_APPLICATION_WINDOW);
 static void
 gdu_window_init (GduWindow *window)
 {
-  window->label_connections = g_hash_table_new_full (g_str_hash,
-                                                     g_str_equal,
-                                                     g_free,
-                                                     NULL);
 }
 
 static void on_client_changed (UDisksClient  *client,
@@ -402,6 +408,10 @@ update_for_show_flags (GduWindow *window,
                             show_flags & SHOW_FLAGS_DISK_POPUP_MENU_FORMAT_DISK);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_drive_menu_item_view_smart),
                             show_flags & SHOW_FLAGS_DISK_POPUP_MENU_VIEW_SMART);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_drive_menu_item_disk_settings),
+                            show_flags & SHOW_FLAGS_DISK_POPUP_MENU_DISK_SETTINGS);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_drive_menu_item_standby_now),
+                            show_flags & SHOW_FLAGS_DISK_POPUP_MENU_STANDBY_NOW);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_drive_menu_item_create_disk_image),
                             show_flags & SHOW_FLAGS_DISK_POPUP_MENU_CREATE_DISK_IMAGE);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_drive_menu_item_restore_disk_image),
@@ -1002,6 +1012,17 @@ gdu_window_constructed (GObject *object)
                                        "active", GDU_DEVICE_TREE_MODEL_COLUMN_JOBS_RUNNING,
                                        "pulse", GDU_DEVICE_TREE_MODEL_COLUMN_PULSE,
                                        NULL);
+  renderer = gtk_cell_renderer_pixbuf_new ();
+  g_object_set (G_OBJECT (renderer),
+                "xalign", 1.0,
+                "stock-size", GTK_ICON_SIZE_MENU,
+                "icon-name", "gnome-disks-state-standby-symbolic",
+                NULL);
+  gtk_tree_view_column_pack_end (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column,
+                                       renderer,
+                                       "visible", GDU_DEVICE_TREE_MODEL_COLUMN_SLEEPING,
+                                       NULL);
 
   /* expand on insertion - hmm, I wonder if there's an easier way to do this */
   g_signal_connect (window->model,
@@ -1093,6 +1114,14 @@ gdu_window_constructed (GObject *object)
                     "activate",
                     G_CALLBACK (on_generic_drive_menu_item_view_smart),
                     window);
+  g_signal_connect (window->generic_drive_menu_item_disk_settings,
+                    "activate",
+                    G_CALLBACK (on_generic_drive_menu_item_disk_settings),
+                    window);
+  g_signal_connect (window->generic_drive_menu_item_standby_now,
+                    "activate",
+                    G_CALLBACK (on_generic_drive_menu_item_standby_now),
+                    window);
   g_signal_connect (window->generic_drive_menu_item_format_disk,
                     "activate",
                     G_CALLBACK (on_generic_drive_menu_item_format_disk),
@@ -1740,6 +1769,15 @@ update_device_page_for_drive (GduWindow      *window,
       g_free (s);
     }
 
+  if (gdu_disk_settings_dialog_should_show (object))
+    *show_flags |= SHOW_FLAGS_DISK_POPUP_MENU_DISK_SETTINGS;
+
+  if (ata != NULL)
+    {
+      if (udisks_drive_ata_get_pm_supported (ata))
+        *show_flags |= SHOW_FLAGS_DISK_POPUP_MENU_STANDBY_NOW;
+    }
+
   size = udisks_drive_get_size (drive);
   if (size > 0)
     {
@@ -2509,6 +2547,58 @@ on_generic_drive_menu_item_view_smart (GtkMenuItem *menu_item,
 }
 
 static void
+on_generic_drive_menu_item_disk_settings (GtkMenuItem *menu_item,
+                                          gpointer     user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  gdu_disk_settings_dialog_show (window, window->current_object);
+}
+
+static void
+ata_pm_standby_cb (GObject      *source_object,
+                   GAsyncResult *res,
+                   gpointer      user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  GError *error = NULL;
+
+  error = NULL;
+  if (!udisks_drive_ata_call_pm_standby_finish (UDISKS_DRIVE_ATA (source_object),
+                                                res,
+                                                &error))
+    {
+      gdu_window_show_error (window,
+                             _("An error occurred when trying to put the drive into standby mode"),
+                             error);
+      g_clear_error (&error);
+    }
+
+  g_object_unref (window);
+}
+
+static void
+on_generic_drive_menu_item_standby_now (GtkMenuItem *menu_item,
+                                        gpointer     user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  UDisksDriveAta *ata;
+
+  ata = udisks_object_peek_drive_ata (window->current_object);
+  if (ata != NULL)
+    {
+      udisks_drive_ata_call_pm_standby (ata,
+                                        g_variant_new ("a{sv}", NULL), /* options */
+                                        NULL, /* GCancellable */
+                                        (GAsyncReadyCallback) ata_pm_standby_cb,
+                                        g_object_ref (window));
+    }
+  else
+    {
+      g_warning ("object is not an ATA drive");
+    }
+}
+
+static void
 on_generic_menu_item_configure_crypttab (GtkMenuItem *menu_item,
                                          gpointer   user_data)
 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]