/Application/Core/PlaylistManager.cs
C# | 790 lines | 522 code | 85 blank | 183 comment | 157 complexity | 8bbcc12f209d821197eaab786449d690 MD5 | raw file
1/** 2 * PlaylistManager.cs 3 * 4 * Takes care of managing the playlists. 5 * 6 * * * * * * * * * 7 * 8 * Copyright 2012 Simplare 9 * 10 * This code is part of the Stoffi Music Player Project. 11 * Visit our website at: stoffiplayer.com 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 16 * 3 of the License, or (at your option) any later version. 17 * 18 * See stoffiplayer.com/license for more information. 19 **/ 20 21using System; 22using System.Collections; 23using System.Collections.Generic; 24using System.Collections.ObjectModel; 25using System.Collections.Specialized; 26using System.IO; 27using System.Linq; 28using System.Text; 29using System.Threading; 30using System.Xml; 31 32namespace Stoffi 33{ 34 /// <summary> 35 /// Represents a manager that takes care of the playlist logic 36 /// </summary> 37 public static class PlaylistManager 38 { 39 #region Members 40 41 private static String supportedFileFormats = "m3u;pls"; 42 43 #endregion 44 45 #region Properties 46 47 /// <summary> 48 /// The currently active playlist that is being played. 49 /// An empty string if no playlist is active. 50 /// </summary> 51 public static String CurrentPlaylist { get; set; } 52 53 #endregion 54 55 #region Methods 56 57 #region Public 58 59 /// <summary> 60 /// Initializes the playlist manager 61 /// </summary> 62 public static void Initialize() 63 { 64 CurrentPlaylist = ""; 65 66 ThreadStart GUIThread = delegate() 67 { 68 foreach (PlaylistData playlist in SettingsManager.Playlists) 69 { 70 // fix null tracks 71 bool allTracksNull = true; 72 foreach (TrackData t in playlist.Tracks) 73 if (t != null) 74 { 75 allTracksNull = false; 76 break; 77 } 78 79 if (allTracksNull) playlist.Tracks.Clear(); 80 playlist.Tracks.CollectionChanged += TracksChanged; 81 DispatchPlaylistModified(playlist, ModifyType.Created); 82 } 83 }; 84 85 if (SettingsManager.CurrentActiveNavigation.StartsWith("Playlist:")) 86 CurrentPlaylist = SettingsManager.CurrentActiveNavigation.Split(new[]{':'},2)[1]; 87 88 Thread thread = new Thread(GUIThread); 89 thread.Name = "Playlist Thread"; 90 thread.Priority = ThreadPriority.BelowNormal; 91 thread.Start(); 92 } 93 94 /// <summary> 95 /// Add tracks to a playlist 96 /// </summary> 97 /// <param name="tracks">The list of tracks to be added</param> 98 /// <param name="playlistName">The name of the playlist to add the tracks to</param> 99 /// <param name="pos">The position to insert the track at (-1 means at the end)</param> 100 public static void AddToPlaylist(List<object> tracks, String playlistName, int pos = -1) 101 { 102 PlaylistData playlist = FindPlaylist(playlistName); 103 if (playlist == null) return; 104 foreach (TrackData track in tracks) 105 { 106 if (!playlist.Tracks.Contains(track)) 107 { 108 if (pos < 0 || pos >= playlist.Tracks.Count) 109 playlist.Tracks.Add(track); 110 else 111 playlist.Tracks.Insert(pos, track); 112 track.Source = "Playlist:" + playlist.Name; 113 } 114 } 115 } 116 117 /// <summary> 118 /// Add tracks to a playlist 119 /// </summary> 120 /// <param name="tracks">The list of tracks to be added</param> 121 /// <param name="playlistName">The name of the playlist to add the tracks to</param> 122 /// <param name="pos">The position to insert the track at (-1 means at the end)</param> 123 public static void AddToPlaylist(ObservableCollection<TrackData> tracks, String playlistName, int pos = -1) 124 { 125 PlaylistData playlist = FindPlaylist(playlistName); 126 if (playlist == null) return; 127 foreach (TrackData track in tracks) 128 { 129 if (!playlist.Tracks.Contains(track)) 130 { 131 if (pos < 0 || pos >= playlist.Tracks.Count) 132 playlist.Tracks.Add(track); 133 else 134 playlist.Tracks.Insert(pos, track); 135 track.Source = "Playlist:" + playlist.Name; 136 } 137 } 138 } 139 140 /// <summary> 141 /// Add tracks to a playlist 142 /// </summary> 143 /// <param name="tracks">The list of tracks to be added</param> 144 /// <param name="playlistName">The name of the playlist to add the tracks to</param> 145 /// <param name="pos">The position to insert the track at (-1 means at the end)</param> 146 public static void AddToPlaylist(List<TrackData> tracks, String playlistName, int pos = -1) 147 { 148 PlaylistData playlist = FindPlaylist(playlistName); 149 if (playlist == null) return; 150 foreach (TrackData track in tracks) 151 { 152 // insert 153 if (!playlist.Tracks.Contains(track)) 154 { 155 if (pos < 0 || pos >= playlist.Tracks.Count) 156 playlist.Tracks.Add(track); 157 else 158 playlist.Tracks.Insert(pos, track); 159 track.Source = "Playlist:" + playlist.Name; 160 } 161 162 // move 163 else if (pos != playlist.Tracks.IndexOf(track)) 164 { 165 if (pos < 0 || pos >= playlist.Tracks.Count) 166 { 167 playlist.Tracks.Remove(track); 168 playlist.Tracks.Add(track); 169 } 170 else if (pos < playlist.Tracks.IndexOf(track)) 171 { 172 playlist.Tracks.Remove(track); 173 playlist.Tracks.Insert(pos, track); 174 } 175 else 176 { 177 playlist.Tracks.Remove(track); 178 playlist.Tracks.Insert(pos-1, track); 179 } 180 } 181 } 182 } 183 184 /// <summary> 185 /// Check to see if a file is a supported playlist file 186 /// </summary> 187 /// <param name="path">The filename to check</param> 188 /// <returns>true of the file can be opened by Stoffi, otherwise false</returns> 189 public static bool IsSupported(String path) 190 { 191 return path.StartsWith("playlist://") || 192 (supportedFileFormats.Split(';').Contains<string>(Path.GetExtension(path))); 193 } 194 195 /// <summary> 196 /// Remove tracks from a playlist if they are found inside the playlist 197 /// </summary> 198 /// <param name="tracks">The list of t to be removed</param> 199 /// <param name="playlistName">The name of the playlist to remove the t from</param> 200 public static void RemoveFromPlaylist(List<TrackData> tracks, String playlistName) 201 { 202 PlaylistData playlist = FindPlaylist(playlistName); 203 if (playlist != null) 204 { 205 foreach (TrackData track in tracks) 206 foreach (TrackData trackInPlaylist in playlist.Tracks) 207 if (trackInPlaylist.Path == track.Path) 208 { 209 playlist.Tracks.Remove(trackInPlaylist); 210 break; 211 } 212 } 213 } 214 215 /// <summary> 216 /// Creates a new playlist 217 /// </summary> 218 /// <param name="name">The name of the new playlist (this will be appended with a number if neccessary)</param> 219 /// <returns>The newly created PlaylistData for the playlist</returns> 220 public static PlaylistData CreatePlaylist(String name) 221 { 222 name = U.CleanXMLString(name); 223 224 if (FindPlaylist(name) != null) 225 { 226 int pExt = 1; 227 while (FindPlaylist(name + pExt) != null) 228 pExt++; 229 name = name + pExt; 230 } 231 232 PlaylistData playlist = new PlaylistData(); 233 playlist.Name = name; 234 playlist.Time = 0; 235 playlist.Tracks = new ObservableCollection<TrackData>(); 236 playlist.Tracks.CollectionChanged += TracksChanged; 237 SettingsManager.Playlists.Add(playlist); 238 239 DispatchPlaylistModified(playlist, ModifyType.Created); 240 241 return playlist; 242 } 243 244 /// <summary> 245 /// Renames a playlist. If a playlist with the new name already exist or the new name is either "Create new" or "" it will do nothing. 246 /// </summary> 247 /// <param name="oldName">The current name of the playlist to be renamed</param> 248 /// <param name="newName">The new name of the playlist</param> 249 public static void RenamePlaylist(String oldName, String newName) 250 { 251 newName = U.CleanXMLString(newName); 252 253 PlaylistData pl = FindPlaylist(oldName); 254 255 if (FindPlaylist(newName) != null) 256 { 257 int pExt = 1; 258 while (FindPlaylist(newName + pExt) != null) 259 pExt++; 260 newName = newName + pExt; 261 } 262 263 if (pl != null && newName != "" && newName.ToLower() != U.T("NavigationCreateNew").ToLower()) 264 DispatchPlaylistRenamed(pl, oldName, newName); 265 } 266 267 /// <summary> 268 /// Saves a playlist as a file 269 /// </summary> 270 /// <param name="path">The path of the saved playlist</param> 271 /// <param name="name">The name of the playlist to save</param> 272 public static void SavePlaylist(String path, String name) 273 { 274 PlaylistData pl = FindPlaylist(name); 275 if (pl != null) 276 { 277 string ext = Path.GetExtension(path); 278 System.IO.StreamWriter sw = System.IO.File.AppendText(path); 279 280 if (ext == ".pls") 281 { 282 sw.WriteLine("[playlist]"); 283 sw.WriteLine(""); 284 int i = 0; 285 foreach (TrackData track in pl.Tracks) 286 { 287 i++; 288 sw.WriteLine(String.Format("File{0}={1}", i, track.Path)); 289 sw.WriteLine(String.Format("Title{0}={1}", i, track.Title)); 290 sw.WriteLine(String.Format("Length{0}={1}", i, (int)track.RawLength)); 291 sw.WriteLine(""); 292 } 293 sw.WriteLine("NumberOfEntries=" + i); 294 sw.WriteLine("Version=2"); 295 } 296 else if (ext == ".m3u") 297 { 298 sw.WriteLine("#EXTM3U"); 299 sw.WriteLine(""); 300 foreach (TrackData track in pl.Tracks) 301 { 302 sw.WriteLine(String.Format("#EXTINF:{0},{1} - {2}", (int)track.RawLength, track.Artist, track.Title)); 303 sw.WriteLine(track.Path); 304 sw.WriteLine(""); 305 } 306 } 307 sw.Close(); 308 } 309 } 310 311 /// <summary> 312 /// Reads a file and creates a playlist using the name of the file. 313 /// </summary> 314 /// <param name="filename">The file to read</param> 315 /// <returns>The PlaylistData of the newly created playlist</returns> 316 public static PlaylistData LoadPlaylist(String filename) 317 { 318 //try 319 //{ 320 string pName = Path.GetFileNameWithoutExtension(filename); 321 if (FindPlaylist(pName) != null) 322 { 323 int pExt = 1; 324 while (FindPlaylist(pName + pExt) != null) 325 pExt++; 326 pName = pName + pExt; 327 } 328 PlaylistData pl = CreatePlaylist(pName); 329 StreamReader sr = new StreamReader(filename); 330 string line; 331 int nr = 0; 332 if (Path.GetExtension(filename) == ".m3u") 333 { 334 bool ext = false; 335 string inf = ""; 336 while ((line = sr.ReadLine()) != null) 337 { 338 nr++; 339 if (line == "#EXTM3U") 340 ext = true; 341 else if (ext && line.StartsWith("#EXTINF:")) 342 inf = line.Substring(8); 343 else if (line.StartsWith("#") || line == "") 344 continue; 345 else 346 { 347 string path = line; 348 349 if (!File.Exists(path) && File.Exists(Path.Combine(Path.GetDirectoryName(filename), path))) 350 path = Path.Combine(Path.GetDirectoryName(filename), path); 351 352 if (File.Exists(path)) 353 { 354 string length = ""; 355 string artist = ""; 356 string title = ""; 357 if (inf != "") 358 { 359 if (!inf.Contains(',')) 360 { 361 U.L(LogLevel.Warning, "PLAYLIST", "Bad format in " + filename + " on line " 362 + nr + ": expecting ','"); 363 continue; 364 } 365 string[] split = inf.Split(','); 366 length = split[0]; 367 if (split[1].Contains('-')) 368 { 369 artist = split[1].Split('-')[0]; 370 title = split[1].Split('-')[1]; 371 } 372 else 373 title = split[1]; 374 } 375 if (!FilesystemManager.PathIsAdded(path)) 376 FilesystemManager.AddSource(new SourceData() 377 { 378 Data = path, 379 HumanType = U.T("SourcesTypeFile"), 380 Type = SourceType.File, 381 Include = true 382 }); 383 foreach (TrackData t in SettingsManager.FileTracks) 384 if (t.Path == path) 385 { 386 if (!pl.Tracks.Contains(t)) 387 pl.Tracks.Add(t); 388 break; 389 } 390 inf = ""; 391 } 392 else if (YouTubeManager.IsYouTube(path)) 393 { 394 string yid = YouTubeManager.GetYouTubeID(path); 395 TrackData t = YouTubeManager.CreateTrack(yid); 396 if (!pl.Tracks.Contains(t)) 397 pl.Tracks.Add(t); 398 } 399 } 400 } 401 } 402 else if (Path.GetExtension(filename) == ".pls") 403 { 404 bool hdr = false; 405 string version = ""; 406 int noe = 0; 407 while ((line = sr.ReadLine()) != null) 408 { 409 nr++; 410 if (line == "[playlist]") 411 hdr = true; 412 else if (!hdr) 413 U.L(LogLevel.Warning, "PLAYLIST", "Bad format in " + filename + " on line " 414 + nr + ": expecting '[playlist]'"); 415 else if (line.StartsWith("NumberOfEntries=")) 416 noe = Convert.ToInt32(line.Split('=')[1]); 417 else if (line.StartsWith("Version=")) 418 version = line.Split('=')[1]; 419 } 420 421 if (!hdr) 422 U.L(LogLevel.Warning, "PLAYLIST", "Error in " + filename + ": No header found"); 423 else if (version != "2") 424 U.L(LogLevel.Warning, "PLAYLIST", "Error in " + filename + ": Unsupported version '" + 425 version + "'"); 426 else 427 { 428 sr.Close(); 429 sr = new StreamReader(filename); 430 string[,] tracks = new string[noe, 3]; 431 nr = 0; 432 while ((line = sr.ReadLine()) != null) 433 { 434 if (line.StartsWith("File") || line.StartsWith("Title") || line.StartsWith("Length")) 435 { 436 int tmp = 4; 437 int index = 0; 438 if (line.StartsWith("Title")) { tmp = 5; index = 1; } 439 else if (line.StartsWith("Length")) { tmp = 6; index = 2; } 440 441 string[] split = line.Split('='); 442 int number = Convert.ToInt32(split[0].Substring(tmp)); 443 444 if (number > noe) 445 U.L(LogLevel.Warning, "PLAYLIST", "Bad format in " + filename + " on line " 446 + nr + ": entry number is '" + number + "' but NumberOfEntries is '" + noe + "'"); 447 else 448 tracks[number-1, index] = split[1]; 449 } 450 else if (!line.StartsWith("NumberOfEntries") && line != "[playlist]" && !line.StartsWith("Version=")) 451 { 452 U.L(LogLevel.Warning, "PLAYLIST", "Bad format in " + filename + " on line " 453 + nr + ": unexpected '" + line + "'"); 454 } 455 } 456 for (int i = 0; i < noe; i++) 457 { 458 string path = tracks[i, 0]; 459 460 if (!File.Exists(path) && File.Exists(Path.Combine(Path.GetDirectoryName(filename), path))) 461 path = Path.Combine(Path.GetDirectoryName(filename), path); 462 463 if (File.Exists(path)) 464 { 465 if (!FilesystemManager.PathIsAdded(path)) 466 FilesystemManager.AddSource(new SourceData() 467 { 468 Data = path, 469 HumanType = U.T("SourcesTypeFile"), 470 Type = SourceType.File, 471 Include = true 472 }); 473 foreach (TrackData t in SettingsManager.FileTracks) 474 if (t.Path == path) 475 { 476 if (!pl.Tracks.Contains(t)) 477 pl.Tracks.Add(t); 478 break; 479 } 480 } 481 else if (YouTubeManager.IsYouTube(path)) 482 { 483 string yid = YouTubeManager.GetYouTubeID(path); 484 TrackData t = YouTubeManager.CreateTrack(yid); 485 if (!pl.Tracks.Contains(t)) 486 pl.Tracks.Add(t); 487 } 488 } 489 } 490 } 491 sr.Close(); 492 return pl; 493 494 495 // TODO: Replace with dispatching a custom event 496 497 //} 498 //catch (Exception e) 499 //{ 500 // MessageBox.Show("Error trying to load playlist:\n" + e.Message, "Couldn't Load Playlist", MessageBoxButton.OK, 501 // MessageBoxImage.Error); 502 // return null; 503 //} 504 } 505 506 /// <summary> 507 /// Deletes a playlist 508 /// </summary> 509 /// <param name="name">The name of the playlist to delete</param> 510 public static void RemovePlaylist(String name) 511 { 512 PlaylistData pl = FindPlaylist(name); 513 if (pl != null) 514 { 515 DispatchPlaylistModified(pl, ModifyType.Removed); 516 517 // and finally remove the playlist altogther (undo?) 518 SettingsManager.Playlists.Remove(pl); 519 } 520 } 521 522 /// <summary> 523 /// Tries to find a playlist with a given name 524 /// </summary> 525 /// <param name="name">The name of the playlist to look for</param> 526 /// <returns>The PlaylistData of the playlist with the name <paramref name="name"/> of such a playlist could be found, otherwise null.</returns> 527 public static PlaylistData FindPlaylist(String name) 528 { 529 foreach (PlaylistData p in SettingsManager.Playlists) 530 if (p.Name == name) return p; 531 return null; 532 } 533 534 /// <summary> 535 /// Update the "Last Played" and "Play Count" information of a given track 536 /// </summary> 537 /// <param name="RefTrack">The track that was just played</param> 538 public static void TrackWasPlayed(TrackData RefTrack) 539 { 540 if (RefTrack == null) return; 541 542 uint pc = RefTrack.PlayCount + 1; 543 foreach (TrackData track in SettingsManager.FileTracks) 544 { 545 if (track.Path == RefTrack.Path) 546 { 547 track.PlayCount = pc; 548 track.LastPlayed = DateTime.Now.ToString("g"); 549 track.RawLastPlayed = DateTime.Now.ToString("yyyyMMddHHmmss"); 550 } 551 } 552 553 foreach (TrackData track in SettingsManager.QueueTracks) 554 { 555 if (track.Path == RefTrack.Path) 556 { 557 track.PlayCount = pc; 558 track.LastPlayed = DateTime.Now.ToString("g"); 559 track.RawLastPlayed = DateTime.Now.ToString("yyyyMMddHHmmss"); 560 } 561 } 562 563 foreach (TrackData track in SettingsManager.HistoryTracks) 564 if (track.Path == RefTrack.Path) 565 track.PlayCount = pc; 566 567 foreach (PlaylistData playlist in SettingsManager.Playlists) 568 { 569 if (playlist.Name == CurrentPlaylist) 570 { 571 foreach (TrackData track in playlist.Tracks) 572 { 573 if (track.Path == RefTrack.Path) 574 { 575 track.PlayCount = pc; 576 track.LastPlayed = DateTime.Now.ToString("g"); 577 track.RawLastPlayed = DateTime.Now.ToString("yyyyMMddHHmmss"); 578 } 579 } 580 } 581 } 582 } 583 584 /// <summary> 585 /// Finds all playlists that contains a given track. 586 /// </summary> 587 /// <param name="track">The track to look for</param> 588 /// <returns>All playlists containing <paramref name="track"/></returns> 589 public static List<PlaylistData> Has(TrackData track) 590 { 591 List<PlaylistData> has = new List<PlaylistData>(); 592 foreach (PlaylistData p in SettingsManager.Playlists) 593 if (Contains(p, track)) 594 has.Add(p); 595 return has; 596 } 597 598 /// <summary> 599 /// Checks whether a given playlist contains a given track. 600 /// </summary> 601 /// <param name="playlist">The playlist to search in</param> 602 /// <param name="track">The track to search for</param> 603 /// <returns>True of <paramref name="playlist"/> contains <paramref name="track"/>, otherwise false</returns> 604 public static bool Contains(PlaylistData playlist, TrackData track) 605 { 606 foreach (TrackData t in playlist.Tracks) 607 if (t.Path == track.Path) 608 return true; 609 return false; 610 } 611 612 /// <summary> 613 /// Checks whether a given playlist contains any of a given list of track. 614 /// </summary> 615 /// <param name="playlist">The playlist to search in</param> 616 /// <param name="tracks">The tracks to search for</param> 617 /// <returns>True of <paramref name="playlist"/> contains any of <paramref name="tracks"/>, otherwise false</returns> 618 public static bool ContainsAny(PlaylistData playlist, List<TrackData> tracks) 619 { 620 foreach (TrackData t1 in playlist.Tracks) 621 foreach (TrackData t2 in tracks) 622 if (t1.Path == t2.Path) 623 return true; 624 return false; 625 } 626 627 /// <summary> 628 /// Checks whether a given playlist contains all of a given list of track. 629 /// </summary> 630 /// <param name="playlist">The playlist to search in</param> 631 /// <param name="tracks">The tracks to search for</param> 632 /// <returns>True of <paramref name="playlist"/> contains all of <paramref name="tracks"/>, otherwise false</returns> 633 public static bool ContainsAll(PlaylistData playlist, List<TrackData> tracks) 634 { 635 foreach (TrackData t1 in playlist.Tracks) 636 foreach (TrackData t2 in tracks) 637 if (t1.Path != t2.Path) 638 return false; 639 return playlist.Tracks.Count != 0; 640 } 641 642 #endregion 643 644 #region Event handlers 645 646 /// <summary> 647 /// Updates the total time of all tracks of a playlist. 648 /// </summary> 649 /// <param name="sender">The sender of the event</param> 650 /// <param name="e">The event data</param> 651 private static void TracksChanged(object sender, NotifyCollectionChangedEventArgs e) 652 { 653 ObservableCollection<TrackData> tracks = sender as ObservableCollection<TrackData>; 654 655 // find the playlist containing the track that sent the event 656 PlaylistData pl = null; 657 foreach (PlaylistData p in SettingsManager.Playlists) 658 { 659 if (p.Tracks == tracks) 660 { 661 pl = p; 662 break; 663 } 664 } 665 666 // no playlist found (weird!) 667 if (pl == null) return; 668 669 pl.Time = 0; 670 foreach (TrackData t in pl.Tracks) 671 pl.Time += t.RawLength; 672 673 if (pl.Time < 0) pl.Time = 0; 674 675 //// update gui if neccessary 676 //if (ParentWindow.NavigationPane.CurrentSelectedNavigation == "Playlist:" + pl.Name) 677 //{ 678 // ParentWindow.InfoPaneTracks.Text = pl.Tracks.Count.ToString() + " songs"; 679 // ParentWindow.InfoPaneDuration.Text = U.TimeSpanToLongString(new TimeSpan(0, 0, (int)pl.Time)); 680 //} 681 } 682 683 #endregion 684 685 #region Dispatchers 686 687 /// <summary> 688 /// The dispatcher of the <see cref="PlaylistRenamed"/> event 689 /// </summary> 690 /// <param name="playlist">The playlist that was renamed</param> 691 /// <param name="oldName">The name of the playlist before the change</param> 692 /// <param name="newName">The name of the playlist after the change</param> 693 private static void DispatchPlaylistRenamed(PlaylistData playlist, string oldName, string newName) 694 { 695 if (PlaylistRenamed != null) 696 PlaylistRenamed(playlist, new RenamedEventArgs(WatcherChangeTypes.Renamed, "playlist", newName, oldName)); 697 } 698 699 /// <summary> 700 /// The dispatcher of the <see cref="PlaylistModified"/> event 701 /// </summary> 702 /// <param name="playlist">The playlist that was modified</param> 703 /// <param name="type">The type of modification that occured</param> 704 private static void DispatchPlaylistModified(PlaylistData playlist, ModifyType type) 705 { 706 if (PlaylistModified != null) 707 PlaylistModified(playlist, new ModifiedEventArgs(type, null)); 708 } 709 710 #endregion 711 712 #endregion 713 714 #region Events 715 716 /// <summary> 717 /// Occurs when a playlist has been renamed 718 /// </summary> 719 public static event RenamedEventHandler PlaylistRenamed; 720 721 /// <summary> 722 /// Occurs when a playlist has been created, removed or changed 723 /// </summary> 724 public static event ModifiedEventHandler PlaylistModified; 725 726 #endregion 727 } 728 729 #region Delegates 730 731 public delegate void ModifiedEventHandler(object sender, ModifiedEventArgs e); 732 733 #endregion 734 735 #region Event arguments 736 737 /// <summary> 738 /// Provides data for the events where something has been modified 739 /// </summary> 740 public class ModifiedEventArgs 741 { 742 /// <summary> 743 /// Gets or sets the type of modification that occured 744 /// </summary> 745 public ModifyType Type { get; set; } 746 747 /// <summary> 748 /// Gets or sets the data of the modification 749 /// </summary> 750 public object Data { get; set; } 751 752 /// <summary> 753 /// Creates an instance of the ModifiedEventArgs class 754 /// </summary> 755 /// <param name="type">The type of modification that occured</param> 756 /// <param name="data">The data of the modification</param> 757 public ModifiedEventArgs(ModifyType type, object data) 758 { 759 Type = type; 760 Data = data; 761 } 762 } 763 764 #endregion 765 766 #region Enums 767 768 /// <summary> 769 /// Represents the type of modification that can occur 770 /// </summary> 771 public enum ModifyType 772 { 773 /// <summary> 774 /// The object was created 775 /// </summary> 776 Created, 777 778 /// <summary> 779 /// The object was removed 780 /// </summary> 781 Removed, 782 783 /// <summary> 784 /// The object was changed 785 /// </summary> 786 Changed 787 } 788 789 #endregion 790}