applet: read speech string from config
[espeaktime] / src / applet.c
1 #include <unistd.h>
2 #include <string.h>
3 #include <gtk/gtk.h>
4 #include <hildon/hildon.h>
5 #include <hildon-cp-plugin/hildon-cp-plugin-interface.h>
6 #include <gconf/gconf-client.h>
7 #include "config.h"
8
9 /* TODO: read these from disk */
10
11 static const struct voice {
12         const char *id;
13         const char *name;
14 } voices[] = {
15         { "",           "Default" },
16         { "en",         "English" },
17         { "en-us",      "English (American)" },
18         { "en-sc",      "English (Scottish)" },
19         { "af",         "Afrikaans" },
20         { "bs",         "Bosnian" },
21         { "ca",         "Catalan" },
22         { "cs",         "Czech" },
23         { "de",         "German" },
24         { "el",         "Greek" },
25         { "eo",         "Esperanto" },
26         { "es",         "Spanish" },
27         { "es-la",      "Spanish (Latin America)" },
28         { "fi",         "Finnish" },
29         { "fr",         "French" },
30         { "hr",         "Croatian" },
31         { "hu",         "Hungarian" },
32         { "it",         "Italian" },
33         { "ku",         "Kurdish" },
34         { "lv",         "Latvian" },
35         { "pl",         "Polish" },
36         { "pt",         "Portuguese (Brazil)" },
37         { "pt-pt",      "Portuguese (European)" },
38         { "ro",         "Romanian" },
39         { "ru",         "Russian [extra]" },
40         { "sk",         "Slovak" },
41         { "sr",         "Serbian" },
42         { "sv",         "Swedish" },
43         { "sw",         "Swahihi" },
44         { "ta",         "Tamil" },
45         { "tr",         "Turkish" },
46         { "zh",         "Chinese (Mandarin) [extra]" },
47         { "zh-yue",     "Chinese (Cantonese) [extra]" },
48 };
49 static const int num_voices = sizeof(voices) / sizeof(voices[0]);
50
51 static const struct effect {
52         const char *id;
53         const char *name;
54 } effects[] = {
55         { "",           "(none)" },
56         { "f1",         "Female 1" },
57         { "f2",         "Female 2" },
58         { "f3",         "Female 3" },
59         { "f4",         "Female 4" },
60         { "m1",         "Male 1" },
61         { "m2",         "Male 2" },
62         { "m3",         "Male 3" },
63         { "m4",         "Male 4" },
64         { "m5",         "Male 5" },
65         { "m6",         "Male 6" },
66         { "m7",         "Male 7" },
67         { "croak",      "Croak" },
68         { "fast",       "Fast" },
69         { "klatt",      "Klatt" },
70         { "klatt2",     "Klatt 2" },
71         { "klatt3",     "Klatt 3" },
72         { "whisper",    "Whisper" },
73 };
74 static const int num_effects = sizeof(effects) / sizeof(effects[0]);
75
76 static const gchar help_message[] =
77         "The Speech string can be any string accepted by strftime(3). "
78         "The default is \"%H:%M\".  An excerpt from the strftime man page is "
79         "provided below:\n"
80         "\n\n"
81         "Ordinary characters placed in the format string are copied to the output "
82         "without conversion. Conversion specifications are introduced by a '%' "
83         "character, and terminated by a conversion specifier character, and are "
84         "replaced in the output as follows:\n"
85         "\n"
86         "%a - The abbreviated weekday name according to the current locale. \n"
87         "\n"
88         "%A - The full weekday name according to the current locale. \n"
89         "\n"
90         "%b - The abbreviated month name according to the current locale. \n"
91         "\n"
92         "%B - The full month name according to the current locale. \n"
93         "\n"
94         "%c - The preferred date and time representation for the current locale. \n"
95         "\n"
96         "%C - The century number (year/100) as a 2-digit integer. (SU) \n"
97         "\n"
98         "%d - The day of the month as a decimal number (range 01 to 31). \n"
99         "\n"
100         "%D - Equivalent to %m/%d/%y. (Yecch -- for Americans only. Americans "
101         "should note that in other countries %d/%m/%y is rather common. This "
102         "means that in international context this format is ambiguous and should "
103         "not be used.) (SU) \n"
104         "\n"
105         "%e - Like %d, the day of the month as a decimal number, but a leading "
106         "zero is replaced by a space. (SU) \n"
107         "\n"
108         "%E - Modifier: use alternative format, see below. (SU) \n"
109         "\n"
110         "%F - Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99) \n"
111         "\n"
112         "%G - The ISO 8601 year with century as a decimal number. The 4-digit "
113         "year corresponding to the ISO week number (see %V). This has the same "
114         "format and value as %y, except that if the ISO week number belongs to "
115         "the previous or next year, that year is used instead. (TZ) \n"
116         "\n"
117         "%g - Like %G, but without century, i.e., with a 2-digit year (00-99).\n"
118         "(TZ) \n"
119         "\n"
120         "%h - Equivalent to %b. (SU) \n"
121         "\n"
122         "%H - The hour as a decimal number using a 24-hour clock (range 00 to 23). \n"
123         "\n"
124         "%I - The hour as a decimal number using a 12-hour clock (range 01 to 12). \n"
125         "\n"
126         "%j - The day of the year as a decimal number (range 001 to 366). \n"
127         "\n"
128         "%k - The hour (24-hour clock) as a decimal number (range 0 to 23); "
129         "single digits are preceded by a blank. (See also %H.) (TZ) \n"
130         "\n"
131         "%l - The hour (12-hour clock) as a decimal number (range 1 to 12); "
132         "single digits are preceded by a blank. (See also %I.) (TZ) \n"
133         "\n"
134         "%m - The month as a decimal number (range 01 to 12). \n"
135         "\n"
136         "%M - The minute as a decimal number (range 00 to 59). \n"
137         "\n"
138         "%n - A newline character. (SU) \n"
139         "\n"
140         "%O - Modifier: use alternative format, see below. (SU) \n"
141         "\n"
142         "%p - Either 'AM' or 'PM' according to the given time value, or the "
143         "corresponding strings for the current locale. Noon is treated as 'pm' "
144         "and midnight as 'am'. \n"
145         "\n"
146         "%P - Like %p but in lowercase: 'am' or 'pm' or a corresponding string "
147         "for the current locale. (GNU) \n"
148         "\n"
149         "%r - The time in a.m. or p.m. notation. In the POSIX locale this is "
150         "equivalent to '%I:%M:%S %p'. (SU) \n"
151         "\n"
152         "%R - The time in 24-hour notation (%H:%M). (SU) For a version including "
153         "the seconds, see %T below. \n"
154         "\n"
155         "%s - The number of seconds since the Epoch, i.e., since 1970-01-01 "
156         "00:00:00 UTC. (TZ) \n"
157         "\n"
158         "%S - The second as a decimal number (range 00 to 60). (The range is up "
159         "to 60 to allow for occasional leap seconds.) \n"
160         "\n"
161         "%t - A tab character. (SU) \n"
162         "\n"
163         "%T - The time in 24-hour notation (%H:%M:%S). (SU) \n"
164         "\n"
165         "%u - The day of the week as a decimal, range 1 to 7, Monday being 1. See "
166         "also %w. (SU) \n"
167         "\n"
168         "%U - The week number of the current year as a decimal number, range 00 "
169         "to 53, starting with the first Sunday as the first day of week 01. See "
170         "also %V and %W. \n"
171         "\n"
172         "%V - The ISO 8601:1988 week number of the current year as a decimal "
173         "number, range 01 to 53, where week 1 is the first week that has at least "
174         "4 days in the current year, and with Monday as the first day of the "
175         "week. See also %U and %W. (SU) \n"
176         "\n"
177         "%w - The day of the week as a decimal, range 0 to 6, Sunday being 0. See "
178         "also %u. \n"
179         "\n"
180         "%W - The week number of the current year as a decimal number, range 00 "
181         "to 53, starting with the first Monday as the first day of week 01. \n"
182         "\n"
183         "%x - The preferred date representation for the current locale without "
184         "the time. \n"
185         "\n"
186         "%X - The preferred time representation for the current locale without "
187         "the date. \n"
188         "\n"
189         "%y - The year as a decimal number without a century (range 00 to 99). \n"
190         "\n"
191         "%Y - The year as a decimal number including the century. \n"
192         "\n"
193         "%z - The time-zone as hour offset from GMT. Required to emit RFC "
194         "822-conformant dates (using \"%a, %d %b %Y %H:%M:%S %z\"). (GNU) \n"
195         "\n"
196         "%Z - The time zone or name or abbreviation. \n"
197         "\n"
198         "%+ - The date and time in date(1) format. (TZ) (Not supported in "
199         "glibc2.) \n"
200         "\n"
201         "%% - A literal '%' character.\n"
202         "\n"
203         "\n"
204         "Some conversion specifications can be modified by preceding the "
205         "conversion specifier character by the E or O modifier to indicate that "
206         "an alternative format should be used. If the alternative format or "
207         "specification does not exist for the current locale, the behaviour will "
208         "be as if the unmodified conversion specification were used. (SU) The "
209         "Single Unix Specification mentions %Ec, %EC, %Ex, %EX, %Ey, %EY, %Od, "
210         "%Oe, %OH, %OI, %Om, %OM, %OS, %Ou, %OU, %OV, %Ow, %OW, %Oy, where the "
211         "effect of the O modifier is to use alternative numeric symbols (say, "
212         "roman numerals), and that of the E modifier is to use a locale-dependent "
213         "alternative representation.\n";
214
215
216
217 static void show_help(GtkButton *button, GtkWidget *parent)
218 {
219         GtkWidget *dialog, *label, *panarea;
220
221         dialog = gtk_dialog_new_with_buttons(
222                 "eSpeakTime Help",
223                 GTK_WINDOW(parent),
224                 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
225                 GTK_STOCK_OK, GTK_RESPONSE_OK,
226                 NULL);
227
228         label = gtk_label_new(help_message);
229         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
230         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
231
232         panarea = hildon_pannable_area_new();
233         gtk_widget_set_size_request(panarea, -1, 800);
234         hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(panarea), label);
235         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), panarea);
236
237         g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
238         gtk_widget_show_all(dialog);
239 }
240
241 static void add_scale(GtkVBox *vbox, GtkSizeGroup *size_group, const char *caption, GtkAdjustment *adjustment)
242 {
243         GtkWidget *scale = gtk_hscale_new(adjustment);
244         gtk_scale_set_digits(GTK_SCALE(scale), 0);
245         gtk_box_pack_start(GTK_BOX(vbox),
246                 hildon_caption_new(size_group, caption, scale, NULL, HILDON_CAPTION_MANDATORY),
247                 FALSE, FALSE, 0);
248 }
249
250 osso_return_t execute(osso_context_t *osso, gpointer data, gboolean user_activated)
251 {
252         struct espeaktime_settings cfg = {
253                 .voice = "en-us",
254                 .effect = "",
255                 .amplitude = 100,
256                 .pitch = 50,
257                 .speed = 170,
258                 .ignore_silent = TRUE,
259         };
260
261         GConfClient *client = gconf_client_get_default();
262         cfg_read(client, &cfg);
263
264         GtkWidget *dialog;
265         dialog = gtk_dialog_new_with_buttons(
266                 "eSpeakTime Settings",
267                 GTK_WINDOW(data),
268                 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
269                 "Test", 1,
270                 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
271                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
272                 NULL);
273
274         int k, voice_sel = 0, effect_sel = 0;
275         GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
276         GtkSizeGroup *title_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
277         GtkSizeGroup *value_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
278
279         /* selectors */
280         HildonTouchSelector *voice_selector = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new_text());
281         for (k = 0; k < num_voices; k++) {
282                 hildon_touch_selector_append_text(voice_selector, voices[k].name);
283                 if (!strcmp(voices[k].id, cfg.voice))
284                         voice_sel = k;
285         }
286
287         HildonTouchSelector *effect_selector = HILDON_TOUCH_SELECTOR(hildon_touch_selector_new_text());
288         for (k = 0; k < num_effects; k++) {
289                 hildon_touch_selector_append_text(effect_selector, effects[k].name);
290                 if (!strcmp(effects[k].id, cfg.effect))
291                         effect_sel = k;
292         }
293
294         HildonPickerButton *voice = HILDON_PICKER_BUTTON(hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL));
295         hildon_button_set_title(HILDON_BUTTON(voice), "Voice");
296         hildon_picker_button_set_selector(voice, voice_selector);
297         hildon_picker_button_set_active(voice, voice_sel);
298         hildon_button_add_size_groups(HILDON_BUTTON(voice), title_group, value_group, NULL);
299         gtk_button_set_alignment(GTK_BUTTON(voice), 0.0, 0.5);
300         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(voice), FALSE, FALSE, 0);
301
302         HildonPickerButton *effect = HILDON_PICKER_BUTTON(hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL));
303         hildon_button_set_title(HILDON_BUTTON(effect), "Effect");
304         hildon_picker_button_set_selector(effect, effect_selector);
305         hildon_picker_button_set_active(effect, effect_sel);
306         hildon_button_add_size_groups(HILDON_BUTTON(effect), title_group, value_group, NULL);
307         gtk_button_set_alignment(GTK_BUTTON(effect), 0.0, 0.5);
308         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(effect), FALSE, FALSE, 0);
309
310         GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
311         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
312
313         HildonEntry *text = HILDON_ENTRY(hildon_entry_new(HILDON_SIZE_AUTO));
314         gtk_entry_set_text(GTK_ENTRY(text), cfg.text);
315         gtk_box_pack_start(GTK_BOX(hbox),
316                 hildon_caption_new(title_group, "Speech string", GTK_WIDGET(text), NULL, HILDON_CAPTION_MANDATORY),
317                 TRUE, TRUE, 0);
318
319         GtkWidget *help_btn = hildon_button_new_with_text(HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
320                 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL, " ? ", NULL);
321         g_signal_connect(help_btn, "clicked", G_CALLBACK(show_help), dialog);
322         gtk_box_pack_start(GTK_BOX(hbox), help_btn, FALSE, FALSE, 0);
323
324         GtkAdjustment *amplitude_adj = GTK_ADJUSTMENT(gtk_adjustment_new(cfg.amplitude, 0, 200, 1, 10, 0));
325         add_scale(vbox, title_group, "Amplitude", amplitude_adj);
326
327         GtkAdjustment *pitch_adj = GTK_ADJUSTMENT(gtk_adjustment_new(cfg.pitch, 00, 99, 1, 10, 0));
328         add_scale(vbox, title_group, "Pitch", pitch_adj);
329
330         GtkAdjustment *speed_adj = GTK_ADJUSTMENT(gtk_adjustment_new(cfg.speed, 80, 390, 1, 10, 0));
331         add_scale(vbox, title_group, "Speed (wpm)", speed_adj);
332
333         HildonCheckButton *ignore_silent = HILDON_CHECK_BUTTON(hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT));
334         hildon_check_button_set_active(ignore_silent, cfg.ignore_silent);
335         gtk_button_set_label(GTK_BUTTON(ignore_silent), "Ignore silent profile");
336         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(ignore_silent), FALSE, FALSE, 0);
337
338         GtkWidget *panarea = hildon_pannable_area_new();
339         gtk_widget_set_size_request(panarea, -1, 800);
340         hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(panarea), GTK_WIDGET(vbox));
341
342         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), panarea);
343         gtk_widget_show_all(dialog);
344         while (1) {
345                 int result = gtk_dialog_run(GTK_DIALOG(dialog));
346
347                 g_free(cfg.voice);
348                 g_free(cfg.effect);
349                 g_free(cfg.text);
350                 cfg.voice = g_strdup(voices[hildon_picker_button_get_active(voice)].id);
351                 cfg.effect = g_strdup(effects[hildon_picker_button_get_active(effect)].id);
352                 cfg.text = g_strdup(gtk_entry_get_text(GTK_ENTRY(text)));
353                 cfg.amplitude = gtk_adjustment_get_value(amplitude_adj);
354                 cfg.pitch = gtk_adjustment_get_value(pitch_adj);
355                 cfg.speed = gtk_adjustment_get_value(speed_adj);
356                 cfg.ignore_silent = hildon_check_button_get_active(ignore_silent);
357
358                 switch (result) {
359                 case 1:
360                         g_print("Test button\n");
361                         cfg_speak(&cfg, TRUE);
362                         continue;
363                 case GTK_RESPONSE_OK:
364                         g_print("Save\n");
365                         cfg_write(client, &cfg);
366                         break;
367                 }
368                 break;
369         }
370         gtk_widget_destroy(GTK_WIDGET(dialog));
371
372         cfg_free(&cfg);
373         g_object_unref(G_OBJECT(client));
374
375         return OSSO_OK;
376 }
377
378 osso_return_t save_state(osso_context_t *osso, gpointer data)
379 {
380         g_print("save_state called\n");
381         return OSSO_OK;
382 }
383
384 #ifdef TEST
385 int main(int argc, char *argv[])
386 {
387         gtk_init(&argc, &argv);
388         return execute(NULL, NULL, 0);
389 }
390 #endif