/* $Id: phone.c,v 1.205.2.22 2011/01/23 00:52:07 khorben Exp $ */ /* Copyright (c) 2011 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Phone */ /* 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, version 3 of the License. * * 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, see . */ #include #include #include #include #include #include #include #include #include #include #include "Phone.h" #include "gsm.h" #include "callbacks.h" #include "phone.h" #include "../config.h" #define _(string) gettext(string) #ifndef PREFIX # define PREFIX "/usr/local" #endif #ifndef LIBDIR # define LIBDIR PREFIX "/lib" #endif /* Phone */ /* private */ /* types */ typedef enum _PhoneCallType { PHONE_CALL_TYPE_OUTGOING = 0, PHONE_CALL_TYPE_INCOMING, PHONE_CALL_TYPE_MISSED } PhoneCallType; typedef enum _PhoneContactColumn { PHONE_CONTACT_COLUMN_ID = 0, PHONE_CONTACT_COLUMN_NAME, PHONE_CONTACT_COLUMN_NUMBER } PhoneContactColumn; #define PHONE_CONTACT_COLUMN_LAST PHONE_CONTACT_COLUMN_NUMBER #define PHONE_CONTACT_COLUMN_COUNT (PHONE_CONTACT_COLUMN_LAST + 1) typedef enum _PhoneLogsColumn { PHONE_LOGS_COLUMN_CALL_TYPE = 0, PHONE_LOGS_COLUMN_CALL_TYPE_DISPLAY, PHONE_LOGS_COLUMN_NUMBER, PHONE_LOGS_COLUMN_DATE, PHONE_LOGS_COLUMN_DATE_DISPLAY } PhoneLogsColumn; #define PHONE_LOGS_COLUMN_LAST PHONE_LOGS_COLUMN_DATE_DISPLAY #define PHONE_LOGS_COLUMN_COUNT (PHONE_LOGS_COLUMN_LAST + 1) typedef enum _PhoneMessageColumn { PHONE_MESSAGE_COLUMN_ID = 0, PHONE_MESSAGE_COLUMN_NUMBER, PHONE_MESSAGE_COLUMN_NUMBER_DISPLAY, PHONE_MESSAGE_COLUMN_DATE, PHONE_MESSAGE_COLUMN_DATE_DISPLAY, PHONE_MESSAGE_COLUMN_CONTENT } PhoneMessageColumn; #define PHONE_MESSAGE_COLUMN_LAST PHONE_MESSAGE_COLUMN_CONTENT #define PHONE_MESSAGE_COLUMN_COUNT (PHONE_MESSAGE_COLUMN_LAST + 1) typedef struct _PhonePluginEntry { Plugin * p; PhonePlugin * pp; } PhonePluginEntry; typedef enum _PhonePluginsColumn { PHONE_PLUGINS_COLUMN_PLUGIN, PHONE_PLUGINS_COLUMN_ENABLED, PHONE_PLUGINS_COLUMN_FILENAME, PHONE_PLUGINS_COLUMN_ICON, PHONE_PLUGINS_COLUMN_NAME } PhonePluginsColumn; # define PHONE_PLUGINS_COLUMN_LAST PHONE_PLUGINS_COLUMN_NAME # define PHONE_PLUGINS_COLUMN_COUNT (PHONE_PLUGINS_COLUMN_LAST + 1) typedef void (*PhoneSettingsCallback)(Phone * phone, gboolean show, ...); typedef enum _PhoneSettingsColumn { PHONE_SETTINGS_COLUMN_CALLBACK, PHONE_SETTINGS_COLUMN_PLUGIN, PHONE_SETTINGS_COLUMN_ICON, PHONE_SETTINGS_COLUMN_NAME } PhoneSettingsColumn; # define PHONE_SETTINGS_COLUMN_LAST PHONE_SETTINGS_COLUMN_NAME # define PHONE_SETTINGS_COLUMN_COUNT (PHONE_SETTINGS_COLUMN_LAST + 1) typedef enum _PhoneTrack { PHONE_TRACK_CODE_ENTERED = 0, PHONE_TRACK_CONTACT_LIST, PHONE_TRACK_MESSAGE_LIST, PHONE_TRACK_MESSAGE_SENT, PHONE_TRACK_REGISTRATION } PhoneTrack; #define PHONE_TRACK_LAST PHONE_TRACK_REGISTRATION #define PHONE_TRACK_COUNT (PHONE_TRACK_LAST + 1) struct _Phone { GSM * gsm; guint source; Config * config; /* tracking */ guint tr_source; gboolean tracks[PHONE_TRACK_COUNT]; /* plugins */ PhonePluginHelper helper; PhonePluginEntry * plugins; size_t plugins_cnt; /* widgets */ PangoFontDescription * bold; /* call */ GtkWidget * ca_window; GtkWidget * ca_name; GtkWidget * ca_number; GtkWidget * ca_answer; GtkWidget * ca_hangup; GtkWidget * ca_image; GtkWidget * ca_reject; GtkWidget * ca_close; GtkWidget * ca_volume; GtkWidget * ca_speaker; GtkWidget * ca_mute; /* code */ ModemAuthenticationMethod en_method; char * en_name; GtkWidget * en_window; GtkWidget * en_title; GtkWidget * en_entry; GtkWidget * en_progress; /* contacts */ GtkWidget * co_window; GtkListStore * co_store; GtkWidget * co_view; /* dialog */ int co_index; GtkWidget * co_dialog; GtkWidget * co_name; GtkWidget * co_number; /* dialer */ GtkWidget * di_window; GtkWidget * di_entry; /* logs */ GtkWidget * lo_window; GtkListStore * lo_store; GtkWidget * lo_view; /* messages */ GtkWidget * me_window; GtkListStore * me_store; GtkWidget * me_view; GtkWidget * me_progress; /* plugins */ GtkWidget * pl_window; GtkListStore * pl_store; GtkWidget * pl_view; /* read */ unsigned int re_index; GtkWidget * re_window; GtkWidget * re_name; GtkWidget * re_number; GtkWidget * re_date; GtkWidget * re_view; /* settings */ GtkWidget * se_window; GtkListStore * se_store; GtkWidget * se_view; /* system preferences */ GtkWidget * sy_window; /* write */ GtkWidget * wr_window; GtkWidget * wr_entry; GtkWidget * wr_count; GtkWidget * wr_view; GtkWidget * wr_progress; }; /* constants */ #define PHONE_CONFIG_FILE ".phone" /* prototypes */ static int _phone_call_number(Phone * phone, char const * number); static void _phone_config_foreach(Phone * phone, char const * section, PhoneConfigForeachCallback callback, void * priv); static char * _phone_config_filename(void); static char const * _phone_config_get(Phone * phone, char const * section, char const * variable); static int _phone_config_save(Phone * phone); static int _phone_config_set(Phone * phone, char const * section, char const * variable, char const * value); static int _phone_config_set_type(Phone * phone, char const * type, char const * section, char const * variable, char const * value); static GtkWidget * _phone_create_button(char const * icon, char const * label); static GtkWidget * _phone_create_dialpad(Phone * phone, char const * button1_image, char const * button1_label, GCallback button1_callback, char const * button2_image, char const * button2_label, GCallback button2_callback, GCallback button_callback); static GtkWidget * _phone_create_progress(GtkWidget * parent, char const * text); static int _phone_confirm(Phone * phone, GtkWidget * window, char const * message); static int _phone_error(GtkWidget * window, char const * message, int ret); static void _phone_info(Phone * phone, GtkWidget * window, char const * message, GCallback callback); static GtkWidget * _phone_progress_delete(GtkWidget * widget); static void _phone_progress_pulse(GtkWidget * widget); static int _phone_queue(Phone * phone, char const * command); static int _phone_request(Phone * phone, ModemRequest * request); static void _phone_show_contacts_dialog(Phone * phone, gboolean show, int index, char const * name, char const * number); static void _phone_track(Phone * phone, PhoneTrack what, gboolean track); static int _phone_trigger(Phone * phone, ModemEventType event); static int _phone_unload(Phone * phone, PhonePlugin * plugin); /* callbacks */ static void _phone_modem_event(void * priv, ModemEvent * event); static void _phone_modem_event_authentication(GtkWidget * widget, gint response, gpointer data); static gboolean _phone_timeout_track(gpointer data); /* public */ /* functions */ /* phone_new */ static int _new_config(Phone * phone); static gboolean _new_idle(gpointer data); static void _idle_settings(Phone * phone); static void _idle_load_plugins(Phone * phone, char const * plugins); Phone * phone_new(char const * plugin, int retry) { Phone * phone; char const * p; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%d)\n", __func__, retry); #endif if((phone = object_new(sizeof(*phone))) == NULL) return NULL; if(_new_config(phone) != 0) { object_delete(phone); return NULL; } if(plugin == NULL && (plugin = config_get(phone->config, NULL, "modem")) == NULL) plugin = "hayes"; if(retry < 0) { retry = 1000; if((p = config_get(phone->config, NULL, "retry")) != NULL) retry = strtoul(p, NULL, 10); } phone->gsm = gsm_new(phone->config, plugin, retry); phone->source = 0; phone->tr_source = 0; memset(&phone->tracks, 0, sizeof(phone->tracks)); phone->helper.config_foreach = _phone_config_foreach; phone->helper.config_get = _phone_config_get; phone->helper.config_set = _phone_config_set; phone->helper.error = phone_error; phone->helper.event = phone_event; phone->helper.queue = _phone_queue; phone->helper.request = _phone_request; phone->helper.trigger = _phone_trigger; phone->helper.phone = phone; phone->plugins = NULL; phone->plugins_cnt = 0; /* widgets */ phone->bold = pango_font_description_new(); pango_font_description_set_weight(phone->bold, PANGO_WEIGHT_BOLD); phone->ca_window = NULL; phone->en_window = NULL; phone->en_method = MODEM_AUTHENTICATION_METHOD_NONE; phone->en_name = NULL; phone->en_progress = NULL; phone->co_window = NULL; phone->co_store = gtk_list_store_new(PHONE_CONTACT_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); phone->co_dialog = NULL; phone->di_window = NULL; phone->lo_window = NULL; phone->lo_store = gtk_list_store_new(PHONE_LOGS_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING); phone->me_window = NULL; phone->me_store = gtk_list_store_new(PHONE_MESSAGE_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); phone->pl_window = NULL; phone->pl_store = gtk_list_store_new(PHONE_PLUGINS_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING); phone->re_index = 0; phone->re_window = NULL; phone->se_window = NULL; phone->sy_window = NULL; phone->se_store = gtk_list_store_new(PHONE_SETTINGS_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING); phone->wr_window = NULL; phone->wr_progress = NULL; /* check errors */ if(phone->gsm == NULL) { phone_error(NULL, error_get(), 1); phone_delete(phone); return NULL; } phone->source = g_idle_add(_new_idle, phone); gsm_set_callback(phone->gsm, _phone_modem_event, phone); return phone; } static int _new_config(Phone * phone) { char * filename; if((phone->config = config_new()) == NULL) return -1; if((filename = _phone_config_filename()) == NULL) return -1; config_load(phone->config, filename); /* we can ignore errors */ free(filename); return 0; } static gboolean _new_idle(gpointer data) { Phone * phone = data; char const * plugins; PhoneEvent event; phone->source = 0; phone_show_call(phone, FALSE); phone_show_contacts(phone, FALSE); phone_show_dialer(phone, FALSE); phone_show_messages(phone, FALSE); phone_show_read(phone, FALSE); phone_show_settings(phone, FALSE); phone_show_system(phone, FALSE); phone_show_write(phone, FALSE); _idle_settings(phone); if((plugins = config_get(phone->config, NULL, "plugins")) != NULL) _idle_load_plugins(phone, plugins); /* try to go online */ memset(&event, 0, sizeof(event)); event.type = PHONE_EVENT_TYPE_STARTING; if(phone_event(phone, &event) == 0) gsm_start(phone->gsm, 0); return FALSE; } static void _idle_settings(Phone * phone) { GtkTreeIter iter; GtkIconTheme * theme; GdkPixbuf * pixbuf; gtk_list_store_append(phone->se_store, &iter); gtk_list_store_set(phone->se_store, &iter, PHONE_SETTINGS_COLUMN_CALLBACK, phone_show_system, PHONE_SETTINGS_COLUMN_PLUGIN, NULL, PHONE_SETTINGS_COLUMN_NAME, _("System preferences"), -1); theme = gtk_icon_theme_get_default(); if((pixbuf = gtk_icon_theme_load_icon(theme, "gnome-settings", 48, 0, NULL)) != NULL) gtk_list_store_set(phone->se_store, &iter, PHONE_SETTINGS_COLUMN_ICON, pixbuf, -1); gtk_list_store_append(phone->se_store, &iter); gtk_list_store_set(phone->se_store, &iter, PHONE_SETTINGS_COLUMN_CALLBACK, phone_show_plugins, PHONE_SETTINGS_COLUMN_PLUGIN, NULL, PHONE_SETTINGS_COLUMN_NAME, _("Plug-ins"), -1); theme = gtk_icon_theme_get_default(); if((pixbuf = gtk_icon_theme_load_icon(theme, "gnome-settings", 48, 0, NULL)) != NULL) gtk_list_store_set(phone->se_store, &iter, PHONE_SETTINGS_COLUMN_ICON, pixbuf, -1); } static void _idle_load_plugins(Phone * phone, char const * plugins) { char * p; char * q; size_t i; if((p = strdup(plugins)) == NULL) return; for(q = p, i = 0;;) { if(q[i] == '\0') { phone_load(phone, q); /* we can ignore errors */ break; } if(q[i++] != ',') continue; q[i - 1] = '\0'; phone_load(phone, q); /* we can ignore errors */ q += i; i = 0; } free(p); } /* phone_delete */ void phone_delete(Phone * phone) { PhoneEvent event; memset(&event, 0, sizeof(event)); event.type = PHONE_EVENT_TYPE_STOPPING; phone_event(phone, &event); phone_unload_all(phone); if(phone->config != NULL) config_delete(phone->config); if(phone->source != 0) g_source_remove(phone->source); if(phone->tr_source != 0) g_source_remove(phone->tr_source); pango_font_description_free(phone->bold); if(phone->gsm != NULL) gsm_delete(phone->gsm); free(phone->en_name); object_delete(phone); } /* useful */ /* phone_error */ static int _error_text(char const * message, int ret); int phone_error(Phone * phone, char const * message, int ret) { if(phone == NULL) return _error_text(message, ret); return _phone_error(NULL, message, ret); } static int _error_text(char const * message, int ret) { fprintf(stderr, "phone: %s\n", message); return ret; } /* calls */ void phone_call_answer(Phone * phone) { gsm_call_answer(phone->gsm); } /* phone_call_hangup */ void phone_call_hangup(Phone * phone) { /* XXX reject seemed to work better than hangup */ gsm_call_hangup(phone->gsm); } /* phone_call_mute */ void phone_call_mute(Phone * phone, gboolean mute) { /* FIXME implement again */ } /* phone_call_reject */ void phone_call_reject(Phone * phone) { gsm_call_hangup(phone->gsm); } /* phone_call_set_volume */ void phone_call_set_volume(Phone * phone, gdouble volume) { PhoneEvent event; memset(&event, 0, sizeof(event)); event.type = PHONE_EVENT_TYPE_SET_VOLUME; event.volume_set.level = volume; phone_event(phone, &event); } /* phone_call_speaker */ void phone_call_speaker(Phone * phone, gboolean speaker) { PhoneEvent event; memset(&event, 0, sizeof(event)); event.type = speaker ? PHONE_EVENT_TYPE_SPEAKER_ON : PHONE_EVENT_TYPE_SPEAKER_OFF; phone_event(phone, &event); } /* phone_code_append */ int phone_code_append(Phone * phone, char character) { char const * text; size_t len; char * p; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%c)\n", __func__, character); #endif if((character < '0' || character > '9') && character != '*' && character != '+' && character != '#') return 1; /* ignore the error */ text = gtk_entry_get_text(GTK_ENTRY(phone->en_entry)); len = strlen(text); if((p = malloc(len + 2)) == NULL) return phone_error(phone, strerror(errno), 1); snprintf(p, len + 2, "%s%c", text, character); gtk_entry_set_text(GTK_ENTRY(phone->en_entry), p); free(p); return 0; } /* phone_code_enter */ void phone_code_enter(Phone * phone) { char const * p; char buf[32]; if(phone->en_window == NULL) return; snprintf(buf, sizeof(buf), _("Checking %s..."), phone->en_name); switch(phone->en_method) { case MODEM_AUTHENTICATION_METHOD_PIN: p = gtk_entry_get_text(GTK_ENTRY(phone->en_entry)); phone->en_progress = _phone_create_progress( phone->en_window, buf); _phone_track(phone, PHONE_TRACK_CODE_ENTERED, TRUE); gsm_enter_sim_pin(phone->gsm, p); break; default: break; } } /* code */ /* phone_code_clear */ void phone_code_clear(Phone * phone) { _phone_track(phone, PHONE_TRACK_CODE_ENTERED, FALSE); phone->en_progress = _phone_progress_delete(phone->en_progress); if(phone->en_window != NULL) gtk_entry_set_text(GTK_ENTRY(phone->en_entry), ""); } /* contacts */ /* phone_contacts_call_selected */ void phone_contacts_call_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; unsigned int index; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->co_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->co_store), &iter, PHONE_CONTACT_COLUMN_ID, &index, -1); gsm_call_contact(phone->gsm, GSM_CALL_TYPE_VOICE, index); } /* phone_contacts_delete_selected */ void phone_contacts_delete_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; int index; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->co_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->co_store), &iter, PHONE_CONTACT_COLUMN_ID, &index, -1); if(_phone_confirm(phone, phone->co_window, _("Delete this contact?")) != 0) return; gtk_list_store_remove(phone->co_store, &iter); /* XXX it may fail */ gsm_contact_delete(phone->gsm, index); } /* phone_contacts_edit_selected */ void phone_contacts_edit_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; int index; gchar * name; gchar * number; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->co_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->co_store), &iter, PHONE_CONTACT_COLUMN_ID, &index, PHONE_CONTACT_COLUMN_NAME, &name, PHONE_CONTACT_COLUMN_NUMBER, &number, -1); _phone_show_contacts_dialog(phone, TRUE, index, name, number); g_free(name); g_free(number); } /* phone_contacts_new */ void phone_contacts_new(Phone * phone) { _phone_show_contacts_dialog(phone, TRUE, -1, "", ""); } /* phone_contacts_set */ void phone_contacts_set(Phone * phone, unsigned int index, char const * name, char const * number) { GtkTreeModel * model = GTK_TREE_MODEL(phone->co_store); GtkTreeIter iter; gboolean valid; unsigned int id; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u, \"%s\", \"%s\")\n", __func__, index, name, number); #endif valid = gtk_tree_model_get_iter_first(model, &iter); for(; valid == TRUE; valid = gtk_tree_model_iter_next(model, &iter)) { gtk_tree_model_get(model, &iter, PHONE_CONTACT_COLUMN_ID, &id, -1); if(id == index) break; } if(valid != TRUE) gtk_list_store_append(phone->co_store, &iter); gtk_list_store_set(phone->co_store, &iter, PHONE_CONTACT_COLUMN_ID, index, PHONE_CONTACT_COLUMN_NAME, name, PHONE_CONTACT_COLUMN_NUMBER, number, -1); } /* phone_contacts_write_selected */ void phone_contacts_write_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; gchar * number = NULL; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->co_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) == TRUE) gtk_tree_model_get(GTK_TREE_MODEL(phone->co_store), &iter, PHONE_CONTACT_COLUMN_NUMBER, &number, -1); phone_messages_write(phone, number, NULL); g_free(number); } /* dialer */ /* phone_dialer_append */ int phone_dialer_append(Phone * phone, char character) { char const * text; size_t len; char * p; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%c)\n", __func__, character); #endif if(phone->di_window == NULL) return -1; if((character < '0' || character > '9') && character != '*' && character != '+' && character != '#') return -1; /* ignore the error */ /* FIXME if in a call send DTMF instead */ text = gtk_entry_get_text(GTK_ENTRY(phone->di_entry)); len = strlen(text) + 2; if((p = malloc(len)) == NULL) return -phone_error(phone, strerror(errno), 1); snprintf(p, len, "%s%c", text, character); gtk_entry_set_text(GTK_ENTRY(phone->di_entry), p); free(p); return 0; } /* phone_dialer_call */ void phone_dialer_call(Phone * phone, char const * number) { /* FIXME check if it's either a name or number */ if(number == NULL) number = gtk_entry_get_text(GTK_ENTRY(phone->di_entry)); if(number[0] == '\0') number = NULL; /* call the last number dialled */ _phone_call_number(phone, number); } /* phone_dialer_hangup */ void phone_dialer_hangup(Phone * phone) { gsm_call_hangup(phone->gsm); if(phone->di_window != NULL) gtk_entry_set_text(GTK_ENTRY(phone->di_entry), ""); } /* events */ /* phone_event */ int phone_event(Phone * phone, PhoneEvent * event) { int ret = 0; size_t i; PhonePlugin * plugin; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u)\n", __func__, event->type); #endif for(i = 0; i < phone->plugins_cnt; i++) { plugin = phone->plugins[i].pp; if(plugin->event == NULL) continue; ret |= plugin->event(plugin, event); } return ret; } /* plugins */ /* phone_load */ int phone_load(Phone * phone, char const * plugin) { Plugin * p; PhonePlugin * pp; PhonePluginEntry * q; GtkTreeIter iter; GtkIconTheme * theme; GdkPixbuf * pixbuf; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, plugin); #endif if((p = plugin_new(LIBDIR, PACKAGE, "plugins", plugin)) == NULL) return phone_error(NULL, error_get(), -1); if((pp = plugin_lookup(p, "plugin")) == NULL) { plugin_delete(p); return phone_error(NULL, error_get(), -1); } pp->helper = &phone->helper; if(pp->init != NULL && pp->init(pp) != 0) { plugin_delete(p); return phone_error(NULL, error_get(), -1); } if((q = realloc(phone->plugins, sizeof(*q) * (phone->plugins_cnt + 1))) == NULL) { plugin_delete(p); return phone_error(NULL, strerror(errno), -1); } phone->plugins = q; phone->plugins[phone->plugins_cnt].p = p; phone->plugins[phone->plugins_cnt++].pp = pp; if(pp->name != NULL && pp->settings != NULL) { gtk_list_store_append(GTK_LIST_STORE(phone->se_store), &iter); gtk_list_store_set(phone->se_store, &iter, PHONE_SETTINGS_COLUMN_CALLBACK, NULL, PHONE_SETTINGS_COLUMN_PLUGIN, pp, PHONE_SETTINGS_COLUMN_NAME, pp->name, -1); theme = gtk_icon_theme_get_default(); if(pp->icon == NULL) pp->icon = "gnome-settings"; if((pixbuf = gtk_icon_theme_load_icon(theme, pp->icon, 48, 0, NULL)) != NULL) gtk_list_store_set(phone->se_store, &iter, PHONE_SETTINGS_COLUMN_ICON, pixbuf, -1); } return 0; } /* logs */ /* phone_logs_call_selected */ void phone_logs_call_selected(Phone * phone) /* XXX code duplication */ { GtkTreeSelection * treesel; GtkTreeIter iter; gchar * number = NULL; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->lo_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->lo_store), &iter, PHONE_LOGS_COLUMN_NUMBER, &number, -1); if(number == NULL) return; _phone_call_number(phone, number); g_free(number); } /* phone_logs_clear */ void phone_logs_clear(Phone * phone) { gtk_list_store_clear(phone->lo_store); } /* phone_logs_write_selected */ void phone_logs_write_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; gchar * number = NULL; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->lo_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->lo_store), &iter, PHONE_LOGS_COLUMN_NUMBER, &number, -1); if(number == NULL) return; phone_show_write(phone, TRUE, number, ""); g_free(number); } /* messages */ /* phone_messages_call_selected */ void phone_messages_call_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; gchar * number = NULL; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->me_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->me_store), &iter, PHONE_MESSAGE_COLUMN_NUMBER, &number, -1); if(number == NULL) return; _phone_call_number(phone, number); g_free(number); } /* phone_messages_delete_selected */ void phone_messages_delete_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; unsigned int index; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->me_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->me_store), &iter, PHONE_MESSAGE_COLUMN_ID, &index, -1); if(_phone_confirm(phone, phone->me_window, _("Delete this message?")) != 0) return; if(phone->re_index == index) phone_show_read(phone, FALSE); phone->me_progress = _phone_create_progress(phone->me_window, _("Deleting message...")); gsm_message_delete(phone->gsm, index); } /* phone_messages_read_selected */ void phone_messages_read_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; unsigned int index; char * number; unsigned int date; char * content; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->me_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->me_store), &iter, PHONE_MESSAGE_COLUMN_ID, &index, PHONE_MESSAGE_COLUMN_NUMBER, &number, PHONE_MESSAGE_COLUMN_DATE, &date, PHONE_MESSAGE_COLUMN_CONTENT, &content, -1); phone_show_read(phone, TRUE, index, NULL, number, date, content); g_free(number); g_free(content); } /* phone_messages_set */ void phone_messages_set(Phone * phone, unsigned int index, char const * number, time_t date, char const * content) { GtkTreeModel * model = GTK_TREE_MODEL(phone->me_store); GtkTreeIter iter; gboolean valid; unsigned int id; char nd[32]; char dd[32]; struct tm t; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u, \"%s\", \"%s\")\n", __func__, index, number, content); #endif valid = gtk_tree_model_get_iter_first(model, &iter); for(; valid == TRUE; valid = gtk_tree_model_iter_next(model, &iter)) { gtk_tree_model_get(model, &iter, PHONE_MESSAGE_COLUMN_ID, &id, -1); if(id == index) break; } if(valid != TRUE) gtk_list_store_append(phone->me_store, &iter); if(number == NULL) number = ""; if(content == NULL) content = ""; /* FIXME this may cut in the middle of a UTF-8 character */ snprintf(nd, sizeof(nd), "%s\n%.12s%s", number, content, (strlen(content) > 12) ? "..." : ""); localtime_r(&date, &t); /* XXX gmtime_r() or localtime_r() ? */ strftime(dd, sizeof(dd), "%d/%m/%Y %H:%M:%S", &t); gtk_list_store_set(phone->me_store, &iter, PHONE_MESSAGE_COLUMN_ID, index, PHONE_MESSAGE_COLUMN_NUMBER, number, PHONE_MESSAGE_COLUMN_NUMBER_DISPLAY, nd, PHONE_MESSAGE_COLUMN_DATE, date, PHONE_MESSAGE_COLUMN_DATE_DISPLAY, dd, PHONE_MESSAGE_COLUMN_CONTENT, content, -1); } /* phone_messages_write */ void phone_messages_write(Phone * phone, char const * number, char const * text) { GtkTextBuffer * tbuf; phone_show_write(phone, TRUE, "", ""); if(number != NULL) gtk_entry_set_text(GTK_ENTRY(phone->wr_entry), number); if(text != NULL) { tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->wr_view)); gtk_text_buffer_set_text(tbuf, text, strlen(text)); } } /* read */ /* phone_read_call */ void phone_read_call(Phone * phone) { char const * number; if(phone->re_window == NULL) return; if((number = gtk_label_get_text(GTK_LABEL(phone->re_number))) == NULL) return; _phone_call_number(phone, number); } /* phone_read_delete */ void phone_read_delete(Phone * phone) { if(_phone_confirm(phone, phone->re_window, _("Delete this message?")) != 0) return; phone_show_read(phone, FALSE); phone->me_progress = _phone_create_progress(phone->me_window, _("Deleting message...")); gsm_message_delete(phone->gsm, phone->re_index); } /* phone_read_forward */ void phone_read_forward(Phone * phone) { GtkTextBuffer * tbuf; GtkTextIter start; GtkTextIter end; gchar * text; if(phone->re_window == NULL) return; tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->re_view)); gtk_text_buffer_get_start_iter(tbuf, &start); gtk_text_buffer_get_end_iter(tbuf, &end); if((text = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE)) == NULL) return; phone_show_write(phone, TRUE, "", text); g_free(text); } /* phone_read_reply */ void phone_read_reply(Phone * phone) { char const * number; if(phone->re_window == NULL) return; if((number = gtk_label_get_text(GTK_LABEL(phone->re_number))) == NULL) return; phone_show_write(phone, TRUE, number, ""); } /* settings */ /* phone_settings_open_selected */ void phone_settings_open_selected(Phone * phone) { GtkTreeSelection * treesel; GtkTreeIter iter; PhoneSettingsCallback callback; PhonePlugin * plugin = NULL; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( phone->se_view))) == NULL) return; if(gtk_tree_selection_get_selected(treesel, NULL, &iter) != TRUE) return; gtk_tree_model_get(GTK_TREE_MODEL(phone->se_store), &iter, PHONE_SETTINGS_COLUMN_CALLBACK, &callback, PHONE_SETTINGS_COLUMN_PLUGIN, &plugin, -1); if(callback != NULL) callback(phone, TRUE); else if(plugin != NULL && plugin->settings != NULL) plugin->settings(plugin); } /* show */ /* phone_show_call */ static void _show_call_window(Phone * phone); void phone_show_call(Phone * phone, gboolean show, ...) /* FIXME let the API closer to ModemEvent.call */ { va_list ap; PhoneCall call; char const * name = NULL; char const * number = NULL; if(show == FALSE) { if(phone->ca_window != NULL) gtk_widget_hide(phone->ca_window); return; } va_start(ap, show); call = va_arg(ap, PhoneCall); if(call == PHONE_CALL_INCOMING || call == PHONE_CALL_OUTGOING) { name = va_arg(ap, char const *); number = va_arg(ap, char const *); } va_end(ap); if(phone->ca_window == NULL) _show_call_window(phone); phone_show_dialer(phone, FALSE); if(name != NULL) { if(name[0] == '\0') /* XXX look it up if we have the number */ name = _("Unknown contact"); gtk_label_set_text(GTK_LABEL(phone->ca_name), name); } if(number != NULL) { if(number[0] == '\0') /* XXX look it up if we have the name */ number = _("Unknown number"); gtk_label_set_text(GTK_LABEL(phone->ca_number), number); } gtk_widget_show_all(phone->ca_window); switch(call) { case PHONE_CALL_ESTABLISHED: #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->ca_window), "call-start"); /* XXX better icon */ #endif gtk_window_set_title(GTK_WINDOW(phone->ca_window), _("In conversation")); gtk_widget_hide(phone->ca_answer); gtk_widget_hide(phone->ca_reject); gtk_widget_hide(phone->ca_close); break; case PHONE_CALL_INCOMING: #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->ca_window), "call-start"); /* XXX better icon */ #endif gtk_window_set_title(GTK_WINDOW(phone->ca_window), _("Incoming call")); gtk_widget_hide(phone->ca_hangup); gtk_widget_hide(phone->ca_close); break; case PHONE_CALL_OUTGOING: #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->ca_window), "call-start"); /* XXX better icon */ #endif gtk_window_set_title(GTK_WINDOW(phone->ca_window), _("Outgoing call")); gtk_widget_hide(phone->ca_answer); gtk_widget_hide(phone->ca_reject); gtk_widget_hide(phone->ca_close); break; case PHONE_CALL_TERMINATED: #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->ca_window), "call-stop"); #endif gtk_window_set_title(GTK_WINDOW(phone->ca_window), _("Call finished")); gtk_widget_hide(phone->ca_answer); gtk_widget_hide(phone->ca_hangup); gtk_widget_hide(phone->ca_reject); break; } gtk_window_present(GTK_WINDOW(phone->ca_window)); } static void _show_call_window(Phone * phone) { GtkWidget * vbox; GtkWidget * hbox; /* FIXME the "Answer" button may be shown smaller than the others */ phone->ca_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(phone->ca_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->ca_window), "call-start"); #endif vbox = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); /* party */ phone->ca_name = gtk_label_new(NULL); gtk_widget_modify_font(phone->ca_name, phone->bold); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_name, FALSE, TRUE, 0); phone->ca_number = gtk_label_new(NULL); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_number, FALSE, TRUE, 0); /* buttons */ /* answer */ phone->ca_answer = _phone_create_button("call-start", _("Answer")); g_signal_connect_swapped(G_OBJECT(phone->ca_answer), "clicked", G_CALLBACK(on_phone_call_answer), phone); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_answer, FALSE, TRUE, 0); /* hangup */ phone->ca_hangup = _phone_create_button("call-stop", _("Hangup")); g_signal_connect_swapped(G_OBJECT(phone->ca_hangup), "clicked", G_CALLBACK(on_phone_call_hangup), phone); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_hangup, TRUE, TRUE, 0); /* reject */ phone->ca_reject = _phone_create_button("call-stop", _("Reject")); g_signal_connect_swapped(G_OBJECT(phone->ca_reject), "clicked", G_CALLBACK(on_phone_call_reject), phone); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_reject, TRUE, TRUE, 0); /* close */ phone->ca_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE); g_signal_connect_swapped(G_OBJECT(phone->ca_close), "clicked", G_CALLBACK(on_phone_call_close), phone); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_close, TRUE, TRUE, 0); /* volume bar */ hbox = gtk_hbox_new(FALSE, 0); phone->ca_image = gtk_image_new_from_icon_name( "audio-volume-muted", GTK_ICON_SIZE_BUTTON); gtk_box_pack_start(GTK_BOX(hbox), phone->ca_image, FALSE, TRUE, 4); phone->ca_volume = gtk_hscale_new_with_range(0.0, 1.0, 0.02); g_signal_connect(G_OBJECT(phone->ca_volume), "value-changed", G_CALLBACK(on_phone_call_volume), phone); gtk_box_pack_start(GTK_BOX(hbox), phone->ca_volume, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); /* speaker mode */ phone->ca_speaker = gtk_toggle_button_new_with_label(_("Loudspeaker")); gtk_button_set_image(GTK_BUTTON(phone->ca_speaker), gtk_image_new_from_icon_name("stock_volume-max", GTK_ICON_SIZE_BUTTON)); g_signal_connect(G_OBJECT(phone->ca_speaker), "toggled", G_CALLBACK( on_phone_call_speaker), phone); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_speaker, TRUE, TRUE, 0); /* mute microphone */ phone->ca_mute = gtk_toggle_button_new_with_label(_("Mute microphone")); gtk_button_set_image(GTK_BUTTON(phone->ca_mute), gtk_image_new_from_icon_name("audio-input-microphone", GTK_ICON_SIZE_BUTTON)); g_signal_connect(G_OBJECT(phone->ca_mute), "toggled", G_CALLBACK( on_phone_call_mute), phone); gtk_box_pack_start(GTK_BOX(vbox), phone->ca_mute, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->ca_window), vbox); } /* phone_show_code */ static void _show_code_window(Phone * phone); void phone_show_code(Phone * phone, gboolean show, ...) { va_list ap; ModemAuthenticationMethod method; char const * name; char buf[32]; if(phone->en_window == NULL) _show_code_window(phone); if(show == FALSE) { if(phone->en_window != NULL) gtk_widget_hide(phone->en_window); return; } va_start(ap, show); method = va_arg(ap, ModemAuthenticationMethod); name = va_arg(ap, char const *); va_end(ap); if(phone->en_name == NULL || strcmp(phone->en_name, name) != 0) phone_code_clear(phone); phone->en_method = method; free(phone->en_name); if((phone->en_name = strdup(name)) == NULL) return; /* XXX report error */ switch(method) { case MODEM_AUTHENTICATION_METHOD_PIN: snprintf(buf, sizeof(buf), _("Enter %s"), name); gtk_window_set_title(GTK_WINDOW(phone->en_window), buf); gtk_label_set_text(GTK_LABEL(phone->en_title), buf); break; default: break; } gtk_window_present(GTK_WINDOW(phone->en_window)); } static void _show_code_window(Phone * phone) { GtkWidget * vbox; GtkWidget * hbox; /* XXX create in phone_create_dialpad? */ GtkWidget * widget; phone->en_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->en_window), "stock_lock"); #endif gtk_container_set_border_width(GTK_CONTAINER(phone->en_window), 4); g_signal_connect(G_OBJECT(phone->en_window), "delete-event", G_CALLBACK( on_phone_closex), NULL); vbox = gtk_vbox_new(FALSE, 4); phone->en_title = gtk_label_new(""); gtk_widget_modify_font(phone->en_title, phone->bold); gtk_box_pack_start(GTK_BOX(vbox), phone->en_title, FALSE, TRUE, 0); hbox = gtk_hbox_new(FALSE, 4); phone->en_entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(phone->en_entry), FALSE); gtk_widget_modify_font(phone->en_entry, phone->bold); g_signal_connect_swapped(G_OBJECT(phone->en_entry), "activate", G_CALLBACK(on_phone_code_enter), phone); gtk_box_pack_start(GTK_BOX(hbox), phone->en_entry, TRUE, TRUE, 0); widget = gtk_button_new(); gtk_button_set_image(GTK_BUTTON(widget), gtk_image_new_from_icon_name( "edit-undo", GTK_ICON_SIZE_BUTTON)); gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(on_phone_code_clear), phone); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); widget = _phone_create_dialpad(phone, GTK_STOCK_OK, _("Enter"), G_CALLBACK(on_phone_code_enter), GTK_STOCK_CANCEL, _("Skip"), G_CALLBACK(on_phone_code_leave), G_CALLBACK(on_phone_code_clicked)); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->en_window), vbox); gtk_widget_show_all(vbox); } /* phone_show_contacts */ void phone_show_contacts(Phone * phone, gboolean show) { GtkWidget * vbox; GtkWidget * widget; GtkToolItem * toolitem; GtkCellRenderer * renderer; GtkTreeViewColumn * column; if(phone->co_window == NULL) { phone->co_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->co_window), "stock_addressbook"); #endif gtk_window_set_default_size(GTK_WINDOW(phone->co_window), 200, 300); gtk_window_set_title(GTK_WINDOW(phone->co_window), _("Contacts")); g_signal_connect(G_OBJECT(phone->co_window), "delete-event", G_CALLBACK(on_phone_closex), NULL); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ widget = gtk_toolbar_new(); toolitem = gtk_tool_button_new(NULL, _("Call")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "call-start"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_contacts_call), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("Write")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "mail-reply-sender"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_contacts_write), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_separator_tool_item_new(); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_contacts_new), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_EDIT); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_contacts_edit), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_contacts_delete), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->co_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( phone->co_store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(phone->co_view), FALSE); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(phone->co_view), TRUE); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", PHONE_CONTACT_COLUMN_NAME, NULL); gtk_tree_view_column_set_sort_column_id(column, PHONE_CONTACT_COLUMN_NAME); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->co_view), column); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE( phone->co_store), PHONE_CONTACT_COLUMN_NAME, GTK_SORT_ASCENDING); gtk_container_add(GTK_CONTAINER(widget), phone->co_view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->co_window), vbox); gtk_widget_show_all(vbox); } if(show) gtk_window_present(GTK_WINDOW(phone->co_window)); else gtk_widget_hide(phone->co_window); } /* phone_show_dialer */ static void _show_dialer_window(Phone * phone); void phone_show_dialer(Phone * phone, gboolean show) { if(phone->di_window == NULL) _show_dialer_window(phone); if(show) gtk_window_present(GTK_WINDOW(phone->di_window)); else gtk_widget_hide(phone->di_window); } static void _show_dialer_window(Phone * phone) { GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; phone->di_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->di_window), "phone-dialer"); #endif gtk_container_set_border_width(GTK_CONTAINER(phone->di_window), 4); gtk_window_set_title(GTK_WINDOW(phone->di_window), _("Dialer")); g_signal_connect(G_OBJECT(phone->di_window), "delete-event", G_CALLBACK( on_phone_closex), NULL); gdk_add_client_message_filter(gdk_atom_intern(PHONE_CLIENT_MESSAGE, FALSE), on_phone_filter, phone); vbox = gtk_vbox_new(FALSE, 4); /* entry */ hbox = gtk_hbox_new(FALSE, 4); phone->di_entry = gtk_entry_new(); gtk_widget_modify_font(phone->di_entry, phone->bold); g_signal_connect_swapped(G_OBJECT(phone->di_entry), "activate", G_CALLBACK(on_phone_dialer_call), phone); gtk_box_pack_start(GTK_BOX(hbox), phone->di_entry, TRUE, TRUE, 0); widget = gtk_button_new(); gtk_button_set_image(GTK_BUTTON(widget), gtk_image_new_from_icon_name( "stock_addressbook", GTK_ICON_SIZE_BUTTON)); gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( on_phone_contacts_show), phone); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); /* dialpad */ widget = _phone_create_dialpad(phone, "call-start", _("Call"), G_CALLBACK(on_phone_dialer_call), "call-stop", _("Hang up"), G_CALLBACK(on_phone_dialer_hangup), G_CALLBACK(on_phone_dialer_clicked)); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->di_window), vbox); gtk_widget_show_all(vbox); } /* phone_show_logs */ void phone_show_logs(Phone * phone, gboolean show) { GtkWidget * vbox; GtkWidget * widget; GtkToolItem * toolitem; GtkCellRenderer * renderer; GtkTreeViewColumn * column; if(phone->lo_window == NULL) { phone->lo_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(phone->lo_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->lo_window), "logviewer"); #endif gtk_window_set_title(GTK_WINDOW(phone->lo_window), _("Phone logs")); g_signal_connect(G_OBJECT(phone->lo_window), "delete-event", G_CALLBACK(on_phone_closex), NULL); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ widget = gtk_toolbar_new(); toolitem = gtk_tool_button_new(NULL, _("Call")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "call-start"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_logs_call), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("Message")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "stock_mail-compose"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_logs_write), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_separator_tool_item_new(); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_CLEAR); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_logs_clear), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->lo_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( phone->lo_store)); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(phone->lo_view), TRUE); g_signal_connect_swapped(G_OBJECT(phone->lo_view), "row-activated", G_CALLBACK( on_phone_logs_activated), phone); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(phone->lo_view), TRUE); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Direction"), renderer, "text", PHONE_LOGS_COLUMN_CALL_TYPE_DISPLAY, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->lo_view), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("To/From"), renderer, "text", PHONE_LOGS_COLUMN_NUMBER, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->lo_view), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer, "text", PHONE_LOGS_COLUMN_DATE_DISPLAY, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->lo_view), column); gtk_container_add(GTK_CONTAINER(widget), phone->lo_view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->lo_window), vbox); gtk_widget_show_all(vbox); } if(show) gtk_window_present(GTK_WINDOW(phone->lo_window)); else gtk_widget_hide(phone->lo_window); } /* phone_show_messages */ void phone_show_messages(Phone * phone, gboolean show) { GtkWidget * vbox; GtkWidget * widget; GtkToolItem * toolitem; GtkWidget * hbox; GtkCellRenderer * renderer; GtkTreeViewColumn * column; if(phone->me_window == NULL) { phone->me_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(phone->me_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->me_window), "stock_mail-send-receive"); #endif gtk_window_set_title(GTK_WINDOW(phone->me_window), _("Messages")); g_signal_connect(G_OBJECT(phone->me_window), "delete-event", G_CALLBACK(on_phone_closex), NULL); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ widget = gtk_toolbar_new(); toolitem = gtk_tool_button_new(NULL, _("Call")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "call-start"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_messages_call), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("New message")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "stock_mail-compose"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_messages_write), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("Reply")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "mail-reply-sender"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_messages_reply), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_messages_delete), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); /* selector */ /* FIXME consider: * - creating a notebook here ("Inbox", "Outbox", "All") * - each page has a GtkTreeView * - each GtkTreeView has a GtkTreeModelFilter as model * - each GtkTreeModelFilter is a variant of the GtkListStore */ hbox = gtk_hbox_new(TRUE, 0); widget = _phone_create_button("stock_inbox", _("Inbox")); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(on_phone_messages_inbox), phone); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); widget = _phone_create_button("stock_outbox", _("Sent")); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(on_phone_messages_outbox), phone); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->me_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( phone->me_store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(phone->me_view), FALSE); /* XXX consider reverting this */ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(phone->me_view), TRUE); g_signal_connect_swapped(G_OBJECT(phone->me_view), "row-activated", G_CALLBACK( on_phone_messages_activated), phone); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("To/From"), renderer, "text", PHONE_MESSAGE_COLUMN_NUMBER_DISPLAY, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->me_view), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer, "text", PHONE_MESSAGE_COLUMN_DATE_DISPLAY, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->me_view), column); gtk_container_add(GTK_CONTAINER(widget), phone->me_view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->me_window), vbox); gtk_widget_show_all(vbox); } if(show) gtk_window_present(GTK_WINDOW(phone->me_window)); else gtk_widget_hide(phone->me_window); } /* phone_show_plugins */ static void _on_plugins_cancel(gpointer data); static void _on_plugins_activated(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data); static gboolean _on_plugins_closex(gpointer data); static void _on_plugins_enabled_toggle(GtkCellRendererToggle * renderer, char * path, gpointer data); static void _on_plugins_ok(gpointer data); void phone_show_plugins(Phone * phone, gboolean show) { GtkWidget * vbox; GtkWidget * widget; GtkCellRenderer * renderer; GtkTreeViewColumn * column; GtkWidget * bbox; if(phone->pl_window == NULL) { phone->pl_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); widget = phone->pl_window; gtk_container_set_border_width(GTK_CONTAINER(widget), 4); gtk_window_set_default_size(GTK_WINDOW(widget), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(widget), "gnome-settings"); #endif gtk_window_set_title(GTK_WINDOW(widget), _("Plug-ins")); g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK( _on_plugins_closex), widget); vbox = gtk_vbox_new(FALSE, 4); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->pl_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( phone->pl_store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(phone->pl_view), FALSE); g_signal_connect(G_OBJECT(phone->pl_view), "row-activated", G_CALLBACK(_on_plugins_activated), phone); renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK( _on_plugins_enabled_toggle), phone); column = gtk_tree_view_column_new_with_attributes(_("Enabled"), renderer, "active", PHONE_PLUGINS_COLUMN_ENABLED, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->pl_view), column); renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf", PHONE_PLUGINS_COLUMN_ICON, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->pl_view), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", PHONE_PLUGINS_COLUMN_NAME, NULL); gtk_tree_view_column_set_sort_column_id(column, PHONE_PLUGINS_COLUMN_NAME); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->pl_view), column); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE( phone->pl_store), PHONE_PLUGINS_COLUMN_NAME, GTK_SORT_ASCENDING); gtk_container_add(GTK_CONTAINER(widget), phone->pl_view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); /* dialog */ bbox = gtk_hbutton_box_new(); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 4); widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(_on_plugins_cancel), phone); gtk_container_add(GTK_CONTAINER(bbox), widget); widget = gtk_button_new_from_stock(GTK_STOCK_OK); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(_on_plugins_ok), phone); gtk_container_add(GTK_CONTAINER(bbox), widget); gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->pl_window), vbox); _on_plugins_cancel(phone); gtk_widget_show_all(vbox); } if(show) gtk_window_present(GTK_WINDOW(phone->pl_window)); else gtk_widget_hide(phone->pl_window); } static void _on_plugins_cancel(gpointer data) { Phone * phone = data; DIR * dir; struct dirent * de; GtkIconTheme * theme; char const ext[] = ".so"; size_t len; GtkTreeIter iter; Plugin * p; PhonePlugin * pp; size_t i; GdkPixbuf * pixbuf; gtk_widget_hide(phone->pl_window); gtk_list_store_clear(phone->pl_store); if((dir = opendir(LIBDIR "/" PACKAGE "/plugins")) == NULL) return; theme = gtk_icon_theme_get_default(); while((de = readdir(dir)) != NULL) { if((len = strlen(de->d_name)) < sizeof(ext)) continue; if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0) continue; de->d_name[len - sizeof(ext) + 1] = '\0'; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, de->d_name); #endif if((p = plugin_new(LIBDIR, PACKAGE, "plugins", de->d_name)) == NULL) continue; if((pp = plugin_lookup(p, "plugin")) == NULL) { plugin_delete(p); continue; } gtk_list_store_append(phone->pl_store, &iter); gtk_list_store_set(phone->pl_store, &iter, PHONE_PLUGINS_COLUMN_FILENAME, de->d_name, PHONE_PLUGINS_COLUMN_NAME, pp->name, -1); for(i = 0; i < phone->plugins_cnt; i++) { if(strcmp(phone->plugins[i].pp->name, pp->name) != 0) continue; gtk_list_store_set(phone->pl_store, &iter, PHONE_PLUGINS_COLUMN_ENABLED, TRUE, PHONE_PLUGINS_COLUMN_PLUGIN, phone->plugins[i].pp, -1); break; } if(pp->icon == NULL) pixbuf = gtk_icon_theme_load_icon(theme, "gnome-settings", 48, 0, NULL); else pixbuf = gtk_icon_theme_load_icon(theme, pp->icon, 48, 0, NULL); gtk_list_store_set(phone->pl_store, &iter, PHONE_PLUGINS_COLUMN_ICON, pixbuf, -1); plugin_delete(p); } closedir(dir); } static void _on_plugins_activated(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data) { Phone * phone = data; GtkTreeIter iter; gboolean active; gtk_tree_model_get_iter(GTK_TREE_MODEL(phone->pl_store), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(phone->pl_store), &iter, PHONE_PLUGINS_COLUMN_ENABLED, &active, -1); gtk_list_store_set(phone->pl_store, &iter, PHONE_PLUGINS_COLUMN_ENABLED, !active, -1); } static gboolean _on_plugins_closex(gpointer data) { Phone * phone = data; _on_plugins_cancel(phone); return TRUE; } static void _on_plugins_enabled_toggle(GtkCellRendererToggle * renderer, char * path, gpointer data) { Phone * phone = data; GtkTreeIter iter; gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(phone->pl_store), &iter, path); gtk_list_store_set(phone->pl_store, &iter, PHONE_PLUGINS_COLUMN_ENABLED, !gtk_cell_renderer_toggle_get_active(renderer), -1); } static void _on_plugins_ok(gpointer data) { Phone * phone = data; GtkTreeModel * model = GTK_TREE_MODEL(phone->pl_store); GtkTreeIter iter; gboolean valid; gboolean enabled; PhonePlugin * plugin; gchar * name; int res = 0; String * value = string_new(""); String * sep = ""; gtk_widget_hide(phone->pl_window); valid = gtk_tree_model_get_iter_first(model, &iter); for(; valid == TRUE; valid = gtk_tree_model_iter_next(model, &iter)) { gtk_tree_model_get(model, &iter, PHONE_PLUGINS_COLUMN_ENABLED, &enabled, PHONE_PLUGINS_COLUMN_PLUGIN, &plugin, PHONE_PLUGINS_COLUMN_FILENAME, &name, -1); if(enabled) { if(plugin == NULL) phone_load(phone, name); res |= string_append(&value, sep); res |= string_append(&value, name); sep = ","; } else if(plugin != NULL) _phone_unload(phone, plugin); g_free(name); } #ifdef DEBUG fprintf(stderr, "DEBUG: %s() value=\"%s\"\n", __func__, value); #endif _phone_config_set(phone, NULL, "plugins", value); string_delete(value); _on_plugins_cancel(phone); } /* phone_show_read */ void phone_show_read(Phone * phone, gboolean show, ...) { va_list ap; GtkWidget * vbox; GtkWidget * widget; GtkToolItem * toolitem; GtkTextBuffer * tbuf; char const * name; char const * number; time_t date; char const * content; struct tm t; char buf[32]; if(show == FALSE) { if(phone->re_window != NULL) gtk_widget_hide(phone->re_window); return; } va_start(ap, show); phone->re_index = va_arg(ap, unsigned int); name = va_arg(ap, char const *); number = va_arg(ap, char const *); date = va_arg(ap, time_t); content = va_arg(ap, char const *); va_end(ap); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %u, %s, %s, %u, %s\n", __func__, phone->re_index, name, number, date, content); #endif if(phone->re_window == NULL) { phone->re_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(phone->re_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->re_window), "stock_mail-open"); #endif gtk_window_set_title(GTK_WINDOW(phone->re_window), _("Read message")); g_signal_connect(G_OBJECT(phone->re_window), "delete-event", G_CALLBACK(on_phone_closex), phone->re_window); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ widget = gtk_toolbar_new(); toolitem = gtk_tool_button_new(NULL, _("Call")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "call-start"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_read_call), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("Reply")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "mail-reply-sender"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_read_reply), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("Forward")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "mail-forward"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_read_forward), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_read_delete), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); /* name */ phone->re_name = gtk_label_new(NULL); gtk_widget_modify_font(phone->re_name, phone->bold); gtk_box_pack_start(GTK_BOX(vbox), phone->re_name, FALSE, TRUE, 0); /* number */ phone->re_number = gtk_label_new(NULL); gtk_box_pack_start(GTK_BOX(vbox), phone->re_number, FALSE, TRUE, 0); /* date */ phone->re_date = gtk_label_new(NULL); gtk_box_pack_start(GTK_BOX(vbox), phone->re_date, FALSE, TRUE, 0); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->re_view = gtk_text_view_new(); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(phone->re_view), FALSE); gtk_text_view_set_editable(GTK_TEXT_VIEW(phone->re_view), FALSE); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(phone->re_view), GTK_WRAP_WORD_CHAR); tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->re_view)); gtk_container_add(GTK_CONTAINER(widget), phone->re_view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 2); gtk_container_add(GTK_CONTAINER(phone->re_window), vbox); gtk_widget_show_all(vbox); } if(name != NULL) gtk_label_set_text(GTK_LABEL(phone->re_name), name); if(number != NULL) gtk_label_set_text(GTK_LABEL(phone->re_number), number); localtime_r(&date, &t); /* XXX gmtime_r() or localtime_r() ? */ strftime(buf, sizeof(buf), "%d/%m/%Y %H:%M:%S", &t); gtk_label_set_text(GTK_LABEL(phone->re_date), buf); tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->re_view)); if(content != NULL) gtk_text_buffer_set_text(tbuf, content, -1); gtk_window_present(GTK_WINDOW(phone->re_window)); } /* phone_show_settings */ void phone_show_settings(Phone * phone, gboolean show) { GtkWidget * widget; GtkCellRenderer * renderer; GtkTreeViewColumn * column; if(phone->se_window == NULL) { phone->se_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(phone->se_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->se_window), "stock_cell-phone"); #endif gtk_window_set_title(GTK_WINDOW(phone->se_window), _("Phone settings")); g_signal_connect(G_OBJECT(phone->se_window), "delete-event", G_CALLBACK(on_phone_closex), phone->se_window); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->se_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( phone->se_store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(phone->se_view), FALSE); g_signal_connect_swapped(G_OBJECT(phone->se_view), "row-activated", G_CALLBACK( phone_settings_open_selected), phone); /* icon */ renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf", PHONE_SETTINGS_COLUMN_ICON, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->se_view), column); /* name */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", PHONE_SETTINGS_COLUMN_NAME, NULL); gtk_tree_view_column_set_sort_column_id(column, PHONE_SETTINGS_COLUMN_NAME); gtk_tree_view_append_column(GTK_TREE_VIEW(phone->se_view), column); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE( phone->se_store), PHONE_SETTINGS_COLUMN_NAME, GTK_SORT_ASCENDING); gtk_container_add(GTK_CONTAINER(widget), phone->se_view); gtk_container_add(GTK_CONTAINER(phone->se_window), widget); gtk_widget_show_all(widget); } if(show) gtk_window_present(GTK_WINDOW(phone->se_window)); else gtk_widget_hide(phone->se_window); } /* phone_show_system */ static void _on_system_cancel(gpointer data); static gboolean _on_system_closex(gpointer data); static void _on_system_ok(gpointer data); void phone_show_system(Phone * phone, gboolean show) { GtkWidget * vbox; GtkWidget * widget; GtkWidget * bbox; ModemConfig * config; size_t i; String * label; /* XXX creation of this window is not cached for performance reasons */ if(show == FALSE) { if(phone->sy_window != NULL) gtk_widget_hide(phone->sy_window); return; } if(phone->sy_window != NULL) { gtk_window_present(GTK_WINDOW(phone->sy_window)); return; } phone->sy_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width(GTK_CONTAINER(phone->sy_window), 4); gtk_window_set_default_size(GTK_WINDOW(phone->sy_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->sy_window), "gnome-settings"); #endif gtk_window_set_title(GTK_WINDOW(phone->sy_window), _("System preferences")); g_signal_connect_swapped(G_OBJECT(phone->sy_window), "delete-event", G_CALLBACK(_on_system_closex), phone); vbox = gtk_vbox_new(FALSE, 4); config = gsm_get_config(phone->gsm); for(i = 0; config[i].name != NULL; i++) { widget = NULL; switch(config[i].type) { case MCT_NONE: /* XXX should not happen */ break; case MCT_BOOLEAN: widget = gtk_check_button_new_with_label( config[i].title); break; case MCT_FILENAME: /* FIXME really implement */ case MCT_STRING: case MCT_UINT32: label = string_new_append(config[i].title, ":", NULL); widget = gtk_label_new(label); string_delete(label); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); widget = gtk_entry_new(); break; } if(widget == NULL) continue; g_object_set_data(G_OBJECT(phone->sy_window), config[i].name, widget); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); } bbox = gtk_hbutton_box_new(); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 4); widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_system_cancel), phone); gtk_container_add(GTK_CONTAINER(bbox), widget); widget = gtk_button_new_from_stock(GTK_STOCK_OK); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_system_ok), phone); gtk_container_add(GTK_CONTAINER(bbox), widget); gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(phone->sy_window), vbox); gtk_widget_show_all(vbox); _on_system_cancel(phone); gtk_window_present(GTK_WINDOW(phone->sy_window)); } static void _on_system_cancel(gpointer data) { Phone * phone = data; ModemConfig * config; size_t i; GtkWidget * widget; char const * p; gboolean active; unsigned int u; char buf[16]; gtk_widget_hide(phone->sy_window); config = gsm_get_config(phone->gsm); for(i = 0; config[i].name != NULL; i++) { if((widget = g_object_get_data(G_OBJECT(phone->sy_window), config[i].name)) == NULL) continue; switch(config[i].type) { case MCT_NONE: /* XXX should not happen */ break; case MCT_BOOLEAN: p = config[i].value; active = (p != NULL) ? TRUE : FALSE; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( widget), active); break; case MCT_FILENAME: /* FIXME really implement */ case MCT_STRING: p = (config[i].value != NULL) ? config[i].value : ""; gtk_entry_set_text(GTK_ENTRY(widget), p); break; case MCT_UINT32: /* FIXME really implement */ u = (unsigned long)config[i].value; snprintf(buf, sizeof(buf), "%u", u); gtk_entry_set_text(GTK_ENTRY(widget), buf); break; } } } static gboolean _on_system_closex(gpointer data) { Phone * phone = data; _on_system_cancel(phone); return TRUE; } static void _on_system_ok(gpointer data) { Phone * phone = data; ModemConfig * config; size_t i; GtkWidget * widget; char const * p; gtk_widget_hide(phone->sy_window); config = gsm_get_config(phone->gsm); for(i = 0; config[i].name != NULL; i++) { if((widget = g_object_get_data(G_OBJECT(phone->sy_window), config[i].name)) == NULL) continue; switch(config[i].type) { case MCT_NONE: /* XXX should not happen */ break; case MCT_BOOLEAN: config[i].value = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget)) ? (void*)1 : NULL; _phone_config_set_type(phone, "modem", "hayes", config[i].name, config[i].value ? "1" : "0"); break; case MCT_FILENAME: /* FIXME really implement */ case MCT_STRING: p = gtk_entry_get_text(GTK_ENTRY(widget)); /* FIXME memory leak */ config[i].value = strdup(p); _phone_config_set_type(phone, "modem", "hayes", config[i].name, p); break; case MCT_UINT32: p = gtk_entry_get_text(GTK_ENTRY(widget)); config[i].value = (void*)strtoul(p, NULL, 10); _phone_config_set_type(phone, "modem", "hayes", config[i].name, p); break; } } _phone_config_save(phone); gsm_stop(phone->gsm); gsm_start(phone->gsm, 0); } /* phone_show_write */ void phone_show_write(Phone * phone, gboolean show, ...) { va_list ap; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; GtkToolItem * toolitem; GtkTextBuffer * tbuf; char const * number; char const * content; if(show == FALSE) { if(phone->lo_window != NULL) gtk_widget_hide(phone->lo_window); return; } va_start(ap, show); number = va_arg(ap, char const *); content = va_arg(ap, char const *); va_end(ap); if(phone->wr_window == NULL) { phone->wr_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(phone->wr_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(phone->wr_window), "stock_mail-compose"); #endif gtk_window_set_title(GTK_WINDOW(phone->wr_window), _("Write message")); g_signal_connect(G_OBJECT(phone->wr_window), "delete-event", G_CALLBACK(on_phone_closex), phone->wr_window); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ widget = gtk_toolbar_new(); toolitem = gtk_tool_button_new(NULL, _("Send")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "mail-send"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_write_send), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new(NULL, _("Attach")); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "stock_attach"); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_write_attach), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_CUT); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_write_cut), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_COPY); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_write_copy), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_PASTE); g_signal_connect_swapped(G_OBJECT(toolitem), "clicked", G_CALLBACK(on_phone_write_paste), phone); gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); /* entry */ hbox = gtk_hbox_new(FALSE, 0); phone->wr_entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), phone->wr_entry, TRUE, TRUE, 2); widget = gtk_button_new(); gtk_button_set_image(GTK_BUTTON(widget), gtk_image_new_from_icon_name( "stock_addressbook", GTK_ICON_SIZE_BUTTON)); gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(on_phone_contacts_show), phone); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 2); /* character count */ hbox = gtk_hbox_new(FALSE, 0); phone->wr_count = gtk_label_new(NULL); gtk_box_pack_start(GTK_BOX(hbox), phone->wr_count, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 2); /* view */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); phone->wr_view = gtk_text_view_new(); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(phone->wr_view), GTK_WRAP_WORD_CHAR); tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->wr_view)); g_signal_connect_swapped(G_OBJECT(tbuf), "changed", G_CALLBACK( on_phone_write_changed), phone); gtk_container_add(GTK_CONTAINER(widget), phone->wr_view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 2); gtk_container_add(GTK_CONTAINER(phone->wr_window), vbox); gtk_widget_show_all(vbox); phone_write_count_buffer(phone); } if(number != NULL) gtk_entry_set_text(GTK_ENTRY(phone->wr_entry), number); if(content != NULL) { tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->wr_view)); gtk_text_buffer_set_text(tbuf, content, -1); } gtk_widget_grab_focus((number == NULL || number[0] == '\0') ? phone->wr_entry : phone->wr_view); gtk_window_present(GTK_WINDOW(phone->wr_window)); } /* phone_unload */ int phone_unload(Phone * phone, char const * name) { /* FIXME implement */ return -1; } /* phone_unload_all */ void phone_unload_all(Phone * phone) { while(phone->plugins_cnt >= 1) _phone_unload(phone, phone->plugins[0].pp); } /* write */ /* phone_write_count_buffer */ void phone_write_count_buffer(Phone * phone) { GtkTextBuffer * tbuf; gint cnt; int max = 160; gint msg_cnt; gint cur_cnt; char buf[32]; tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->wr_view)); if((cnt = gtk_text_buffer_get_char_count(tbuf)) < 0) return; if(cnt <= max) { cur_cnt = cnt; msg_cnt = 1; } else { max = 153; /* for the User Data Header */ msg_cnt = (cnt / max) + 1; if((cur_cnt = cnt % max) == 0) { msg_cnt--; if(cnt > 0) cur_cnt = max; } } snprintf(buf, sizeof(buf), _("%d message%s, %d/%d characters"), msg_cnt, (msg_cnt > 1) ? _("s") : "", cur_cnt, max); gtk_label_set_text(GTK_LABEL(phone->wr_count), buf); } /* phone_write_send */ void phone_write_send(Phone * phone) { gchar const * number; gchar * text; GtkTextBuffer * tbuf; GtkTextIter start; GtkTextIter end; size_t length; GSMEncoding encoding = GSM_ENCODING_UTF8; phone_show_write(phone, TRUE, NULL, NULL); number = gtk_entry_get_text(GTK_ENTRY(phone->wr_entry)); tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(phone->wr_view)); gtk_text_buffer_get_start_iter(tbuf, &start); gtk_text_buffer_get_end_iter(tbuf, &end); text = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE); if(number == NULL || number[0] == '\0' || text == NULL) return; length = strlen(text); phone->wr_progress = _phone_create_progress(phone->wr_window, _("Sending message...")); _phone_track(phone, PHONE_TRACK_MESSAGE_SENT, TRUE); gsm_message_send(phone->gsm, number, encoding, text, length); g_free(text); } /* private */ /* phone_call_number */ static int _phone_call_number(Phone * phone, char const * number) { PhoneEvent event; GtkTreeIter iter; time_t date; struct tm t; char dd[32]; memset(&event, 0, sizeof(event)); event.type = PHONE_EVENT_TYPE_CALLING; event.calling.number = number; if(number != NULL && phone_event(phone, &event) != 0) return -1; gsm_call(phone->gsm, GSM_CALL_TYPE_VOICE, number); phone_show_call(phone, TRUE, PHONE_CALL_OUTGOING, "", number); /* add a log entry */ gtk_list_store_append(phone->lo_store, &iter); date = time(NULL); localtime_r(&date, &t); strftime(dd, sizeof(dd), "%d/%m/%Y %H:%M:%S", &t); gtk_list_store_set(phone->lo_store, &iter, PHONE_LOGS_COLUMN_CALL_TYPE, PHONE_CALL_TYPE_OUTGOING, PHONE_LOGS_COLUMN_CALL_TYPE_DISPLAY, _("Outgoing"), PHONE_LOGS_COLUMN_NUMBER, number, PHONE_LOGS_COLUMN_DATE_DISPLAY, dd, -1); return 0; } /* phone_config_filename */ static char * _phone_config_filename(void) { char const * homedir; size_t len; char * filename; if((homedir = getenv("HOME")) == NULL) homedir = g_get_home_dir(); len = strlen(homedir) + 1 + sizeof(PHONE_CONFIG_FILE); if((filename = malloc(len)) == NULL) return NULL; snprintf(filename, len, "%s/%s", homedir, PHONE_CONFIG_FILE); return filename; } /* phone_config_foreach */ static void _phone_config_foreach(Phone * phone, char const * section, PhoneConfigForeachCallback callback, void * priv) { config_foreach_section(phone->config, section, callback, priv); } /* phone_config_get */ static char const * _phone_config_get(Phone * phone, char const * section, char const * variable) { char const * ret; String * s; if((s = string_new_append("plugin_", section, NULL)) == NULL) return NULL; ret = config_get(phone->config, s, variable); string_delete(s); return ret; } /* phone_config_save */ static int _phone_config_save(Phone * phone) { int ret = 0; char * filename; if((filename = _phone_config_filename()) == NULL) return -1; /* XXX warn the user */ if(config_save(phone->config, filename) != 0) ret = -phone_error(phone, error_get(), 1); free(filename); return ret; } /* phone_config_set */ static int _phone_config_set(Phone * phone, char const * section, char const * variable, char const * value) { int ret; ret = _phone_config_set_type(phone, "plugin", section, variable, value); if(ret == 0) ret = _phone_config_save(phone); return ret; } /* phone_config_set_type */ static int _phone_config_set_type(Phone * phone, char const * type, char const * section, char const * variable, char const * value) { int ret; String * s; if((s = string_new_append(type, (section != NULL) ? "_" : NULL, section, NULL)) == NULL) return -1; ret = config_set(phone->config, s, variable, value); string_delete(s); return ret; } /* phone_create_button */ static GtkWidget * _phone_create_button(char const * icon, char const * label) { GtkWidget * ret; ret = gtk_button_new_with_label(label); gtk_button_set_image(GTK_BUTTON(ret), gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON)); return ret; } /* phone_create_dialpad */ static GtkWidget * _phone_create_dialpad(Phone * phone, char const * button1_image, char const * button1_label, GCallback button1_callback, char const * button2_image, char const * button2_label, GCallback button2_callback, GCallback button_callback) { static struct { char character; char const * label; } numbers[12] = { { '1', "_1\n" }, { '2', "_2\nABC" }, { '3', "_3\nDEF" }, { '4', "_4\nGHI" }, { '5', "_5\nJKL" }, { '6', "_6\nMNO" }, { '7', "_7\nPQRS" }, { '8', "_8\nTUV" }, { '9', "_9\nWXYZ" }, { '*', "_*\n+" }, { '0', "_0\n" }, { '#', "_#\n" } }; GtkWidget * table; GtkWidget * button; GtkWidget * image; GtkWidget * label; int i; table = gtk_table_new(5, 6, TRUE); /* call */ button = gtk_button_new(); image = gtk_image_new_from_icon_name(button1_image, GTK_ICON_SIZE_BUTTON); gtk_button_set_image(GTK_BUTTON(button), image); gtk_button_set_label(GTK_BUTTON(button), button1_label); g_signal_connect_swapped(G_OBJECT(button), "clicked", button1_callback, phone); gtk_table_attach(GTK_TABLE(table), button, 0, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); button = gtk_button_new(); image = gtk_image_new_from_icon_name(button2_image, GTK_ICON_SIZE_BUTTON); gtk_button_set_image(GTK_BUTTON(button), image); gtk_button_set_label(GTK_BUTTON(button), button2_label); g_signal_connect_swapped(G_OBJECT(button), "clicked", button2_callback, phone); gtk_table_attach(GTK_TABLE(table), button, 3, 6, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); /* numbers */ for(i = 0; i < 12; i++) { button = gtk_button_new(); label = gtk_label_new(NULL); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), numbers[i].label); gtk_container_add(GTK_CONTAINER(button), label); g_object_set_data(G_OBJECT(button), "character", &numbers[i].character); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK( button_callback), phone); gtk_table_attach(GTK_TABLE(table), button, (i % 3) * 2, ((i % 3) + 1) * 2, (i / 3) + 1, (i / 3) + 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); } return table; } /* phone_create_progress */ static GtkWidget * _phone_create_progress(GtkWidget * parent, char const * text) { GtkWidget * window; GtkWidget * vbox; GtkWidget * widget; /* window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK( on_phone_closex), NULL); gtk_window_set_modal(GTK_WINDOW(window), TRUE); gtk_window_set_title(GTK_WINDOW(window), _("Operation in progress...")); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent)); vbox = gtk_vbox_new(FALSE, 0); widget = gtk_label_new(text); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 2); /* progress bar */ widget = gtk_progress_bar_new(); g_object_set_data(G_OBJECT(window), "progress", widget); gtk_progress_bar_pulse(GTK_PROGRESS_BAR(widget)); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), " "); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 2); /* close button */ widget = gtk_button_new_from_stock(GTK_STOCK_CLOSE); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( gtk_widget_hide), window); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 2); gtk_container_set_border_width(GTK_CONTAINER(window), 4); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show_all(window); return window; } /* phone_confirm */ static int _phone_confirm(Phone * phone, GtkWidget * window, char const * message) { GtkWidget * dialog; int res; dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", #if GTK_CHECK_VERSION(2, 8, 0) _("Question")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", #endif message); gtk_window_set_title(GTK_WINDOW(dialog), _("Question")); res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(res == GTK_RESPONSE_YES) return 0; return 1; } /* phone_error */ static int _phone_error(GtkWidget * window, char const * message, int ret) { GtkWidget * dialog; GtkWindow * w = (window != NULL) ? GTK_WINDOW(window) : NULL; GtkDialogFlags flags = (window != NULL) ? GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT : 0; dialog = gtk_message_dialog_new(w, flags, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", #if GTK_CHECK_VERSION(2, 8, 0) _("Error")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", #endif message); gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK( gtk_widget_destroy), NULL); gtk_widget_show(dialog); return ret; } /* phone_info */ static void _phone_info(Phone * phone, GtkWidget * window, char const * message, GCallback callback) { GtkWidget * dialog; if(callback == NULL) callback = G_CALLBACK(gtk_widget_destroy); dialog = gtk_message_dialog_new((window != NULL) ? GTK_WINDOW(window) : NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", #if GTK_CHECK_VERSION(2, 8, 0) _("Information")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", #endif message); gtk_window_set_title(GTK_WINDOW(dialog), _("Information")); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(callback), phone); gtk_widget_show(dialog); } /* phone_progress_delete */ static GtkWidget * _phone_progress_delete(GtkWidget * widget) { if(widget != NULL) gtk_widget_destroy(widget); return NULL; } /* phone_progress_pulse */ static void _phone_progress_pulse(GtkWidget * widget) { GtkWidget * progress; if((progress = g_object_get_data(G_OBJECT(widget), "progress")) == NULL) return; gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress)); } /* phone_queue */ static int _phone_queue(Phone * phone, char const * command) { return (gsm_queue(phone->gsm, command) != NULL) ? 0 : 1; } /* phone_request */ static int _phone_request(Phone * phone, ModemRequest * request) { return gsm_request(phone->gsm, request); } /* phone_show_contacts_dialog */ static void _on_contacts_dialog_response(GtkWidget * widget, gint response, gpointer data); static void _phone_show_contacts_dialog(Phone * phone, gboolean show, int index, char const * name, char const * number) { char buf[256]; GtkDialogFlags f = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; GtkSizeGroup * group; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; if(phone->co_dialog == NULL) { group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); phone->co_dialog = gtk_dialog_new_with_buttons("", GTK_WINDOW(phone->co_window), f, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); g_signal_connect(G_OBJECT(phone->co_dialog), "delete-event", G_CALLBACK(on_phone_closex), NULL); g_signal_connect(G_OBJECT(phone->co_dialog), "response", G_CALLBACK(_on_contacts_dialog_response), phone); #if GTK_CHECK_VERSION(2, 14, 0) vbox = gtk_dialog_get_content_area(GTK_DIALOG( phone->co_dialog)); #else vbox = GTK_DIALOG(phone->co_dialog)->vbox; #endif gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); hbox = gtk_hbox_new(FALSE, 4); widget = gtk_label_new(_("Name: ")); gtk_size_group_add_widget(group, widget); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); phone->co_name = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), phone->co_name, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); hbox = gtk_hbox_new(FALSE, 4); widget = gtk_label_new(_("Number: ")); gtk_size_group_add_widget(group, widget); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); phone->co_number = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), phone->co_number, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4); gtk_widget_show_all(vbox); } if(show == FALSE) { gtk_widget_hide(phone->co_dialog); return; } if(index < 0) snprintf(buf, sizeof(buf), "%s", _("New contact")); else snprintf(buf, sizeof(buf), "%s%s", _("Edit contact: "), (name != NULL) ? name : ""); gtk_window_set_title(GTK_WINDOW(phone->co_dialog), buf); phone->co_index = index; if(name != NULL) gtk_entry_set_text(GTK_ENTRY(phone->co_name), name); if(number != NULL) gtk_entry_set_text(GTK_ENTRY(phone->co_number), number); gtk_widget_show(phone->co_dialog); } static void _on_contacts_dialog_response(GtkWidget * widget, gint response, gpointer data) { Phone * phone = data; char const * name; char const * number; if(response != GTK_RESPONSE_ACCEPT) { gtk_widget_hide(widget); return; } name = gtk_entry_get_text(GTK_ENTRY(phone->co_name)); number = gtk_entry_get_text(GTK_ENTRY(phone->co_number)); if(strlen(name) == 0) { _phone_error(widget, _("The name cannot be empty"), 0); return; } else if(strlen(number) == 0) { _phone_error(widget, _("The number cannot be empty"), 0); return; } gtk_widget_hide(widget); if(phone->co_index < 0) gsm_contact_new(phone->gsm, name, number); else gsm_contact_edit(phone->gsm, phone->co_index, name, number); } /* phone_track */ static void _phone_track(Phone * phone, PhoneTrack what, gboolean track) { size_t i; phone->tracks[what] = track; if(track) { if(phone->tr_source == 0) phone->tr_source = g_timeout_add(2000, _phone_timeout_track, phone); } else if(phone->tr_source != 0) { for(i = 0; i < PHONE_TRACK_COUNT; i++) if(phone->tracks[i] != FALSE) return; g_source_remove(phone->tr_source); phone->tr_source = 0; } } /* phone_trigger */ static int _phone_trigger(Phone * phone, ModemEventType event) { return gsm_trigger(phone->gsm, event); } /* phone_unload */ static int _phone_unload(Phone * phone, PhonePlugin * plugin) { gboolean valid; GtkTreeModel * model = GTK_TREE_MODEL(phone->se_store); GtkTreeIter iter; size_t i; PhonePlugin * pp; if(plugin == NULL) return phone_error(NULL, strerror(EINVAL), -1); /* view */ for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;) { gtk_tree_model_get(model, &iter, PHONE_SETTINGS_COLUMN_PLUGIN, &pp, -1); if(pp == plugin) { gtk_list_store_remove(phone->se_store, &iter); break; } else valid = gtk_tree_model_iter_next(model, &iter); } /* plugins */ for(i = 0; i < phone->plugins_cnt; i++) { if(phone->plugins[i].pp != plugin) continue; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() plugin %lu\n", __func__, (unsigned long)i); #endif if(plugin->destroy != NULL && plugin->destroy(plugin) != 0) phone_error(phone, error_get(), 0); plugin_delete(phone->plugins[i].p); memmove(&phone->plugins[i], &phone->plugins[i + 1], sizeof(*phone->plugins) * (--phone->plugins_cnt - i)); /* FIXME could call realloc() to gain some memory */ return 0; } #ifdef DEBUG fprintf(stderr, "DEBUG: %s() no such plugin\n", __func__); #endif return phone_error(NULL, strerror(ENOENT), -1); /* XXX not explicit */ } /* callbacks */ /* phone_modem_event */ static void _modem_event_authentication(Phone * phone, ModemEvent * event); static void _modem_event_call(Phone * phone, ModemEvent * event); static void _modem_event_error(Phone * phone, ModemEvent * event); static void _modem_event_message(Phone * phone, ModemEvent * event); static void _phone_modem_event(void * priv, ModemEvent * event) { Phone * phone = priv; PhoneEvent pevent; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u)\n", __func__, event->type); #endif switch(event->type) { case MODEM_EVENT_TYPE_ERROR: _modem_event_error(phone, event); break; case MODEM_EVENT_TYPE_AUTHENTICATION: _modem_event_authentication(phone, event); break; case MODEM_EVENT_TYPE_CALL: _modem_event_call(phone, event); break; case MODEM_EVENT_TYPE_CONTACT: phone_contacts_set(phone, event->contact.id, event->contact.name, event->contact.number); break; case MODEM_EVENT_TYPE_MESSAGE: _modem_event_message(phone, event); break; case MODEM_EVENT_TYPE_MESSAGE_SENT: _phone_track(phone, PHONE_TRACK_MESSAGE_SENT, FALSE); phone->wr_progress = _phone_progress_delete( phone->wr_progress); _phone_info(phone, phone->wr_window, _("Message sent"), NULL); break; case MODEM_EVENT_TYPE_MODEL: break; #ifndef DEBUG _phone_track(phone, PHONE_TRACK_CONTACT_LIST, TRUE); _phone_track(phone, PHONE_TRACK_MESSAGE_LIST, TRUE); #endif break; default: break; } memset(&pevent, 0, sizeof(pevent)); pevent.type = PHONE_EVENT_TYPE_MODEM_EVENT; pevent.modem_event.event = event; phone_event(phone, &pevent); } static void _modem_event_authentication(Phone * phone, ModemEvent * event) { ModemAuthenticationMethod method = event->authentication.method; char const * name = event->authentication.name; char buf[32]; GCallback callback = G_CALLBACK(_phone_modem_event_authentication); switch(event->authentication.status) { case MODEM_AUTHENTICATION_STATUS_ERROR: phone_code_clear(phone); snprintf(buf, sizeof(buf), _("Wrong %s"), name); _phone_error(phone->en_window, buf, 0); break; case MODEM_AUTHENTICATION_STATUS_OK: phone_code_clear(phone); if(name == NULL) break; snprintf(buf, sizeof(buf), _("%s is valid"), name); _phone_info(phone, phone->en_window, buf, callback); break; case MODEM_AUTHENTICATION_STATUS_REQUIRED: if(event->authentication.method == MODEM_AUTHENTICATION_METHOD_PIN) phone_show_code(phone, TRUE, method, name); break; } } static void _modem_event_call(Phone * phone, ModemEvent * event) { PhoneCall call; ModemCallStatus call_status; ModemCallDirection call_direction; if(event->call.number == NULL) return; /* XXX ignore for now */ call_status = event->call.status; call_direction = event->call.direction; if(call_status == MODEM_CALL_STATUS_ACTIVE) call = PHONE_CALL_ESTABLISHED; else if(call_status == MODEM_CALL_STATUS_RINGING) call = (call_direction == MODEM_CALL_DIRECTION_INCOMING) ? PHONE_CALL_INCOMING : PHONE_CALL_OUTGOING; else call = PHONE_CALL_TERMINATED; phone_show_call(phone, TRUE, call, "", event->call.number); } static void _modem_event_error(Phone * phone, ModemEvent * event) { phone_error(phone, event->error.message, 0); } static void _modem_event_message(Phone * phone, ModemEvent * event) { GSMEncoding encoding; char * content; size_t length; encoding = event->message.encoding; length = event->message.length; if((content = malloc(length + 1)) == NULL) return; /* XXX report error */ memcpy(content, event->message.content, length); content[length] = '\0'; /* just in case */ switch(encoding) { case GSM_ENCODING_UTF8: phone_messages_set(phone, event->message.id, event->message.number, event->message.date, content); break; case GSM_ENCODING_RAW_DATA: phone_messages_set(phone, event->message.id, event->message.number, event->message.date, _("Raw data (not shown)")); break; } free(content); } /* phone_modem_event_authentication */ static void _phone_modem_event_authentication(GtkWidget * widget, gint response, gpointer data) { Phone * phone = data; phone_show_code(phone, FALSE); gtk_widget_destroy(widget); } /* phone_timeout_track */ static gboolean _phone_timeout_track(gpointer data) { Phone * phone = data; if(phone->tracks[PHONE_TRACK_CODE_ENTERED]) _phone_progress_pulse(phone->en_progress); if(phone->tracks[PHONE_TRACK_CONTACT_LIST]) { _phone_track(phone, PHONE_TRACK_CONTACT_LIST, FALSE); gsm_fetch_contact_list(phone->gsm); } if(phone->tracks[PHONE_TRACK_MESSAGE_LIST]) { _phone_track(phone, PHONE_TRACK_MESSAGE_LIST, FALSE); gsm_fetch_message_list(phone->gsm, GSM_MESSAGE_LIST_ALL); } if(phone->tracks[PHONE_TRACK_MESSAGE_SENT]) _phone_progress_pulse(phone->wr_progress); if(phone->tracks[PHONE_TRACK_REGISTRATION]) gsm_is_registered(phone->gsm); return TRUE; }