tidied code formatting
[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
610                  case APPSTATE_READY:
611                  default:
612                     /* destroyPipelines(app); */
613                      break;
614              }
615              break;
616         }
617         case GST_MESSAGE_STATE_CHANGED: 
618         {
619             GstState old;
620             GstState new;
621             GstState pending;
622
623             gst_message_parse_state_changed(message, &old, &new, &pending);
624
625             pipelineStateChanged(NULL, old, new, pending, app);
626
627             break;
628         }
629
630         default:
631             /* unhandled message */
632             ULOG_WARN("%s() - Unhandled message, type = %d", G_STRFUNC, message->type);
633             break;
634     }
635
636     /* remove message from the queue */
637     return TRUE;
638 }
639
640 static void cbDestroy(GtkWidget* widget, GdkEvent *event, gpointer data)
641 {     
642     AppData* app;
643
644     g_assert(data);
645
646     app = (AppData *) data;
647
648     if (!closeFile(app))
649         return;
650
651     destroyPipelines(app);
652     if (app->playPipeline)
653         gst_object_unref(GST_OBJECT(app->playPipeline));
654
655     if (app->recPipeline)
656         gst_object_unref(GST_OBJECT(app->recPipeline));
657
658     gtk_main_quit();
659 }
660
661 static gboolean cbCheckPosition (AppData *data)
662 {
663     GstFormat fmt = GST_FORMAT_TIME;
664     gint64 pos = 0, len = 0;
665     static gboolean lengthSet = FALSE;
666
667     g_assert(NULL != data);
668
669     /* get length */
670     if(!lengthSet && gst_element_query_duration(data->playPipeline, &fmt, &len))
671     {
672         if (len > 0)
673         {
674             gdouble size = 0;
675             size = GST_TIME_TO_SECS(len);
676             setLength(data, size); /* sets lengthEntry and adjustment */
677             lengthSet = TRUE;
678         }
679     }
680
681     /* calculate position */
682     if (gst_element_query_position(data->playPipeline, &fmt, &pos))
683     {
684         gdouble time = GST_TIME_TO_SECS(pos);
685
686         ULOG_DEBUG("pos = %lld, time = %f", 
687              pos,
688            time 
689            );
690
691         gtk_adjustment_set_value( 
692            GTK_ADJUSTMENT(data->mainViewData.adjustment),
693            time);
694         gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
695     }
696
697     if (APPSTATE_PLAYING == getAppState(data))
698     {
699         return TRUE;
700     }
701
702     return FALSE;
703 }
704
705 static void cbNew(GtkWidget* widget, AppData *data) 
706 {
707     g_assert(NULL != data);
708
709     if (!closeFile(data))
710         return;
711
712     /* remove pipelines if existing */
713     destroyPipelines(data);
714     ULOG_DEBUG_F("cbNew");
715     /* destroy tmp file */
716
717     /* clear filenames */
718     g_free(data->openFileName);
719     data->openFileName = NULL;
720     g_free(data->saveFileName);
721     data->saveFileName = NULL;
722 /*    data->filter = FORMAT_NONE;*/
723     data->file_format = FORMAT_NONE;
724
725     /* update display */
726     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
727         RECORDER_FILE_UNTITLED);
728     setLength(data, 0.0);
729     /* update the display + scale */
730     gtk_adjustment_set_value(GTK_ADJUSTMENT(data->mainViewData.adjustment),
731               0);
732     gtk_widget_set_sensitive(data->buttonSave, FALSE);
733     gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
734     data->saved = TRUE;
735
736     gtk_window_set_title(GTK_WINDOW(data->mainView), RECORDER_FILE_UNTITLED);
737
738     ULOG_DEBUG_F("cbNew end");
739 }
740
741 static void cbOpen(GtkWidget* widget, AppData *data) 
742 {
743     GtkWidget* dialog = NULL;
744     gchar *tmpfile = NULL;
745     gchar *selected = NULL;
746     AudioFormat format;
747     gchar *basename;
748     gdouble len = -1.0;
749 #if 0
750     GtkFileFilter *filter;
751 #endif
752
753     ULOG_DEBUG_F("begin");
754     g_assert(NULL != data);
755
756     if (!closeFile(data))
757         return;
758
759 #if 0
760     /* create filter */
761     filter = gtk_file_filter_new();
762     gtk_file_filter_add_mime_type(filter, "audio/x-mp3");
763 #endif
764
765     g_assert(GTK_IS_WINDOW(data->mainView));
766     /* create dialog */
767     dialog = hildon_file_chooser_dialog_new_with_properties(
768               GTK_WINDOW(data->mainView), 
769               "action", GTK_FILE_CHOOSER_ACTION_OPEN,
770               "file-system-model", NULL,
771               "local-only", TRUE,
772               NULL
773               );
774
775     /* show it */
776     gtk_widget_show_all(dialog);
777
778     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
779     {
780         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
781     }
782
783     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
784
785     if (dialog != NULL && GTK_IS_WIDGET(dialog))
786     {
787         gtk_widget_destroy(dialog);
788         /*
789         ULOG_DEBUG("%s() - dialog destroyed", G_STRFUNC);
790         */
791     }
792
793     if (NULL == selected) /* no file selected */
794         return;
795
796     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
797
798     if (openFile(selected, &format, &tmpfile))
799     {
800         ULOG_INFO("%s() - openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
801
802         g_assert(tmpfile);
803
804         /* update filenames */
805         basename = g_path_get_basename(selected);
806
807         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), selected);
808         gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
809         g_free(basename);
810
811         g_free(data->openFileName);
812         data->openFileName = tmpfile;
813         data->file_format = format;
814         data->filter = format;
815         g_free(data->saveFileName);
816         data->saveFileName = NULL;
817         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
818
819         len = guessMediaLength(data);
820         if (len > 0.0)
821             setLength(data, len);
822         else
823             setLength(data, 0.0);
824
825         data->saved = TRUE;
826     }
827     else
828     {
829         ULOG_WARN("%s() - openFile() failed", G_STRFUNC);
830         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not open file!"));
831     }
832
833     g_free(selected);
834
835     ULOG_DEBUG_F("end");
836 }
837
838 static gboolean
839 openURI(gpointer user_data)
840 {
841     AppData *data;
842     GnomeVFSURI *uri;
843     gchar *selected = NULL;
844     AudioFormat format;
845     gchar *tmpfile = NULL;
846     gchar *basename = NULL;
847     gdouble len = -1.0;
848
849     g_assert(user_data);
850     data = (AppData *) user_data;
851
852     if (NULL == data->mimeURI)
853         return FALSE;
854
855     uri = gnome_vfs_uri_new(data->mimeURI);
856     selected = g_strdup(gnome_vfs_uri_get_path(uri));
857
858     gnome_vfs_uri_unref(uri);
859     uri = NULL;
860
861     g_free(data->mimeURI);
862     data->mimeURI = NULL;
863
864     /* TODO: the following is duplicated in cbOpen(), move to a tryOpen() function ? */
865
866     if (NULL == selected)
867         return FALSE;
868
869     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
870
871     if (openFile(selected, &format, &tmpfile))
872     {
873         ULOG_INFO("%s: openFile() succeeded, format: %d, tmpfile %s", G_STRFUNC, format, tmpfile);
874
875         g_assert(tmpfile);
876
877         /* update filenames */
878         basename = g_path_get_basename(selected);
879         
880         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), selected);
881         gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
882         g_free(basename);
883
884         g_free(data->openFileName);
885         data->openFileName = tmpfile;
886         data->file_format = format;
887         data->filter = format;
888         g_free(data->saveFileName);
889         data->saveFileName = NULL;
890         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
891         
892         len = guessMediaLength(data);
893         if (len > 0.0)
894             setLength(data, len);
895         else
896             setLength(data, 0.0);
897
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 #if 0
1012 static void cbSave(GtkWidget* widget, AppData *data) 
1013 {
1014      GtkWidget* dialog = NULL;
1015      const gchar *current;
1016      gchar *selected = NULL;
1017
1018      g_assert(data);
1019
1020      ULOG_DEBUG("%s() - begin", G_STRFUNC);
1021
1022      current = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
1023      if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0) 
1024      {
1025        hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
1026        return;
1027      }
1028
1029      /* if saveFileName does not exist run saveas */
1030      if (NULL == data->saveFileName)
1031      {
1032       /* create dialog */
1033       dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
1034                    GTK_WINDOW(data->mainView), 
1035                    GTK_FILE_CHOOSER_ACTION_SAVE));
1036       
1037       /* show it */
1038       gtk_widget_show_all(dialog);
1039       
1040       if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
1041       {
1042            selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1043       }
1044      
1045       ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
1046       
1047       gtk_widget_destroy(dialog);
1048       
1049       if (NULL != selected)
1050       {
1051            ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC,
1052             selected);
1053            g_free(data->saveFileName);
1054            data->saveFileName = g_strdup_printf("%s%s", selected, getExtension(data->file_format));
1055            g_free(selected);
1056            selected = NULL;
1057       }
1058       else
1059       {
1060            return;
1061       }
1062      }
1063
1064      /* save the file */
1065      if (doSave(gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry)), data->saveFileName, data->file_format))
1066      {
1067       gchar *basename = g_path_get_basename(data->saveFileName);
1068       ULOG_INFO("%s() - file succesfully saved!", G_STRFUNC);
1069       g_free(data->openFileName);
1070       data->openFileName = g_strdup(data->saveFileName);
1071             
1072       gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1073                  data->saveFileName);
1074       gtk_widget_set_sensitive(data->buttonSave, FALSE);
1075       gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
1076       g_free(basename);
1077      }
1078      
1079      ULOG_DEBUG("%s() - end", G_STRFUNC);
1080 }
1081 #endif
1082
1083 static void cbSaveAs(GtkWidget* widget, AppData *data) 
1084 {
1085     GtkWidget* dialog = NULL;
1086     const gchar *current;
1087     gchar *selected = NULL;
1088
1089     g_assert(NULL != data);
1090
1091     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1092
1093     current = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
1094     if (NULL == current || strcmp(current, RECORDER_FILE_UNTITLED) == 0) 
1095     {
1096         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Nothing to save"));
1097         return;
1098     }
1099
1100     /* create dialog */
1101     dialog = GTK_WIDGET(hildon_file_chooser_dialog_new(
1102               GTK_WINDOW(data->mainView), 
1103               GTK_FILE_CHOOSER_ACTION_SAVE));
1104
1105     /* show it */
1106     gtk_widget_show_all(dialog);
1107
1108     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) 
1109     {
1110         selected = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1111     }
1112
1113     ULOG_DEBUG("%s() - dialog finished", G_STRFUNC);
1114     gtk_widget_destroy(dialog);
1115
1116     if (NULL == selected)
1117         return;
1118
1119     ULOG_INFO("%s() - selected filename = '%s'", G_STRFUNC, selected);
1120
1121     g_free(data->saveFileName);
1122     data->saveFileName = NULL;
1123
1124     if (saveFile(selected, data->openFileName, data->file_format, &(data->saveFileName)))
1125     {
1126         gchar *basename;
1127         const gchar *ext;
1128
1129         g_assert(data->saveFileName);
1130
1131         /* set new title that has the file name */
1132         basename = g_path_get_basename(data->saveFileName);
1133         ULOG_DEBUG("%s() - file '%s' succesfully saved!", G_STRFUNC, data->saveFileName);
1134
1135         gtk_window_set_title(GTK_WINDOW(data->mainView), basename);
1136
1137         /* Houston, we have a kludge:
1138          * for AU files we need to keep the old tmpfile for playback
1139          * for RAW/iLBC files, we can remove the tmpfile and point openFileName to the saved file
1140          */
1141         ext = getExtension(data->file_format);
1142         if (strcmp(ext, EXTENSION_AU) != 0)
1143         {
1144             g_free(data->openFileName);
1145             data->openFileName = g_strdup(data->saveFileName);
1146         }
1147
1148         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1149                  data->saveFileName);
1150         
1151         g_free(basename);
1152         data->saved = TRUE;
1153     }
1154     else
1155     {
1156         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Saving file failed!"));
1157     }
1158
1159     g_free(selected);
1160     selected = NULL;
1161
1162     ULOG_DEBUG("%s() - end", G_STRFUNC);
1163 }
1164
1165 static void cbRec(GtkWidget* widget, AppData *data) 
1166 {
1167     g_assert(NULL != data);
1168
1169     ULOG_DEBUG("%s() - begin", G_STRFUNC);     
1170
1171     if (APPSTATE_READY != getAppState(data))
1172     {
1173         ULOG_WARN("%s() - state different than READY -> return", G_STRFUNC);
1174         return;
1175     }
1176
1177     if (!closeFile(data))
1178      return;
1179
1180     /* clear filenames, use tmp file */
1181     g_free(data->openFileName);
1182     data->openFileName = NULL;
1183
1184     g_free(data->saveFileName);
1185     data->saveFileName = NULL;     
1186
1187     switch (data->filter)
1188     {
1189         case FORMAT_PCM:
1190             data->saveFileName = g_strdup(DEFAULT_TMP_FILE);
1191             data->openFileName = g_strdup(DEFAULT_TMP_FILE);
1192             break;
1193
1194         case FORMAT_PCMA:
1195             data->saveFileName = g_strdup(DEFAULT_TMP_PCMA_FILE);
1196             data->openFileName = g_strdup(DEFAULT_TMP_PCMA_FILE);
1197             break;
1198          
1199         case FORMAT_PCMU:
1200             data->saveFileName = g_strdup(DEFAULT_TMP_PCMU_FILE);
1201             data->openFileName = g_strdup(DEFAULT_TMP_PCMU_FILE);
1202             break;
1203
1204         case FORMAT_WAV:
1205             data->saveFileName = g_strdup(DEFAULT_TMP_WAV_FILE);
1206             data->openFileName = g_strdup(DEFAULT_TMP_WAV_FILE);
1207             break;
1208
1209         case FORMAT_ILBC:
1210         default:
1211             data->saveFileName = g_strdup(DEFAULT_TMP_ILBC_FILE);
1212             data->openFileName = g_strdup(DEFAULT_TMP_ILBC_FILE);
1213             break;
1214     }
1215
1216     ULOG_INFO("%s() - creating pipelines", G_STRFUNC);
1217     /* start recording */
1218     /* create related pipelines */
1219     if (createPipeline(data, PIPELINE_REC))
1220     {
1221         ULOG_INFO("%s() - starting recording", G_STRFUNC);
1222         /* start recording */
1223         gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1224                            GST_STATE_PLAYING);
1225
1226         /* update display */
1227         gtk_entry_set_text(GTK_ENTRY(data->mainViewData.fileNameEntry), 
1228             data->saveFileName);
1229
1230         setAppState(data, APPSTATE_RECORDING);
1231         gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1232         gtk_window_set_title(GTK_WINDOW(data->mainView), RECORDER_FILE_UNTITLED);
1233         data->file_format = data->filter;
1234     }
1235     else
1236     {
1237         ULOG_ERR("Could not create rec pipeline!");
1238         hildon_banner_show_information(GTK_WIDGET(data->mainView), GTK_STOCK_DIALOG_ERROR, _("Could not create pipeline"));
1239         setAppState(data, APPSTATE_READY);
1240     }
1241  
1242     ULOG_DEBUG("%s() - end", G_STRFUNC);     
1243 }
1244
1245 static void cbPlay(GtkWidget* widget, AppData *data) 
1246 {
1247     const gchar * file = NULL;
1248      
1249     g_assert(NULL != data);
1250
1251     ULOG_DEBUG("%s() - begin", G_STRFUNC);
1252
1253     file = gtk_entry_get_text(GTK_ENTRY(data->mainViewData.fileNameEntry));
1254     if (NULL == data->openFileName || NULL == file || strcmp(file, RECORDER_FILE_UNTITLED) == 0) 
1255     {
1256         ULOG_WARN("%s() - nothing to play", G_STRFUNC);
1257         return;
1258     }
1259
1260     openPlayPipeline(data);
1261      
1262     if (APPSTATE_PLAYING == getAppState(data))
1263     {
1264          if (GST_IS_ELEMENT(data->playPipeline)) 
1265          {
1266              gst_element_set_state(GST_ELEMENT(data->playPipeline), GST_STATE_PAUSED);
1267              setAppState(data, APPSTATE_READY);
1268          }
1269          return;
1270     }
1271
1272     if (APPSTATE_READY != getAppState(data))
1273     {
1274         ULOG_WARN("%s() - state different than PLAYING or READY -> return", G_STRFUNC);
1275         return;
1276     }
1277
1278     ULOG_INFO("filename %s", file);
1279      
1280     /*openPlayPipeline( data );*/
1281      
1282     /*if ( ! GST_IS_ELEMENT(data->playPipeline) ) 
1283     {
1284         if (g_strrstr(data->openFileName, EXTENSION_RAW))
1285         {
1286              ULOG_INFO("cbOpen() - file was raw, assuming audio/x-raw-int, 8kHz, 1 ch, 16-bit");
1287              destroyPipelines(data);
1288              createPipeline(data, PIPELINE_PLAY);      
1289         }
1290     }*/
1291      
1292     if (! GST_IS_ELEMENT(data->playPipeline))
1293     {
1294         ULOG_WARN("%s() - playPipeline does not exist", G_STRFUNC);
1295         return;
1296     }
1297
1298     gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1299                GST_STATE_PLAYING);
1300
1301     setAppState(data, APPSTATE_PLAYING);
1302
1303     g_timeout_add(PLAY_UPDATE_INTERVAL, (GSourceFunc)cbCheckPosition, data);
1304
1305     ULOG_DEBUG("%s() - end", G_STRFUNC);
1306 }
1307
1308 static void cbStop(GtkWidget* widget, AppData *data) 
1309 {
1310     g_assert(NULL != data);
1311
1312     ULOG_DEBUG("%s() - begin", G_STRFUNC); 
1313
1314     /* check if we are playing/recording */
1315
1316     /*
1317     if (APPSTATE_PLAYING != getAppState(data) &&
1318     APPSTATE_RECORDING != getAppState(data))
1319     {
1320       ULOG_WARN("cbStop() - state different than PLAYING or RECORDING "
1321           "-> return");
1322       return;
1323     } 
1324     */
1325
1326     /* stop playing or recording */
1327     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1328     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1329     gtk_widget_set_state(data->buttonRec, GTK_STATE_NORMAL);
1330
1331     /* destroy related pipeline */
1332     switch(getAppState(data))
1333     {
1334         case APPSTATE_PLAYING:
1335             /* don't destroy the playing pipeline. Instead, set the pipeline to PAUSED */
1336             /* destroyPipeline(data, PIPELINE_PLAY); */
1337             ULOG_INFO("%s() - Setting playPipeline state to PAUSED", G_STRFUNC);
1338             gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1339                      GST_STATE_PAUSED);
1340             /* flow through */
1341         case APPSTATE_READY:
1342             /* seek to zero, but not for PCM pipeline */
1343             /* if (data->playPipelineType == PIPELINE_PLAY || seekToZero(data, GST_ELEMENT(data->playPipeline))) */
1344             if ( !GST_IS_ELEMENT(data->playPipeline) || seekToZero(data, GST_ELEMENT(data->playPipeline)))
1345             {
1346               gtk_adjustment_set_value( 
1347                    GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1348               gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1349               gtk_entry_set_text(GTK_ENTRY(data->mainViewData.stateEntry), 
1350                          RECORDER_MSG_STOPPED);  
1351             }
1352             break;
1353
1354         case APPSTATE_RECORDING:
1355         {
1356             gdouble len = -1.0;
1357             gst_element_set_state(GST_ELEMENT(data->recPipeline), 
1358                  GST_STATE_PAUSED);
1359             destroyPipeline(data, PIPELINE_REC);
1360             gtk_widget_set_sensitive(data->buttonSaveAs, TRUE);
1361             data->saved = FALSE;
1362
1363             len = guessMediaLength(data);
1364             if (len > 0.0)
1365                 setLength(data, len);
1366
1367             break;
1368         }
1369      
1370         default:
1371             /* seekToZero(data, GST_ELEMENT(data->playPipeline)); */
1372             /* should not come here */
1373             break;
1374     }
1375
1376     setAppState(data, APPSTATE_READY);
1377
1378     ULOG_DEBUG("%s() - end", G_STRFUNC); 
1379 }
1380
1381
1382 /* ui construction functions */
1383
1384 static GtkWidget* createToolBar(AppData *data)
1385 {
1386      GtkToolbar* toolBar = NULL;
1387      
1388      GtkToolItem* new = NULL;
1389      GtkToolItem* open = NULL;
1390      GtkToolItem* save = NULL;
1391      GtkToolItem* saveas = NULL;
1392      GtkToolItem* sep = NULL;
1393      GtkToolItem* play = NULL;
1394      GtkToolItem* rec = NULL;
1395      GtkToolItem* stop = NULL;
1396      
1397      /* create buttons */
1398      new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); 
1399      open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); 
1400      save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
1401      data->buttonSave = GTK_WIDGET(save);
1402      saveas = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE_AS);
1403      data->buttonSaveAs = GTK_WIDGET(saveas);
1404      gtk_widget_set_sensitive(data->buttonSave, FALSE);
1405      gtk_widget_set_sensitive(data->buttonSaveAs, FALSE);
1406      data->saved = TRUE;
1407
1408      rec = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_RECORD); 
1409      data->buttonRec = GTK_WIDGET(rec);
1410      play = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY); 
1411      data->buttonPlay = GTK_WIDGET(play);
1412      stop = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
1413      
1414      /* create separator */
1415      sep  = gtk_separator_tool_item_new();
1416
1417      /* create the toolbar itself */
1418      toolBar = GTK_TOOLBAR(gtk_toolbar_new());
1419
1420      /* add items to toolbar */
1421      gtk_toolbar_insert(toolBar, new, -1);
1422      gtk_toolbar_insert(toolBar, open, -1);
1423      /*
1424      gtk_toolbar_insert(toolBar, save, -1);
1425      */
1426      gtk_toolbar_insert(toolBar, saveas, -1);
1427      gtk_toolbar_insert(toolBar, sep,  -1);
1428      gtk_toolbar_insert(toolBar, rec, -1);
1429      gtk_toolbar_insert(toolBar, play, -1);
1430      gtk_toolbar_insert(toolBar, stop, -1);
1431
1432      /* connect signals */
1433      g_signal_connect(G_OBJECT(new), "clicked",
1434               G_CALLBACK(cbNew), 
1435               data);
1436      g_signal_connect(G_OBJECT(open), "clicked",
1437               G_CALLBACK(cbOpen), 
1438               data);
1439      /*
1440      g_signal_connect(G_OBJECT(save), "clicked",
1441               G_CALLBACK(cbSave), 
1442               data);
1443      */
1444      g_signal_connect(G_OBJECT(saveas), "clicked",
1445               G_CALLBACK(cbSaveAs), 
1446               data);
1447      g_signal_connect(G_OBJECT(rec), "clicked",
1448               G_CALLBACK(cbRec), 
1449               data);
1450      g_signal_connect(G_OBJECT(play), "clicked",
1451               G_CALLBACK(cbPlay), 
1452               data);
1453      g_signal_connect(G_OBJECT(stop), "clicked",
1454               G_CALLBACK(cbStop), 
1455               data);
1456
1457      return GTK_WIDGET(toolBar);
1458
1459 }
1460
1461 static void cbItemGroupChanged( gpointer data )
1462 {
1463     AppData* app = (AppData* ) data;
1464     GValue active ={G_TYPE_INVALID};
1465     gint pcma,ilbc,pcm;
1466     
1467     g_value_init(&active, G_TYPE_INT);
1468
1469     g_object_get_property(G_OBJECT(app->radio_pcma), "active", &active);
1470     pcma = g_value_get_int(&active);
1471     g_object_get_property(G_OBJECT(app->radio_ilbc), "active", &active);
1472     ilbc = g_value_get_int(&active);
1473     g_object_get_property(G_OBJECT(app->radio_pcm), "active", &active);
1474     pcm = g_value_get_int(&active);
1475     
1476     ULOG_INFO("change type pcma=%d ilbc=%d pcm=%d",pcma, ilbc, pcm);
1477     if ( pcma == 1 )
1478         app->filter = FORMAT_PCMA;
1479     else if ( ilbc == 1 )
1480         app->filter = FORMAT_ILBC;
1481     else if ( pcm == 1 )
1482         app->filter = FORMAT_WAV;
1483     else
1484         app->filter = -1;
1485   
1486     ULOG_INFO("filter type=%d", app->filter);
1487 }
1488
1489 static void cbItemClose(GtkWidget *widget, gpointer data)
1490 {
1491     g_assert(data);
1492
1493     if (!closeFile(data))
1494         return;
1495
1496     gtk_main_quit();
1497 }
1498
1499 /* Create the menu items needed for the main view */
1500 static void createMenu( AppData *data )
1501 {
1502     /* Create needed variables */
1503     GSList *group = NULL;
1504     GtkMenu *main_menu;
1505     GtkWidget *menu_file;
1506     GtkWidget *menu_others;
1507     GtkWidget *item_file;
1508     GtkWidget *item_file_open;
1509     GtkWidget *item_file_save_as;
1510     GtkWidget *item_others;
1511     GtkWidget *item_pcma;
1512     GtkWidget *item_pcmu;
1513     GtkWidget *item_ilbc;
1514     GtkWidget *item_pcm;
1515    
1516     /*
1517     GtkWidget *item_radio_type1;
1518     */
1519     GtkWidget *item_close;
1520     GtkWidget *item_separator;
1521
1522     /* Get the menu from view */
1523     main_menu = GTK_MENU(gtk_menu_new());
1524     hildon_window_set_menu(data->mainView, main_menu);
1525     
1526     /* Create new submenu for "Others" */
1527     menu_file = gtk_menu_new ();
1528     menu_others = gtk_menu_new ();
1529
1530     /* Create menu items */
1531     item_file = gtk_menu_item_new_with_label (_("File"));
1532     item_file_open = gtk_menu_item_new_with_label(_("Open..."));
1533     item_file_save_as = gtk_menu_item_new_with_label(_("Save as..."));
1534     item_others = gtk_menu_item_new_with_label (_("Recording format"));
1535     
1536     item_pcma = gtk_radio_menu_item_new_with_label(
1537         group, "PCM A-law");
1538     item_ilbc = gtk_radio_menu_item_new_with_label_from_widget(
1539         GTK_RADIO_MENU_ITEM(item_pcma), "iLBC");
1540     item_pcmu = gtk_radio_menu_item_new_with_label_from_widget(
1541         GTK_RADIO_MENU_ITEM(item_pcma), "PCM u-law");
1542     item_pcm = gtk_radio_menu_item_new_with_label_from_widget(
1543         GTK_RADIO_MENU_ITEM(item_pcma), "WAV");
1544     
1545     /* default is iLBC */
1546     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_ilbc), TRUE); 
1547     data->filter = FORMAT_ILBC;
1548
1549     data->radio_pcma = item_pcma;
1550     data->radio_ilbc = item_ilbc;
1551     data->radio_pcm = item_pcm;        
1552     /*
1553     data->radiotype = item_radio_type1;
1554     */
1555     item_close = gtk_menu_item_new_with_label(_("Close"));
1556     item_separator = gtk_separator_menu_item_new();
1557     
1558     /* Add menu items to right menus */
1559     gtk_menu_append( main_menu, item_file );
1560     gtk_menu_append( menu_file, item_file_open );
1561     gtk_menu_append( menu_file, item_file_save_as );
1562     gtk_menu_append( main_menu, item_others );
1563     gtk_menu_append( menu_others, item_pcm );
1564     gtk_menu_append( menu_others, item_pcma );
1565     gtk_menu_append( menu_others, item_ilbc);
1566     
1567     gtk_menu_append( main_menu, item_close );
1568
1569     /* Add others submenu to the "Others" item */
1570     gtk_menu_item_set_submenu(
1571         GTK_MENU_ITEM(item_file), menu_file );
1572     gtk_menu_item_set_submenu(
1573         GTK_MENU_ITEM(item_others), menu_others );
1574
1575     /* Attach the callback functions to the activate signal */
1576     g_signal_connect( G_OBJECT( item_file_open), "activate",
1577         GTK_SIGNAL_FUNC (cbOpen), data);
1578     g_signal_connect( G_OBJECT( item_file_save_as), "activate",
1579         GTK_SIGNAL_FUNC (cbSaveAs), data);
1580     g_signal_connect( G_OBJECT( item_close ), "activate",
1581         GTK_SIGNAL_FUNC (cbItemClose), data);
1582
1583     g_signal_connect_swapped(G_OBJECT(item_pcma), "activate", G_CALLBACK(cbItemGroupChanged), data);
1584     g_signal_connect_swapped(G_OBJECT(item_pcm), "activate", G_CALLBACK(cbItemGroupChanged), data);
1585     
1586     /* Make all menu widgets visible */
1587
1588     gtk_widget_show_all( GTK_WIDGET( main_menu ) );
1589 }
1590
1591 gboolean maemo_recorder_ui_new(AppData *data)
1592 {
1593     HildonProgram *app = NULL;
1594     HildonWindow *window = NULL;
1595     GtkWidget *hbox = NULL;
1596     GtkWidget *vbox = NULL;
1597     GtkWidget *label = NULL;
1598     GtkWidget *entry1 = NULL;
1599     GtkWidget *entry2 = NULL;
1600     GtkWidget *entry3 = NULL;
1601     GtkWidget *toolBar = NULL;
1602     GtkWidget *table = NULL;
1603     GtkWidget *scale = NULL;
1604     GtkObject *adjustment = NULL;
1605
1606     g_assert(NULL != data);
1607
1608     app = HILDON_PROGRAM(hildon_program_get_instance());
1609     g_set_application_name(RECORDER_APP_TITLE);
1610      
1611     /* main window */
1612     window = HILDON_WINDOW(hildon_window_new());
1613
1614     hildon_program_add_window(app, window);
1615
1616     /* content for main view */
1617
1618     /* create vbox, divides control area and view area */
1619     vbox = gtk_vbox_new(FALSE, 0);
1620
1621     /* create hbox to divide control area */
1622     hbox = gtk_hbox_new(FALSE, HILDON_MARGIN_DEFAULT);
1623
1624     /* create toolbar */
1625     toolBar = createToolBar(data);
1626
1627     /* create table for labels */
1628     table = gtk_table_new (4, 3, FALSE);
1629     gtk_table_set_homogeneous(GTK_TABLE(table), FALSE);
1630
1631     gtk_table_set_row_spacings (GTK_TABLE (table), 4);
1632     gtk_table_set_col_spacings (GTK_TABLE (table), 0);
1633
1634     label = gtk_label_new_with_mnemonic(_("Filename:"));
1635     gtk_table_attach_defaults (GTK_TABLE (table),
1636             label,
1637             0, 1, 0, 1);
1638
1639     entry1 = gtk_entry_new ();
1640     gtk_entry_set_has_frame(GTK_ENTRY(entry1), FALSE);
1641     gtk_entry_set_text (GTK_ENTRY (entry1), _(RECORDER_FILE_UNTITLED));
1642     gtk_table_attach_defaults (GTK_TABLE (table), entry1, 1, 3, 0, 1);
1643     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry1);
1644
1645     label = gtk_label_new_with_mnemonic (_("Length:"));
1646     gtk_table_attach_defaults (GTK_TABLE (table),
1647             label,
1648             0, 1, 1, 2);
1649
1650     entry2 = gtk_entry_new ();
1651     gtk_entry_set_has_frame(GTK_ENTRY(entry2), FALSE);
1652     gtk_entry_set_text (GTK_ENTRY (entry2), "0:00.00");
1653     gtk_table_attach_defaults (GTK_TABLE (table), entry2, 1, 3, 1, 2);
1654     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry2);
1655
1656     label = gtk_label_new_with_mnemonic (_("State:"));
1657     gtk_table_attach_defaults (GTK_TABLE (table),
1658             label,
1659             0, 1, 2, 3);
1660
1661     entry3 = gtk_entry_new ();
1662     gtk_entry_set_has_frame(GTK_ENTRY(entry3), FALSE);
1663     gtk_entry_set_text (GTK_ENTRY (entry3), RECORDER_MSG_READY);
1664     gtk_table_attach_defaults (GTK_TABLE (table), entry3, 1, 3, 2, 3);
1665     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry3);
1666
1667     adjustment = gtk_adjustment_new (0.00,
1668                   0.00,
1669                   100.00,
1670                   0.01,
1671                   0.01,
1672                   0);
1673
1674     scale = gtk_hscale_new(GTK_ADJUSTMENT(adjustment));
1675       
1676 /*     gtk_table_attach_defaults (GTK_TABLE (table),
1677                 label,
1678                 0, 3, 2, 3);
1679 */
1680     /* connect signals */
1681     g_signal_connect(G_OBJECT(adjustment), "value-changed", G_CALLBACK(cbUserSeek), data);
1682     g_signal_connect(G_OBJECT(scale), "format-value", G_CALLBACK(cbFormatSeekbarValue), data);
1683     g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(cbDestroy), data);
1684
1685     /* packing the view */
1686     gtk_container_add (GTK_CONTAINER(window), vbox);
1687     gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, TRUE, 0);
1688     gtk_box_pack_start (GTK_BOX(vbox), scale, FALSE, FALSE, 0);
1689 /*    gtk_box_pack_start (GTK_BOX(vbox), hbox, TRUE, TRUE, 0); */
1690     
1691     hildon_window_add_toolbar(window, GTK_TOOLBAR(toolBar)); 
1692
1693     /* initialise the ui */
1694     gtk_entry_set_editable(GTK_ENTRY(entry1), FALSE);
1695     gtk_entry_set_editable(GTK_ENTRY(entry2), FALSE);
1696     gtk_entry_set_editable(GTK_ENTRY(entry3), FALSE);
1697
1698     /* store needed widgets */
1699     data->app = app;
1700     data->mainView = window;
1701     data->mainViewData.toolBar = GTK_WIDGET(toolBar);
1702     data->mainViewData.fileNameEntry = GTK_WIDGET(entry1);
1703     data->mainViewData.lengthEntry = GTK_WIDGET(entry2);
1704     data->mainViewData.stateEntry = GTK_WIDGET(entry3);
1705     data->mainViewData.adjustment = GTK_OBJECT(adjustment);
1706
1707     /* show the app */
1708     gtk_widget_show_all(GTK_WIDGET(window));
1709
1710     createMenu(data);
1711     
1712     return TRUE;
1713 }
1714
1715 void 
1716 maemo_recorder_mime_open(gpointer user_data, gint argc, gchar **argv)
1717 {
1718     AppData *data;
1719
1720     ULOG_DEBUG("%s with %d arguments", __FUNCTION__, argc);
1721
1722     if (argc == 0)
1723         return;
1724
1725     g_assert(user_data);
1726     data = (AppData *) user_data;
1727
1728     if (argv[0] != NULL)
1729     {
1730         ULOG_DEBUG("request to open %s", argv[0]);
1731         g_free(data->mimeURI);
1732         data->mimeURI = g_strdup(argv[0]);
1733         g_idle_add(openURI, (gpointer) data); 
1734         gtk_window_present(GTK_WINDOW(data->mainView));
1735     }
1736 }
1737
1738 static void seekToTime(GstElement *pipeline, gdouble secs)
1739 {
1740     g_assert(NULL != pipeline);
1741     ULOG_DEBUG("Seeking to: %.2f", secs);
1742
1743     /* time must be nanoseconds */
1744     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1745             GST_SEEK_TYPE_SET, (gint64) (secs * GST_SECOND),
1746             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1747     {
1748         ULOG_WARN("seekToTime failed!");
1749         return;
1750     }
1751     ULOG_DEBUG("seekToTime succeeded");
1752 }
1753
1754 static gboolean seekToZero(AppData *data, GstElement *pipeline)
1755 {
1756     gint plType;
1757     g_assert(NULL != pipeline);
1758     ULOG_DEBUG("Seeking to zero");
1759
1760     /* time must be nanoseconds */
1761     if (!gst_element_seek(pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1762             GST_SEEK_TYPE_SET, (gint64) 0,
1763             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1764     {
1765         ULOG_ERR("seekToZero failed! Trying to destroy and re-create pipeline");
1766         plType = data->playPipelineType;
1767
1768         /* gst_element_set_state(pipeline, GST_STATE_READY); */
1769         destroyPipeline(data, plType);
1770         return createPipeline(data, plType);
1771     }
1772
1773     ULOG_DEBUG("seekToZero succeeded");
1774     return TRUE;
1775 }
1776
1777 static void setLength(AppData *data, gdouble secs)
1778 {
1779     guint mins = 0;
1780     gchar *tmp;
1781
1782     if (secs < 0.0)
1783         return;
1784
1785     if (secs > 0)
1786     {
1787         g_object_set(G_OBJECT(data->mainViewData.adjustment), 
1788             "upper", secs, 
1789             NULL);    
1790         gtk_adjustment_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1791     }
1792
1793     if (secs >= 60.0)
1794     {
1795         mins = secs / 60;
1796         secs -= mins * 60.0;
1797     }
1798
1799     tmp = g_strdup_printf("%u:%05.2f", mins, secs);
1800
1801     /*
1802     ULOG_INFO("Setting length to %s", tmp);
1803     */
1804     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry), 
1805                  tmp);
1806     g_free(tmp);
1807 }
1808
1809 static gdouble guessMediaLength(AppData *data)
1810 {
1811     GnomeVFSFileSize size = 0;
1812     gdouble bitrate = 0.0;
1813     gdouble len = -1.0;
1814
1815     if (data->openFileName)
1816         size = getFileLength(data->openFileName);
1817     else
1818         return -1.0;
1819            
1820     if (size == 0)
1821         return -1.0;
1822
1823     ULOG_DEBUG("file size: %llu bytes", size);
1824
1825     switch (data->file_format)
1826     {
1827         case FORMAT_ILBC:
1828             bitrate = ILBC_BITRATE_20;
1829             break; 
1830             
1831         case FORMAT_PCMA:
1832         case FORMAT_PCMU:
1833             bitrate = PCMA_BITRATE;
1834             break;
1835
1836         default:
1837             return -1.0;
1838     }
1839     if (bitrate == 0.0)
1840         return -1.0;
1841
1842     len = (((gdouble) size) * 8.0) / (bitrate);
1843     ULOG_DEBUG("guessed media length: %.2f secs", len);
1844
1845     return len;
1846 }
1847
1848 static GstCaps *createCapsFilter(AudioFormat format)
1849 {
1850     switch (format)
1851     {
1852         case FORMAT_ILBC:
1853             return gst_caps_new_simple(
1854                 GST_TYPE_ILBC,
1855                 "rate", G_TYPE_INT, ILBC_RATE,
1856                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1857                 "mode", G_TYPE_INT, 20, /* 20 ms frames */
1858                 NULL);
1859         case FORMAT_PCMA:
1860             return gst_caps_new_simple(
1861                 GST_TYPE_PCMA,
1862                 "rate", G_TYPE_INT, DEFAULT_RATE,
1863                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1864                 NULL);
1865         case FORMAT_PCMU:
1866             return gst_caps_new_simple(
1867                 GST_TYPE_PCMU,
1868                 "rate", G_TYPE_INT, DEFAULT_RATE,
1869                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1870                 NULL);
1871         case FORMAT_WAV:
1872         case FORMAT_PCM:
1873             return gst_caps_new_simple(
1874                 GST_TYPE_PCM,
1875                 "rate", G_TYPE_INT, PCM_RATE,
1876                 "signed", G_TYPE_BOOLEAN, TRUE,
1877                 "channels", G_TYPE_INT, DEFAULT_CHANNELS,
1878                 "endianness", G_TYPE_INT, PCM_ENDIANNESS,
1879                 "width", G_TYPE_INT, PCM_WIDTH,
1880                 "depth", G_TYPE_INT, PCM_DEPTH,
1881                 NULL);
1882         default:
1883             ULOG_WARN("%s(): creating ANY caps", G_STRFUNC);
1884             return gst_caps_new_any();
1885     }
1886 }
1887
1888 static gboolean cbStopPlayback(AppData *data)
1889 {
1890     gint ret;
1891     ULOG_INFO("Stopping playback");
1892    
1893     g_assert(data != NULL);
1894
1895     ret = gst_element_set_state(GST_ELEMENT(data->playPipeline), 
1896                         GST_STATE_PAUSED);
1897     if (seekToZero(data, GST_ELEMENT(data->playPipeline)))
1898     {
1899         gtk_adjustment_set_value( 
1900            GTK_ADJUSTMENT(data->mainViewData.adjustment), 0);
1901         gtk_adjustment_value_changed(GTK_ADJUSTMENT(data->mainViewData.adjustment));
1902     }
1903     setAppState(data, APPSTATE_READY);
1904     gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(data->buttonPlay), GTK_STOCK_MEDIA_PLAY);
1905     gtk_widget_set_state(data->buttonPlay, GTK_STATE_NORMAL);
1906
1907     return FALSE;
1908 }
1909
1910 static void cbUserSeek(GtkAdjustment *adjustment, gpointer data)
1911 {   
1912     /*ULOG_INFO("cbUserSeek");*/
1913     AppData *app;
1914
1915     g_return_if_fail(data != NULL);
1916     app = (AppData *) data;
1917
1918     if (getAppState(app) != APPSTATE_READY || NULL == app->playPipeline)
1919         return;
1920
1921     seekToTime(app->playPipeline, gtk_adjustment_get_value(adjustment));
1922 }
1923
1924 static gchar *cbFormatSeekbarValue(GtkScale *scale, gdouble value)
1925 {
1926 /*    ULOG_INFO("cbFormatSeekbarValue");*/
1927     gint mins = 0;
1928     gint digits = gtk_scale_get_digits(scale);
1929
1930     if (value >= 60.0)
1931     {
1932         mins = value / 60;
1933         value -= mins * 60.0;
1934         return g_strdup_printf("%d:%0*.*f", mins, digits + 3, digits, value);
1935     }
1936     /* mins:sec.frac */
1937     return g_strdup_printf("%0.*f", digits, value);
1938 }
1939
1940 static gboolean cbUpdateRecLength(AppData *data)
1941 {
1942     struct timeval tv;
1943     guint mins = 0;
1944     gdouble secs;
1945     gchar *tmp;
1946
1947     if (gettimeofday(&tv, NULL) != 0)
1948         return FALSE;
1949
1950     secs = tv.tv_sec - data->recStartTv.tv_sec;
1951     secs += ((tv.tv_usec - data->recStartTv.tv_usec) / 1000000.0);
1952
1953     if (secs >= 60.0)
1954     {
1955         mins = secs / 60;
1956         secs -= mins * 60.0;
1957         tmp = g_strdup_printf("%u:%05.2f", mins, secs);
1958     }
1959     else
1960         tmp = g_strdup_printf("%0.2f", secs);
1961
1962     gtk_entry_set_text(GTK_ENTRY(data->mainViewData.lengthEntry), 
1963                  tmp);
1964     g_free(tmp);
1965
1966     if (getAppState(data) == APPSTATE_RECORDING)
1967         return TRUE;
1968
1969     data->recUpdateId = 0;
1970     return FALSE;
1971 }
1972