using System;
using System.IO;
using System.Collections;
namespace Photo_Album_Structure
{
/// <summary>
/// PhotoItem represents the base class for both Photo and Album
/// </summary>
public abstract class PhotoItem : System.IDisposable//, System.IComparable
{
#region "Properties"
#region "Property: Path"
private string path;
public string Path
{
get
{
return this.path;
}
}
internal string i_Path
{
set
{
this.path = value;
this.OnRename(this);
}
}
//internal void set_Path(string path)
//{
// this.path = path;
// this.OnRename(this);
//}
#endregion
protected PhotoItemUpdater photoItemUpdater;
#region "Property: Parent"
private Album m_Parent = null;
internal Album i_Parent
{
get
{
return this.m_Parent;
}
set
{
if(this.m_Parent == value) return;
Album OldParent = this.m_Parent;
this.m_Parent = value;
this.OnParentChange(this, OldParent);
}
}
public Album Parent
{
get
{
return this.i_Parent;
}
}
#endregion
// depreciated
#region "Property: ChildIndex"
internal int childIndex = -1;
public int ChildIndex
{
get
{
return this.childIndex;
}
}
#endregion
#region "Property: Date"
public virtual DateTime BeginDate
{
get
{
return DateTime.MinValue;
}
}
public virtual DateTime EndDate
{
get
{
return DateTime.MinValue;
}
}
#endregion
#region "Array Properties"
public virtual int Count
{
get
{
return 1;
}
}
public virtual PhotoItem this[string s]
{
get
{
return this.FindMember(s);
}
}
public virtual PhotoItem this[int i]
{
get
{
if (i != 0) throw new IndexOutOfRangeException();
return this;
}
}
#endregion
#endregion
#region "Events"
#region "Event: Date"
public event DatesChangeEventHandler DatesChange;
protected virtual void OnDatesChange(PhotoItem pi)
{
if(this.DatesChange != null)
this.DatesChange(pi);
// forward the event...
if(this.Parent != null)
this.Parent.OnDatesChange(pi);
}
public delegate void DatesChangeEventHandler(PhotoItem sender);
#endregion
#region "Event: PropertyChange"
public event PropertyChangeEventHandler PropertyChange;
protected virtual void OnPropertyChange(PhotoItem pi, string propertyName, object newValue)
{
if(this.PropertyChange != null)
this.PropertyChange(pi, propertyName, newValue);
// forward the event...
if(this.Parent != null)
this.Parent.OnPropertyChange(pi, propertyName, newValue);
}
public delegate void PropertyChangeEventHandler(
PhotoItem sender, string propertyName, object newValue);
#endregion
#region "Event: Rename"
public event RenameEventHandler Rename;
protected virtual void OnRename(PhotoItem pi)
{
if(this.Rename != null)
this.Rename(pi);
// forward the event...
if(this.Parent != null)
this.Parent.OnRename(pi);
}
public delegate void RenameEventHandler(PhotoItem sender);
#endregion
#region "Event: ParentChange"
public event ParentChangeEventHandler ParentChange;
protected virtual void OnParentChange(PhotoItem pi, Album oldParent)
{
if(this.ParentChange != null)
this.ParentChange(pi, oldParent);
// forward the event...
if(this.Parent != null)
this.Parent.OnParentChange(pi,oldParent);
}
public delegate void ParentChangeEventHandler(PhotoItem sender, Album OldParent);
#endregion
#region "Event: Request Refresh"
/*
* this is needed because it could happen that the root folder initially
* containes only photos. However, if folder is pasted in (from the outside),
* the album needs to become an AlbumAlbum. However, from inside the code, we can't
* change the "this" pointer. Therefore we externally request that we be refreshed.
*/
public event EventHandler RequestRefresh;
protected void OnRequestRefresh()
{
if(this.Parent != null)
{
this.Parent.OnRequestRefresh();
return;
}
this.RequestRefresh(this, EventArgs.Empty);
}
#endregion
#region "Event: PhotoItemIndexChange Event"
public event PhotoItemIndexChangeEventHandler PhotoItemIndexChange;
public delegate void PhotoItemIndexChangeEventHandler(
PhotoItem sender, PhotoItem pi, int oldIdx, int newIdx);
protected virtual void OnPhotoItemIndexChange(
PhotoItem sender, PhotoItem pi, int oldIdx, int newIdx)
{
if (this.PhotoItemIndexChange != null)
this.PhotoItemIndexChange(sender, pi, oldIdx, newIdx);
// forward the event if necessary
if (this.Parent != null)
this.Parent.OnPhotoItemIndexChange(sender, pi, oldIdx, newIdx);
}
protected void OnPhotoItemAdd(PhotoItem pi, int idx)
{
this.OnPhotoItemIndexChange(this, pi, -1, idx);
}
#endregion
#region "Event: Deleted"
public event DeleteEventHandler Delete;
protected virtual void OnDelete(PhotoItem pi)
{
if (this.Delete != null)
this.Delete(pi);
// forward the event...
if (this.Parent != null)
this.Parent.OnDelete(pi);
}
public delegate void DeleteEventHandler(PhotoItem sender);
#endregion
#endregion
protected PhotoItem(string path)
{
this.path = path;
}
internal virtual PhotoItem FindMember(string path)
{
if(path.Length == 0) return this;
if(path == this.Path) return this;
return null;
}
//private static PhotoItem FindMember(PhotoItem pi, string path)
//{
// if(pi.Path == path) return pi;
// if(pi is Album)
// {
// ((Album)pi)
// }
// throw new IndexOutOfRangeException(
// "Unable to find PhotoItem with path: \""+path+"\"");
//}
#region "File Operations"
static internal void Move(PhotoItem pi, string newPath)
{
if(pi is Photo)
File.Move(pi.Path, newPath);
else
Directory.Move(pi.Path, newPath);
}
#endregion
#region IDisposable Members
protected virtual void Dispose(bool disposing)
{
if(this.photoItemUpdater != null)
this.photoItemUpdater.Dispose();
}
public void Dispose()
{
if(this.photoItemUpdater == null) return;
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region IComparable Members
public virtual int CompareTo(object obj)
{
if(obj is PhotoItem)
{
return this.BeginDate.CompareTo(((PhotoItem)obj).BeginDate);
}
throw new ArgumentException("object is not a PhotoItem");
return 0;
}
/*public static bool operator < (PhotoItem pi1, PhotoItem pi2)
{
return pi1.CompareTo(pi2) < 0;
}
public static bool operator <= (PhotoItem pi1, PhotoItem pi2)
{
return pi1.CompareTo(pi2) <= 0;
}
public static bool operator > (PhotoItem pi1, PhotoItem pi2)
{
return pi1.CompareTo(pi2) > 0;
}
public static bool operator >= (PhotoItem pi1, PhotoItem pi2)
{
return pi1.CompareTo(pi2) >= 0;
}
public static bool operator == (PhotoItem pi1, PhotoItem pi2)
{
if((object)pi1 == null && (object)pi2 == null) return true;
if((object)pi1 == null || (object)pi2 == null) return false;
return pi1.CompareTo(pi2) == 0;
}
public static bool operator != (PhotoItem pi1, PhotoItem pi2)
{
return !(pi1 == pi2);
}
public override bool Equals(object obj)
{
if(obj is PhotoItem)
return ((PhotoItem)obj) == this;
return base.Equals(obj);
}
public override int GetHashCode()
{
return this.BeginDate.GetHashCode();
}*/
#endregion
public override string ToString()
{
System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append(System.IO.Path.GetFileNameWithoutExtension(this.Path));
s.Append(" : ");
s.Append(this.BeginDate.ToString());
return s.ToString();
}
public class PhotoItemUpdater : System.IDisposable
{
private System.IO.FileSystemWatcher FileWatcher;
private System.IO.FileSystemWatcher DirectoryWatcher;
private PhotoItem photoItem;
public PhotoItemUpdater(PhotoItem pi)
{
this.photoItem = pi;
// set up the File Watcher
this.FileWatcher = new System.IO.FileSystemWatcher(pi.Path);
// set up the Directory Watcher
this.DirectoryWatcher = new FileSystemWatcher(pi.path);
this.FileWatcher.IncludeSubdirectories =
this.DirectoryWatcher.IncludeSubdirectories = true;
this.FileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
this.DirectoryWatcher.NotifyFilter = NotifyFilters.DirectoryName;
// Hook up events:
this.FileWatcher.Changed += new FileSystemEventHandler(FileWatcher_Changed);
this.FileWatcher.Created += new FileSystemEventHandler(FileWatcher_Created);
this.FileWatcher.Deleted += new FileSystemEventHandler(FileWatcher_Deleted);
this.FileWatcher.Renamed += new RenamedEventHandler(FileWatcher_Renamed);
// For some reason this fires all of the time, so we choose not to listen
// for it. It can always be re-enabled
//this.DirectoryWatcher.Changed += new FileSystemEventHandler();
this.DirectoryWatcher.Created += new FileSystemEventHandler(DirectoryWatcher_Created);
this.DirectoryWatcher.Deleted += new FileSystemEventHandler(DirectoryWatcher_Deleted);
this.DirectoryWatcher.Renamed += new RenamedEventHandler(DirectoryWatcher_Renamed);
this.FileWatcher.EnableRaisingEvents =
this.DirectoryWatcher.EnableRaisingEvents = true;
}
#region IDisposable Members
protected void Dispose(bool disposing)
{
if(this.FileWatcher != null)
this.FileWatcher.Dispose();
if(this.DirectoryWatcher != null)
this.DirectoryWatcher.Dispose();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
internal static bool VerifyFileType(string path)
{
string ext = System.IO.Path.GetExtension(path).ToLower();
return (ext == ".jpeg" || ext == ".jpg" || ext == ".tiff" || ext == ".tif");
}
internal static string GetRealFullPath(string fullPath)
{
return Directory.GetFileSystemEntries(System.IO.Path.GetDirectoryName(fullPath),
System.IO.Path.GetFileName(fullPath))[0];
}
#region "FileWatcher events"
private void FileWatcher_Created(object sender, FileSystemEventArgs e)
{
if(!VerifyFileType(e.FullPath)) return;
Album album = (Album)
this.photoItem.FindMember(System.IO.Path.GetDirectoryName(e.FullPath));
// we need to fail silently, because when we are setting up
// the directory structure in memory, we alter the filesystem.
// therefore, if we have not yet read in the directory, will
// not be found by findmember!
if(album == null) return;
if(album.AlbumType != AlbumType.Photo)
{
File.Move(e.FullPath,
album.GetMiscellaneousDirectoryName()+"\\"+
System.IO.Path.GetFileName(GetRealFullPath(e.FullPath)));
return;
}
album.Add(new Photo(e.FullPath));
}
private void FileWatcher_Changed(object sender, FileSystemEventArgs e)
{
if(e.ChangeType != WatcherChangeTypes.Changed) return;
if(!VerifyFileType(e.FullPath)) return;
Photo photo = (Photo)this.photoItem.FindMember(e.FullPath);
if(photo == null) return;
Photo.RefreshProperties(photo);
}
private void FileWatcher_Deleted(object sender, FileSystemEventArgs e)
{
if(!VerifyFileType(e.FullPath)) return;
PhotoItem pi = this.photoItem.FindMember(e.FullPath);
if(pi == null) return;
pi.Parent.Remove(pi);
pi.OnDelete(pi);
pi.Dispose();
}
private void FileWatcher_Renamed(object sender, RenamedEventArgs e)
{
// rename is special
//OldName: eg:"testing\filename" or "testing";
if (VerifyFileType(e.OldFullPath) && VerifyFileType(e.FullPath))
{
PhotoItem pi = this.photoItem.FindMember(e.OldFullPath);
if (pi == null) return;
Album newParent = (Album)this.photoItem.FindMember(
System.IO.Path.GetDirectoryName(e.FullPath));
if (newParent == null) return;
pi.i_Path = GetRealFullPath(e.FullPath);
if (pi.Parent == newParent) return;
pi.Parent.Remove(pi);
newParent.Add(pi);
}
else if (VerifyFileType(e.OldFullPath))
{
PhotoItem pi = this.photoItem.FindMember(e.OldFullPath);
if (pi == null) return;
pi.Parent.Remove(pi);
pi.OnDelete(pi);
pi.Dispose();
}
else if (VerifyFileType(e.FullPath))
{
Album album = (Album)
this.photoItem.FindMember(System.IO.Path.GetDirectoryName(e.FullPath));
if (album == null) return;
if (album.AlbumType != AlbumType.Photo)
{
File.Move(e.FullPath,
album.GetMiscellaneousDirectoryName() + "\\" +
System.IO.Path.GetFileName(GetRealFullPath(e.FullPath)));
return;
}
album.Add(new Photo(e.FullPath));
}
}
#endregion
#region "DirectoryWatcher events"
private void DirectoryWatcher_Created(object sender, FileSystemEventArgs e)
{
Album album = (Album)
this.photoItem.FindMember(System.IO.Path.GetDirectoryName(e.FullPath));
// we need to fail silently, because when we are setting up
// the directory structure in memory, we alter the filesystem.
// therefore, if we have not yet read in the directory, will
// not be found by findmember!
if(album == null) return;
if(album.AlbumType != AlbumType.Album)
{
PhotoItem [] pis = new PhotoItem[album.Count];
for(int idx = album.Count-1;idx >= 0;idx--)
{
pis[idx] = album[0];
album.Remove(pis[idx]);
}
album.i_AlbumType = AlbumType.Album;
string miscDir = album.GetMiscellaneousDirectoryName();
DirectoryInfo di = new DirectoryInfo(miscDir);
if(!di.Exists) di.Create();
for(int i=0;i<pis.Length;i++)
{
File.Move(pis[i].Path, miscDir+"\\"+System.IO.Path.GetFileName(pis[i].Path));
}
}
album.Add(Album.CreateAlbum(GetRealFullPath(e.FullPath)));
}
private void DirectoryWatcher_Deleted(object sender, FileSystemEventArgs e)
{
PhotoItem pi = this.photoItem.FindMember(e.FullPath);
if(pi == null) return;
pi.Parent.Remove(pi);
pi.Dispose();
}
private void DirectoryWatcher_Renamed(object sender, RenamedEventArgs e)
{
// rename is special
//OldName: eg:"testing\filename" or "testing";
PhotoItem pi = this.photoItem.FindMember(e.OldFullPath);
if(pi == null) return;
Album newParent = (Album)this.photoItem.FindMember(
System.IO.Path.GetDirectoryName(e.FullPath));
if(newParent == null) return;
pi.i_Path = GetRealFullPath(e.FullPath);
if(pi.Parent == newParent) return;
pi.Parent.Remove(pi);
newParent.Add(pi);
}
#endregion
}
}
}