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