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