Prep for 0.9.3 release.
[stopish] / src / stopish-countdown.c
1 //      stopish-countdown.c
2 //
3 //      Copyright 2010 Michael Cronenworth <mike@cchtml.com>
4 //
5 //      This program is free software; you can redistribute it and/or modify
6 //      it under the terms of the GNU General Public License as published by
7 //      the Free Software Foundation; either version 2 of the License, or
8 //      (at your option) any later version.
9 //
10 //      This program is distributed in the hope that it will be useful,
11 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //      GNU General Public License for more details.
14 //
15 //      You should have received a copy of the GNU General Public License
16 //      along with this program; if not, write to the Free Software
17 //      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 //      MA 02110-1301, USA.
19
20 #include <gtk/gtk.h>
21 #include <hildon/hildon.h>
22 #include <stdlib.h>
23
24 #include "stopish.h"
25
26 struct timerData {
27     GtkWidget *vBox;
28     GtkWidget *label;
29     GtkWidget *labelHour;
30     GtkWidget *labelMinute;
31     GtkWidget *labelSecond;
32 };
33
34 static struct timerData timerdata;
35 static int timerHandle = -1;
36 static GtkWidget *window = NULL;
37 static GtkWidget *resetButton;
38 static int startMinutes = 0;
39 static int startSeconds = 0;
40
41 //Prototypes
42 static void main_menu( GtkWindow *window );
43 static void close_cb( void );
44 static void countdown_perf_timer_hour( GtkRadioButton* radio, GtkLabel *label );
45 static void countdown_perf_timer_minute( GtkRadioButton* radio, GtkLabel *label );
46 static gint timeout_cb( gpointer data );
47 static void start_cb( GtkButton* button, gpointer data );
48 static void reset_cb( GtkButton* button, gpointer data );
49 static void set_cb( GtkButton* button, gpointer data );
50
51
52 void stopish_countdown_new( void )
53 {
54     GtkWidget *button, *label;
55     GtkWidget *vBoxMain, *vBox0, *hBox0;
56
57     window = hildon_stackable_window_new(  );
58
59     gtk_container_set_border_width( GTK_CONTAINER( window ), 20 );
60
61     gtk_window_set_title( GTK_WINDOW( window ), "Stopish" );
62
63     // attach signals to main window
64     g_signal_connect( G_OBJECT( window ), "destroy",
65                       G_CALLBACK( close_cb ), NULL );
66     g_signal_connect( G_OBJECT( window ), "focus-in-event",
67                       G_CALLBACK( stopish_focus_in_cb ), NULL );
68     g_signal_connect( G_OBJECT( window ), "focus-out-event",
69                       G_CALLBACK( stopish_focus_out_cb ), NULL );
70
71     // initialize menu
72     main_menu( GTK_WINDOW( window ) );
73
74     vBoxMain = gtk_vbox_new( FALSE, 10 );
75
76     // separator
77     label = gtk_label_new( NULL );
78     gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
79
80     vBox0 = gtk_vbox_new( FALSE, 5 );
81
82     // countdown area
83     timerdata.vBox = gtk_vbox_new( FALSE, 0 );
84     gtk_container_add( GTK_CONTAINER( vBox0 ), timerdata.vBox );
85     gtk_container_add( GTK_CONTAINER( vBoxMain ), vBox0 );
86     if ( stopish_get_orientation(  ) == STOPISH_LANDSCAPE )
87         stopish_countdown_label_timer_landscape(  );
88     else
89         stopish_countdown_label_timer_portrait(  );
90
91     // separator
92     label = gtk_label_new( NULL );
93     gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
94
95     // button area
96     hBox0 = gtk_hbox_new( FALSE, 15 );
97     gtk_widget_set_size_request( hBox0, -1, 80 );
98
99     // start/pause countdown button
100     button = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
101                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
102                                           "Start", NULL );
103     resetButton = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
104                                                HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
105                                               "Reset", NULL );
106     g_signal_connect( G_OBJECT( button ), "clicked",
107                       G_CALLBACK( start_cb ), resetButton );
108     gtk_container_add( GTK_CONTAINER( hBox0 ), button );
109
110     // reset button
111     gtk_widget_set_sensitive( resetButton, FALSE );
112     g_signal_connect( G_OBJECT( resetButton ), "clicked",
113                       G_CALLBACK( reset_cb ), button );
114     gtk_container_add( GTK_CONTAINER( hBox0 ), resetButton );
115
116     // set button
117     button = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
118                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
119                                           "Set", NULL );
120     g_signal_connect( G_OBJECT( button ), "clicked",
121                       G_CALLBACK( set_cb ), button );
122     gtk_container_add( GTK_CONTAINER( hBox0 ), button );
123
124
125     gtk_box_pack_start( GTK_BOX( vBoxMain ), hBox0, FALSE, FALSE, 0 );
126     gtk_container_add( GTK_CONTAINER( window ), vBoxMain );
127
128     gtk_widget_show_all( window );
129 }
130
131
132 void stopish_countdown_label_timer_landscape( void )
133 {
134     gtk_widget_set_size_request( timerdata.vBox, 800, -1 );
135
136     gtk_widget_destroy( timerdata.label );
137     timerdata.label = gtk_label_new( NULL );
138     if ( stopish_timer_get_precision(  ) == TIMER_PRECISION_MINUTE )
139         gtk_label_set_markup( GTK_LABEL( timerdata.label ),
140                               "<span font_family=\"monospace\" "
141                               "size=\"80000\" weight=\"ultrabold\">"
142                               "00:00.0</span>" );
143     else
144         gtk_label_set_markup( GTK_LABEL( timerdata.label ),
145                               "<span font_family=\"monospace\" "
146                               "size=\"80000\" weight=\"ultrabold\">"
147                               "00:00:00.0</span>" );
148     gtk_misc_set_alignment( GTK_MISC( timerdata.label ), 0.5f, 0.5f );
149     gtk_container_add( GTK_CONTAINER( timerdata.vBox ), timerdata.label );
150     gtk_widget_show( timerdata.label );
151 }
152
153
154 void stopish_countdown_label_timer_portrait( void )
155 {
156     GtkWidget *vBox, *hBox, *label;
157
158     gtk_widget_set_size_request( timerdata.vBox, 480, -1 );
159
160     gtk_widget_destroy( timerdata.label );
161     vBox = gtk_vbox_new( FALSE, 10 );
162
163     if ( stopish_timer_get_precision(  ) == TIMER_PRECISION_HOUR ) {
164         hBox = gtk_hbox_new( FALSE, 10 );
165         label = gtk_label_new( "Hours" );
166         gtk_widget_set_size_request( label, 100, -1 );
167         gtk_misc_set_alignment( GTK_MISC( label ), 1.0f, 0.5f );
168         gtk_container_add( GTK_CONTAINER( hBox ), label );
169         timerdata.labelHour = gtk_label_new( NULL );
170         gtk_widget_set_size_request( timerdata.labelHour, 350, -1 );
171         gtk_misc_set_alignment( GTK_MISC( timerdata.labelHour ), 0.0f, 0.5f );
172         gtk_label_set_markup( GTK_LABEL( timerdata.labelHour ),
173                               "<span font_family=\"monospace\" "
174                               "size=\"90000\" weight=\"ultrabold\">"
175                               "00</span>" );
176         gtk_container_add( GTK_CONTAINER( hBox ), timerdata.labelHour );
177         gtk_container_add( GTK_CONTAINER( vBox ), hBox );
178     }
179
180     hBox = gtk_hbox_new( FALSE, 10 );
181     label = gtk_label_new( "Minutes" );
182     gtk_widget_set_size_request( label, 100, -1 );
183     gtk_misc_set_alignment( GTK_MISC( label ), 1.0f, 0.5f );
184     gtk_container_add( GTK_CONTAINER( hBox ), label );
185     timerdata.labelMinute = gtk_label_new( NULL );
186     gtk_widget_set_size_request( timerdata.labelMinute, 350, -1 );
187     gtk_misc_set_alignment( GTK_MISC( timerdata.labelMinute ), 0.0f, 0.5f );
188     gtk_label_set_markup( GTK_LABEL( timerdata.labelMinute ),
189                           "<span font_family=\"monospace\" "
190                           "size=\"90000\" weight=\"ultrabold\">"
191                           "00</span>" );
192     gtk_container_add( GTK_CONTAINER( hBox ), timerdata.labelMinute );
193     gtk_container_add( GTK_CONTAINER( vBox ), hBox );
194
195     hBox = gtk_hbox_new( FALSE, 10 );
196     label = gtk_label_new( "Seconds" );
197     gtk_widget_set_size_request( label, 100, -1 );
198     gtk_misc_set_alignment( GTK_MISC( label ), 1.0f, 0.5f );
199     gtk_container_add( GTK_CONTAINER( hBox ), label );
200     timerdata.labelSecond = gtk_label_new( NULL );
201     gtk_widget_set_size_request( timerdata.labelSecond, 350, -1 );
202     gtk_misc_set_alignment( GTK_MISC( timerdata.labelSecond ), 0.0f, 0.5f );
203     gtk_label_set_markup( GTK_LABEL( timerdata.labelSecond ),
204                           "<span font_family=\"monospace\" "
205                           "size=\"90000\" weight=\"ultrabold\">"
206                           "00.0</span>" );
207     gtk_container_add( GTK_CONTAINER( hBox ), timerdata.labelSecond );
208     gtk_container_add( GTK_CONTAINER( vBox ), hBox );
209
210     timerdata.label = vBox;
211     gtk_container_add( GTK_CONTAINER( timerdata.vBox ), vBox );
212     gtk_widget_show_all( vBox );
213 }
214
215
216 static void main_menu( GtkWindow *window )
217 {
218     HildonAppMenu *menu;
219     GtkWidget *button, *radio;
220
221     menu = ( HildonAppMenu * ) hildon_app_menu_new(  );
222
223     button = gtk_button_new_with_label( "About" );
224     g_signal_connect_after( G_OBJECT( button ), "clicked",
225                             G_CALLBACK( stopish_about_cb ),
226                             STOPISH_TYPE_STOPWATCH );
227     hildon_app_menu_append( menu, GTK_BUTTON( button ) );
228
229     // Hour preference
230     radio = gtk_radio_button_new_with_label( NULL, "Hour" );
231     gtk_toggle_button_set_mode( GTK_TOGGLE_BUTTON( radio ), FALSE );
232     g_signal_connect_after( G_OBJECT( radio ), "clicked",
233                             G_CALLBACK( countdown_perf_timer_hour ), NULL );
234     hildon_app_menu_add_filter( menu, GTK_BUTTON( radio ) );
235
236     // Minute preference
237     radio = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON( radio ), "Minute" );
238     gtk_toggle_button_set_mode( GTK_TOGGLE_BUTTON ( radio ), FALSE );
239     g_signal_connect_after( G_OBJECT( radio ), "clicked",
240                             G_CALLBACK( countdown_perf_timer_minute ), NULL );
241     hildon_app_menu_add_filter( menu, GTK_BUTTON( radio ) );
242
243     // default to minute
244     gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( radio ), TRUE );
245
246     gtk_widget_show_all( GTK_WIDGET( menu ) );
247
248     hildon_window_set_app_menu( HILDON_WINDOW( window ), menu );
249 }
250
251
252 static void close_cb( void )
253 {
254     gtk_widget_destroy( window );
255     if ( timerHandle != -1 &&
256          stopish_get_mode(  ) != STOPISH_MODE_RESUME )
257         gtk_button_clicked( GTK_BUTTON( resetButton ) );
258     startMinutes = startSeconds = 0;
259     stopish_set_type( STOPISH_TYPE_STOPWATCH );
260     if ( stopish_get_orientation(  ) == STOPISH_LANDSCAPE )
261         stopish_stopwatch_label_timer_landscape(  );
262     else
263         stopish_stopwatch_label_timer_portrait(  );
264 }
265
266
267 static void countdown_perf_timer_hour( GtkRadioButton* radio, GtkLabel *label )
268 {
269     stopish_timer_set_precision( TIMER_PRECISION_HOUR );
270     if ( stopish_get_orientation(  ) == STOPISH_LANDSCAPE )
271         stopish_countdown_label_timer_landscape(  );
272     else
273         stopish_countdown_label_timer_portrait(  );
274 }
275
276
277 static void countdown_perf_timer_minute( GtkRadioButton* radio, GtkLabel *label )
278 {
279     stopish_timer_set_precision( TIMER_PRECISION_MINUTE );
280     if ( stopish_get_orientation(  ) == STOPISH_LANDSCAPE )
281         stopish_countdown_label_timer_landscape(  );
282     else
283         stopish_countdown_label_timer_portrait(  );
284 }
285
286
287 //
288 // Timer callback
289 //
290 static gint timeout_cb( gpointer data )
291 {
292     char formatBuffer[128], tempBuffer[8];
293     char *tempString;
294
295     // print to screen
296     tempString = stopish_get_time_string(  );
297     if ( tempString == NULL ) {
298         gtk_button_clicked( GTK_BUTTON( resetButton ) );
299         return FALSE;
300     }
301     if ( stopish_get_orientation(  ) == STOPISH_LANDSCAPE ) {
302         sprintf( formatBuffer, "<span font_family=\"monospace\" "
303                                "size=\"80000\" weight=\"ultrabold\">"
304                                "%s</span>", tempString );
305         gtk_label_set_markup( GTK_LABEL( timerdata.label ), formatBuffer );
306     }
307     else {
308         if ( stopish_timer_get_precision(  ) == TIMER_PRECISION_HOUR ) {
309             sprintf( tempBuffer, "%.2s", tempString );
310             sprintf( formatBuffer, "<span font_family=\"monospace\" "
311                                    "size=\"90000\" weight=\"ultrabold\">"
312                                    "%s</span>", tempBuffer );
313             gtk_label_set_markup( GTK_LABEL( timerdata.labelHour ),
314                                   formatBuffer );
315         }
316         if ( stopish_timer_get_precision(  ) == TIMER_PRECISION_HOUR )
317             sprintf( tempBuffer, "%.2s", tempString + 3 );
318         else
319             sprintf( tempBuffer, "%.2s", tempString );
320         sprintf( formatBuffer, "<span font_family=\"monospace\" "
321                                "size=\"90000\" weight=\"ultrabold\">"
322                                "%s</span>", tempBuffer );
323         gtk_label_set_markup( GTK_LABEL( timerdata.labelMinute ),
324                               formatBuffer );
325         if ( stopish_timer_get_precision(  ) == TIMER_PRECISION_HOUR )
326             sprintf( tempBuffer, "%.4s", tempString + 6 );
327         else
328             sprintf( tempBuffer, "%.4s", tempString + 3 );
329         sprintf( formatBuffer, "<span font_family=\"monospace\" "
330                                "size=\"90000\" weight=\"ultrabold\">"
331                                "%s</span>", tempBuffer );
332         gtk_label_set_markup( GTK_LABEL( timerdata.labelSecond ),
333                               formatBuffer );
334     }
335     free( tempString );
336
337     return TRUE;
338 }
339
340
341 static void start_cb( GtkButton* button, gpointer data )
342 {
343     long int offset;
344
345     if ( stopish_get_mode(  ) == STOPISH_MODE_START ) {
346         // set label text and add timer handle
347         offset = ( startMinutes * 600 ) + ( startSeconds * 10 );
348         if ( offset <= 0 )
349             return;
350         gtk_button_set_label( button, "Pause" );
351         stopish_set_mode( STOPISH_MODE_PAUSE );
352         stopish_set_time_start( stopish_current_time(  ) + offset );
353         timerHandle = g_timeout_add( 100, timeout_cb, NULL );
354     }
355     else if ( stopish_get_mode(  ) == STOPISH_MODE_RESUME ) {
356         // resume timer
357         gtk_button_set_label( button, "Pause" );
358         stopish_set_mode( STOPISH_MODE_PAUSE );
359         stopish_timer_resume(  );
360         timerHandle = g_timeout_add( 100, timeout_cb, NULL );
361     }
362     else {
363         // pause timer, remove timeout
364         gtk_button_set_label( button, "Resume" );
365         stopish_set_mode( STOPISH_MODE_RESUME );
366         g_source_remove( timerHandle );
367         timerHandle = -1;
368         stopish_timer_save(  );
369     }
370
371     // allow user to reset timer
372     gtk_widget_set_sensitive( GTK_WIDGET( data ), TRUE );
373 }
374
375
376 static void reset_cb( GtkButton* button, gpointer data )
377 {
378     if ( stopish_get_mode(  ) == STOPISH_MODE_RESUME )
379         stopish_timer_resume(  );
380
381     // set label text and remove timer handle
382     gtk_button_set_label( GTK_BUTTON( data ), "Start" );
383     stopish_set_mode( STOPISH_MODE_START );
384     if ( stopish_get_orientation(  ) == STOPISH_LANDSCAPE )
385         stopish_countdown_label_timer_landscape(  );
386     else
387         stopish_countdown_label_timer_portrait(  );
388     g_source_remove( timerHandle );
389     timerHandle = -1;
390
391     // reset start time
392     stopish_set_time_start( 0 );
393
394     // disallow user to reset timer
395     gtk_widget_set_sensitive( GTK_WIDGET( button ), FALSE );
396 }
397
398
399 static void set_cb( GtkButton* button, gpointer data )
400 {
401     GtkWidget *picker;
402     GtkWidget *selector;
403     GtkListStore *minutes, *seconds;
404     GtkTreeIter iter;
405     HildonTouchSelectorColumn *column = NULL;
406     int i, ret;
407
408     // create popup window for setting start time
409     picker = hildon_picker_dialog_new( GTK_WINDOW( window ) );
410     gtk_window_set_title( GTK_WINDOW( picker ), "Set Timer Start" );
411
412     selector = hildon_touch_selector_new(  );
413
414     minutes = gtk_list_store_new( 1, G_TYPE_INT );
415     for ( i = 0; i < 60; ++i ) {
416         gtk_list_store_append( minutes, &iter );
417         gtk_list_store_set( minutes, &iter, 0, i, -1 );
418     }
419
420     seconds = gtk_list_store_new( 1, G_TYPE_INT );
421     for ( i = 0; i < 60; ++i ) {
422         gtk_list_store_append( seconds, &iter );
423         gtk_list_store_set( seconds, &iter, 0, i, -1 );
424     }
425
426     column = hildon_touch_selector_append_text_column( HILDON_TOUCH_SELECTOR( selector ),
427                                                   GTK_TREE_MODEL( minutes ),
428                                                   TRUE );
429     hildon_touch_selector_column_set_text_column( column, 0 );
430     g_object_unref( minutes );
431
432     column = hildon_touch_selector_append_text_column( HILDON_TOUCH_SELECTOR( selector ),
433                                                   GTK_TREE_MODEL( seconds ),
434                                                   TRUE );
435     hildon_touch_selector_column_set_text_column( column, 0 );
436     g_object_unref( seconds );
437
438     hildon_picker_dialog_set_selector( HILDON_PICKER_DIALOG( picker ),
439                                        HILDON_TOUCH_SELECTOR( selector ) );
440
441     ret = gtk_dialog_run( GTK_DIALOG( picker ) );
442
443     // evaluate dialog response
444     switch ( ret ) {
445         case GTK_RESPONSE_OK:
446             startMinutes =
447                 hildon_touch_selector_get_active( HILDON_TOUCH_SELECTOR( selector ),
448                                                   0 );
449             startSeconds =
450                 hildon_touch_selector_get_active( HILDON_TOUCH_SELECTOR( selector ),
451                                                   1 );
452             break;
453         default:
454             break;
455     }
456     gtk_widget_destroy( GTK_WIDGET( picker ) );
457 }