diff --git a/umbra/controller.py b/umbra/controller.py
index a817fe7a91ddae309824b1fd612d15990e78dfde..dc63a1bb2d81676321e9b2adbe39b3ac7376006a 100644
--- a/umbra/controller.py
+++ b/umbra/controller.py
@@ -1,7 +1,6 @@
 from filereader import FileReader, CSVReader, TxtReader
 from filereader import FileWriter, CSVWriter, TxtWriter
 from utils import Utils as ut
-import tkinter.filedialog as fd
 import os
 
 
@@ -57,12 +56,14 @@ class Controller:
         Select a folder from which multiple files can be read.
         """
         folder_path = self._view.dir_dialog()
-        file_names = os.listdir(folder_path)
-        file_paths = [folder_path + "/" + x for x in file_names]
-        self._set_path(file_paths, type)
-        self._view.update_files(file_paths, type)
-        if file_paths:
-            self._view.button_status("select {}".format(type), "disabled")
+        if folder_path:
+            file_names = os.listdir(folder_path)
+            file_paths = [folder_path + "/" + x for x in file_names \
+                            if ".csv" in x]
+            self._set_path(file_paths, type)
+            self._view.update_files(file_paths, type)
+            if file_paths:
+                self._view.button_status("select {}".format(type), "disabled")
         # TODO: Call 'buttons' by functionality, not object (type)
     def _set_path(self, paths, type):
         if type == 'source':
diff --git a/umbra/view.py b/umbra/view.py
index d97a6f3f095f995284f51ff2d913d9447cdf94cb..6617179ffdfbf541273052533ac3770151d2fb20 100644
--- a/umbra/view.py
+++ b/umbra/view.py
@@ -25,7 +25,7 @@ class View:
         self._elements = {}  # Dict to avoid large number of attributes
         self._selected_source = "No file selected"
         self._selected_shadow = "No file selected"
-        # self._options = Options(self)
+        #self._options = Options(self)
         # Options are not redundant quite yet
         self._actionlistener = None
 
@@ -67,7 +67,7 @@ class View:
 
     def display(self):
         """Start main loop, displaying GUI elements."""
-        self._splash.terminate()
+        #self._splash.terminate()
         self._window.deiconify()
         self._window.mainloop()
 
diff --git a/umbra_app/umbra/controller.py b/umbra_app/umbra/controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..a54f8faf1bae9f3ae3ec86d5d442831766c32b31
--- /dev/null
+++ b/umbra_app/umbra/controller.py
@@ -0,0 +1,203 @@
+from filereader import CSVReader
+from filereader import CSVWriter
+from utils import Utils as ut
+import os
+
+
+class Controller:
+    """Control of general functionality and communication between Model
+    and View classes.
+    """
+
+    def __init__(self, view, model):
+        self._model = model
+        self._view = view
+        self._view.actionlistener = self.actionlistener
+
+        # Assume .csv only
+        self._filereader = CSVReader()
+        self._filewriter = CSVWriter()
+
+        # Lists of paths, represented as string
+        self._source_files = []
+        self._shadow_files = []
+
+    @property
+    def source_files(self):
+        return self._source_files
+
+    @source_files.setter
+    def shadow_files(self, val):
+        self._source_files = val
+
+    @property
+    def shadow_files(self):
+        return self._shadow_files
+
+    @shadow_files.setter
+    def shadow_files(self, val):
+        self._shadow_files = val
+
+    def start(self):
+        """Start program user interface."""
+        self._view.display()
+
+    def actionlistener(self, key):
+        """Action listener for view events. Called by view.
+
+        Args:
+            key(str): Identifier for view functionality
+        """
+        action, *type = key.split(' ')  # type is a (possibly empty) list
+        if action == 'select':
+            self._select_files(type[0])
+        elif action == 'delete':
+            self._delete_files(type[0])
+        elif action == 'compare':
+            self._compare_files()
+        elif action == 'save':
+            self._save_results()
+        elif action == 'save_csv':
+            self._filewriter = CSVWriter()
+        elif action == 'save_txt':
+            self._filewriter = TxtWriter()
+        elif action == 'select_folder':
+            self._select_folder(type[0])
+        elif action == 'rm_all':
+            self._delete_files(type[0], True)
+
+    def _select_folder(self, type="shadow"):
+        """
+        Select a folder from which multiple files can be read.
+        """
+        folder_path = self._view.dir_dialog()
+        if folder_path:
+            file_names = os.listdir(folder_path)
+            file_paths = [folder_path + "/" + x for x in file_names]
+            self._set_path(file_paths, type)
+            self._view.update_files(file_paths, type)
+            if file_paths:
+                self._view.button_status("select {}".format(type), "disabled")
+        # TODO: Call 'buttons' by functionality, not object (type)
+        else:
+            self._view.update_message('no_file')
+
+    def _set_path(self, paths, type):
+        if type == 'source':
+            self._source_files = paths
+        else:
+            self._shadow_files = paths
+
+    def _select_files(self, type):
+        """Select files and add them to files list corresponding to type.
+
+        Args:
+            type (str): Role of files ('source' or 'shadow')
+        """
+        files = getattr(self, '_{}_files'.format(type))
+        selection = self._view.select_files(type)
+        # TODO: Check if not already present
+        files.extend(selection)
+        self._view.update_files(files, type)
+        if files:
+            self._view.button_status("select_folder {}".format(type),
+                "disabled")
+
+    def _delete_files(self, type, all=False):  # TODO: Not yet implemented fully
+        """Remove selected file from files list corresponding to type.
+
+        Args:
+            type (str): Role of files ('source' or 'shadow')
+            all (boolean): whether to remove all files, by default false.
+        """
+        selection = self._view.selected(type)
+        files = getattr(self, '_{}_files'.format(type))
+        if selection is not None:  # TODO: This is supposed to make sure deletion of 'No file selected' is impossible, but does not work.
+            if all:
+                selection = [file for file in files]
+                for s in selection:
+                    files.remove(s)
+                self._view.button_status("select {}".format(type), "normal")
+            else:
+                selection = [file for file in files if selection.lower() in file.lower()][0]
+                files.remove(selection)
+                self._view.button_status("select_folder {}".format(type), "normal")
+            self._view.update_files(files, type)
+
+    def _compare_files(self):
+        """Check source and shadow data availability, read in, and compare.
+            If multiple, read in and compare accordingly.
+        """
+        if not self._source_files:
+            self._view.update_message('no source')
+        elif not self._shadow_files:
+            self._view.update_message('no shadow')
+        else:
+            self._view.update_message('files ok')
+            self._view.button_status("save", "normal")
+
+            self._read_folder("shadow")
+            self._read_folder("source")
+
+            self._model.compare()
+            if self._model.analysis_complete:
+                self._view.update_message('comparison complete')
+                self._view.button_status("compare", "disabled")
+
+    def _save_results(self):
+        """Save analysis results to file. If there is multiple results,
+        save those.
+        """
+        if not self._model.analysis_complete():
+            self._view.update_message('no comparison')
+        else:
+
+            if self._model.multi_results:
+                path = self._view.ask_save_location()
+                results = self._model.multi_results
+                self._filewriter.write_multiple(path, results)
+                self._view.update_message('saved')
+            elif not self._model.multi_results \
+                    and not self._model.analysis_results:
+                self._view.update_message('nonexistent')
+
+    """
+        def _read_files(self, path, type):
+            Read data from file paths and save to model.
+
+            Args:
+                type (str): Role of file ('source' or 'shadow')
+
+            data = self._filereader.read(path, type)
+            if type == "source":
+                self._model.data_source = data
+                self._model.id = ut.id_regex(path)
+            else:
+                self._model.data_shadow = data
+    """
+
+    def _read_folder(self, type):
+        """
+        Read in multiple source files.
+        Extensible to consider "type". Now assume shadow
+        files.
+        """
+        if type == "shadow":
+            for file_path in self._shadow_files:
+                if "AO" not in file_path or ".csv" not in file_path:
+                    # Very dirty
+                    pass  # TODO: raise error
+                else:
+                    dict = self._model.get_multi_data(type)
+                    key = ut.shadow_regex(file_path)
+                    data = self._filereader.read(file_path, type)
+                    ut.add_to_dict(key, data, dict, alternative=True)
+        elif type == "source":
+            for file_path in self._source_files:
+                if ".csv" not in file_path:
+                    pass
+                else:
+                    dict = self._model.get_multi_data(type)
+                    key = ut.id_regex(file_path)
+                    data = self._filereader.read(file_path, type)
+                    ut.add_to_dict(key, data, dict, alternative=True)