/*
 * Copyright (c) 2009, 2010 Intel, Inc.
 * Copyright (c) 2010 Red Hat, Inc.
 * Copyright (c) 2016 Endless, Inc.
 *
 * The Control Center 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.
 *
 * The Control Center 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 the Control Center; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Author: Thomas Wood <thos@gnome.org>
 */

#define G_LOG_DOMAIN "cc-window"

#include <config.h>

#include "cc-log.h"
#include "cc-window.h"

#include <gio/gdesktopappinfo.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <string.h>
#include <time.h>

#include "cc-application.h"
#include "cc-panel-list.h"
#include "cc-panel-loader.h"
#include "cc-panel.h"
#include "cc-shell-model.h"
#include "cc-util.h"

#define MOUSE_BACK_BUTTON 8

#define DEFAULT_WINDOW_ICON_NAME "gnome-control-center"

struct _CcWindow {
    AdwApplicationWindow parent;

    AdwBreakpoint *break_point;

    AdwNavigationSplitView *split_view;
    CcPanelList *panel_list;
    GtkSearchBar *search_bar;
    GtkSearchEntry *search_entry;

    GtkWidget *old_panel;
    GtkWidget *current_panel;
    char *current_panel_id;
    GQueue *previous_panels;
    gboolean single_panel_mode;

    GtkWidget *custom_titlebar;

    CcShellModel *store;

    CcPanel *active_panel;
    GSettings *settings;

    CcPanelListView previous_list_view;
};

G_DEFINE_FINAL_TYPE (CcWindow, cc_window, ADW_TYPE_APPLICATION_WINDOW)

enum {
    PROP_0,
    PROP_ACTIVE_PANEL,
    PROP_MODEL,
    PROP_COLLAPSED,
};

/* Auxiliary methods */
static void
load_window_state (CcWindow *self)
{
    gint current_width = -1;
    gint current_height = -1;
    gboolean maximized = FALSE;

    g_settings_get (self->settings, "window-state", "(iib)", &current_width, &current_height, &maximized);

    if (current_width != -1 && current_height != -1)
        gtk_window_set_default_size (GTK_WINDOW (self), current_width, current_height);
    if (maximized)
        gtk_window_maximize (GTK_WINDOW (self));
}

static gboolean
activate_panel (CcWindow *self, const gchar *id, GVariant *parameters, const gchar *name, GIcon *gicon,
                CcPanelVisibility visibility)
{
    g_autoptr(GTimer) timer = NULL;
    gdouble elapsed_time;

    CC_ENTRY;

    if (!id)
        CC_RETURN (FALSE);

    if (visibility == CC_PANEL_HIDDEN)
        CC_RETURN (FALSE);

    timer = g_timer_new ();

    /* Begin the profile */
    g_timer_start (timer);

    if (self->current_panel)
        g_signal_handlers_disconnect_by_data (self->current_panel, self);
    self->current_panel = GTK_WIDGET (cc_panel_loader_load_by_name (CC_WINDOW (self), id, name, parameters));
    cc_window_set_active_panel (CC_WINDOW (self), CC_PANEL (self->current_panel));

    adw_navigation_split_view_set_content (self->split_view, ADW_NAVIGATION_PAGE (self->current_panel));

    /* Finish profiling */
    g_timer_stop (timer);

    elapsed_time = g_timer_elapsed (timer, NULL);

    g_debug ("Time to open panel '%s': %lfs", name, elapsed_time);

    g_settings_set_string (self->settings, "last-panel", id);

    CC_RETURN (TRUE);
}

static void
add_current_panel_to_history (CcWindow *self, const char *start_id)
{
    g_return_if_fail (start_id != NULL);

    if (!self->current_panel_id || g_strcmp0 (self->current_panel_id, start_id) == 0)
        return;

    g_queue_push_head (self->previous_panels, g_strdup (self->current_panel_id));
    g_debug ("Added '%s' to the previous panels", self->current_panel_id);
}

static gboolean
find_iter_for_panel_id (CcWindow *self, const gchar *panel_id, GtkTreeIter *out_iter)
{
    GtkTreeIter iter;
    gboolean valid;

    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->store), &iter);

    while (valid) {
        g_autofree gchar *id = NULL;

        gtk_tree_model_get (GTK_TREE_MODEL (self->store), &iter, COL_ID, &id, -1);

        if (g_strcmp0 (id, panel_id) == 0)
            break;

        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->store), &iter);
    }

    g_assert (out_iter != NULL);
    *out_iter = iter;

    return valid;
}

static void
on_row_changed_cb (CcWindow *self, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model)
{
    g_autofree gchar *id = NULL;
    CcPanelVisibility visibility;

    gtk_tree_model_get (model, iter, COL_ID, &id, COL_VISIBILITY, &visibility, -1);

    cc_panel_list_set_panel_visibility (self->panel_list, id, visibility);
}

static void
setup_model (CcWindow *self)
{
    GtkTreeModel *model;
    GtkTreeIter iter;
    gboolean valid;

    /* CcApplication must have a valid model at this point */
    g_assert (self->store != NULL);

    model = GTK_TREE_MODEL (self->store);

    cc_panel_loader_fill_model (self->store);

    /* Create a row for each panel */
    valid = gtk_tree_model_get_iter_first (model, &iter);

    while (valid) {
        CcPanelCategory category;
        g_autoptr(GIcon) icon = NULL;
        g_autofree gchar *name = NULL;
        g_autofree gchar *description = NULL;
        g_autofree gchar *id = NULL;
        g_auto(GStrv) keywords = NULL;
        CcPanelVisibility visibility;
        const gchar *icon_name = NULL;

        gtk_tree_model_get (model, &iter, COL_CATEGORY, &category, COL_DESCRIPTION, &description, COL_GICON, &icon,
                            COL_ID, &id, COL_NAME, &name, COL_KEYWORDS, &keywords, COL_VISIBILITY, &visibility, -1);

        if (G_IS_THEMED_ICON (icon))
            icon_name = g_themed_icon_get_names (G_THEMED_ICON (icon))[0];

        cc_panel_list_add_panel (self->panel_list, category, id, name, description, keywords, icon_name, visibility);

        valid = gtk_tree_model_iter_next (model, &iter);
    }

    /* React to visibility changes */
    g_signal_connect_object (model, "row-changed", G_CALLBACK (on_row_changed_cb), self, G_CONNECT_SWAPPED);
}

static void
close_toplevels_and_dialogs (CcWindow *self)
{
    AdwDialog *dialog;
    GList *toplevels, *item;
    toplevels = item = gtk_window_list_toplevels ();

    for (; item != NULL; item = g_list_next (item)) {
        GtkWindow *window = GTK_WINDOW (item->data);
        if (window != GTK_WINDOW (self) && gtk_widget_is_visible (GTK_WIDGET (window))) {
            if (ADW_IS_WINDOW (window)) {
                dialog = adw_window_get_visible_dialog (ADW_WINDOW (window));
                if (dialog)
                    adw_dialog_force_close (dialog);
            }
            gtk_window_close (window);
        }
    }
    g_list_free (toplevels);

    dialog = adw_application_window_get_visible_dialog (ADW_APPLICATION_WINDOW (self));
    if (dialog)
        adw_dialog_force_close (dialog);
}

static gboolean
set_active_panel_from_id (CcWindow *self, const gchar *start_id, GVariant *parameters, gboolean add_to_history,
                          gboolean force_moving_to_the_panel, GError **error)
{
    g_autoptr(GIcon) gicon = NULL;
    g_autofree gchar *name = NULL;
    CcPanelVisibility visibility;
    CcPanelCategory category;
    g_autoptr(GVariant) system_param_overwrite = NULL;
    GtkTreeIter iter;
    CcPanelListView view;
    gboolean activated;
    gboolean found;

    CC_ENTRY;

    view = cc_panel_list_get_view (self->panel_list);

    /* When loading the same panel again, just set its parameters */
    if (g_strcmp0 (self->current_panel_id, start_id) == 0) {
        g_object_set (G_OBJECT (self->current_panel), "parameters", parameters, NULL);
        if (force_moving_to_the_panel || self->previous_list_view == view)
            adw_navigation_split_view_set_show_content (self->split_view, TRUE);
        self->previous_list_view = view;
        CC_RETURN (TRUE);
    }

    found = find_iter_for_panel_id (self, start_id, &iter);
    if (!found) {
        g_warning ("Could not find settings panel \"%s\"", start_id);
        CC_RETURN (TRUE);
    }

    /* Close toplevel windows and opened dialogs before loading a new panel - Issue #3391 */
    close_toplevels_and_dialogs (self);

    self->old_panel = self->current_panel;
    if (self->old_panel)
        cc_panel_deactivate (CC_PANEL (self->old_panel));

    gtk_tree_model_get (GTK_TREE_MODEL (self->store), &iter, COL_NAME, &name, COL_GICON, &gicon, COL_VISIBILITY,
                        &visibility, COL_CATEGORY, &category, -1);

    /* Handle "System" subpages by overwriting the start_id and parameters arguments.
     * This is needed because they have their own desktop files. */
    if (category == CC_CATEGORY_SYSTEM) {
        g_autofree gchar *param_str = NULL;

        param_str = g_strdup_printf ("[<'%s'>]", start_id);
        system_param_overwrite = g_variant_new_parsed (param_str);
        g_variant_ref_sink (system_param_overwrite);

        g_warning ("The direct access to `%s` is now deprecated. Please, use `system %s` instead.", start_id, start_id);

        start_id = "system";
    }

    /* Activate the panel */
    activated = activate_panel (self, start_id, system_param_overwrite != NULL ? system_param_overwrite : parameters,
                                name, gicon, visibility);

    /* Failed to activate the panel for some reason, let's keep the old
     * panel around instead */
    if (!activated) {
        g_debug ("Failed to activate panel");
        CC_RETURN (TRUE);
    }

    if (self->single_panel_mode)
        cc_panel_enable_single_page_mode (CC_PANEL (self->current_panel));

    if (!self->single_panel_mode && add_to_history)
        add_current_panel_to_history (self, start_id);

    if (force_moving_to_the_panel)
        adw_navigation_split_view_set_show_content (self->split_view, TRUE);

    g_free (self->current_panel_id);
    self->current_panel_id = g_strdup (start_id);

    CC_TRACE_MSG ("Current panel id: %s", start_id);

    /* Don't activate the panel list in single panel mode. */
    if (!self->single_panel_mode)
        cc_panel_list_set_active_panel (self->panel_list, start_id);

    CC_RETURN (TRUE);
}

static void
set_active_panel (CcWindow *self, CcPanel *panel)
{
    g_return_if_fail (CC_IS_WINDOW (self));
    g_return_if_fail (panel == NULL || CC_IS_PANEL (panel));

    if (panel != self->active_panel) {
        /* remove the old panel */
        g_clear_object (&self->active_panel);

        /* set the new panel */
        if (panel)
            self->active_panel = g_object_ref (panel);

        g_object_notify (G_OBJECT (self), "active-panel");
    }
}

static void
switch_to_previous_panel (CcWindow *self)
{
    g_autofree gchar *previous_panel_id = NULL;

    CC_ENTRY;

    if (g_queue_get_length (self->previous_panels) == 0)
        CC_RETURN ();

    previous_panel_id = g_queue_pop_head (self->previous_panels);

    g_debug ("Going to previous panel (%s)", previous_panel_id);

    set_active_panel_from_id (self, previous_panel_id, NULL, FALSE, FALSE, NULL);

    CC_EXIT;
}

/* Callbacks */

static void
on_split_view_collapsed_changed_cb (CcWindow *self)
{
    GtkSelectionMode selection_mode;
    gboolean collapsed;

    g_assert (CC_IS_WINDOW (self));

    collapsed = adw_navigation_split_view_get_collapsed (self->split_view);

    selection_mode = collapsed ? GTK_SELECTION_NONE : GTK_SELECTION_SINGLE;
    cc_panel_list_set_selection_mode (self->panel_list, selection_mode);

    g_object_notify (G_OBJECT (self), "collapsed");
}

static void
show_panel_cb (CcWindow *self, const gchar *panel_id, const gchar *parent_id)
{
    GVariant *parameters;
    GVariantBuilder builder;

    if (!panel_id)
        return;

    if (!parent_id) {
        set_active_panel_from_id (self, panel_id, NULL, TRUE, FALSE, NULL);

        return;
    }

    g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
    g_variant_builder_add (&builder, "v", g_variant_new_string (panel_id));
    parameters = g_variant_builder_end (&builder);

    set_active_panel_from_id (self, parent_id, parameters, TRUE, FALSE, NULL);
}

static void
search_entry_activate_cb (CcWindow *self)
{
    gboolean changed;

    if (cc_panel_list_get_view (self->panel_list) != CC_PANEL_LIST_SEARCH)
        return;

    changed = cc_panel_list_activate (self->panel_list);

    gtk_search_bar_set_search_mode (self->search_bar, !changed);
}

static gboolean
go_back_shortcut_cb (GtkWidget *widget, GVariant *args, gpointer user_data)
{
    g_debug ("Going to previous panel");
    switch_to_previous_panel (CC_WINDOW (widget));

    return GDK_EVENT_STOP;
}

static gboolean
search_shortcut_cb (GtkWidget *widget, GVariant *args, gpointer user_data)
{
    CcPanelListView view;
    CcWindow *self;

    self = CC_WINDOW (widget);
    view = cc_panel_list_get_view (self->panel_list);

    /* The search only happens when we're in the MAIN view */
    if (view != CC_PANEL_LIST_MAIN && view != CC_PANEL_LIST_SEARCH)
        return GDK_EVENT_PROPAGATE;

    gtk_search_bar_set_search_mode (self->search_bar, TRUE);
    gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));

    return GDK_EVENT_STOP;
}

/**
 * cc_window_get_active_panel:
 * @window: A #CcWindow
 *
 * Get the current active panel
 *
 * Returns: a #CcPanel or NULL if no panel is active
 */
CcPanel *
cc_window_get_active_panel (CcWindow *window)
{
    CcPanel *panel = NULL;

    g_return_val_if_fail (CC_IS_WINDOW (window), NULL);

    g_object_get (window, "active-panel", &panel, NULL);

    return panel;
}

/**
 * cc_window_set_active_panel:
 * @window: A #CcWindow
 * @panel: A #CcPanel
 *
 * Set the current active panel. If @panel is NULL, then the window is returned
 * to a state where no panel is being displayed (for example, the list of panels
 * may be shown instead).
 *
 */
void
cc_window_set_active_panel (CcWindow *window, CcPanel *panel)
{
    g_return_if_fail (CC_IS_WINDOW (window));
    g_return_if_fail (panel == NULL || CC_IS_PANEL (panel));

    g_object_set (window, "active-panel", panel, NULL);
}

/**
 * cc_window_set_active_panel_from_id:
 * @window: A #CcWindow
 * @id: The ID of the panel to set as active
 * @parameters: A #GVariant with additional parameters
 * @error: A #GError
 *
 * Find a panel corresponding to the specified id and set it as active.
 *
 * Returns: #TRUE if the panel was found and set as the active panel
 */
gboolean
cc_window_set_active_panel_from_id (CcWindow *window, const gchar *start_id, GVariant *parameters, GError **error)
{
    g_return_val_if_fail (window != NULL, FALSE);

    cc_panel_list_center_activated_row (window->panel_list, TRUE);
    return set_active_panel_from_id (window, start_id, parameters, TRUE, TRUE, error);
}

static void
cc_window_unmap (GtkWidget *widget)
{
    CcWindow *self = CC_WINDOW (widget);
    gboolean maximized;
    gint height;
    gint width;

    maximized = gtk_window_is_maximized (GTK_WINDOW (self));
    gtk_window_get_default_size (GTK_WINDOW (self), &width, &height);

    g_settings_set (self->settings, "window-state", "(iib)", width, height, maximized);

    GTK_WIDGET_CLASS (cc_window_parent_class)->unmap (widget);
}

/* GObject Implementation */
static void
cc_window_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
    CcWindow *self = CC_WINDOW (object);

    switch (property_id) {
    case PROP_ACTIVE_PANEL:
        g_value_set_object (value, self->active_panel);
        break;

    case PROP_MODEL:
        g_value_set_object (value, self->store);
        break;

    case PROP_COLLAPSED:
        g_value_set_boolean (value, adw_navigation_split_view_get_collapsed (self->split_view));
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
}

static void
cc_window_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
    CcWindow *self = CC_WINDOW (object);

    switch (property_id) {
    case PROP_ACTIVE_PANEL:
        set_active_panel (self, g_value_get_object (value));
        break;

    case PROP_MODEL:
        g_assert (self->store == NULL);
        self->store = g_value_dup_object (value);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
}

static void
maybe_load_last_panel (CcWindow *self)
{
    g_autofree char *id = NULL;

    id = g_settings_get_string (self->settings, "last-panel");
    if (cc_panel_list_get_current_panel (self->panel_list))
        return;

    /* select the last used panel, if any, or the first visible panel */
    if (id != NULL && cc_shell_model_has_panel (self->store, id)) {
        cc_panel_list_center_activated_row (self->panel_list, TRUE);
        cc_panel_list_set_active_panel (self->panel_list, id);
    } else
        cc_panel_list_activate (self->panel_list);
}

static void
cc_window_constructed (GObject *object)
{
    CcWindow *self = CC_WINDOW (object);

    load_window_state (self);

    /* Add the panels */
    setup_model (self);

    /* After everything is loaded, select the last used panel, if any,
     * or the first visible panel. We do that in an idle handler so we
     * have a chance to skip it when another panel has been explicitly
     * activated from commandline parameter or from DBus method */
    g_idle_add_once ((GSourceOnceFunc) maybe_load_last_panel, self);

    G_OBJECT_CLASS (cc_window_parent_class)->constructed (object);
}

static void
cc_window_dispose (GObject *object)
{
    CcWindow *self = CC_WINDOW (object);

    g_clear_pointer (&self->current_panel_id, g_free);
    g_clear_object (&self->store);
    g_clear_object (&self->active_panel);

    G_OBJECT_CLASS (cc_window_parent_class)->dispose (object);
}

static void
cc_window_finalize (GObject *object)
{
    CcWindow *self = CC_WINDOW (object);

    if (self->previous_panels) {
        g_queue_free_full (self->previous_panels, g_free);
        self->previous_panels = NULL;
    }

    g_clear_object (&self->settings);

    G_OBJECT_CLASS (cc_window_parent_class)->finalize (object);
}

static gboolean
search_entry_key_pressed_cb (CcWindow *self, guint keyval, guint keycode, GdkModifierType state,
                             GtkEventControllerKey *key_controller)
{
    GtkWidget *toplevel;

    /* When pressing Arrow Down on the entry we move focus to match results list */
    if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) {
        toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (self)));

        if (!toplevel)
            return FALSE;

        return gtk_widget_child_focus (toplevel, GTK_DIR_TAB_FORWARD);
    }

    return FALSE;
}

static void
cc_window_class_init (CcWindowClass *klass)
{
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->get_property = cc_window_get_property;
    object_class->set_property = cc_window_set_property;
    object_class->constructed = cc_window_constructed;
    object_class->dispose = cc_window_dispose;
    object_class->finalize = cc_window_finalize;

    widget_class->unmap = cc_window_unmap;

    g_object_class_install_property (
        object_class, PROP_ACTIVE_PANEL,
        g_param_spec_object ("active-panel", NULL, NULL, CC_TYPE_PANEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        object_class, PROP_MODEL,
        g_param_spec_object ("model", NULL, NULL, CC_TYPE_SHELL_MODEL,
                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        object_class, PROP_COLLAPSED,
        g_param_spec_boolean ("collapsed", NULL, NULL, FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Settings/gtk/cc-window.ui");

    gtk_widget_class_bind_template_child (widget_class, CcWindow, break_point);
    gtk_widget_class_bind_template_child (widget_class, CcWindow, split_view);
    gtk_widget_class_bind_template_child (widget_class, CcWindow, panel_list);
    gtk_widget_class_bind_template_child (widget_class, CcWindow, search_bar);
    gtk_widget_class_bind_template_child (widget_class, CcWindow, search_entry);

    gtk_widget_class_bind_template_callback (widget_class, on_split_view_collapsed_changed_cb);
    gtk_widget_class_bind_template_callback (widget_class, search_entry_activate_cb);
    gtk_widget_class_bind_template_callback (widget_class, show_panel_cb);
    gtk_widget_class_bind_template_callback (widget_class, search_entry_key_pressed_cb);

    gtk_widget_class_add_binding (widget_class, GDK_KEY_Left, GDK_ALT_MASK, go_back_shortcut_cb, NULL);
    gtk_widget_class_add_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
    gtk_widget_class_add_binding (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
    gtk_widget_class_add_binding_action (widget_class, GDK_KEY_q, GDK_CONTROL_MASK, "window.close", NULL);
    gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Q, GDK_CONTROL_MASK, "window.close", NULL);
    gtk_widget_class_add_binding_action (widget_class, GDK_KEY_w, GDK_CONTROL_MASK, "window.close", NULL);
    gtk_widget_class_add_binding_action (widget_class, GDK_KEY_W, GDK_CONTROL_MASK, "window.close", NULL);

    g_type_ensure (CC_TYPE_PANEL_LIST);
}

static void
cc_window_init (CcWindow *self)
{
    gtk_widget_init_template (GTK_WIDGET (self));

    self->settings = g_settings_new ("org.gnome.Settings");
    self->previous_panels = g_queue_new ();
    self->previous_list_view = cc_panel_list_get_view (self->panel_list);

    gtk_search_bar_set_key_capture_widget (self->search_bar, GTK_WIDGET (self));
}

CcWindow *
cc_window_new (GtkApplication *application, CcShellModel *model)
{
    g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);

    return g_object_new (CC_TYPE_WINDOW, "application", application, "resizable", TRUE, "title",
                         _("Settings"), "icon-name", DEFAULT_WINDOW_ICON_NAME, "show-menubar", FALSE, "model", model,
                           NULL);
}

void
cc_window_set_search_item (CcWindow *window, const char *search)
{
    gtk_search_bar_set_search_mode (window->search_bar, TRUE);
    gtk_editable_set_text (GTK_EDITABLE (window->search_entry), search);
    gtk_editable_set_position (GTK_EDITABLE (window->search_entry), -1);
}

void
cc_window_enable_single_panel_mode (CcWindow *self)
{
    g_return_if_fail (CC_IS_WINDOW (self));

    self->single_panel_mode = TRUE;

    adw_navigation_split_view_set_collapsed (self->split_view, TRUE);
    adw_navigation_split_view_set_sidebar (self->split_view, NULL);
    adw_breakpoint_set_condition (self->break_point, NULL);
}
