fix misreported audiolenght, playback timer
[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 500
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, GdkEventButton *event, AppData *data);
101 static void cbPlay(GtkWidget* widget, GdkEventButton *event, AppData *data);
102 static void cbRec(GtkWidget* widget, GdkEventButton *event, 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, filter, parse, sink, NULL);
306                     if (!gst_element_link_many (src, filter, 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_label_set_text(GTK_LABEL(data->mainViewData.stateEntry), "");
397     gtk_widget_set_sensitive(data->buttonRec, TRUE);
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_label_set_text(GTK_LABEL(data->mainViewData.stateEntry), 
460                       RECORDER_MSG_RECORDING);
461
462                gtk_widget_set_state(data->buttonRec, GTK_STATE_ACTIVE);
463
464                if (data->recUpdateId == 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_label_set_text(GTK_LABEL(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_label_set_text(GTK_LABEL(data->mainViewData.stateEntry), ""); 
490             break;
491
492         case GST_STATE_PAUSED:
493           {
494               gint64 pos = 0;
495               GstFormat fmt = GST_FORMAT_TIME;
496               ULOG_INFO("%s() - Paused", G_STRFUNC);
497
498               /* if pipeline pos == 0 => stopped, else => paused */
499               if (GST_IS_ELEMENT(data->playPipeline) && gst_element_query_position(data->playPipeline, &fmt, &pos) && pos != 0)
500               {
501                   hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_PAUSED);       
502                   gtk_label_set_text(GTK_LABEL(data->mainViewData.stateEntry), 
503                          RECORDER_MSG_PAUSED);
504               }
505               gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
506           }
507           break;
508           
509         case GST_STATE_NULL:
510           ULOG_INFO("%s() - Null", G_STRFUNC);
511           gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
512           gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
513           gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
514           
515           break;
516
517         default:
518           ULOG_WARN("%s() - default case", G_STRFUNC);
519           break;
520     }
521 }
522
523
524 static gboolean cbBus(GstBus *bus,
525                GstMessage *message,
526                gpointer   data)
527 {
528     AppData *app = (AppData*)data;
529
530     switch (GST_MESSAGE_TYPE(message)) 
531     {
532         case GST_MESSAGE_WARNING:
533         {
534             GError *err;
535             gchar *debug;
536
537             gst_message_parse_error (message, &err, &debug);
538             ULOG_WARN("%s() - Warning: %s", G_STRFUNC, err->message);
539             g_error_free (err);
540             g_free (debug);
541             break;
542         }
543
544         case GST_MESSAGE_ERROR: 
545         {
546             GError *err;
547             gchar *debug;
548
549             gst_message_parse_error (message, &err, &debug);
550             ULOG_ERR("%s() - Error: %s", G_STRFUNC, err->message);
551             g_error_free (err);
552             g_free (debug);
553             /* break; */
554             /* flow through to eos */
555         }
556
557         case GST_MESSAGE_EOS:
558         {
559             ULOG_INFO("%s() - eos", G_STRFUNC);
560
561              switch(getAppState(app))
562              {
563                  case APPSTATE_PLAYING:
564                     /* stop playback after a short break*/
565                     g_timeout_add(STOP_DELAY, (GSourceFunc)cbStopPlayback, data);
566                     //hildon_banner_show_information(GTK_WIDGET(app->mainView), NULL, RECORDER_MSG_STOPPED);       
567                     gtk_label_set_text(GTK_LABEL(app->mainViewData.stateEntry), 
568                          "");  
569
570                     break;
571
572                  case APPSTATE_RECORDING:
573                      gst_element_set_state(GST_ELEMENT(app->recPipeline), 
574                            GST_STATE_PAUSED);
575                      destroyPipeline(app, PIPELINE_REC);
576                      app->saved = FALSE;
577                      setAppState(data, APPSTATE_READY);
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         guint mins = 0;
657         gchar* tmp;
658
659         ULOG_DEBUG("pos = %lld, time = %f", 
660              pos,
661            time 
662            );
663
664         gtk_adjustment_set_value( 
665            GTK_ADJUSTMENT(data->mainViewData.adjustment),
666            time);
667         gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
668         if (time >= 60.0)
669         {
670             mins = time / 60;
671             time -= mins * 60.0;
672         }
673
674         tmp = g_strdup_printf("%02u:%02d", mins, (int)time);
675
676         gtk_label_set_text(GTK_LABEL(data->mainViewData.ctime),
677                  tmp);
678         g_free(tmp);
679
680     }
681
682     if (APPSTATE_PLAYING == getAppState(data))
683     {
684         return TRUE;
685     }
686
687     return FALSE;
688 }
689
690 static void cbNew(GtkWidget* widget, AppData *data) 
691 {
692     g_assert(NULL != data);
693
694     if (!closeFile(data))
695         return;
696
697     /* remove pipelines if existing */
698     if (APPSTATE_PLAYING == getAppState(data) || APPSTATE_RECORDING == getAppState(data)) {
699         cbStop(widget, NULL, data);
700         destroyPipelines(data);
701     }
702
703     setAppState(data, APPSTATE_READY);
704     ULOG_DEBUG_F("cbNew");
705     /* destroy tmp file */
706
707     /* clear filenames */
708     g_free(data->openFileName);
709     data->openFileName = NULL;
710     g_free(data->saveFileName);
711     data->saveFileName = NULL;
712 /*    data->filter = FORMAT_NONE;*/
713     data->file_format = FORMAT_NONE;
714
715     /* update display */
716     gtk_label_set_text(GTK_LABEL(data->mainViewData.fileNameEntry), 
717         RECORDER_FILE_UNTITLED);
718     setLength(data, 0.0);
719     /* update the display + scale */
720     gtk_adjustment_set_value(GTK_ADJUSTMENT(data->mainViewData.adjustment),
721               0);
722     gtk_label_set_text(GTK_LABEL(data->mainViewData.ctime), "00:00");
723     gtk_widget_set_sensitive(data->buttonSave, FALSE);
724     gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
725     data->saved = TRUE;
726
727     ULOG_DEBUG_F("cbNew end");
728 }
729
730 static void cbOpen(GtkWidget* widget, AppData *data) 
731 {
732     GtkWidget* dialog = NULL;
733     gchar *tmpfile = NULL;
734     gchar *selected = NULL;
735     AudioFormat format;
736     gchar *basename;
737     gdouble len = -1.0;
738 #if 0
739     GtkFileFilter *filter;
740 #endif
741
742     ULOG_DEBUG_F("begin");
743     g_assert(NULL != data);
744
745     if (!closeFile(data))
746         return;
747     if (APPSTATE_PLAYING == getAppState(data) || APPSTATE_RECORDING == getAppState(data)) {
748         cbStop(widget, NULL, data);
749         destroyPipelines(data);
750     }
751
752 #if 0
753     /* create filter */
754     filter = gtk_file_filter_new();
755     gtk_file_filter_add_mime_type(filter, "audio/x-mp3");
756 #endif
757
758     g_assert(GTK_IS_WINDOW(data->mainView));
759     /* create dialog */
760     dialog = hildon_file_chooser_dialog_new_with_properties(
761               GTK_WINDOW(data->mainView), 
762               "action", GTK_FILE_CHOOSER_ACTION_OPEN,
763               "file-system-model", NULL,
764               "local-only", TRUE,
765               NULL
766               );
767
768     /* show it */
769     gtk_widget_show_all(dialog);
770
771     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
772     {
773         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
774     }
775
776     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
777
778     if (dialog != NULL && GTK_IS_WIDGET(dialog))
779     {
780         gtk_widget_destroy(dialog);
781         /*
782         ULOG_DEBUG("%s() - dialog destroyed", G_STRFUNC);
783         */
784     }
785
786     if (NULL == selected) 
787     { 
788         /* no file selected */
789         if (!g_file_test(getFileName(data), G_FILE_TEST_EXISTS))
790         {
791                 cbNew(widget, data);
792         }
793         return;
794     }
795
796     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
797
798     if (openFile(selected, &format, &tmpfile))
799     {
800         ULOG_INFO("%s() - openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
801
802         g_assert(tmpfile);
803
804         /* update filenames */
805         basename = g_path_get_basename(selected);
806
807         gtk_label_set_text(GTK_LABEL(data->mainViewData.fileNameEntry), basename);
808         g_free(basename);
809
810         g_free(data->openFileName);
811         data->openFileName = tmpfile;
812         data->file_format = format;
813         //data->filter = format;
814         g_free(data->saveFileName);
815         data->saveFileName = NULL;
816         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
817
818         len = guessMediaLength(data);
819         if (len > 0.0)
820             setLength(data, len);
821         else
822             setLength(data, 0.0);
823
824         setFormatString(data, data->file_format);
825         data->saved = TRUE;
826     }
827     else
828     {
829         ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
830         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
831     }
832
833     g_free(selected);
834
835     ULOG_DEBUG_F("end");
836 }
837
838 static gboolean
839 openURI(gpointer user_data)
840 {
841     AppData *data;
842     GnomeVFSURI *uri;
843     gchar *selected = NULL;
844     AudioFormat format;
845     gchar *tmpfile = NULL;
846     gchar *basename = NULL;
847     gdouble len = -1.0;
848
849     g_assert(user_data);
850     data = (AppData *) user_data;
851
852     if (NULL == data->mimeURI)
853         return FALSE;
854
855     uri = gnome_vfs_uri_new(data->mimeURI);
856     selected = g_strdup(gnome_vfs_uri_get_path(uri));
857
858     gnome_vfs_uri_unref(uri);
859     uri = NULL;
860
861     g_free(data->mimeURI);
862     data->mimeURI = NULL;
863
864     /* TODO: the following is duplicated in cbOpen(), move to a tryOpen() function ? */
865
866     if (NULL == selected)
867         return FALSE;
868
869     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
870
871     if (openFile(selected, &format, &tmpfile))
872     {
873         ULOG_INFO("%s: openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
874
875         g_assert(tmpfile);
876
877         /* update filenames */
878         basename = g_path_get_basename(selected);
879         
880         gtk_label_set_text(GTK_LABEL(data->mainViewData.fileNameEntry), basename);
881         g_free(basename);
882
883         g_free(data->openFileName);
884         data->openFileName = tmpfile;
885         data->file_format = format;
886         //data->filter = format;
887         g_free(data->saveFileName);
888         data->saveFileName = NULL;
889         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
890         
891         len = guessMediaLength(data);
892         if (len > 0.0)
893             setLength(data, len);
894         else
895             setLength(data, 0.0);
896
897         setFormatString(data, data->file_format);
898         data->saved = TRUE;
899     }
900     else
901     {
902         ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
903         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
904     }
905
906     g_free(selected);
907
908     return FALSE;
909 }
910
911 static void openPlayPipeline( AppData *data )
912 {
913
914     GstFormat fmt = GST_FORMAT_TIME;
915     gint64 len;
916     gdouble size = 0;
917     lengthSet = FALSE;
918     /* create pipelines */
919     /* check file type */
920     switch (data->file_format)
921     {
922         case FORMAT_PCMA:
923         case FORMAT_PCMU:
924         case FORMAT_PCM:
925         case FORMAT_ILBC:
926         case FORMAT_WAV:
927             //destroyPipelines(data);
928             //data->filter = data->file_format;
929             createPipeline(data, PIPELINE_PLAY);
930             break;
931
932         case FORMAT_MP3:
933             destroyPipelines(data);
934             //data->filter = data->file_format;
935             createPipeline(data, PIPELINE_PLAY_MP3);
936             break;
937
938         case FORMAT_NONE:
939         default:
940             ULOG_WARN("%s() - unknown file_format", G_STRFUNC);
941             hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Unknown filetype!"));
942             return;
943             break;
944     }
945
946     if (!GST_IS_ELEMENT(data->playPipeline))
947     {
948         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline!"));
949         return;
950     }
951
952     gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_READY);
953     gst_element_get_state(GST_ELEMENT(data->playPipeline), NULL, NULL, GST_CLOCK_TIME_NONE /* or ns */);
954
955     /* calculate length */
956     if (gst_element_query_duration (data->playPipeline, &fmt, &len))
957     {
958         if (len > 0)
959         {
960             size = GST_TIME_TO_SECS(len);
961             ULOG_INFO("playSize: len:%lld size:%f", len, size);
962             setLength(data, size);
963         }
964     }
965     else 
966     {
967         setLength(data, 0.0);
968     }
969     
970 }
971
972 /* returns whether the action can proceed or should be aborted */
973 static gboolean 
974 closeFile(AppData *data)
975 {
976     GtkWidget *note;
977     gint i;
978
979     if (data->saved)
980         return TRUE;
981
982     note = hildon_note_new_confirmation_add_buttons(GTK_WINDOW(data->mainView), _("Save recording?"),
983             _("Yes"), GTK_RESPONSE_YES,
984             _("No"), GTK_RESPONSE_NO,
985             NULL);
986
987     i = gtk_dialog_run(GTK_DIALOG(note));
988     gtk_widget_destroy(note);
989
990     switch (i)
991     {
992         case GTK_RESPONSE_CANCEL:
993             return FALSE;
994
995         case GTK_RESPONSE_NO:
996             if (data->saveFileName)
997                 g_unlink(data->saveFileName);
998             data->saved = TRUE;
999             return TRUE;
1000
1001         case GTK_RESPONSE_YES:
1002         {
1003             cbSaveAs(NULL, data);
1004             return data->saved;
1005         }
1006         default:
1007             ULOG_WARN("%s(): unknown response from dialog: %d", G_STRFUNC, i);
1008     }
1009     return FALSE;
1010 }
1011
1012 static const gchar *
1013 getFileName(AppData *data)
1014 {
1015     g_assert(data);
1016     return gtk_label_get_text(GTK_LABEL(data->mainViewData.fileNameEntry));
1017
1018 }
1019
1020 static void cbSettings(GtkWidget* widget, AppData *data)
1021 {
1022     settings_edit( widget, data );
1023 }
1024
1025 static void cbEmailing(GtkWidget* widget, AppData *data)
1026 {
1027
1028     gboolean result;
1029     GSList *list = NULL;    
1030     gchar *file = NULL;
1031
1032     g_assert(NULL != data);
1033     
1034     if (g_file_test(getFileName(data), G_FILE_TEST_EXISTS))
1035     {
1036         file = file2uri(getFileName(data));
1037         ULOG_INFO("Emailing: %s", file);
1038         list = g_slist_append(list, file);
1039         result = libmodest_dbus_client_compose_mail(data->osso,
1040                 NULL, /*to*/
1041                 NULL, /*cc*/
1042                 NULL, /*bcc*/
1043                 NULL, /*body*/
1044                 NULL, /*subj*/
1045                 list);
1046         if (!result)
1047             hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Emailing failed"));
1048
1049         g_slist_free(list);
1050         g_free(file);
1051     }
1052 }
1053
1054 static void cbSaveAs(GtkWidget* widget, AppData *data) 
1055 {
1056     GtkWidget* dialog = NULL;
1057     const gchar *current;
1058     gchar *selected = NULL;
1059
1060     g_assert(NULL != data);
1061
1062     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1063
1064     current = getFileName(data);
1065     if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0) 
1066     {
1067         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
1068         return;
1069     }
1070
1071     /* create dialog */
1072     dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
1073               GTK_WINDOW(data->mainView), 
1074               GTK_FILE_CHOOSER_ACTION_SAVE));
1075
1076     gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), 
1077             get_default_dir() );
1078
1079     /* show it */
1080     gtk_widget_show_all(dialog);
1081
1082     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
1083     {
1084         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1085     }
1086
1087     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
1088     gtk_widget_destroy(dialog);
1089
1090     if (NULL == selected)
1091         return;
1092
1093     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
1094
1095     g_free(data->saveFileName);
1096     data->saveFileName = NULL;
1097
1098     if (saveFile(selected, data->openFileName, data->file_format, &(data->saveFileName)))
1099     {
1100         gchar *basename;
1101         const gchar *ext;
1102
1103         g_assert(data->saveFileName);
1104
1105         /* set new title that has the file name */
1106         basename = g_path_get_basename(data->saveFileName);
1107         ULOG_DEBUG("%s() - file '%s' succesfully saved!", G_STRFUNC, data->saveFileName);
1108
1109         /* Houston, we have a kludge:
1110          * for AU files we need to keep the old tmpfile for playback
1111          * for RAW/iLBC files, we can remove the tmpfile and point openFileName to the saved file
1112          */
1113         ext = getExtension(data->file_format);
1114         if (strcmp(ext, EXTENSION_AU) != 0)
1115         {
1116             g_free(data->openFileName);
1117             data->openFileName = g_strdup(data->saveFileName);
1118         }
1119
1120         gtk_label_set_text(GTK_LABEL(data->mainViewData.fileNameEntry), 
1121                  basename);
1122         
1123         g_free(basename);
1124         data->saved = TRUE;
1125     }
1126     else
1127     {
1128         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Saving file failed!"));
1129     }
1130
1131     g_free(selected);
1132     selected = NULL;
1133
1134     ULOG_DEBUG("%s() - end", G_STRFUNC);
1135 }
1136
1137 static void cbRec(GtkWidget* widget, GdkEventButton *event, AppData *data) 
1138 {
1139     g_assert(NULL != data);
1140
1141     ULOG_DEBUG("%s() - begin", G_STRFUNC);     
1142
1143     if (APPSTATE_RECORDING == getAppState(data))
1144     {
1145          if (GST_IS_ELEMENT(data->recPipeline))
1146          {
1147              gst_element_set_state(GST_ELEMENT(data->recPipeline), GST_STATE_PAUSED);
1148              gtk_label_set_text(GTK_LABEL(data->mainViewData.stateEntry),
1149                                               RECORDER_MSG_PAUSED);
1150              hildon_banner_show_information(GTK_WIDGET(data->mainView), NULL, RECORDER_MSG_PAUSED);
1151              setAppState(data, APPSTATE_PAUSED);
1152          }
1153          return;
1154     }
1155
1156     if (APPSTATE_PAUSED == getAppState(data)) 
1157     {
1158          if (GST_IS_ELEMENT(data->recPipeline))
1159          {
1160              gst_element_set_state(GST_ELEMENT(data->recPipeline),
1161                            GST_STATE_PLAYING);
1162              setAppState(data, APPSTATE_RECORDING);
1163              return;
1164          }
1165     }
1166
1167     if (APPSTATE_READY != getAppState(data))
1168     {
1169         ULOG_WARN("%s() - state different than READY -> return", G_STRFUNC);
1170         if (APPSTATE_RECORDING == getAppState(data))
1171             cbStop(widget, NULL,data);
1172         return;
1173     }
1174
1175     if (!closeFile(data))
1176      return;
1177
1178     /* clear filenames, use tmp file */
1179     g_free(data->openFileName);
1180     data->openFileName = NULL;
1181
1182     g_free(data->saveFileName);
1183     data->saveFileName = NULL;     
1184
1185     switch (data->filter)
1186     {
1187         case FORMAT_PCM:
1188              data->saveFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_FILE);
1189              data->openFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_FILE);
1190              break;
1191
1192          case FORMAT_PCMA:
1193              data->saveFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_PCMA_FILE);
1194              data->openFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_PCMA_FILE);
1195              break;
1196              
1197          case FORMAT_PCMU:
1198              data->saveFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_PCMU_FILE);
1199              data->openFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_PCMU_FILE);
1200              break;
1201
1202          case FORMAT_WAV:
1203              data->saveFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_WAV_FILE);
1204              data->openFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_WAV_FILE);
1205              break;
1206
1207          case FORMAT_ILBC:
1208          default:
1209              data->saveFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_ILBC_FILE);
1210              data->openFileName = g_strdup_printf("%s/%s", DEFAULT_TMP_DIR, DEFAULT_TMP_ILBC_FILE);
1211             break;
1212     }
1213
1214     g_mkdir(DEFAULT_TMP_DIR, 755);
1215     
1216     ULOG_INFO("%s() - creating pipelines", G_STRFUNC);
1217     /* start recording */
1218     /* create related pipelines */
1219     if (createPipeline(data, PIPELINE_REC))
1220     {
1221         ULOG_INFO("%s() - starting recording", G_STRFUNC);
1222         gchar *basename;
1223         /* start recording */
1224         data->rectime = 0;
1225         gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1226                            GST_STATE_PLAYING);
1227
1228         /* update display */
1229         basename = g_path_get_basename(data->saveFileName);
1230         gtk_label_set_text(GTK_LABEL(data->mainViewData.fileNameEntry), 
1231             basename);
1232         g_free(basename);
1233
1234         setAppState(data, APPSTATE_RECORDING);
1235         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1236         data->file_format = data->filter;
1237         setFormatString(data, data->file_format);
1238     }
1239     else
1240     {
1241         ULOG_ERR("Could not create rec pipeline!");
1242         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline"));
1243         setAppState(data, APPSTATE_READY);
1244     }
1245
1246     gtk_widget_set_sensitive(data->buttonPlay, FALSE);
1247
1248     ULOG_DEBUG("%s() - end", G_STRFUNC);     
1249 }
1250
1251 static void cbPlay(GtkWidget* widget, GdkEventButton *event, AppData *data) 
1252 {
1253     const gchar * file = NULL;
1254      
1255     g_assert(NULL != data);
1256
1257     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1258
1259     file = getFileName(data);
1260     if (NULL == data->openFileName || NULL == file || strcmp(file, RECORDER_FILE_UNTITLED) == 0) 
1261     {
1262         ULOG_WARN("%s() - nothing to play", G_STRFUNC);
1263         return;
1264     }
1265
1266     if (APPSTATE_PLAYING == getAppState(data))
1267     {
1268          if (GST_IS_ELEMENT(data->playPipeline)) 
1269          {
1270              gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_PAUSED);
1271              setAppState(data, APPSTATE_PAUSED);
1272          }
1273          return;
1274     }
1275
1276     if (APPSTATE_PAUSED != getAppState(data)) {
1277         openPlayPipeline(data);
1278         setAppState(data, APPSTATE_READY);
1279     }
1280     
1281     if (APPSTATE_READY != getAppState(data) && APPSTATE_PAUSED != getAppState(data))
1282     {
1283         ULOG_WARN("%s() - state different than PLAYING or READY -> return", G_STRFUNC);
1284         return;
1285     }
1286
1287     ULOG_INFO("filename %s", file);
1288      
1289     if (! GST_IS_ELEMENT(data->playPipeline))
1290     {
1291         ULOG_WARN("%s() - playPipeline does not exist", G_STRFUNC);
1292         return;
1293     }
1294
1295     gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1296                GST_STATE_PLAYING);
1297
1298     setAppState(data, APPSTATE_PLAYING);
1299
1300     g_timeout_add(PLAY_UPDATE_INTERVAL, (GSourceFunc)cbCheckPosition, data);
1301
1302     gtk_widget_set_sensitive(data->buttonRec, FALSE);
1303
1304     ULOG_DEBUG("%s() - end", G_STRFUNC);
1305 }
1306
1307 static void cbStop(GtkWidget* widget, GdkEventButton *event, AppData *data) 
1308 {
1309     g_assert(NULL != data);
1310
1311     ULOG_DEBUG("%s() - begin", G_STRFUNC); 
1312
1313     /* check if we are playing/recording */
1314
1315     /* stop playing or recording */
1316     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1317     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1318     gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
1319     gtk_widget_set_sensitive(data->buttonPlay, TRUE);
1320     gtk_widget_set_sensitive(data->buttonRec, TRUE);
1321
1322     /* destroy related pipeline */
1323     switch(getAppState(data))
1324     {
1325         case APPSTATE_PLAYING:
1326             /* don't destroy the playing pipeline. Instead, set the pipeline to PAUSED */
1327             /* destroyPipeline(data, PIPELINE_PLAY); */
1328             ULOG_INFO("%s() - Setting playPipeline state to PAUSED", G_STRFUNC);
1329             gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1330                      GST_STATE_PAUSED);
1331             setAppState(data, APPSTATE_READY);
1332             /* flow through */
1333         case APPSTATE_PAUSED:
1334         case APPSTATE_READY:
1335             /* seek to zero, but not for PCM pipeline */
1336             /* if (data->playPipelineType == PIPELINE_PLAY || seekToZero(data, GST_ELEMENT(data->playPipeline))) */
1337             if ( !GST_IS_ELEMENT(data->playPipeline) || seekToZero(data, GST_ELEMENT(data->playPipeline)))
1338             {
1339               gtk_adjustment_set_value( 
1340                    GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1341               gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1342               gtk_label_set_text(GTK_LABEL(data->mainViewData.ctime), "00:00");
1343               gtk_label_set_text(GTK_LABEL(data->mainViewData.stateEntry), 
1344                          "");  
1345             }
1346             break;
1347
1348         case APPSTATE_RECORDING:
1349         {
1350             gdouble len = -1.0;
1351             gst_element_send_event(GST_ELEMENT(data->recPipeline),
1352                  gst_event_new_eos());
1353             gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1354             data->saved = FALSE;
1355
1356             len = guessMediaLength(data);
1357             if (len > 0.0)
1358                 setLength(data, len);
1359
1360             break;
1361         }
1362      
1363         default:
1364             /* seekToZero(data, GST_ELEMENT(data->playPipeline)); */
1365             /* should not come here */
1366             break;
1367     }
1368
1369     ULOG_DEBUG("%s() - end", G_STRFUNC); 
1370 }
1371
1372
1373 /* ui construction functions */
1374
1375 static void cbItemGroupChanged( gpointer data )
1376 {
1377     AppData* app = (AppData* ) data;
1378     GValue active ={G_TYPE_INVALID};
1379     gint pcma,ilbc,pcm;
1380     
1381     g_value_init(&active, G_TYPE_INT);
1382
1383     g_object_get_property(G_OBJECT(app->radio_pcma), "active", &active);
1384     pcma = g_value_get_int(&active);
1385     g_object_get_property(G_OBJECT(app->radio_ilbc), "active", &active);
1386     ilbc = g_value_get_int(&active);
1387     g_object_get_property(G_OBJECT(app->radio_pcm), "active", &active);
1388     pcm = g_value_get_int(&active);
1389     
1390     ULOG_INFO("change type pcma=%d ilbc=%d pcm=%d",pcma, ilbc, pcm);
1391     if ( pcma == 1 )
1392         app->filter = FORMAT_PCMA;
1393     else if ( ilbc == 1 )
1394         app->filter = FORMAT_ILBC;
1395     else if ( pcm == 1 )
1396         app->filter = FORMAT_WAV;
1397     else
1398         app->filter = -1;
1399   
1400     ULOG_INFO("filter type=%d", app->filter);
1401 }
1402
1403 static void cbItemClose(GtkWidget *widget, gpointer data)
1404 {
1405     g_assert(data);
1406
1407     if (!closeFile(data))
1408         return;
1409
1410     gtk_main_quit();
1411 }
1412
1413 /* Create the menu items needed for the main view */
1414 static void createMenu( AppData *data )
1415 {
1416     HildonAppMenu *menu;
1417     GtkWidget *button_new;
1418     GtkWidget *button_open;
1419     GtkWidget *button_save;
1420     GtkWidget *button_email;
1421
1422     menu = HILDON_APP_MENU( hildon_app_menu_new() );
1423     button_new = hildon_gtk_button_new(HILDON_SIZE_AUTO);
1424     gtk_button_set_label( GTK_BUTTON(button_new), "New");
1425     button_open = hildon_gtk_button_new(HILDON_SIZE_AUTO);
1426     gtk_button_set_label( GTK_BUTTON(button_open), "Open");
1427     button_save = hildon_gtk_button_new(HILDON_SIZE_AUTO);
1428     gtk_button_set_label( GTK_BUTTON(button_save), "Save");
1429     button_email = hildon_gtk_button_new(HILDON_SIZE_AUTO);
1430     gtk_button_set_label( GTK_BUTTON(button_email), "Send via email");
1431
1432     hildon_app_menu_append( menu, GTK_BUTTON(button_new));
1433     hildon_app_menu_append( menu, GTK_BUTTON(button_open));
1434     hildon_app_menu_append( menu, GTK_BUTTON(button_save));
1435     hildon_app_menu_append( menu, GTK_BUTTON(button_email));
1436     g_signal_connect(G_OBJECT(button_new), "clicked",
1437               G_CALLBACK(cbNew),
1438               data);
1439     g_signal_connect(G_OBJECT(button_open), "clicked",
1440               G_CALLBACK(cbOpen),
1441               data);
1442     g_signal_connect(G_OBJECT(button_save), "clicked",
1443               G_CALLBACK(cbSaveAs),
1444               data); 
1445     g_signal_connect( G_OBJECT( button_email ), "clicked",
1446         GTK_SIGNAL_FUNC (cbEmailing), data);
1447     
1448     gtk_widget_show_all( GTK_WIDGET(menu));
1449
1450     hildon_window_set_app_menu(HILDON_WINDOW(data->mainView), menu);
1451
1452     data->filter = get_default_filter();
1453
1454     setFormatString(data, data->filter);
1455
1456 }
1457
1458 gboolean
1459 cbScaleRelease(GtkWidget *widget, GdkEventButton *ev, gpointer data)
1460 {
1461     AppData* app = (AppData* ) data;
1462   
1463     if (getAppState(app) == APPSTATE_RECORDING || NULL == app->playPipeline)
1464         return FALSE;
1465
1466     seekToTime(app->playPipeline, gtk_adjustment_get_value(app->mainViewData.adjustment));
1467   
1468     return FALSE;
1469
1470 }
1471
1472 gboolean
1473 evKeypress(GtkWidget *widget, GdkEventKey *ev, AppData *appdata)
1474 {
1475
1476   switch (ev->keyval)
1477   {
1478     case GDK_Return:
1479       cbRec(widget, NULL, appdata);
1480       return TRUE;
1481     case GDK_Right:
1482       cbPlay(widget, NULL, appdata);
1483       return TRUE;
1484     case GDK_Escape:
1485       cbStop(widget, NULL, appdata);
1486       return TRUE;
1487     default:
1488       break;
1489   }
1490
1491   return FALSE;
1492 }
1493
1494
1495 gboolean maemo_recorder_ui_new(AppData *data)
1496 {
1497     HildonProgram *app = NULL;
1498     HildonWindow *window = NULL;
1499     GtkWidget *hbox = NULL;
1500     GtkWidget *label = NULL;
1501     GtkWidget *ctime = NULL;
1502     GtkWidget *etime = NULL;
1503     GtkWidget *filename = NULL;
1504     GtkWidget *format = NULL;
1505     GtkWidget *state = NULL;
1506     GtkWidget *toolBar = NULL;
1507     GtkWidget *infohbox = NULL;
1508     GtkWidget *table = NULL;
1509     GtkWidget *scale = NULL;
1510     GtkObject *adjustment = NULL;
1511
1512     GtkWidget *rec = NULL;
1513     GtkWidget *play = NULL;
1514     GtkWidget *stop = NULL;
1515     GtkWidget *recimage = NULL;
1516     GtkWidget *playimage = NULL;
1517     GtkWidget *stopimage = NULL;
1518
1519     g_assert(NULL != data);
1520
1521     app = HILDON_PROGRAM(hildon_program_get_instance());
1522     g_set_application_name(RECORDER_APP_TITLE);
1523      
1524     /* main window */
1525     window = HILDON_WINDOW(hildon_window_new());
1526
1527     hildon_program_add_window(app, window);
1528
1529     /* content for main view */
1530
1531     /* create hbox to divide control area */
1532     hbox = gtk_hbox_new(FALSE, HILDON_MARGIN_DEFAULT);
1533
1534     /* create toolbar */
1535
1536     /* create table for labels */
1537     table = gtk_table_new (6, 6, TRUE);
1538     gtk_table_set_homogeneous(GTK_TABLE(table), TRUE);
1539
1540     gtk_table_set_row_spacings (GTK_TABLE (table), 4);
1541     gtk_table_set_col_spacings (GTK_TABLE (table), HILDON_MARGIN_TRIPLE);
1542
1543     filename = gtk_label_new(_(RECORDER_FILE_UNTITLED));
1544     format = gtk_label_new(RECORDER_FMT_STRING_NONE);
1545     state = gtk_label_new("");
1546     adjustment = gtk_adjustment_new (0.00,
1547                   0.00,
1548                   100.00,
1549                   0.01,
1550                   0.01,
1551                   0);
1552
1553     ctime = gtk_label_new("00:00");
1554     etime = gtk_label_new("00:00");
1555     scale = gtk_hscale_new(GTK_ADJUSTMENT(adjustment));
1556     gtk_scale_set_draw_value(scale, FALSE);
1557     gtk_box_pack_start(hbox, ctime, FALSE, FALSE, HILDON_MARGIN_DOUBLE);
1558     gtk_box_pack_end(hbox, etime, FALSE, FALSE, HILDON_MARGIN_DOUBLE);
1559     gtk_box_pack_end(hbox, scale, TRUE, TRUE, HILDON_MARGIN_DOUBLE);
1560     gtk_box_set_homogeneous(hbox, FALSE);
1561       
1562     gtk_table_attach_defaults(table, filename,
1563                     1, 4, 1, 2);
1564     gtk_table_attach_defaults(table, format,
1565                     1, 4, 2, 3);
1566     gtk_table_attach_defaults(table, state,
1567                     1, 4, 3, 4);
1568     gtk_table_attach_defaults(table, hbox,
1569                     0, 5, 5, 6);
1570
1571
1572     recimage = gtk_image_new_from_file(REC_ICON);
1573     rec = gtk_event_box_new();
1574     data->buttonRec = GTK_WIDGET(rec);
1575     gtk_container_add(rec, recimage);
1576     playimage = gtk_image_new_from_file(PLAY_ICON);
1577     play = gtk_event_box_new();
1578     data->buttonPlay = GTK_WIDGET(play);
1579     gtk_container_add(play, playimage);
1580     stopimage = gtk_image_new_from_file(STOP_ICON);
1581     stop = gtk_event_box_new();
1582     gtk_container_add(stop, stopimage);
1583
1584     gtk_table_attach_defaults(table, rec,
1585                     5, 6, 4, 6);
1586     gtk_table_attach_defaults(table, play,
1587                     5, 6, 2, 4);
1588     gtk_table_attach_defaults(table, stop,
1589                     5, 6, 0, 2);
1590
1591
1592     /* connect signals */
1593     g_signal_connect(G_OBJECT(scale), "button-release-event", G_CALLBACK(cbScaleRelease), data);
1594     g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(cbDestroy), data);
1595     g_signal_connect(G_OBJECT(window), "key-press-event",
1596             G_CALLBACK(evKeypress), data);
1597
1598      g_signal_connect(G_OBJECT(rec), "button-release-event",
1599               G_CALLBACK(cbRec),
1600               data);
1601      g_signal_connect(G_OBJECT(play), "button-release-event",
1602               G_CALLBACK(cbPlay),
1603               data);
1604      g_signal_connect(G_OBJECT(stop), "button-release-event",
1605               G_CALLBACK(cbStop),
1606               data);
1607
1608
1609     /* packing the view */
1610     gtk_container_add(GTK_CONTAINER(window), table);
1611     
1612     /* store needed widgets */
1613     data->app = app;
1614     data->mainView = window;
1615     data->mainViewData.fileNameEntry = GTK_WIDGET(filename);
1616     data->mainViewData.formatEntry = GTK_WIDGET(format);
1617     data->mainViewData.lengthEntry = GTK_WIDGET(etime);
1618     data->mainViewData.ctime = GTK_WIDGET(ctime);
1619     data->mainViewData.stateEntry = GTK_WIDGET(state);
1620     data->mainViewData.adjustment = GTK_OBJECT(adjustment);
1621
1622     /* show the app */
1623     gtk_widget_show_all(GTK_WIDGET(window));
1624
1625     createMenu(data);
1626     
1627     return TRUE;
1628 }
1629
1630 void 
1631 maemo_recorder_mime_open(gpointer user_data, gint argc, gchar **argv)
1632 {
1633     AppData *data;
1634
1635     ULOG_DEBUG("%s with %d arguments", __FUNCTION__, argc);
1636
1637     if (argc == 0)
1638         return;
1639
1640     g_assert(user_data);
1641     data = (AppData *) user_data;
1642
1643     if (argv[0] != NULL)
1644     {
1645         ULOG_DEBUG("request to open %s", argv[0]);
1646         g_free(data->mimeURI);
1647         data->mimeURI = g_strdup(argv[0]);
1648         g_idle_add(openURI, (gpointer) data); 
1649         gtk_window_present(GTK_WINDOW(data->mainView));
1650     }
1651 }
1652
1653 static void seekToTime(GstElement *pipeline, gdouble secs)
1654 {
1655     g_assert(NULL != pipeline);
1656     ULOG_DEBUG("Seeking to: %.2f", secs);
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) (secs * GST_SECOND),
1661             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1662     {
1663         ULOG_WARN("seekToTime failed!");
1664         return;
1665     }
1666     ULOG_DEBUG("seekToTime succeeded");
1667 }
1668
1669 static gboolean seekToZero(AppData *data, GstElement *pipeline)
1670 {
1671     gint plType;
1672     g_assert(NULL != pipeline);
1673     ULOG_DEBUG("Seeking to zero");
1674
1675     /* time must be nanoseconds */
1676     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1677             GST_SEEK_TYPE_SET, (gint64) 0,
1678             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1679     {
1680         ULOG_ERR("seekToZero failed! Trying to destroy and re-create pipeline");
1681         plType = data->playPipelineType;
1682
1683         /* gst_element_set_state(pipeline, GST_STATE_READY); */
1684         destroyPipeline(data, plType);
1685         return createPipeline(data, plType);
1686     }
1687
1688     ULOG_DEBUG("seekToZero succeeded");
1689     return TRUE;
1690 }
1691
1692 static void
1693 setFormatString(AppData *data, AudioFormat afmt)
1694 {
1695     gchar *str;
1696     gchar *format;
1697
1698     /* these are pretty much always the same */
1699     gint channels = 1; 
1700     gint rate = DEFAULT_RATE; /* 8000 */
1701     gint bits = 8;
1702
1703     g_assert(data);
1704     g_assert(GTK_IS_LABEL(data->mainViewData.formatEntry));
1705
1706     switch (afmt)
1707     {
1708         case FORMAT_PCMA:
1709             format = FORMAT_NAME_PCMA;
1710             break;
1711         case FORMAT_PCMU:
1712             format = FORMAT_NAME_PCMU;
1713             break;
1714         case FORMAT_ILBC:
1715             format = FORMAT_NAME_ILBC;
1716             rate = ILBC_RATE;
1717             break;
1718         /* TODO: we can play wavs with many sampling rates, 2 channels */
1719         /* we really should migrate to the better format spec */
1720         case FORMAT_WAV:
1721             format = FORMAT_NAME_WAV;
1722             bits = PCM_WIDTH;
1723             break;
1724         case FORMAT_PCM:
1725             format = FORMAT_NAME_PCM;
1726             bits = PCM_WIDTH;
1727             break;
1728         default:
1729             gtk_label_set_text(GTK_LABEL(data->mainViewData.formatEntry), RECORDER_FMT_STRING_NONE);
1730             return;
1731     }
1732
1733     str = g_strdup_printf("%s, %d %s, %d kHz, %d %s", format, channels, _("ch"), rate/1000, bits, _("bits"));
1734     gtk_label_set_text(GTK_LABEL(data->mainViewData.formatEntry), str);
1735     g_free(str);
1736 }
1737
1738 static void setLength(AppData *data, gdouble secs)
1739 {
1740     guint mins = 0;
1741     gchar *tmp;
1742
1743     if (secs < 0.0)
1744         return;
1745
1746     if (secs > 0)
1747     {
1748         g_object_set(G_OBJECT(data->mainViewData.adjustment), 
1749             "upper", secs, 
1750             NULL);    
1751         gtk_adjustment_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1752     }
1753
1754     if (secs >= 60.0)
1755     {
1756         mins = secs / 60;
1757         secs -= mins * 60.0;
1758     }
1759
1760     tmp = g_strdup_printf("%02u:%02d", mins, (int)secs);
1761
1762     /*
1763     ULOG_INFO("Setting length to %s", tmp);
1764     */
1765     gtk_label_set_text(GTK_LABEL(data->mainViewData.lengthEntry), 
1766                  tmp);
1767     g_free(tmp);
1768 }
1769
1770 static gdouble guessMediaLength(AppData *data)
1771 {
1772     GnomeVFSFileSize size = 0;
1773     gdouble bitrate = 0.0;
1774     gdouble len = -1.0;
1775
1776     if (data->openFileName)
1777         size = getFileLength(data->openFileName);
1778     else
1779         return -1.0;
1780            
1781     if (size == 0)
1782         return -1.0;
1783
1784     ULOG_DEBUG("file size: %llu bytes", size);
1785
1786     switch (data->file_format)
1787     {
1788         case FORMAT_ILBC:
1789             bitrate = ILBC_BITRATE_30;
1790             break; 
1791             
1792         case FORMAT_PCMA:
1793         case FORMAT_PCMU:
1794             bitrate = PCMA_BITRATE;
1795             break;
1796
1797         default:
1798             return -1.0;
1799     }
1800
1801     if (bitrate == 0.0)
1802         return -1.0;
1803
1804     len = (((gdouble) size) * 8.0) / (bitrate);
1805     ULOG_DEBUG("guessed media length: %.2f secs", len);
1806
1807     return len;
1808 }
1809
1810 static GstCaps *createCapsFilter(AudioFormat format)
1811 {
1812     switch (format)
1813     {
1814         case FORMAT_ILBC:
1815             return gst_caps_new_simple(
1816                 GST_TYPE_ILBC,
1817                 "rate", G_TYPE_INT, ILBC_RATE,
1818                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1819                 "mode", G_TYPE_INT, 30, /* 30 ms frames */
1820                 NULL);
1821         case FORMAT_PCMA:
1822             return gst_caps_new_simple(
1823                 GST_TYPE_PCMA,
1824                 "rate", G_TYPE_INT, DEFAULT_RATE,
1825                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1826                 NULL);
1827         case FORMAT_PCMU:
1828             return gst_caps_new_simple(
1829                 GST_TYPE_PCMU,
1830                 "rate", G_TYPE_INT, DEFAULT_RATE,
1831                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1832                 NULL);
1833         case FORMAT_WAV:
1834         case FORMAT_PCM:
1835             return gst_caps_new_simple(
1836                 GST_TYPE_PCM,
1837                 "rate", G_TYPE_INT, PCM_RATE,
1838                 "signed", G_TYPE_BOOLEAN, TRUE,
1839                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1840                 "endianness", G_TYPE_INT, PCM_ENDIANNESS,
1841                 "width", G_TYPE_INT, PCM_WIDTH,
1842                 "depth", G_TYPE_INT, PCM_DEPTH,
1843                 NULL);
1844         default:
1845             ULOG_WARN("%s(): creating ANY caps", G_STRFUNC);
1846             return gst_caps_new_any();
1847     }
1848 }
1849
1850 static gboolean cbStopPlayback(AppData *data)
1851 {
1852     gint ret;
1853     ULOG_INFO("Stopping playback");
1854    
1855     g_assert(data != NULL);
1856     destroyPipelines(data);
1857     setAppState(data, APPSTATE_READY);
1858     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1859     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1860
1861     return FALSE;
1862 }
1863
1864 static void cbUserSeek(GtkAdjustment *adjustment, gpointer data)
1865 {   
1866     AppData *app;
1867
1868     g_return_if_fail(data != NULL);
1869     app = (AppData *) data;
1870
1871     if (getAppState(app) != APPSTATE_READY && getAppState(app) != APPSTATE_PAUSED || NULL == app->playPipeline)
1872         return;
1873
1874     seekToTime(app->playPipeline, gtk_adjustment_get_value(adjustment));
1875 }
1876
1877 static gchar *cbFormatSeekbarValue(GtkScale *scale, gdouble value)
1878 {
1879 /*    ULOG_INFO("cbFormatSeekbarValue");*/
1880     gint mins = 0;
1881     gint digits = gtk_scale_get_digits(scale);
1882
1883     if (value >= 60.0)
1884     {
1885         mins = value / 60;
1886         value -= mins * 60.0;
1887         return g_strdup_printf("%d:%0*.*f", mins, digits + 3, digits, value);
1888     }
1889     /* mins:sec.frac */
1890     return g_strdup_printf("%0.*f", digits, value);
1891 }
1892
1893 static gboolean cbUpdateRecLength(AppData *data)
1894 {
1895     struct timeval tv;
1896     guint mins = 0;
1897     gdouble secs;
1898     gchar *tmp;
1899
1900     data->rectime += REC_UPDATE_INTERVAL/1000.0;
1901
1902     mins = data->rectime / 60;
1903     secs = data->rectime - (mins * 60.0);
1904     tmp = g_strdup_printf("%02u:%02d", mins, (int)secs);
1905
1906     gtk_label_set_text(GTK_LABEL(data->mainViewData.lengthEntry), 
1907                  tmp);
1908     g_free(tmp);
1909
1910     if (getAppState(data) == APPSTATE_RECORDING)
1911         return TRUE;
1912
1913     data->recUpdateId = 0;
1914     return FALSE;
1915 }
1916
1917