/* Copyright (C) 2005 Emmanuele Bassi
* Copyright (C) 2012-2021 MATE Developers
*
* This file is part of MATE Utils.
*
* MATE Utils 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.
*
* MATE Utils 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 MATE Utils. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#ifdef HAVE_UNISTD_H
#include
#endif
#include
#include
#include "gdict-source-dialog.h"
#include "gdict-common.h"
#define GDICT_SOURCE_UI PKGDATADIR "/mate-dictionary-source.ui"
#define GET_WIDGET(x) (GTK_WIDGET (gtk_builder_get_object (dialog->builder, (x))))
#define GET_EDITABLE(x) (GTK_EDITABLE (gtk_builder_get_object (dialog->builder, (x))))
/*********************
* GdictSourceDialog *
*********************/
struct _GdictSourceDialog
{
GtkDialog parent_instance;
GtkBuilder *builder;
GSettings *settings;
GdictSourceLoader *loader;
GdictSource *source;
gchar *source_name;
GdictContext *context;
GdictSourceDialogAction action;
GdictSourceTransport transport;
GtkWidget *add_button;
GtkWidget *close_button;
GtkWidget *cancel_button;
GtkWidget *help_button;
GtkWidget *db_chooser;
GtkWidget *strat_chooser;
GtkWidget *transport_combo;
};
struct _GdictSourceDialogClass
{
GtkDialogClass parent_class;
};
enum
{
PROP_0,
PROP_SOURCE_LOADER,
PROP_SOURCE_NAME,
PROP_ACTION
};
G_DEFINE_TYPE (GdictSourceDialog, gdict_source_dialog, GTK_TYPE_DIALOG);
static void
set_source_loader (GdictSourceDialog *dialog,
GdictSourceLoader *loader)
{
if (dialog->loader)
g_object_unref (dialog->loader);
dialog->loader = g_object_ref (loader);
}
static void
transport_combo_changed_cb (GtkWidget *widget,
gpointer user_data)
{
GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (user_data);
gint transport;
transport = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
if (transport == dialog->transport)
return;
if (transport == GDICT_SOURCE_TRANSPORT_DICTD)
{
gtk_widget_show (GET_WIDGET ("hostname_label"));
gtk_widget_show (GET_WIDGET ("hostname_entry"));
gtk_widget_show (GET_WIDGET ("port_label"));
gtk_widget_show (GET_WIDGET ("port_entry"));
if (dialog->action == GDICT_SOURCE_DIALOG_CREATE)
{
gtk_widget_set_sensitive (dialog->add_button, TRUE);
dialog->transport = GDICT_SOURCE_TRANSPORT_DICTD;
}
}
else
{
gtk_widget_hide (GET_WIDGET ("hostname_label"));
gtk_widget_hide (GET_WIDGET ("hostname_entry"));
gtk_widget_hide (GET_WIDGET ("port_label"));
gtk_widget_hide (GET_WIDGET ("port_entry"));
if (dialog->action == GDICT_SOURCE_DIALOG_CREATE)
{
gtk_widget_set_sensitive (dialog->add_button, FALSE);
dialog->transport = GDICT_SOURCE_TRANSPORT_INVALID;
}
}
}
static gchar *
get_text_from_entry (GdictSourceDialog *dialog,
const gchar *entry_name)
{
GtkWidget *entry;
gchar *retval;
entry = GET_WIDGET (entry_name);
if (!entry)
return NULL;
retval = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
return retval;
}
static void
set_text_to_entry (GdictSourceDialog *dialog,
const gchar *entry_name,
const gchar *text)
{
GtkWidget *entry;
entry = GET_WIDGET (entry_name);
if (!entry)
return;
gtk_entry_set_text (GTK_ENTRY (entry), text);
}
static void
set_transport_settings (GdictSourceDialog *dialog)
{
switch (dialog->transport)
{
case GDICT_SOURCE_TRANSPORT_DICTD:
{
GdictClientContext *context;
const gchar *hostname;
gchar *port_str;
guint port;
context = GDICT_CLIENT_CONTEXT (dialog->context);
hostname = gdict_client_context_get_hostname (context);
port = gdict_client_context_get_port (context);
port_str = g_strdup_printf ("%d", port);
set_text_to_entry (dialog, "hostname_entry", hostname);
set_text_to_entry (dialog, "port_entry", port_str);
gtk_widget_show (GET_WIDGET ("hostname_label"));
gtk_widget_show (GET_WIDGET ("hostname_entry"));
gtk_widget_show (GET_WIDGET ("port_label"));
gtk_widget_show (GET_WIDGET ("port_entry"));
g_free (port_str);
}
break;
case GDICT_SOURCE_TRANSPORT_INVALID:
default:
break;
}
}
static void
update_dialog_ui (GdictSourceDialog *dialog)
{
GdictSource *source;
/* TODO - add code to update the contents of the dialog depending
* on the action; if we are in _CREATE, no action is needed
*/
switch (dialog->action)
{
case GDICT_SOURCE_DIALOG_VIEW:
case GDICT_SOURCE_DIALOG_EDIT:
if (!dialog->source_name)
{
g_warning ("Attempting to retrieve source, but no "
"source name has been defined. Aborting...");
return;
}
source = gdict_source_loader_get_source (dialog->loader,
dialog->source_name);
if (!source)
{
g_warning ("Attempting to retrieve source, but no "
"source named `%s' was found. Aborting...",
dialog->source_name);
return;
}
g_object_ref (source);
dialog->source = source;
set_text_to_entry (dialog, "description_entry",
gdict_source_get_description (source));
dialog->transport = gdict_source_get_transport (source);
gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->transport_combo),
(gint) dialog->transport);
/* set the context for the database and strategy choosers */
dialog->context = gdict_source_get_context (source);
if (!dialog->context)
{
g_warning ("Attempting to retrieve the context, but "
"none was found for source `%s'.",
dialog->source_name);
return;
}
set_transport_settings (dialog);
gdict_database_chooser_set_context (GDICT_DATABASE_CHOOSER (dialog->db_chooser),
dialog->context);
gdict_database_chooser_refresh (GDICT_DATABASE_CHOOSER (dialog->db_chooser));
gdict_strategy_chooser_set_context (GDICT_STRATEGY_CHOOSER (dialog->strat_chooser),
dialog->context);
gdict_strategy_chooser_refresh (GDICT_STRATEGY_CHOOSER (dialog->strat_chooser));
break;
case GDICT_SOURCE_DIALOG_CREATE:
/* DICTD transport is default */
gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->transport_combo), 0);
g_signal_emit_by_name (dialog->transport_combo, "changed");
break;
default:
g_assert_not_reached ();
break;
}
}
static void
build_new_source (GdictSourceDialog *dialog)
{
GdictSource *source;
gchar *name, *text;
GdictSourceTransport transport;
gchar *host, *port;
gchar *data;
gsize length;
GError *error;
gchar *filename;
GdictDatabaseChooser *db_chooser;
GdictStrategyChooser *strat_chooser;
source = gdict_source_new ();
/* use the timestamp and the pid to get a unique name */
name = g_strdup_printf ("source-%lu-%u",
(gulong) time (NULL),
(guint) getpid ());
gdict_source_set_name (source, name);
g_free (name);
text = get_text_from_entry (dialog, "description_entry");
gdict_source_set_description (source, text);
g_free (text);
db_chooser = GDICT_DATABASE_CHOOSER (dialog->db_chooser);
text = gdict_database_chooser_get_current_database (db_chooser);
gdict_source_set_database (source, text);
g_free (text);
strat_chooser = GDICT_STRATEGY_CHOOSER (dialog->strat_chooser);
text = gdict_strategy_chooser_get_current_strategy (strat_chooser);
gdict_source_set_strategy (source, text);
g_free (text);
/* get the selected transport id */
transport = dialog->transport;
switch (transport)
{
case GDICT_SOURCE_TRANSPORT_DICTD:
host = get_text_from_entry (dialog, "hostname_entry");
port = get_text_from_entry (dialog, "port_entry");
gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD,
"hostname", host,
"port", atoi (port),
NULL);
g_free (host);
g_free (port);
break;
case GDICT_SOURCE_TRANSPORT_INVALID:
default:
g_warning ("Invalid transport");
return;
}
error = NULL;
data = gdict_source_to_data (source, &length, &error);
if (error)
{
gdict_show_gerror_dialog (GTK_WINDOW (dialog),
_("Unable to create a source file"),
error);
g_object_unref (source);
return;
}
name = g_strdup_printf ("%s.desktop", gdict_source_get_name (source));
filename = g_build_filename (g_get_user_config_dir (),
"mate",
"mate-dictionary",
name,
NULL);
g_free (name);
g_file_set_contents (filename, data, length, &error);
if (error)
gdict_show_gerror_dialog (GTK_WINDOW (dialog),
_("Unable to save source file"),
error);
g_free (filename);
g_free (data);
g_object_unref (source);
}
static void
save_source (GdictSourceDialog *dialog)
{
GdictSource *source;
GdictDatabaseChooser *db_chooser;
GdictStrategyChooser *strat_chooser;
gchar *name, *text;
GdictSourceTransport transport;
gchar *host, *port;
gchar *data;
gsize length;
GError *error;
gchar *filename;
source = gdict_source_loader_get_source (dialog->loader,
dialog->source_name);
if (!source)
{
g_warning ("Attempting to save source `%s', but no "
"source for that name was found.",
dialog->source_name);
return;
}
text = get_text_from_entry (dialog, "description_entry");
gdict_source_set_description (source, text);
g_free (text);
db_chooser = GDICT_DATABASE_CHOOSER (dialog->db_chooser);
text = gdict_database_chooser_get_current_database (db_chooser);
gdict_source_set_database (source, text);
g_free (text);
strat_chooser = GDICT_STRATEGY_CHOOSER (dialog->strat_chooser);
text = gdict_strategy_chooser_get_current_strategy (strat_chooser);
gdict_source_set_strategy (source, text);
g_free (text);
/* get the selected transport id */
transport = dialog->transport;
switch (transport)
{
case GDICT_SOURCE_TRANSPORT_DICTD:
host = get_text_from_entry (dialog, "hostname_entry");
port = get_text_from_entry (dialog, "port_entry");
gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD,
"hostname", host,
"port", atoi (port),
NULL);
g_free (host);
g_free (port);
break;
case GDICT_SOURCE_TRANSPORT_INVALID:
default:
g_warning ("Invalid transport");
return;
}
error = NULL;
data = gdict_source_to_data (source, &length, &error);
if (error)
{
gdict_show_gerror_dialog (GTK_WINDOW (dialog),
_("Unable to create a source file"),
error);
g_object_unref (source);
return;
}
name = g_strdup_printf ("%s.desktop", gdict_source_get_name (source));
filename = g_build_filename (g_get_user_config_dir (),
"mate",
"mate-dictionary",
name,
NULL);
g_free (name);
g_file_set_contents (filename, data, length, &error);
if (error)
gdict_show_gerror_dialog (GTK_WINDOW (dialog),
_("Unable to save source file"),
error);
g_free (filename);
g_free (data);
g_object_unref (source);
}
static void
gdict_source_dialog_response_cb (GtkDialog *dialog,
gint response_id,
gpointer user_data)
{
GError *err = NULL;
switch (response_id)
{
case GTK_RESPONSE_ACCEPT:
build_new_source (GDICT_SOURCE_DIALOG (dialog));
break;
case GTK_RESPONSE_HELP:
gtk_show_uri_on_window (GTK_WINDOW (dialog),
"help:mate-dictionary/mate-dictionary-add-source",
gtk_get_current_event_time (), &err);
if (err)
{
gdict_show_gerror_dialog (GTK_WINDOW (dialog),
_("There was an error while displaying help"),
err);
}
/* we don't want the dialog to close itself */
g_signal_stop_emission_by_name (dialog, "response");
break;
case GTK_RESPONSE_CLOSE:
save_source (GDICT_SOURCE_DIALOG (dialog));
break;
case GTK_RESPONSE_CANCEL:
break;
default:
break;
}
}
static void
gdict_source_dialog_finalize (GObject *object)
{
GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
if (dialog->settings)
g_object_unref (dialog->settings);
if (dialog->builder)
g_object_unref (dialog->builder);
if (dialog->source_name)
g_free (dialog->source_name);
if (dialog->source)
g_object_unref (dialog->source);
if (dialog->loader)
g_object_unref (dialog->loader);
G_OBJECT_CLASS (gdict_source_dialog_parent_class)->finalize (object);
}
static void
gdict_source_dialog_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
switch (prop_id)
{
case PROP_SOURCE_LOADER:
set_source_loader (dialog, g_value_get_object (value));
break;
case PROP_SOURCE_NAME:
g_free (dialog->source_name);
dialog->source_name = g_strdup (g_value_get_string (value));
break;
case PROP_ACTION:
dialog->action = (GdictSourceDialogAction) g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdict_source_dialog_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
switch (prop_id)
{
case PROP_SOURCE_LOADER:
g_value_set_object (value, dialog->loader);
break;
case PROP_SOURCE_NAME:
g_value_set_string (value, dialog->source_name);
break;
case PROP_ACTION:
g_value_set_int (value, dialog->action);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GObject *
gdict_source_dialog_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
GdictSourceDialog *dialog;
GError *error = NULL;
object = G_OBJECT_CLASS (gdict_source_dialog_parent_class)->constructor (type,
n_construct_properties,
construct_params);
dialog = GDICT_SOURCE_DIALOG (object);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
/* get the UI from the GtkBuilder file */
dialog->builder = gtk_builder_new ();
gtk_builder_add_from_file (dialog->builder, GDICT_SOURCE_UI, &error);
if (error) {
g_critical ("Unable to load the user interface definition file: %s",
error->message);
g_error_free (error);
g_assert_not_reached ();
}
/* the main widget */
gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
GET_WIDGET ("source_root"));
/* the transport combo changes the UI by changing the visible widgets
* bound to the transport's own options.
*/
dialog->transport_combo = GET_WIDGET ("transport_combo");
g_signal_connect (dialog->transport_combo, "changed",
G_CALLBACK (transport_combo_changed_cb),
dialog);
/* the help button is always visible */
dialog->help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
"gtk-help",
GTK_RESPONSE_HELP);
dialog->db_chooser = gdict_database_chooser_new ();
gtk_box_pack_start (GTK_BOX (GET_WIDGET ("db-box")), dialog->db_chooser, TRUE, TRUE, 0);
gtk_widget_show (dialog->db_chooser);
dialog->strat_chooser = gdict_strategy_chooser_new ();
gtk_box_pack_start (GTK_BOX (GET_WIDGET ("strat-box")), dialog->strat_chooser, TRUE, TRUE, 0);
gtk_widget_show (dialog->strat_chooser);
/* the UI changes depending on the action that the source dialog
* should perform
*/
switch (dialog->action)
{
case GDICT_SOURCE_DIALOG_VIEW:
/* disable every editable widget */
gtk_editable_set_editable (GET_EDITABLE ("name_entry"), FALSE);
gtk_editable_set_editable (GET_EDITABLE ("description_entry"), FALSE);
gtk_editable_set_editable (GET_EDITABLE ("hostname_entry"), FALSE);
gtk_editable_set_editable (GET_EDITABLE ("port_entry"), FALSE);
gtk_widget_set_sensitive (dialog->transport_combo, FALSE);
/* we just allow closing the dialog */
dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
"gtk-close",
GTK_RESPONSE_CLOSE);
break;
case GDICT_SOURCE_DIALOG_CREATE:
dialog->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
"gtk-cancel",
GTK_RESPONSE_CANCEL);
dialog->add_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
"gtk-add",
GTK_RESPONSE_ACCEPT);
/* the "add" button sensitivity is controlled by the transport_combo
* since it's the only setting that makes a source usable.
*/
gtk_widget_set_sensitive (dialog->add_button, FALSE);
break;
case GDICT_SOURCE_DIALOG_EDIT:
dialog->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
"gtk-cancel",
GTK_RESPONSE_CANCEL);
dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
"gtk-close",
GTK_RESPONSE_CLOSE);
break;
default:
g_assert_not_reached ();
break;
}
/* this will take care of updating the contents of the dialog
* based on the action
*/
update_dialog_ui (dialog);
return object;
}
static void
gdict_source_dialog_class_init (GdictSourceDialogClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructor = gdict_source_dialog_constructor;
gobject_class->set_property = gdict_source_dialog_set_property;
gobject_class->get_property = gdict_source_dialog_get_property;
gobject_class->finalize = gdict_source_dialog_finalize;
g_object_class_install_property (gobject_class,
PROP_SOURCE_LOADER,
g_param_spec_object ("source-loader",
"Source Loader",
"The GdictSourceLoader used by the application",
GDICT_TYPE_SOURCE_LOADER,
(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
g_object_class_install_property (gobject_class,
PROP_SOURCE_NAME,
g_param_spec_string ("source-name",
"Source Name",
"The source name",
NULL,
(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
g_object_class_install_property (gobject_class,
PROP_ACTION,
g_param_spec_int ("action",
"Action",
"The action the source dialog should perform",
-1,
GDICT_SOURCE_DIALOG_EDIT,
GDICT_SOURCE_DIALOG_VIEW,
(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
}
static void
gdict_source_dialog_init (GdictSourceDialog *dialog)
{
gtk_widget_set_size_request (GTK_WIDGET (dialog), 400, 300);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
dialog->transport = GDICT_SOURCE_TRANSPORT_INVALID;
g_signal_connect (dialog, "response",
G_CALLBACK (gdict_source_dialog_response_cb),
NULL);
}
GtkWidget *
gdict_source_dialog_new (GtkWindow *parent,
const gchar *title,
GdictSourceDialogAction action,
GdictSourceLoader *loader,
const gchar *source_name)
{
GtkWidget *retval;
g_return_val_if_fail ((parent == NULL || GTK_IS_WINDOW (parent)), NULL);
g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL);
retval = g_object_new (GDICT_TYPE_SOURCE_DIALOG,
"source-loader", loader,
"source-name", source_name,
"action", action,
"title", title,
NULL);
if (parent)
{
gtk_window_set_transient_for (GTK_WINDOW (retval), parent);
gtk_window_set_destroy_with_parent (GTK_WINDOW (retval), TRUE);
gtk_window_set_screen (GTK_WINDOW (retval),
gtk_widget_get_screen (GTK_WIDGET (parent)));
}
return retval;
}