Index: src/Core/Banshee/Main.cs
===================================================================
--- src/Core/Banshee/Main.cs	(revision 2166)
+++ src/Core/Banshee/Main.cs	(working copy)
@@ -115,6 +115,10 @@
                 new Banshee.PlayerUI();
             });
             
+            if(UpdateManager.ScanEnabledSchema.Get()) {
+                UpdateManager.Instance.StartListening();
+            }
+            
             program.Run();
         }
     
Index: src/Core/Banshee.Base/Gui/PreferencesDialog.cs
===================================================================
--- src/Core/Banshee.Base/Gui/PreferencesDialog.cs	(revision 2166)
+++ src/Core/Banshee.Base/Gui/PreferencesDialog.cs	(working copy)
@@ -46,8 +46,10 @@
         [Widget] private Button library_reset;
         [Widget] private CheckButton copy_on_import;
         [Widget] private CheckButton write_metadata;
+        [Widget] private CheckButton scan_filesystem;
         [Widget] private CheckButton error_correction;
         [Widget] private Table organization_table;
+        [Widget] private Button close_button;
         
         private Tooltips tips = new Tooltips();
         private FileChooserButton library_location_chooser;
@@ -55,6 +57,8 @@
         private DictionaryComboBox<string> folder_box;
         private DictionaryComboBox<string> file_box;
         
+        private string initialLibraryLocation;
+        
         public PreferencesDialog() : base("PreferencesDialog")
         {        
             BuildWindow();
@@ -120,6 +124,10 @@
                 "Enable this option to save tags and other metadata inside supported audio files"),
                 "write-metadata");
                 
+            tips.SetTip(Glade["scan_filesystem"], Catalog.GetString(
+                "Enable this option to automatically scan your music folders for new songs"),
+                "scan-filesystem");
+                
             tips.SetTip(Glade["error_correction"], Catalog.GetString(
                 "Error correction tries to work around problem areas on a disc, such " +
                 "as surface scratches, but will slow down importing substantially."),
@@ -133,6 +141,7 @@
                 location = Paths.DefaultLibraryPath;
             }
             
+            initialLibraryLocation = location;
             library_location_chooser.SetFilename(location);
             Globals.Library.Location = location;   
             
@@ -142,6 +151,7 @@
 
             copy_on_import.Active = LibrarySchema.CopyOnImport.Get();
             write_metadata.Active = LibrarySchema.WriteMetadata.Get();
+            scan_filesystem.Active = UpdateManager.ScanEnabledSchema.Get();
             error_correction.Active = ImportSchema.AudioCDErrorCorrection.Get();
         }
         
@@ -163,6 +173,15 @@
             write_metadata.Toggled += delegate {
                 LibrarySchema.WriteMetadata.Set(write_metadata.Active);
             };
+            
+            scan_filesystem.Toggled += delegate {
+                UpdateManager.ScanEnabledSchema.Set(scan_filesystem.Active);
+                if(scan_filesystem.Active) {
+                    UpdateManager.Instance.StartListening();
+                } else {
+                    UpdateManager.Instance.StopListenting();
+                }
+            };
 
             error_correction.Toggled += delegate {
                 ImportSchema.AudioCDErrorCorrection.Set(error_correction.Active);
@@ -170,6 +189,13 @@
             
             folder_box.Changed += OnFolderFileChanged;
             file_box.Changed += OnFolderFileChanged;
+            
+            close_button.Clicked += delegate {
+                if(Globals.Library.Location != initialLibraryLocation) {
+                    UpdateManager.Instance.RemoveDirectoryFromWatchList(initialLibraryLocation);
+                    UpdateManager.Instance.AddDirectoryToWatchList(Globals.Library.Location, false);
+                }
+            };
         }
         
         private void OnFolderFileChanged(object o, EventArgs args)
Index: src/Core/Banshee.Base/FolderImportSource.cs
===================================================================
--- src/Core/Banshee.Base/FolderImportSource.cs	(revision 2166)
+++ src/Core/Banshee.Base/FolderImportSource.cs	(working copy)
@@ -62,6 +62,7 @@
             
             if(chooser.Run() == (int)ResponseType.Ok) { 
                 Banshee.Library.Import.QueueSource(chooser.Uri);
+                UpdateManager.Instance.AddDirectoryToWatchList(chooser.Uri);
             }
             
             chooser.Destroy();
Index: src/Core/Banshee.Base/ImportManager.cs
===================================================================
--- src/Core/Banshee.Base/ImportManager.cs	(revision 2166)
+++ src/Core/Banshee.Base/ImportManager.cs	(working copy)
@@ -63,6 +63,17 @@
                 return instance;
             }
         }
+        private static ImportManager updateInstance;
+        public static ImportManager UpdateInstance {
+            get {
+                if(updateInstance == null) {
+                    updateInstance = new ImportManager();
+                    updateInstance.update = true;
+                }
+                
+                return updateInstance;
+            }
+        }
         
         private static Gdk.Pixbuf user_event_icon = IconThemeUtils.LoadIcon(22, "system-search", Stock.Find);
         private static readonly object user_event_mutex = new object();
@@ -73,6 +84,7 @@
         private int processed_count;
         private int scan_ref_count = 0;
         private bool processing_queue = false;
+        private bool update;
         
         public event ImportEventHandler ImportRequested;
         public event EventHandler ImportFinished;
@@ -140,7 +152,9 @@
         {
             path_queue.Clear();
             processing_queue = false;
-            DestroyUserEvent();
+            if(!update) {
+                DestroyUserEvent();
+            }
         }
         
         private void Enqueue(string path)
@@ -157,7 +171,9 @@
         
         public void QueueSource(UriList uris)
         {
-            CreateUserEvent();
+            if(!update) {
+                CreateUserEvent();
+            }
             ThreadAssist.Spawn(delegate {
                 try {
                     foreach(string path in uris.LocalPaths) {
@@ -246,30 +262,30 @@
             
             processing_queue = true;
             
-            using(new Banshee.Base.Timer("Importing")) { 
-            
+            using(new Banshee.Base.Timer("Importing")) {
             while(path_queue.Count > 0) {
                 CheckForCanceled();
                 
                 string filename = path_queue.Dequeue() as string;
                     
+                string returnMessage = null;
                 ImportEventHandler handler = ImportRequested;
                 if(handler != null && filename != null) {
                     ImportEventArgs args = new ImportEventArgs();
                     args.FileName = filename;
                     handler(this, args);
-                    UpdateCount(args.ReturnMessage);
-                } else {
-                    UpdateCount(null);
+                    returnMessage = args.ReturnMessage;
                 }
+                if(!update) {
+                    UpdateCount(returnMessage);
+                }
             }
-            
             }
             
             path_queue.Clear();
             processing_queue = false;
             
-            if(scan_ref_count == 0) {
+            if(scan_ref_count == 0 && !update) {
                 DestroyUserEvent();
                 if(ImportFinished != null) {
                     ImportFinished(this, new EventArgs());
Index: src/Core/Banshee.Base/UpdateManager.cs
===================================================================
--- src/Core/Banshee.Base/UpdateManager.cs	(revision 0)
+++ src/Core/Banshee.Base/UpdateManager.cs	(revision 0)
@@ -0,0 +1,397 @@
+/***************************************************************************
+ *  UpdateManager.cs
+ *
+ *  Written by Scott Peterson <lunchtimemama@gmail.com>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Threading;
+
+using Mono.Unix;
+
+using Banshee.Configuration;
+using Banshee.IO;
+using Banshee.Widgets;
+
+namespace Banshee.Base
+{
+    public class UpdateManager
+    {
+        private class DirectoryScanInfo : IDisposable
+        {
+            public readonly string Uri;
+            public DateTime LastChanged;
+            private FileSystemWatcher watcher;
+            public event FileSystemEventHandler Created;
+            
+            public DirectoryScanInfo(string uri, DateTime last_changed)
+            {
+                Uri = uri;
+                LastChanged = last_changed;
+                watcher = new FileSystemWatcher();
+                SetupWatcher();
+            }
+            
+            private void SetupWatcher()
+            {
+                watcher.Path = Uri;
+                watcher.NotifyFilter = NotifyFilters.LastWrite;
+                watcher.IncludeSubdirectories = true;
+                watcher.Created += new FileSystemEventHandler(OnCreated);
+                watcher.Error += new ErrorEventHandler(OnError);
+                watcher.EnableRaisingEvents = true;
+            }
+            
+            private void OnError(object o, ErrorEventArgs args)
+            {
+                watcher = new FileSystemWatcher();
+                while(!watcher.EnableRaisingEvents) {
+                    try {
+                        SetupWatcher();
+                    } catch {
+                        Thread.Sleep(5000);
+                    }
+                }
+            }
+            
+            private void OnCreated(object sender, FileSystemEventArgs args)
+            {
+                if(Created != null) {
+                    Created(this, args);
+                }
+            }
+            
+            public void Dispose()
+            {
+                watcher.Dispose();
+            }
+        }
+
+        private List<DirectoryScanInfo> directories = new List<DirectoryScanInfo>();
+        private Thread thread;
+        private bool updated;
+        
+        private ActiveUserEvent user_event;
+        private System.Threading.Timer user_event_timer;
+        private readonly object user_event_mutex = new object();
+        
+        private System.Threading.Timer refresh_timer;
+        private readonly object refresh_timer_mutex = new object();
+
+        private static Gdk.Pixbuf user_event_icon = IconThemeUtils.LoadIcon(22, "system-search", Gtk.Stock.Find);
+        
+        private static TimeSpan threshold = new TimeSpan(0, 0, 2);
+
+        private static UpdateManager instance;
+        public static UpdateManager Instance {
+            get {
+                if(instance == null) {
+                    instance = new UpdateManager();
+                }
+                return instance;
+            }
+        }
+
+        private volatile bool user_event_timer_delay; // lame, but better than a bunch of Threading.Timer allocs
+        private void CreateUserEvent()
+        {
+            user_event_timer_delay = true;
+            if(user_event == null) {
+                lock(user_event_mutex) {
+                    user_event = new ActiveUserEvent(Catalog.GetString("Found New Music"));
+                    user_event.Message = Catalog.GetString("Scanning") + "...";
+                    user_event.Icon = user_event_icon;
+                    user_event_timer = new System.Threading.Timer(new TimerCallback(OnUserEventTimer), null, 1000, 3000);
+                }
+            }
+        }
+
+        private void OnUserEventTimer(object o)
+        {
+            if(user_event_timer_delay) {
+                user_event_timer_delay = false;
+                return;
+            }
+            lock(user_event_mutex) {
+                user_event.Dispose();
+                user_event = null;
+                user_event_timer.Dispose();
+                user_event_timer = null;
+            }
+        }
+        
+        private void OnImportRequested(object o, ImportEventArgs args)
+        {
+            bool retry = false;
+            do {
+                try {
+                    retry = false;
+                    TrackInfo ti = new LibraryTrackInfo(args.FileName);
+                    CreateUserEvent();
+                    user_event.Message = String.Format("{0} - {1}", ti.Artist, ti.Title);
+                } catch(Exception e) {
+                    // This happens if a song isn't done copying
+                    if(e.Message == "Array index is out of range.") {
+                        Thread.Sleep(2000);
+                        retry = true;
+                    }
+                }
+            } while(retry);
+        }
+        
+        private void QueueUpdate(string path)
+        {
+            if(!updated) {
+                ImportManager.UpdateInstance.ImportRequested += new ImportEventHandler(OnImportRequested);
+                updated = true;
+            }
+            ImportManager.UpdateInstance.QueueSource(path);
+        }
+        
+        private void UpdateDirectoryLastChanged(DirectoryScanInfo dir, DateTime last_changed)
+        {
+            dir.LastChanged = last_changed;
+            try {
+                Globals.Library.Db.Execute(String.Format(
+                   "UPDATE ScanDirectories SET LastUpdated = {0} WHERE Uri = \"{1}\"",
+                   DateTimeUtil.FromDateTime(last_changed),
+                   dir.Uri));
+            } catch {
+            }
+        }
+        
+        public void StartListening()
+        {
+            if(thread != null && thread.IsAlive) {
+                return;
+            }
+            Refresh();
+        }
+
+        public void StopListenting()
+        {
+            lock(directories) {
+                foreach(DirectoryScanInfo dir in directories) {
+                    dir.Dispose();
+                }
+                directories.Clear();
+            }
+        }
+
+        public void Refresh()
+        {
+            thread = new Thread(new ThreadStart(RefreshBase));
+            thread.Priority = ThreadPriority.Lowest;
+            thread.IsBackground = true;
+            thread.Start();
+        }
+        
+        private void RefreshBase()
+        {
+            lock(directories) {
+                foreach(DirectoryScanInfo dir in directories) {
+                    dir.Dispose();
+                }
+                directories.Clear();
+                
+                IDataReader reader = Globals.Library.Db.Query(
+                    "SELECT Uri, LastUpdated FROM ScanDirectories ORDER BY LENGTH(Uri) ASC");
+
+                while(reader.Read()) {
+                    string uri = reader[0] as string;
+                    DateTime last_changed = DateTimeUtil.ToDateTime(Convert.ToInt64(reader[1]));
+
+                    // Child paths will be added to the database, but not loaded for scanning.
+                    // This is done so that if the parent path is removed from the database
+                    // at a later time, the child path will then be searched.
+                    bool childPath = false;
+                        foreach(DirectoryScanInfo info in directories) {
+                            if(uri.StartsWith(info.Uri)) {
+                                childPath = true;
+                                break;
+                            }
+                    }
+                    if(!childPath) {
+                        DirectoryScanInfo dir = new DirectoryScanInfo(uri, last_changed);
+                        CheckDirectoryForUpdates(dir);
+                        dir.Created += new FileSystemEventHandler(OnCreated);
+                        directories.Add(dir);
+                    }
+                }
+                reader.Dispose();
+            }
+        }
+        
+        private void OnCreated(object o, FileSystemEventArgs args)
+        {
+            DirectoryScanInfo dir = o as DirectoryScanInfo;
+            SafeUri safe_uri = new SafeUri(args.FullPath);
+            if(File.Exists(safe_uri.AbsolutePath)) {
+                QueueUpdate(safe_uri.AbsolutePath);
+                UpdateDirectoryLastChanged(dir, DateTime.UtcNow);
+            }
+        }
+        
+        private void CheckDirectoryForUpdates(DirectoryScanInfo dir)
+        {
+            DirectoryInfo info = new DirectoryInfo(dir.Uri);
+            if(!info.Exists) {
+                return;
+            }
+
+            DateTime last_changed = CheckDirectoryForUpdates(info.FullName, dir.LastChanged);
+
+            if(last_changed != new DateTime() && last_changed.Subtract(dir.LastChanged) > threshold) {
+                UpdateDirectoryLastChanged(dir, last_changed);
+            }
+        }
+
+        private DateTime CheckDirectoryForUpdates(string path, DateTime compare_time)
+        {
+            DirectoryInfo info = new DirectoryInfo(new SafeUri(path).AbsolutePath);
+
+            if(!info.Exists || info.Name.StartsWith(".")) {
+                return new DateTime();
+            }
+
+            DateTime this_time = info.LastWriteTimeUtc;
+            
+            foreach(string dir in IOProxy.Directory.GetDirectories(path)) {
+                DateTime child_time = CheckDirectoryForUpdates(dir, compare_time);
+                if(child_time != new DateTime() && child_time > this_time) {
+                    this_time = child_time;
+                }
+            }
+
+            if(this_time.Subtract(compare_time) > threshold) {
+                foreach(string file in IOProxy.Directory.GetFiles(path)) {
+                    QueueUpdate(new SafeUri(file).AbsolutePath);
+                }
+            }
+            return this_time;
+        }
+        
+        public void AddDirectoryToWatchList(string path)
+        {
+            AddDirectoryToWatchList(path, true);
+        }
+
+        public void AddDirectoryToWatchList(string path, bool longevous)
+        {
+            SafeUri safe_uri = new SafeUri(path);
+            if(!Directory.Exists(safe_uri.AbsolutePath)) {
+                return; // TODO throw and exception?
+            }
+            
+            if(longevous) {
+                try {
+                    Globals.Library.Db.Execute(String.Format(
+                        "UPDATE ScanDirectories SET Longevous = {0} WHERE Uri = \"{1}\"",
+                        "1",
+                        safe_uri.AbsolutePath));
+                } catch {
+                }
+            }
+
+            try {
+                Globals.Library.Db.Execute(String.Format(
+                    "INSERT INTO ScanDirectories (Uri, LastUpdated, Longevous) VALUES (\"{0}\", {1}, {2})",
+                    safe_uri.AbsolutePath,
+                    DateTimeUtil.FromDateTime(DateTime.UtcNow),
+                    (longevous) ? "1" : "0"));
+                QueueRefresh();
+            } catch {
+            }
+        }
+
+        public void RemoveDirectoryFromWatchList(string path)
+        {
+            RemoveDirectoryFromWatchList(path, false);
+        }
+        
+        public void RemoveDirectoryFromWatchList(string path, bool longevous)
+        {
+            SafeUri safe_uri = new SafeUri(path);
+            try {
+                if(longevous) {
+                    Globals.Library.Db.Execute(String.Format(
+                        "DELETE FROM ScanDirectories WHERE Uri = \"{0}\"",
+                        safe_uri.AbsolutePath));
+                } else {
+                    Globals.Library.Db.Execute(String.Format(
+                        "DELETE FROM ScanDirectories WHERE Uri = \"{0}\" AND Longevous = 0",
+                        safe_uri.AbsolutePath));
+                }
+                QueueRefresh();
+            } catch {
+            }
+        }
+        
+        private volatile bool refresh_timer_delay;
+        public void QueueRefresh()
+        {
+            if(refresh_timer == null) {
+                lock(refresh_timer_mutex) {
+                    refresh_timer = new System.Threading.Timer(new TimerCallback(OnRefreshTimer), null, 100, 1000);
+                }
+            } else {
+                refresh_timer_delay = true;
+            }
+        }
+        
+        private void OnRefreshTimer(object o)
+        {
+            if(refresh_timer_delay) {
+                refresh_timer_delay = false;
+                return;
+            }
+            lock(refresh_timer_mutex) {
+                Refresh();
+                refresh_timer.Dispose();
+                refresh_timer = null;
+            }
+        }
+
+        public UpdateManager()
+        {
+            if(!Globals.Library.Db.TableExists("ScanDirectories")) {
+                Globals.Library.Db.Execute(
+                    "CREATE TABLE ScanDirectories (Uri TEXT PRIMARY KEY NOT NULL, " +
+                    "LastUpdated INTEGER, Longevous INTEGER)");
+                AddDirectoryToWatchList(Globals.Library.Location, false);
+            }
+        }
+
+        public static readonly SchemaEntry<bool> ScanEnabledSchema = new SchemaEntry<bool>(
+            "base.updateManager", "scan_enabled",
+            false,
+            "Folder scanning enabled",
+            "Scan the filesystem for new music"
+        );
+    }
+}
Index: src/Core/Banshee.Base/Makefile.am
===================================================================
--- src/Core/Banshee.Base/Makefile.am	(revision 2166)
+++ src/Core/Banshee.Base/Makefile.am	(working copy)
@@ -285,6 +285,7 @@
 	$(srcdir)/GstMisc.cs \
 	$(srcdir)/ImportManager.cs \
 	$(srcdir)/UriList.cs \
+	$(srcdir)/UpdateManager.cs \
 	$(srcdir)/Transcoder.cs \
 	$(srcdir)/GstTranscoder.cs \
 	$(srcdir)/BatchTranscoder.cs \
Index: data/banshee-dialogs.glade
===================================================================
--- data/banshee-dialogs.glade	(revision 2166)
+++ data/banshee-dialogs.glade	(working copy)
@@ -3142,7 +3142,7 @@
 	  <property name="layout_style">GTK_BUTTONBOX_END</property>
 
 	  <child>
-	    <widget class="GtkButton" id="closebutton2">
+	    <widget class="GtkButton" id="close_button">
 	      <property name="visible">True</property>
 	      <property name="can_default">True</property>
 	      <property name="can_focus">True</property>
@@ -3345,6 +3345,25 @@
 			  <property name="fill">False</property>
 			</packing>
 		      </child>
+
+		      <child>
+			<widget class="GtkCheckButton" id="scan_filesystem">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">_Watch music folders for new songs</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <property name="active">False</property>
+			  <property name="inconsistent">False</property>
+			  <property name="draw_indicator">True</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
 		    </widget>
 		  </child>
 		</widget>
