[gnome-sound-recorder/bilelmoussaoui/refresh] Start refreshing the HIG of Sound Recorder



commit 1818ca8edeae10ef2cdab69ced0aa7f6f1a97394
Author: Bilal Elmoussaoui <bil elmoussaoui gmail com>
Date:   Sun Jun 16 16:52:35 2019 +0200

    Start refreshing the HIG of Sound Recorder

 data/application.css                             |   7 +
 data/org.gnome.SoundRecorder.data.gresource.xml  |   3 +
 data/ui/main_window.ui                           | 217 ++++++++++++++
 data/ui/recording_row.ui                         | 149 ++++++++++
 data/ui/recording_waveform_row.ui                |  50 ++++
 org.gnome.SoundRecorder.json                     |  16 ++
 src/application.js                               |   4 +-
 src/audioProfile.js                              |  12 +-
 src/fileUtil.js                                  | 110 -------
 src/listview.js                                  | 352 -----------------------
 src/main.js                                      |   6 +-
 src/mainWindow.js                                | 219 +++++++-------
 src/org.gnome.SoundRecorder.src.gresource.xml.in |   9 +-
 src/{play.js => player.js}                       |  55 +++-
 src/{record.js => recorder.js}                   | 107 +++----
 src/recording.js                                 | 199 +++++++++++++
 src/recordingsManager.js                         | 251 ++++++++++++++++
 src/util.js                                      |  40 ---
 src/utils.js                                     |  79 +++++
 src/waveform.js                                  |  52 ++--
 20 files changed, 1232 insertions(+), 705 deletions(-)
---
diff --git a/data/application.css b/data/application.css
index 7aeab22..92f2cc7 100644
--- a/data/application.css
+++ b/data/application.css
@@ -1,4 +1,11 @@
+.record-button label{
+  color: @theme_bg_color;
+}
+
+.new-recording-widget{
+  background-color: white;
 
+}
 .emptyGrid {
    background-color: @theme_bg_color;
 }
diff --git a/data/org.gnome.SoundRecorder.data.gresource.xml b/data/org.gnome.SoundRecorder.data.gresource.xml
index c0306b2..be02925 100644
--- a/data/org.gnome.SoundRecorder.data.gresource.xml
+++ b/data/org.gnome.SoundRecorder.data.gresource.xml
@@ -2,6 +2,9 @@
 <gresources>
   <gresource prefix="/org/gnome/SoundRecorder">
     <file>application.css</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="main_window.ui">ui/main_window.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="recording_row.ui">ui/recording_row.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="recording_waveform_row.ui">ui/recording_waveform_row.ui</file>
   </gresource>
 </gresources>
 
diff --git a/data/ui/main_window.ui b/data/ui/main_window.ui
new file mode 100644
index 0000000..6c9c651
--- /dev/null
+++ b/data/ui/main_window.ui
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="libhandy" version="0.0"/>
+  <template class="Gjs_MainWindow" parent="GtkApplicationWindow">
+    <property name="width_request">480</property>
+    <property name="height_request">640</property>
+    <property name="can_focus">False</property>
+    <property name="default_width">480</property>
+    <property name="default_height">780</property>
+    <property name="icon_name">@APP_ID@</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="title" translatable="yes">SoundRecorder</property>
+        <property name="show_close_button">True</property>
+        <child>
+          <object class="GtkButton" id="record_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">media-record-symbolic</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="record_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Record</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <style>
+              <class name="suggested-action"/>
+              <class name="record-button"/>
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkMenuButton" id="menu_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_popover">False</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">open-menu-symbolic</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkStack" id="main_stack">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="icon_name">audio-input-microphone-symbolic</property>
+                <property name="icon_size">6</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="label" translatable="yes">Add Recordings</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="label" translatable="yes">Use the &lt;b&gt;Record&lt;/b&gt; button to make 
sound recordings</property>
+                <property name="use_markup">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="name">empty_view</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkViewport">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="HdyColumn">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="maximum_width">760</property>
+                    <property name="linear_growth_width">760</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="valign">start</property>
+                        <property name="vexpand">True</property>
+                        <property name="border_width">18</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkRevealer" id="new_recording_revealer">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="valign">start</property>
+                            <property name="hexpand">True</property>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkListBox" id="records_listbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="valign">start</property>
+                            <property name="vexpand">True</property>
+                            <property name="selection_mode">none</property>
+                            <style>
+                              <class name="frame"/>
+                            </style>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="name">records_view</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/recording_row.ui b/data/ui/recording_row.ui
new file mode 100644
index 0000000..91dcf2f
--- /dev/null
+++ b/data/ui/recording_row.ui
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="Gjs_RecordingRow" parent="GtkListBoxRow">
+    <property name="width_request">100</property>
+    <property name="height_request">80</property>
+    <property name="visible">True</property>
+    <property name="can_focus">True</property>
+    <child>
+      <object class="GtkBox" id="container">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkStack" id="buttons_stack">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkButton" id="play_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="tooltip_text" translatable="yes">Pause Recording</property>
+                <property name="halign">start</property>
+                <property name="valign">center</property>
+                <child>
+                  <object class="GtkImage" id="play_image">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">media-playback-start-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="name">play</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="pause_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="icon_name">media-playback-pause-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="name">pause</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="padding">6</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="valign">center</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkLabel" id="clip_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="valign">center</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">6</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="created_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="valign">center</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">6</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="padding">6</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkOverlay" id="overlay">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkLabel" id="time_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">end</property>
+                <property name="valign">start</property>
+                <property name="margin_left">6</property>
+                <property name="margin_right">6</property>
+                <property name="margin_top">6</property>
+                <property name="margin_bottom">6</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="index">-1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/recording_waveform_row.ui b/data/ui/recording_waveform_row.ui
new file mode 100644
index 0000000..3743fd1
--- /dev/null
+++ b/data/ui/recording_waveform_row.ui
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="Gjs_NewRecording" parent="GtkBox">
+    <property name="height_request">120</property>
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="valign">start</property>
+    <property name="margin_bottom">18</property>
+    <property name="hexpand">True</property>
+    <property name="vexpand">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkOverlay" id="overlay">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <child>
+          <object class="GtkLabel" id="record_time_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">end</property>
+            <property name="valign">start</property>
+            <property name="margin_left">6</property>
+            <property name="margin_right">6</property>
+            <property name="margin_top">6</property>
+            <property name="margin_bottom">6</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="index">-1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <style>
+      <class name="new-recording-widget"/>
+      <class name="frame"/>
+    </style>
+  </template>
+</interface>
diff --git a/org.gnome.SoundRecorder.json b/org.gnome.SoundRecorder.json
index 4edae0b..b8abfbf 100644
--- a/org.gnome.SoundRecorder.json
+++ b/org.gnome.SoundRecorder.json
@@ -20,6 +20,22 @@
         "--filesystem=~/Recordings:create"
     ],
     "modules" : [
+       {
+               "name": "libhandy",
+               "buildsystem": "meson",
+               "config-opts": [
+                       "-Dexamples=false",
+                       "-Dglade_catalog=disabled",
+                       "-Dtests=false",
+                       "-Dvapi=false"
+               ],
+               "sources":[{
+                       "type": "git",
+                       "url": "https://source.puri.sm/Librem5/libhandy.git";,
+                       "tag": "v0.0.10",
+                       "commit": "2d777677352d037b6f5cc24d9c1c8d9a74ac0ded"
+               }]
+       },
         {
             "name" : "gnome-sound-recorder",
             "buildsystem": "meson",
diff --git a/src/application.js b/src/application.js
index 5149272..6062c6c 100644
--- a/src/application.js
+++ b/src/application.js
@@ -83,7 +83,9 @@ var Application = GObject.registerClass(class Application extends Gtk.Applicatio
     }
 
     vfunc_activate() {
-        (this.window = new MainWindow.MainWindow({ application: this })).show();
+        this.window = new MainWindow.MainWindow();
+        this.window.set_application(this);
+        this.window.show();
         if (pkg.name.endsWith('Devel'))
             this.window.get_style_context().add_class("devel");
     }
diff --git a/src/audioProfile.js b/src/audioProfile.js
index 090d976..fcee4eb 100644
--- a/src/audioProfile.js
+++ b/src/audioProfile.js
@@ -52,8 +52,13 @@ var audioCodecMap = {
     VORBIS: "audio/x-vorbis"
 };
 
-var AudioProfile = class AudioProfile {
-    profile(profileName) {
+var AudioProfile = GObject.registerClass({
+
+
+  },
+  class AudioProfile extends GObject.Object {
+    _init(profileName) {
+        super._init();
         if (profileName) {
             this._profileName = profileName;
         } else {
@@ -120,4 +125,5 @@ var AudioProfile = class AudioProfile {
         this.audioSuffix = ("." + suffixName);
         return this.audioSuffix;
     }
-}
+  }
+);
diff --git a/src/main.js b/src/main.js
index 80e4f2a..854a38d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -34,7 +34,11 @@ pkg.require({ 'Gdk': '3.0',
               'Gtk': '3.0',
               'Gst': '1.0',
               'GstAudio': '1.0',
-              'GstPbutils': '1.0' });
+              'GstPbutils': '1.0',
+              'Handy': '0.0' });
+
+const Handy = imports.gi.Handy;
+Handy.init(null);
 
 const Application = imports.application;
 
diff --git a/src/mainWindow.js b/src/mainWindow.js
index 37194c5..abc7409 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -1,4 +1,4 @@
-/* exported audioProfile displayTime list offsetController
+/* exported displayTime list offsetController
             play recordPipeline view volumeValue wave ActiveArea
             RecordPipelineStates _SEC_TIMEOUT MainWindow
             EncoderComboBox ChannelsComboBox */
@@ -33,17 +33,18 @@ const Gtk = imports.gi.Gtk;
 const Pango = imports.gi.Pango;
 
 const Application = imports.application;
-const AudioProfile = imports.audioProfile;
-const FileUtil = imports.fileUtil;
+
 const Info = imports.info;
-const Listview = imports.listview;
-const Play = imports.play;
+const RecordingsManager = imports.recordingsManager.RecordingsManager;
+const NewRecording = imports.recording.NewRecording;
+const RecordingRow = imports.recording.RecordingRow;
+
 const Preferences = imports.preferences;
-const Record = imports.record;
+
 const Waveform = imports.waveform;
 
 let activeProfile = null;
-var audioProfile = null;
+
 var displayTime = null;
 let grid = null;
 let groupGrid;
@@ -85,53 +86,131 @@ var RecordPipelineStates = {
     STOPPED: 2
 };
 
+var RecordingState = {
+  RECORDING: 0,
+  PAUSED: 1,
+  STOPPED: 2
+};
+
 const _TIME_DIVISOR = 60;
 var _SEC_TIMEOUT = 100;
 
-var MainWindow = GObject.registerClass(class MainWindow extends Gtk.ApplicationWindow {
-     _init(params) {
-        audioProfile = new AudioProfile.AudioProfile();
-        offsetController = new FileUtil.OffsetController;
-        displayTime = new FileUtil.DisplayTime;
-        view = new MainView();
-        play = new Play.Play();
+var MainWindow = GObject.registerClass({
+      Template: 'resource:///org/gnome/SoundRecorder/main_window.ui',
+      Properties: {
+        'recording-state': GObject.ParamSpec.int(
+            'recording-state',
+            'RecordingState',
+            'The recording state',
+            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
+            0, 2,
+            RecordingState.STOPPED
+        )
+      },
+      InternalChildren: [
+        'menu_button',
+        'main_stack',
+        'record_button',
+        'record_label',
+        'records_listbox',
+        'new_recording_revealer',
+      ]
+  },
+  class MainWindow extends Gtk.ApplicationWindow {
+     _init() {
+        super._init();
+        this._addAppMenu();
+        this._recordingsManager = new RecordingsManager();
+        this._initWidgets();
 
-        super._init(Object.assign({
-            title: GLib.get_application_name(),
-            default_height: 480,
-            default_width: 780,
-            height_request: 480,
-            width_request: 640,
-            hexpand: true,
-            vexpand: true,
-            icon_name: pkg.name
-        }, params));
+        this.show_all();
+    }
 
-        header = new Gtk.HeaderBar({ hexpand: true,
-                                     show_close_button: true });
-        this.set_titlebar(header);
-        header.get_style_context().add_class('titlebar');
+    _initWidgets() {
 
-        recordButton = new RecordButton({ label: _("Record") });
-        recordButton.get_style_context().add_class('suggested-action');
-        header.pack_start(recordButton);
+        this._record_button.connect('clicked', () => this._onRecordClicked())
+        this._recordingsManager.connect('recording-added', (obj, recording) => {
+          this._onRecordingAdded(recording);
+        });
 
-        this._addAppMenu();
+        this._records_listbox.set_header_func(this._updateHeaderFunc)
+        this._records_listbox.set_sort_func(this._sortRecordings);
 
-        this.add(view);
-        this.show_all();
+        this._newRecordingWidget = new NewRecording();
+        this._newRecordingWidget.connect("paused", () => this._record.pauseRecording());
+        this._newRecordingWidget.connect("resumed", () => this._record.resumeRecording());
+        this._recordingsManager._recorder.connect("timer-updated", (obj ,recordTime) => {
+          this._newRecordingWidget.updateRecordTime(recordTime);
+        });
+        this._new_recording_revealer.add(this._newRecordingWidget);
     }
 
+
+
     _addAppMenu() {
         let menu = new Gio.Menu();
         menu.append(_("Preferences"), 'app.preferences');
         menu.append(_("About Sound Recorder"), 'app.about');
 
-        appMenuButton = new Gtk.MenuButton({
-          image: new Gtk.Image({ icon_name: 'open-menu-symbolic' }),
-          menu_model: menu
-        });
-        header.pack_end(appMenuButton);
+        this._menu_button.set_menu_model(menu);
+    }
+
+    _onRecordClicked() {
+        if(this.recording_state === RecordingState.STOPPED) {
+            this._main_stack.set_visible_child_name('records_view');
+            this._main_stack.show_all();
+            this._record_button.get_style_context().remove_class('suggeted-action');
+            this._record_button.get_style_context().add_class('destructive-action');
+
+            this._record_label.set_text('Stop');
+
+           let wave = this._recordingsManager.startNewRecording();
+           this._newRecordingWidget.setWave(wave);
+            this._newRecordingWidget.show_all();
+
+            this._new_recording_revealer.set_reveal_child(true);
+
+            this.set_property('recording-state', RecordingState.RECORDING);
+        } else {
+
+            this._recordingsManager.saveRecording();
+            this._newRecordingWidget.reset();
+
+            this._record_button.get_style_context().add_class('suggested-action');
+            this._record_button.get_style_context().remove_class('destructive-action');
+            this._record_label.set_text('Record');
+            this.set_property('recording-state', RecordingState.STOPPED);
+
+            this._new_recording_revealer.set_reveal_child(false);
+        }
+    }
+
+    _onRecordingAdded(recording) {
+        this._main_stack.set_visible_child_name('records_view');
+        let recordingRow = new RecordingRow(recording);
+        this._records_listbox.add(recordingRow);
+        this._records_listbox.show_all();
+    }
+
+
+        _updateHeaderFunc(row, before) {
+        if (before) {
+            let separator = new Gtk.Separator();
+            separator.connect("realize", (seperator) => {
+              separator.set_size_request(before.get_allocated_width(), -1)
+            })
+            row.set_header(separator)
+            separator.show()
+        }
+    }
+    _sortRecordings(row1, row2) {
+      if (row2.recording) {
+        if(row1.recording.fileName < row2.recording.fileName) {
+          return -1;
+        } else {
+          return 1;
+        }
+      }
     }
 });
 
@@ -148,38 +227,11 @@ const MainView = GObject.registerClass(class MainView extends Gtk.Stack {
         this.labelID = null;
     }
 
-    _addEmptyPage() {
-        this.emptyGrid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
-                                        hexpand: true,
-                                        vexpand: true,
-                                        halign: Gtk.Align.CENTER,
-                                        valign: Gtk.Align.CENTER });
-        this._scrolledWin.add(this.emptyGrid);
-
-        let emptyPageImage = new Gtk.Image({ icon_name: 'audio-input-microphone-symbolic',
-                                             icon_size: Gtk.IconSize.DIALOG });
-        emptyPageImage.get_style_context().add_class('dim-label');
-        this.emptyGrid.add(emptyPageImage);
-        let emptyPageTitle = new Gtk.Label({ label: _("Add Recordings"),
-                                             halign: Gtk.Align.CENTER,
-                                             valign: Gtk.Align.CENTER });
-        emptyPageTitle.get_style_context().add_class('dim-label');
-        this.emptyGrid.add(emptyPageTitle);
-        let emptyPageDirections = new Gtk.Label({ label: _("Use the <b>Record</b> button to make sound 
recordings"),
-                                                  use_markup: true,
-                                                  max_width_chars: 30,
-                                                  halign: Gtk.Align.CENTER,
-                                                  valign: Gtk.Align.CENTER });
-        emptyPageDirections.get_style_context().add_class('dim-label');
-        this.emptyGrid.add(emptyPageDirections);
-        this.emptyGrid.show_all();
-    }
-
     _addListviewPage(name) {
         list = new Listview.Listview();
         list.setListTypeNew();
         list.enumerateDirectory();
-        this._record = new Record.Record(audioProfile);
+
 
         groupGrid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
                                    hexpand: true,
@@ -773,39 +825,6 @@ const MainView = GObject.registerClass(class MainView extends Gtk.Stack {
     }
 });
 
-const RecordButton = GObject.registerClass(class RecordButton extends Gtk.Button {
-    _init(activeProfile) {
-        super._init();
-        this.image = Gtk.Image.new_from_icon_name('media-record-symbolic', Gtk.IconSize.BUTTON);
-        this.set_always_show_image(true);
-        this.set_valign(Gtk.Align.CENTER);
-        this.set_label(_("Record"));
-        this.get_style_context().add_class('text-button');
-        this.connect("clicked", () => this._onRecord());
-    }
-
-    _onRecord() {
-        view.destroyLoadMoreButton();
-        view.hasPreviousSelRow();
-
-        if (view.listBox) {
-            view.listBox.set_selection_mode(Gtk.SelectionMode.NONE);
-        } else {
-            view.emptyGrid.destroy();
-        }
-
-        this.set_sensitive(false);
-        setVisibleID = ActiveArea.RECORD;
-        view.recordGrid.show_all();
-
-        if (activeProfile == null)
-            activeProfile = 0;
-
-        audioProfile.profile(activeProfile);
-        view._record.startRecording(activeProfile);
-        wave = new Waveform.WaveForm(view.recordGrid, null);
-    }
-});
 
 var EncoderComboBox = GObject.registerClass(class EncoderComboBox extends Gtk.ComboBoxText {
     // encoding setting labels in combobox
diff --git a/src/org.gnome.SoundRecorder.src.gresource.xml.in 
b/src/org.gnome.SoundRecorder.src.gresource.xml.in
index 7fb1ff4..48f7ff9 100644
--- a/src/org.gnome.SoundRecorder.src.gresource.xml.in
+++ b/src/org.gnome.SoundRecorder.src.gresource.xml.in
@@ -3,14 +3,15 @@
   <gresource prefix="/org/gnome/SoundRecorder@profile@/js">
     <file>application.js</file>
     <file>audioProfile.js</file>
-    <file>fileUtil.js</file>
+    <file>utils.js</file>
     <file>info.js</file>
-    <file>listview.js</file>
     <file>main.js</file>
     <file>mainWindow.js</file>
-    <file>play.js</file>
+    <file>player.js</file>
     <file>preferences.js</file>
-    <file>record.js</file>
+    <file>recording.js</file>
+    <file>recordingsManager.js</file>
+    <file>recorder.js</file>
     <file>waveform.js</file>
   </gresource>
 </gresources>
diff --git a/src/play.js b/src/player.js
similarity index 88%
rename from src/play.js
rename to src/player.js
index 0d2bcd4..c6606d2 100644
--- a/src/play.js
+++ b/src/player.js
@@ -20,10 +20,12 @@
 
 const _ = imports.gettext.gettext;
 const Gio = imports.gi.Gio;
+const GObject = imports.gi.GObject;
 const GLib = imports.gi.GLib;
 const Gst = imports.gi.Gst;
 const GstAudio = imports.gi.GstAudio;
 const GstPbutils = imports.gi.GstPbutils;
+
 const Gtk = imports.gi.Gtk;
 const Mainloop = imports.mainloop;
 
@@ -47,16 +49,28 @@ let errorDialogState;
 
 const _TENTH_SEC = 100000000;
 
-var Play = class Play {
+var Player = GObject.registerClass({
+    Signals: {
+      'timer-updated': {
+          flags: GObject.SignalFlags.RUN_FIRST,
+          param_types: [ GObject.TYPE_INT ]
+      }
+    }
+
+  },
+  class Player extends GObject.Object {
+    _init() {
+      super._init();
+      this.playState = PipelineStates.STOPPED;
+      this.play = Gst.ElementFactory.make("playbin", "play");
+      this.sink = Gst.ElementFactory.make("pulsesink", "sink");
+      this.play.set_property("audio-sink", this.sink);
+      this.clock = this.play.get_clock();
+      this.playBus = this.play.get_bus();
+      this._asset = null;
+    }
     _playPipeline() {
         errorDialogState = ErrState.OFF;
-        let uri = this._fileToPlay.get_uri();
-        this.play = Gst.ElementFactory.make("playbin", "play");
-        this.play.set_property("uri", uri);
-        this.sink = Gst.ElementFactory.make("pulsesink", "sink");
-        this.play.set_property("audio-sink", this.sink);
-        this.clock = this.play.get_clock();
-        this.playBus = this.play.get_bus();
         this.playBus.add_signal_watch();
         this.playBus.connect("message", (playBus, message) => {
             if (message != null) {
@@ -65,6 +79,16 @@ var Play = class Play {
         });
     }
 
+    get duration() {
+      return this.play.query_duration(Gst.Format.TIME)
+
+    }
+
+    setUri(uri) {
+        this.play.set_property("uri", uri);
+    }
+
+
     startPlaying() {
         this.baseTime = 0;
 
@@ -85,7 +109,7 @@ var Play = class Play {
             this._showErrorDialog(_('Unable to play recording'));
             errorDialogState = ErrState.ON;
         } else if (this.ret == Gst.StateChangeReturn.SUCCESS) {
-            MainWindow.view.setVolume();
+            /*MainWindow.view.setVolume();*/
         }
         GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, Application.SIGINT, 
Application.application.onWindowDestroy);
         GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, Application.SIGTERM, 
Application.application.onWindowDestroy);
@@ -117,15 +141,17 @@ var Play = class Play {
             GLib.source_remove(this.timeout);
             this.timeout = null;
         }
-
+        /*
         if (MainWindow.wave != null)
             MainWindow.wave.endDrawing();
-
+        */
         errorDialogState = ErrState.OFF;
     }
 
     onEndOfStream() {
+      /*
         MainWindow.view.onPlayStopClicked();
+      */
     }
 
     _onMessageReceived(message) {
@@ -179,9 +205,9 @@ var Play = class Play {
         this.trackDurationSecs = this.trackDuration/Gst.SECOND;
 
         if (time >= 0 && this.playState != PipelineStates.STOPPED) {
-            MainWindow.view.setLabel(time);
+            this.emit("timer-updated", time);
         } else if (time >= 0 && this.playState == PipelineStates.STOPPED) {
-            MainWindow.view.setLabel(0);
+            this.emit("timer-updated", 0);
         }
 
         let absoluteTime = 0;
@@ -253,4 +279,5 @@ var Play = class Play {
             errorDialog.show();
         }
     }
-}
+  }
+);
diff --git a/src/record.js b/src/recorder.js
similarity index 85%
rename from src/record.js
rename to src/recorder.js
index 00405c5..5ab1586 100644
--- a/src/record.js
+++ b/src/recorder.js
@@ -32,8 +32,9 @@ const Signals = imports.signals;
 
 const Application = imports.application;
 const AudioProfile = imports.audioProfile;
+const WaveForm = imports.waveform.WaveForm;
 const MainWindow = imports.mainWindow;
-const Listview = imports.listview;
+const RecordingsManager = imports.recordingsManager;
 
 const PipelineStates = {
     PLAYING: 0,
@@ -55,17 +56,33 @@ const _TENTH_SEC = 100000000;
 
 let errorDialogState;
 
-var Record = class Record {
-    _recordPipeline() {
+var Recorder = GObject.registerClass({
+    Signals: {
+      'timer-updated': {
+          flags: GObject.SignalFlags.RUN_FIRST,
+          param_types: [ GObject.TYPE_INT ]
+      }
+    }
+
+  },class Recorder extends GObject.Object {
+    _init() {
+      super._init();
+
+        let audioProfile = new AudioProfile.AudioProfile();
+        this._mediaProfile = audioProfile.mediaProfile();
+        this.pipeline = null;
+        this.wave = null;
+        this.clipFile = null;
+    }
+
+    _recordPipeline(clipFile) {
         errorDialogState = ErrState.OFF;
+        this.clipFile = clipFile;
         this.baseTime = 0;
-        this._view = MainWindow.view;
-        this._buildFileName = new BuildFileName();
-        this.initialFileName = this._buildFileName.buildInitialFilename();
-        let localDateTime = this._buildFileName.getOrigin();
-        this.gstreamerDateTime = Gst.DateTime.new_from_g_date_time(localDateTime);
+        this.wave = new WaveForm(null);
 
-        if (this.initialFileName == -1) {
+        let gstreamerDateTime = Gst.DateTime.new_from_g_date_time(GLib.DateTime.new_now_local());
+        if (!clipFile) {
             this._showErrorDialog(_("Unable to create Recordings directory."));
             errorDialogState = ErrState.ON;
             this.onEndOfStream();
@@ -114,9 +131,9 @@ var Record = class Record {
                     this.taglist = Gst.TagList.new_empty();
                     this.taglist.add_value(Gst.TagMergeMode.APPEND, Gst.TAG_APPLICATION_NAME, _("Sound 
Recorder"));
                     element.merge_tags(this.taglist, Gst.TagMergeMode.REPLACE);
-                    this.taglist.add_value(Gst.TagMergeMode.APPEND, Gst.TAG_TITLE, this.initialFileName);
+                    this.taglist.add_value(Gst.TagMergeMode.APPEND, Gst.TAG_TITLE, clipFile.get_uri());
                     element.merge_tags(this.taglist, Gst.TagMergeMode.REPLACE);
-                    this.taglist.add_value(Gst.TagMergeMode.APPEND, Gst.TAG_DATE_TIME, 
this.gstreamerDateTime);
+                    this.taglist.add_value(Gst.TagMergeMode.APPEND, Gst.TAG_DATE_TIME, gstreamerDateTime);
                     element.merge_tags(this.taglist, Gst.TagMergeMode.REPLACE);
                 }
             }
@@ -125,7 +142,7 @@ var Record = class Record {
         let ebinProfile = this.ebin.set_property("profile", this._mediaProfile);
         let srcpad = this.ebin.get_static_pad("src");
         this.filesink = Gst.ElementFactory.make("filesink", "filesink");
-        this.filesink.set_property("location", this.initialFileName);
+        this.filesink.set_property("location", clipFile.get_path());
         this.pipeline.add(this.filesink);
 
         if (!this.pipeline || !this.filesink) {
@@ -154,51 +171,63 @@ var Record = class Record {
         let time = this.pipeline.query_position(Gst.Format.TIME)[1]/Gst.SECOND;
 
         if (time >= 0) {
-            this._view.setLabel(time, 0);
+            this.emit("timer-updated", time);
         }
 
         return true;
     }
 
-    startRecording(profile) {
-        this.profile = profile;
-        this._audioProfile = MainWindow.audioProfile;
-        this._mediaProfile = this._audioProfile.mediaProfile();
-
+    startNewRecording(uri) {
+        this.uri = uri;
         if (this._mediaProfile == -1) {
             this._showErrorDialog(_("No Media Profile was set."));
             errorDialogState = ErrState.ON;
         }
 
         if (!this.pipeline || this.pipeState == PipelineStates.STOPPED )
-            this._recordPipeline();
+            this._recordPipeline(uri);
 
         let ret = this.pipeline.set_state(Gst.State.PLAYING);
         this.pipeState = PipelineStates.PLAYING;
 
+          /* FIX ME
         if (ret == Gst.StateChangeReturn.FAILURE) {
             this._showErrorDialog(_("Unable to set the pipeline \n to the recording state."));
             errorDialogState = ErrState.ON;
-            this._buildFileName.getTitle().delete_async(GLib.PRIORITY_DEFAULT, null, null);
+            clip.delete_async(GLib.PRIORITY_DEFAULT, null, null);
         } else {
             MainWindow.view.setVolume();
-        }
+                  }
+        */
 
         if (!this.timeout) {
             this.timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MainWindow._SEC_TIMEOUT, () => 
this._updateTime());
         }
     }
 
+    isRecording ()  {
+      if (!this.pipeline)
+        return false;
+      return this.pipeline.get_state(10000) === Gst.State.PLAYING;
+    }
+
+    pauseRecording () {
+      this.pipeline.set_state(Gst.State.PAUSED);
+    }
+
+    resumeRecording () {
+      this.pipeline.set_state(Gst.State.PLAYING);
+    }
+
     stopRecording() {
         let sent = this.pipeline.send_event(Gst.Event.new_eos());
-
+        let clipFile = this.clipFile;
+        this.clipFile = null;
         if (this.timeout) {
             GLib.source_remove(this.timeout);
             this.timeout = null;
         }
-
-        if (MainWindow.wave != null)
-            MainWindow.wave.endDrawing();
+        return clipFile;
     }
 
     onEndOfStream() {
@@ -215,8 +244,8 @@ var Record = class Record {
     _onMessageReceived(message) {
         this.localMsg = message;
         let msg = message.type;
-        switch(msg) {
 
+        switch(msg) {
         case Gst.MessageType.ELEMENT:
             if (GstPbutils.is_missing_plugin_message(this.localMsg)) {
                 let errorOne = null;
@@ -271,7 +300,7 @@ var Record = class Record {
 
                             this.runTime = this.absoluteTime- this.baseTime;
                             let approxTime = Math.round(this.runTime/_TENTH_SEC);
-                            MainWindow.wave._drawEvent(approxTime, this.peak);
+                            this.wave._drawEvent(approxTime, this.peak);
                             }
                         }
                     }
@@ -343,27 +372,5 @@ var Record = class Record {
             errorDialog.show();
         }
     }
-}
-
-const BuildFileName = class BuildFileName {
-    buildInitialFilename() {
-        var fileExtensionName = MainWindow.audioProfile.fileExtensionReturner();
-        var dir = Gio.Application.get_default().saveDir;
-        this.dateTime = GLib.DateTime.new_now_local();
-        var clipNumber = Listview.trackNumber + 1;
-        /* Translators: ""Clip %d"" is the default name assigned to a file created
-            by the application (for example, "Clip 1"). */
-        var clipName = _("Clip %d").format(clipNumber.toString());
-        this.clip = dir.get_child_for_display_name(clipName);
-        var file = this.clip.get_path();
-        return file;
-    }
+});
 
-    getTitle() {
-        return this.clip;
-    }
-
-    getOrigin() {
-        return this.dateTime;
-    }
-}
diff --git a/src/recording.js b/src/recording.js
new file mode 100644
index 0000000..8608e74
--- /dev/null
+++ b/src/recording.js
@@ -0,0 +1,199 @@
+/* exported Record */
+/*
+ * Copyright 2019 Bilal Elmoussaoui
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *  Author: Bilal Elmoussaoui <bilal elmoussaoui gnome org>
+ *
+ */
+const Gtk = imports.gi.Gtk;
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const GObject = imports.gi.GObject;
+
+const Gst = imports.gi.Gst;
+const GstPbutils = imports.gi.GstPbutils;
+const Player = imports.player.Player;
+const utils = imports.utils;
+
+const WaveForm = imports.waveform.WaveForm;
+
+var Recording = GObject.registerClass({},
+  class Recording extends GObject.Object {
+
+    _init(file) {
+      super._init();
+      this._file = file;
+
+      this.fileName = null;
+      this.uri = null;
+      this.dateCreated = null;
+      this.duration = 0;
+
+      this._parseInfo();
+      this.wave = new WaveForm(this);
+    }
+
+    get file() {
+      return this._file;
+    }
+
+    _parseInfo() {
+        let returnedName = this._file.get_attribute_as_string("standard::display-name");
+
+        let filePath = GLib.build_filenamev([Gio.Application.get_default().saveDir.get_path(),
+                                             returnedName]);
+        this.uri = GLib.filename_to_uri(filePath, null);
+
+        if (this._file.has_attribute("time::created")) {
+            let dateCreatedVal = this._file.get_attribute_uint64("time::created");
+            this.dateCreated = GLib.DateTime.new_from_timeval_local(dateCreatedVal);
+        } else {
+          let modificationTime = this._file.get_modification_time();
+          this.dateCreated = GLib.DateTime.new_from_timeval_local(modificationTime);
+        }
+        this.fileName = returnedName;
+
+        let discoverer = new GstPbutils.Discoverer();
+        discoverer.start();
+        discoverer.discover_uri_async(this.uri);
+        discoverer.connect('discovered', (_discoverer, info, error) => {
+            let result = info.get_result();
+            log(result);
+            this._onDiscovererFinished(result, info, error);
+        });
+    }
+
+    _onDiscovererFinished(res, info, err) {
+
+        if (res == GstPbutils.DiscovererResult.OK) {
+
+            this.tagInfo = info.get_tags();
+
+            let dateTimeTag = this.tagInfo.get_date_time('datetime')[1];
+            let durationInfo = info.get_duration();
+            this.duration = durationInfo;
+
+            /* this.file.dateCreated will usually be null since time::created it doesn't usually exist.
+               Therefore, we prefer to set it with tags */
+            if (dateTimeTag != null) {
+                this.dateCreated =  dateTimeTag.to_g_date_time();;
+            }
+            /* FIX ME
+            this._getCapsForList(info);
+            */
+        } else {
+            // don't index files we can't play
+
+            log("File cannot be played");
+        }
+
+    }
+
+  }
+);
+
+
+var RecordingRow = GObject.registerClass({
+  Template: 'resource:///org/gnome/SoundRecorder/recording_row.ui',
+  InternalChildren: [
+    'clip_label',
+    'created_label',
+    'time_label',
+    'play_button',
+    'pause_button',
+    'overlay',
+    'buttons_stack'
+  ],
+  Signals: {
+  }
+  },
+  class RecordingRow extends Gtk.ListBoxRow {
+    _init(recording) {
+      super._init();
+
+      this._player = new Player();
+      this.recording = recording;
+      this._clip_label.set_text(recording.fileName);
+      this._created_label.set_text(utils.getDisplayTime(recording.dateCreated));
+
+      this._overlay.add_overlay(recording.wave);
+
+      this._player.setUri(recording.uri);
+
+      this._play_button.connect('clicked', () => {
+        this._player.startPlaying();
+        this._buttons_stack.set_visible_child_name("pause");
+      });
+
+      this._pause_button.connect('clicked', () => {
+        this._player.pausePlaying();
+        this._buttons_stack.set_visible_child_name("play");
+      });
+
+      this._player.connect("timer-updated", (obj, seconds)=> {
+
+        this._time_label.set_text(utils.getDisplayDuration(seconds));
+      })
+
+      this.show_all();
+    }
+  }
+);
+
+
+var NewRecording = GObject.registerClass({
+  Template: 'resource:///org/gnome/SoundRecorder/recording_waveform_row.ui',
+  InternalChildren: [
+    'overlay',
+    'record_time_label'
+  ],
+  Signals: {
+      'paused': {
+        flags: GObject.SignalFlags.RUN_FIRST
+      },
+      'resumed': {
+        flags: GObject.SignalFlags.RUN_FIRST
+      }
+  },
+  }, class NewRecording extends Gtk.Box {
+
+    _init() {
+      super._init();
+
+
+      this._state = 0;
+
+      this.show_all();
+    }
+
+    updateRecordTime(seconds) {
+      this._record_time_label.set_text(utils.getDisplayDuration(seconds));
+    }
+
+    setWave(wave) {
+        this._overlay.add_overlay(wave);
+    }
+
+    reset() {
+        this._overlay.remove(this._overlay.get_children()[0])
+        this.updateRecordTime(0);
+
+    }
+
+
+  }
+);
+
+
diff --git a/src/recordingsManager.js b/src/recordingsManager.js
new file mode 100644
index 0000000..2468098
--- /dev/null
+++ b/src/recordingsManager.js
@@ -0,0 +1,251 @@
+/* exported Listview */
+/*
+ * Copyright 2013 Meg Ford
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Author: Meg Ford <megford gnome org>
+ *
+ */
+
+const _ = imports.gettext.gettext;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gst = imports.gi.Gst;
+const GstPbutils = imports.gi.GstPbutils;
+const Signals = imports.signals;
+
+
+const Recording  = imports.recording.Recording;
+const AudioProfile = imports.audioProfile;
+const MainWindow = imports.mainWindow;
+const Recorder = imports.recorder.Recorder;
+
+
+const utils = imports.utils;
+
+const EnumeratorState = {
+    ACTIVE: 0,
+    CLOSED: 1
+};
+
+const mediaTypeMap = {
+    FLAC: "FLAC",
+    OGG_VORBIS: "Ogg Vorbis",
+    OPUS: "Opus",
+    MP3: "MP3",
+    MP4: "MP4"
+};
+
+const ListType = {
+    NEW: 0,
+    REFRESH: 1
+};
+
+const CurrentlyEnumerating = {
+    TRUE: 0,
+    FALSE: 1
+};
+
+
+let currentlyEnumerating = null;
+let fileInfo = null;
+let listType = null;
+let startRecording = false;
+let stopVal = null;
+
+var RecordingsManager = GObject.registerClass({
+    Signals: {
+      'recording-added': {
+        flags: GObject.SignalFlags.RUN_FIRST,
+        param_types: [ GObject.Object ]
+      }
+    }
+  },
+  class RecordingsManager extends GObject.Object {
+    _init() {
+        super._init();
+
+        stopVal = EnumeratorState.ACTIVE;
+        this._recordings = [];
+
+        this._recorder = new Recorder();
+
+        // Save a reference to the savedir to quickly access it
+        this._saveDir = Gio.Application.get_default().saveDir;
+        this.enumerateDirectory();
+        this.dirMonitor = this._saveDir.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, null);
+        this.dirMonitor.connect('changed', (dirMonitor, file1, file2, eventType) => 
this._onDirChanged(dirMonitor, file1, file2, eventType));
+
+    }
+
+    getTracksCount() {
+      return this._recordings.length;
+    }
+
+    startNewRecording() {
+      if (!this._recorder.isRecording()) {
+          var dir = Gio.Application.get_default().saveDir;
+          var clipNumber = this._recordings.length + 1;
+          /* Translators: ""Clip %d"" is the default name assigned to a file created
+              by the application (for example, "Clip 1"). */
+          var clipName = _("Clip %d").format(clipNumber.toString());
+          let clipFile = dir.get_child_for_display_name(clipName);
+          this._recorder.startNewRecording(clipFile);
+          return this._recorder.wave;
+      }
+    }
+
+    saveRecording() {
+      let clipFile = this._recorder.stopRecording();
+      let fileInfo = clipFile.query_info('standard::display-name,time::created,time::modified',
+                                          Gio.FileQueryInfoFlags.NONE, null)
+      let recording = new Recording(fileInfo);
+      this._recordings.push(recording);
+      this.emit("recording-added", recording);
+
+    }
+
+    enumerateDirectory() {
+        this._saveDir.enumerate_children_async('standard::display-name,time::created,time::modified',
+                                     Gio.FileQueryInfoFlags.NONE,
+                                     GLib.PRIORITY_LOW,
+                                     null,
+                                     (obj, res) => this._onEnumerator(obj, res));
+    }
+
+    _onEnumerator(obj, res) {
+        this._enumerator = obj.enumerate_children_finish(res);
+
+        if (this._enumerator == null)
+            log("The contents of the Recordings directory were not indexed.");
+        else
+            this._onNextFileComplete();
+    }
+
+    _onNextFileComplete () {
+        fileInfo = [];
+        try{
+            this._enumerator.next_files_async(20, GLib.PRIORITY_DEFAULT, null, (obj, res) => {
+                let files = obj.next_files_finish(res);
+
+
+                files.forEach((file) => {
+
+                   let recording = new Recording(file);
+                   this._recordings.push(recording);
+                    this.emit("recording-added", recording);
+
+                });
+                // this._sortItems(fileInfo);
+            });
+        } catch(e) {
+            log(e);
+        }
+    }
+
+
+    _onDirChanged(dirMonitor, file1, file2, eventType) {
+
+        if (eventType == Gio.FileMonitorEvent.DELETED && Gio.Application.get_default().saveDir.equal(file1)) 
{
+            Gio.Application.get_default().ensure_directory();
+            this._saveDir = Gio.Application.get_default().saveDir;
+        }
+        if ((eventType == Gio.FileMonitorEvent.MOVED_OUT) ||
+            (eventType == Gio.FileMonitorEvent.CHANGES_DONE_HINT
+                && MainWindow.recordPipeline == MainWindow.RecordPipelineStates.STOPPED) || (eventType == 
Gio.FileMonitorEvent.RENAMED)) {
+            stopVal = EnumeratorState.ACTIVE;
+
+            listType = ListType.REFRESH;
+
+            if (currentlyEnumerating == CurrentlyEnumerating.FALSE) {
+                currentlyEnumerating = CurrentlyEnumerating.TRUE;
+                MainWindow.view.listBoxRefresh();
+            }
+        }
+        log(eventType);
+        if (eventType == Gio.FileMonitorEvent.CHANGES_DONE_HINT ) {
+
+        }
+    }
+
+
+    _onDirChangedDeb(dirMonitor, file1, file2, eventType) {
+        /* Workaround for Debian and Tails not recognizing Gio.FileMointor.WATCH_MOVES */
+        if (eventType == Gio.FileMonitorEvent.DELETED && Gio.Application.get_default().saveDir.equal(file1)) 
{
+            Gio.Application.get_default().ensure_directory();
+            this._saveDir = Gio.Application.get_default().saveDir;
+        }
+        if ((eventType == Gio.FileMonitorEvent.DELETED) ||
+            (eventType == Gio.FileMonitorEvent.CHANGES_DONE_HINT && MainWindow.recordPipeline == 
MainWindow.RecordPipelineStates.STOPPED)) {
+            stopVal = EnumeratorState.ACTIVE;
+            allFilesInfo.length = 0;
+            fileInfo.length = 0;
+            this.idx = 0;
+            listType = ListType.REFRESH;
+
+            if (currentlyEnumerating == CurrentlyEnumerating.FALSE) {
+                currentlyEnumerating = CurrentlyEnumerating.TRUE;
+                MainWindow.view.listBoxRefresh();
+            }
+        }
+
+        else if (eventType == Gio.FileMonitorEvent.CREATED) {
+            log("hey) ")
+            startRecording = true;
+        }
+    }
+
+    _getCapsForList(info) {
+        let discovererStreamInfo = null;
+        discovererStreamInfo = info.get_stream_info();
+        let containerStreams = info.get_container_streams()[0];
+        let containerCaps = discovererStreamInfo.get_caps();
+        let audioStreams = info.get_audio_streams()[0];
+        let audioCaps =  audioStreams.get_caps();
+
+        if (containerCaps.can_intersect(this.capTypes(AudioProfile.containerProfileMap.AUDIO_OGG))) {
+
+            if (audioCaps.can_intersect(this.capTypes(AudioProfile.audioCodecMap.VORBIS)))
+                allFilesInfo[this.idx].mediaType = mediaTypeMap.OGG_VORBIS;
+            else if (audioCaps.can_intersect(this.capTypes(AudioProfile.audioCodecMap.OPUS)))
+                allFilesInfo[this.idx].mediaType = mediaTypeMap.OPUS;
+
+        } else if (containerCaps.can_intersect(this.capTypes(AudioProfile.containerProfileMap.ID3))) {
+
+            if (audioCaps.can_intersect(this.capTypes(AudioProfile.audioCodecMap.MP3)))
+                allFilesInfo[this.idx].mediaType = mediaTypeMap.MP3;
+
+        } else if (containerCaps.can_intersect(this.capTypes(AudioProfile.containerProfileMap.MP4))) {
+
+            if (audioCaps.can_intersect(this.capTypes(AudioProfile.audioCodecMap.MP4)))
+                allFilesInfo[this.idx].mediaType = mediaTypeMap.MP4;
+
+        } else if (audioCaps.can_intersect(this.capTypes(AudioProfile.audioCodecMap.FLAC))) {
+            allFilesInfo[this.idx].mediaType = mediaTypeMap.FLAC;
+
+        }
+
+        if (allFilesInfo[this.idx].mediaType == null) {
+                // Remove the file from the array if we don't recognize it
+                allFilesInfo.splice(this.idx, 1);
+        }
+    }
+
+    capTypes(capString) {
+        let caps = Gst.Caps.from_string(capString);
+        return caps;
+    }
+});
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..e052954
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,79 @@
+/* exported getDisplayTime */
+/*
+ * Copyright 2013 Meg Ford
+ * Copyright (c) 2013 Giovanni Campagna <scampa giovanni gmail com>
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Meg Ford <megford gnome org>
+ *
+ */
+ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+
+const Gdk = imports.gi.Gdk;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+
+
+const Gettext = imports.gettext;
+const _ = imports.gettext.gettext;
+
+
+var getDisplayTime = function (mtime) {
+    let text = "";
+    let DAY = 86400000000;
+    let now = GLib.DateTime.new_now_local();
+    let difference = now.difference(mtime);
+    let days = Math.floor(difference / DAY);
+    let weeks = Math.floor(difference / (7 * DAY));
+    let months = Math.floor(difference / (30 * DAY));
+    let years = Math.floor(difference / (365 * DAY));
+
+    if (difference < DAY) {
+        let time = mtime.format('%X');
+        text = _(`Today, ${time}`);
+    } else if (difference < 2 * DAY) {
+        text = _("Yesterday");
+    } else if (difference < 7 * DAY) {
+        text = Gettext.ngettext("%d day ago",
+                                "%d days ago",
+                                 days).format(days);
+    } else if (difference < 14 * DAY) {
+        text = _("Last week");
+    } else if (difference < 28 * DAY) {
+        text = Gettext.ngettext("%d week ago",
+                                 "%d weeks ago",
+                                 weeks).format(weeks);
+    } else if (difference < 60 * DAY) {
+        text = _("Last month");
+    } else if (difference < 360 * DAY) {
+        text = Gettext.ngettext("%d month ago",
+                                 "%d months ago",
+                                 months).format(months);
+    } else if (difference < 730 * DAY) {
+        text = _("Last year");
+    } else {
+        text = Gettext.ngettext("%d year ago",
+                                "%d years ago",
+                                years).format(years);
+    }
+    return text;
+}
+
+
+var getDisplayDuration = function (seconds) {
+    let hoursStr = String(parseInt((seconds / 3600) % 60)).padStart(2, '0')
+    let minutesStr = String(parseInt((seconds / 60) % 60)).padStart(2, '0')
+    let secondsStr = String(parseInt(seconds % 60)).padStart(2, '0')
+    return `${hoursStr}:${minutesStr}:${secondsStr}`;
+}
diff --git a/src/waveform.js b/src/waveform.js
index 6813884..7ab1351 100644
--- a/src/waveform.js
+++ b/src/waveform.js
@@ -45,10 +45,9 @@ const WaveType = {
     PLAY: 1
 };
 
-var WaveForm = class WaveForm {
-    constructor(grid, file) {
-        this._grid = grid;
-
+var WaveForm =  GObject.registerClass(class WaveForm extends Gtk.DrawingArea {
+    _init(file) {
+        super._init();
         let placeHolder = -100;
         for (let i = 0; i < 40; i++)
             peaks.push(placeHolder);
@@ -56,34 +55,30 @@ var WaveForm = class WaveForm {
             this.waveType = WaveType.PLAY;
             this.file = file;
             this.duration = this.file.duration;
+
             this._uri = this.file.uri;
+            log(this.duration );
         } else {
           this.waveType = WaveType.RECORD;
         }
+        this.recordTime = -1;
+        this.playTime = 0;
 
         let gridWidth = 0;
         let drawingWidth = 0;
         let drawingHeight = 0;
-        this.drawing = Gtk.DrawingArea.new();
-        if (this.waveType == WaveType.RECORD) {
-            this.drawing.set_property("valign", Gtk.Align.FILL);
-            this.drawing.set_property("hexpand",true);
-            this._grid.attach(this.drawing, 2, 0, 3, 2);
-        } else {
-            this.drawing.set_property("valign", Gtk.Align.FILL);
-            this.drawing.set_property("hexpand",true);
-            this.drawing.set_property("vexpand",true);
-            this._grid.add(this.drawing);
-        }
 
-        this.drawing.connect("draw", (drawing, cr) => this.fillSurface(drawing, cr));
-        this.drawing.show_all();
-        this._grid.show_all();
+        this.set_property("hexpand", true);
+
+        this.connect("draw", (drawing, cr) => this.fillSurface(drawing, cr));
 
         if (this.waveType == WaveType.PLAY) {
             this._launchPipeline();
             this.startGeneration();
+
         }
+        this.show_all();
+
     }
 
     _launchPipeline() {
@@ -93,6 +88,7 @@ var WaveForm = class WaveForm {
         let decode = this.pipeline.get_by_name("decode");
         let bus = this.pipeline.get_bus();
         bus.add_signal_watch();
+
         GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, Application.SIGINT, 
Application.application.onWindowDestroy);
         GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, Application.SIGTERM, 
Application.application.onWindowDestroy);
 
@@ -107,7 +103,6 @@ var WaveForm = class WaveForm {
 
     _messageCb(message) {
         let msg = message.type;
-
         switch(msg) {
         case Gst.MessageType.ELEMENT:
             let s = message.get_structure();
@@ -154,6 +149,7 @@ var WaveForm = class WaveForm {
 
     stopGeneration() {
         this.pipeline.set_state(Gst.State.NULL);
+
     }
 
     fillSurface(drawing, cr) {
@@ -168,14 +164,16 @@ var WaveForm = class WaveForm {
         } else {
             if (this.recordTime >= 0) {
                 start = this.recordTime;
+            } else {
+              return
             }
         }
 
         let i = 0;
         let xAxis = 0;
         let end = start + 40;
-        let width = this.drawing.get_allocated_width();
-        let waveheight = this.drawing.get_allocated_height();
+        let width = this.get_allocated_width();
+        let waveheight = this.get_allocated_height();
         let length = this.nSamples;
         let pixelsPerSample = width/waveSamples;
         let gradient = new Cairo.LinearGradient(0, 0, width , waveheight);
@@ -231,7 +229,7 @@ var WaveForm = class WaveForm {
             }
 
             if (lastTime != this.playTime) {
-                this.drawing.queue_draw();
+                this.queue_draw();
             }
 
         } else {
@@ -243,8 +241,7 @@ var WaveForm = class WaveForm {
                 log("error");
                 return true;
             }
-            if (this.drawing)
-                this.drawing.queue_draw();
+            this.queue_draw();
         }
         return true;
     }
@@ -255,10 +252,5 @@ var WaveForm = class WaveForm {
 
         this.count = 0;
         peaks.length = 0;
-        try {
-            this.drawing.destroy();
-        } catch (e) {
-            log(e);
-        }
     }
-}
+});


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]