3 // Copyright 2010 Michael Cronenworth <mike@cchtml.com>
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.
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.
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.
25 #include <hildon/hildon.h>
26 #include <dbus/dbus.h>
27 #include <mce/mode-names.h>
28 #include <mce/dbus-names.h>
29 #define MCE_SIGNAL_MATCH "type='signal'," \
30 "interface='" MCE_SIGNAL_IF "'," \
31 "member='" MCE_DEVICE_ORIENTATION_SIG "'"
35 // Application data struct
36 typedef struct _AppData AppData;
38 GtkWindow *main_window;
39 osso_context_t *osso_context;
40 DBusConnection *system_bus;
43 typedef struct timerData {
47 GtkWidget *labelMinute;
48 GtkWidget *labelSecond;
51 static AppData appdata;
52 static struct timerData timerdata;
53 static GtkWidget *timerHistoryLabel1 = NULL;
54 static GtkWidget *timerHistoryLabel2 = NULL;
55 static GtkWidget *timerHistoryLabel3 = NULL;
56 static GtkWidget *timerHistoryLabel4 = NULL;
57 static GSList *historyList = NULL;
58 static int stopishMode = STOPISH_MODE_START;
59 static int stopishOrientation = STOPISH_LANDSCAPE;
60 static int timerHandle = -1;
64 gint dbus_callback( const gchar *interface, const gchar *method,
65 GArray *arguments, gpointer data, osso_rpc_t *retval );
66 static GtkWindow *stopish_new( void );
67 static void main_menu( GtkWindow *window );
68 static void label_timer_landscape( void );
69 static void label_timer_portrait( void );
70 static gint timeout_cb( gpointer data );
71 static void start_cb( GtkButton* button, gpointer data );
72 static void reset_cb( GtkButton* button, gpointer data );
73 static void close_cb( GtkButton* button, gpointer data );
74 static gboolean focus_in_cb( GtkWidget *widget, GdkEventFocus *event,
76 static gboolean focus_out_cb( GtkWidget *widget, GdkEventFocus *event,
78 static void preference_timer_hour( GtkRadioButton* radio, GtkLabel *label );
79 static void preference_timer_minute( GtkRadioButton* radio, GtkLabel *label );
80 static void accelerometer_enable( void );
81 static void accelerometer_disable( void );
82 static DBusHandlerResult mce_filter_func( DBusConnection * connection,
83 DBusMessage * message,
87 int main( int argc, char *argv[] )
90 HildonProgram *program;
92 appdata.osso_context = osso_initialize( "com.nokia.stopish",
93 PACKAGE_VERSION, TRUE, NULL );
94 if ( appdata.osso_context == NULL ) {
95 fprintf( stderr, "osso_initialize failed.\n" );
99 // initialize Hildonized GTK libraries
100 hildon_gtk_init( &argc, &argv );
101 program = hildon_program_get_instance( );
103 // create main window
104 appdata.main_window = stopish_new( );
105 hildon_program_add_window( program, HILDON_WINDOW( appdata.main_window ) );
107 // Connect to session bus, add a match rule, install filter callback
108 appdata.system_bus = osso_get_sys_dbus_connection( appdata.osso_context );
109 if ( appdata.system_bus ) {
110 dbus_bus_add_match( appdata.system_bus, MCE_SIGNAL_MATCH, NULL );
111 dbus_connection_add_filter( appdata.system_bus,
116 g_printerr( "ERROR: Cannot connect to system dbus.\n" );
118 ret = osso_rpc_set_default_cb_f( appdata.osso_context,
119 dbus_callback, appdata.main_window );
120 if ( ret != OSSO_OK ) {
121 fprintf( stderr, "osso_rpc_set_default_cb_f failed: %d.\n", ret );
131 gint dbus_callback( const gchar *interface, const gchar *method,
132 GArray *arguments, gpointer data, osso_rpc_t *retval )
134 //printf( "stopish dbus: %s, %s\n", interface, method );
136 if ( !strcmp( method, "top_application" ) )
137 gtk_window_present( GTK_WINDOW( data ) );
139 retval->type = DBUS_TYPE_INVALID;
145 int stopish_get_mode( void )
151 static GtkWindow *stopish_new( void )
153 GtkWidget *window, *button, *button0, *label;
154 GtkWidget *vBoxMain, *vBox0, *hBox0;
156 window = hildon_stackable_window_new( );
158 gtk_container_set_border_width( GTK_CONTAINER( window ), 20 );
160 gtk_window_set_title( GTK_WINDOW( window ), "Stopish" );
162 // attach signals to main window
163 g_signal_connect( G_OBJECT( window ), "destroy",
164 G_CALLBACK( close_cb ), window );
165 g_signal_connect( G_OBJECT( window ), "focus-in-event",
166 G_CALLBACK( focus_in_cb ), NULL );
167 g_signal_connect( G_OBJECT( window ), "focus-out-event",
168 G_CALLBACK( focus_out_cb ), NULL );
173 vBoxMain = gtk_vbox_new( FALSE, 10 );
176 label = gtk_label_new( NULL );
177 gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
179 vBox0 = gtk_vbox_new( FALSE, 5 );
182 timerdata.vBox = gtk_vbox_new( FALSE, 0 );
183 gtk_container_add( GTK_CONTAINER( vBox0 ), timerdata.vBox );
184 label_timer_landscape( );
187 timerHistoryLabel1 = gtk_label_new( NULL );
188 gtk_label_set_markup( GTK_LABEL( timerHistoryLabel1 ),
189 "<span size=\"large\"> </span>" );
190 gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel1, FALSE, FALSE, 0 );
191 timerHistoryLabel2 = gtk_label_new( NULL );
192 gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel2, FALSE, FALSE, 0 );
193 timerHistoryLabel3 = gtk_label_new( NULL );
194 gtk_label_set_markup( GTK_LABEL( timerHistoryLabel3 ),
195 "<span size=\"small\"> </span>" );
196 gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel3, FALSE, FALSE, 0 );
197 timerHistoryLabel4 = gtk_label_new( NULL );
198 gtk_label_set_markup( GTK_LABEL( timerHistoryLabel4 ),
199 "<span size=\"x-small\"> </span>" );
200 gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel4, FALSE, FALSE, 0 );
202 gtk_container_add( GTK_CONTAINER( vBoxMain ), vBox0 );
205 label = gtk_label_new( NULL );
206 gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
209 hBox0 = gtk_hbox_new( FALSE, 15 );
210 gtk_widget_set_size_request( hBox0, -1, 80 );
212 // start/pause stopwatch button
213 button = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
214 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
216 button0 = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
217 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
219 g_signal_connect( G_OBJECT( button ), "clicked",
220 G_CALLBACK( start_cb ), button0 );
221 gtk_container_add( GTK_CONTAINER( hBox0 ), button );
224 gtk_widget_set_sensitive( button0, FALSE );
225 g_signal_connect( G_OBJECT( button0 ), "clicked",
226 G_CALLBACK( reset_cb ), button );
227 gtk_container_add( GTK_CONTAINER( hBox0 ), button0 );
229 gtk_box_pack_start( GTK_BOX( vBoxMain ), hBox0, FALSE, FALSE, 0 );
231 gtk_container_add( GTK_CONTAINER( window ), vBoxMain );
233 gtk_widget_show_all( window );
235 return GTK_WINDOW( window );
239 static void main_menu( GtkWindow *window )
241 GtkWidget *menu, *radio;
243 menu = hildon_app_menu_new( );
246 radio = gtk_radio_button_new_with_label( NULL, "Hour" );
247 gtk_toggle_button_set_mode( GTK_TOGGLE_BUTTON( radio ), FALSE );
248 g_signal_connect_after( radio, "clicked",
249 G_CALLBACK( preference_timer_hour ), NULL );
250 hildon_app_menu_add_filter( menu, GTK_BUTTON( radio ) );
253 radio = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON( radio ), "Minute" );
254 gtk_toggle_button_set_mode( GTK_TOGGLE_BUTTON ( radio ), FALSE );
255 g_signal_connect_after( radio, "clicked",
256 G_CALLBACK( preference_timer_minute ), NULL );
257 hildon_app_menu_add_filter( menu, GTK_BUTTON( radio ) );
260 gtk_toggle_button_set_active( radio, TRUE );
262 gtk_widget_show_all( menu );
264 hildon_window_set_app_menu( HILDON_WINDOW( window ), menu );
268 static void label_timer_landscape( void )
270 gtk_widget_set_size_request( timerdata.vBox, 800, -1 );
272 gtk_widget_destroy( timerdata.label );
273 timerdata.label = gtk_label_new( NULL );
274 if ( stopish_timer_get_precision( ) == TIMER_PRECISION_MINUTE )
275 gtk_label_set_markup( GTK_LABEL( timerdata.label ),
276 "<span font_family=\"monospace\" "
277 "size=\"70000\" weight=\"ultrabold\">"
280 gtk_label_set_markup( GTK_LABEL( timerdata.label ),
281 "<span font_family=\"monospace\" "
282 "size=\"70000\" weight=\"ultrabold\">"
283 "00:00:00.0</span>" );
284 gtk_misc_set_alignment( GTK_MISC( timerdata.label ), 0.5f, 0.5f );
285 gtk_container_add( GTK_CONTAINER( timerdata.vBox ), timerdata.label );
286 gtk_widget_show( timerdata.label );
290 static void label_timer_portrait( void )
292 GtkWidget *vBox, *hBox, *label;
294 gtk_widget_set_size_request( timerdata.vBox, 480, -1 );
296 gtk_widget_destroy( timerdata.label );
297 vBox = gtk_vbox_new( FALSE, 10 );
299 if ( stopish_timer_get_precision( ) == TIMER_PRECISION_HOUR ) {
300 hBox = gtk_hbox_new( FALSE, 10 );
301 label = gtk_label_new( "Hours" );
302 gtk_widget_set_size_request( label, 100, -1 );
303 gtk_misc_set_alignment( GTK_MISC( label ), 1.0f, 0.5f );
304 gtk_container_add( GTK_CONTAINER( hBox ), label );
305 timerdata.labelHour = gtk_label_new( NULL );
306 gtk_widget_set_size_request( timerdata.labelHour, 350, -1 );
307 gtk_misc_set_alignment( GTK_MISC( timerdata.labelHour ), 0.0f, 0.5f );
308 gtk_label_set_markup( GTK_LABEL( timerdata.labelHour ),
309 "<span font_family=\"monospace\" "
310 "size=\"80000\" weight=\"ultrabold\">"
312 gtk_container_add( GTK_CONTAINER( hBox ), timerdata.labelHour );
313 gtk_container_add( GTK_CONTAINER( vBox ), hBox );
316 hBox = gtk_hbox_new( FALSE, 10 );
317 label = gtk_label_new( "Minutes" );
318 gtk_widget_set_size_request( label, 100, -1 );
319 gtk_misc_set_alignment( GTK_MISC( label ), 1.0f, 0.5f );
320 gtk_container_add( GTK_CONTAINER( hBox ), label );
321 timerdata.labelMinute = gtk_label_new( NULL );
322 gtk_widget_set_size_request( timerdata.labelMinute, 350, -1 );
323 gtk_misc_set_alignment( GTK_MISC( timerdata.labelMinute ), 0.0f, 0.5f );
324 gtk_label_set_markup( GTK_LABEL( timerdata.labelMinute ),
325 "<span font_family=\"monospace\" "
326 "size=\"80000\" weight=\"ultrabold\">"
328 gtk_container_add( GTK_CONTAINER( hBox ), timerdata.labelMinute );
329 gtk_container_add( GTK_CONTAINER( vBox ), hBox );
331 hBox = gtk_hbox_new( FALSE, 10 );
332 label = gtk_label_new( "Seconds" );
333 gtk_widget_set_size_request( label, 100, -1 );
334 gtk_misc_set_alignment( GTK_MISC( label ), 1.0f, 0.5f );
335 gtk_container_add( GTK_CONTAINER( hBox ), label );
336 timerdata.labelSecond = gtk_label_new( NULL );
337 gtk_widget_set_size_request( timerdata.labelSecond, 350, -1 );
338 gtk_misc_set_alignment( GTK_MISC( timerdata.labelSecond ), 0.0f, 0.5f );
339 gtk_label_set_markup( GTK_LABEL( timerdata.labelSecond ),
340 "<span font_family=\"monospace\" "
341 "size=\"80000\" weight=\"ultrabold\">"
343 gtk_container_add( GTK_CONTAINER( hBox ), timerdata.labelSecond );
344 gtk_container_add( GTK_CONTAINER( vBox ), hBox );
346 timerdata.label = vBox;
347 gtk_container_add( GTK_CONTAINER( timerdata.vBox ), vBox );
348 gtk_widget_show_all( vBox );
355 static gint timeout_cb( gpointer data )
357 char formatBuffer[128], tempBuffer[8];
361 tempString = stopish_get_time_string( );
362 if ( stopishOrientation == STOPISH_LANDSCAPE ) {
363 sprintf( formatBuffer, "<span font_family=\"monospace\" "
364 "size=\"70000\" weight=\"ultrabold\">"
365 "%s</span>", tempString );
366 gtk_label_set_markup( GTK_LABEL( timerdata.label ), formatBuffer );
369 if ( stopish_timer_get_precision( ) == TIMER_PRECISION_HOUR ) {
370 sprintf( tempBuffer, "%.2s", tempString );
371 sprintf( formatBuffer, "<span font_family=\"monospace\" "
372 "size=\"80000\" weight=\"ultrabold\">"
373 "%s</span>", tempBuffer );
374 gtk_label_set_markup( GTK_LABEL( timerdata.labelHour ),
377 if ( stopish_timer_get_precision( ) == TIMER_PRECISION_HOUR )
378 sprintf( tempBuffer, "%.2s", tempString + 3 );
380 sprintf( tempBuffer, "%.2s", tempString );
381 sprintf( formatBuffer, "<span font_family=\"monospace\" "
382 "size=\"80000\" weight=\"ultrabold\">"
383 "%s</span>", tempBuffer );
384 gtk_label_set_markup( GTK_LABEL( timerdata.labelMinute ),
386 if ( stopish_timer_get_precision( ) == TIMER_PRECISION_HOUR )
387 sprintf( tempBuffer, "%.4s", tempString + 6 );
389 sprintf( tempBuffer, "%.4s", tempString + 3 );
390 sprintf( formatBuffer, "<span font_family=\"monospace\" "
391 "size=\"80000\" weight=\"ultrabold\">"
392 "%s</span>", tempBuffer );
393 gtk_label_set_markup( GTK_LABEL( timerdata.labelSecond ),
402 static void start_cb( GtkButton* button, gpointer data )
404 if ( stopishMode == STOPISH_MODE_START ) {
405 // set label text and add timer handle
406 gtk_button_set_label( button, "Pause" );
407 stopishMode = STOPISH_MODE_PAUSE;
408 stopish_set_time_start( stopish_current_time( ) );
409 timerHandle = g_timeout_add( 100, timeout_cb, NULL );
411 else if ( stopishMode == STOPISH_MODE_RESUME ) {
413 gtk_button_set_label( button, "Pause" );
414 stopishMode = STOPISH_MODE_PAUSE;
415 stopish_timer_resume( );
416 timerHandle = g_timeout_add( 100, timeout_cb, NULL );
419 // pause timer, remove timeout
420 gtk_button_set_label( button, "Resume" );
421 stopishMode = STOPISH_MODE_RESUME;
422 g_source_remove( timerHandle );
423 stopish_timer_save( );
426 // allow user to reset timer
427 gtk_widget_set_sensitive( GTK_WIDGET( data ), TRUE );
431 static void reset_cb( GtkButton* button, gpointer data )
435 char formatString[128];
437 if ( stopishMode == STOPISH_MODE_RESUME )
438 stopish_timer_resume( );
440 // set label text and remove timer handle
441 gtk_button_set_label( GTK_BUTTON( data ), "Start" );
442 stopishMode = STOPISH_MODE_START;
443 if ( stopish_timer_get_precision( ) == TIMER_PRECISION_MINUTE )
444 gtk_label_set_markup( GTK_LABEL( timerdata.label ),
445 "<span font_family=\"monospace\" "
446 "size=\"70000\" weight=\"ultrabold\">"
449 gtk_label_set_markup( GTK_LABEL( timerdata.label ),
450 "<span font_family=\"monospace\" "
451 "size=\"70000\" weight=\"ultrabold\">"
452 "00:00:00.0</span>" );
453 g_source_remove( timerHandle );
455 // add current time to history
456 historyList = g_slist_prepend( historyList,
457 ( gpointer ) stopish_get_time_string( ) );
458 sprintf( formatString, "<span size=\"large\">%s</span>",
459 ( char * ) historyList->data );
460 gtk_label_set_markup( GTK_LABEL( timerHistoryLabel1 ),
462 tempList = historyList;
463 tempList = g_slist_next( tempList );
465 gtk_label_set_text( GTK_LABEL( timerHistoryLabel2 ),
466 ( char * ) tempList->data );
468 tempList = g_slist_next( tempList );
470 sprintf( formatString, "<span size=\"small\">%s</span>",
471 ( char * ) tempList->data );
472 gtk_label_set_markup( GTK_LABEL( timerHistoryLabel3 ),
475 tempList = g_slist_next( tempList );
477 sprintf( formatString, "<span size=\"x-small\">%s</span>",
478 ( char * ) tempList->data );
479 gtk_label_set_markup( GTK_LABEL( timerHistoryLabel4 ),
483 // remove the history time after the 4th
484 tempList = g_slist_next( tempList );
486 tempString = tempList->data;
487 historyList = g_slist_remove( historyList, tempList->data );
492 stopish_set_time_start( 0 );
494 // disallow user to reset timer
495 gtk_widget_set_sensitive( GTK_WIDGET( button ), FALSE );
499 static void close_cb( GtkButton* button, gpointer data )
501 // disable accelerometer for battery savings
502 accelerometer_disable( );
504 // destroy main window and exit gtk main loop
505 gtk_widget_destroy( GTK_WIDGET( data ) );
510 static gboolean focus_in_cb( GtkWidget *widget, GdkEventFocus *event,
513 // enable accelerometer hardware for portrait mode support
514 accelerometer_enable( );
520 static gboolean focus_out_cb( GtkWidget *widget, GdkEventFocus *event,
523 // disable accelerometer for battery savings
524 accelerometer_disable( );
530 static void preference_timer_hour( GtkRadioButton* radio, GtkLabel *label )
532 stopish_timer_set_precision( TIMER_PRECISION_HOUR );
533 if ( stopishOrientation == STOPISH_LANDSCAPE )
534 label_timer_landscape( );
536 label_timer_portrait( );
540 static void preference_timer_minute( GtkRadioButton* radio, GtkLabel *label )
542 stopish_timer_set_precision( TIMER_PRECISION_MINUTE );
543 if ( stopishOrientation == STOPISH_LANDSCAPE )
544 label_timer_landscape( );
546 label_timer_portrait( );
550 static void accelerometer_enable( void )
552 if ( osso_rpc_run_system( appdata.osso_context, MCE_SERVICE,
553 MCE_REQUEST_PATH, MCE_REQUEST_IF,
554 "req_accelerometer_enable", NULL,
555 DBUS_TYPE_INVALID ) != OSSO_OK ) {
556 g_printerr("WARN: Cannot enable accelerometers\n");
561 static void accelerometer_disable( void )
563 if ( osso_rpc_run_system( appdata.osso_context, MCE_SERVICE,
564 MCE_REQUEST_PATH, MCE_REQUEST_IF,
565 "req_accelerometer_disable", NULL,
566 DBUS_TYPE_INVALID ) != OSSO_OK ) {
567 g_printerr("WARN: Cannot disable accelerometers\n");
572 static DBusHandlerResult mce_filter_func( DBusConnection * connection,
573 DBusMessage * message,
576 DBusMessageIter iter;
577 char *rotation = NULL;
579 if ( dbus_message_is_signal( message, MCE_SIGNAL_IF,
580 MCE_DEVICE_ORIENTATION_SIG ) ) {
581 // here if we received an orientation dbus signal
582 if ( dbus_message_iter_init( message, &iter ) ) {
583 dbus_message_iter_get_basic( &iter, &rotation );
585 // Rotate main window
586 if ( !strcmp( rotation, MCE_ORIENTATION_PORTRAIT ) ) {
587 hildon_gtk_window_set_portrait_flags( GTK_WINDOW( appdata.main_window ),
588 HILDON_PORTRAIT_MODE_REQUEST );
589 label_timer_portrait( );
590 stopishOrientation = STOPISH_PORTRAIT;
593 hildon_gtk_window_set_portrait_flags( GTK_WINDOW( appdata.main_window ),
594 ~HILDON_PORTRAIT_MODE_REQUEST );
595 label_timer_landscape( );
596 stopishOrientation = STOPISH_LANDSCAPE;
600 g_printerr( "ERROR: dbus_message_iter_init() failed.\n" );
603 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;