Cleaned up code, Added Regions and comments
[banshee-community-extensions:banshee-community-extensions.git] / src / DuplicateSongDetector / Banshee.DuplicateSongDetector / SongDuplicateView.cs
1 //
2 // DuplicateSongView.cs
3 // 
4 // Author:
5 //   Kevin Anthony <Kevin@NoSideRacing.com>
6 // 
7 // Copyright (c) 2011 Kevin Anthony
8 // 
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 using System;
28 using System.IO;
29
30 using Mono.Addins;
31 using Mono.Unix;
32
33 using Gtk;
34
35 using Hyena;
36 using Hyena.Data.Sqlite;
37 using Hyena.Widgets;
38
39 using Banshee.Base;
40 using Banshee.Library;
41 using Banshee.Sources;
42 using Banshee.Sources.Gui;
43 using Banshee.ServiceStack;
44 using Banshee.Gui;
45
46 namespace Banshee.DuplicateSongDetector
47 {
48     public class SongDuplicateView : RoundedFrame, ISourceContents
49     {
50         private static Gtk.ListStore MusicListStore;
51
52         public SongDuplicateView ()
53         {
54             Gtk.ScrolledWindow Scroll = new Gtk.ScrolledWindow ();
55             Gtk.TreeView Tree = new Gtk.TreeView ();
56             Gtk.VBox vbox = new Gtk.VBox (false, 1);
57             Gtk.HBox hbox = new Gtk.HBox (false, 1);
58
59             //Buttons For Header
60             Gtk.Button removeButton = new Gtk.Button ();
61             removeButton.Label = AddinManager.CurrentLocalizer.GetString ("Remove Selected Songs");
62             removeButton.Clicked += OnRemoveCommand;
63             Gtk.Button deleteButton = new Gtk.Button ();
64             deleteButton.Label = AddinManager.CurrentLocalizer.GetString ("Delete Selected Songs");
65             deleteButton.Clicked += OnDeleteCommand;
66
67             //Create 5 columns, first column is a checkbox, next 4 are text boxes
68             Gtk.CellRendererToggle selectCell = new Gtk.CellRendererToggle ();
69             selectCell.Activatable = true;
70             selectCell.Toggled += OnSelectToggled;
71             selectColumn.PackStart (selectCell, true);
72             Tree.AppendColumn(AddinManager.CurrentLocalizer.GetString ("Select"),selectCell, "active", 0);
73             Tree.AppendColumn(AddinManager.CurrentLocalizer.GetString ("Song Title"),new Gtk.CellRendererText (), "text", 1);
74             Tree.AppendColumn(AddinManager.CurrentLocalizer.GetString ("Artist"),new Gtk.CellRendererText (), "text", 2);
75             Tree.AppendColumn(AddinManager.CurrentLocalizer.GetString ("Album"),new Gtk.CellRendererText (), "text", 3);
76             Tree.AppendColumn(AddinManager.CurrentLocalizer.GetString ("File URI"),new Gtk.CellRendererText (), "text", 4);
77             // Remove From Library, Delete From Drive, Song Name, Artist Name, Album Name, Formated URI, Actual URI, Database Track ID
78             MusicListStore = new Gtk.ListStore (typeof(bool), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(int));
79             Tree.Model = MusicListStore;
80             //Pack the Tree in a scroll window
81             Scroll.AddWithViewport (Tree);
82             //Pack the buttons in a hbox
83             hbox.PackStart (removeButton, false, false, 0);
84             hbox.PackStart (deleteButton, false, false, 0);
85             //pack the hbox->buttons and Scroll->Tree in a Vbox, tell the Scroll window to Expand and Fill the vbox
86             vbox.PackStart (hbox, false, false, 0);
87             vbox.PackStart (Scroll, true, true, 0);
88             //pack the vbox in the Rounded Frame
89             Add(vbox);
90             //Finally, show everything
91             ShowAll();
92             
93         }
94
95         void OnSelectToggled (object o, ToggledArgs args)
96         {
97             //get the toggled row, pull out the value for the select box, and store the opposite.
98             TreeIter Iter;
99             if (MusicListStore.GetIter (out Iter, new TreePath (args.Path))) {
100                 bool OldValue = (bool)MusicListStore.GetValue (Iter, 0);
101                 MusicListStore.SetValue (Iter, 0, !OldValue);
102                 Log.DebugFormat ("Setting Selection Value For Row {0} -> {1}", MusicListStore.GetStringFromIter (Iter), !OldValue);
103             }
104         }
105 #region ButtonPushed
106         void OnRemoveCommand (object o, EventArgs args)
107         {
108             OnExecuteCommand (false);
109         }
110
111         void OnDeleteCommand (object o, EventArgs args)
112         {
113             OnExecuteCommand (true);
114         }
115
116         void OnExecuteCommand (bool Delete)
117         {
118             if (ConfirmRemove (Delete)) {
119                 MusicLibrarySource Library = ServiceManager.SourceManager.MusicLibrary;
120                 if (Library.CanRemoveTracks && Library.CanDeleteTracks) {
121                     Gtk.TreeIter Iter = new Gtk.TreeIter ();
122                     if (MusicListStore.GetIterFirst (out Iter)) {
123                         do {
124                             if (Delete && (bool)MusicListStore.GetValue (Iter, 0)) {
125                                 //delete
126                                 string Uri = (string)MusicListStore.GetValue (Iter, 4);
127                                 Uri = Uri.Replace ("file://", "");
128                                 RemoveTrack ((int)MusicListStore.GetValue (Iter, 6));
129                                 DeleteTrack (Uri);
130                             } else if ((bool)MusicListStore.GetValue (Iter, 0)) {
131                                 RemoveTrack ((int)MusicListStore.GetValue (Iter, 6));
132                             }
133                         } while (MusicListStore.IterNext (ref Iter));
134                         Library.Reload ();
135                     } else {
136                         Log.Warning ("Please Don't Click Execute with nothing selected");
137                     }
138                 } else {
139                     Log.Warning ("Can not remove or delete any tracks");
140                 }
141                 ReloadWindow ();
142             }
143         }
144
145         private static bool ConfirmRemove (bool delete)
146         {
147             bool ret = false;
148             string header = null;
149             string message = null;
150             string button_label = null;
151             if (delete) {
152                 header = AddinManager.CurrentLocalizer.GetString ("Deleting Selected Files");
153                 button_label = "gtk-delete";
154             } else {
155                 message = AddinManager.CurrentLocalizer.GetString ("Are you sure you want to proceed?");
156                 button_label = "gtk-remove";
157             }
158
159             HigMessageDialog md = new HigMessageDialog (ServiceManager.Get<GtkElementsService> ().PrimaryWindow, DialogFlags.DestroyWithParent, delete ? MessageType.Warning : MessageType.Question, ButtonsType.None, header, message);
160             // Delete from Disk defaults to Cancel and the others to OK/Confirm.
161             md.AddButton ("gtk-cancel", ResponseType.No, delete);
162             md.AddButton (button_label, ResponseType.Yes, !delete);
163             
164             try {
165                 if (md.Run () == (int)ResponseType.Yes) {
166                     ret = true;
167                 }
168             } finally {
169                 md.Destroy ();
170             }
171             return ret;
172         }
173 #endregion
174 #region DataHandlers
175         public static void AddData (int ID, String song, String artist, String album, String uri)
176         {
177             string NewUri = Uri.UnescapeDataString (uri).Replace ("file://", "");
178             if (File.Exists (NewUri)) {
179                 MusicListStore.AppendValues (false,  song, artist, album, Uri.UnescapeDataString (uri), uri, ID);
180             } else {
181                 MusicListStore.AppendValues (true,  song, artist, album, Uri.UnescapeDataString (uri), uri, ID);
182             }
183         }
184
185         public static void ReloadWindow ()
186         {
187             ClearData ();
188             HyenaDataReader reader = new HyenaDataReader (ServiceManager.DbConnection.Query (@"SELECT
189                              CT.TrackID,CT.Title,CA.Title, CA.ArtistName,CT.URI
190                              FROM CoreTracks CT,CoreAlbums CA ON Ct.AlbumID = CA.AlbumID
191                              AND CT.TrackID IN (
192                                  SELECT
193                                      CT1.TrackID from CoreTracks CT1,CoreTracks CT2 where
194                                      CT1.PrimarySourceID=1
195                                      AND CT1.TrackID <> CT2.TrackID
196                                      AND CT1.TitleLowered = CT2.TitleLowered
197                                      AND CT1.TrackNumber = CT2.TrackNumber
198                                      AND CT1.AlbumID = CT2.AlbumID
199                                      AND CT1.ArtistID = CT2.ArtistID
200                              )
201                              ORDER BY CT.Title"));
202             while (reader.Read ()) {
203                 int ID = reader.Get<int> (0);
204                 String Title = reader.Get<String> (1);
205                 String Album = reader.Get<String> (2);
206                 String Artist = reader.Get<String> (3);
207                 String URI = reader.Get<String> (4);
208                 AddData (ID, Title, Album, Artist, URI);
209             }
210         }
211
212         public void RefreshWindow(){
213             ReloadWindow();
214         }
215
216         public static void ClearData ()
217         {
218             Gtk.TreeIter Iter = new Gtk.TreeIter ();
219             if (MusicListStore.GetIterFirst (out Iter)) {
220                 do {
221                     MusicListStore.Remove (ref Iter);
222                 } while (MusicListStore.IterIsValid (Iter));
223             }
224         }
225 #endregion
226 #region RemoveWorkhorse
227         //I would love to replace the database and File.Delete commands with MusicLibrary.RemoveTracks and MusicLibrary.DeleteTracks
228         //But i have to wait until better documentation comes out
229         private void RemoveTrack (int id)
230         {
231             ServiceManager.DbConnection.Execute (@"Delete From CoreTracks where TrackId = ?", id);
232         }
233         private void DeleteTrack (string uri)
234         {
235             File.Delete (uri);
236         }
237 #endregion
238 #region ISourceContents
239         private MusicLibrarySource source;
240         public bool SetSource (ISource source)
241         {
242             this.source = source as MusicLibrarySource;
243             return this.source != null;
244         }
245
246         public ISource Source {
247             get { return source; }
248         }
249
250         public void ResetSource ()
251         {
252             source = null;
253         }
254
255         public Widget Widget {
256             get { return this; }
257         }
258 #endregion
259
260     } //End of Class
261     
262 } // End of NameSpace
263
264