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