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