Wrap timer string for now. Bug #4913
[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_widget_set_size_request( timerLabel, 700, -1 );
170     gtk_label_set_line_wrap( GTK_LABEL( timerLabel ), TRUE );
171     gtk_label_set_line_wrap_mode( GTK_LABEL( timerLabel ), PANGO_WRAP_CHAR );
172     gtk_misc_set_alignment( GTK_MISC( timerLabel ), 1.0f, 0.5f );
173     gtk_container_add( GTK_CONTAINER( vBox0 ), timerLabel );
174
175     // history area
176     timerHistoryLabel1 = gtk_label_new( NULL );
177     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel1 ),
178                           "<span size=\"large\"> </span>" );
179     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel1, FALSE, FALSE, 0 );
180     timerHistoryLabel2 = gtk_label_new( NULL );
181     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel2, FALSE, FALSE, 0 );
182     timerHistoryLabel3 = gtk_label_new( NULL );
183     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel3 ),
184                           "<span size=\"small\"> </span>" );
185     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel3, FALSE, FALSE, 0 );
186     timerHistoryLabel4 = gtk_label_new( NULL );
187     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel4 ),
188                           "<span size=\"x-small\"> </span>" );
189     gtk_box_pack_start( GTK_BOX( vBox0 ), timerHistoryLabel4, FALSE, FALSE, 0 );
190
191     gtk_container_add( GTK_CONTAINER( vBoxMain ), vBox0 );
192
193     // separator
194     label = gtk_label_new( NULL );
195     gtk_container_add( GTK_CONTAINER( vBoxMain ), label );
196
197     // button area
198     hBox0 = gtk_hbox_new( FALSE, 15 );
199     gtk_widget_set_size_request( hBox0, -1, 80 );
200
201     // start/pause stopwatch button
202     button = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
203                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
204                                           "Start", NULL );
205     button0 = hildon_button_new_with_text( HILDON_SIZE_HALFSCREEN_WIDTH,
206                                            HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
207                                            "Reset", NULL );
208     g_signal_connect( G_OBJECT( button ), "clicked",
209                       G_CALLBACK( start_cb ), button0 );
210     gtk_container_add( GTK_CONTAINER( hBox0 ), button );
211
212     // reset button
213     gtk_widget_set_sensitive( button0, FALSE );
214     g_signal_connect( G_OBJECT( button0 ), "clicked",
215                       G_CALLBACK( reset_cb ), button );
216     gtk_container_add( GTK_CONTAINER( hBox0 ), button0 );
217
218     gtk_box_pack_start( GTK_BOX( vBoxMain ), hBox0, FALSE, FALSE, 0 );
219
220     gtk_container_add( GTK_CONTAINER( window ), vBoxMain );
221
222     gtk_widget_show_all( window );
223
224     return GTK_WINDOW( window );
225 }
226
227
228 static void start_cb( GtkButton* button, gpointer data )
229 {
230     if ( stopishMode == STOPISH_MODE_START ) {
231         // set label text and add timer handle
232         gtk_button_set_label( button, "Pause" );
233         stopishMode = STOPISH_MODE_PAUSE;
234         stopish_set_time_start( stopish_current_time(  ) );
235         timerHandle = g_timeout_add( 100, stopish_timeout_cb, timerLabel );
236     }
237     else if ( stopishMode == STOPISH_MODE_RESUME ) {
238         // resume timer
239         gtk_button_set_label( button, "Pause" );
240         stopishMode = STOPISH_MODE_PAUSE;
241         stopish_timer_resume(  );
242         timerHandle = g_timeout_add( 100, stopish_timeout_cb, timerLabel );
243     }
244     else {
245         // pause timer, remove timeout
246         gtk_button_set_label( button, "Resume" );
247         stopishMode = STOPISH_MODE_RESUME;
248         g_source_remove( timerHandle );
249         stopish_timer_save(  );
250     }
251
252     // allow user to reset timer
253     gtk_widget_set_sensitive( GTK_WIDGET( data ), TRUE );
254 }
255
256
257 static void reset_cb( GtkButton* button, gpointer data )
258 {
259     GSList *tempList;
260     char *tempString;
261     char formatString[128];
262
263     if ( stopishMode == STOPISH_MODE_RESUME )
264         stopish_timer_resume(  );
265
266     // set label text and remove timer handle
267     gtk_button_set_label( GTK_BUTTON( data ), "Start" );
268     stopishMode = STOPISH_MODE_START;
269     gtk_label_set_markup( GTK_LABEL( timerLabel ),
270                           "<span font_family=\"monospace\" "
271                           "size=\"70000\" weight=\"ultrabold\">"
272                           "00:00:00.0</span>" );
273     g_source_remove( timerHandle );
274
275     // add current time to history
276     historyList = g_slist_prepend( historyList,
277                                    ( gpointer ) stopish_get_time_string(  ) );
278     sprintf( formatString, "<span size=\"large\">%s</span>",
279              ( char * ) historyList->data );
280     gtk_label_set_markup( GTK_LABEL( timerHistoryLabel1 ),
281                           formatString );
282     tempList = historyList;
283     tempList = g_slist_next( tempList );
284     if ( tempList ) {
285         gtk_label_set_text( GTK_LABEL( timerHistoryLabel2 ),
286                             ( char * ) tempList->data );
287     }
288     tempList = g_slist_next( tempList );
289     if ( tempList ) {
290         sprintf( formatString, "<span size=\"small\">%s</span>",
291                  ( char * ) tempList->data );
292         gtk_label_set_markup( GTK_LABEL( timerHistoryLabel3 ),
293                               formatString );
294     }
295     tempList = g_slist_next( tempList );
296     if ( tempList ) {
297         sprintf( formatString, "<span size=\"x-small\">%s</span>",
298                  ( char * ) tempList->data );
299         gtk_label_set_markup( GTK_LABEL( timerHistoryLabel4 ),
300                               formatString );
301     }
302
303     // remove the history time after the 4th
304     tempList = g_slist_next( tempList );
305     if ( tempList ) {
306         tempString = tempList->data;
307         historyList = g_slist_remove( historyList, tempList->data );
308         free( tempString );
309     }
310
311     // reset start time
312     stopish_set_time_start( 0 );
313
314     // disallow user to reset timer
315     gtk_widget_set_sensitive( GTK_WIDGET( button ), FALSE );
316 }
317
318
319 static void close_cb( GtkButton* button, gpointer data )
320 {
321     // disable accelerometer for battery savings
322     accelerometer_disable(  );
323
324     // destroy main window and exit gtk main loop
325     gtk_widget_destroy( GTK_WIDGET( data ) );
326     gtk_main_quit(  );
327 }
328
329
330 static gboolean focus_in_cb( GtkWidget *widget, GdkEventFocus *event,
331                              gpointer data )
332 {
333     // enable accelerometer hardware for portrait mode support
334     accelerometer_enable(  );
335
336     return FALSE;
337 }
338
339
340 static gboolean focus_out_cb( GtkWidget *widget, GdkEventFocus *event,
341                               gpointer data )
342 {
343     // disable accelerometer for battery savings
344     accelerometer_disable(  );
345
346     return FALSE;
347 }
348
349
350 static void accelerometer_enable( void )
351 {
352     if ( osso_rpc_run_system( appdata.osso_context, MCE_SERVICE,
353                               MCE_REQUEST_PATH, MCE_REQUEST_IF,
354                               "req_accelerometer_enable", NULL,
355                               DBUS_TYPE_INVALID ) != OSSO_OK ) {
356         g_printerr("WARN: Cannot enable accelerometers\n");
357     }
358 }
359
360
361 static void accelerometer_disable( void )
362 {
363     if ( osso_rpc_run_system( appdata.osso_context, MCE_SERVICE,
364                               MCE_REQUEST_PATH, MCE_REQUEST_IF,
365                               "req_accelerometer_disable", NULL,
366                               DBUS_TYPE_INVALID ) != OSSO_OK ) {
367         g_printerr("WARN: Cannot disable accelerometers\n");
368     }
369 }
370
371
372 static DBusHandlerResult mce_filter_func( DBusConnection * connection,
373                                           DBusMessage * message,
374                                           void *data )
375 {
376     DBusMessageIter iter;
377     char *rotation = NULL;
378
379     if ( dbus_message_is_signal( message, MCE_SIGNAL_IF,
380                                  MCE_DEVICE_ORIENTATION_SIG ) ) {
381         // here if we received an orientation dbus signal
382         if ( dbus_message_iter_init( message, &iter ) ) {
383             dbus_message_iter_get_basic( &iter, &rotation );
384
385             // Rotate main window
386             if ( !strcmp( rotation, MCE_ORIENTATION_PORTRAIT ) ) {
387                 hildon_gtk_window_set_portrait_flags( GTK_WINDOW( appdata.main_window ),
388                                                       HILDON_PORTRAIT_MODE_REQUEST );
389                 gtk_widget_set_size_request( timerLabel, 400, -1 );
390             }
391             else {
392                 hildon_gtk_window_set_portrait_flags( GTK_WINDOW( appdata.main_window ),
393                                                       ~HILDON_PORTRAIT_MODE_REQUEST );
394                 gtk_widget_set_size_request( timerLabel, 700, -1 );
395             }
396         }
397         else
398             g_printerr( "ERROR: dbus_message_iter_init() failed.\n" );
399     }
400
401     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
402 }