[dconf-editor] Introduce AdaptativeWindow.



commit 9b6c767b5750393077af980755febae87afd3e95
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Sun Nov 25 16:14:00 2018 +0100

    Introduce AdaptativeWindow.

 editor/adaptative-pathbar.vala     |   4 +-
 editor/adaptative-window.vala      | 269 +++++++++++++++++++++++++++++++++
 editor/browser-headerbar.vala      |  42 +++---
 editor/browser-view.vala           |  15 --
 editor/dconf-editor.gresource.xml  |   1 +
 editor/dconf-editor.ui             |  47 +-----
 editor/dconf-window.vala           | 295 +++++++++++--------------------------
 editor/key-list-box-row.vala       |   4 +-
 editor/meson.build                 |   3 +
 editor/modifications-revealer.vala |  11 +-
 editor/notifications-revealer.ui   |  46 ++++++
 editor/notifications-revealer.vala |  55 +++++++
 editor/overlayed-list.vala         |   2 +-
 editor/pathentry.vala              |   6 +-
 editor/pathwidget.vala             |   4 +-
 15 files changed, 498 insertions(+), 306 deletions(-)
---
diff --git a/editor/adaptative-pathbar.vala b/editor/adaptative-pathbar.vala
index 7cd1860..cd55689 100644
--- a/editor/adaptative-pathbar.vala
+++ b/editor/adaptative-pathbar.vala
@@ -26,8 +26,8 @@ private class AdaptativePathbar : Stack, Pathbar, AdaptativeWidget
     private bool thin_window = false;
     private void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        bool _thin_window = AdaptativeWidget.WindowSize.is_thin (new_size);
-        if (_thin_window == thin_window)
+        bool _thin_window = AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+        if (thin_window == _thin_window)
             return;
         thin_window = _thin_window;
 
diff --git a/editor/adaptative-window.vala b/editor/adaptative-window.vala
new file mode 100644
index 0000000..cab86be
--- /dev/null
+++ b/editor/adaptative-window.vala
@@ -0,0 +1,269 @@
+/*
+  This file is part of Dconf Editor
+
+  Dconf Editor is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Dconf Editor 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Dconf Editor.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+private interface AdaptativeWidget : Object
+{ /*
+       ╎ extra ╎
+       ╎ thin  ╎
+  ╶╶╶╶ ┏━━━━━━━┳━━━━━━━┳━━━━━──╴
+ extra ┃ PHONE ┃ PHONE ┃ EXTRA
+ flat  ┃ _BOTH ┃ _HZTL ┃ _FLAT
+  ╶╶╶╶ ┣━━━━━━━╋━━━━━━━╋━━━━╾──╴
+       ┃ PHONE ┃       ┃
+       ┃ _VERT ┃       ┃
+       ┣━━━━━━━┫       ┃
+       ┃ EXTRA ┃ QUITE ╿ USUAL
+       ╿ _THIN │ _THIN │ _SIZE
+       ╵       ╵       ╵
+       ╎     thin      ╎
+                              */
+
+    internal enum WindowSize {
+        START_SIZE,
+        USUAL_SIZE,
+        QUITE_THIN,
+        PHONE_VERT,
+        PHONE_HZTL,
+        PHONE_BOTH,
+        EXTRA_THIN,
+        EXTRA_FLAT;
+
+        internal static inline bool is_phone_size (WindowSize window_size)
+        {
+            return (window_size == PHONE_BOTH) || (window_size == PHONE_VERT) || (window_size == PHONE_HZTL);
+        }
+
+        internal static inline bool is_extra_thin (WindowSize window_size)
+        {
+            return (window_size == PHONE_BOTH) || (window_size == PHONE_VERT) || (window_size == EXTRA_THIN);
+        }
+
+        internal static inline bool is_extra_flat (WindowSize window_size)
+        {
+            return (window_size == PHONE_BOTH) || (window_size == PHONE_HZTL) || (window_size == EXTRA_FLAT);
+        }
+
+        internal static inline bool is_quite_thin (WindowSize window_size)
+        {
+            return is_extra_thin (window_size) || (window_size == PHONE_HZTL) || (window_size == QUITE_THIN);
+        }
+    }
+
+    internal abstract void set_window_size (WindowSize new_size);
+}
+
+private class AdaptativeWindow : ApplicationWindow
+{
+    private StyleContext context;
+
+    construct
+    {
+        context = get_style_context ();
+        context.add_class ("startup");
+
+        manage_high_contrast ();
+
+        window_state_event.connect (on_window_state_event);
+        size_allocate.connect (on_size_allocate);
+        destroy.connect (on_destroy);
+
+        load_window_state ();
+
+        Timeout.add (300, () => { context.remove_class ("startup"); return Source.REMOVE; });
+    }
+
+    /*\
+    * * callbacks
+    \*/
+
+    private bool on_window_state_event (Widget widget, Gdk.EventWindowState event)
+    {
+        if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
+            window_is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
+        /* We don’t save this state, but track it for saving size allocation */
+        if ((event.changed_mask & Gdk.WindowState.TILED) != 0)
+            window_is_tiled = (event.new_window_state & Gdk.WindowState.TILED) != 0;
+
+        return false;
+    }
+
+    private void on_size_allocate (Allocation allocation)
+    {
+        int height = allocation.height;
+        int width = allocation.width;
+
+        update_adaptative_children (ref width, ref height);
+        update_window_state ();
+    }
+
+    private void on_destroy ()
+    {
+        save_window_state ();
+        base.destroy ();
+    }
+
+    /*\
+    * * manage adaptative children
+    \*/
+
+    protected AdaptativeWidget.WindowSize window_size = AdaptativeWidget.WindowSize.START_SIZE;
+    protected List<AdaptativeWidget> adaptative_children = new List<AdaptativeWidget> ();
+
+    private void update_adaptative_children (ref int width, ref int height)
+    {
+        bool extra_flat = height < 400;
+
+        if (width < 590)
+        {
+            if (extra_flat)         change_window_size (AdaptativeWidget.WindowSize.PHONE_BOTH);
+            else if (height < 787)  change_window_size (AdaptativeWidget.WindowSize.PHONE_VERT);
+            else                    change_window_size (AdaptativeWidget.WindowSize.EXTRA_THIN);
+
+            set_style_classes (true, true, false);
+        }
+        else if (width < 787)
+        {
+            if (extra_flat)         change_window_size (AdaptativeWidget.WindowSize.PHONE_HZTL);
+            else                    change_window_size (AdaptativeWidget.WindowSize.QUITE_THIN);
+
+            set_style_classes (false, true, false);
+        }
+        else
+        {
+            if (extra_flat)         change_window_size (AdaptativeWidget.WindowSize.EXTRA_FLAT);
+            else                    change_window_size (AdaptativeWidget.WindowSize.USUAL_SIZE);
+
+            if (width > MAX_ROW_WIDTH + 42)
+                set_style_classes (false, false, true);
+            else
+                set_style_classes (false, false, false);
+        }
+    }
+
+    private void change_window_size (AdaptativeWidget.WindowSize new_window_size)
+    {
+        if (window_size == new_window_size)
+            return;
+        window_size = new_window_size;
+        adaptative_children.@foreach ((adaptative_child) => adaptative_child.set_window_size 
(new_window_size));
+    }
+
+    /*\
+    * * manage style classes
+    \*/
+
+    private bool has_extra_small_window_class = false;
+    private bool has_small_window_class = false;
+    private bool has_large_window_class = false;
+
+    private void set_style_classes (bool extra_small_window, bool small_window, bool large_window)
+    {
+        // remove first
+        if (has_extra_small_window_class && !extra_small_window)
+            set_style_class ("extra-small-window", extra_small_window, ref has_extra_small_window_class);
+        if (has_small_window_class && !small_window)
+            set_style_class ("small-window", small_window, ref has_small_window_class);
+
+        if (large_window != has_large_window_class)
+            set_style_class ("large-window", large_window, ref has_large_window_class);
+        if (small_window != has_small_window_class)
+            set_style_class ("small-window", small_window, ref has_small_window_class);
+        if (extra_small_window != has_extra_small_window_class)
+            set_style_class ("extra-small-window", extra_small_window, ref has_extra_small_window_class);
+    }
+
+    private inline void set_style_class (string class_name, bool new_state, ref bool old_state)
+    {
+        old_state = new_state;
+        if (new_state)
+            context.add_class (class_name);
+        else
+            context.remove_class (class_name);
+    }
+
+    /*\
+    * * manage window state
+    \*/
+
+    private GLib.Settings settings = new GLib.Settings ("ca.desrt.dconf-editor.Settings");
+
+    private int window_width = 0;
+    private int window_height = 0;
+    private bool window_is_maximized = false;
+    private bool window_is_tiled = false;
+
+    private void load_window_state ()   // called on construct
+    {
+        if (settings.get_boolean ("window-is-maximized"))
+            maximize ();
+        set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
+    }
+
+    private void update_window_state () // called on size-allocate
+    {
+        if (window_is_maximized || window_is_tiled)
+            return;
+        int? _window_width = null;
+        int? _window_height = null;
+        get_size (out _window_width, out _window_height);
+        if (_window_width == null || _window_height == null)
+            return;
+        window_width = (!) _window_width;
+        window_height = (!) _window_height;
+    }
+
+    private void save_window_state ()   // called on destroy
+    {
+        settings.delay ();
+        if (window_width <= 630)    settings.set_int ("window-width", 630);
+        else                        settings.set_int ("window-width", window_width);
+        if (window_height <= 420)   settings.set_int ("window-height", 420);
+        else                        settings.set_int ("window-height", window_height);
+        settings.set_boolean ("window-is-maximized", window_is_maximized);
+        settings.apply ();
+    }
+
+    /*\
+    * * manage high-constrast
+    \*/
+
+    private void manage_high_contrast ()
+    {
+        Gtk.Settings? nullable_gtk_settings = Gtk.Settings.get_default ();
+        if (nullable_gtk_settings == null)
+            return;
+
+        Gtk.Settings gtk_settings = (!) nullable_gtk_settings;
+        gtk_settings.notify ["gtk-theme-name"].connect (update_highcontrast_state);
+        update_highcontrast_state (gtk_settings);
+    }
+
+    private bool highcontrast_state = false;
+    private void update_highcontrast_state (Object gtk_settings, ParamSpec? unused = null)
+    {
+        bool highcontrast_new_state = "HighContrast" in ((Gtk.Settings) gtk_settings).gtk_theme_name;
+        if (highcontrast_new_state == highcontrast_state)
+            return;
+        highcontrast_state = highcontrast_new_state;
+        if (highcontrast_state)
+            context.add_class ("hc-theme");
+        else
+            context.remove_class ("hc-theme");
+    }
+}
diff --git a/editor/browser-headerbar.vala b/editor/browser-headerbar.vala
index 0682461..4795392 100644
--- a/editor/browser-headerbar.vala
+++ b/editor/browser-headerbar.vala
@@ -35,32 +35,34 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
     internal signal void search_stopped ();
     internal signal void update_bookmarks_icons (Variant bookmarks_variant);
 
-    private bool phone_window = false;
+    private bool disable_popovers = false;
+    private bool disable_action_bar = false;
     private void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        bool _phone_window = AdaptativeWidget.WindowSize.is_phone (new_size);
-        if (phone_window != _phone_window)
+        bool _disable_popovers = AdaptativeWidget.WindowSize.is_phone_size (new_size)
+                              || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+        if (disable_popovers != _disable_popovers)
         {
-            phone_window = _phone_window;
-            if (phone_window)
+            disable_popovers = _disable_popovers;
+            if (_disable_popovers)
             {
                 bookmarks_button.active = false;
+
                 bookmarks_button.sensitive = false;
                 bookmarks_revealer.set_reveal_child (false);
             }
             else
             {
                 bookmarks_button.sensitive = true;
+                if (!in_window_modifications)
+                    bookmarks_button.show ();
                 bookmarks_revealer.set_reveal_child (true);
-                if (in_window_bookmarks)
-                    hide_in_window_bookmarks ();
-                else if (in_window_modifications)
-                    hide_in_window_modifications ();
-                else if (in_window_about)
-                    hide_in_window_about ();
             }
         }
 
+        disable_action_bar = _disable_popovers
+                          || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
+
         update_hamburger_menu (delay_mode);
         update_modifications_button ();
 
@@ -195,7 +197,7 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         bookmarks_stack.hexpand = false;    // hack 2/7
         title_stack.set_visible_child (path_widget);
         in_window_about = false;
-        if (phone_window)
+        if (disable_action_bar)
             modifications_separator.show ();
         info_button.show ();
     }
@@ -231,7 +233,7 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
 
     private void update_modifications_button ()
     {
-        if (phone_window)
+        if (disable_action_bar)
         {
             set_show_close_button (false);
             if (in_window_modifications)
@@ -281,6 +283,8 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         info_button.hide ();
         modifications_separator.hide ();
         show_modifications_button.hide ();
+        if (disable_action_bar && !disable_popovers)
+            bookmarks_button.hide ();
         modifications_actions_button.show ();
         hide_modifications_button.show ();
         bookmarks_stack.hexpand = false;    // hack 3/7
@@ -292,11 +296,13 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
     {
         hide_modifications_button.hide ();
         modifications_actions_button.hide ();
-        if (phone_window)
+        if (disable_action_bar)
         {
             show_modifications_button.show ();
             modifications_separator.show ();
         }
+        if (!disable_popovers)
+            bookmarks_button.show ();
         bookmarks_stack.hexpand = false;    // hack 4/7
         title_stack.set_visible_child (path_widget);
         in_window_modifications = false;
@@ -429,13 +435,13 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         }
         else if (current_type != ViewType.SEARCH) */
 
-        if (phone_window)
+        if (disable_popovers)
             append_bookmark_section (current_type, current_path, BookmarksList.get_bookmark_name 
(current_path, current_type) in get_bookmarks (), in_window_bookmarks, ref menu);
 
         if (!in_window_bookmarks)
             append_or_not_delay_mode_section (delay_mode, current_type == ViewType.FOLDER, current_path, ref 
menu);
 
-        append_app_actions_section (night_time, dark_theme, automatic_night_mode, phone_window, ref menu);
+        append_app_actions_section (night_time, dark_theme, automatic_night_mode, disable_popovers, ref 
menu);
 
         menu.freeze ();
         info_button.set_menu_model ((MenuModel) menu);
@@ -478,11 +484,11 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         menu.append_section (null, section);
     }
 
-    private static void append_app_actions_section (bool night_time, bool dark_theme, bool auto_night, bool 
phone_window, ref GLib.Menu menu)
+    private static void append_app_actions_section (bool night_time, bool dark_theme, bool auto_night, bool 
disable_popovers, ref GLib.Menu menu)
     {
         GLib.Menu section = new GLib.Menu ();
         append_or_not_night_mode_entry (night_time, dark_theme, auto_night, ref section);
-        if (!phone_window)    // TODO else...
+        if (!disable_popovers)    // TODO else...
             section.append (_("Keyboard Shortcuts"), "win.show-help-overlay");
         section.append (_("About Dconf Editor"), "ui.about");
         section.freeze ();
diff --git a/editor/browser-view.vala b/editor/browser-view.vala
index 5f22b7a..e9d6548 100644
--- a/editor/browser-view.vala
+++ b/editor/browser-view.vala
@@ -61,27 +61,12 @@ private class BrowserView : Stack, AdaptativeWidget
 
     internal bool small_keys_list_rows { set { current_child.small_keys_list_rows = value; }}
 
-    private bool phone_window = false;
     private void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
         current_child.set_window_size (new_size);
         bookmarks_list.set_window_size (new_size);
         modifications_list.set_window_size (new_size);
         about_list.set_window_size (new_size);
-
-        bool _phone_window = AdaptativeWidget.WindowSize.is_phone (new_size);
-        if (phone_window == _phone_window)
-            return;
-        if (phone_window)
-        {
-            if (in_window_bookmarks)
-                hide_in_window_bookmarks ();
-            else if (in_window_modifications)
-                hide_in_window_modifications ();
-            else if (in_window_about)
-                hide_in_window_about ();
-        }
-        phone_window = _phone_window;
     }
 
     private ModificationsHandler _modifications_handler;
diff --git a/editor/dconf-editor.gresource.xml b/editor/dconf-editor.gresource.xml
index 7473eec..8e39d9a 100644
--- a/editor/dconf-editor.gresource.xml
+++ b/editor/dconf-editor.gresource.xml
@@ -19,6 +19,7 @@
     <file preprocess="xml-stripblanks">large-pathbar.ui</file>
     <file preprocess="xml-stripblanks">large-pathbar-item.ui</file>
     <file preprocess="xml-stripblanks">modifications-revealer.ui</file>
+    <file preprocess="xml-stripblanks">notifications-revealer.ui</file>
     <file preprocess="xml-stripblanks">overlayed-list.ui</file>
     <file preprocess="xml-stripblanks">pathentry.ui</file>
     <file preprocess="xml-stripblanks">pathwidget.ui</file>
diff --git a/editor/dconf-editor.ui b/editor/dconf-editor.ui
index 576f1f0..5b87c72 100644
--- a/editor/dconf-editor.ui
+++ b/editor/dconf-editor.ui
@@ -1,19 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface domain="dconf-editor">
   <!-- interface-requires gtk+ 3.0 -->
-  <template class="DConfWindow" parent="GtkApplicationWindow">
+  <template class="DConfWindow" parent="AdaptativeWindow">
     <property name="visible">False</property>
     <property name="title" translatable="yes">dconf Editor</property>
     <property name="height-request">283</property> <!-- 294 max for Purism Librem 5 landscape -->
     <property name="width-request">349</property>  <!-- 360 max for Purism Librem 5 portrait -->
     <signal name="key-press-event" handler="on_key_press_event"/>
     <signal name="button-press-event" handler="on_button_press_event"/>
-    <signal name="window-state-event" handler="on_window_state_event"/>
-    <signal name="size-allocate" handler="on_size_allocate"/>
     <signal name="destroy" handler="on_destroy"/>
     <style>
       <class name="dconf-editor"/>
-      <class name="startup"/>
     </style>
     <child type="titlebar">
       <object class="BrowserHeaderBar" id="headerbar">
@@ -44,48 +41,8 @@
           </object>
         </child>
         <child type="overlay">
-          <object class="GtkRevealer" id="notification_revealer">
+          <object class="NotificationsRevealer" id="notifications_revealer">
             <property name="visible">True</property>
-            <property name="halign">center</property>
-            <property name="valign">start</property>
-            <child>
-              <object class="GtkFrame">
-                <property name="visible">True</property>
-                <style>
-                  <class name="app-notification"/>
-                </style>
-                <child>
-                  <object class="GtkGrid">
-                    <property name="visible">True</property>
-                    <property name="column-spacing">20</property>
-                    <child>
-                      <object class="GtkLabel" id="notification_label">
-                        <property name="visible">True</property>
-                        <property name="wrap">True</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkButton">
-                        <property name="visible">True</property>
-                        <property name="focus-on-click">False</property>
-                        <property name="relief">none</property>
-                        <property name="action-name">ui.hide-notification</property>
-                        <style>
-                          <class name="image-button"/>
-                        </style>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="icon-name">window-close-symbolic</property>
-                            <property name="icon-size">1</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
           </object>
         </child>
       </object>
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index b9fb8d3..0128ad9 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -85,49 +85,8 @@ internal enum ViewType {
     }
 }
 
-private interface AdaptativeWidget
-{ /*
-     ┏━━━━━━━┳━━━━━━━┳━━━━━──╴
-     ┃       ┃       ┃
-     ┃ phone ┃ phone ┃ extra
-     ┃ vert. ┃ hztl. ┃ flat
-     ┠╌╌╌╌╌╌╌╊━━━━━━━┻━━━━╾──╴
-     ┃       ┃
-     ┃       ┃
-     ┣━━━━━━━┫     usual
-     ┃ extra ┃     size
-     ╿ thin  │
-     ╵       ╵             */
-
-    internal enum WindowSize {
-        START_SIZE,
-        USUAL_SIZE,
-        PHONE_VERT,
-        PHONE_HZTL,
-        EXTRA_THIN,
-        EXTRA_FLAT;
-
-        internal static inline bool is_phone (WindowSize window_size)
-        {
-            return (window_size != USUAL_SIZE) && (window_size != EXTRA_FLAT);
-        }
-
-        internal static inline bool is_thin (WindowSize window_size)
-        {
-            return (window_size == PHONE_VERT) || (window_size == EXTRA_THIN);
-        }
-
-        internal static inline bool is_fun (WindowSize window_size)
-        {
-            return (window_size == PHONE_HZTL) || (window_size == EXTRA_FLAT);
-        }
-    }
-
-    internal abstract void set_window_size (WindowSize new_size);
-}
-
 [GtkTemplate (ui = "/ca/desrt/dconf-editor/ui/dconf-editor.ui")]
-private class DConfWindow : ApplicationWindow
+private class DConfWindow : AdaptativeWindow, AdaptativeWidget
 {
     private ViewType current_type = ViewType.FOLDER;
     private string current_path = "/";
@@ -138,11 +97,6 @@ private class DConfWindow : ApplicationWindow
     private SettingsModel model = new SettingsModel ();
     private ModificationsHandler modifications_handler;
 
-    private int window_width = 0;
-    private int window_height = 0;
-    private bool window_is_maximized = false;
-    private bool window_is_tiled = false;
-
     internal bool mouse_extra_buttons   { private get; set; default = true; }
     internal int mouse_back_button      { private get; set; default = 8; }
     internal int mouse_forward_button   { private get; set; default = 9; }
@@ -154,16 +108,25 @@ private class DConfWindow : ApplicationWindow
     [GtkChild] private BrowserView browser_view;
     [GtkChild] private ModificationsRevealer revealer;
 
-    [GtkChild] private Revealer notification_revealer;
-    [GtkChild] private Label notification_label;
+    [GtkChild] private NotificationsRevealer notifications_revealer;
 
     private ulong use_shortpaths_changed_handler = 0;
     private ulong behaviour_changed_handler = 0;
 
+    private ulong headerbar_update_bookmarks_icons_handler = 0;
+    private ulong browserview_update_bookmarks_icons_handler = 0;
+
+    private ulong delayed_changes_changed_handler = 0;
+
     private StyleContext context;
     construct
     {
         context = get_style_context ();
+
+        adaptative_children.append (headerbar);
+        adaptative_children.append (browser_view);
+        adaptative_children.append (revealer);
+        adaptative_children.append (this);
     }
 
     internal DConfWindow (bool disable_warning, string? schema, string? path, string? key_name, bool 
_night_time, bool _dark_theme, bool _automatic_night_mode)
@@ -174,8 +137,8 @@ private class DConfWindow : ApplicationWindow
         install_kbd_action_entries ();
         install_bmk_action_entries ();
 
-        headerbar.update_bookmarks_icons.connect (update_bookmarks_icons_from_variant);
-        browser_view.update_bookmarks_icons.connect (update_bookmarks_icons_from_variant);
+        headerbar_update_bookmarks_icons_handler = headerbar.update_bookmarks_icons.connect 
(update_bookmarks_icons_from_variant);
+        browserview_update_bookmarks_icons_handler = browser_view.update_bookmarks_icons.connect 
(update_bookmarks_icons_from_variant);
 
         use_shortpaths_changed_handler = settings.changed ["use-shortpaths"].connect_after (reload_view);
         settings.bind ("use-shortpaths", model, "use-shortpaths", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
@@ -183,8 +146,9 @@ private class DConfWindow : ApplicationWindow
         modifications_handler = new ModificationsHandler (model);
         revealer.modifications_handler = modifications_handler;
         browser_view.modifications_handler = modifications_handler;
-        modifications_handler.delayed_changes_changed.connect (() => {
-                if (!AdaptativeWidget.WindowSize.is_phone (window_size))
+        delayed_changes_changed_handler = modifications_handler.delayed_changes_changed.connect (() => {
+                if (!AdaptativeWidget.WindowSize.is_extra_thin (window_size)
+                 && !AdaptativeWidget.WindowSize.is_extra_flat (window_size))
                     return;
 
                 uint total_changes_count = modifications_handler.dconf_changes_count + 
modifications_handler.gsettings_changes_count;
@@ -204,10 +168,6 @@ private class DConfWindow : ApplicationWindow
         if (!disable_warning && settings.get_boolean ("show-warning"))
             show.connect (show_initial_warning);
 
-        if (settings.get_boolean ("window-is-maximized"))
-            maximize ();
-        set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
-
         set_css_styles ();
 
         settings.bind ("mouse-use-extra-buttons", this, "mouse-extra-buttons", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
@@ -303,13 +263,16 @@ private class DConfWindow : ApplicationWindow
             request_object (startup_path, ModelUtils.undefined_context_id, true, (!) schema);
         else
             request_object (startup_path, ModelUtils.undefined_context_id, true);
-
-        Timeout.add (300, () => { context.remove_class ("startup"); return Source.REMOVE; });
     }
 
+    ulong paths_changed_handler = 0;
+    ulong gkey_value_push_handler = 0;
+    ulong dkey_value_push_handler = 0;
+    ulong settings_user_paths_changed_handler = 0;
+    ulong settings_enabled_mappings_changed_handler = 0;
     private void prepare_model ()
     {
-        settings.changed ["relocatable-schemas-user-paths"].connect (() => {
+        settings_user_paths_changed_handler = settings.changed ["relocatable-schemas-user-paths"].connect 
(() => {
                 RelocatableSchemasEnabledMappings enabled_mappings_flags = 
(RelocatableSchemasEnabledMappings) settings.get_flags ("relocatable-schemas-enabled-mappings");
                 if (!(RelocatableSchemasEnabledMappings.USER in enabled_mappings_flags))
                     return;
@@ -320,7 +283,7 @@ private class DConfWindow : ApplicationWindow
                                                         RelocatableSchemasEnabledMappings.STARTUP  in 
enabled_mappings_flags,
                                                         settings.get_value 
("relocatable-schemas-user-paths"));
             });
-        settings.changed ["relocatable-schemas-enabled-mappings"].connect (() => {
+        settings_enabled_mappings_changed_handler = settings.changed 
["relocatable-schemas-enabled-mappings"].connect (() => {
                 RelocatableSchemasEnabledMappings enabled_mappings_flags = 
(RelocatableSchemasEnabledMappings) settings.get_flags ("relocatable-schemas-enabled-mappings");
                 model.refresh_relocatable_schema_paths (RelocatableSchemasEnabledMappings.USER     in 
enabled_mappings_flags,
                                                         RelocatableSchemasEnabledMappings.BUILT_IN in 
enabled_mappings_flags,
@@ -339,9 +302,9 @@ private class DConfWindow : ApplicationWindow
         settings.bind ("refresh-settings-schema-source", model, "refresh-source", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
         model.finalize_model ();
 
-        model.paths_changed.connect (on_paths_changed);
-        model.gkey_value_push.connect (propagate_gkey_value_push);
-        model.dkey_value_push.connect (propagate_dkey_value_push);
+        paths_changed_handler = model.paths_changed.connect (on_paths_changed);
+        gkey_value_push_handler = model.gkey_value_push.connect (propagate_gkey_value_push);
+        dkey_value_push_handler = model.dkey_value_push.connect (propagate_dkey_value_push);
     }
     private void on_paths_changed (SettingsModelCore _model, GenericSet<string> unused, bool 
internal_changes)
     {
@@ -388,29 +351,6 @@ private class DConfWindow : ApplicationWindow
         if (has_small_keys_list_rows_class)
             context.add_class ("small-keys-list-rows");
         browser_view.small_keys_list_rows = has_small_keys_list_rows_class;
-
-        Gtk.Settings? gtk_settings = Gtk.Settings.get_default ();
-        if (gtk_settings == null)
-            return;
-        ((!) gtk_settings).notify ["gtk-theme-name"].connect (update_highcontrast_state);
-        update_highcontrast_state ();
-    }
-
-    private bool highcontrast_state = false;
-    private void update_highcontrast_state ()
-    {
-        Gtk.Settings? gtk_settings = Gtk.Settings.get_default ();
-        if (gtk_settings == null)
-            return;
-
-        bool highcontrast_new_state = "HighContrast" in ((!) gtk_settings).gtk_theme_name;
-        if (highcontrast_new_state == highcontrast_state)
-            return;
-        highcontrast_state = highcontrast_new_state;
-        if (highcontrast_state)
-            context.add_class ("hc-theme");
-        else
-            context.remove_class ("hc-theme");
     }
 
     /*\
@@ -466,120 +406,6 @@ private class DConfWindow : ApplicationWindow
         dialog.destroy ();
     }
 
-    [GtkCallback]
-    private bool on_window_state_event (Widget widget, Gdk.EventWindowState event)
-    {
-        if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
-            window_is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
-        /* We don’t save this state, but track it for saving size allocation */
-        if ((event.changed_mask & Gdk.WindowState.TILED) != 0)
-            window_is_tiled = (event.new_window_state & Gdk.WindowState.TILED) != 0;
-
-        return false;
-    }
-
-    private AdaptativeWidget.WindowSize window_size = AdaptativeWidget.WindowSize.START_SIZE;
-    private void set_window_size (AdaptativeWidget.WindowSize new_window_size)
-        requires (new_window_size != AdaptativeWidget.WindowSize.START_SIZE)
-    {
-        if (window_size == new_window_size)
-            return;
-        window_size = new_window_size;
-        headerbar.set_window_size (new_window_size);
-        browser_view.set_window_size (new_window_size);
-        revealer.set_window_size (new_window_size);
-    }
-
-    private bool has_extra_small_window_class = false;
-    private bool has_small_window_class = false;
-    private bool has_large_window_class = false;
-    private void set_style_classes (bool extra_small_window, bool small_window, bool large_window)
-    {
-        // remove first
-        if (has_extra_small_window_class && !extra_small_window)
-            set_style_class ("extra-small-window", extra_small_window, ref has_extra_small_window_class);
-        if (has_small_window_class && !small_window)
-            set_style_class ("small-window", small_window, ref has_small_window_class);
-
-        if (large_window != has_large_window_class)
-            set_style_class ("large-window", large_window, ref has_large_window_class);
-        if (small_window != has_small_window_class)
-            set_style_class ("small-window", small_window, ref has_small_window_class);
-        if (extra_small_window != has_extra_small_window_class)
-            set_style_class ("extra-small-window", extra_small_window, ref has_extra_small_window_class);
-    }
-    private inline void set_style_class (string class_name, bool new_state, ref bool old_state)
-    {
-        old_state = new_state;
-        if (new_state)
-            context.add_class (class_name);
-        else
-            context.remove_class (class_name);
-    }
-
-    [GtkCallback]
-    private void on_size_allocate (Allocation allocation)
-    {
-        /* responsive design */
-
-        int height = allocation.height;
-        int width = allocation.width;
-        bool is_thin_window = width < 787;
-
-        if (width < 590)
-        {
-            if (height < 787)   set_window_size (AdaptativeWidget.WindowSize.PHONE_VERT);
-            else                set_window_size (AdaptativeWidget.WindowSize.EXTRA_THIN);
-        }
-        else if (height < 400)
-        {
-            if (is_thin_window) set_window_size (AdaptativeWidget.WindowSize.PHONE_HZTL);
-            else                set_window_size (AdaptativeWidget.WindowSize.EXTRA_FLAT);
-        }
-        else                    set_window_size (AdaptativeWidget.WindowSize.USUAL_SIZE);
-
-        if (width > MAX_ROW_WIDTH + 42)
-        {
-            set_style_classes (false, false, true);
-
-            notification_revealer.hexpand = false;
-            notification_revealer.halign = Align.CENTER;
-        }
-        else if (width < 590)
-        {
-            set_style_classes (true, true, false);
-
-            notification_revealer.hexpand = true;
-            notification_revealer.halign = Align.FILL;
-        }
-        else if (is_thin_window)
-        {
-            set_style_classes (false, true, false);
-
-            notification_revealer.hexpand = true;
-            notification_revealer.halign = Align.FILL;
-        }
-        else
-        {
-            set_style_classes (false, false, false);
-
-            notification_revealer.hexpand = false;
-            notification_revealer.halign = Align.CENTER;
-        }
-
-        /* save size */
-
-        if (window_is_maximized || window_is_tiled)
-            return;
-        int? _window_width = null;
-        int? _window_height = null;
-        get_size (out _window_width, out _window_height);
-        if (_window_width == null || _window_height == null)
-            return;
-        window_width = (!) _window_width;
-        window_height = (!) _window_height;
-    }
-
     internal bool quit_if_no_pending_changes ()
     {
         if (modifications_handler.has_pending_changes ())
@@ -602,6 +428,18 @@ private class DConfWindow : ApplicationWindow
     {
         ((ConfigurationEditor) get_application ()).clean_copy_notification ();
 
+        modifications_handler.disconnect (delayed_changes_changed_handler);
+
+        headerbar.disconnect (headerbar_update_bookmarks_icons_handler);
+        browser_view.disconnect (browserview_update_bookmarks_icons_handler);
+
+        settings.disconnect (settings_user_paths_changed_handler);
+        settings.disconnect (settings_enabled_mappings_changed_handler);
+
+        model.disconnect (paths_changed_handler);
+        model.disconnect (gkey_value_push_handler);
+        model.disconnect (dkey_value_push_handler);
+
         settings.disconnect (behaviour_changed_handler);
         settings.disconnect (use_shortpaths_changed_handler);
         settings.disconnect (small_keys_list_rows_handler);
@@ -609,11 +447,6 @@ private class DConfWindow : ApplicationWindow
         settings.delay ();
         settings.set_string ("saved-view", saved_view);
         settings.set_string ("saved-pathbar-path", headerbar.get_complete_path ());
-        if (window_width <= 630)    settings.set_int ("window-width", 630);
-        else                        settings.set_int ("window-width", window_width);
-        if (window_height <= 420)   settings.set_int ("window-height", 420);
-        else                        settings.set_int ("window-height", window_height);
-        settings.set_boolean ("window-is-maximized", window_is_maximized);
         settings.apply ();
 
         base.destroy ();
@@ -888,7 +721,8 @@ private class DConfWindow : ApplicationWindow
     }
     private void update_bookmark_icon (string bookmark, BookmarkIcon icon)
     {
-        if (AdaptativeWidget.WindowSize.is_phone (window_size))
+        if (AdaptativeWidget.WindowSize.is_extra_thin (window_size)
+         || AdaptativeWidget.WindowSize.is_extra_flat (window_size))
             browser_view.update_bookmark_icon (bookmark, icon);
         else
             headerbar.update_bookmark_icon (bookmark, icon);
@@ -988,12 +822,13 @@ private class DConfWindow : ApplicationWindow
 
     private void hide_notification (/* SimpleAction action, Variant? variant */)
     {
-        notification_revealer.set_reveal_child (false);
+        notifications_revealer.hide_notification ();
     }
 
     private void about_cb ()    // register as "win.about"?
     {
-        if (!AdaptativeWidget.WindowSize.is_phone (window_size))
+        if (!AdaptativeWidget.WindowSize.is_phone_size (window_size)
+         && !AdaptativeWidget.WindowSize.is_extra_thin (window_size))
         {   // TODO hide the dialog if visible
             string [] authors = AboutDialogInfos.authors;
             Gtk.show_about_dialog (this,
@@ -1021,6 +856,35 @@ private class DConfWindow : ApplicationWindow
         browser_view.show_in_window_about ();
     }
 
+    /*\
+    * * adaptative stuff that needs to be there
+    \*/
+
+    private bool disable_popovers = false;
+    private bool disable_action_bar = false;
+    private void set_window_size (AdaptativeWidget.WindowSize new_size)
+    {
+        bool _disable_popovers = AdaptativeWidget.WindowSize.is_phone_size (new_size)
+                              || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+        if (disable_popovers != _disable_popovers)
+        {
+            disable_popovers = _disable_popovers;
+            if (browser_view.in_window_bookmarks)
+                hide_in_window_bookmarks ();
+            else if (browser_view.in_window_about)
+                hide_in_window_about ();
+        }
+
+        bool _disable_action_bar = _disable_popovers
+                                || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
+        if (disable_action_bar != _disable_action_bar)
+        {
+            disable_action_bar = _disable_action_bar;
+            if (browser_view.in_window_modifications)
+                hide_in_window_modifications ();
+        }
+    }
+
     /*\
     * * bookmarks action entries
     \*/
@@ -1201,8 +1065,13 @@ private class DConfWindow : ApplicationWindow
     private void toggle_bookmark                        (/* SimpleAction action, Variant? variant */)
     {
         browser_view.discard_row_popover ();
-        if (!AdaptativeWidget.WindowSize.is_phone (window_size))
+        if (!AdaptativeWidget.WindowSize.is_phone_size (window_size)
+         && !AdaptativeWidget.WindowSize.is_extra_thin (window_size))
+        {
+            if (browser_view.in_window_modifications)
+                hide_in_window_modifications ();
             headerbar.click_bookmarks_button ();
+        }
         else if (browser_view.in_window_bookmarks)
             hide_in_window_bookmarks ();
         else
@@ -1316,7 +1185,8 @@ private class DConfWindow : ApplicationWindow
         if (!modifications_handler.get_current_delay_mode ())
             return;
 
-        if (!AdaptativeWidget.WindowSize.is_phone (window_size))
+        if (!AdaptativeWidget.WindowSize.is_extra_thin (window_size)
+         && !AdaptativeWidget.WindowSize.is_extra_flat (window_size))
             revealer.toggle_modifications_list ();
         else if (browser_view.in_window_modifications)
             hide_in_window_modifications ();
@@ -1872,8 +1742,7 @@ private class DConfWindow : ApplicationWindow
 
     private void show_notification (string notification)
     {
-        notification_label.set_text (notification);
-        notification_revealer.set_reveal_child (true);
+        notifications_revealer.show_notification (notification);
     }
 
     private void cannot_find_key (string full_name)
diff --git a/editor/key-list-box-row.vala b/editor/key-list-box-row.vala
index 1ff0b32..90fb310 100644
--- a/editor/key-list-box-row.vala
+++ b/editor/key-list-box-row.vala
@@ -222,12 +222,12 @@ private class KeyListBoxRow : ClickableListBoxRow, AdaptativeWidget
     private bool thin_window = false;
     internal void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        bool _thin_window = AdaptativeWidget.WindowSize.is_thin (new_size);
+        bool _thin_window = AdaptativeWidget.WindowSize.is_extra_thin (new_size);
         if (thin_window == _thin_window)
             return;
         thin_window = _thin_window;
 
-        if (thin_window)
+        if (_thin_window)
         {
             if (boolean_switch != null)
                 ((!) boolean_switch).hide ();
diff --git a/editor/meson.build b/editor/meson.build
index 66b7f91..f637d4e 100644
--- a/editor/meson.build
+++ b/editor/meson.build
@@ -68,6 +68,7 @@ install_data(
 sources = files(
   'about-list.vala',
   'adaptative-pathbar.vala',
+  'adaptative-window.vala',
   'bookmarks.vala',
   'bookmarks-controller.vala',
   'bookmarks-list.vala',
@@ -87,6 +88,7 @@ sources = files(
   'modifications-list.vala',
   'modifications-revealer.vala',
   'night-light-monitor.vala',
+  'notifications-revealer.vala',
   'overlayed-list.vala',
   'pathentry.vala',
   'pathwidget.vala',
@@ -121,6 +123,7 @@ resource_data = files(
   'large-pathbar.css',
   'large-pathbar.ui',
   'modifications-revealer.ui',
+  'notifications-revealer.ui',
   'overlayed-list.ui',
   'pathentry.ui',
   'pathwidget.ui',
diff --git a/editor/modifications-revealer.vala b/editor/modifications-revealer.vala
index 9265e23..67d18a7 100644
--- a/editor/modifications-revealer.vala
+++ b/editor/modifications-revealer.vala
@@ -31,13 +31,14 @@ private class ModificationsRevealer : Revealer, AdaptativeWidget
         }
     }
 
-    private bool phone_window = false;
+    private bool disable_action_bar = false;
     private void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        bool _phone_window = AdaptativeWidget.WindowSize.is_phone (new_size);
-        if (phone_window == _phone_window)
+        bool _disable_action_bar = AdaptativeWidget.WindowSize.is_extra_thin (new_size)
+                                || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
+        if (disable_action_bar == _disable_action_bar)
             return;
-        phone_window = _phone_window;
+        disable_action_bar = _disable_action_bar;
 
         update ();
     }
@@ -248,7 +249,7 @@ private class ModificationsRevealer : Revealer, AdaptativeWidget
 
     private void update ()
     {
-        if (phone_window)
+        if (disable_action_bar)
         {
             set_reveal_child (false);
             return;
diff --git a/editor/notifications-revealer.ui b/editor/notifications-revealer.ui
new file mode 100644
index 0000000..2e6bf26
--- /dev/null
+++ b/editor/notifications-revealer.ui
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="dconf-editor">
+  <!-- interface-requires gtk+ 3.0 -->
+  <template class="NotificationsRevealer" parent="GtkRevealer">
+    <property name="halign">center</property>
+    <property name="valign">start</property>
+    <child>
+      <object class="GtkFrame">
+        <property name="visible">True</property>
+        <style>
+          <class name="app-notification"/>
+        </style>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">True</property>
+            <property name="column-spacing">20</property>
+            <child>
+              <object class="GtkLabel" id="notification_label">
+                <property name="visible">True</property>
+                <property name="wrap">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="focus-on-click">False</property>
+                <property name="relief">none</property>
+                <property name="action-name">ui.hide-notification</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="icon-name">window-close-symbolic</property>
+                    <property name="icon-size">1</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/editor/notifications-revealer.vala b/editor/notifications-revealer.vala
new file mode 100644
index 0000000..78b7e79
--- /dev/null
+++ b/editor/notifications-revealer.vala
@@ -0,0 +1,55 @@
+/*
+  This file is part of Dconf Editor
+
+  Dconf Editor is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Dconf Editor 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Dconf Editor.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/ca/desrt/dconf-editor/ui/notifications-revealer.ui")]
+private class NotificationsRevealer : Revealer, AdaptativeWidget
+{
+    [GtkChild] private Label notification_label;
+
+    internal void show_notification (string notification)
+    {
+        notification_label.set_text (notification);
+        set_reveal_child (true);
+    }
+
+    internal void hide_notification ()
+    {
+        set_reveal_child (false);
+    }
+
+    private bool is_thin = false;
+    private void set_window_size (AdaptativeWidget.WindowSize new_size)
+    {
+        bool _is_thin = AdaptativeWidget.WindowSize.is_quite_thin (new_size);
+        if (is_thin == _is_thin)
+            return;
+        is_thin = _is_thin;
+
+        if (_is_thin)
+        {
+            hexpand = true;
+            halign = Align.FILL;
+        }
+        else
+        {
+            hexpand = false;
+            halign = Align.CENTER;
+        }
+    }
+}
diff --git a/editor/overlayed-list.vala b/editor/overlayed-list.vala
index 4d324be..a004a48 100644
--- a/editor/overlayed-list.vala
+++ b/editor/overlayed-list.vala
@@ -86,7 +86,7 @@ private abstract class OverlayedList : Overlay, AdaptativeWidget
     private StyleContext main_context;
     internal void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        if (!AdaptativeWidget.WindowSize.is_thin (new_size) && AdaptativeWidget.WindowSize.is_fun (new_size))
+        if (!AdaptativeWidget.WindowSize.is_extra_thin (new_size) && 
AdaptativeWidget.WindowSize.is_extra_flat (new_size))
         {
             main_context.remove_class ("vertical");
             edit_mode_box.halign = Align.END;
diff --git a/editor/pathentry.vala b/editor/pathentry.vala
index 23ffcb1..7b63470 100644
--- a/editor/pathentry.vala
+++ b/editor/pathentry.vala
@@ -34,12 +34,12 @@ private class PathEntry : Box, AdaptativeWidget
     private bool thin_window = false;
     private void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        bool _thin_window = AdaptativeWidget.WindowSize.is_thin (new_size);
-        if (_thin_window == thin_window)
+        bool _thin_window = AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+        if (thin_window == _thin_window)
             return;
         thin_window = _thin_window;
 
-        if (thin_window)
+        if (_thin_window)
         {
             search_entry.set_icon_from_pixbuf (EntryIconPosition.PRIMARY, null);
 
diff --git a/editor/pathwidget.vala b/editor/pathwidget.vala
index b6ede44..cb1fff0 100644
--- a/editor/pathwidget.vala
+++ b/editor/pathwidget.vala
@@ -42,11 +42,11 @@ private class PathWidget : Box, AdaptativeWidget
     {
         pathbar.set_window_size (new_size);
 
-        bool _thin_window = AdaptativeWidget.WindowSize.is_thin (new_size);
+        bool _thin_window = AdaptativeWidget.WindowSize.is_extra_thin (new_size);
         if (thin_window != _thin_window)
         {
             thin_window = _thin_window;
-            if (thin_window)
+            if (_thin_window)
             {
                 search_toggle.hide ();
                 search_button.show ();


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