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