*Hildon-ize the app for better widget support.
[stopish] / src / stopish.c
1 //      stopish.c
2 //
3 //      Copyright 2009 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 <string.h>
21 #include <stdlib.h>
22 #include <sys/time.h>
23 #include <gtk/gtk.h>
24 #include <libosso.h>
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 "'"
32
33 #include "stopish.h"
34
35 // Application data struct
36 typedef struct _AppData AppData;
37 struct _AppData {
38     GtkWindow *main_window;
39     osso_context_t *osso_context;
40     DBusConnection *system_bus;
41 };
42
43 static AppData appdata;
44 static GtkWidget *timerLabel = NULL;
45 static GtkWidget *timerHistoryLabel1 = NULL;
46 static GtkWidget *timerHistoryLabel2 = NULL;
47 static GtkWidget *timerHistoryLabel3 = NULL;
48 static GtkWidget *timerHistoryLabel4 = NULL;
49 static GSList *historyList = NULL;
50 static int stopishMode = STOPISH_MODE_START;
51 static int timerHandle = -1;
52
53 //Prototypes
54 gint dbus_callback( const gchar *interface, const gchar *method,
55                         GArray *arguments, gpointer data, osso_rpc_t *retval );
56 static GtkWindow *stopish_new( void );
57 static void start_cb( GtkButton* button, gpointer data );
58 static void reset_cb( GtkButton* button, gpointer data );
59 static void close_cb( GtkButton* button, gpointer data );
60 static gboolean focus_in_cb( GtkWidget *widget, GdkEventFocus *event,
61                              gpointer data );
62 static gboolean focus_out_cb( GtkWidget *widget, GdkEventFocus *event,
63                               gpointer data );
64 static void accelerometer_enable( void );
65 static void accelerometer_disable( void );
66 static DBusHandlerResult mce_filter_func( DBusConnection * connection,
67                                           DBusMessage * message,
68                                           void *data );
69
70
71 int main( int argc, char *argv[] )
72 {
73     osso_return_t ret;
74     HildonProgram *program;
75
76     appdata.osso_context = osso_initialize( "com.nokia.stopish",
77                                             PACKAGE_VERSION, TRUE, NULL );
78     if ( appdata.osso_context == NULL ) {
79         fprintf( stderr, "osso_initialize failed.\n" );
80         exit( 1 );
81     }
82
83     // initialize Hildonized GTK libraries
84     hildon_gtk_init( &argc, &argv );
85     program = hildon_program_get_instance(  );
86
87     // create main window
88     appdata.main_window = stopish_new(  );
89     hildon_program_add_window( program, HILDON_WINDOW( appdata.main_window ) );
90
91     // Connect to session bus, add a match rule, install filter callback
92     appdata.system_bus = osso_get_sys_dbus_connection( appdata.osso_context );
93     if ( appdata.system_bus ) {
94         dbus_bus_add_match( appdata.system_bus, MCE_SIGNAL_MATCH, NULL );
95         dbus_connection_add_filter( appdata.system_bus,
96                                     mce_filter_func,
97                                     NULL, NULL );
98     }
99     else
100         g_printerr( "ERROR: Cannot connect to system dbus.\n" );
101
102     ret = osso_rpc_set_default_cb_f( appdata.osso_context,
103                                      dbus_callback, appdata.main_window );
104     if ( ret != OSSO_OK ) {
105         fprintf( stderr, "osso_rpc_set_default_cb_f failed: %d.\n", ret );
106         exit( 1 );
107     }
108
109     gtk_main(  );
110
111     return 0;
112 }
113
114
115 gint dbus_callback( const gchar *interface, const gchar *method,
116                     GArray *arguments, gpointer data, osso_rpc_t *retval )
117 {
118     //printf( "stopish dbus: %s, %s\n", interface, method );
119
120     if ( !strcmp( method, "top_application" ) )
121         gtk_window_present( GTK_WINDOW( data ) );
122
123     retval->type = DBUS_TYPE_INVALID;
124
125     return OSSO_OK;
126 }
127
128
129 int stopish_get_mode( void )
130 {
131     return stopishMode;
132 }
133
134
135 static GtkWindow *stopish_new( void )
136 {
137     GtkWidget *window, *button, *button0, *label;
138     GtkWidget *vBoxMain, *vBox0, *hBox0;
139
140     window = hildon_stackable_window_new(  );
141
142     gtk_container_set_border_width( GTK_CONTAINER( window ), 20 );
143
144     gtk_window_set_title( GTK_WINDOW( window ), "Stopish" );
145
146     // attach signals to main window
147     g_signal_connect( G_OBJECT( window ), "destroy",
148                       G_CALLBACK( close_cb ), window );
149     g_signal_connect( G_OBJECT( window ), "focus-in-event",
150                       G_CALLBACK( focus_in_cb ), NULL );
151     g_signal_connect( G_OBJECT( window ), "focus-out-event",
152                       G_CALLBACK( focus_out_cb ), NULL );
153
154     vBoxMain = gtk_vbox_new( FALSE, 10 );
155
156     // separator
157     label = gtk_label_new( NULL );
158     gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
159
160     // stop watch area
161     vBox0 = gtk_vbox_new( FALSE, 5 );
162
163     // main timer
164     timerLabel = gtk_label_new( NULL );
165     gtk_label_set_markup( GTK_LABEL( timerLabel ),
166                           "<span font_family=\"monospace\" "
167                           "size=\"70000\" weight=\"ultrabold\">"
168                           "00:00:00.0</span>" );
169     gtk_container_add( GTK_CONTAINER( vBox0 ), timerLabel );
170
171     // history area
172     timerHistoryLabel1 = gtk_label_new( NULL );
173     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel1 ),
174                           "<span size=\"large\"> </span>" );
175     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel1, FALSE, FALSE, 0 );
176     timerHistoryLabel2 = gtk_label_new( NULL );
177     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel2, FALSE, FALSE, 0 );
178     timerHistoryLabel3 = gtk_label_new( NULL );
179     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel3 ),
180                           "<span size=\"small\"> </span>" );
181     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel3, FALSE, FALSE, 0 );
182     timerHistoryLabel4 = gtk_label_new( NULL );
183     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel4 ),
184                           "<span size=\"x-small\"> </span>" );
185     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel4, FALSE, FALSE, 0 );
186
187     gtk_container_add( GTK_CONTAINER( vBoxMain ), vBox0 );
188
189     // separator
190     label = gtk_label_new( NULL );
191     gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
192
193     // button area
194     hBox0 = gtk_hbox_new( FALSE, 15 );
195     gtk_widget_set_size_request( hBox0, -1, 80 );
196
197     // start/pause stopwatch button
198     button = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
199                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
200                                           "Start", NULL );
201     button0 = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
202                                            HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
203                                            "Reset", NULL );
204     g_signal_connect( G_OBJECT( button ), "clicked",
205                       G_CALLBACK( start_cb ), button0 );
206     gtk_container_add( GTK_CONTAINER( hBox0 ), button );
207
208     // reset button
209     gtk_widget_set_sensitive( button0, FALSE );
210     g_signal_connect( G_OBJECT( button0 ), "clicked",
211                       G_CALLBACK( reset_cb ), button );
212     gtk_container_add( GTK_CONTAINER( hBox0 ), button0 );
213
214     gtk_box_pack_start( GTK_BOX( vBoxMain ), hBox0, FALSE, FALSE, 0 );
215
216     gtk_container_add( GTK_CONTAINER( window ), vBoxMain );
217
218     gtk_widget_show_all( window );
219
220     return GTK_WINDOW( window );
221 }
222
223
224 static void start_cb( GtkButton* button, gpointer data )
225 {
226     if ( stopishMode == STOPISH_MODE_START ) {
227         // set label text and add timer handle
228         gtk_button_set_label( button, "Pause" );
229         stopishMode = STOPISH_MODE_PAUSE;
230         stopish_set_time_start( stopish_current_time(  ) );
231         timerHandle = g_timeout_add( 100, stopish_timeout_cb, timerLabel );
232     }
233     else if ( stopishMode == STOPISH_MODE_RESUME ) {
234         // resume timer
235         gtk_button_set_label( button, "Pause" );
236         stopishMode = STOPISH_MODE_PAUSE;
237         stopish_timer_resume(  );
238         timerHandle = g_timeout_add( 100, stopish_timeout_cb, timerLabel );
239     }
240     else {
241         // pause timer, remove timeout
242         gtk_button_set_label( button, "Resume" );
243         stopishMode = STOPISH_MODE_RESUME;
244         g_source_remove( timerHandle );
245         stopish_timer_save(  );
246     }
247
248     // allow user to reset timer
249     gtk_widget_set_sensitive( GTK_WIDGET( data ), TRUE );
250 }
251
252
253 static void reset_cb( GtkButton* button, gpointer data )
254 {
255     GSList *tempList;
256     char *tempString;
257     char formatString[128];
258
259     if ( stopishMode == STOPISH_MODE_RESUME )
260         stopish_timer_resume(  );
261
262     // set label text and remove timer handle
263     gtk_button_set_label( GTK_BUTTON( data ), "Start" );
264     stopishMode = STOPISH_MODE_START;
265     gtk_label_set_markup( GTK_LABEL( timerLabel ),
266                           "<span font_family=\"monospace\" "
267                           "size=\"70000\" weight=\"ultrabold\">"
268                           "00:00:00.0</span>" );
269     g_source_remove( timerHandle );
270
271     // add current time to history
272     historyList = g_slist_prepend( historyList,
273                                    ( gpointer ) stopish_get_time_string(  ) );
274     sprintf( formatString, "<span size=\"large\">%s</span>",
275              ( char * ) historyList->data );
276     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel1 ),
277                           formatString );
278     tempList = historyList;
279     tempList = g_slist_next( tempList );
280     if ( tempList ) {
281         gtk_label_set_text( GTK_LABEL( timerHistoryLabel2 ),
282                             ( char * ) tempList->data );
283     }
284     tempList = g_slist_next( tempList );
285     if ( tempList ) {
286         sprintf( formatString, "<span size=\"small\">%s</span>",
287                  ( char * ) tempList->data );
288         gtk_label_set_markup( GTK_LABEL( timerHistoryLabel3 ),
289                               formatString );
290     }
291     tempList = g_slist_next( tempList );
292     if ( tempList ) {
293         sprintf( formatString, "<span size=\"x-small\">%s</span>",
294                  ( char * ) tempList->data );
295         gtk_label_set_markup( GTK_LABEL( timerHistoryLabel4 ),
296                               formatString );
297     }
298
299     // remove the history time after the 4th
300     tempList = g_slist_next( tempList );
301     if ( tempList ) {
302         tempString = tempList->data;
303         historyList = g_slist_remove( historyList, tempList->data );
304         free( tempString );
305     }
306
307     // reset start time
308     stopish_set_time_start( 0 );
309
310     // disallow user to reset timer
311     gtk_widget_set_sensitive( GTK_WIDGET( button ), FALSE );
312 }
313
314
315 static void close_cb( GtkButton* button, gpointer data )
316 {
317     // disable accelerometer for battery savings
318     accelerometer_disable(  );
319
320     // destroy main window and exit gtk main loop
321     gtk_widget_destroy( GTK_WIDGET( data ) );
322     gtk_main_quit(  );
323 }
324
325
326 static gboolean focus_in_cb( GtkWidget *widget, GdkEventFocus *event,
327                              gpointer data )
328 {
329     // enable accelerometer hardware for portrait mode support
330     accelerometer_enable(  );
331
332     return FALSE;
333 }
334
335
336 static gboolean focus_out_cb( GtkWidget *widget, GdkEventFocus *event,
337                               gpointer data )
338 {
339     // disable accelerometer for battery savings
340     accelerometer_disable(  );
341
342     return FALSE;
343 }
344
345
346 static void accelerometer_enable( void )
347 {
348     if ( osso_rpc_run_system( appdata.osso_context, MCE_SERVICE,
349                               MCE_REQUEST_PATH, MCE_REQUEST_IF,
350                               "req_accelerometer_enable", NULL,
351                               DBUS_TYPE_INVALID ) != OSSO_OK ) {
352         g_printerr("WARN: Cannot enable accelerometers\n");
353     }
354 }
355
356
357 static void accelerometer_disable( void )
358 {
359     if ( osso_rpc_run_system( appdata.osso_context, MCE_SERVICE,
360                               MCE_REQUEST_PATH, MCE_REQUEST_IF,
361                               "req_accelerometer_disable", NULL,
362                               DBUS_TYPE_INVALID ) != OSSO_OK ) {
363         g_printerr("WARN: Cannot disable accelerometers\n");
364     }
365 }
366
367
368 static DBusHandlerResult mce_filter_func( DBusConnection * connection,
369                                           DBusMessage * message,
370                                           void *data )
371 {
372     DBusMessageIter iter;
373     char *rotation = NULL;
374
375     if ( dbus_message_is_signal( message, MCE_SIGNAL_IF,
376                                  MCE_DEVICE_ORIENTATION_SIG ) ) {
377         // here if we received an orientation dbus signal
378         if ( dbus_message_iter_init( message, &iter ) ) {
379             dbus_message_iter_get_basic( &iter, &rotation );
380
381             // Rotate main window
382             if ( !strcmp( rotation, MCE_ORIENTATION_PORTRAIT ) )
383                 hildon_gtk_window_set_portrait_flags( GTK_WINDOW( appdata.main_window ),
384                                                       HILDON_PORTRAIT_MODE_REQUEST );
385             else
386                 hildon_gtk_window_set_portrait_flags( GTK_WINDOW( appdata.main_window ),
387                                                       ~HILDON_PORTRAIT_MODE_REQUEST );
388         }
389         else
390             g_printerr( "ERROR: dbus_message_iter_init() failed.\n" );
391     }
392
393     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
394 }