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