1 /* Copyright 2006-2011 Enrico Tröger <enrico(at)xfce(dot)org>
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Library General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <gtk/gtk.h>
23 #include <libxfce4ui/libxfce4ui.h>
24
25 #include "common.h"
26 #include "speedreader.h"
27
28
29 typedef struct _XfdSpeedReaderPrivate XfdSpeedReaderPrivate;
30
31 struct _XfdSpeedReaderPrivate
32 {
33 GtkWidget *first_page;
34 GtkWidget *second_page;
35
36 GtkWidget *button_start;
37 GtkWidget *button_stop;
38 GtkWidget *button_pause;
39
40 GtkWidget *spin_wpm;
41 GtkWidget *spin_grouping;
42 GtkWidget *button_font;
43 GtkWidget *check_mark_paragraphs;
44 GtkWidget *display_label;
45 GtkTextBuffer *buffer;
46
47 guint timer_id;
48 guint word_idx;
49 gsize words_len;
50 gchar **words;
51
52 GString *group;
53 gsize group_size;
54
55 gboolean paused;
56
57 DictData *dd;
58 };
59
60 enum
61 {
62 RESPONSE_START,
63 RESPONSE_STOP,
64 RESPONSE_PAUSE
65 };
66
67 enum
68 {
69 XSR_STATE_INITIAL,
70 XSR_STATE_RUNNING,
71 XSR_STATE_FINISHED
72 };
73
74 #define XFD_TITLE_PAUSE _("P_ause")
75 #define XFD_TITLE_RESUME _("_Resume")
76
77
78 G_DEFINE_TYPE_WITH_PRIVATE(XfdSpeedReader, xfd_speed_reader, GTK_TYPE_DIALOG);
79
80 static void sr_stop(XfdSpeedReader *dialog);
81 static void sr_stop_timer(XfdSpeedReader *dialog);
82 static void sr_pause(XfdSpeedReader *dialog, gboolean paused);
83
84
xfd_speed_reader_finalize(GObject * object)85 static void xfd_speed_reader_finalize(GObject *object)
86 {
87 g_return_if_fail(object != NULL);
88 g_return_if_fail(IS_XFD_SPEED_READER(object));
89
90 sr_stop_timer(XFD_SPEED_READER(object));
91
92 G_OBJECT_CLASS(xfd_speed_reader_parent_class)->finalize(object);
93 }
94
95
xfd_speed_reader_class_init(XfdSpeedReaderClass * klass)96 static void xfd_speed_reader_class_init(XfdSpeedReaderClass *klass)
97 {
98 GObjectClass *g_object_class;
99
100 g_object_class = G_OBJECT_CLASS(klass);
101 g_object_class->finalize = xfd_speed_reader_finalize;
102 }
103
104
105 /* Based on GLib's g_strsplit_set() but slightly modified to split exactly what we need for
106 * speed reading (e.g. splitting but not removing dashes). */
sr_strsplit_set(const gchar * string,const gchar * delimiters)107 static gchar **sr_strsplit_set(const gchar *string, const gchar *delimiters)
108 {
109 gboolean delim_table[256];
110 GSList *tokens, *list;
111 gint n_tokens;
112 guint x;
113 const gchar *s;
114 const gchar *current;
115 gchar *token;
116 gchar **result;
117
118 g_return_val_if_fail(string != NULL, NULL);
119 g_return_val_if_fail(delimiters != NULL, NULL);
120
121 if (*string == '\0')
122 {
123 result = g_new(char *, 1);
124 result[0] = NULL;
125 return result;
126 }
127
128 memset(delim_table, FALSE, sizeof (delim_table));
129 for (s = delimiters; *s != '\0'; ++s)
130 delim_table[*(guchar *)s] = TRUE;
131
132 tokens = NULL;
133 n_tokens = 0;
134
135 s = current = string;
136 while (*s != '\0')
137 {
138 if (delim_table[*(guchar *)s])
139 {
140 x = (*s == '-') ? 1 : 0;
141 token = g_strndup(current, s - current + x);
142 tokens = g_slist_prepend(tokens, token);
143 ++n_tokens;
144
145 current = s + 1;
146 }
147 ++s;
148 }
149
150 token = g_strndup(current, s - current);
151 tokens = g_slist_prepend(tokens, token);
152 ++n_tokens;
153
154 result = g_new(gchar *, n_tokens + 1);
155
156 result[n_tokens] = NULL;
157 for (list = tokens; list != NULL; list = list->next)
158 result[--n_tokens] = list->data;
159
160 g_slist_free(tokens);
161
162 return result;
163 }
164
165
sr_replace_unicode_characters(const gchar * text,gboolean mark_paragraphs)166 static gchar *sr_replace_unicode_characters(const gchar *text, gboolean mark_paragraphs)
167 {
168 GString *str;
169 gchar *result;
170 gunichar c = 0, last_c, x;
171 gboolean last_line_was_empty = FALSE;
172
173 g_return_val_if_fail(text != NULL, NULL);
174
175 if (! g_utf8_validate(text, -1, NULL))
176 return g_strdup(text);
177
178 str = g_string_new(NULL);
179
180 while (*text)
181 {
182 last_c = c;
183 c = g_utf8_get_char(text);
184
185 switch (g_unichar_type(c))
186 {
187 case G_UNICODE_DASH_PUNCTUATION:
188 g_string_append_c(str, '-');
189 break;
190 case G_UNICODE_SPACE_SEPARATOR:
191 g_string_append_c(str, ' ');
192 break;
193 case G_UNICODE_PARAGRAPH_SEPARATOR:
194 if (mark_paragraphs)
195 g_string_append_unichar(str, 182); /* 182 = ¶ */
196 /* intended fall-through */
197 case G_UNICODE_LINE_SEPARATOR:
198 g_string_append_c(str, '\n');
199 break;
200 case G_UNICODE_CONTROL:
201 {
202 /* if not \n or \r, add it literally */
203 if (! mark_paragraphs || strchr("\n\r", c) == NULL)
204 {
205 g_string_append_unichar(str, c);
206 }
207 else /* add pilcrows for paragraphs */
208 {
209 if ((last_c == '\r' && c == '\n') || /* CRLF */
210 (last_c == '\r' && c != '\n') || /* CR */
211 (last_c != '\r' && c == '\n')) /* LF */
212 {
213 if (c == '\n')
214 /* skip to the next character */
215 x = g_utf8_get_char(g_utf8_next_char(text));
216 else
217 x = c;
218
219 if (strchr("\r\n", x))
220 {
221 last_line_was_empty = TRUE;
222 }
223 else if (last_line_was_empty)
224 {
225 last_line_was_empty = FALSE;
226 g_string_append(str, "¶\n");
227 }
228 }
229 g_string_append_unichar(str, c);
230 }
231 break;
232 }
233 default:
234 g_string_append_unichar(str, c);
235 }
236 text = g_utf8_next_char(text);
237 }
238
239 result = g_string_free(str, (str->len == 0));
240
241 return (result) ? result : g_strdup(text);
242 }
243
244
xfd_speed_reader_set_window_title(XfdSpeedReader * dialog,gint state)245 static void xfd_speed_reader_set_window_title(XfdSpeedReader *dialog, gint state)
246 {
247 gchar *title, *state_str, *name;
248 const gchar *button_label = _("S_top");
249 const gchar *button_image = "media-playback-stop";
250 gboolean pausable = TRUE;
251 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
252
253 switch (state)
254 {
255 case XSR_STATE_RUNNING:
256 state_str = _("Running");
257 break;
258 case XSR_STATE_FINISHED:
259 state_str = _("Finished");
260 button_label = _("_Back");
261 button_image = "go-previous";
262 pausable = FALSE;
263 break;
264 default:
265 state_str = "";
266 }
267
268 name = _("Speed Reader");
269 title = g_strdup_printf("%s%s%s", name, (NZV(state_str)) ? " - " : "", state_str);
270
271 gtk_window_set_title(GTK_WINDOW(dialog), title);
272 gtk_button_set_label(GTK_BUTTON(priv->button_stop), button_label);
273 gtk_button_set_image(GTK_BUTTON(priv->button_stop),
274 gtk_image_new_from_icon_name(button_image, GTK_ICON_SIZE_MENU));
275 gtk_widget_set_sensitive(priv->button_pause, pausable);
276
277 g_free(title);
278 }
279
280
sr_set_label_text(XfdSpeedReader * dialog)281 static void sr_set_label_text(XfdSpeedReader *dialog)
282 {
283 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
284
285 if (NZV(priv->group->str))
286 gtk_label_set_text(GTK_LABEL(priv->display_label), priv->group->str);
287 g_string_erase(priv->group, 0, -1);
288 }
289
290
sr_timer(gpointer data)291 static gboolean sr_timer(gpointer data)
292 {
293 gsize i;
294 XfdSpeedReader *dialog = XFD_SPEED_READER(data);
295 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
296
297 if (priv->paused)
298 return TRUE;
299
300 if (priv->word_idx >= priv->words_len)
301 {
302 sr_stop(dialog);
303 xfd_speed_reader_set_window_title(dialog, XSR_STATE_FINISHED);
304 return FALSE;
305 }
306
307 for (i = 0; (i < priv->group_size) && (priv->word_idx < priv->words_len); i++)
308 {
309 /* skip empty elements */
310 while (priv->word_idx < priv->words_len && ! NZV(priv->words[priv->word_idx]))
311 priv->word_idx++;
312
313 if (priv->word_idx < priv->words_len)
314 {
315 if (g_utf8_get_char(priv->words[priv->word_idx]) == 182)
316 { /* paragraph sign inside the group */
317 g_string_append_unichar(priv->group, 182);
318 sr_set_label_text(data);
319 priv->word_idx++;
320 return TRUE;
321 }
322 if ((priv->word_idx + 1) < priv->words_len &&
323 g_utf8_get_char(priv->words[priv->word_idx + 1]) == 182)
324 { /* paragraph sign in the next group, so move it to this group */
325 g_string_append(priv->group, priv->words[priv->word_idx]);
326 g_string_append_unichar(priv->group, 182);
327 sr_set_label_text(data);
328 priv->word_idx += 2;
329 return TRUE;
330 }
331 else
332 {
333 g_string_append(priv->group, priv->words[priv->word_idx]);
334 if (i < (priv->group_size - 1))
335 g_string_append_c(priv->group, ' ');
336 }
337 }
338 priv->word_idx++;
339 }
340 sr_set_label_text(data);
341
342 return TRUE;
343 }
344
345
sr_start(XfdSpeedReader * dialog)346 static void sr_start(XfdSpeedReader *dialog)
347 {
348 gint wpm, grouping;
349 gint interval;
350 gchar *fontname;
351 gchar *text, *cleaned_text;
352 GtkTextIter start, end;
353 gchar *css;
354 GtkCssProvider *provider;
355 PangoFontDescription *font;
356
357 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
358
359 /* clear the label text */
360 gtk_label_set_text(GTK_LABEL(priv->display_label), NULL);
361
362 /* get the text */
363 gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(priv->buffer), &start);
364 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(priv->buffer), &end);
365 text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(priv->buffer), &start, &end, FALSE);
366 if (! NZV(text))
367 {
368 gtk_dialog_response(GTK_DIALOG(dialog), RESPONSE_STOP);
369 dict_show_msgbox(priv->dd, GTK_MESSAGE_ERROR, _("You must enter a text."));
370 return;
371 }
372
373 xfd_speed_reader_set_window_title(dialog, XSR_STATE_RUNNING);
374
375 /* mark paragraphs? */
376 priv->dd->speedreader_mark_paragraphs = gtk_toggle_button_get_active(
377 GTK_TOGGLE_BUTTON(priv->check_mark_paragraphs));
378
379 /* set the font */
380 fontname = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(priv->button_font));
381 font = pango_font_description_from_string(fontname);
382
383 if (G_LIKELY (font))
384 {
385 css = g_strdup_printf("label { font-family: %s; font-size: %dpt; font-style: %s; font-weight: %s }",
386 pango_font_description_get_family (font),
387 pango_font_description_get_size (font) / PANGO_SCALE,
388 (pango_font_description_get_style(font) == PANGO_STYLE_ITALIC ||
389 pango_font_description_get_style(font) == PANGO_STYLE_OBLIQUE) ? "italic" : "normal",
390 (pango_font_description_get_weight(font) >= PANGO_WEIGHT_BOLD) ? "bold" : "normal");
391 pango_font_description_free (font);
392 }
393 else
394 css = g_strdup_printf("* { font: %s; }", fontname);
395
396 provider = gtk_css_provider_new ();
397 gtk_css_provider_load_from_data (provider, css, -1, NULL);
398 gtk_style_context_add_provider (
399 GTK_STYLE_CONTEXT (gtk_widget_get_style_context (GTK_WIDGET (priv->display_label))),
400 GTK_STYLE_PROVIDER(provider),
401 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
402 g_free(css);
403
404 /* word grouping */
405 grouping = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(priv->spin_grouping));
406 if (grouping >= 1 && grouping < 100) /* paranoia */
407 priv->group_size = grouping;
408
409 /* calculate the rate */
410 wpm = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(priv->spin_wpm));
411 if (wpm < 1)
412 wpm = 400;
413 interval = 60000 / wpm;
414
415 /* save the settings */
416 priv->dd->speedreader_wpm = wpm;
417 priv->dd->speedreader_grouping = grouping;
418 g_free(priv->dd->speedreader_font);
419 priv->dd->speedreader_font = g_strdup(fontname);
420
421 /* prepare word list and start the timer */
422 priv->word_idx = 0;
423 priv->group = g_string_new(NULL);
424 /* replace Unicode dashes and spaces and mark paragraphs */
425 cleaned_text = sr_replace_unicode_characters(text, priv->dd->speedreader_mark_paragraphs);
426 priv->words = sr_strsplit_set(cleaned_text, " -_=\t\n\r");
427 priv->words_len = g_strv_length(priv->words);
428
429 priv->timer_id = g_timeout_add(interval, sr_timer, dialog);
430 sr_pause(dialog, FALSE);
431
432 g_free(text);
433 g_free(cleaned_text);
434 g_free(fontname);
435 }
436
437
sr_stop_timer(XfdSpeedReader * dialog)438 static void sr_stop_timer(XfdSpeedReader *dialog)
439 {
440 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
441
442 if (priv->timer_id > 0)
443 {
444 g_source_remove(priv->timer_id);
445 priv->timer_id = 0;
446
447 g_string_free(priv->group, TRUE);
448 priv->group = NULL;
449 g_strfreev(priv->words);
450 priv->words = NULL;
451 }
452 }
453
454
sr_stop(XfdSpeedReader * dialog)455 static void sr_stop(XfdSpeedReader *dialog)
456 {
457 sr_stop_timer(dialog);
458 sr_pause(dialog, FALSE);
459 xfd_speed_reader_set_window_title(dialog, XSR_STATE_INITIAL);
460 }
461
462
sr_pause(XfdSpeedReader * dialog,gboolean paused)463 static void sr_pause(XfdSpeedReader *dialog, gboolean paused)
464 {
465 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
466
467 if (paused)
468 {
469 gtk_button_set_image(GTK_BUTTON(priv->button_pause),
470 gtk_image_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_MENU));
471 gtk_button_set_label(GTK_BUTTON(priv->button_pause), XFD_TITLE_RESUME);
472 }
473 else
474 {
475 gtk_button_set_image(GTK_BUTTON(priv->button_pause),
476 gtk_image_new_from_icon_name("media-playback-pause", GTK_ICON_SIZE_MENU));
477 gtk_button_set_label(GTK_BUTTON(priv->button_pause), XFD_TITLE_PAUSE);
478 }
479 /* set the new value */
480 priv->paused = paused;
481 }
482
483
xfd_speed_reader_response_cb(XfdSpeedReader * dialog,gint response,gpointer data)484 static void xfd_speed_reader_response_cb(XfdSpeedReader *dialog, gint response, gpointer data)
485 {
486 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
487
488 if (response == GTK_RESPONSE_CLOSE || response == GTK_RESPONSE_DELETE_EVENT)
489 {
490 gtk_widget_destroy(GTK_WIDGET(dialog));
491 }
492 else if (response == RESPONSE_START)
493 {
494 gtk_widget_hide(priv->button_start);
495 gtk_widget_show(priv->button_stop);
496 gtk_widget_show(priv->button_pause);
497
498 gtk_widget_hide(priv->first_page);
499 gtk_widget_show(priv->second_page);
500
501 sr_start(dialog);
502 }
503 else if (response == RESPONSE_STOP)
504 {
505 gtk_widget_hide(priv->button_stop);
506 gtk_widget_hide(priv->button_pause);
507 gtk_widget_show(priv->button_start);
508
509 gtk_widget_hide(priv->second_page);
510 gtk_widget_show(priv->first_page);
511
512 sr_stop(dialog);
513 }
514 else if (response == RESPONSE_PAUSE)
515 {
516 /* update the GUI */
517 sr_pause(dialog, ! priv->paused);
518 }
519 }
520
521
sr_open_clicked_cb(GtkButton * button,XfdSpeedReader * window)522 static void sr_open_clicked_cb(GtkButton *button, XfdSpeedReader *window)
523 {
524 GtkWidget *dialog;
525
526 dialog = gtk_file_chooser_dialog_new(_("Choose a file to load"),
527 GTK_WINDOW(window),
528 GTK_FILE_CHOOSER_ACTION_OPEN,
529 _("_Cancel"), GTK_RESPONSE_CANCEL,
530 _("_Open"), GTK_RESPONSE_ACCEPT,
531 NULL);
532
533 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
534 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
535 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
536 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
537 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
538 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
539
540 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
541 {
542 gchar *filename;
543 gchar *text;
544 gsize len;
545 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(window);
546
547 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
548 if (g_file_get_contents(filename, &text, &len, NULL))
549 {
550 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(priv->buffer), text, len);
551 g_free(text);
552 }
553 else
554 dict_show_msgbox(priv->dd, GTK_MESSAGE_ERROR,
555 _("The file '%s' could not be loaded."), filename);
556
557 g_free(filename);
558 }
559 gtk_widget_destroy(dialog);
560 }
561
562
sr_clear_clicked_cb(GtkButton * button,GtkTextBuffer * buffer)563 static void sr_clear_clicked_cb(GtkButton *button, GtkTextBuffer *buffer)
564 {
565 gtk_text_buffer_set_text(buffer, "", 0);
566 }
567
568
sr_paste_clicked_cb(GtkButton * button,GtkTextBuffer * buffer)569 static void sr_paste_clicked_cb(GtkButton *button, GtkTextBuffer *buffer)
570 {
571 GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
572 gtk_text_buffer_set_text(buffer, "", 0);
573 gtk_text_buffer_paste_clipboard(buffer, clipboard, NULL, TRUE);
574 }
575
576
sr_spin_grouping_changed_cb(GtkSpinButton * button,GtkLabel * label)577 static void sr_spin_grouping_changed_cb(GtkSpinButton *button, GtkLabel *label)
578 {
579 gint count = gtk_spin_button_get_value_as_int(button);
580 gchar *text = g_strdup_printf(ngettext(
581 "(display %d word at a time)",
582 "(display %d words at a time)", count),
583 count);
584
585 gtk_label_set_text(label, text);
586 g_free(text);
587 }
588
589
xfd_speed_reader_init(XfdSpeedReader * dialog)590 static void xfd_speed_reader_init(XfdSpeedReader *dialog)
591 {
592 GtkWidget *label_intro, *label_words, *label_font, *label_grouping, *label_grouping_desc;
593 GtkWidget *vbox, *hbox_words, *hbox_font, *hbox_grouping, *swin, *textview;
594 GtkWidget *vbox_text_buttons, *hbox_text, *button_clear, *button_paste, *button_open, *button_close;
595 GtkSizeGroup *sizegroup;
596 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(dialog);
597
598 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
599 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 330);
600 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
601 gtk_widget_set_name(GTK_WIDGET(dialog), "Xfce4Dict");
602
603 /* First page */
604 label_intro = gtk_label_new(
605 _("This is an easy speed reading utility to help train you to read faster. "
606 "It does this by flashing words at a rapid rate on the screen."));
607 gtk_label_set_line_wrap (GTK_LABEL (label_intro), TRUE);
608 gtk_label_set_line_wrap ( GTK_LABEL (label_intro), TRUE);
609
610 label_words = gtk_label_new_with_mnemonic(_("_Words per Minute:"));
611 gtk_widget_set_halign(label_words, GTK_ALIGN_END);
612 gtk_widget_set_valign(label_words, GTK_ALIGN_CENTER);
613
614 priv->spin_wpm = gtk_spin_button_new_with_range(5.0, 10000.0, 5);
615 gtk_label_set_mnemonic_widget(GTK_LABEL(label_words), priv->spin_wpm);
616
617 priv->check_mark_paragraphs = gtk_check_button_new_with_mnemonic(_("_Mark Paragraphs"));
618
619 hbox_words = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
620 gtk_box_pack_start(GTK_BOX(hbox_words), label_words, FALSE, FALSE, 6);
621 gtk_box_pack_start(GTK_BOX(hbox_words), priv->spin_wpm, FALSE, FALSE, 6);
622 gtk_box_pack_start(GTK_BOX(hbox_words), priv->check_mark_paragraphs, FALSE, FALSE, 12);
623
624 label_grouping = gtk_label_new_with_mnemonic(_("Word _Grouping:"));
625 gtk_widget_set_halign(label_grouping, GTK_ALIGN_END);
626 gtk_widget_set_valign(label_grouping, GTK_ALIGN_CENTER);
627
628 label_grouping_desc = gtk_label_new(NULL);
629
630 priv->spin_grouping = gtk_spin_button_new_with_range(1.0, 100.0, 1);
631 gtk_label_set_mnemonic_widget(GTK_LABEL(label_grouping), priv->spin_grouping);
632 g_signal_connect(priv->spin_grouping, "value-changed",
633 G_CALLBACK(sr_spin_grouping_changed_cb), label_grouping_desc);
634 sr_spin_grouping_changed_cb(GTK_SPIN_BUTTON(priv->spin_grouping), GTK_LABEL(label_grouping_desc));
635
636 hbox_grouping = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
637 gtk_box_pack_start(GTK_BOX(hbox_grouping), label_grouping, FALSE, FALSE, 6);
638 gtk_box_pack_start(GTK_BOX(hbox_grouping), priv->spin_grouping, FALSE, FALSE, 6);
639 gtk_box_pack_start(GTK_BOX(hbox_grouping), label_grouping_desc, FALSE, FALSE, 6);
640
641 label_font = gtk_label_new_with_mnemonic(_("_Font Size:"));
642 gtk_widget_set_halign(label_font, GTK_ALIGN_END);
643 gtk_widget_set_valign(label_font, GTK_ALIGN_CENTER);
644
645 priv->button_font = gtk_font_button_new();
646 gtk_label_set_mnemonic_widget(GTK_LABEL(label_font), priv->button_font);
647
648 hbox_font = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
649 gtk_box_pack_start(GTK_BOX(hbox_font), label_font, FALSE, FALSE, 6);
650 gtk_box_pack_start(GTK_BOX(hbox_font), priv->button_font, FALSE, FALSE, 6);
651
652 sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
653 gtk_size_group_add_widget(sizegroup, label_words);
654 gtk_size_group_add_widget(sizegroup, label_grouping);
655 gtk_size_group_add_widget(sizegroup, label_font);
656 g_object_unref(G_OBJECT(sizegroup));
657
658 textview = gtk_text_view_new();
659 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD);
660 priv->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
661 gtk_text_buffer_set_text(priv->buffer,
662 _("Enter some text here you would like to read.\n\n"
663 "Be relaxed and make yourself comfortable, then "
664 "press Start to begin speed reading."),
665 -1);
666
667 swin = gtk_scrolled_window_new(NULL, NULL);
668 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin), GTK_SHADOW_IN);
669 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
670 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
671 gtk_container_add(GTK_CONTAINER(swin), textview);
672
673 button_open = gtk_button_new_from_icon_name("document-open", GTK_ICON_SIZE_MENU);
674 g_signal_connect(button_open, "clicked", G_CALLBACK(sr_open_clicked_cb), dialog);
675 gtk_widget_set_tooltip_text(button_open, _("Load the contents of a file"));
676
677 button_paste = gtk_button_new_from_icon_name("edit-paste", GTK_ICON_SIZE_MENU);
678 g_signal_connect(button_paste, "clicked", G_CALLBACK(sr_paste_clicked_cb), priv->buffer);
679 gtk_widget_set_tooltip_text(button_paste,
680 _("Clear the contents of the text field and paste the contents of the clipboard"));
681
682 button_clear = gtk_button_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU);
683 g_signal_connect(button_clear, "clicked", G_CALLBACK(sr_clear_clicked_cb), priv->buffer);
684 gtk_widget_set_tooltip_text(button_clear, _("Clear the contents of the text field"));
685
686 vbox_text_buttons = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
687 gtk_box_pack_start(GTK_BOX(vbox_text_buttons), button_open, FALSE, FALSE, 0);
688 gtk_box_pack_start(GTK_BOX(vbox_text_buttons), button_paste, FALSE, FALSE, 0);
689 gtk_box_pack_start(GTK_BOX(vbox_text_buttons), button_clear, FALSE, FALSE, 0);
690
691 hbox_text = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
692 gtk_box_pack_start(GTK_BOX(hbox_text), swin, TRUE, TRUE, 0);
693 gtk_box_pack_start(GTK_BOX(hbox_text), vbox_text_buttons, FALSE, FALSE, 3);
694
695 priv->button_pause = gtk_dialog_add_button(GTK_DIALOG(dialog), _("P_ause"), RESPONSE_PAUSE);
696 priv->button_start = gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Start"), RESPONSE_START);
697 priv->button_stop = gtk_dialog_add_button(GTK_DIALOG(dialog), _("S_top"), RESPONSE_STOP);
698 button_close = gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Close"), GTK_RESPONSE_CLOSE);
699
700 gtk_widget_hide(priv->button_pause);
701 gtk_widget_hide(priv->button_stop);
702
703 gtk_button_set_image(GTK_BUTTON(priv->button_pause),
704 gtk_image_new_from_icon_name("media-playback-pause", GTK_ICON_SIZE_MENU));
705 gtk_button_set_image(GTK_BUTTON(priv->button_start),
706 gtk_image_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_MENU));
707 gtk_button_set_image(GTK_BUTTON(priv->button_stop),
708 gtk_image_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_MENU));
709 gtk_button_set_image(GTK_BUTTON(button_close),
710 gtk_image_new_from_icon_name("window-close", GTK_ICON_SIZE_MENU));
711
712 g_signal_connect(dialog, "response", G_CALLBACK(xfd_speed_reader_response_cb), NULL);
713
714 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
715 gtk_box_pack_start(GTK_BOX(vbox), label_intro, FALSE, FALSE, 5);
716 gtk_box_pack_start(GTK_BOX(vbox), hbox_words, FALSE, FALSE, 3);
717 gtk_box_pack_start(GTK_BOX(vbox), hbox_grouping, FALSE, FALSE, 3);
718 gtk_box_pack_start(GTK_BOX(vbox), hbox_font, FALSE, FALSE, 3);
719 gtk_box_pack_start(GTK_BOX(vbox), hbox_text, TRUE, TRUE, 0);
720
721 priv->first_page = vbox;
722
723 /* Second page */
724 priv->display_label = gtk_label_new(NULL);
725 gtk_widget_show(priv->display_label);
726
727 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
728 gtk_box_pack_start(GTK_BOX(vbox), priv->display_label, TRUE, TRUE, 6);
729
730 priv->second_page = vbox;
731
732 gtk_widget_show_all(priv->first_page);
733
734 gtk_widget_grab_focus(textview);
735
736 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), priv->first_page, TRUE, TRUE, 6);
737 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), priv->second_page, TRUE, TRUE, 6);
738
739 xfd_speed_reader_set_window_title(dialog, XSR_STATE_INITIAL);
740 }
741
742
xfd_speed_reader_new(GtkWindow * parent,DictData * dd)743 GtkWidget *xfd_speed_reader_new(GtkWindow *parent, DictData *dd)
744 {
745 GtkWidget *dialog = g_object_new(XFD_SPEED_READER_TYPE, "transient-for", parent, NULL);
746 XfdSpeedReaderPrivate *priv = xfd_speed_reader_get_instance_private(XFD_SPEED_READER (dialog));
747
748 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->spin_wpm), dd->speedreader_wpm);
749 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->spin_grouping), dd->speedreader_grouping);
750 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(priv->button_font), dd->speedreader_font);
751 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->check_mark_paragraphs),
752 dd->speedreader_mark_paragraphs);
753
754 priv->dd = dd;
755
756 return dialog;
757 }
758