added default dir&format settings
[maemo-recorder] / src / maemo-recorder-ui.c
1 /* vim: set ts=4 sw=4 et: */
2 /*
3  * maemo-recorder-ui.c
4  *
5  * Copyright (C) 2006 Nokia Corporation
6  *
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  */
22
23
24 #include <gst/gst.h>
25 #include <glib/gi18n-lib.h>
26 #include <libgnomevfs/gnome-vfs.h>
27 #include <locale.h>
28 #include <hildon-widgets/hildon-program.h>
29 #include <hildon-widgets/hildon-note.h>
30 #include <hildon-widgets/hildon-banner.h>
31 #include <hildon-widgets/hildon-defines.h>
32 #include <hildon-widgets/hildon-file-system-model.h>
33 #include <hildon-widgets/hildon-file-chooser-dialog.h>
34 #include <string.h>
35 #include <sys/time.h>
36
37 #include "maemo-recorder.h"
38 #include "maemo-recorder-ui.h"
39 #include "maemo-recorder-file.h"
40 #include "settings.h"
41
42 #define DEFAULT_REC_BLOCKSIZE "160"
43
44 #define STOP_DELAY 500
45 #define REC_UPDATE_INTERVAL 750
46 #define PLAY_UPDATE_INTERVAL 200
47
48 /* MACROs */
49
50 #define GST_TIME_MINS(t) \
51         (guint) ((((GstClockTime)(t)) / (GST_SECOND * 60)) % 60)
52 #define GST_TIME_SECS(t) \
53         (guint) ((((GstClockTime)(t)) / GST_SECOND) % 60)
54 #define GST_TIME_TO_SECS(t) \
55         (gdouble) (((gdouble)(t)) / (gdouble) GST_SECOND) /* GST_SECOND should be 1e9 */
56 #define GST_TIME_MSECS(t) \
57         (guint) (((GstClockTime)(t)) % GST_SECOND)
58
59 #define RECORDER_APP_TITLE "Maemo Recorder"
60 #define RECORDER_MSG_READY _("Ready")
61 #define RECORDER_MSG_STOPPED _("Stopped")
62 #define RECORDER_MSG_PAUSED _("Paused")
63 #define RECORDER_MSG_PLAYING  _("Playing")
64 #define RECORDER_MSG_RECORDING _("Recording")
65 #define RECORDER_FILE_UNTITLED _("Untitled")
66
67 /* general enumerations */
68     
69 typedef enum
70 {
71     DTX_OFF = 0,
72     DTX_ON = 1
73 } DTX;
74
75 /* menu codes */
76 typedef enum 
77 {
78     MENU_FILE_NEW = 1,
79     MENU_FILE_OPEN,
80     MENU_FILE_SAVE,
81     MENU_FILE_SAVE_AS,
82     MENU_FILE_REC,
83     MENU_FILE_PLAY,
84     MENU_FILE_STOP,
85     MENU_FILE_QUIT
86 } MenuActionCode;
87
88 typedef enum 
89 {
90     PIPELINE_PLAY = 1,
91     PIPELINE_PLAY_MP3,
92     PIPELINE_REC
93 } PipeLineType;
94
95 /* function prototypes */
96
97 static gboolean cbBus (GstBus *bus, 
98                GstMessage *message, 
99                gpointer data);
100
101 static void pipelineStateChanged (GstElement *element,
102                   GstState old,
103                   GstState new,
104                   GstState pending,
105                   AppData *data);
106
107 static void seekToTime(GstElement *pipeline, gdouble secs);
108 static gboolean seekToZero(AppData *data, GstElement *pipeline);
109 static void setLength(AppData *data, gdouble secs);
110 static gboolean cbStopPlayback(AppData *data);
111 static void cbStop(GtkWidget* widget, AppData *data);
112 static void cbPlay(GtkWidget* widget, AppData *data);
113 static void cbRec(GtkWidget* widget, AppData *data);
114 static void cbNew(GtkWidget* widget, AppData *data);
115 static void cbOpen(GtkWidget* widget, AppData *data);
116 /*static void cbSave(GtkWidget* widget, AppData *data);*/
117 static void cbSaveAs(GtkWidget* widget, AppData *data);
118 static void cbItemClose(GtkWidget *widget, gpointer data);
119 static void cbUserSeek(GtkAdjustment *adjustment, gpointer data);
120 static gchar* cbFormatSeekbarValue(GtkScale *scale, gdouble value);
121 static GtkWidget* createToolBar(AppData *data);
122 static void createMenu( AppData *data );
123 static gboolean createPipeline(AppData *app, PipeLineType type);
124 static void openPlayPipeline( AppData *data );
125 static gboolean destroyPipeline(AppData *data, PipeLineType type);
126 static gboolean destroyPipelines(AppData *data);
127 static void cbItemGroupChanged(gpointer data);
128 static gboolean cbUpdateRecLength(AppData *data);
129 static void cbDestroy(GtkWidget* widget, GdkEvent *event, gpointer data);
130 static gboolean openURI(gpointer user_data);
131 static gboolean closeFile(AppData *data);
132 static gdouble guessMediaLength(AppData *data);
133 static GstCaps *createCapsFilter(AudioFormat format);
134
135 static void new_pad_cb (GstElement *wavparse, GstPad *new_pad, gpointer data)
136 {
137     GstElement *sink;
138     AppData* app = (AppData*) data; 
139
140     ULOG_INFO("new pad");
141  
142     sink = gst_element_factory_make ("dsppcmsink", "sink");
143  
144     gst_bin_add (GST_BIN (app->playPipeline), sink);
145  
146     if (!gst_element_link (wavparse, sink))
147       g_error ("link(wavparse, sink) failed!");
148     gst_element_sync_state_with_parent(sink); 
149  
150 }
151
152 static gboolean createPipeline(AppData *app, PipeLineType type)
153 {
154     GstElement *src = NULL;
155     GstElement *sink = NULL;
156     GstElement *filter = NULL;
157     GstElement *queue = NULL;
158     GstElement *pipeline = NULL;
159     GstElement *parse = NULL;
160     GstCaps *caps = NULL;
161
162     g_assert(NULL != app);
163
164     pipeline = gst_pipeline_new("pipeline");
165
166     gst_bus_add_watch(gst_pipeline_get_bus (GST_PIPELINE (pipeline)),
167                    cbBus, app);
168
169     /* create elements */
170     switch (type)
171     {
172         case PIPELINE_PLAY_MP3:
173             ULOG_INFO("mp3 playback - queue");
174             src = gst_element_factory_make ("gnomevfssrc", "source");
175             queue = gst_element_factory_make ("queue", "queue");
176             sink = gst_element_factory_make ("dspmp3sink", "sink");
177
178             g_object_set(G_OBJECT (queue), 
179                 "min-threshold-bytes", 131072,
180                 NULL );
181             g_object_set(G_OBJECT(src), 
182                     "location", app->openFileName, 
183                     "blocksize", 65536,
184                     NULL);
185             break;
186
187         case PIPELINE_PLAY:
188             src = gst_element_factory_make ("filesrc", "source");
189             /* we need also a filter to modify caps */
190             filter = gst_element_factory_make("capsfilter", "filter");
191             switch (app->filter)
192             {
193                 case FORMAT_ILBC:
194                     ULOG_INFO("using ilbc sink");
195                     sink = gst_element_factory_make ("dspilbcsink", "sink");
196                     break;
197
198                 case FORMAT_PCMA:
199                 case FORMAT_PCMU:
200                 case FORMAT_PCM:
201                     ULOG_INFO("using pcm sink");
202                     sink = gst_element_factory_make ("dsppcmsink", "sink");
203                     break;
204
205                 case FORMAT_WAV:
206                     ULOG_INFO("using wavparse & pcm sink");
207                     parse = gst_element_factory_make ("wavparse", "parse");
208                     break;
209                     
210                 default:
211                     break;  
212             }
213
214             g_object_set(G_OBJECT(src), 
215                     "location", app->openFileName, 
216                     NULL);
217
218             caps = createCapsFilter(app->filter);
219             g_object_set(G_OBJECT(filter), 
220                   "caps", caps,
221                   NULL);
222             break;
223
224         case PIPELINE_REC:
225             switch (app->filter)  
226             {    
227                 case FORMAT_ILBC:
228                     ULOG_INFO("using ilbc source");
229                     src = gst_element_factory_make("dspilbcsrc", "source");
230                     g_object_set(G_OBJECT(src),
231                         "dtx", DTX_OFF,
232                         NULL);
233                     break;
234         
235                 case FORMAT_PCMA:
236                 case FORMAT_PCMU:
237                 case FORMAT_PCM:
238                     ULOG_INFO("using pcm source");
239                     src = gst_element_factory_make("dsppcmsrc", "source");
240                     g_object_set(G_OBJECT (src), 
241                           "blocksize", DEFAULT_REC_BLOCKSIZE, 
242                           "dtx", DTX_OFF,
243                         NULL);
244                     break;
245             
246                 case FORMAT_WAV:
247                     ULOG_INFO("using pcm source & wavenc");
248                     src = gst_element_factory_make("dsppcmsrc", "source");
249                     g_object_set(G_OBJECT (src),
250                             "blocksize", DEFAULT_REC_BLOCKSIZE,
251                             "dtx", DTX_OFF,
252                             NULL);
253                     parse = gst_element_factory_make("wavenc", "enc");
254                     break;   
255             
256                 default:
257                     ULOG_WARN("Unknown filter type!");
258                     break; 
259             }
260
261             filter = gst_element_factory_make("capsfilter", "filter");
262             caps = createCapsFilter(app->filter);
263             g_object_set(G_OBJECT(filter), 
264                   "caps", caps,
265                   NULL);
266
267             sink = gst_element_factory_make("filesink", "sink");
268
269             g_object_set(G_OBJECT(sink), 
270                 "location", app->saveFileName,
271                 NULL);
272             break;
273
274         default:
275             ULOG_ERR("Invalid pipeline type!");
276             gst_object_unref(pipeline);
277             return FALSE;
278     }
279
280     if (!src || !pipeline)
281     {
282         ULOG_ERR("Could not create GstElement!");
283         return FALSE;
284     }
285
286     if (!sink && app->filter != FORMAT_WAV)
287     {
288         ULOG_ERR("Could not create GstElement!");
289         return FALSE;        
290     }
291         
292     ULOG_INFO("Create pipeline");
293      
294     /* add to pipeline and link */
295     switch (type)
296     {
297         case PIPELINE_REC:
298             switch (app->filter)
299             {
300                 case FORMAT_ILBC:
301                 case FORMAT_PCM:
302                 case FORMAT_PCMA:
303                     if (!filter)
304                     {
305                        ULOG_ERR("Could not create filter GstElement!");
306                        return FALSE;
307                     }
308                     gst_bin_add_many(GST_BIN(pipeline), src, filter, sink, NULL);
309
310                     if (!gst_element_link_many (src, filter, sink, NULL))
311                     {
312                         ULOG_ERR("gst_element_link failed for src, filter and sink!");
313                         return FALSE;
314                     }
315                     break;   
316                 case FORMAT_WAV:
317                     gst_bin_add_many(GST_BIN(pipeline), src, parse, sink, NULL);
318                     if (!gst_element_link_many (src, parse, sink, NULL))
319                     {
320                         ULOG_ERR("gst_element_link failed for src, parse and sink!");
321                     }
322                     break;
323          
324                 default:
325                     break;
326           
327             }
328             break; 
329            
330         case PIPELINE_PLAY:
331             switch (app->filter)
332             {
333                 case FORMAT_ILBC:
334                 case FORMAT_PCM:
335                 case FORMAT_PCMA:
336                     if (!filter)
337                     {
338                        ULOG_ERR("Could not create filter GstElement!");
339                        return FALSE;
340                     }
341                     gst_bin_add_many(GST_BIN(pipeline), src, filter, sink, NULL);
342
343                     if (!gst_element_link_many (src, filter, sink, NULL))
344                     {
345                         ULOG_ERR("gst_element_link failed for src, filter and sink!");
346                         return FALSE;
347                     }
348
349                     break;
350                 case FORMAT_WAV:
351                     gst_bin_add_many(GST_BIN(pipeline), src, parse, NULL);
352                     if (!gst_element_link_many (src, parse, NULL))
353                     {
354                         ULOG_ERR("gst_element_link failed for src, parse and sink!");
355                         return FALSE;
356                     }
357                     app->playPipeline = pipeline;
358                     g_signal_connect(parse, "pad_added",
359                                  G_CALLBACK(new_pad_cb), app);
360              
361                     break;
362
363                 default:
364                     break;
365             }
366
367             break;
368      
369         case PIPELINE_PLAY_MP3:
370         default:
371             gst_bin_add_many(GST_BIN(pipeline), src, queue, sink, NULL);
372  
373             if(!gst_element_link_many(src, queue, sink, NULL))
374             {
375                  ULOG_ERR("gst_element_link failed for src and sink!");
376                  return FALSE;
377             }
378
379             break;
380     }
381
382     /* set application data */
383     if (type == PIPELINE_REC)
384     {
385         app->recPipeline = pipeline;
386     }
387     else
388     {
389         app->playPipeline = pipeline;
390         app->playPipelineType = type;
391     }
392      
393     if (caps)
394     {
395         gst_caps_unref(caps);
396         caps = NULL;
397     }
398
399     return TRUE;
400 }
401
402 static gboolean destroyPipelines(AppData *data)
403 {
404     gboolean ret = FALSE;
405
406     /* ugly hack with pipeline types, but works though */
407     ret != destroyPipeline(data, PIPELINE_REC);
408     ret != destroyPipeline(data, PIPELINE_PLAY);
409     return ret;
410 }
411
412 static gboolean destroyPipeline(AppData *data, PipeLineType type)
413 {
414     GstState state;
415     GstState pending;
416     GstElement *pipeline = NULL;
417
418     ULOG_INFO("%s() - Stopping playback/recording", G_STRFUNC);
419
420     switch (type)
421     {
422         case PIPELINE_REC:
423             pipeline = data->recPipeline;
424             /*
425             data->recPipeline = NULL;
426             */
427             break;
428         default:
429             pipeline = data->playPipeline;
430             /*
431             data->playPipeline = NULL;
432             */
433             break;
434     }
435     
436     if (!GST_IS_ELEMENT(pipeline))
437         return TRUE;
438
439     /* this unallocates everything */
440     gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
441
442     /* for some reason the state does not update manually */
443     gst_element_get_state(pipeline, &state, 
444             &pending, GST_CLOCK_TIME_NONE);
445     pipelineStateChanged(pipeline,
446            state,
447            state,
448            pending,
449            data);
450
451     /*
452     gst_object_unref(pipeline);
453     */
454
455     return TRUE;
456 }
457
458
459 /* callbacks */
460
461 static void pipelineStateChanged (GstElement *element,
462                   GstState old,
463                   GstState new,
464                   GstState pending,
465                   AppData * data)
466 {
467     g_assert(NULL != data);
468      
469     switch (new)
470     {
471         case GST_STATE_PLAYING:
472           if(APPSTATE_RECORDING == getAppState(data))
473           {
474                gchar *tmp = g_strdup_printf("%s...", RECORDER_MSG_RECORDING);
475                hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, tmp);
476                g_free(tmp);
477                tmp = NULL;
478                ULOG_INFO("%s() - Recording", G_STRFUNC);
479                gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
480                       RECORDER_MSG_RECORDING);
481
482                gtk_widget_set_state(data->buttonRec, GTK_STATE_ACTIVE);
483
484                if (data->recUpdateId == 0 && gettimeofday(&data->recStartTv, NULL) == 0)
485                {
486                    data->recUpdateId = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, REC_UPDATE_INTERVAL, (GSourceFunc) cbUpdateRecLength, data, NULL);
487                }
488           }
489           else
490           {
491                gchar *tmp = g_strdup_printf("%s...", RECORDER_MSG_PLAYING);
492                hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, tmp);
493                g_free(tmp);
494                tmp = NULL;
495                ULOG_INFO("%s() - Playing", G_STRFUNC);
496                gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
497                       RECORDER_MSG_PLAYING);  
498                gtk_widget_set_state(data->buttonPlay, GTK_STATE_ACTIVE);
499                /*
500                gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PAUSE);
501                */
502           }
503
504           break;
505
506         case GST_STATE_READY:
507           /* hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, "Ready..."); */
508           ULOG_INFO("%s() - Ready", G_STRFUNC);
509           gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
510                      RECORDER_MSG_READY);  
511           break;
512
513         case GST_STATE_PAUSED:
514           {
515               gint64 pos = 0;
516               GstFormat fmt = GST_FORMAT_TIME;
517               ULOG_INFO("%s() - Paused", G_STRFUNC);
518
519               /* if pipeline pos == 0 => stopped, else => paused */
520               if (GST_IS_ELEMENT(data->playPipeline) && gst_element_query_position(data->playPipeline, &fmt, &pos) && pos != 0)
521               {
522                   hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_PAUSED);       
523                   gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
524                          RECORDER_MSG_PAUSED);
525                   /*
526                   gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
527                   gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
528                   */
529               }
530               else
531               {
532                   hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_STOPPED);       
533                   gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
534                          RECORDER_MSG_STOPPED);  
535                   gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
536                   gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
537               }
538               gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
539           }
540           break;
541           
542         case GST_STATE_NULL:
543           ULOG_INFO("%s() - Null", G_STRFUNC);
544           hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_STOPPED);
545           gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
546                      RECORDER_MSG_READY);  
547           gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
548           gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
549           gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
550           
551           break;
552
553         default:
554           ULOG_WARN("%s() - default case", G_STRFUNC);
555           break;
556     }
557 }
558
559
560 static gboolean cbBus(GstBus *bus,
561                GstMessage *message,
562                gpointer   data)
563 {
564     AppData *app = (AppData*)data;
565
566     switch (GST_MESSAGE_TYPE(message)) 
567     {
568         case GST_MESSAGE_WARNING:
569         {
570             GError *err;
571             gchar *debug;
572
573             gst_message_parse_error (message, &err, &debug);
574             ULOG_WARN("%s() - Warning: %s", G_STRFUNC, err->message);
575             g_error_free (err);
576             g_free (debug);
577             break;
578         }
579         case GST_MESSAGE_ERROR: 
580         {
581             GError *err;
582             gchar *debug;
583
584             gst_message_parse_error (message, &err, &debug);
585             ULOG_ERR("%s() - Error: %s", G_STRFUNC, err->message);
586             g_error_free (err);
587             g_free (debug);
588             /* break; */
589             /* flow through to eos */
590         }
591         case GST_MESSAGE_EOS:
592         {
593             ULOG_INFO("%s() - eos", G_STRFUNC);
594
595              switch(getAppState(app))
596              {
597                  case APPSTATE_PLAYING:
598                     /* stop playback after a short break*/
599                     g_timeout_add(STOP_DELAY, (GSourceFunc)cbStopPlayback, data);
600                     break;
601
602                  case APPSTATE_RECORDING:
603                      gst_element_set_state(GST_ELEMENT(app->recPipeline), 
604                            GST_STATE_PAUSED);
605                      destroyPipeline(app, PIPELINE_REC);
606                      app->saved = FALSE;
607                      break;
608
609                  case APPSTATE_READY:
610                  default:
611                     /* destroyPipelines(app); */
612                      break;
613              }
614              break;
615         }
616         case GST_MESSAGE_STATE_CHANGED: 
617         {
618             GstState old;
619             GstState new;
620             GstState pending;
621
622             gst_message_parse_state_changed(message, &old, &new, &pending);
623
624             pipelineStateChanged(NULL, old, new, pending, app);
625
626             break;
627         }
628
629         default:
630             /* unhandled message */
631             ULOG_WARN("%s() - Unhandled message, type = %d", G_STRFUNC, message->type);
632             break;
633     }
634
635     /* remove message from the queue */
636     return TRUE;
637 }
638
639 static void cbDestroy(GtkWidget* widget, GdkEvent *event, gpointer data)
640 {     
641     AppData* app;
642
643     g_assert(data);
644
645     app = (AppData *) data;
646
647     if (!closeFile(app))
648         return;
649
650     destroyPipelines(app);
651     if (app->playPipeline)
652         gst_object_unref(GST_OBJECT(app->playPipeline));
653
654     if (app->recPipeline)
655         gst_object_unref(GST_OBJECT(app->recPipeline));
656
657     gtk_main_quit();
658 }
659
660 static gboolean cbCheckPosition (AppData *data)
661 {
662     GstFormat fmt = GST_FORMAT_TIME;
663     gint64 pos = 0, len = 0;
664     static gboolean lengthSet = FALSE;
665
666     g_assert(NULL != data);
667
668     /* get length */
669     if(!lengthSet && gst_element_query_duration(data->playPipeline, &fmt, &len))
670     {
671         if (len > 0)
672         {
673             gdouble size = 0;
674             size = GST_TIME_TO_SECS(len);
675             setLength(data, size); /* sets lengthEntry and adjustment */
676             lengthSet = TRUE;
677         }
678     }
679
680     /* calculate position */
681     if (gst_element_query_position(data->playPipeline, &fmt, &pos))
682     {
683         gdouble time = GST_TIME_TO_SECS(pos);
684
685         ULOG_DEBUG("pos = %lld, time = %f", 
686              pos,
687            time 
688            );
689
690         gtk_adjustment_set_value( 
691            GTK_ADJUSTMENT(data->mainViewData.adjustment),
692            time);
693         gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
694     }
695
696     if (APPSTATE_PLAYING == getAppState(data))
697     {
698         return TRUE;
699     }
700
701     return FALSE;
702 }
703
704 static void cbNew(GtkWidget* widget, AppData *data) 
705 {
706     g_assert(NULL != data);
707
708     if (!closeFile(data))
709         return;
710
711     /* remove pipelines if existing */
712     destroyPipelines(data);
713     ULOG_DEBUG_F("cbNew");
714     /* destroy tmp file */
715
716     /* clear filenames */
717     g_free(data->openFileName);
718     data->openFileName = NULL;
719     g_free(data->saveFileName);
720     data->saveFileName = NULL;
721 /*    data->filter = FORMAT_NONE;*/
722     data->file_format = FORMAT_NONE;
723
724     /* update display */
725     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
726         RECORDER_FILE_UNTITLED);
727     setLength(data, 0.0);
728     /* update the display + scale */
729     gtk_adjustment_set_value(GTK_ADJUSTMENT(data->mainViewData.adjustment),
730               0);
731     gtk_widget_set_sensitive(data->buttonSave, FALSE);
732     gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
733     data->saved = TRUE;
734
735     gtk_window_set_title(GTK_WINDOW(data->mainView), RECORDER_FILE_UNTITLED);
736
737     ULOG_DEBUG_F("cbNew end");
738 }
739
740 static void cbOpen(GtkWidget* widget, AppData *data) 
741 {
742     GtkWidget* dialog = NULL;
743     gchar *tmpfile = NULL;
744     gchar *selected = NULL;
745     AudioFormat format;
746     gchar *basename;
747     gdouble len = -1.0;
748 #if 0
749     GtkFileFilter *filter;
750 #endif
751
752     ULOG_DEBUG_F("begin");
753     g_assert(NULL != data);
754
755     if (!closeFile(data))
756         return;
757
758 #if 0
759     /* create filter */
760     filter = gtk_file_filter_new();
761     gtk_file_filter_add_mime_type(filter, "audio/x-mp3");
762 #endif
763
764     g_assert(GTK_IS_WINDOW(data->mainView));
765     /* create dialog */
766     dialog = hildon_file_chooser_dialog_new_with_properties(
767               GTK_WINDOW(data->mainView), 
768               "action", GTK_FILE_CHOOSER_ACTION_OPEN,
769               "file-system-model", NULL,
770               "local-only", TRUE,
771               NULL
772               );
773
774     /* show it */
775     gtk_widget_show_all(dialog);
776
777     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
778     {
779         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
780     }
781
782     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
783
784     if (dialog != NULL && GTK_IS_WIDGET(dialog))
785     {
786         gtk_widget_destroy(dialog);
787         /*
788         ULOG_DEBUG("%s() - dialog destroyed", G_STRFUNC);
789         */
790     }
791
792     if (NULL == selected) /* no file selected */
793         return;
794
795     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
796
797     if (openFile(selected, &format, &tmpfile))
798     {
799         ULOG_INFO("%s() - openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
800
801         g_assert(tmpfile);
802
803         /* update filenames */
804         basename = g_path_get_basename(selected);
805
806         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), selected);
807         gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
808         g_free(basename);
809
810         g_free(data->openFileName);
811         data->openFileName = tmpfile;
812         data->file_format = format;
813         data->filter = format;
814         g_free(data->saveFileName);
815         data->saveFileName = NULL;
816         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
817
818         len = guessMediaLength(data);
819         if (len > 0.0)
820             setLength(data, len);
821         else
822             setLength(data, 0.0);
823
824         data->saved = TRUE;
825     }
826     else
827     {
828         ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
829         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
830     }
831
832     g_free(selected);
833
834     ULOG_DEBUG_F("end");
835 }
836
837 static gboolean
838 openURI(gpointer user_data)
839 {
840     AppData *data;
841     GnomeVFSURI *uri;
842     gchar *selected = NULL;
843     AudioFormat format;
844     gchar *tmpfile = NULL;
845     gchar *basename = NULL;
846     gdouble len = -1.0;
847
848     g_assert(user_data);
849     data = (AppData *) user_data;
850
851     if (NULL == data->mimeURI)
852         return FALSE;
853
854     uri = gnome_vfs_uri_new(data->mimeURI);
855     selected = g_strdup(gnome_vfs_uri_get_path(uri));
856
857     gnome_vfs_uri_unref(uri);
858     uri = NULL;
859
860     g_free(data->mimeURI);
861     data->mimeURI = NULL;
862
863     /* TODO: the following is duplicated in cbOpen(), move to a tryOpen() function ? */
864
865     if (NULL == selected)
866         return FALSE;
867
868     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
869
870     if (openFile(selected, &format, &tmpfile))
871     {
872         ULOG_INFO("%s: openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
873
874         g_assert(tmpfile);
875
876         /* update filenames */
877         basename = g_path_get_basename(selected);
878         
879         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), selected);
880         gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
881         g_free(basename);
882
883         g_free(data->openFileName);
884         data->openFileName = tmpfile;
885         data->file_format = format;
886         data->filter = format;
887         g_free(data->saveFileName);
888         data->saveFileName = NULL;
889         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
890         
891         len = guessMediaLength(data);
892         if (len > 0.0)
893             setLength(data, len);
894         else
895             setLength(data, 0.0);
896
897         data->saved = TRUE;
898     }
899     else
900     {
901         ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
902         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
903     }
904
905     g_free(selected);
906
907     return FALSE;
908 }
909
910 static void openPlayPipeline( AppData *data )
911 {
912
913     GstFormat fmt = GST_FORMAT_TIME;
914     gint64 len;
915     gdouble size = 0;
916     /* create pipelines */
917     /* check file type */
918     switch (data->file_format)
919     {
920         case FORMAT_PCMA:
921         case FORMAT_PCMU:
922         case FORMAT_PCM:
923         case FORMAT_ILBC:
924         case FORMAT_WAV:
925             destroyPipelines(data);
926             data->filter = data->file_format;
927             createPipeline(data, PIPELINE_PLAY);
928             break;
929
930         case FORMAT_MP3:
931             destroyPipelines(data);
932             data->filter = data->file_format;
933             createPipeline(data, PIPELINE_PLAY_MP3);
934             break;
935
936         case FORMAT_NONE:
937         default:
938             ULOG_WARN("%s() - unknown file_format", G_STRFUNC);
939             hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Unknown filetype!"));
940             return;
941             break;
942     }
943
944     if (!GST_IS_ELEMENT(data->playPipeline))
945     {
946         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline!"));
947         return;
948     }
949
950     gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_READY);
951     gst_element_get_state(GST_ELEMENT(data->playPipeline), NULL, NULL, GST_CLOCK_TIME_NONE /* or ns */);
952
953     /* calculate length */
954     if (gst_element_query_duration (data->playPipeline, &fmt, &len))
955     {
956         if (len > 0)
957         {
958             size = GST_TIME_TO_SECS(len);
959             ULOG_INFO("playSize: len:%lld size:%f", len, size);
960             setLength(data, size);
961         }
962     }
963     /*
964     else 
965     {
966         ULOG_INFO("playSize else");
967         setLength(data, 0.0);
968     }
969     */
970 }
971
972 /* returns whether the action can proceed or should be aborted */
973 static gboolean 
974 closeFile(AppData *data)
975 {
976     GtkWidget *note;
977     gint i;
978
979     if (data->saved)
980         return TRUE;
981
982     note = hildon_note_new_confirmation_add_buttons(GTK_WINDOW(data->mainView), _("Save recording?"),
983             _("Yes"), GTK_RESPONSE_YES,
984             _("No"), GTK_RESPONSE_NO,
985             _("Cancel"), GTK_RESPONSE_CANCEL,
986             NULL);
987
988     i = gtk_dialog_run(GTK_DIALOG(note));
989     gtk_widget_destroy(note);
990
991     switch (i)
992     {
993         case GTK_RESPONSE_CANCEL:
994             return FALSE;
995
996         case GTK_RESPONSE_NO:
997             return TRUE;
998
999         case GTK_RESPONSE_YES:
1000         {
1001             cbSaveAs(NULL, data);
1002             return data->saved;
1003         }
1004         default:
1005             ULOG_WARN("%s(): unknown response from dialog: %d", G_STRFUNC, i);
1006     }
1007     return FALSE;
1008 }
1009      
1010 #if 0
1011 static void cbSave(GtkWidget* widget, AppData *data) 
1012 {
1013      GtkWidget* dialog = NULL;
1014      const gchar *current;
1015      gchar *selected = NULL;
1016
1017      g_assert(data);
1018
1019      ULOG_DEBUG("%s() - begin", G_STRFUNC);
1020
1021      current = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
1022      if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0) 
1023      {
1024        hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
1025        return;
1026      }
1027
1028      /* if saveFileName does not exist run saveas */
1029      if (NULL == data->saveFileName)
1030      {
1031       /* create dialog */
1032       dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
1033                    GTK_WINDOW(data->mainView), 
1034                    GTK_FILE_CHOOSER_ACTION_SAVE));
1035       
1036       /* show it */
1037       gtk_widget_show_all(dialog);
1038       
1039       if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
1040       {
1041            selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1042       }
1043      
1044       ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
1045       
1046       gtk_widget_destroy(dialog);
1047       
1048       if (NULL != selected)
1049       {
1050            ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC,
1051             selected);
1052            g_free(data->saveFileName);
1053            data->saveFileName = g_strdup_printf("%s%s", selected, getExtension(data->file_format));
1054            g_free(selected);
1055            selected = NULL;
1056       }
1057       else
1058       {
1059            return;
1060       }
1061      }
1062
1063      /* save the file */
1064      if (doSave(gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry)), data->saveFileName, data->file_format))
1065      {
1066       gchar *basename = g_path_get_basename(data->saveFileName);
1067       ULOG_INFO("%s() - file succesfully saved!", G_STRFUNC);
1068       g_free(data->openFileName);
1069       data->openFileName = g_strdup(data->saveFileName);
1070             
1071       gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1072                  data->saveFileName);
1073       gtk_widget_set_sensitive(data->buttonSave, FALSE);
1074       gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
1075       g_free(basename);
1076      }
1077      
1078      ULOG_DEBUG("%s() - end", G_STRFUNC);
1079 }
1080 #endif
1081
1082 static void cbSettings(GtkWidget* widget, AppData *data)
1083 {
1084     edit_settings( widget, data );
1085 }
1086
1087 static void cbSaveAs(GtkWidget* widget, AppData *data) 
1088 {
1089     GtkWidget* dialog = NULL;
1090     const gchar *current;
1091     gchar *selected = NULL;
1092
1093     g_assert(NULL != data);
1094
1095     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1096
1097     current = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
1098     if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0) 
1099     {
1100         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
1101         return;
1102     }
1103
1104     /* create dialog */
1105     dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
1106               GTK_WINDOW(data->mainView), 
1107               GTK_FILE_CHOOSER_ACTION_SAVE));
1108
1109     gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), 
1110             get_default_dir() );
1111
1112     /* show it */
1113     gtk_widget_show_all(dialog);
1114
1115     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
1116     {
1117         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1118     }
1119
1120     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
1121     gtk_widget_destroy(dialog);
1122
1123     if (NULL == selected)
1124         return;
1125
1126     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
1127
1128     g_free(data->saveFileName);
1129     data->saveFileName = NULL;
1130
1131     if (saveFile(selected, data->openFileName, data->file_format, &(data->saveFileName)))
1132     {
1133         gchar *basename;
1134         const gchar *ext;
1135
1136         g_assert(data->saveFileName);
1137
1138         /* set new title that has the file name */
1139         basename = g_path_get_basename(data->saveFileName);
1140         ULOG_DEBUG("%s() - file '%s' succesfully saved!", G_STRFUNC, data->saveFileName);
1141
1142         gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
1143
1144         /* Houston, we have a kludge:
1145          * for AU files we need to keep the old tmpfile for playback
1146          * for RAW/iLBC files, we can remove the tmpfile and point openFileName to the saved file
1147          */
1148         ext = getExtension(data->file_format);
1149         if (strcmp(ext, EXTENSION_AU) != 0)
1150         {
1151             g_free(data->openFileName);
1152             data->openFileName = g_strdup(data->saveFileName);
1153         }
1154
1155         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1156                  data->saveFileName);
1157         
1158         g_free(basename);
1159         data->saved = TRUE;
1160     }
1161     else
1162     {
1163         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Saving file failed!"));
1164     }
1165
1166     g_free(selected);
1167     selected = NULL;
1168
1169     ULOG_DEBUG("%s() - end", G_STRFUNC);
1170 }
1171
1172 static void cbRec(GtkWidget* widget, AppData *data) 
1173 {
1174     g_assert(NULL != data);
1175
1176     ULOG_DEBUG("%s() - begin", G_STRFUNC);     
1177
1178     if (APPSTATE_READY != getAppState(data))
1179     {
1180         ULOG_WARN("%s() - state different than READY -> return", G_STRFUNC);
1181         return;
1182     }
1183
1184     if (!closeFile(data))
1185      return;
1186
1187     /* clear filenames, use tmp file */
1188     g_free(data->openFileName);
1189     data->openFileName = NULL;
1190
1191     g_free(data->saveFileName);
1192     data->saveFileName = NULL;     
1193
1194     switch (data->filter)
1195     {
1196         case FORMAT_PCM:
1197              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_FILE);
1198              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_FILE);
1199              break;
1200
1201          case FORMAT_PCMA:
1202              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMA_FILE);
1203              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMA_FILE);
1204              break;
1205              
1206          case FORMAT_PCMU:
1207              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMU_FILE);
1208              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMU_FILE);
1209              break;
1210
1211          case FORMAT_WAV:
1212              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_WAV_FILE);
1213              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_WAV_FILE);
1214              break;
1215
1216          case FORMAT_ILBC:
1217          default:
1218              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_ILBC_FILE);
1219              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_ILBC_FILE);
1220             break;
1221     }
1222
1223     ULOG_INFO("%s() - creating pipelines", G_STRFUNC);
1224     /* start recording */
1225     /* create related pipelines */
1226     if (createPipeline(data, PIPELINE_REC))
1227     {
1228         ULOG_INFO("%s() - starting recording", G_STRFUNC);
1229         /* start recording */
1230         gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1231                            GST_STATE_PLAYING);
1232
1233         /* update display */
1234         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1235             data->saveFileName);
1236
1237         setAppState(data, APPSTATE_RECORDING);
1238         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1239         gtk_window_set_title(GTK_WINDOW(data->mainView), RECORDER_FILE_UNTITLED);
1240         data->file_format = data->filter;
1241     }
1242     else
1243     {
1244         ULOG_ERR("Could not create rec pipeline!");
1245         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline"));
1246         setAppState(data, APPSTATE_READY);
1247     }
1248  
1249     ULOG_DEBUG("%s() - end", G_STRFUNC);     
1250 }
1251
1252 static void cbPlay(GtkWidget* widget, AppData *data) 
1253 {
1254     const gchar * file = NULL;
1255      
1256     g_assert(NULL != data);
1257
1258     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1259
1260     file = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
1261     if (NULL == data->openFileName || NULL == file || strcmp(file, RECORDER_FILE_UNTITLED) == 0) 
1262     {
1263         ULOG_WARN("%s() - nothing to play", G_STRFUNC);
1264         return;
1265     }
1266
1267     openPlayPipeline(data);
1268      
1269     if (APPSTATE_PLAYING == getAppState(data))
1270     {
1271          if (GST_IS_ELEMENT(data->playPipeline)) 
1272          {
1273              gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_PAUSED);
1274              setAppState(data, APPSTATE_READY);
1275          }
1276          return;
1277     }
1278
1279     if (APPSTATE_READY != getAppState(data))
1280     {
1281         ULOG_WARN("%s() - state different than PLAYING or READY -> return", G_STRFUNC);
1282         return;
1283     }
1284
1285     ULOG_INFO("filename %s", file);
1286      
1287     /*openPlayPipeline( data );*/
1288      
1289     /*if ( ! GST_IS_ELEMENT(data->playPipeline) ) 
1290     {
1291         if (g_strrstr(data->openFileName, EXTENSION_RAW))
1292         {
1293              ULOG_INFO("cbOpen() - file was raw, assuming audio/x-raw-int, 8kHz, 1 ch, 16-bit");
1294              destroyPipelines(data);
1295              createPipeline(data, PIPELINE_PLAY);      
1296         }
1297     }*/
1298      
1299     if (! GST_IS_ELEMENT(data->playPipeline))
1300     {
1301         ULOG_WARN("%s() - playPipeline does not exist", G_STRFUNC);
1302         return;
1303     }
1304
1305     gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1306                GST_STATE_PLAYING);
1307
1308     setAppState(data, APPSTATE_PLAYING);
1309
1310     g_timeout_add(PLAY_UPDATE_INTERVAL, (GSourceFunc)cbCheckPosition, data);
1311
1312     ULOG_DEBUG("%s() - end", G_STRFUNC);
1313 }
1314
1315 static void cbStop(GtkWidget* widget, AppData *data) 
1316 {
1317     g_assert(NULL != data);
1318
1319     ULOG_DEBUG("%s() - begin", G_STRFUNC); 
1320
1321     /* check if we are playing/recording */
1322
1323     /*
1324     if (APPSTATE_PLAYING != getAppState(data) &&
1325     APPSTATE_RECORDING != getAppState(data))
1326     {
1327       ULOG_WARN("cbStop() - state different than PLAYING or RECORDING "
1328           "-> return");
1329       return;
1330     } 
1331     */
1332
1333     /* stop playing or recording */
1334     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1335     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1336     gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
1337
1338     /* destroy related pipeline */
1339     switch(getAppState(data))
1340     {
1341         case APPSTATE_PLAYING:
1342             /* don't destroy the playing pipeline. Instead, set the pipeline to PAUSED */
1343             /* destroyPipeline(data, PIPELINE_PLAY); */
1344             ULOG_INFO("%s() - Setting playPipeline state to PAUSED", G_STRFUNC);
1345             gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1346                      GST_STATE_PAUSED);
1347             /* flow through */
1348         case APPSTATE_READY:
1349             /* seek to zero, but not for PCM pipeline */
1350             /* if (data->playPipelineType == PIPELINE_PLAY || seekToZero(data, GST_ELEMENT(data->playPipeline))) */
1351             if ( !GST_IS_ELEMENT(data->playPipeline) || seekToZero(data, GST_ELEMENT(data->playPipeline)))
1352             {
1353               gtk_adjustment_set_value( 
1354                    GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1355               gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1356               gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
1357                          RECORDER_MSG_STOPPED);  
1358             }
1359             break;
1360
1361         case APPSTATE_RECORDING:
1362         {
1363             gdouble len = -1.0;
1364             gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1365                  GST_STATE_PAUSED);
1366             destroyPipeline(data, PIPELINE_REC);
1367             gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1368             data->saved = FALSE;
1369
1370             len = guessMediaLength(data);
1371             if (len > 0.0)
1372                 setLength(data, len);
1373
1374             break;
1375         }
1376      
1377         default:
1378             /* seekToZero(data, GST_ELEMENT(data->playPipeline)); */
1379             /* should not come here */
1380             break;
1381     }
1382
1383     setAppState(data, APPSTATE_READY);
1384
1385     ULOG_DEBUG("%s() - end", G_STRFUNC); 
1386 }
1387
1388
1389 /* ui construction functions */
1390
1391 static GtkWidget* createToolBar(AppData *data)
1392 {
1393      GtkToolbar* toolBar = NULL;
1394      
1395      GtkToolItem* new = NULL;
1396      GtkToolItem* open = NULL;
1397      GtkToolItem* save = NULL;
1398      GtkToolItem* saveas = NULL;
1399      GtkToolItem* sep = NULL;
1400      GtkToolItem* play = NULL;
1401      GtkToolItem* rec = NULL;
1402      GtkToolItem* stop = NULL;
1403      
1404      /* create buttons */
1405      new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); 
1406      open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); 
1407      save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
1408      data->buttonSave = GTK_WIDGET(save);
1409      saveas = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE_AS);
1410      data->buttonSaveAs = GTK_WIDGET(saveas);
1411      gtk_widget_set_sensitive(data->buttonSave, FALSE);
1412      gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
1413      data->saved = TRUE;
1414
1415      rec = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_RECORD); 
1416      data->buttonRec = GTK_WIDGET(rec);
1417      play = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY); 
1418      data->buttonPlay = GTK_WIDGET(play);
1419      stop = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
1420      
1421      /* create separator */
1422      sep  = gtk_separator_tool_item_new();
1423
1424      /* create the toolbar itself */
1425      toolBar = GTK_TOOLBAR(gtk_toolbar_new());
1426
1427      /* add items to toolbar */
1428      gtk_toolbar_insert(toolBar, new, -1);
1429      gtk_toolbar_insert(toolBar, open, -1);
1430      /*
1431      gtk_toolbar_insert(toolBar, save, -1);
1432      */
1433      gtk_toolbar_insert(toolBar, saveas, -1);
1434      gtk_toolbar_insert(toolBar, sep,  -1);
1435      gtk_toolbar_insert(toolBar, rec, -1);
1436      gtk_toolbar_insert(toolBar, play, -1);
1437      gtk_toolbar_insert(toolBar, stop, -1);
1438
1439      /* connect signals */
1440      g_signal_connect(G_OBJECT(new), "clicked",
1441               G_CALLBACK(cbNew), 
1442               data);
1443      g_signal_connect(G_OBJECT(open), "clicked",
1444               G_CALLBACK(cbOpen), 
1445               data);
1446      /*
1447      g_signal_connect(G_OBJECT(save), "clicked",
1448               G_CALLBACK(cbSave), 
1449               data);
1450      */
1451      g_signal_connect(G_OBJECT(saveas), "clicked",
1452               G_CALLBACK(cbSaveAs), 
1453               data);
1454      g_signal_connect(G_OBJECT(rec), "clicked",
1455               G_CALLBACK(cbRec), 
1456               data);
1457      g_signal_connect(G_OBJECT(play), "clicked",
1458               G_CALLBACK(cbPlay), 
1459               data);
1460      g_signal_connect(G_OBJECT(stop), "clicked",
1461               G_CALLBACK(cbStop), 
1462               data);
1463
1464      return GTK_WIDGET(toolBar);
1465
1466 }
1467
1468 static void cbItemGroupChanged( gpointer data )
1469 {
1470     AppData* app = (AppData* ) data;
1471     GValue active ={G_TYPE_INVALID};
1472     gint pcma,ilbc,pcm;
1473     
1474     g_value_init(&active, G_TYPE_INT);
1475
1476     g_object_get_property(G_OBJECT(app->radio_pcma), "active", &active);
1477     pcma = g_value_get_int(&active);
1478     g_object_get_property(G_OBJECT(app->radio_ilbc), "active", &active);
1479     ilbc = g_value_get_int(&active);
1480     g_object_get_property(G_OBJECT(app->radio_pcm), "active", &active);
1481     pcm = g_value_get_int(&active);
1482     
1483     ULOG_INFO("change type pcma=%d ilbc=%d pcm=%d",pcma, ilbc, pcm);
1484     if ( pcma == 1 )
1485         app->filter = FORMAT_PCMA;
1486     else if ( ilbc == 1 )
1487         app->filter = FORMAT_ILBC;
1488     else if ( pcm == 1 )
1489         app->filter = FORMAT_WAV;
1490     else
1491         app->filter = -1;
1492   
1493     ULOG_INFO("filter type=%d", app->filter);
1494 }
1495
1496 static void cbItemClose(GtkWidget *widget, gpointer data)
1497 {
1498     g_assert(data);
1499
1500     if (!closeFile(data))
1501         return;
1502
1503     gtk_main_quit();
1504 }
1505
1506 /* Create the menu items needed for the main view */
1507 static void createMenu( AppData *data )
1508 {
1509     /* Create needed variables */
1510     GSList *group = NULL;
1511     GtkMenu *main_menu;
1512     GtkWidget *menu_file;
1513     GtkWidget *menu_others;
1514     GtkWidget *item_file;
1515     GtkWidget *item_file_open;
1516     GtkWidget *item_file_save_as;
1517     GtkWidget *item_others;
1518     GtkWidget *item_pcma;
1519     GtkWidget *item_pcmu;
1520     GtkWidget *item_ilbc;
1521     GtkWidget *item_pcm;
1522     GtkWidget *item_settings;
1523     /*
1524     GtkWidget *item_radio_type1;
1525     */
1526     GtkWidget *item_close;
1527     GtkWidget *item_separator;
1528
1529     /* Get the menu from view */
1530     main_menu = GTK_MENU(gtk_menu_new());
1531     hildon_window_set_menu(data->mainView, main_menu);
1532     
1533     /* Create new submenu for "Others" */
1534     menu_file = gtk_menu_new ();
1535     menu_others = gtk_menu_new ();
1536
1537     /* Create menu items */
1538     item_file = gtk_menu_item_new_with_label (_("File"));
1539     item_file_open = gtk_menu_item_new_with_label(_("Open..."));
1540     item_file_save_as = gtk_menu_item_new_with_label(_("Save as..."));
1541     item_others = gtk_menu_item_new_with_label (_("Recording format"));
1542     item_settings = gtk_menu_item_new_with_label (_("Settings"));
1543     
1544     item_pcma = gtk_radio_menu_item_new_with_label(
1545         group, "PCM A-law");
1546     item_ilbc = gtk_radio_menu_item_new_with_label_from_widget(
1547         GTK_RADIO_MENU_ITEM(item_pcma), "iLBC");
1548     item_pcmu = gtk_radio_menu_item_new_with_label_from_widget(
1549         GTK_RADIO_MENU_ITEM(item_pcma), "PCM u-law");
1550     item_pcm = gtk_radio_menu_item_new_with_label_from_widget(
1551         GTK_RADIO_MENU_ITEM(item_pcma), "WAV");
1552     
1553     data->filter = get_default_filter();
1554
1555     if (data->filter == FORMAT_ILBC)
1556         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_ilbc), TRUE);
1557     else if (data->filter == FORMAT_WAV)
1558         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_pcm), TRUE);
1559     else if (data->filter == FORMAT_PCMA)
1560         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_pcma), TRUE);
1561     else 
1562     {
1563         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_ilbc), TRUE);
1564         data->filter = FORMAT_ILBC;
1565     }
1566     
1567     data->radio_pcma = item_pcma;
1568     data->radio_ilbc = item_ilbc;
1569     data->radio_pcm = item_pcm;        
1570     /*
1571     data->radiotype = item_radio_type1;
1572     */
1573     item_close = gtk_menu_item_new_with_label(_("Close"));
1574     item_separator = gtk_separator_menu_item_new();
1575     
1576     /* Add menu items to right menus */
1577     gtk_menu_append( main_menu, item_file );
1578     gtk_menu_append( menu_file, item_file_open );
1579     gtk_menu_append( menu_file, item_file_save_as );
1580     gtk_menu_append( main_menu, item_others );
1581     gtk_menu_append( menu_others, item_pcm );
1582     gtk_menu_append( menu_others, item_pcma );
1583     gtk_menu_append( menu_others, item_ilbc);
1584     
1585     gtk_menu_append( main_menu, item_settings );
1586     
1587     gtk_menu_append( main_menu, item_close );
1588
1589     /* Add others submenu to the "Others" item */
1590     gtk_menu_item_set_submenu(
1591         GTK_MENU_ITEM(item_file), menu_file );
1592     gtk_menu_item_set_submenu(
1593         GTK_MENU_ITEM(item_others), menu_others );
1594
1595     /* Attach the callback functions to the activate signal */
1596     g_signal_connect( G_OBJECT( item_file_open), "activate",
1597         GTK_SIGNAL_FUNC (cbOpen), data);
1598     g_signal_connect( G_OBJECT( item_file_save_as), "activate",
1599         GTK_SIGNAL_FUNC (cbSaveAs), data);
1600     g_signal_connect( G_OBJECT( item_settings ), "activate",
1601         GTK_SIGNAL_FUNC (cbSettings), data);
1602     
1603     g_signal_connect( G_OBJECT( item_close ), "activate",
1604         GTK_SIGNAL_FUNC (cbItemClose), data);
1605
1606     g_signal_connect_swapped(G_OBJECT(item_pcma), "activate", G_CALLBACK(cbItemGroupChanged), data);
1607     g_signal_connect_swapped(G_OBJECT(item_pcm), "activate", G_CALLBACK(cbItemGroupChanged), data);
1608     
1609     /* Make all menu widgets visible */
1610
1611     gtk_widget_show_all( GTK_WIDGET( main_menu ) );
1612 }
1613
1614 gboolean maemo_recorder_ui_new(AppData *data)
1615 {
1616     HildonProgram *app = NULL;
1617     HildonWindow *window = NULL;
1618     GtkWidget *hbox = NULL;
1619     GtkWidget *vbox = NULL;
1620     GtkWidget *label = NULL;
1621     GtkWidget *entry1 = NULL;
1622     GtkWidget *entry2 = NULL;
1623     GtkWidget *entry3 = NULL;
1624     GtkWidget *toolBar = NULL;
1625     GtkWidget *table = NULL;
1626     GtkWidget *scale = NULL;
1627     GtkObject *adjustment = NULL;
1628
1629     g_assert(NULL != data);
1630
1631     app = HILDON_PROGRAM(hildon_program_get_instance());
1632     g_set_application_name(RECORDER_APP_TITLE);
1633      
1634     /* main window */
1635     window = HILDON_WINDOW(hildon_window_new());
1636
1637     hildon_program_add_window(app, window);
1638
1639     /* content for main view */
1640
1641     /* create vbox, divides control area and view area */
1642     vbox = gtk_vbox_new(FALSE, 0);
1643
1644     /* create hbox to divide control area */
1645     hbox = gtk_hbox_new(FALSE, HILDON_MARGIN_DEFAULT);
1646
1647     /* create toolbar */
1648     toolBar = createToolBar(data);
1649
1650     /* create table for labels */
1651     table = gtk_table_new (4, 3, FALSE);
1652     gtk_table_set_homogeneous(GTK_TABLE(table), FALSE);
1653
1654     gtk_table_set_row_spacings (GTK_TABLE (table), 4);
1655     gtk_table_set_col_spacings (GTK_TABLE (table), 0);
1656
1657     label = gtk_label_new_with_mnemonic(_("Filename:"));
1658     gtk_table_attach_defaults (GTK_TABLE (table),
1659             label,
1660             0, 1, 0, 1);
1661
1662     entry1 = gtk_entry_new ();
1663     gtk_entry_set_has_frame(GTK_ENTRY(entry1), FALSE);
1664     gtk_entry_set_text (GTK_ENTRY (entry1), _(RECORDER_FILE_UNTITLED));
1665     gtk_table_attach_defaults (GTK_TABLE (table), entry1, 1, 3, 0, 1);
1666     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry1);
1667
1668     label = gtk_label_new_with_mnemonic (_("Length:"));
1669     gtk_table_attach_defaults (GTK_TABLE (table),
1670             label,
1671             0, 1, 1, 2);
1672
1673     entry2 = gtk_entry_new ();
1674     gtk_entry_set_has_frame(GTK_ENTRY(entry2), FALSE);
1675     gtk_entry_set_text (GTK_ENTRY (entry2), "0:00.00");
1676     gtk_table_attach_defaults (GTK_TABLE (table), entry2, 1, 3, 1, 2);
1677     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry2);
1678
1679     label = gtk_label_new_with_mnemonic (_("State:"));
1680     gtk_table_attach_defaults (GTK_TABLE (table),
1681             label,
1682             0, 1, 2, 3);
1683
1684     entry3 = gtk_entry_new ();
1685     gtk_entry_set_has_frame(GTK_ENTRY(entry3), FALSE);
1686     gtk_entry_set_text (GTK_ENTRY (entry3), RECORDER_MSG_READY);
1687     gtk_table_attach_defaults (GTK_TABLE (table), entry3, 1, 3, 2, 3);
1688     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry3);
1689
1690     adjustment = gtk_adjustment_new (0.00,
1691                   0.00,
1692                   100.00,
1693                   0.01,
1694                   0.01,
1695                   0);
1696
1697     scale = gtk_hscale_new(GTK_ADJUSTMENT(adjustment));
1698       
1699 /*     gtk_table_attach_defaults (GTK_TABLE (table),
1700                 label,
1701                 0, 3, 2, 3);
1702 */
1703     /* connect signals */
1704     g_signal_connect(G_OBJECT(adjustment), "value-changed", G_CALLBACK(cbUserSeek), data);
1705     g_signal_connect(G_OBJECT(scale), "format-value", G_CALLBACK(cbFormatSeekbarValue), data);
1706     g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(cbDestroy), data);
1707
1708     /* packing the view */
1709     gtk_container_add (GTK_CONTAINER(window), vbox);
1710     gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, TRUE, 0);
1711     gtk_box_pack_start (GTK_BOX(vbox), scale, FALSE, FALSE, 0);
1712 /*    gtk_box_pack_start (GTK_BOX(vbox), hbox, TRUE, TRUE, 0); */
1713     
1714     hildon_window_add_toolbar(window, GTK_TOOLBAR(toolBar)); 
1715
1716     /* initialise the ui */
1717     gtk_entry_set_editable(GTK_ENTRY(entry1), FALSE);
1718     gtk_entry_set_editable(GTK_ENTRY(entry2), FALSE);
1719     gtk_entry_set_editable(GTK_ENTRY(entry3), FALSE);
1720
1721     /* store needed widgets */
1722     data->app = app;
1723     data->mainView = window;
1724     data->mainViewData.toolBar = GTK_WIDGET(toolBar);
1725     data->mainViewData.fileNameEntry = GTK_WIDGET(entry1);
1726     data->mainViewData.lengthEntry = GTK_WIDGET(entry2);
1727     data->mainViewData.stateEntry = GTK_WIDGET(entry3);
1728     data->mainViewData.adjustment = GTK_OBJECT(adjustment);
1729
1730     /* show the app */
1731     gtk_widget_show_all(GTK_WIDGET(window));
1732
1733     createMenu(data);
1734     
1735     return TRUE;
1736 }
1737
1738 void 
1739 maemo_recorder_mime_open(gpointer user_data, gint argc, gchar **argv)
1740 {
1741     AppData *data;
1742
1743     ULOG_DEBUG("%s with %d arguments", __FUNCTION__, argc);
1744
1745     if (argc == 0)
1746         return;
1747
1748     g_assert(user_data);
1749     data = (AppData *) user_data;
1750
1751     if (argv[0] != NULL)
1752     {
1753         ULOG_DEBUG("request to open %s", argv[0]);
1754         g_free(data->mimeURI);
1755         data->mimeURI = g_strdup(argv[0]);
1756         g_idle_add(openURI, (gpointer) data); 
1757         gtk_window_present(GTK_WINDOW(data->mainView));
1758     }
1759 }
1760
1761 static void seekToTime(GstElement *pipeline, gdouble secs)
1762 {
1763     g_assert(NULL != pipeline);
1764     ULOG_DEBUG("Seeking to: %.2f", secs);
1765
1766     /* time must be nanoseconds */
1767     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1768             GST_SEEK_TYPE_SET, (gint64) (secs * GST_SECOND),
1769             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1770     {
1771         ULOG_WARN("seekToTime failed!");
1772         return;
1773     }
1774     ULOG_DEBUG("seekToTime succeeded");
1775 }
1776
1777 static gboolean seekToZero(AppData *data, GstElement *pipeline)
1778 {
1779     gint plType;
1780     g_assert(NULL != pipeline);
1781     ULOG_DEBUG("Seeking to zero");
1782
1783     /* time must be nanoseconds */
1784     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1785             GST_SEEK_TYPE_SET, (gint64) 0,
1786             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1787     {
1788         ULOG_ERR("seekToZero failed! Trying to destroy and re-create pipeline");
1789         plType = data->playPipelineType;
1790
1791         /* gst_element_set_state(pipeline, GST_STATE_READY); */
1792         destroyPipeline(data, plType);
1793         return createPipeline(data, plType);
1794     }
1795
1796     ULOG_DEBUG("seekToZero succeeded");
1797     return TRUE;
1798 }
1799
1800 static void setLength(AppData *data, gdouble secs)
1801 {
1802     guint mins = 0;
1803     gchar *tmp;
1804
1805     if (secs < 0.0)
1806         return;
1807
1808     if (secs > 0)
1809     {
1810         g_object_set(G_OBJECT(data->mainViewData.adjustment), 
1811             "upper", secs, 
1812             NULL);    
1813         gtk_adjustment_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1814     }
1815
1816     if (secs >= 60.0)
1817     {
1818         mins = secs / 60;
1819         secs -= mins * 60.0;
1820     }
1821
1822     tmp = g_strdup_printf("%u:%05.2f", mins, secs);
1823
1824     /*
1825     ULOG_INFO("Setting length to %s", tmp);
1826     */
1827     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry), 
1828                  tmp);
1829     g_free(tmp);
1830 }
1831
1832 static gdouble guessMediaLength(AppData *data)
1833 {
1834     GnomeVFSFileSize size = 0;
1835     gdouble bitrate = 0.0;
1836     gdouble len = -1.0;
1837
1838     if (data->openFileName)
1839         size = getFileLength(data->openFileName);
1840     else
1841         return -1.0;
1842            
1843     if (size == 0)
1844         return -1.0;
1845
1846     ULOG_DEBUG("file size: %llu bytes", size);
1847
1848     switch (data->file_format)
1849     {
1850         case FORMAT_ILBC:
1851             bitrate = ILBC_BITRATE_20;
1852             break; 
1853             
1854         case FORMAT_PCMA:
1855         case FORMAT_PCMU:
1856             bitrate = PCMA_BITRATE;
1857             break;
1858
1859         default:
1860             return -1.0;
1861     }
1862     if (bitrate == 0.0)
1863         return -1.0;
1864
1865     len = (((gdouble) size) * 8.0) / (bitrate);
1866     ULOG_DEBUG("guessed media length: %.2f secs", len);
1867
1868     return len;
1869 }
1870
1871 static GstCaps *createCapsFilter(AudioFormat format)
1872 {
1873     switch (format)
1874     {
1875         case FORMAT_ILBC:
1876             return gst_caps_new_simple(
1877                 GST_TYPE_ILBC,
1878                 "rate", G_TYPE_INT, ILBC_RATE,
1879                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1880                 "mode", G_TYPE_INT, 20, /* 20 ms frames */
1881                 NULL);
1882         case FORMAT_PCMA:
1883             return gst_caps_new_simple(
1884                 GST_TYPE_PCMA,
1885                 "rate", G_TYPE_INT, DEFAULT_RATE,
1886                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1887                 NULL);
1888         case FORMAT_PCMU:
1889             return gst_caps_new_simple(
1890                 GST_TYPE_PCMU,
1891                 "rate", G_TYPE_INT, DEFAULT_RATE,
1892                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1893                 NULL);
1894         case FORMAT_WAV:
1895         case FORMAT_PCM:
1896             return gst_caps_new_simple(
1897                 GST_TYPE_PCM,
1898                 "rate", G_TYPE_INT, PCM_RATE,
1899                 "signed", G_TYPE_BOOLEAN, TRUE,
1900                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1901                 "endianness", G_TYPE_INT, PCM_ENDIANNESS,
1902                 "width", G_TYPE_INT, PCM_WIDTH,
1903                 "depth", G_TYPE_INT, PCM_DEPTH,
1904                 NULL);
1905         default:
1906             ULOG_WARN("%s(): creating ANY caps", G_STRFUNC);
1907             return gst_caps_new_any();
1908     }
1909 }
1910
1911 static gboolean cbStopPlayback(AppData *data)
1912 {
1913     gint ret;
1914     ULOG_INFO("Stopping playback");
1915    
1916     g_assert(data != NULL);
1917
1918     ret = gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1919                         GST_STATE_PAUSED);
1920     if (seekToZero(data, GST_ELEMENT(data->playPipeline)))
1921     {
1922         gtk_adjustment_set_value( 
1923            GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1924         gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1925     }
1926     setAppState(data, APPSTATE_READY);
1927     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1928     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1929
1930     return FALSE;
1931 }
1932
1933 static void cbUserSeek(GtkAdjustment *adjustment, gpointer data)
1934 {   
1935     /*ULOG_INFO("cbUserSeek");*/
1936     AppData *app;
1937
1938     g_return_if_fail(data != NULL);
1939     app = (AppData *) data;
1940
1941     if (getAppState(app) != APPSTATE_READY || NULL == app->playPipeline)
1942         return;
1943
1944     seekToTime(app->playPipeline, gtk_adjustment_get_value(adjustment));
1945 }
1946
1947 static gchar *cbFormatSeekbarValue(GtkScale *scale, gdouble value)
1948 {
1949 /*    ULOG_INFO("cbFormatSeekbarValue");*/
1950     gint mins = 0;
1951     gint digits = gtk_scale_get_digits(scale);
1952
1953     if (value >= 60.0)
1954     {
1955         mins = value / 60;
1956         value -= mins * 60.0;
1957         return g_strdup_printf("%d:%0*.*f", mins, digits + 3, digits, value);
1958     }
1959     /* mins:sec.frac */
1960     return g_strdup_printf("%0.*f", digits, value);
1961 }
1962
1963 static gboolean cbUpdateRecLength(AppData *data)
1964 {
1965     struct timeval tv;
1966     guint mins = 0;
1967     gdouble secs;
1968     gchar *tmp;
1969
1970     if (gettimeofday(&tv, NULL) != 0)
1971         return FALSE;
1972
1973     secs = tv.tv_sec - data->recStartTv.tv_sec;
1974     secs += ((tv.tv_usec - data->recStartTv.tv_usec) / 1000000.0);
1975
1976     if (secs >= 60.0)
1977     {
1978         mins = secs / 60;
1979         secs -= mins * 60.0;
1980         tmp = g_strdup_printf("%u:%05.2f", mins, secs);
1981     }
1982     else
1983         tmp = g_strdup_printf("%0.2f", secs);
1984
1985     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry), 
1986                  tmp);
1987     g_free(tmp);
1988
1989     if (getAppState(data) == APPSTATE_RECORDING)
1990         return TRUE;
1991
1992     data->recUpdateId = 0;
1993     return FALSE;
1994 }
1995