[gthumb] sidebar: show the property views in expandable sections



commit c7273c06abb7a77eaae0c3ef1f1a299f17501174
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Tue Jan 8 17:23:42 2019 +0100

    sidebar: show the property views in expandable sections

 data/gschemas/org.gnome.gthumb.gschema.xml    |   3 +
 extensions/image_viewer/gth-image-histogram.c |  34 ++--
 extensions/map_view/gth-map-view.c            |  19 +-
 gthumb/gth-browser.c                          |  14 +-
 gthumb/gth-file-comment.c                     | 188 +++++++++++++++++++
 gthumb/gth-file-comment.h                     |  54 ++++++
 gthumb/gth-file-details.c                     |  18 +-
 gthumb/gth-file-properties.c                  | 140 +++-----------
 gthumb/gth-main-default-types.c               |   2 +
 gthumb/gth-multipage.c                        | 252 --------------------------
 gthumb/gth-multipage.h                        |  82 ---------
 gthumb/gth-preferences.h                      |   1 +
 gthumb/gth-sidebar-section.c                  | 202 +++++++++++++++++++++
 gthumb/gth-sidebar-section.h                  |  62 +++++++
 gthumb/gth-sidebar.c                          | 226 +++++++++++++++--------
 gthumb/gth-sidebar.h                          |  19 +-
 gthumb/meson.build                            |   5 +-
 17 files changed, 734 insertions(+), 587 deletions(-)
---
diff --git a/data/gschemas/org.gnome.gthumb.gschema.xml b/data/gschemas/org.gnome.gthumb.gschema.xml
index f1fdc66b..1db7334b 100644
--- a/data/gschemas/org.gnome.gthumb.gschema.xml
+++ b/data/gschemas/org.gnome.gthumb.gschema.xml
@@ -107,6 +107,9 @@
     <key name="sidebar-visible" type="b">
       <default>true</default>
     </key>
+    <key name="sidebar-sections" type="as">
+      <default>[]</default>
+    </key>
     <key name="properties-visible" type="b">
       <default>false</default>
     </key>
diff --git a/extensions/image_viewer/gth-image-histogram.c b/extensions/image_viewer/gth-image-histogram.c
index 54032167..79930346 100644
--- a/extensions/image_viewer/gth-image-histogram.c
+++ b/extensions/image_viewer/gth-image-histogram.c
@@ -31,7 +31,6 @@
 #define MIN_HISTOGRAM_HEIGHT 280
 
 
-static void gth_image_histogram_gth_multipage_child_interface_init (GthMultipageChildInterface *iface);
 static void gth_image_histogram_gth_property_view_interface_init (GthPropertyViewInterface *iface);
 
 
@@ -45,13 +44,11 @@ G_DEFINE_TYPE_WITH_CODE (GthImageHistogram,
                         gth_image_histogram,
                         GTK_TYPE_BOX,
                         G_ADD_PRIVATE (GthImageHistogram)
-                        G_IMPLEMENT_INTERFACE (GTH_TYPE_MULTIPAGE_CHILD,
-                                               gth_image_histogram_gth_multipage_child_interface_init)
                         G_IMPLEMENT_INTERFACE (GTH_TYPE_PROPERTY_VIEW,
                                                gth_image_histogram_gth_property_view_interface_init))
 
 
-static void
+static gboolean
 gth_image_histogram_real_set_file (GthPropertyView *base,
                                   GthFileData     *file_data)
 {
@@ -61,34 +58,32 @@ gth_image_histogram_real_set_file (GthPropertyView *base,
 
        if (file_data == NULL) {
                gth_histogram_calculate_for_image (self->priv->histogram, NULL);
-               return;
+               return FALSE;
        }
 
        browser = (GthBrowser *) gtk_widget_get_toplevel (GTK_WIDGET (base));
-       if (! gtk_widget_is_toplevel (GTK_WIDGET (browser))) {
-               gth_histogram_calculate_for_image (self->priv->histogram, NULL);
-               return;
-       }
+       if (! gtk_widget_is_toplevel (GTK_WIDGET (browser)))
+               return FALSE;
 
        viewer_page = gth_browser_get_viewer_page (browser);
-       if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page)) {
-               gth_histogram_calculate_for_image (self->priv->histogram, NULL);
-               return;
-       }
+       if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page))
+               return FALSE;
 
        gth_histogram_calculate_for_image (self->priv->histogram, gth_image_viewer_page_get_current_image 
(GTH_IMAGE_VIEWER_PAGE (viewer_page)));
+
+       return TRUE;
 }
 
 
 static const char *
-gth_image_histogram_real_get_name (GthMultipageChild *self)
+gth_image_histogram_real_get_name (GthPropertyView *self)
 {
        return _("Histogram");
 }
 
 
 static const char *
-gth_image_histogram_real_get_icon (GthMultipageChild *self)
+gth_image_histogram_real_get_icon (GthPropertyView *self)
 {
        return "histogram-symbolic";
 }
@@ -112,16 +107,10 @@ gth_image_histogram_class_init (GthImageHistogramClass *klass)
 
 
 static void
-gth_image_histogram_gth_multipage_child_interface_init (GthMultipageChildInterface *iface)
+gth_image_histogram_gth_property_view_interface_init (GthPropertyViewInterface *iface)
 {
        iface->get_name = gth_image_histogram_real_get_name;
        iface->get_icon = gth_image_histogram_real_get_icon;
-}
-
-
-static void
-gth_image_histogram_gth_property_view_interface_init (GthPropertyViewInterface *iface)
-{
        iface->set_file = gth_image_histogram_real_set_file;
 }
 
@@ -150,7 +139,6 @@ gth_image_histogram_init (GthImageHistogram *self)
 
        gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
        gtk_box_set_spacing (GTK_BOX (self), 6);
-       gtk_container_set_border_width (GTK_CONTAINER (self), 2);
 
        settings = g_settings_new (GTHUMB_IMAGE_VIEWER_SCHEMA);
 
diff --git a/extensions/map_view/gth-map-view.c b/extensions/map_view/gth-map-view.c
index 612f8ca2..a9b3f4c2 100644
--- a/extensions/map_view/gth-map-view.c
+++ b/extensions/map_view/gth-map-view.c
@@ -31,7 +31,6 @@
 #define LABEL_MAX_WIDTH 200
 
 
-static void gth_map_view_gth_multipage_child_interface_init (GthMultipageChildInterface *iface);
 static void gth_map_view_gth_property_view_interface_init (GthPropertyViewInterface *iface);
 
 
@@ -48,8 +47,6 @@ G_DEFINE_TYPE_WITH_CODE (GthMapView,
                         gth_map_view,
                         GTK_TYPE_BOX,
                         G_ADD_PRIVATE (GthMapView)
-                        G_IMPLEMENT_INTERFACE (GTH_TYPE_MULTIPAGE_CHILD,
-                                               gth_map_view_gth_multipage_child_interface_init)
                         G_IMPLEMENT_INTERFACE (GTH_TYPE_PROPERTY_VIEW,
                                                gth_map_view_gth_property_view_interface_init))
 
@@ -139,7 +136,7 @@ decimal_coordinates_to_string (double latitude,
 }
 
 
-static void
+static gboolean
 gth_map_view_real_set_file (GthPropertyView *base,
                            GthFileData     *file_data)
 {
@@ -197,18 +194,20 @@ gth_map_view_real_set_file (GthPropertyView *base,
                gtk_widget_hide (self->priv->embed);
                gtk_widget_show (self->priv->no_gps_label);
        }
+
+       return (coordinates_available == 2);
 }
 
 
 static const char *
-gth_map_view_real_get_name (GthMultipageChild *self)
+gth_map_view_real_get_name (GthPropertyView *self)
 {
        return _("Map");
 }
 
 
 static const char *
-gth_map_view_real_get_icon (GthMultipageChild *self)
+gth_map_view_real_get_icon (GthPropertyView *self)
 {
        return "map-symbolic";
 }
@@ -320,15 +319,9 @@ gth_map_view_init (GthMapView *self)
 
 
 static void
-gth_map_view_gth_multipage_child_interface_init (GthMultipageChildInterface *iface)
+gth_map_view_gth_property_view_interface_init (GthPropertyViewInterface *iface)
 {
        iface->get_name = gth_map_view_real_get_name;
        iface->get_icon = gth_map_view_real_get_icon;
-}
-
-
-static void
-gth_map_view_gth_property_view_interface_init (GthPropertyViewInterface *iface)
-{
        iface->set_file = gth_map_view_real_set_file;
 }
diff --git a/gthumb/gth-browser.c b/gthumb/gth-browser.c
index 8ead7f30..b37cb155 100644
--- a/gthumb/gth-browser.c
+++ b/gthumb/gth-browser.c
@@ -1986,8 +1986,9 @@ _gth_browser_close_final_step (gpointer user_data)
        last_window = g_list_length (gtk_application_get_windows (gtk_window_get_application (GTK_WINDOW 
(browser)))) == 1;
 
        if (gtk_widget_get_realized (GTK_WIDGET (browser))) {
-               gboolean       maximized;
-               GtkAllocation  allocation;
+               gboolean        maximized;
+               GtkAllocation   allocation;
+               char          **sidebar_sections;
 
                /* Save visualization options only if the window is not maximized. */
 
@@ -2022,6 +2023,10 @@ _gth_browser_close_final_step (gpointer user_data)
 
                g_settings_set_enum (browser->priv->browser_settings, PREF_FULLSCREEN_SIDEBAR, 
browser->priv->fullscreen_state.sidebar);
                g_settings_set_boolean (browser->priv->browser_settings, PREF_FULLSCREEN_THUMBNAILS_VISIBLE, 
browser->priv->fullscreen_state.thumbnail_list);
+
+               sidebar_sections = gth_sidebar_get_sections_status (GTH_SIDEBAR 
(browser->priv->file_properties));
+               g_settings_set_strv (browser->priv->browser_settings, PREF_BROWSER_SIDEBAR_SECTIONS, (const 
char **) sidebar_sections);
+               g_strfreev (sidebar_sections);
        }
 
        /**/
@@ -4255,6 +4260,7 @@ gth_browser_init (GthBrowser *browser)
 {
        int             window_width;
        int             window_height;
+       char          **sidebar_sections;
        GtkWidget      *vbox;
        GtkWidget      *scrolled_window;
        char           *general_filter;
@@ -4797,13 +4803,15 @@ gth_browser_init (GthBrowser *browser)
 
        /* the file property box */
 
-       browser->priv->file_properties = gth_sidebar_new ();
+       sidebar_sections = g_settings_get_strv (browser->priv->browser_settings, 
PREF_BROWSER_SIDEBAR_SECTIONS);
+       browser->priv->file_properties = gth_sidebar_new (sidebar_sections);
        gtk_widget_set_size_request (browser->priv->file_properties, -1, FILE_PROPERTIES_MINIMUM_HEIGHT);
        gtk_widget_hide (browser->priv->file_properties);
        gtk_paned_pack2 (GTK_PANED (_gth_browser_get_browser_file_properties_container (browser)),
                         browser->priv->file_properties,
                         ! browser->priv->file_properties_on_the_right,
                         FALSE);
+       g_strfreev (sidebar_sections);
 
        g_signal_connect (gth_sidebar_get_toolbox (GTH_SIDEBAR (browser->priv->file_properties)),
                          "options-visibility",
diff --git a/gthumb/gth-file-comment.c b/gthumb/gth-file-comment.c
new file mode 100644
index 00000000..0dc32c2c
--- /dev/null
+++ b/gthumb/gth-file-comment.c
@@ -0,0 +1,188 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "glib-utils.h"
+#include "gth-file-comment.h"
+#include "gth-main.h"
+#include "gth-sidebar.h"
+
+
+#define GTH_STYLE_CLASS_COMMENT "comment"
+
+
+struct _GthFileCommentPrivate {
+       GtkWidget   *comment_view;
+       GtkWidget   *comment_win;
+       GthFileData *last_file_data;
+};
+
+
+static void gth_file_comment_gth_property_view_interface_init (GthPropertyViewInterface *iface);
+
+
+G_DEFINE_TYPE_WITH_CODE (GthFileComment,
+                        gth_file_comment,
+                        GTK_TYPE_BOX,
+                        G_ADD_PRIVATE (GthFileComment)
+                        G_IMPLEMENT_INTERFACE (GTH_TYPE_PROPERTY_VIEW,
+                                               gth_file_comment_gth_property_view_interface_init))
+
+
+static char *
+get_comment (GthFileData *file_data)
+{
+       GString     *string;
+       GthMetadata *value;
+       gboolean     not_void = FALSE;
+
+       string = g_string_new (NULL);
+
+       value = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::description");
+       if (value != NULL) {
+               const char *formatted;
+
+               formatted = gth_metadata_get_formatted (value);
+               if ((formatted != NULL) && (*formatted != '\0')) {
+                       g_string_append (string, formatted);
+                       not_void = TRUE;
+               }
+       }
+
+       return g_string_free (string, ! not_void);
+}
+
+
+static gboolean
+gth_file_comment_real_set_file (GthPropertyView *base,
+                                  GthFileData     *file_data)
+{
+       GthFileComment *self;
+       GtkTextBuffer  *text_buffer;
+       char           *comment;
+
+       self = GTH_FILE_COMMENT (base);
+
+       if (file_data != self->priv->last_file_data) {
+               _g_object_unref (self->priv->last_file_data);
+               self->priv->last_file_data = gth_file_data_dup (file_data);
+       }
+
+       if (file_data == NULL) {
+               gtk_widget_hide (self->priv->comment_win);
+               return FALSE;
+       }
+
+       text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->priv->comment_view));
+       comment = get_comment (file_data);
+       if (comment != NULL) {
+               GtkTextIter    iter;
+               GtkAdjustment *vadj;
+
+               gtk_text_buffer_set_text (text_buffer, comment, strlen (comment));
+               gtk_text_buffer_get_iter_at_line (text_buffer, &iter, 0);
+               gtk_text_buffer_place_cursor (text_buffer, &iter);
+
+               vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->priv->comment_win));
+               gtk_adjustment_set_value (vadj, 0.0);
+
+               gtk_widget_show (self->priv->comment_win);
+
+               g_free (comment);
+       }
+       else
+               gtk_widget_hide (self->priv->comment_win);
+
+       return (comment != NULL);
+}
+
+
+static const char *
+gth_file_comment_real_get_name (GthPropertyView *self)
+{
+       return _("Comment");
+}
+
+
+static const char *
+gth_file_comment_real_get_icon (GthPropertyView *self)
+{
+       return "comment-symbolic";
+}
+
+
+static void
+gth_file_comment_finalize (GObject *base)
+{
+       GthFileComment *self;
+
+       self = (GthFileComment *) base;
+       _g_object_unref (self->priv->last_file_data);
+
+       G_OBJECT_CLASS (gth_file_comment_parent_class)->finalize (base);
+}
+
+
+
+static void
+gth_file_comment_class_init (GthFileCommentClass *klass)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = gth_file_comment_finalize;
+}
+
+
+static void
+gth_file_comment_init (GthFileComment *self)
+{
+       self->priv = gth_file_comment_get_instance_private (self);
+       self->priv->last_file_data = NULL;
+
+       gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+
+       self->priv->comment_win = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->comment_win), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_NEVER);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->priv->comment_win), 
GTK_SHADOW_ETCHED_IN);
+       //gtk_widget_set_size_request (self->priv->comment_win, -1, COMMENT_DEFAULT_HEIGHT);
+       gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->comment_win), 
GTH_STYLE_CLASS_COMMENT);
+       gtk_box_pack_start (GTK_BOX (self), self->priv->comment_win, TRUE, TRUE, 0);
+
+       self->priv->comment_view = gtk_text_view_new ();
+       gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->comment_view), 
GTH_STYLE_CLASS_COMMENT);
+       gtk_text_view_set_editable (GTK_TEXT_VIEW (self->priv->comment_view), FALSE);
+       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (self->priv->comment_view), GTK_WRAP_WORD);
+       gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (self->priv->comment_view), TRUE);
+
+       gtk_widget_show (self->priv->comment_view);
+       gtk_container_add (GTK_CONTAINER (self->priv->comment_win), self->priv->comment_view);
+}
+
+
+static void
+gth_file_comment_gth_property_view_interface_init (GthPropertyViewInterface *iface)
+{
+       iface->get_name = gth_file_comment_real_get_name;
+       iface->get_icon = gth_file_comment_real_get_icon;
+       iface->set_file = gth_file_comment_real_set_file;
+}
diff --git a/gthumb/gth-file-comment.h b/gthumb/gth-file-comment.h
new file mode 100644
index 00000000..d87562ee
--- /dev/null
+++ b/gthumb/gth-file-comment.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_FILE_COMMENT_H
+#define GTH_FILE_COMMENT_H
+
+#include <gtk/gtk.h>
+#include "gth-file-data.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_FILE_COMMENT            (gth_file_comment_get_type ())
+#define GTH_FILE_COMMENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_FILE_COMMENT, 
GthFileComment))
+#define GTH_FILE_COMMENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_FILE_COMMENT, 
GthFileCommentClass))
+#define GTH_IS_FILE_COMMENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_FILE_COMMENT))
+#define GTH_IS_FILE_COMMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_FILE_COMMENT))
+#define GTH_FILE_COMMENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_FILE_COMMENT, 
GthFileCommentClass))
+
+typedef struct _GthFileComment GthFileComment;
+typedef struct _GthFileCommentClass GthFileCommentClass;
+typedef struct _GthFileCommentPrivate GthFileCommentPrivate;
+
+struct _GthFileComment {
+       GtkBox parent_instance;
+       GthFileCommentPrivate *priv;
+};
+
+struct _GthFileCommentClass {
+       GtkBoxClass parent_class;
+};
+
+GType gth_file_comment_get_type (void);
+
+G_END_DECLS
+
+#endif /* GTH_FILE_COMMENT_H */
diff --git a/gthumb/gth-file-details.c b/gthumb/gth-file-details.c
index 541418ce..78c3fe40 100644
--- a/gthumb/gth-file-details.c
+++ b/gthumb/gth-file-details.c
@@ -23,37 +23,37 @@
 #include <glib/gi18n.h>
 #include "glib-utils.h"
 #include "gth-file-details.h"
-#include "gth-multipage.h"
 #include "gth-sidebar.h"
 
 
-static void gth_file_details_gth_multipage_child_interface_init (GthMultipageChildInterface *iface);
+static void gth_file_details_gth_property_view_interface_init (GthPropertyViewInterface *iface);
 
 
 G_DEFINE_TYPE_WITH_CODE (GthFileDetails,
                         gth_file_details,
                         GTH_TYPE_FILE_PROPERTIES,
-                        G_IMPLEMENT_INTERFACE (GTH_TYPE_MULTIPAGE_CHILD,
-                                               gth_file_details_gth_multipage_child_interface_init))
+                        G_IMPLEMENT_INTERFACE (GTH_TYPE_PROPERTY_VIEW,
+                                               gth_file_details_gth_property_view_interface_init))
 
 
 static const char *
-gth_file_details_real_get_name (GthMultipageChild *self)
+gth_file_details_real_get_name (GthPropertyView *self)
 {
-       return _("Details");
+       return _("Metadata");
 }
 
 
 static const char *
-gth_file_details_real_get_icon (GthMultipageChild *self)
+gth_file_details_real_get_icon (GthPropertyView *self)
 {
-       return "format-justify-fill-symbolic";
+       return "tag-symbolic";
 }
 
 
 static void
 gth_file_details_class_init (GthFileDetailsClass *klass)
 {
+       /* void */
 }
 
 
@@ -65,7 +65,7 @@ gth_file_details_init (GthFileDetails *self)
 
 
 static void
-gth_file_details_gth_multipage_child_interface_init (GthMultipageChildInterface *iface)
+gth_file_details_gth_property_view_interface_init (GthPropertyViewInterface *iface)
 {
        iface->get_name = gth_file_details_real_get_name;
        iface->get_icon = gth_file_details_real_get_icon;
diff --git a/gthumb/gth-file-properties.c b/gthumb/gth-file-properties.c
index 956e8eaf..bba658f9 100644
--- a/gthumb/gth-file-properties.c
+++ b/gthumb/gth-file-properties.c
@@ -24,7 +24,6 @@
 #include "glib-utils.h"
 #include "gth-file-properties.h"
 #include "gth-main.h"
-#include "gth-multipage.h"
 #include "gth-sidebar.h"
 #include "gth-string-list.h"
 #include "gth-time.h"
@@ -36,7 +35,6 @@
 #define COMMENT_DEFAULT_HEIGHT 100
 #define CATEGORY_SIZE 1000
 #define MAX_ATTRIBUTE_LENGTH 128
-#define GTH_STYLE_CLASS_COMMENT "comment"
 
 /* Properties */
 enum {
@@ -59,18 +57,15 @@ enum {
 
 
 struct _GthFilePropertiesPrivate {
+       GtkWidget     *main_container;
        GtkWidget     *tree_view;
-       GtkWidget     *comment_view;
-       GtkWidget     *comment_win;
        GtkListStore  *tree_model;
        GtkWidget     *popup_menu;
        gboolean       show_details;
-       gboolean       details_available;
        GthFileData   *last_file_data;
 };
 
 
-static void gth_file_properties_gth_multipage_child_interface_init (GthMultipageChildInterface *iface);
 static void gth_file_properties_gth_property_view_interface_init (GthPropertyViewInterface *iface);
 
 
@@ -78,46 +73,19 @@ G_DEFINE_TYPE_WITH_CODE (GthFileProperties,
                         gth_file_properties,
                         GTK_TYPE_BOX,
                         G_ADD_PRIVATE (GthFileProperties)
-                        G_IMPLEMENT_INTERFACE (GTH_TYPE_MULTIPAGE_CHILD,
-                                               gth_file_properties_gth_multipage_child_interface_init)
                         G_IMPLEMENT_INTERFACE (GTH_TYPE_PROPERTY_VIEW,
                                                gth_file_properties_gth_property_view_interface_init))
 
 
-static char *
-get_comment (GthFileData *file_data)
-{
-       GString     *string;
-       GthMetadata *value;
-       gboolean     not_void = FALSE;
-
-       string = g_string_new (NULL);
-
-       value = (GthMetadata *) g_file_info_get_attribute_object (file_data->info, "general::description");
-       if (value != NULL) {
-               const char *formatted;
-
-               formatted = gth_metadata_get_formatted (value);
-               if ((formatted != NULL) && (*formatted != '\0')) {
-                       g_string_append (string, formatted);
-                       not_void = TRUE;
-               }
-       }
-
-       return g_string_free (string, ! not_void);
-}
-
-
-static void
+static gboolean
 gth_file_properties_real_set_file (GthPropertyView *base,
                                   GthFileData     *file_data)
 {
        GthFileProperties *self;
+       gboolean           data_available;
        GHashTable        *category_hash;
        GList             *metadata_info;
        GList             *scan;
-       GtkTextBuffer     *text_buffer;
-       char              *comment;
 
        self = GTH_FILE_PROPERTIES (base);
 
@@ -129,13 +97,13 @@ gth_file_properties_real_set_file (GthPropertyView *base,
        gtk_list_store_clear (self->priv->tree_model);
 
        if (file_data == NULL) {
-               gtk_widget_hide (self->priv->comment_win);
-               return;
+               gtk_widget_hide (self->priv->main_container);
+               return FALSE;
        }
 
        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->priv->tree_model), 
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 0);
 
-       self->priv->details_available = FALSE;
+       data_available = FALSE;
        category_hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
        metadata_info = gth_main_get_all_metadata_info ();
        for (scan = metadata_info; scan; scan = scan->next) {
@@ -156,20 +124,20 @@ gth_file_properties_real_set_file (GthPropertyView *base,
 
                if (info->id != NULL) {
                        if (g_str_has_prefix (info->id, "Exif")) {
-                               self->priv->details_available = TRUE;
                                if (! self->priv->show_details)
                                        continue;
                        }
-                       if (g_str_has_prefix (info->id, "Iptc")) {
-                               self->priv->details_available = TRUE;
+                       else if (g_str_has_prefix (info->id, "Iptc")) {
                                if (! self->priv->show_details)
                                        continue;
                        }
-                       if (g_str_has_prefix (info->id, "Xmp")) {
-                               self->priv->details_available = TRUE;
+                       else if (g_str_has_prefix (info->id, "Xmp")) {
                                if (! self->priv->show_details)
                                        continue;
                        }
+                       else
+                               if (self->priv->show_details)
+                                       continue;
                }
 
                if (value != NULL) {
@@ -215,50 +183,30 @@ gth_file_properties_real_set_file (GthPropertyView *base,
 
                g_free (tooltip);
                g_free (value);
+
+               data_available = TRUE;
        }
        g_list_free (metadata_info);
 
        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->priv->tree_model), POS_COLUMN, 
GTK_SORT_ASCENDING);
        gtk_tree_view_expand_all (GTK_TREE_VIEW (self->priv->tree_view));
+       gtk_widget_set_visible (self->priv->main_container, data_available);
 
        g_hash_table_destroy (category_hash);
 
-       /* comment */
-
-       comment = NULL;
-       if (! self->priv->show_details) {
-               text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->priv->comment_view));
-               comment = get_comment (file_data);
-       }
-       if (comment != NULL) {
-               GtkTextIter    iter;
-               GtkAdjustment *vadj;
-
-               gtk_text_buffer_set_text (text_buffer, comment, strlen (comment));
-               gtk_text_buffer_get_iter_at_line (text_buffer, &iter, 0);
-               gtk_text_buffer_place_cursor (text_buffer, &iter);
-
-               vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->priv->comment_win));
-               gtk_adjustment_set_value (vadj, 0.0);
-
-               gtk_widget_show (self->priv->comment_win);
-
-               g_free (comment);
-       }
-       else
-               gtk_widget_hide (self->priv->comment_win);
+       return data_available;
 }
 
 
 static const char *
-gth_file_properties_real_get_name (GthMultipageChild *self)
+gth_file_properties_real_get_name (GthPropertyView *self)
 {
        return _("Properties");
 }
 
 
 static const char *
-gth_file_properties_real_get_icon (GthMultipageChild *self)
+gth_file_properties_real_get_icon (GthPropertyView *self)
 {
        return "document-properties-symbolic";
 }
@@ -287,7 +235,7 @@ gth_file_properties_set_property (GObject      *object,
 {
        GthFileProperties *self;
 
-        self = GTH_FILE_PROPERTIES (object);
+       self = GTH_FILE_PROPERTIES (object);
 
        switch (property_id) {
        case PROP_SHOW_DETAILS:
@@ -307,7 +255,7 @@ gth_file_properties_get_property (GObject    *object,
 {
        GthFileProperties *self;
 
-        self = GTH_FILE_PROPERTIES (object);
+       self = GTH_FILE_PROPERTIES (object);
 
        switch (property_id) {
        case PROP_SHOW_DETAILS:
@@ -408,15 +356,11 @@ tree_view_popup_menu_cb (GtkWidget *widget,
 static void
 gth_file_properties_init (GthFileProperties *self)
 {
-       GtkWidget         *vpaned;
-       GtkWidget         *properties_box;
        GtkWidget         *scrolled_win;
-       GtkWidget         *button_box;
        GtkWidget         *menu_item;
        GtkCellRenderer   *renderer;
        GtkTreeViewColumn *column;
 
-
        self->priv = gth_file_properties_get_instance_private (self);
        self->priv->show_details = FALSE;
        self->priv->last_file_data = NULL;
@@ -424,24 +368,12 @@ gth_file_properties_init (GthFileProperties *self)
        gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
        gtk_box_set_spacing (GTK_BOX (self), 6);
 
-       vpaned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
-       gtk_widget_show (vpaned);
-       gtk_box_pack_start (GTK_BOX (self), vpaned, TRUE, TRUE, 0);
-
-       properties_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-       gtk_widget_show (properties_box);
-       gtk_paned_pack1 (GTK_PANED (vpaned), properties_box, TRUE, FALSE);
-
-       scrolled_win = gtk_scrolled_window_new (NULL, NULL);
-       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
-       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_NONE);
+       self->priv->main_container = scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_NEVER);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
        gtk_widget_show (scrolled_win);
        gtk_widget_set_size_request (scrolled_win, -1, MIN_HEIGHT);
-       gtk_box_pack_start (GTK_BOX (properties_box), scrolled_win, TRUE, TRUE, 0);
-
-       button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-       gtk_widget_show (button_box);
-       gtk_box_pack_start (GTK_BOX (properties_box), button_box, FALSE, FALSE, 0);
+       gtk_box_pack_start (GTK_BOX (self), scrolled_win, TRUE, TRUE, 0);
 
        self->priv->tree_view = gtk_tree_view_new ();
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->priv->tree_view), FALSE);
@@ -519,37 +451,13 @@ gth_file_properties_init (GthFileProperties *self)
                                     column);
 
        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->priv->tree_model), POS_COLUMN, 
GTK_SORT_ASCENDING);
-
-       /* comment */
-
-       self->priv->comment_win = gtk_scrolled_window_new (NULL, NULL);
-       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->comment_win), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
-       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->priv->comment_win), 
GTK_SHADOW_ETCHED_IN);
-       gtk_widget_set_size_request (self->priv->comment_win, -1, COMMENT_DEFAULT_HEIGHT);
-       gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->comment_win), 
GTH_STYLE_CLASS_COMMENT);
-       gtk_paned_pack2 (GTK_PANED (vpaned), self->priv->comment_win, FALSE, FALSE);
-
-       self->priv->comment_view = gtk_text_view_new ();
-       gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->comment_view), 
GTH_STYLE_CLASS_COMMENT);
-       gtk_text_view_set_editable (GTK_TEXT_VIEW (self->priv->comment_view), FALSE);
-       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (self->priv->comment_view), GTK_WRAP_WORD);
-       gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (self->priv->comment_view), TRUE);
-
-       gtk_widget_show (self->priv->comment_view);
-       gtk_container_add (GTK_CONTAINER (self->priv->comment_win), self->priv->comment_view);
 }
 
 
 static void
-gth_file_properties_gth_multipage_child_interface_init (GthMultipageChildInterface *iface)
+gth_file_properties_gth_property_view_interface_init (GthPropertyViewInterface *iface)
 {
        iface->get_name = gth_file_properties_real_get_name;
        iface->get_icon = gth_file_properties_real_get_icon;
-}
-
-
-static void
-gth_file_properties_gth_property_view_interface_init (GthPropertyViewInterface *iface)
-{
        iface->set_file = gth_file_properties_real_set_file;
 }
diff --git a/gthumb/gth-main-default-types.c b/gthumb/gth-main-default-types.c
index f481f8a9..7109a79b 100644
--- a/gthumb/gth-main-default-types.c
+++ b/gthumb/gth-main-default-types.c
@@ -25,6 +25,7 @@
 #include "dlg-preferences-browser.h"
 #include "dlg-preferences-extensions.h"
 #include "dlg-preferences-general.h"
+#include "gth-file-comment.h"
 #include "gth-file-details.h"
 #include "gth-file-properties.h"
 #include "gth-main.h"
@@ -64,6 +65,7 @@ void
 gth_main_register_default_types (void)
 {
        gth_main_register_type ("file-properties", GTH_TYPE_FILE_PROPERTIES);
+       gth_main_register_type ("file-properties", GTH_TYPE_FILE_COMMENT);
        gth_main_register_type ("file-properties", GTH_TYPE_FILE_DETAILS);
        gth_main_register_default_file_loader ();
        gth_hook_add_callback ("dlg-preferences-construct", 1, G_CALLBACK 
(general__dlg_preferences_construct_cb), NULL);
diff --git a/gthumb/gth-preferences.h b/gthumb/gth-preferences.h
index 5b9ad355..ec7ae859 100644
--- a/gthumb/gth-preferences.h
+++ b/gthumb/gth-preferences.h
@@ -70,6 +70,7 @@ G_BEGIN_DECLS
 #define PREF_BROWSER_WINDOW_MAXIMIZED         "maximized"
 #define PREF_BROWSER_STATUSBAR_VISIBLE        "statusbar-visible"
 #define PREF_BROWSER_SIDEBAR_VISIBLE          "sidebar-visible"
+#define PREF_BROWSER_SIDEBAR_SECTIONS         "sidebar-sections"
 #define PREF_BROWSER_PROPERTIES_VISIBLE       "properties-visible"
 #define PREF_BROWSER_PROPERTIES_ON_THE_RIGHT  "properties-on-the-right"
 #define PREF_BROWSER_THUMBNAIL_LIST_VISIBLE   "thumbnail-list-visible"
diff --git a/gthumb/gth-sidebar-section.c b/gthumb/gth-sidebar-section.c
new file mode 100644
index 00000000..21bfa2d6
--- /dev/null
+++ b/gthumb/gth-sidebar-section.c
@@ -0,0 +1,202 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include "glib-utils.h"
+#include "gth-sidebar.h"
+#include "gth-sidebar-section.h"
+#include "gtk-utils.h"
+
+
+#define MIN_SECTION_WIDTH 365
+
+
+struct _GthSidebarSectionPrivate {
+       GtkWidget       *container;
+       GtkWidget       *expander;
+       GthPropertyView *view;
+       GthFileData     *file_data;
+       gboolean         dirty;
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (GthSidebarSection,
+                        gth_sidebar_section,
+                        GTK_TYPE_BOX,
+                        G_ADD_PRIVATE (GthSidebarSection))
+
+
+static void
+gth_sidebar_section_finalize (GObject *object)
+{
+       GthSidebarSection *self = GTH_SIDEBAR_SECTION (object);
+
+       _g_object_unref (self->priv->file_data);
+       G_OBJECT_CLASS (gth_sidebar_section_parent_class)->finalize (object);
+}
+
+
+static void
+gth_sidebar_section_class_init (GthSidebarSectionClass *klass)
+{
+       GObjectClass *object_class;
+
+       object_class = (GObjectClass *) klass;
+       object_class->finalize = gth_sidebar_section_finalize;
+}
+
+
+static void
+gth_sidebar_section_init (GthSidebarSection *self)
+{
+       self->priv = gth_sidebar_section_get_instance_private (self);
+       self->priv->container = NULL;
+       self->priv->expander = NULL;
+       self->priv->view = NULL;
+       self->priv->dirty = FALSE;
+}
+
+
+static void
+_gth_sidebar_section_update_view (GthSidebarSection *self)
+{
+       gboolean success;
+
+       success = gth_property_view_set_file (self->priv->view, self->priv->file_data);
+       gtk_widget_set_visible (GTK_WIDGET (self->priv->view), success);
+       gtk_widget_set_sensitive (self->priv->expander, success);
+       self->priv->dirty = FALSE;
+}
+
+
+static void
+expander_expanded_cb (GObject    *object,
+                     GParamSpec *param_spec,
+                     gpointer    user_data)
+{
+       GthSidebarSection *self = user_data;
+
+       if (gtk_expander_get_expanded (GTK_EXPANDER (self->priv->expander))) {
+               if (self->priv->dirty)
+                       _gth_sidebar_section_update_view (self);
+       }
+}
+
+
+static void
+_gth_sidebar_section_add_view (GthSidebarSection *self,
+                              GthPropertyView   *view)
+{
+       GtkWidget *exp;
+       GtkWidget *exp_label;
+       GtkWidget *icon;
+       GtkWidget *label;
+
+       gtk_widget_set_size_request (GTK_WIDGET (self), MIN_SECTION_WIDTH, -1);
+
+       self->priv->view = view;
+       _gtk_widget_set_margin (GTK_WIDGET (view), 3, 2, 3, 2);
+       gtk_widget_hide (GTK_WIDGET (view));
+
+       self->priv->expander = exp = gtk_expander_new (NULL);
+       exp_label = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
+       _gtk_widget_set_margin (GTK_WIDGET (exp_label), 3, 2, 3, 2);
+       icon = gtk_image_new_from_icon_name (gth_property_view_get_icon (view), GTK_ICON_SIZE_MENU);
+       label = gtk_label_new (gth_property_view_get_name (view));
+       gtk_box_pack_start (GTK_BOX (exp_label), icon, FALSE, FALSE, 0);
+       gtk_box_pack_start (GTK_BOX (exp_label), label, FALSE, FALSE, 0);
+
+       gtk_widget_show_all (exp_label);
+       gtk_expander_set_label_widget (GTK_EXPANDER (exp), exp_label);
+
+       gtk_expander_set_expanded (GTK_EXPANDER (exp), TRUE);
+       gtk_container_add (GTK_CONTAINER (exp), GTK_WIDGET (view));
+       gtk_widget_show (exp);
+
+       gtk_box_pack_start (GTK_BOX (self), exp, FALSE, FALSE, 0);
+
+       g_signal_connect (exp,
+                         "notify::expanded",
+                         G_CALLBACK (expander_expanded_cb),
+                         self);
+}
+
+
+GtkWidget *
+gth_sidebar_section_new (GthPropertyView *view)
+{
+       GthSidebarSection *sidebar;
+
+       sidebar = g_object_new (GTH_TYPE_SIDEBAR_SECTION,
+                               "orientation", GTK_ORIENTATION_VERTICAL,
+                               NULL);
+       _gth_sidebar_section_add_view (sidebar, view);
+
+       return (GtkWidget *) sidebar;
+}
+
+
+static gboolean
+_gth_sidebar_section_visible (GthSidebarSection *self)
+{
+       return gtk_expander_get_expanded (GTK_EXPANDER (self->priv->expander));
+}
+
+
+void
+gth_sidebar_section_set_file (GthSidebarSection  *self,
+                             GthFileData        *file_data)
+{
+       _g_object_unref (self->priv->file_data);
+       self->priv->file_data = NULL;
+       if (file_data != NULL)
+               self->priv->file_data = _g_object_ref (file_data);
+
+       if (! _gth_sidebar_section_visible (self)) {
+               self->priv->dirty = TRUE;
+               return;
+       }
+
+       _gth_sidebar_section_update_view (self);
+}
+
+
+void
+gth_sidebar_section_set_expanded (GthSidebarSection *self,
+                                 gboolean           expanded)
+{
+       gtk_expander_set_expanded (GTK_EXPANDER (self->priv->expander), expanded);
+}
+
+
+gboolean
+gth_sidebar_section_get_expanded (GthSidebarSection *self)
+{
+       return gtk_expander_get_expanded (GTK_EXPANDER (self->priv->expander));
+}
+
+
+const char *
+gth_sidebar_section_get_id (GthSidebarSection *self)
+{
+       return G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (self->priv->view));
+}
diff --git a/gthumb/gth-sidebar-section.h b/gthumb/gth-sidebar-section.h
new file mode 100644
index 00000000..938b26de
--- /dev/null
+++ b/gthumb/gth-sidebar-section.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_SIDEBAR_SECTION_H
+#define GTH_SIDEBAR_SECTION_H
+
+#include <gtk/gtk.h>
+#include "gth-file-data.h"
+#include "gth-sidebar.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_SIDEBAR_SECTION              (gth_sidebar_section_get_type ())
+#define GTH_SIDEBAR_SECTION(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_SIDEBAR_SECTION, 
GthSidebarSection))
+#define GTH_SIDEBAR_SECTION_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_SIDEBAR_SECTION, 
GthSidebarSectionClass))
+#define GTH_IS_SIDEBAR_SECTION(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_SIDEBAR_SECTION))
+#define GTH_IS_SIDEBAR_SECTION_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_SIDEBAR_SECTION))
+#define GTH_SIDEBAR_SECTION_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_SIDEBAR_SECTION, 
GthSidebarSectionClass))
+
+typedef struct _GthSidebarSection        GthSidebarSection;
+typedef struct _GthSidebarSectionClass   GthSidebarSectionClass;
+typedef struct _GthSidebarSectionPrivate GthSidebarSectionPrivate;
+
+struct _GthSidebarSection {
+       GtkBox __parent;
+       GthSidebarSectionPrivate *priv;
+};
+
+struct _GthSidebarSectionClass {
+       GtkBoxClass __parent_class;
+};
+
+GType          gth_sidebar_section_get_type    (void);
+GtkWidget *    gth_sidebar_section_new         (GthPropertyView        *view);
+void           gth_sidebar_section_set_file    (GthSidebarSection      *section,
+                                                GthFileData            *file_data);
+void           gth_sidebar_section_set_expanded(GthSidebarSection      *section,
+                                                gboolean                expanded);
+gboolean       gth_sidebar_section_get_expanded(GthSidebarSection      *section);
+const char *   gth_sidebar_section_get_id      (GthSidebarSection      *section);
+
+G_END_DECLS
+
+#endif /* GTH_SIDEBAR_SECTION_H */
diff --git a/gthumb/gth-sidebar.c b/gthumb/gth-sidebar.c
index 44ab0c5f..192daf0d 100644
--- a/gthumb/gth-sidebar.c
+++ b/gthumb/gth-sidebar.c
@@ -23,8 +23,8 @@
 #include <gtk/gtk.h>
 #include "glib-utils.h"
 #include "gth-main.h"
-#include "gth-multipage.h"
 #include "gth-sidebar.h"
+#include "gth-sidebar-section.h"
 #include "gth-toolbox.h"
 #include "gtk-utils.h"
 
@@ -36,8 +36,8 @@
 struct _GthSidebarPrivate {
        GtkWidget   *properties;
        GtkWidget   *toolbox;
-       gboolean    *dirty;
        GthFileData *file_data;
+       GList       *sections;
 };
 
 
@@ -52,120 +52,166 @@ gth_sidebar_finalize (GObject *object)
 {
        GthSidebar *sidebar = GTH_SIDEBAR (object);
 
-       g_free (sidebar->priv->dirty);
        _g_object_unref (sidebar->priv->file_data);
 
        G_OBJECT_CLASS (gth_sidebar_parent_class)->finalize (object);
 }
 
 
-static gboolean
-_gth_sidebar_properties_visible (GthSidebar *sidebar)
+static void
+gth_sidebar_class_init (GthSidebarClass *klass)
 {
-       return (gtk_widget_get_mapped (GTK_WIDGET (sidebar->priv->properties))
-               && (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (sidebar)), 
GTH_SIDEBAR_PAGE_PROPERTIES) == 0));
+       GObjectClass *object_class;
+
+       object_class = (GObjectClass *) klass;
+       object_class->finalize = gth_sidebar_finalize;
 }
 
 
 static void
-_gth_sidebar_update_current_child (GthSidebar *sidebar)
+gth_sidebar_init (GthSidebar *sidebar)
 {
-       int current;
+       sidebar->priv = gth_sidebar_get_instance_private (sidebar);
+       sidebar->priv->file_data = NULL;
+       sidebar->priv->sections = NULL;
+}
 
-       if (! _gth_sidebar_properties_visible (sidebar))
-               return;
 
-       current = gth_multipage_get_current (GTH_MULTIPAGE (sidebar->priv->properties));
-       if (sidebar->priv->dirty == NULL)
-               return;
+#define STATUS_DELIMITER ":"
+#define STATUS_EXPANDED  "expanded"
+#define STATUS_NOT_EXPANDED  "collapsed"
 
-       if (sidebar->priv->dirty[current]) {
-               GList     *children;
-               GtkWidget *current_child;
 
-               children = gth_multipage_get_children (GTH_MULTIPAGE (sidebar->priv->properties));
-               current_child = g_list_nth_data (children, current);
+typedef struct {
+       char     *id;
+       gboolean  expanded;
+} SectionStatus;
 
-               sidebar->priv->dirty[current] = FALSE;
-               gth_property_view_set_file (GTH_PROPERTY_VIEW (current_child), sidebar->priv->file_data);
-       }
+
+static SectionStatus *
+section_status_new (const char *str)
+{
+       SectionStatus  *status;
+       char          **strv;
+
+       status = g_new (SectionStatus, 1);
+       status->id = NULL;
+       status->expanded = TRUE;
+
+       strv = g_strsplit (str, STATUS_DELIMITER, 2);
+       if (strv[0] != NULL)
+               status->id = g_strdup (strv[0]);
+       if (strv[1] != NULL)
+               status->expanded = (strcmp (strv[1], STATUS_EXPANDED) == 0);
+       g_strfreev (strv);
+
+       return status;
 }
 
 
 static void
-gth_sidebar_class_init (GthSidebarClass *klass)
+section_status_free (SectionStatus *status)
 {
-       GObjectClass *object_class;
-
-       object_class = (GObjectClass *) klass;
-       object_class->finalize = gth_sidebar_finalize;
+       g_free (status->id);
+       g_free (status);
 }
 
 
-static void
-gth_sidebar_init (GthSidebar *sidebar)
+static GHashTable *
+create_section_status_hash (char **sections_status)
 {
-       sidebar->priv = gth_sidebar_get_instance_private (sidebar);
-       sidebar->priv->dirty = NULL;
-       sidebar->priv->file_data = NULL;
+       GHashTable *status_hash;
+       int         i;
+
+       status_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) 
section_status_free);
+       if (sections_status == NULL)
+               return status_hash;
+
+       for (i = 0; sections_status[i] != NULL; i++) {
+               SectionStatus *status = section_status_new (sections_status[i]);
+               if (status != NULL)
+                       g_hash_table_insert (status_hash, status->id, status);
+       }
+
+       return status_hash;
 }
 
 
 static void
-_gth_sidebar_add_property_views (GthSidebar *sidebar)
+_gth_sidebar_add_sections (GthSidebar  *sidebar,
+                          char       **sections_status)
 {
-       GArray *children;
-       int     i;
+       GArray     *children;
+       GHashTable *status_hash;
+       int         i;
 
        children = gth_main_get_type_set ("file-properties");
        if (children == NULL)
                return;
 
+       status_hash = create_section_status_hash (sections_status);
        for (i = 0; i < children->len; i++) {
-               GType      child_type;
-               GtkWidget *child;
+               GType          child_type;
+               GtkWidget     *child;
+               GtkWidget     *section;
+               SectionStatus *status;
 
                child_type = g_array_index (children, GType, i);
                child = g_object_new (child_type, NULL);
-               gth_multipage_add_child (GTH_MULTIPAGE (sidebar->priv->properties), GTH_MULTIPAGE_CHILD 
(child));
+               g_return_if_fail (GTH_IS_PROPERTY_VIEW (child));
+
+               section = gth_sidebar_section_new (GTH_PROPERTY_VIEW (child));
+               status = g_hash_table_lookup (status_hash, gth_sidebar_section_get_id (GTH_SIDEBAR_SECTION 
(section)));
+               if (status != NULL) {
+                       gth_sidebar_section_set_expanded (GTH_SIDEBAR_SECTION (section), status->expanded);
+               }
+               gtk_widget_show (section);
+
+               sidebar->priv->sections = g_list_prepend (sidebar->priv->sections, section);
+               gtk_box_pack_start (GTK_BOX (sidebar->priv->properties),
+                                   section,
+                                   FALSE,
+                                   FALSE,
+                                   0);
        }
-       gth_multipage_set_current (GTH_MULTIPAGE (sidebar->priv->properties), 0);
+       g_hash_table_unref (status_hash);
+
+       sidebar->priv->sections = g_list_reverse (sidebar->priv->sections);
 }
 
 
 static void
 _gth_sidebar_construct (GthSidebar *sidebar)
 {
-       sidebar->priv->properties = gth_multipage_new ();
-       gtk_style_context_add_class (gtk_widget_get_style_context (sidebar->priv->properties), 
GTK_STYLE_CLASS_SIDEBAR);
-       gtk_widget_set_vexpand (sidebar->priv->properties, TRUE);
-       gtk_widget_show (sidebar->priv->properties);
-       gtk_stack_add_named (GTK_STACK (sidebar), sidebar->priv->properties, GTH_SIDEBAR_PAGE_PROPERTIES);
+       GtkWidget *properties_win;
 
-       g_signal_connect_swapped (sidebar->priv->properties,
-                                 "map",
-                                 G_CALLBACK (_gth_sidebar_update_current_child),
-                                 sidebar);
-       g_signal_connect_swapped (sidebar->priv->properties,
-                                 "changed",
-                                 G_CALLBACK (_gth_sidebar_update_current_child),
-                                 sidebar);
+       properties_win = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (properties_win), GTK_POLICY_NEVER, 
GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (properties_win), GTK_SHADOW_NONE);
+       gtk_widget_set_vexpand (properties_win, TRUE);
+       gtk_widget_show (properties_win);
+       gtk_stack_add_named (GTK_STACK (sidebar), properties_win, GTH_SIDEBAR_PAGE_PROPERTIES);
+
+       sidebar->priv->properties = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_widget_show (sidebar->priv->properties);
+       gtk_container_add (GTK_CONTAINER (properties_win), sidebar->priv->properties);
 
        sidebar->priv->toolbox = gth_toolbox_new ("file-tools");
        gtk_style_context_add_class (gtk_widget_get_style_context (sidebar->priv->toolbox), 
GTK_STYLE_CLASS_SIDEBAR);
+       gtk_widget_set_vexpand (sidebar->priv->toolbox, TRUE);
        gtk_widget_show (sidebar->priv->toolbox);
        gtk_stack_add_named (GTK_STACK (sidebar), sidebar->priv->toolbox, GTH_SIDEBAR_PAGE_TOOLS);
 }
 
 
 GtkWidget *
-gth_sidebar_new (void)
+gth_sidebar_new (char **sections_status)
 {
        GthSidebar *sidebar;
 
        sidebar = g_object_new (GTH_TYPE_SIDEBAR, NULL);
        _gth_sidebar_construct (sidebar);
-       _gth_sidebar_add_property_views (sidebar);
+       _gth_sidebar_add_sections (sidebar, sections_status);
 
        return (GtkWidget *) sidebar;
 }
@@ -182,38 +228,17 @@ void
 gth_sidebar_set_file (GthSidebar  *sidebar,
                      GthFileData *file_data)
 {
-       GList *children;
-       int    current;
        GList *scan;
-       int    i;
 
        if ((file_data == NULL) || ! g_file_info_get_attribute_boolean (file_data->info, 
"gth::file::is-modified"))
                gth_toolbox_deactivate_tool (GTH_TOOLBOX (sidebar->priv->toolbox));
 
-       children = gth_multipage_get_children (GTH_MULTIPAGE (sidebar->priv->properties));
-       current = gth_multipage_get_current (GTH_MULTIPAGE (sidebar->priv->properties));
-
        _g_object_unref (sidebar->priv->file_data);
        sidebar->priv->file_data = gth_file_data_dup (file_data);
 
-       g_free (sidebar->priv->dirty);
-       sidebar->priv->dirty = g_new0 (gboolean, g_list_length (children));
-
-       for (scan = children, i = 0; scan; scan = scan->next, i++) {
+       for (scan = sidebar->priv->sections; scan; scan = scan->next) {
                GtkWidget *child = scan->data;
-
-               if (! GTH_IS_PROPERTY_VIEW (child)) {
-                       sidebar->priv->dirty[i] = FALSE;
-                       continue;
-               }
-
-               if (! _gth_sidebar_properties_visible (sidebar) || (i != current)) {
-                       sidebar->priv->dirty[i] = TRUE;
-                       continue;
-               }
-
-               sidebar->priv->dirty[i] = FALSE;
-               gth_property_view_set_file (GTH_PROPERTY_VIEW (child), sidebar->priv->file_data);
+               gth_sidebar_section_set_file (GTH_SIDEBAR_SECTION (child), sidebar->priv->file_data);
        }
 }
 
@@ -253,6 +278,35 @@ gth_sidebar_update_sensitivity (GthSidebar *sidebar)
 }
 
 
+char **
+gth_sidebar_get_sections_status (GthSidebar *sidebar)
+{
+       GList  *status_list;
+       GList  *scan;
+       char  **result;
+
+       status_list = NULL;
+       for (scan = sidebar->priv->sections; scan; scan = scan->next) {
+               GtkWidget *section = scan->data;
+               GString   *status;
+
+               status = g_string_new ("");
+               g_string_append (status, gth_sidebar_section_get_id (GTH_SIDEBAR_SECTION (section)));
+               g_string_append (status, STATUS_DELIMITER);
+               g_string_append (status, gth_sidebar_section_get_expanded (GTH_SIDEBAR_SECTION (section)) ? 
STATUS_EXPANDED : STATUS_NOT_EXPANDED);
+               status_list = g_list_prepend (status_list, status->str);
+
+               g_string_free (status, FALSE);
+       }
+       status_list = g_list_reverse (status_list);
+
+       result = _g_string_list_to_strv (status_list);
+       _g_string_list_free (status_list);
+
+       return result;
+}
+
+
 /* -- gth_property_view -- */
 
 
@@ -266,9 +320,23 @@ gth_property_view_default_init (GthPropertyViewInterface *iface)
 }
 
 
-void
+const char *
+gth_property_view_get_name (GthPropertyView *self)
+{
+       return GTH_PROPERTY_VIEW_GET_INTERFACE (self)->get_name (self);
+}
+
+
+const char *
+gth_property_view_get_icon (GthPropertyView *self)
+{
+       return GTH_PROPERTY_VIEW_GET_INTERFACE (self)->get_icon (self);
+}
+
+
+gboolean
 gth_property_view_set_file (GthPropertyView *self,
                            GthFileData     *file_data)
 {
-       GTH_PROPERTY_VIEW_GET_INTERFACE (self)->set_file (self, file_data);
+       return GTH_PROPERTY_VIEW_GET_INTERFACE (self)->set_file (self, file_data);
 }
diff --git a/gthumb/gth-sidebar.h b/gthumb/gth-sidebar.h
index b47a5c18..8a63e613 100644
--- a/gthumb/gth-sidebar.h
+++ b/gthumb/gth-sidebar.h
@@ -49,14 +49,12 @@ typedef struct _GthSidebar        GthSidebar;
 typedef struct _GthSidebarClass   GthSidebarClass;
 typedef struct _GthSidebarPrivate GthSidebarPrivate;
 
-struct _GthSidebar
-{
+struct _GthSidebar {
        GtkStack __parent;
        GthSidebarPrivate *priv;
 };
 
-struct _GthSidebarClass
-{
+struct _GthSidebarClass {
        GtkStackClass __parent_class;
 };
 
@@ -65,12 +63,14 @@ typedef struct _GthPropertyViewInterface GthPropertyViewInterface;
 
 struct _GthPropertyViewInterface {
        GTypeInterface parent_iface;
-       void  (*set_file)  (GthPropertyView *self,
-                           GthFileData     *file_data);
+       const char *    (*get_name)     (GthPropertyView *self);
+       const char *    (*get_icon)     (GthPropertyView *self);
+       gboolean        (*set_file)     (GthPropertyView *self,
+                                        GthFileData     *file_data);
 };
 
 GType          gth_sidebar_get_type            (void);
-GtkWidget *    gth_sidebar_new                 (void);
+GtkWidget *    gth_sidebar_new                 (char           **sections_status);
 GtkWidget *    gth_sidebar_get_toolbox         (GthSidebar      *sidebar);
 void           gth_sidebar_set_file            (GthSidebar      *sidebar,
                                                GthFileData     *file_data);
@@ -79,9 +79,12 @@ void           gth_sidebar_show_tools          (GthSidebar      *sidebar);
 gboolean       gth_sidebar_tool_is_active      (GthSidebar      *sidebar);
 void           gth_sidebar_deactivate_tool     (GthSidebar      *sidebar);
 void           gth_sidebar_update_sensitivity  (GthSidebar      *sidebar);
+char **        gth_sidebar_get_sections_status (GthSidebar      *sidebar);
 
 GType          gth_property_view_get_type      (void);
-void           gth_property_view_set_file      (GthPropertyView *self,
+const char *   gth_property_view_get_name      (GthPropertyView *self);
+const char *   gth_property_view_get_icon      (GthPropertyView *self);
+gboolean       gth_property_view_set_file      (GthPropertyView *self,
                                                GthFileData     *file_data);
 
 G_END_DECLS
diff --git a/gthumb/meson.build b/gthumb/meson.build
index 08a69230..d0eb5b70 100644
--- a/gthumb/meson.build
+++ b/gthumb/meson.build
@@ -70,7 +70,6 @@ public_header_files = [
   'gth-metadata-chooser.h',
   'gth-metadata-provider.h',
   'gth-monitor.h',
-  'gth-multipage.h',
   'gth-overwrite-dialog.h',
   'gth-paned.h',
   'gth-preferences.h',
@@ -80,6 +79,7 @@ public_header_files = [
   'gth-save-image-task.h',
   'gth-screensaver.h',
   'gth-sidebar.h',
+  'gth-sidebar-section.h',
   'gth-statusbar.h',
   'gth-source-tree.h',
   'gth-string-list.h',
@@ -178,6 +178,7 @@ source_files = files(
   'gth-error.c',
   'gth-extensions.c',
   'gth-file-chooser-dialog.c',
+  'gth-file-comment.c',
   'gth-file-data.c',
   'gth-file-details.c',
   'gth-file-list.c',
@@ -231,7 +232,6 @@ source_files = files(
   'gth-metadata-provider.c',
   'gth-metadata-provider-file.c',
   'gth-monitor.c',
-  'gth-multipage.c',
   'gth-overwrite-dialog.c',
   'gth-paned.c',
   'gth-preferences.c',
@@ -241,6 +241,7 @@ source_files = files(
   'gth-save-image-task.c',
   'gth-screensaver.c',
   'gth-sidebar.c',
+  'gth-sidebar-section.c',
   'gth-source-tree.c',
   'gth-statusbar.c',
   'gth-string-list.c',


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