fremantle port
[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 "Maemo 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_STATE_CHANGED: 
589         {
590             GstState old;
591             GstState new;
592             GstState pending;
593
594             gst_message_parse_state_changed(message, &old, &new, &pending);
595
596             pipelineStateChanged(NULL, old, new, pending, app);
597
598             break;
599         }
600
601         default:
602             /* unhandled message */
603             ULOG_WARN("%s() - Unhandled message, type = %d", G_STRFUNC, message->type);
604             break;
605     }
606
607     /* remove message from the queue */
608     return TRUE;
609 }
610
611 static void cbDestroy(GtkWidget* widget, GdkEvent *event, gpointer data)
612 {     
613     AppData* app;
614
615     g_assert(data);
616
617     app = (AppData *) data;
618
619     ULOG_DEBUG("delete_event");
620     if (!closeFile(app))
621         return;
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             _("Cancel"), GTK_RESPONSE_CANCEL,
955             NULL);
956
957     i = gtk_dialog_run(GTK_DIALOG(note));
958     gtk_widget_destroy(note);
959
960     switch (i)
961     {
962         case GTK_RESPONSE_CANCEL:
963             return FALSE;
964
965         case GTK_RESPONSE_NO:
966             return TRUE;
967
968         case GTK_RESPONSE_YES:
969         {
970             cbSaveAs(NULL, data);
971             return data->saved;
972         }
973         default:
974             ULOG_WARN("%s(): unknown response from dialog: %d", G_STRFUNC, i);
975     }
976     return FALSE;
977 }
978
979 static const gchar *
980 getFileName(AppData *data)
981 {
982     g_assert(data);
983     return gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
984
985 }
986
987 static void cbSettings(GtkWidget* widget, AppData *data)
988 {
989     settings_edit( widget, data );
990 }
991
992 static void cbEmailing(GtkWidget* widget, AppData *data)
993 {
994
995     gboolean result;
996     GSList *list = NULL;    
997     gchar *file = NULL;
998
999     g_assert(NULL != data);
1000     
1001     if (g_file_test(getFileName(data), G_FILE_TEST_EXISTS))
1002     {
1003         file = file2uri(getFileName(data));
1004         ULOG_INFO("Emailing: %s", file);
1005         list = g_slist_append(list, file);
1006         result = libmodest_dbus_client_compose_mail(data->osso,
1007                 NULL, /*to*/
1008                 NULL, /*cc*/
1009                 NULL, /*bcc*/
1010                 NULL, /*body*/
1011                 NULL, /*subj*/
1012                 list);
1013         if (!result)
1014             hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Emailing failed"));
1015
1016         g_slist_free(list);
1017         g_free(file);
1018     }
1019 }
1020
1021 static void cbSaveAs(GtkWidget* widget, AppData *data) 
1022 {
1023     GtkWidget* dialog = NULL;
1024     const gchar *current;
1025     gchar *selected = NULL;
1026
1027     g_assert(NULL != data);
1028
1029     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1030
1031     current = getFileName(data);
1032     if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0) 
1033     {
1034         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
1035         return;
1036     }
1037
1038     /* create dialog */
1039     dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
1040               GTK_WINDOW(data->mainView), 
1041               GTK_FILE_CHOOSER_ACTION_SAVE));
1042
1043     gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), 
1044             get_default_dir() );
1045
1046     /* show it */
1047     gtk_widget_show_all(dialog);
1048
1049     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
1050     {
1051         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1052     }
1053
1054     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
1055     gtk_widget_destroy(dialog);
1056
1057     if (NULL == selected)
1058         return;
1059
1060     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
1061
1062     g_free(data->saveFileName);
1063     data->saveFileName = NULL;
1064
1065     if (saveFile(selected, data->openFileName, data->file_format, &(data->saveFileName)))
1066     {
1067         gchar *basename;
1068         const gchar *ext;
1069
1070         g_assert(data->saveFileName);
1071
1072         /* set new title that has the file name */
1073         basename = g_path_get_basename(data->saveFileName);
1074         ULOG_DEBUG("%s() - file '%s' succesfully saved!", G_STRFUNC, data->saveFileName);
1075
1076         /* Houston, we have a kludge:
1077          * for AU files we need to keep the old tmpfile for playback
1078          * for RAW/iLBC files, we can remove the tmpfile and point openFileName to the saved file
1079          */
1080         ext = getExtension(data->file_format);
1081         if (strcmp(ext, EXTENSION_AU) != 0)
1082         {
1083             g_free(data->openFileName);
1084             data->openFileName = g_strdup(data->saveFileName);
1085         }
1086
1087         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1088                  data->saveFileName);
1089         
1090         g_free(basename);
1091         data->saved = TRUE;
1092     }
1093     else
1094     {
1095         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Saving file failed!"));
1096     }
1097
1098     g_free(selected);
1099     selected = NULL;
1100
1101     ULOG_DEBUG("%s() - end", G_STRFUNC);
1102 }
1103
1104 static void cbRec(GtkWidget* widget, AppData *data) 
1105 {
1106     g_assert(NULL != data);
1107
1108     ULOG_DEBUG("%s() - begin", G_STRFUNC);     
1109
1110     if (APPSTATE_READY != getAppState(data))
1111     {
1112         ULOG_WARN("%s() - state different than READY -> return", G_STRFUNC);
1113         return;
1114     }
1115
1116     if (!closeFile(data))
1117      return;
1118
1119     /* clear filenames, use tmp file */
1120     g_free(data->openFileName);
1121     data->openFileName = NULL;
1122
1123     g_free(data->saveFileName);
1124     data->saveFileName = NULL;     
1125
1126     switch (data->filter)
1127     {
1128         case FORMAT_PCM:
1129              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_FILE);
1130              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_FILE);
1131              break;
1132
1133          case FORMAT_PCMA:
1134              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMA_FILE);
1135              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMA_FILE);
1136              break;
1137              
1138          case FORMAT_PCMU:
1139              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMU_FILE);
1140              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_PCMU_FILE);
1141              break;
1142
1143          case FORMAT_WAV:
1144              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_WAV_FILE);
1145              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_WAV_FILE);
1146              break;
1147
1148          case FORMAT_ILBC:
1149          default:
1150              data->saveFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_ILBC_FILE);
1151              data->openFileName = g_strdup_printf("%s/%s", get_default_dir(), DEFAULT_TMP_ILBC_FILE);
1152             break;
1153     }
1154
1155     ULOG_INFO("%s() - creating pipelines", G_STRFUNC);
1156     /* start recording */
1157     /* create related pipelines */
1158     if (createPipeline(data, PIPELINE_REC))
1159     {
1160         ULOG_INFO("%s() - starting recording", G_STRFUNC);
1161         /* start recording */
1162         gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1163                            GST_STATE_PLAYING);
1164
1165         /* update display */
1166         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1167             data->saveFileName);
1168
1169         setAppState(data, APPSTATE_RECORDING);
1170         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1171         data->file_format = data->filter;
1172         setFormatString(data, data->file_format);
1173     }
1174     else
1175     {
1176         ULOG_ERR("Could not create rec pipeline!");
1177         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline"));
1178         setAppState(data, APPSTATE_READY);
1179     }
1180  
1181     ULOG_DEBUG("%s() - end", G_STRFUNC);     
1182 }
1183
1184 static void cbPlay(GtkWidget* widget, AppData *data) 
1185 {
1186     const gchar * file = NULL;
1187      
1188     g_assert(NULL != data);
1189
1190     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1191
1192     file = getFileName(data);
1193     if (NULL == data->openFileName || NULL == file || strcmp(file, RECORDER_FILE_UNTITLED) == 0) 
1194     {
1195         ULOG_WARN("%s() - nothing to play", G_STRFUNC);
1196         return;
1197     }
1198
1199     openPlayPipeline(data);
1200      
1201     if (APPSTATE_PLAYING == getAppState(data))
1202     {
1203          if (GST_IS_ELEMENT(data->playPipeline)) 
1204          {
1205              gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_PAUSED);
1206              setAppState(data, APPSTATE_READY);
1207          }
1208          return;
1209     }
1210
1211     if (APPSTATE_READY != getAppState(data))
1212     {
1213         ULOG_WARN("%s() - state different than PLAYING or READY -> return", G_STRFUNC);
1214         return;
1215     }
1216
1217     ULOG_INFO("filename %s", file);
1218      
1219     if (! GST_IS_ELEMENT(data->playPipeline))
1220     {
1221         ULOG_WARN("%s() - playPipeline does not exist", G_STRFUNC);
1222         return;
1223     }
1224
1225     gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1226                GST_STATE_PLAYING);
1227
1228     setAppState(data, APPSTATE_PLAYING);
1229
1230     g_timeout_add(PLAY_UPDATE_INTERVAL, (GSourceFunc)cbCheckPosition, data);
1231
1232     ULOG_DEBUG("%s() - end", G_STRFUNC);
1233 }
1234
1235 static void cbStop(GtkWidget* widget, AppData *data) 
1236 {
1237     g_assert(NULL != data);
1238
1239     ULOG_DEBUG("%s() - begin", G_STRFUNC); 
1240
1241     /* check if we are playing/recording */
1242
1243     /* stop playing or recording */
1244     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1245     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1246     gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
1247
1248     /* destroy related pipeline */
1249     switch(getAppState(data))
1250     {
1251         case APPSTATE_PLAYING:
1252             /* don't destroy the playing pipeline. Instead, set the pipeline to PAUSED */
1253             /* destroyPipeline(data, PIPELINE_PLAY); */
1254             ULOG_INFO("%s() - Setting playPipeline state to PAUSED", G_STRFUNC);
1255             gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1256                      GST_STATE_PAUSED);
1257             /* flow through */
1258         case APPSTATE_READY:
1259             /* seek to zero, but not for PCM pipeline */
1260             /* if (data->playPipelineType == PIPELINE_PLAY || seekToZero(data, GST_ELEMENT(data->playPipeline))) */
1261             if ( !GST_IS_ELEMENT(data->playPipeline) || seekToZero(data, GST_ELEMENT(data->playPipeline)))
1262             {
1263               gtk_adjustment_set_value( 
1264                    GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1265               gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1266               gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
1267                          RECORDER_MSG_STOPPED);  
1268             }
1269             break;
1270
1271         case APPSTATE_RECORDING:
1272         {
1273             gdouble len = -1.0;
1274             gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1275                  GST_STATE_PAUSED);
1276             destroyPipeline(data, PIPELINE_REC);
1277             gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1278             data->saved = FALSE;
1279
1280             len = guessMediaLength(data);
1281             if (len > 0.0)
1282                 setLength(data, len);
1283
1284             break;
1285         }
1286      
1287         default:
1288             /* seekToZero(data, GST_ELEMENT(data->playPipeline)); */
1289             /* should not come here */
1290             break;
1291     }
1292
1293     setAppState(data, APPSTATE_READY);
1294
1295     ULOG_DEBUG("%s() - end", G_STRFUNC); 
1296 }
1297
1298
1299 /* ui construction functions */
1300
1301 static GtkWidget* createToolBar(AppData *data)
1302 {
1303      GtkToolbar* toolBar = NULL;
1304      
1305      GtkToolItem* new = NULL;
1306      GtkToolItem* open = NULL;
1307      GtkToolItem* save = NULL;
1308      GtkToolItem* saveas = NULL;
1309      GtkToolItem* sep = NULL;
1310      GtkToolItem* play = NULL;
1311      GtkToolItem* rec = NULL;
1312      GtkToolItem* stop = NULL;
1313      
1314      /* create buttons */
1315      new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); 
1316      open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); 
1317      save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
1318      data->buttonSave = GTK_WIDGET(save);
1319      saveas = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE_AS);
1320      data->buttonSaveAs = GTK_WIDGET(saveas);
1321      gtk_widget_set_sensitive(data->buttonSave, FALSE);
1322      gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
1323      data->saved = TRUE;
1324
1325      gtk_tool_item_set_expand( GTK_TOOL_ITEM(new), TRUE );
1326      gtk_tool_item_set_expand( GTK_TOOL_ITEM(open), TRUE );
1327      gtk_tool_item_set_expand( GTK_TOOL_ITEM(saveas), TRUE );
1328
1329      rec = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_RECORD); 
1330      data->buttonRec = GTK_WIDGET(rec);
1331      play = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY); 
1332      data->buttonPlay = GTK_WIDGET(play);
1333      stop = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
1334      
1335      gtk_tool_item_set_expand( GTK_TOOL_ITEM(rec), TRUE );
1336      gtk_tool_item_set_expand( GTK_TOOL_ITEM(play), TRUE );
1337      gtk_tool_item_set_expand( GTK_TOOL_ITEM(stop), TRUE );
1338
1339      /* create separator */
1340      sep  = gtk_separator_tool_item_new();
1341
1342      /* create the toolbar itself */
1343      toolBar = GTK_TOOLBAR(gtk_toolbar_new());
1344
1345      /* add items to toolbar */
1346      gtk_toolbar_insert(toolBar, new, -1);
1347      gtk_toolbar_insert(toolBar, open, -1);
1348      gtk_toolbar_insert(toolBar, saveas, -1);
1349      gtk_toolbar_insert(toolBar, sep,  -1);
1350      gtk_toolbar_insert(toolBar, rec, -1);
1351      gtk_toolbar_insert(toolBar, play, -1);
1352      gtk_toolbar_insert(toolBar, stop, -1);
1353
1354      /* connect signals */
1355      g_signal_connect(G_OBJECT(new), "clicked",
1356               G_CALLBACK(cbNew), 
1357               data);
1358      g_signal_connect(G_OBJECT(open), "clicked",
1359               G_CALLBACK(cbOpen), 
1360               data);
1361      g_signal_connect(G_OBJECT(saveas), "clicked",
1362               G_CALLBACK(cbSaveAs), 
1363               data);
1364      g_signal_connect(G_OBJECT(rec), "clicked",
1365               G_CALLBACK(cbRec), 
1366               data);
1367      g_signal_connect(G_OBJECT(play), "clicked",
1368               G_CALLBACK(cbPlay), 
1369               data);
1370      g_signal_connect(G_OBJECT(stop), "clicked",
1371               G_CALLBACK(cbStop), 
1372               data);
1373
1374      return GTK_WIDGET(toolBar);
1375
1376 }
1377
1378 static void cbItemGroupChanged( gpointer data )
1379 {
1380     AppData* app = (AppData* ) data;
1381     GValue active ={G_TYPE_INVALID};
1382     gint pcma,ilbc,pcm;
1383     
1384     g_value_init(&active, G_TYPE_INT);
1385
1386     g_object_get_property(G_OBJECT(app->radio_pcma), "active", &active);
1387     pcma = g_value_get_int(&active);
1388     g_object_get_property(G_OBJECT(app->radio_ilbc), "active", &active);
1389     ilbc = g_value_get_int(&active);
1390     g_object_get_property(G_OBJECT(app->radio_pcm), "active", &active);
1391     pcm = g_value_get_int(&active);
1392     
1393     ULOG_INFO("change type pcma=%d ilbc=%d pcm=%d",pcma, ilbc, pcm);
1394     if ( pcma == 1 )
1395         app->filter = FORMAT_PCMA;
1396     else if ( ilbc == 1 )
1397         app->filter = FORMAT_ILBC;
1398     else if ( pcm == 1 )
1399         app->filter = FORMAT_WAV;
1400     else
1401         app->filter = -1;
1402   
1403     ULOG_INFO("filter type=%d", app->filter);
1404 }
1405
1406 static void cbItemClose(GtkWidget *widget, gpointer data)
1407 {
1408     g_assert(data);
1409
1410     if (!closeFile(data))
1411         return;
1412
1413     gtk_main_quit();
1414 }
1415
1416 /* Create the menu items needed for the main view */
1417 static void createMenu( AppData *data )
1418 {
1419     HildonAppMenu *menu;
1420     GtkWidget *button_email;
1421
1422     menu = HILDON_APP_MENU( hildon_app_menu_new() );
1423     button_email = hildon_gtk_button_new(HILDON_SIZE_AUTO);
1424     gtk_button_set_label( GTK_BUTTON(button_email), "Send via email");
1425     hildon_app_menu_append( menu, GTK_BUTTON(button_email));
1426    
1427     g_signal_connect( G_OBJECT( button_email ), "clicked",
1428         GTK_SIGNAL_FUNC (cbEmailing), data);
1429
1430     
1431     gtk_widget_show_all( GTK_WIDGET(menu));
1432
1433     hildon_window_set_app_menu(HILDON_WINDOW(data->mainView), menu);
1434
1435     data->filter = get_default_filter();
1436
1437 }
1438
1439 gboolean
1440 evKeypress(GtkWidget *widget, GdkEventKey *ev, AppData *appdata)
1441 {
1442
1443   switch (ev->keyval)
1444   {
1445     case GDK_Return:
1446       cbRec(widget, appdata);
1447       return TRUE;
1448     case GDK_Right:
1449       cbPlay(widget, appdata);
1450       return TRUE;
1451     case GDK_Escape:
1452       cbStop(widget, appdata);
1453       return TRUE;
1454     default:
1455       break;
1456   }
1457
1458   return FALSE;
1459 }
1460
1461
1462 gboolean maemo_recorder_ui_new(AppData *data)
1463 {
1464     HildonProgram *app = NULL;
1465     HildonWindow *window = NULL;
1466     GtkWidget *hbox = NULL;
1467     GtkWidget *vbox = NULL;
1468     GtkWidget *label = NULL;
1469     GtkWidget *entry1 = NULL;
1470     GtkWidget *entry2 = NULL;
1471     GtkWidget *entry3 = NULL;
1472     GtkWidget *toolBar = NULL;
1473     GtkWidget *infohbox = NULL;
1474     GtkWidget *table = NULL;
1475     GtkWidget *scale = NULL;
1476     GtkObject *adjustment = NULL;
1477
1478     g_assert(NULL != data);
1479
1480     app = HILDON_PROGRAM(hildon_program_get_instance());
1481     g_set_application_name(RECORDER_APP_TITLE);
1482      
1483     /* main window */
1484     window = HILDON_WINDOW(hildon_window_new());
1485
1486     hildon_program_add_window(app, window);
1487
1488     /* content for main view */
1489
1490     /* create vbox, divides control area and view area */
1491     vbox = gtk_vbox_new(FALSE, 0);
1492
1493     /* create hbox to divide control area */
1494     hbox = gtk_hbox_new(FALSE, HILDON_MARGIN_DEFAULT);
1495
1496     /* create toolbar */
1497     toolBar = createToolBar(data);
1498
1499     /* create table for labels */
1500     table = gtk_table_new (4, 2, FALSE);
1501     gtk_table_set_homogeneous(GTK_TABLE(table), FALSE);
1502
1503     gtk_table_set_row_spacings (GTK_TABLE (table), 4);
1504     gtk_table_set_col_spacings (GTK_TABLE (table), HILDON_MARGIN_TRIPLE);
1505
1506     label = gtk_label_new_with_mnemonic(_("Filename:"));
1507     gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1508     gtk_table_attach_defaults (GTK_TABLE (table),
1509             label,
1510             0, 1, 0, 1);
1511
1512     entry1 = gtk_entry_new();
1513     gtk_entry_set_has_frame(GTK_ENTRY(entry1), FALSE);
1514     gtk_entry_set_text(GTK_ENTRY (entry1), _(RECORDER_FILE_UNTITLED));
1515     gtk_entry_set_editable(GTK_ENTRY(entry1), FALSE);
1516     gtk_table_attach_defaults(GTK_TABLE (table), entry1, 1, 2, 0, 1);
1517     gtk_label_set_mnemonic_widget(GTK_LABEL (label), entry1);
1518
1519     label = gtk_label_new_with_mnemonic (_("Length:"));
1520     gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1521     gtk_table_attach_defaults (GTK_TABLE (table),
1522             label,
1523             0, 1, 1, 2);
1524
1525     entry2 = gtk_entry_new ();
1526     gtk_entry_set_has_frame(GTK_ENTRY(entry2), FALSE);
1527     gtk_entry_set_text (GTK_ENTRY (entry2), "0:00.00");
1528     gtk_entry_set_editable(GTK_ENTRY(entry2), FALSE);
1529     gtk_table_attach_defaults (GTK_TABLE (table), entry2, 1, 2, 1, 2);
1530     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry2);
1531
1532     /* audio format field */
1533     label = gtk_label_new_with_mnemonic(_("Format:"));
1534     gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1535     gtk_table_attach_defaults (GTK_TABLE (table),
1536             label,
1537             0, 1, 2, 3);
1538
1539     entry3 = gtk_entry_new();
1540     gtk_entry_set_has_frame(GTK_ENTRY(entry3), FALSE);
1541     gtk_entry_set_width_chars(GTK_ENTRY(entry3), 40);
1542     gtk_entry_set_text (GTK_ENTRY (entry3), RECORDER_FMT_STRING_NONE);
1543     gtk_entry_set_editable(GTK_ENTRY(entry3), FALSE);
1544     data->mainViewData.formatEntry = GTK_WIDGET(entry3);
1545     
1546     gtk_table_attach_defaults (GTK_TABLE (table), entry3, 1, 2, 2, 3);
1547     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry3);
1548
1549     label = gtk_label_new_with_mnemonic(_("State:"));
1550     gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
1551     gtk_table_attach_defaults (GTK_TABLE (table),
1552             label,
1553             0, 1, 3, 4);
1554
1555     entry3 = gtk_entry_new ();
1556     gtk_entry_set_has_frame(GTK_ENTRY(entry3), FALSE);
1557     gtk_entry_set_text (GTK_ENTRY (entry3), RECORDER_MSG_READY);
1558     gtk_entry_set_editable(GTK_ENTRY(entry3), FALSE);
1559     gtk_table_attach_defaults (GTK_TABLE (table), entry3, 1, 2, 3, 4);
1560     gtk_label_set_mnemonic_widget(GTK_LABEL (label), entry3);
1561
1562     adjustment = gtk_adjustment_new (0.00,
1563                   0.00,
1564                   100.00,
1565                   0.01,
1566                   0.01,
1567                   0);
1568
1569     scale = gtk_hscale_new(GTK_ADJUSTMENT(adjustment));
1570       
1571 /*     gtk_table_attach_defaults (GTK_TABLE (table),
1572                 label,
1573                 0, 3, 2, 3);
1574 */
1575     /* connect signals */
1576     g_signal_connect(G_OBJECT(adjustment), "value-changed", G_CALLBACK(cbUserSeek), data);
1577     g_signal_connect(G_OBJECT(scale), "format-value", G_CALLBACK(cbFormatSeekbarValue), data);
1578     g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(cbDestroy), data);
1579     g_signal_connect(G_OBJECT(window), "key-press-event",
1580             G_CALLBACK(evKeypress), data);
1581
1582     /* packing the view */
1583     gtk_container_add (GTK_CONTAINER(window), vbox);
1584     infohbox = gtk_hbox_new(FALSE, 0);
1585     gtk_box_pack_start (GTK_BOX(infohbox), table, FALSE, TRUE, 0);
1586     gtk_box_pack_start (GTK_BOX(vbox), infohbox, FALSE, TRUE, 0);
1587     gtk_box_pack_start (GTK_BOX(vbox), scale, FALSE, FALSE, 0);
1588 /*    gtk_box_pack_start (GTK_BOX(vbox), hbox, TRUE, TRUE, 0); */
1589     
1590     hildon_window_add_toolbar(window, GTK_TOOLBAR(toolBar)); 
1591
1592     /* store needed widgets */
1593     data->app = app;
1594     data->mainView = window;
1595     data->mainViewData.toolBar = GTK_WIDGET(toolBar);
1596     data->mainViewData.fileNameEntry = GTK_WIDGET(entry1);
1597     data->mainViewData.lengthEntry = GTK_WIDGET(entry2);
1598     data->mainViewData.stateEntry = GTK_WIDGET(entry3);
1599     data->mainViewData.adjustment = GTK_OBJECT(adjustment);
1600
1601     /* show the app */
1602     gtk_widget_show_all(GTK_WIDGET(window));
1603
1604     createMenu(data);
1605     
1606     return TRUE;
1607 }
1608
1609 void 
1610 maemo_recorder_mime_open(gpointer user_data, gint argc, gchar **argv)
1611 {
1612     AppData *data;
1613
1614     ULOG_DEBUG("%s with %d arguments", __FUNCTION__, argc);
1615
1616     if (argc == 0)
1617         return;
1618
1619     g_assert(user_data);
1620     data = (AppData *) user_data;
1621
1622     if (argv[0] != NULL)
1623     {
1624         ULOG_DEBUG("request to open %s", argv[0]);
1625         g_free(data->mimeURI);
1626         data->mimeURI = g_strdup(argv[0]);
1627         g_idle_add(openURI, (gpointer) data); 
1628         gtk_window_present(GTK_WINDOW(data->mainView));
1629     }
1630 }
1631
1632 static void seekToTime(GstElement *pipeline, gdouble secs)
1633 {
1634     g_assert(NULL != pipeline);
1635     ULOG_DEBUG("Seeking to: %.2f", secs);
1636
1637     /* time must be nanoseconds */
1638     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1639             GST_SEEK_TYPE_SET, (gint64) (secs * GST_SECOND),
1640             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1641     {
1642         ULOG_WARN("seekToTime failed!");
1643         return;
1644     }
1645     ULOG_DEBUG("seekToTime succeeded");
1646 }
1647
1648 static gboolean seekToZero(AppData *data, GstElement *pipeline)
1649 {
1650     gint plType;
1651     g_assert(NULL != pipeline);
1652     ULOG_DEBUG("Seeking to zero");
1653
1654     /* time must be nanoseconds */
1655     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1656             GST_SEEK_TYPE_SET, (gint64) 0,
1657             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1658     {
1659         ULOG_ERR("seekToZero failed! Trying to destroy and re-create pipeline");
1660         plType = data->playPipelineType;
1661
1662         /* gst_element_set_state(pipeline, GST_STATE_READY); */
1663         destroyPipeline(data, plType);
1664         return createPipeline(data, plType);
1665     }
1666
1667     ULOG_DEBUG("seekToZero succeeded");
1668     return TRUE;
1669 }
1670
1671 static void
1672 setFormatString(AppData *data, AudioFormat afmt)
1673 {
1674     gchar *str;
1675     gchar *format;
1676
1677     /* these are pretty much always the same */
1678     gint channels = 1; 
1679     gint rate = DEFAULT_RATE; /* 8000 */
1680     gint bits = 8;
1681
1682     g_assert(data);
1683     g_assert(GTK_IS_ENTRY(data->mainViewData.formatEntry));
1684
1685     switch (afmt)
1686     {
1687         case FORMAT_PCMA:
1688             format = FORMAT_NAME_PCMA;
1689             break;
1690         case FORMAT_PCMU:
1691             format = FORMAT_NAME_PCMU;
1692             break;
1693         case FORMAT_ILBC:
1694             format = FORMAT_NAME_ILBC;
1695             rate = ILBC_RATE;
1696             break;
1697         /* TODO: we can play wavs with many sampling rates, 2 channels */
1698         /* we really should migrate to the better format spec */
1699         case FORMAT_WAV:
1700             format = FORMAT_NAME_WAV;
1701             bits = PCM_WIDTH;
1702             break;
1703         case FORMAT_PCM:
1704             format = FORMAT_NAME_PCM;
1705             bits = PCM_WIDTH;
1706             break;
1707         default:
1708             gtk_entry_set_text(GTK_ENTRY(data->mainViewData.formatEntry), RECORDER_FMT_STRING_NONE);
1709             return;
1710     }
1711
1712     str = g_strdup_printf("%s, %d %s, %d Hz, %d %s", format, channels, _("ch"), rate, bits, _("bits"));
1713     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.formatEntry), str);
1714     g_free(str);
1715 }
1716
1717 static void setLength(AppData *data, gdouble secs)
1718 {
1719     guint mins = 0;
1720     gchar *tmp;
1721
1722     if (secs < 0.0)
1723         return;
1724
1725     if (secs > 0)
1726     {
1727         g_object_set(G_OBJECT(data->mainViewData.adjustment), 
1728             "upper", secs, 
1729             NULL);    
1730         gtk_adjustment_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1731     }
1732
1733     if (secs >= 60.0)
1734     {
1735         mins = secs / 60;
1736         secs -= mins * 60.0;
1737     }
1738
1739     tmp = g_strdup_printf("%u:%05.2f", mins, secs);
1740
1741     /*
1742     ULOG_INFO("Setting length to %s", tmp);
1743     */
1744     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry), 
1745                  tmp);
1746     g_free(tmp);
1747 }
1748
1749 static gdouble guessMediaLength(AppData *data)
1750 {
1751     GnomeVFSFileSize size = 0;
1752     gdouble bitrate = 0.0;
1753     gdouble len = -1.0;
1754
1755     if (data->openFileName)
1756         size = getFileLength(data->openFileName);
1757     else
1758         return -1.0;
1759            
1760     if (size == 0)
1761         return -1.0;
1762
1763     ULOG_DEBUG("file size: %llu bytes", size);
1764
1765     switch (data->file_format)
1766     {
1767         case FORMAT_ILBC:
1768             bitrate = ILBC_BITRATE_30;
1769             break; 
1770             
1771         case FORMAT_PCMA:
1772         case FORMAT_PCMU:
1773             bitrate = PCMA_BITRATE;
1774             break;
1775
1776         default:
1777             return -1.0;
1778     }
1779
1780     if (bitrate == 0.0)
1781         return -1.0;
1782
1783     len = (((gdouble) size) * 8.0) / (bitrate);
1784     ULOG_DEBUG("guessed media length: %.2f secs", len);
1785
1786     return len;
1787 }
1788
1789 static GstCaps *createCapsFilter(AudioFormat format)
1790 {
1791     switch (format)
1792     {
1793         case FORMAT_ILBC:
1794             return gst_caps_new_simple(
1795                 GST_TYPE_ILBC,
1796                 "rate", G_TYPE_INT, ILBC_RATE,
1797                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1798                 "mode", G_TYPE_INT, 30, /* 30 ms frames */
1799                 NULL);
1800         case FORMAT_PCMA:
1801             return gst_caps_new_simple(
1802                 GST_TYPE_PCMA,
1803                 "rate", G_TYPE_INT, DEFAULT_RATE,
1804                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1805                 NULL);
1806         case FORMAT_PCMU:
1807             return gst_caps_new_simple(
1808                 GST_TYPE_PCMU,
1809                 "rate", G_TYPE_INT, DEFAULT_RATE,
1810                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1811                 NULL);
1812         case FORMAT_WAV:
1813         case FORMAT_PCM:
1814             return gst_caps_new_simple(
1815                 GST_TYPE_PCM,
1816                 "rate", G_TYPE_INT, PCM_RATE,
1817                 "signed", G_TYPE_BOOLEAN, TRUE,
1818                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1819                 "endianness", G_TYPE_INT, PCM_ENDIANNESS,
1820                 "width", G_TYPE_INT, PCM_WIDTH,
1821                 "depth", G_TYPE_INT, PCM_DEPTH,
1822                 NULL);
1823         default:
1824             ULOG_WARN("%s(): creating ANY caps", G_STRFUNC);
1825             return gst_caps_new_any();
1826     }
1827 }
1828
1829 static gboolean cbStopPlayback(AppData *data)
1830 {
1831     gint ret;
1832     ULOG_INFO("Stopping playback");
1833    
1834     g_assert(data != NULL);
1835
1836     ret = gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1837                         GST_STATE_PAUSED);
1838     if (seekToZero(data, GST_ELEMENT(data->playPipeline)))
1839     {
1840         gtk_adjustment_set_value( 
1841            GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1842         gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1843     }
1844     setAppState(data, APPSTATE_READY);
1845     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1846     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1847
1848     return FALSE;
1849 }
1850
1851 static void cbUserSeek(GtkAdjustment *adjustment, gpointer data)
1852 {   
1853     /*ULOG_INFO("cbUserSeek");*/
1854     AppData *app;
1855
1856     g_return_if_fail(data != NULL);
1857     app = (AppData *) data;
1858
1859     if (getAppState(app) != APPSTATE_READY || NULL == app->playPipeline)
1860         return;
1861
1862     seekToTime(app->playPipeline, gtk_adjustment_get_value(adjustment));
1863 }
1864
1865 static gchar *cbFormatSeekbarValue(GtkScale *scale, gdouble value)
1866 {
1867 /*    ULOG_INFO("cbFormatSeekbarValue");*/
1868     gint mins = 0;
1869     gint digits = gtk_scale_get_digits(scale);
1870
1871     if (value >= 60.0)
1872     {
1873         mins = value / 60;
1874         value -= mins * 60.0;
1875         return g_strdup_printf("%d:%0*.*f", mins, digits + 3, digits, value);
1876     }
1877     /* mins:sec.frac */
1878     return g_strdup_printf("%0.*f", digits, value);
1879 }
1880
1881 static gboolean cbUpdateRecLength(AppData *data)
1882 {
1883     struct timeval tv;
1884     guint mins = 0;
1885     gdouble secs;
1886     gchar *tmp;
1887
1888     if (gettimeofday(&tv, NULL) != 0)
1889         return FALSE;
1890
1891     secs = tv.tv_sec - data->recStartTv.tv_sec;
1892     secs += ((tv.tv_usec - data->recStartTv.tv_usec) / 1000000.0);
1893
1894     if (secs >= 60.0)
1895     {
1896         mins = secs / 60;
1897         secs -= mins * 60.0;
1898         tmp = g_strdup_printf("%u:%05.2f", mins, secs);
1899     }
1900     else
1901         tmp = g_strdup_printf("%0.2f", secs);
1902
1903     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry), 
1904                  tmp);
1905     g_free(tmp);
1906
1907     if (getAppState(data) == APPSTATE_RECORDING)
1908         return TRUE;
1909
1910     data->recUpdateId = 0;
1911     return FALSE;
1912 }
1913
1914