/* vim: set sw=2 ts=2 sts=2 et: */
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* autoar-gtk-chooser.c
* GTK+ widgets to choose archive format and filter
*
* Copyright (C) 2013, 2014 Ting-Wei Lan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "autoar-gtk-chooser.h"
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
/**
* SECTION:autoar-gtk-chooser
* @Short_description: GTK+ widgets to choose archive format and filter
* @Title: autoar-gtk-chooser
* @Include: gnome-autoar/autoar-gtk.h
*
* autoar-gtk-chooser contains two widgets for users to choose preferred
* archive format and filter.
**/
/* autoar_gtk_chooser_simple */
enum
{
SIMPLE_COL_FORMAT,
SIMPLE_COL_FILTER,
SIMPLE_COL_DESCRIPTION,
N_SIMPLE_COLS
};
static char*
format_filter_full_description (AutoarFormat format,
AutoarFilter filter)
{
char *description, *extension, *full_description;
description = autoar_format_filter_get_description (format, filter);
extension = autoar_format_filter_get_extension (format, filter);
full_description = g_strdup_printf ("%s (*%s)", description, extension);
g_free (description);
g_free (extension);
return full_description;
}
static gboolean
simple_row_separator_cb (GtkTreeModel *model,
GtkTreeIter *iter,
void *data)
{
char *description, first_char;
gtk_tree_model_get (model, iter, SIMPLE_COL_DESCRIPTION, &description, -1);
first_char = description != NULL ? *description : '\0';
g_free (description);
if (first_char == '\0')
return TRUE;
else
return FALSE;
}
static void
simple_get_variable_row (GtkTreeModel *model,
GtkTreeIter *base_iter,
GtkTreeIter *dest_iter)
{
GtkTreeIter iter;
iter = *base_iter;
if (!gtk_tree_model_iter_previous (model, &iter) ||
!gtk_tree_model_iter_previous (model, &iter) ||
!simple_row_separator_cb (model, &iter, NULL)) {
/* Create two new rows if it does not exist */
GtkListStore *store;
store = GTK_LIST_STORE (model);
gtk_list_store_insert_before (store, dest_iter, base_iter);
gtk_list_store_insert_before (store, &iter, dest_iter);
gtk_list_store_set (store, &iter,
SIMPLE_COL_FORMAT, 0,
SIMPLE_COL_FILTER, 0,
SIMPLE_COL_DESCRIPTION, "", -1);
} else {
/* Use the existing row */
iter = *base_iter;
gtk_tree_model_iter_previous (model, &iter);
*dest_iter = iter;
}
}
static void
simple_set_active (GtkComboBox *simple,
GtkTreeModel *model,
AutoarFormat format,
AutoarFilter filter)
{
GtkTreeIter iter, prev;
AutoarFormat this_format;
AutoarFilter this_filter;
int get_format, get_filter;
int *previous;
previous = g_object_get_data ((GObject*)simple, "previous");
if (autoar_format_is_valid (format) && autoar_filter_is_valid (filter)) {
gtk_tree_model_get_iter_first (model, &iter);
do {
gtk_tree_model_get (model, &iter,
SIMPLE_COL_FORMAT, &this_format,
SIMPLE_COL_FILTER, &this_filter, -1);
if (this_format == format && this_filter == filter) {
gtk_combo_box_set_active_iter (simple, &iter);
previous[0] = format;
previous[1] = filter;
return;
}
prev = iter;
} while (gtk_tree_model_iter_next (model, &iter));
if (gtk_tree_model_iter_previous (model, &prev)) {
GtkTreeIter active;
char *description_string;
simple_get_variable_row (model, &prev, &active);
description_string = format_filter_full_description (format, filter);
gtk_list_store_set (GTK_LIST_STORE (model), &active,
SIMPLE_COL_FORMAT, format,
SIMPLE_COL_FILTER, filter,
SIMPLE_COL_DESCRIPTION, description_string, -1);
g_free (description_string);
gtk_combo_box_set_active_iter (simple, &active);
previous[0] = format;
previous[1] = filter;
return;
}
}
gtk_tree_model_get_iter_first (model, &iter);
gtk_combo_box_set_active_iter (simple, &iter);
gtk_tree_model_get (model, &iter,
SIMPLE_COL_FORMAT, &get_format,
SIMPLE_COL_FILTER, &get_filter, -1);
previous[0] = format;
previous[1] = filter;
}
static void
simple_changed_cb (GtkComboBox *simple,
void *data) {
GtkTreeIter iter;
GtkTreeModel* model;
int format, filter;
int *previous;
if (!gtk_combo_box_get_active_iter (simple, &iter))
return;
model = gtk_combo_box_get_model (simple);
gtk_tree_model_get (model, &iter,
SIMPLE_COL_FORMAT, &format,
SIMPLE_COL_FILTER, &filter, -1);
previous = g_object_get_data ((GObject*)simple, "previous");
if (!format) {
GtkWidget *dialog_widget;
GtkDialog *dialog;
GtkWidget *dialog_content;
GtkWidget *simple_widget;
GtkWidget *advanced_widget;
int response;
simple_widget = GTK_WIDGET (simple);
dialog_widget =
gtk_dialog_new_with_buttons (
_("Choose an archive format"),
GTK_WINDOW (gtk_widget_get_ancestor (simple_widget, GTK_TYPE_WINDOW)),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
_("Cancel"), GTK_RESPONSE_CANCEL,
_("OK"), GTK_RESPONSE_ACCEPT, NULL);
dialog = GTK_DIALOG (dialog_widget);
gtk_dialog_set_default_response (dialog, GTK_RESPONSE_ACCEPT);
dialog_content = gtk_dialog_get_content_area (dialog);
advanced_widget = autoar_gtk_chooser_advanced_new (previous[0],
previous[1]);
gtk_container_add (GTK_CONTAINER (dialog_content), advanced_widget);
gtk_widget_show_all (dialog_widget);
response = gtk_dialog_run (dialog);
if (response == GTK_RESPONSE_ACCEPT &&
gtk_tree_model_iter_previous (model, &iter) &&
autoar_gtk_chooser_advanced_get (advanced_widget, &format, &filter))
simple_set_active (simple, model, format, filter);
else
simple_set_active (simple, model, previous[0], previous[1]);
gtk_widget_destroy (dialog_widget);
} else {
previous[0] = format;
previous[1] = filter;
}
}
/**
* autoar_gtk_chooser_simple_new:
* @default_format: an #AutoarFormat
* @default_filter: an #AutoarFilter
*
* Create a #GtkComboBox with a list of common archive format. There is also
* an option called "Other format…", which will use
* autoar_gtk_chooser_advanced_new() and
* autoar_gtk_chooser_advanced_get() to select less common archive
* format. Arguments @default_format and @default_filter are the default archive
* format selected on the returned widget. You may want to get the preferred
* format of users using autoar_pref_get_default_format() and
* autoar_pref_get_default_filter(), or just set them to 1 to select
* the default archive format.
*
* Returns: (transfer full): a new #GtkComboBox widget
**/
GtkWidget*
autoar_gtk_chooser_simple_new (AutoarFormat default_format,
AutoarFilter default_filter)
{
GtkWidget *simple_widget;
GtkComboBox *simple_combo;
GtkCellLayout *simple;
GtkCellRenderer *cell_renderer;
GtkTreeModel *model;
GtkListStore *store;
GtkTreeIter iter;
int i;
int *previous;
struct format_filter
{
AutoarFormat format;
AutoarFilter filter;
};
struct format_filter defaults [] = {
{ AUTOAR_FORMAT_ZIP, AUTOAR_FILTER_NONE },
{ AUTOAR_FORMAT_TAR, AUTOAR_FILTER_NONE },
{ AUTOAR_FORMAT_TAR, AUTOAR_FILTER_GZIP },
{ AUTOAR_FORMAT_TAR, AUTOAR_FILTER_BZIP2 },
{ AUTOAR_FORMAT_TAR, AUTOAR_FILTER_XZ },
{ AUTOAR_FORMAT_CPIO, AUTOAR_FILTER_NONE },
{ AUTOAR_FORMAT_7ZIP, AUTOAR_FILTER_NONE },
};
store = gtk_list_store_new (N_SIMPLE_COLS, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING);
model = GTK_TREE_MODEL (store);
for (i = 0; i < sizeof (defaults) / sizeof (struct format_filter); i++) {
char *description;
gtk_list_store_append (store, &iter);
description = format_filter_full_description (defaults[i].format,
defaults[i].filter);
gtk_list_store_set (store, &iter,
SIMPLE_COL_FORMAT, defaults[i].format,
SIMPLE_COL_FILTER, defaults[i].filter,
SIMPLE_COL_DESCRIPTION, description, -1);
g_free (description);
}
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
SIMPLE_COL_FORMAT, 0,
SIMPLE_COL_FILTER, 0,
SIMPLE_COL_DESCRIPTION, "", -1);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
SIMPLE_COL_FORMAT, 0,
SIMPLE_COL_FILTER, 0,
SIMPLE_COL_DESCRIPTION, _("Other format…"), -1);
simple_widget = gtk_combo_box_new_with_model (model);
simple = GTK_CELL_LAYOUT (simple_widget);
simple_combo = GTK_COMBO_BOX (simple_widget);
cell_renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (simple, cell_renderer, FALSE);
gtk_cell_layout_add_attribute (simple, cell_renderer, "text", SIMPLE_COL_DESCRIPTION);
previous = g_new (int, 2);
g_object_set_data_full ((GObject*)simple, "previous", previous, g_free);
simple_set_active (simple_combo, model, default_format, default_filter);
gtk_combo_box_set_row_separator_func (simple_combo, simple_row_separator_cb, NULL, NULL);
g_signal_connect (simple, "changed", G_CALLBACK (simple_changed_cb), NULL);
g_object_unref (store);
return simple_widget;
}
/**
* autoar_gtk_chooser_simple_get:
* @simple: a #GtkComboBox returned by autoar_gtk_chooser_simple_new()
* @format: the place to store the #AutoarFormat selected by the user
* @filter: the place to store the #AutoarFilter selected by the user
*
* Gets the selected archive format of the widget created by
* autoar_gtk_chooser_simple_new().
*
* Returns: %TRUE if @format and @filter are set. %FALSE if there is no
* selected item on @simple, so @format and @filter are not modified.
**/
gboolean
autoar_gtk_chooser_simple_get (GtkWidget *simple,
int *format,
int *filter)
{
GtkComboBox *combo;
GtkTreeModel *model;
GtkTreeIter iter;
if (!gtk_combo_box_get_active_iter (combo = GTK_COMBO_BOX (simple), &iter))
return FALSE;
model = gtk_combo_box_get_model (combo);
gtk_tree_model_get (model, &iter,
SIMPLE_COL_FORMAT, format,
SIMPLE_COL_FILTER, filter, -1);
return TRUE;
}
/* autoar_gtk_chooser_advanced */
enum
{
ADVANCED_FORMAT_COL_FORMAT,
ADVANCED_FORMAT_COL_DESCRIPTION,
N_ADVANCED_FORMAT_COLS
};
enum
{
ADVANCED_FILTER_COL_FILTER,
ADVANCED_FILTER_COL_DESCRIPTION,
N_ADVANCED_FILTER_COLS
};
static void
advanced_update_description_cb (GtkTreeView *unused_variable,
GtkWidget *advanced)
{
int format;
int filter;
GtkLabel *description;
char *description_string;
if (!autoar_gtk_chooser_advanced_get (advanced, &format, &filter))
return;
description = GTK_LABEL (gtk_grid_get_child_at (GTK_GRID (advanced), 0, 1));
description_string = format_filter_full_description (format, filter);
gtk_label_set_text (description, description_string);
g_free (description_string);
}
static GtkTreeModel*
advanced_format_store (void)
{
GtkListStore *store;
int i, last;
store = gtk_list_store_new (N_ADVANCED_FORMAT_COLS, G_TYPE_INT, G_TYPE_STRING);
last = autoar_format_last ();
for (i = 1; i < last; i++) {
GtkTreeIter iter;
const char *description;
gtk_list_store_append (store, &iter);
description = autoar_format_get_description (i);
gtk_list_store_set (store, &iter,
ADVANCED_FORMAT_COL_FORMAT, i,
ADVANCED_FORMAT_COL_DESCRIPTION, description, -1);
}
return GTK_TREE_MODEL (store);
}
static GtkTreeModel*
advanced_filter_store (void)
{
GtkListStore *store;
int i, last;
store = gtk_list_store_new (N_ADVANCED_FILTER_COLS, G_TYPE_INT, G_TYPE_STRING);
last = autoar_filter_last ();
for (i = 1; i < last; i++) {
GtkTreeIter iter;
const char *description;
gtk_list_store_append (store, &iter);
description = autoar_filter_get_description (i);
gtk_list_store_set (store, &iter,
ADVANCED_FILTER_COL_FILTER, i,
ADVANCED_FILTER_COL_DESCRIPTION, description, -1);
}
return GTK_TREE_MODEL (store);
}
/**
* autoar_gtk_chooser_advanced_new:
* @default_format: an #AutoarFormat
* @default_filter: an #AutoarFilter
*
* Create a #GtkGrid with two lists. One list shows all available formats,
* and the other list shows all available filters.
*
* Returns: (transfer full): a new #GtkGrid widget
**/
GtkWidget*
autoar_gtk_chooser_advanced_new (AutoarFormat default_format,
AutoarFilter default_filter)
{
GtkWidget *advanced_widget;
GtkGrid *advanced;
GtkTreeModel *format_model;
GtkWidget *format_widget;
GtkTreeView *format;
GtkTreeSelection *format_selection;
GtkCellRenderer *format_renderer;
GtkTreePath *format_path;
GtkTreeModel *filter_model;
GtkWidget *filter_widget;
GtkTreeView *filter;
GtkTreeSelection *filter_selection;
GtkCellRenderer *filter_renderer;
GtkTreePath *filter_path;
GtkWidget *description_widget;
GtkLabel *description;
advanced_widget = gtk_grid_new ();
advanced = GTK_GRID (advanced_widget);
gtk_grid_set_row_spacing (advanced, 5);
gtk_grid_set_column_spacing (advanced, 5);
gtk_grid_set_column_homogeneous (advanced, TRUE);
format_model = advanced_format_store ();
format_widget = gtk_tree_view_new_with_model (format_model);
format = GTK_TREE_VIEW (format_widget);
format_selection = gtk_tree_view_get_selection (format);
format_renderer = gtk_cell_renderer_text_new ();
gtk_tree_selection_set_mode (format_selection, GTK_SELECTION_SINGLE);
gtk_tree_view_insert_column_with_attributes (format, -1, _("Format"),
format_renderer, "text",
ADVANCED_FORMAT_COL_DESCRIPTION,
NULL);
if (autoar_format_is_valid (default_format)) {
GtkTreeIter iter;
gboolean valid;
format_path = NULL;
for (valid = gtk_tree_model_get_iter_first (format_model, &iter);
valid;
valid = gtk_tree_model_iter_next (format_model, &iter)) {
int get_format;
gtk_tree_model_get (format_model, &iter,
ADVANCED_FORMAT_COL_FORMAT, &get_format, -1);
if (default_format == get_format) {
format_path = gtk_tree_model_get_path (format_model, &iter);
break;
}
}
if (format_path == NULL)
format_path = gtk_tree_path_new_first ();
} else {
format_path = gtk_tree_path_new_first ();
}
gtk_tree_view_set_cursor (format, format_path, NULL, FALSE);
gtk_tree_path_free (format_path);
gtk_grid_attach (advanced, format_widget, 0, 0, 1, 1);
g_object_unref (format_model);
filter_model = advanced_filter_store ();
filter_widget = gtk_tree_view_new_with_model (filter_model);
filter = GTK_TREE_VIEW (filter_widget);
filter_selection = gtk_tree_view_get_selection (filter);
filter_renderer = gtk_cell_renderer_text_new ();
gtk_tree_selection_set_mode (filter_selection, GTK_SELECTION_SINGLE);
gtk_tree_view_insert_column_with_attributes (filter, -1, _("Filter"),
filter_renderer, "text",
ADVANCED_FILTER_COL_DESCRIPTION,
NULL);
if (autoar_filter_is_valid (default_filter)) {
GtkTreeIter iter;
gboolean valid;
filter_path = NULL;
for (valid = gtk_tree_model_get_iter_first (filter_model, &iter);
valid;
valid = gtk_tree_model_iter_next (filter_model, &iter)) {
int get_filter;
gtk_tree_model_get (filter_model, &iter,
ADVANCED_FILTER_COL_FILTER, &get_filter, -1);
if (default_filter == get_filter) {
filter_path = gtk_tree_model_get_path (filter_model, &iter);
break;
}
}
if (filter_path == NULL)
filter_path = gtk_tree_path_new_first ();
} else {
filter_path = gtk_tree_path_new_first ();
}
gtk_tree_view_set_cursor (filter, filter_path, NULL, FALSE);
gtk_tree_path_free (filter_path);
gtk_grid_attach (advanced, filter_widget, 1, 0, 1, 1);
g_object_unref (filter_model);
description_widget = gtk_label_new (NULL);
description = GTK_LABEL (description_widget);
gtk_label_set_justify (description, GTK_JUSTIFY_CENTER);
gtk_grid_attach (advanced, description_widget, 0, 1, 2, 1);
g_signal_connect (format_widget, "cursor-changed",
G_CALLBACK (advanced_update_description_cb), advanced);
g_signal_connect (filter_widget, "cursor-changed",
G_CALLBACK (advanced_update_description_cb), advanced);
/* Run the callback now to set the initial text on the label */
advanced_update_description_cb (NULL, advanced_widget);
return advanced_widget;
}
/**
* autoar_gtk_chooser_advanced_get:
* @advanced: a #GtkGrid returned by autoar_gtk_chooser_advanced_new()
* @format: the place to store the #AutoarFormat selected by the user
* @filter: the place to store the #AutoarFilter selected by the user
*
* Gets the selected archive format of the widget created by
* autoar_gtk_chooser_advanced_new().
*
* Returns: %TRUE if @format and @filter are set. %FALSE if there is no
* selected item on @advanced, so @format and @filter are not modified.
**/
gboolean
autoar_gtk_chooser_advanced_get (GtkWidget *advanced,
int *format,
int *filter)
{
GtkGrid *grid;
GtkTreeIter format_iter, filter_iter;
GtkTreeView *format_view, *filter_view;
GtkTreePath *format_path, *filter_path;
GtkTreeModel *format_model, *filter_model;
grid = GTK_GRID (advanced);
format_view = GTK_TREE_VIEW (gtk_grid_get_child_at (grid, 0, 0));
filter_view = GTK_TREE_VIEW (gtk_grid_get_child_at (grid, 1, 0));
gtk_tree_view_get_cursor (format_view, &format_path, NULL);
gtk_tree_view_get_cursor (filter_view, &filter_path, NULL);
if (format_path == NULL || filter_path == NULL) {
gtk_tree_path_free (format_path);
gtk_tree_path_free (filter_path);
return FALSE;
}
format_model = gtk_tree_view_get_model (format_view);
filter_model = gtk_tree_view_get_model (filter_view);
if (!gtk_tree_model_get_iter (format_model, &format_iter, format_path) ||
!gtk_tree_model_get_iter (filter_model, &filter_iter, filter_path)) {
gtk_tree_path_free (format_path);
gtk_tree_path_free (filter_path);
return FALSE;
}
gtk_tree_path_free (format_path);
gtk_tree_path_free (filter_path);
gtk_tree_model_get (format_model, &format_iter,
ADVANCED_FORMAT_COL_FORMAT, format, -1);
gtk_tree_model_get (filter_model, &filter_iter,
ADVANCED_FILTER_COL_FILTER, filter, -1);
return TRUE;
}