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