/* This file is part of GNUnet (C) 2012 Christian Grothoff (and other contributing authors) GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/setup/gnunet-setup-gns-edit.c * @author Christian Grothoff * @brief editing dialogs for GNS records */ #include "gnunet-setup-gns-edit.h" #include /** * Columns in the 'zone_liststore'. */ enum ZoneListColumn { /** * A gchararray with the name of the zone for users. */ ZONELIST_COL_NAME = 0, /** * A gchararray with the name of the configuration file option with * the zone key. */ ZONELIST_COL_OPTION = 1 }; /** * Disable 'save' button, dialog state is not acceptable. * * @param edc dialog to modify */ static void edit_dialog_disable_save (struct EditDialogContext *edc) { gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_save_button")), FALSE); } /** * Enable 'save' button, dialog state is acceptable. * * @param edc dialog to modify */ static void edit_dialog_enable_save (struct EditDialogContext *edc) { gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_save_button")), TRUE); } /** * Check that the common elements of the edit dialog are valid; * if so, call 'edit_dialog_enable_save', otherwise 'edit_dialog_disable_save'. * * @param edc edit dialog to check */ static void edit_dialog_check_save (struct EditDialogContext *edc) { GtkEditable *entry; const gchar *name; /* check name */ entry = GTK_EDITABLE (gtk_builder_get_object (edc->builder, "edit_dialog_name_entry")); name = gtk_editable_get_chars (entry, 0, -1); if ( (GNUNET_SYSERR == GNUNET_DNSPARSER_check_label (name)) && (0 != strcmp (name, GNUNET_GNS_MASTERZONE_STR)) ) { edit_dialog_disable_save (edc); return; } /* any other checks should go here */ edit_dialog_enable_save (edc); } /** * The user has edited the A record value. Enable/disable 'save' * button depending on the validity of the value. * * @param entry editing widget * @param preedit new value * @param user_data the 'struct EditDialogContext' of the dialog */ void GNS_edit_dialog_name_entry_changed_cb (GtkEditable *entry, gpointer user_data) { struct EditDialogContext *edc = user_data; edc->validator (edc); } /** * The 'relative' expiration time radiobutton was toggled (on or off). * * @param button the button * @param user_data the 'struct EditDialogContext' of the dialog */ void GNS_edit_dialog_expiration_relative_radiobutton_toggled_cb (GtkToggleButton *button, gpointer user_data) { struct EditDialogContext *edc = user_data; if (gtk_toggle_button_get_active (button)) gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_combobox"))); else gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_combobox"))); } /** * The 'forever' expiration time radiobutton was toggled (on or off). * * @param button the button * @param user_data the 'struct EditDialogContext' of the dialog */ void GNS_edit_dialog_expiration_forever_radiobutton_toggled_cb (GtkToggleButton *button, gpointer user_data) { /* nothing to do */ } /** * The 'absolute' expiration time radiobutton was toggled (on or off). * * @param button the button * @param user_data the 'struct EditDialogContext' of the dialog */ void GNS_edit_dialog_expiration_absolute_radiobutton_toggled_cb (GtkToggleButton *button, gpointer user_data) { struct EditDialogContext *edc = user_data; if (gtk_toggle_button_get_active (button)) { gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_calendar"))); gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_hbox"))); } else { gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_calendar"))); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_hbox"))); } } /** * Initialize widgets of the edit dialog that are the same regardless of * the type of the record. * * @param edc dialog context */ static void edit_dialog_setup_common_elements (struct EditDialogContext *edc) { GtkComboBox *cb; GtkTreeModel *tm; GtkListStore *ls; GtkTreeIter iter; gchar *opt; struct GNUNET_TIME_Absolute at; struct GNUNET_TIME_Relative rt; time_t tp; struct tm *ymd; GtkCalendar *cal; if (GNUNET_YES != edc->old_record_in_namestore) { gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_delete_button"))); edit_dialog_disable_save (edc); } gtk_entry_set_text (GTK_ENTRY (gtk_builder_get_object (edc->builder, "edit_dialog_name_entry")), edc->n_new_name); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_options_public_checkbutton")), edc->n_public); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_options_shadow_checkbutton")), edc->n_is_shadow); if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value == edc->n_exp_time) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_never_radiobutton")), TRUE); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_calendar"))); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_hbox"))); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_combobox"))); } if ( (edc->n_is_relative) && (GNUNET_TIME_UNIT_FOREVER_REL.rel_value != edc->n_exp_time) ) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_radiobutton")), TRUE); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_calendar"))); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_hbox"))); rt.rel_value = edc->n_exp_time; } else { /* select a sane default */ rt = GNUNET_TIME_UNIT_DAYS; } cb = GTK_COMBO_BOX (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_combobox")); ls = GTK_LIST_STORE (gtk_combo_box_get_model (cb)); gtk_list_store_insert_with_values (ls, &iter, -1 /* position: append */, 0, GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_NO), -1); gtk_combo_box_set_active_iter (cb, &iter); if ( (! edc->n_is_relative) && (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value != edc->n_exp_time) ) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_radiobutton")), TRUE); gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_combobox"))); at.abs_value = edc->n_exp_time; } else { /* select a sane default: right now */ at = GNUNET_TIME_absolute_get (); } tp = (time_t) (at.abs_value / 1000LL); /* convert to seconds */ ymd = gmtime (&tp); cal = GTK_CALENDAR (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_calendar")); gtk_calendar_select_month (cal, ymd->tm_mon, ymd->tm_year + 1900); gtk_calendar_mark_day (cal, ymd->tm_mday); gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_hours_spinbutton")), (double) ymd->tm_hour); gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_minutes_spinbutton")), (double) ymd->tm_min); gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_seconds_spinbutton")), (double) ymd->tm_sec); /* set source zone! */ cb = GTK_COMBO_BOX (gtk_builder_get_object (edc->builder, "edit_dialog_zone_combobox")); tm = gtk_combo_box_get_model (cb); opt = NULL; if (gtk_tree_model_get_iter_first (tm, &iter)) { do { gtk_tree_model_get (tm, &iter, ZONELIST_COL_OPTION, &opt, -1); if ( (NULL != opt) && (0 == strcasecmp (opt, edc->new_zone_option)) ) break; g_free (opt); opt = NULL; } while (gtk_tree_model_iter_next (tm, &iter)); } if (NULL != opt) { gtk_combo_box_set_active_iter (cb, &iter); g_free (opt); } } /** * Perform the reverse of the 'edit_dialog_setup_common_elements' function, * that is, extract the values from the (common) widgets and store the * values in 'edc'. * * @param edc edit dialog to extract data from */ static void edit_dialog_putes_common_elements (struct EditDialogContext *edc) { GtkEntry *entry; const char *rt_s; struct GNUNET_TIME_Relative rt; GtkComboBox *cb; GtkTreeModel *tm; GtkTreeIter iter; gchar *opt; /* record name */ entry = GTK_ENTRY (gtk_builder_get_object (edc->builder, "edit_dialog_name_entry")); g_free (edc->n_new_name); edc->n_new_name = g_strdup (gtk_entry_get_text (entry)); /* is public flag */ edc->n_public = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_options_public_checkbutton"))); /* is shadow flag */ edc->n_is_shadow = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_options_shadow_checkbutton"))); /* 'forever' expiration time */ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_never_radiobutton")))) { edc->n_exp_time = GNUNET_TIME_UNIT_FOREVER_REL.rel_value; edc->n_is_relative = TRUE; /* doesn't matter, but make sure it is well-defined anyway */ } /* 'relative' expiration time */ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_radiobutton")))) { cb = GTK_COMBO_BOX (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_relative_combobox")); tm = gtk_combo_box_get_model (cb); if (TRUE != gtk_combo_box_get_active_iter (cb, &iter)) { GNUNET_break (0); return; } gtk_tree_model_get (tm, &iter, 0, &rt_s, -1); GNUNET_break (GNUNET_YES == GNUNET_STRINGS_fancy_time_to_relative (rt_s, &rt)); edc->n_exp_time = rt.rel_value; edc->n_is_relative = TRUE; } /* 'absolute' expiration time */ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_radiobutton")))) { guint year; guint month; guint day; guint hour; guint minute; guint second; char fancydate[128]; struct GNUNET_TIME_Absolute atime; gtk_calendar_get_date (GTK_CALENDAR (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_calendar")), &year, &month, &day); hour = gtk_spin_button_get_value (GTK_SPIN_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_hours_spinbutton"))); minute = gtk_spin_button_get_value (GTK_SPIN_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_minutes_spinbutton"))); second = gtk_spin_button_get_value (GTK_SPIN_BUTTON (gtk_builder_get_object (edc->builder, "edit_dialog_expiration_absolute_seconds_spinbutton"))); GNUNET_snprintf (fancydate, sizeof (fancydate), "%u-%u-%u %u:%u:%u", (unsigned int) year, (unsigned int) month + 1, (unsigned int) day, (unsigned int) hour, (unsigned int) minute, (unsigned int) second); GNUNET_break (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_absolute (fancydate, &atime)); edc->n_exp_time = atime.abs_value; edc->n_is_relative = FALSE; } /* extract target zone! */ cb = GTK_COMBO_BOX (gtk_builder_get_object (edc->builder, "edit_dialog_zone_combobox")); tm = gtk_combo_box_get_model (cb); if (! gtk_combo_box_get_active_iter (cb, &iter)) { GNUNET_break (0); } else { gtk_tree_model_get (tm, &iter, ZONELIST_COL_OPTION, &opt, -1); if (NULL == opt) GNUNET_break (0); else { g_free (edc->new_zone_option); edc->new_zone_option = g_strdup (opt); g_free (opt); } } } /** * Run the edit dialog. Performs all of the common initialization * steps to run an edit dialog for records. * * @param edc editing context */ static void run_edit_dialog (struct EditDialogContext *edc) { edit_dialog_setup_common_elements (edc); gtk_dialog_set_default_response (edc->dialog, GTK_RESPONSE_OK); gtk_window_present (GTK_WINDOW (edc->dialog)); } /* ************************ A records *********************** */ /** * Check validity of the value in the edit dialog for A-records. * Then call the shared validity check if the result is OK. * * @param edc edit dialog context */ static void edit_dialog_a_validity_check (struct EditDialogContext *edc) { GtkEditable *entry; const gchar *preedit; struct in_addr v4; entry = GTK_EDITABLE (gtk_builder_get_object (edc->builder, "edit_dialog_a_entry")), preedit = gtk_editable_get_chars (entry, 0, -1); if ( (NULL == preedit) || (1 != inet_pton (AF_INET, preedit, &v4)) ) { edit_dialog_disable_save (edc); return; } edit_dialog_check_save (edc); } /** * Editing dialog was closed, get the data and call the * continuation. * * @param dialog editing dialog * @param user_data the 'struct EditDialogContext' */ void GNS_edit_a_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { struct EditDialogContext *edc = user_data; GtkEntry *entry; const gchar *value; if (GTK_RESPONSE_OK == response_id) { edit_dialog_putes_common_elements (edc); entry = GTK_ENTRY (gtk_builder_get_object (edc->builder, "edit_dialog_a_entry")); value = gtk_entry_get_text (entry); g_free (edc->n_value); edc->n_value = g_strdup (value); } gtk_widget_destroy (GTK_WIDGET (edc->dialog)); g_object_unref (edc->builder); edc->builder = NULL; edc->cont (edc, response_id); } /** * The user has edited the A record value. Enable/disable 'save' * button depending on the validity of the value. * * @param entry editing widget * @param preedit new value * @param user_data the 'struct EditDialogContext' of the dialog */ void GNS_edit_dialog_a_entry_changed_cb (GtkEditable *entry, gpointer user_data) { struct EditDialogContext *edc = user_data; edit_dialog_a_validity_check (edc); } /** * Run an GNS Edit dialog for an 'A' Record. * * @param cont continuation to call when done * @param edc editing context to use */ void GNS_edit_dialog_a (struct EditDialogContext *edc) { edc->builder = GNUNET_GTK_get_new_builder ("gnunet_setup_gns_edit_a.glade", edc); if (NULL == edc->builder) { GNUNET_break (0); edc->cont (edc, GTK_RESPONSE_CANCEL); /* treat as 'cancel' */ return; } if (GNUNET_YES == edc->old_record_in_namestore) { /* set A record */ gtk_entry_set_text (GTK_ENTRY (gtk_builder_get_object (edc->builder, "edit_dialog_a_entry")), edc->n_value); } edc->validator = &edit_dialog_a_validity_check; edc->dialog = GTK_DIALOG (gtk_builder_get_object (edc->builder, "edit_a_dialog")); run_edit_dialog (edc); } /* end of gnunet-setup-gns-edit.c */