Port PidginAwayPrefs to PurpleSavedPresence and GSettings
I didn't port the stuff that uses these settings because that stuff is still incomplete.
Testing Done:
Set all of the settings in the dialog as well as from `pidgin3.ini` and verified everything updated properly.
Reviewed at https://reviews.imfreedom.org/r/2813/
--- a/ChangeLog.API Fri Nov 17 00:30:55 2023 -0600
+++ b/ChangeLog.API Fri Nov 17 00:59:47 2023 -0600
@@ -1197,6 +1197,7 @@
* pidgin_status_editor_show, use pidgin_status_editor_new instead.
* pidgin_status_get_handle
* pidgin_status_window_hide
* pidgin_status_window_show, use pidgin_status_manager_new instead.
--- a/libpurple/data/im.pidgin.Purple.gschema.xml Fri Nov 17 00:30:55 2023 -0600
+++ b/libpurple/data/im.pidgin.Purple.gschema.xml Fri Nov 17 00:59:47 2023 -0600
@@ -27,13 +27,13 @@
<enum id="im.pidgin.Purple.Idle.Method">
- <value nick="Never" value="0"/>
- <value nick="Purple" value="1"/>
- <value nick="System" value="2"/>
+ <value nick="none" value="0"/> + <value nick="purple" value="1"/> + <value nick="system" value="2"/> <schema path="/purple/idle/" id="im.pidgin.Purple.Idle">
<key name="method" enum="im.pidgin.Purple.Idle.Method">
- <default>"System"</default>
+ <default>"system"</default> <summary>Idle reporting method</summary>
The method to use to report idle time.
@@ -49,19 +49,38 @@
- <key name="change-status" type="b">
+ <key name="change-presence" type="b"> - <summary>Change status when idle</summary>
+ <summary>Change presence when idle</summary> - When going idle switch statuses.
+ When going idle switch presences. - <key name="status" type="s">
+ <key name="saved-presence" type="s"> - <summary>The status to use when idle</summary>
+ <summary>The saved presence to use when idle</summary> - The ID of the status to use when the user has gone idle.
+ The name of the saved presence to use when the user has gone idle. + <schema path="/purple/startup/" id="im.pidgin.Purple.Startup"> + <key name="use-previous-presence" type="b"> + <default>true</default> + <summary>Use the saved presence from last exit</summary> + Use the saved presence that was in use the last time the application + <key name="saved-presence" type="s"> + <summary>The saved presence to use at startup</summary> + The name of the saved presence to use at startup. --- a/pidgin/gtksavedstatuses.c Fri Nov 17 00:30:55 2023 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,300 +0,0 @@
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
-#include <glib/gi18n-lib.h>
-#include "gtksavedstatuses.h"
-#include "pidginiconname.h"
- SS_MENU_ENTRY_TYPE_PRIMITIVE,
- SS_MENU_ENTRY_TYPE_SAVEDSTATUS
- * This is a GdkPixbuf (the other columns are strings).
- * This column is visible.
- /* The text displayed on the status box. This column is visible. */
- * This value depends on SS_MENU_TYPE_COLUMN. For _SAVEDSTATUS types,
- * this is the creation time. For _PRIMITIVE types,
- * this is the PurpleStatusPrimitive.
- * This is the emblem to use for this status
- * And whether or not that emblem is visible
- SS_MENU_EMBLEM_VISIBLE_COLUMN,
-status_menu_cb(GtkComboBox *widget, void(*callback)(PurpleSavedStatus*))
- PurpleSavedStatus *status = NULL;
- if (!gtk_combo_box_get_active_iter(widget, &iter))
- gtk_tree_model_get(gtk_combo_box_get_model(widget), &iter,
- SS_MENU_TYPE_COLUMN, &type,
- SS_MENU_DATA_COLUMN, &data,
- if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE)
- PurpleStatusPrimitive primitive = GPOINTER_TO_INT(data);
- status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL);
- status = purple_savedstatus_new(NULL, primitive);
- else if (type == SS_MENU_ENTRY_TYPE_SAVEDSTATUS)
- status = purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data));
-saved_status_sort_alphabetically_func(gconstpointer a, gconstpointer b)
- const PurpleSavedStatus *saved_status_a = a;
- const PurpleSavedStatus *saved_status_b = b;
- return g_utf8_collate(purple_savedstatus_get_title(saved_status_a),
- purple_savedstatus_get_title(saved_status_b));
-pidgin_status_menu_add_primitive(GtkListStore *model,
- G_GNUC_UNUSED GtkWidget *w,
- PurpleStatusPrimitive primitive,
- PurpleSavedStatus *current_status)
- gboolean currently_selected = FALSE;
- gtk_list_store_append(model, &iter);
- gtk_list_store_set(model, &iter,
- SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_PRIMITIVE,
- SS_MENU_ICON_COLUMN, pidgin_icon_name_from_status_primitive(primitive, NULL),
- SS_MENU_TEXT_COLUMN, purple_primitive_get_name_from_type(primitive),
- SS_MENU_DATA_COLUMN, GINT_TO_POINTER(primitive),
- SS_MENU_EMBLEM_VISIBLE_COLUMN, FALSE,
- if (purple_savedstatus_is_transient(current_status)
- && !purple_savedstatus_has_substatuses(current_status)
- && purple_savedstatus_get_primitive_type(current_status) == primitive)
- currently_selected = TRUE;
- return currently_selected;
-pidgin_status_menu_update_iter(GtkWidget *combobox, GtkListStore *store, GtkTreeIter *iter,
- PurpleSavedStatus *status)
- PurpleStatusPrimitive primitive;
- store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
- primitive = purple_savedstatus_get_primitive_type(status);
- gtk_list_store_set(store, iter,
- SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS,
- SS_MENU_ICON_COLUMN, pidgin_icon_name_from_status_primitive(primitive, NULL),
- SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status),
- SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)),
- SS_MENU_EMBLEM_COLUMN, "document-save",
- SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE,
-pidgin_status_menu_find_iter(GtkListStore *store, GtkTreeIter *iter, PurpleSavedStatus *find)
- time_t creation_time = purple_savedstatus_get_creation_time(find);
- GtkTreeModel *model = GTK_TREE_MODEL(store);
- if (!gtk_tree_model_get_iter_first(model, iter))
- gtk_tree_model_get(model, iter,
- SS_MENU_TYPE_COLUMN, &type,
- SS_MENU_DATA_COLUMN, &data,
- if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE)
- if (GPOINTER_TO_INT(data) == creation_time)
- } while (gtk_tree_model_iter_next(model, iter));
-savedstatus_added_cb(PurpleSavedStatus *status, GtkWidget *combobox)
- if (purple_savedstatus_is_transient(status))
- store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
- gtk_list_store_append(store, &iter);
- pidgin_status_menu_update_iter(combobox, store, &iter, status);
-savedstatus_deleted_cb(PurpleSavedStatus *status, GtkWidget *combobox)
- if (purple_savedstatus_is_transient(status))
- store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
- if (pidgin_status_menu_find_iter(store, &iter, status))
- gtk_list_store_remove(store, &iter);
-savedstatus_modified_cb(PurpleSavedStatus *status, GtkWidget *combobox)
- if (purple_savedstatus_is_transient(status))
- store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
- if (pidgin_status_menu_find_iter(store, &iter, status))
- pidgin_status_menu_update_iter(combobox, store, &iter, status);
-GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callback)
- GtkCellRenderer *text_rend;
- GtkCellRenderer *icon_rend;
- GtkCellRenderer *emblem_rend;
- model = gtk_list_store_new(SS_MENU_NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
- combobox = gtk_combo_box_new();
- if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_AVAILABLE, current_status))
- if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_AWAY, current_status))
- if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_INVISIBLE, current_status))
- if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_OFFLINE, current_status))
- sorted = g_list_copy((GList *)purple_savedstatuses_get_all());
- sorted = g_list_sort(sorted, saved_status_sort_alphabetically_func);
- for (cur = sorted; cur; cur = cur->next)
- PurpleSavedStatus *status = (PurpleSavedStatus *) cur->data;
- if (!purple_savedstatus_is_transient(status))
- gtk_list_store_append(model, &iter);
- pidgin_status_menu_update_iter(combobox, model, &iter, status);
- if (status == current_status)
- gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(model));
- text_rend = gtk_cell_renderer_text_new();
- icon_rend = gtk_cell_renderer_pixbuf_new();
- emblem_rend = gtk_cell_renderer_pixbuf_new();
- gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), icon_rend, FALSE);
- gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), text_rend, TRUE);
- gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), emblem_rend, FALSE);
- gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), icon_rend, "icon-name", SS_MENU_ICON_COLUMN, NULL);
- gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), text_rend, "markup", SS_MENU_TEXT_COLUMN, NULL);
- gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), emblem_rend,
- "icon-name", SS_MENU_EMBLEM_COLUMN, "visible", SS_MENU_EMBLEM_VISIBLE_COLUMN, NULL);
- g_object_set(text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
- gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index);
- g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback);
- /* Make sure the list is updated dynamically when a substatus is changed/deleted
- * or a new one is added. */
- purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-added",
- combobox, G_CALLBACK(savedstatus_added_cb), combobox);
- purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-deleted",
- combobox, G_CALLBACK(savedstatus_deleted_cb), combobox);
- purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-modified",
- combobox, G_CALLBACK(savedstatus_modified_cb), combobox);
- g_signal_connect(G_OBJECT(combobox), "destroy",
- G_CALLBACK(purple_signals_disconnect_by_handle), NULL);
--- a/pidgin/gtksavedstatuses.h Fri Nov 17 00:30:55 2023 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
-#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
-# error "only <pidgin.h> may be included directly"
-#ifndef _PIDGINSAVEDSTATUSES_H_
-#define _PIDGINSAVEDSTATUSES_H_
-#include "pidginversion.h"
- * @status: The default saved_status to show as 'selected'
- * @callback: (scope call): The callback to call when the selection changes
- * Creates a dropdown menu of saved statuses and calls a callback
- * Returns: (transfer full): The menu widget
-GtkWidget *pidgin_status_menu(PurpleSavedStatus *status, GCallback callback);
-#endif /* _PIDGINSAVEDSTATUSES_H_ */
--- a/pidgin/meson.build Fri Nov 17 00:30:55 2023 -0600
+++ b/pidgin/meson.build Fri Nov 17 00:59:47 2023 -0600
@@ -11,7 +11,6 @@
@@ -81,7 +80,6 @@
--- a/pidgin/pidginapplication.c Fri Nov 17 00:30:55 2023 -0600
+++ b/pidgin/pidginapplication.c Fri Nov 17 00:59:47 2023 -0600
@@ -36,7 +36,6 @@
-#include "gtksavedstatuses.h"
#include "pidginaccounteditor.h"
--- a/pidgin/prefs/pidginawayprefs.c Fri Nov 17 00:30:55 2023 -0600
+++ b/pidgin/prefs/pidginawayprefs.c Fri Nov 17 00:59:47 2023 -0600
@@ -27,8 +27,8 @@
#include "pidginawayprefs.h"
-#include "gtksavedstatuses.h"
+#include "pidginiconname.h" #include "pidginprefsinternal.h"
struct _PidginAwayPrefs {
@@ -48,8 +48,7 @@
*****************************************************************************/
-idle_reporting_expression_cb(GObject *self, G_GNUC_UNUSED gpointer data)
+idle_reporting_expression_cb(GObject *self, G_GNUC_UNUSED gpointer data) { const gchar *value = NULL;
@@ -65,18 +64,136 @@
-set_idle_away(PurpleSavedStatus *status)
+idle_presence_icon_name_from_primitive(G_GNUC_UNUSED GObject *self, + PurplePresencePrimitive primitive, + G_GNUC_UNUSED gpointer data) + const char *icon_name = NULL; + icon_name = pidgin_icon_name_from_presence_primitive(primitive, + return g_strdup(icon_name); +idle_get_reporting_mapping(GValue *value, GVariant *variant, + G_GNUC_UNUSED gpointer data) - purple_prefs_set_int("/purple/savedstatus/idleaway",
- purple_savedstatus_get_creation_time(status));
+ id = g_variant_get_string(variant, NULL); + if(purple_strequal(id, "none")) { + g_value_set_uint(value, 0); + } else if(purple_strequal(id, "purple")) { + g_value_set_uint(value, 1); + } else if(purple_strequal(id, "system")) { + g_value_set_uint(value, 2); +idle_set_reporting_mapping(const GValue *value, const GVariantType *expected, + G_GNUC_UNUSED gpointer data) + if(!g_variant_type_equal(expected, G_VARIANT_TYPE_STRING)) { + switch(g_value_get_uint(value)) { + return g_variant_new_string(id);
-set_startupstatus(PurpleSavedStatus *status)
+idle_get_saved_presence_mapping(GValue *value, GVariant *variant, + G_GNUC_UNUSED gpointer data) - purple_prefs_set_int("/purple/savedstatus/startup",
- purple_savedstatus_get_creation_time(status));
+ GListModel *model = NULL; + const char *name = NULL; + model = purple_presence_manager_get_default_as_model(); + name = g_variant_get_string(variant, NULL); + n_items = g_list_model_get_n_items(model); + for(guint i = 0; i < n_items; i++) { + PurpleSavedPresence *presence = NULL; + presence = g_list_model_get_item(model, i); + if(PURPLE_IS_SAVED_PRESENCE(presence)) { + const char *presence_name = NULL; + presence_name = purple_saved_presence_get_name(presence); + if(purple_strequal(presence_name, name)) { + g_value_set_uint(value, i); + g_clear_object(&presence); + g_clear_object(&presence); + g_value_set_uint(value, 0); +idle_set_saved_presence_mapping(const GValue *value, + const GVariantType *expected, + G_GNUC_UNUSED gpointer data) + PurpleSavedPresence *presence = NULL; + GListModel *model = NULL; + if(!g_variant_type_equal(expected, G_VARIANT_TYPE_STRING)) { + model = purple_presence_manager_get_default_as_model(); + position = g_value_get_uint(value); + presence = g_list_model_get_item(model, position); + if(PURPLE_IS_SAVED_PRESENCE(presence)) { + const char *name = NULL; + name = purple_saved_presence_get_name(presence); + ret = g_variant_new_string(name); + g_clear_object(&presence); /******************************************************************************
@@ -106,47 +223,64 @@
gtk_widget_class_bind_template_child(widget_class, PidginAwayPrefs,
+ gtk_widget_class_bind_template_callback(widget_class, + idle_presence_icon_name_from_primitive); pidgin_away_prefs_init(PidginAwayPrefs *prefs)
+ GListModel *model = NULL; + GSettings *settings = NULL; + gpointer backend = NULL; gtk_widget_init_template(GTK_WIDGET(prefs));
- pidgin_prefs_bind_combo_row("/purple/away/idle_reporting",
- prefs->idle_reporting);
+ backend = purple_core_get_settings_backend(); + model = purple_presence_manager_get_default_as_model(); - pidgin_prefs_bind_spin_button("/purple/away/mins_before_away",
- prefs->mins_before_away);
+ /* Finish setting up our idle preferences. */ + adw_combo_row_set_model(ADW_COMBO_ROW(prefs->idle_row), model); - pidgin_prefs_bind_switch("/purple/away/away_when_idle",
- prefs->away_when_idle);
+ settings = g_settings_new_with_backend("im.pidgin.Purple.Idle", backend); - /* TODO: Show something useful if we don't have any saved statuses. */
- menu = pidgin_status_menu(purple_savedstatus_get_idleaway(),
- G_CALLBACK(set_idle_away));
- gtk_widget_set_valign(menu, GTK_ALIGN_CENTER);
- adw_action_row_add_suffix(ADW_ACTION_ROW(prefs->idle_row), menu);
- adw_action_row_set_activatable_widget(ADW_ACTION_ROW(prefs->idle_row),
- g_object_bind_property(prefs->away_when_idle, "active",
- G_BINDING_SYNC_CREATE);
+ g_settings_bind_with_mapping(settings, "method", + prefs->idle_reporting, "selected", + G_SETTINGS_BIND_DEFAULT, + idle_get_reporting_mapping, + idle_set_reporting_mapping, + g_settings_bind(settings, "duration", prefs->mins_before_away, + "value", G_SETTINGS_BIND_DEFAULT); + g_settings_bind(settings, "change-presence", prefs->away_when_idle, + "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind_with_mapping(settings, "saved-presence", + prefs->idle_row, "selected", + G_SETTINGS_BIND_DEFAULT, + idle_get_saved_presence_mapping, + idle_set_saved_presence_mapping, - /* Signon status stuff */
- pidgin_prefs_bind_switch("/purple/savedstatus/startup_current_status",
- prefs->startup_current_status);
+ g_clear_object(&settings); + /* Finish setting up the startup presence preferences. */ + adw_combo_row_set_model(ADW_COMBO_ROW(prefs->startup_row), model); + settings = g_settings_new_with_backend("im.pidgin.Purple.Startup", - /* TODO: Show something useful if we don't have any saved statuses. */
- menu = pidgin_status_menu(purple_savedstatus_get_startup(),
- G_CALLBACK(set_startupstatus));
- gtk_widget_set_valign(menu, GTK_ALIGN_CENTER);
- adw_action_row_add_suffix(ADW_ACTION_ROW(prefs->startup_row), menu);
- adw_action_row_set_activatable_widget(ADW_ACTION_ROW(prefs->startup_row),
+ g_settings_bind(settings, "use-previous-presence", + prefs->startup_current_status, "active", + G_SETTINGS_BIND_DEFAULT); + g_settings_bind_with_mapping(settings, "saved-presence", + prefs->startup_row, "selected", + G_SETTINGS_BIND_DEFAULT, + idle_get_saved_presence_mapping, + idle_set_saved_presence_mapping, + g_clear_object(&settings); /******************************************************************************
@@ -154,5 +288,5 @@
*****************************************************************************/
pidgin_away_prefs_new(void) {
- return GTK_WIDGET(g_object_new(PIDGIN_TYPE_AWAY_PREFS, NULL));
+ return g_object_new(PIDGIN_TYPE_AWAY_PREFS, NULL); --- a/pidgin/resources/Prefs/away.ui Fri Nov 17 00:30:55 2023 -0600
+++ b/pidgin/resources/Prefs/away.ui Fri Nov 17 00:59:47 2023 -0600
@@ -23,6 +23,44 @@
<!-- interface-name Pidgin -->
<!-- interface-description Internet Messenger -->
<!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+ <object class="GtkBuilderListItemFactory" id="saved_presence_factory"> + <property name="bytes"> +<?xml version="1.0" encoding="UTF-8"?> + <template class="GtkListItem"> + <property name="child"> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="spacing">6</property> + <object class="GtkImage"> + <binding name="icon-name"> + <closure type="gchararray" function="idle_presence_icon_name_from_primitive"> + <lookup name="primitive" type="PurpleSavedPresence"> + <lookup name="item">GtkListItem</lookup> + <object class="GtkLabel"> + <lookup name="name" type="PurpleSavedPresence"> + <lookup name="item">GtkListItem</lookup> <template class="PidginAwayPrefs" parent="AdwPreferencesPage">
<object class="AdwPreferencesGroup">
@@ -70,10 +108,11 @@
- <object class="AdwActionRow" id="idle_row">
+ <object class="AdwComboRow" id="idle_row"> <property name="activatable-widget">away_when_idle</property>
<property name="title" translatable="1">Change to this status when _idle</property>
<property name="use-underline">1</property>
+ <property name="factory">saved_presence_factory</property> <object class="GtkSwitch" id="away_when_idle">
<property name="focusable">1</property>
@@ -101,10 +140,11 @@
- <object class="AdwActionRow" id="startup_row">
+ <object class="AdwComboRow" id="startup_row"> <property name="title" translatable="1">Status to a_pply at startup</property>
<property name="use-underline">1</property>
<property name="sensitive" bind-source="startup_current_status" bind-property="active" bind-flags="sync-create|invert-boolean">0</property>
+ <property name="factory">saved_presence_factory</property> --- a/po/POTFILES.in Fri Nov 17 00:30:55 2023 -0600
+++ b/po/POTFILES.in Fri Nov 17 00:59:47 2023 -0600
@@ -241,7 +241,6 @@
-pidgin/gtksavedstatuses.c