Good evening Banshee-Lovers,
attached patch is for improved browser to use with cvs BANSHEE_0_10.
P.S. Abock, the patch also converts QueryBuilderModel.cs and QueryBuilder.cs tabs-to-space.
sorry if this is a problem for you (probably not right?)
Below is something like features+changelog
vertical scrollbar of the two lists is always one, the horizontal only when needed
a border around every list,
the lists now update when songs are added or removed
disabling and enabling the match checkbox in the query works as expected
better handling of switching between library and sources. like remembering the last query etc..
smaller search button..
sort the album/artist list alphabetically
*data/glade/player.glade: new widgets and containers for the browser notebook.
*src/LibraryTransactions.cs: new class SqlQueryTransaction : LibraryTransaction to
handle querying transactions.
*src/PlayerInterface.cs: new BrowserNoteBook browser_notebook propetry for the browserview
new CustomExpander widget.
*src/PlayerInterface.cs: OnQueryTransactionComplete callback function to handle queries.
*src/PlayerInterface.cs: OnExpandAreaClicked callback function for CustomExpanderWidget.
*src/PlayListModel.cs: LoadFromQuery method for firing up sqlquerytransactions
*src/QueryBuilder.cs: search button and click event handlers added for better placement
*src/QueryBuilderModel.cs: new constructor SqlBuilderUI(VBox parentVBox) to enable
builder to go into browsernotebook.
*src/ArtistAlbumBrowser: new file. handles the browser gui and logic
*src/BrowserNoteBook.cs: new file handles browser parent notebook gui
Have Fun!
Aydemir Ulas Sahin
Index: data/glade/player.glade =================================================================== RCS file: /cvs/gnome/banshee/data/glade/player.glade,v retrieving revision 1.35 diff -u -r1.35 player.glade --- data/glade/player.glade 5 Dec 2005 05:11:48 -0000 1.35 +++ data/glade/player.glade 7 Dec 2005 18:57:19 -0000 @@ -954,28 +954,95 @@ <property name="spacing">5</property> <child> - <widget class="GtkHBox" id="PlaylistHeaderBox"> + <widget class="GtkVBox" id="HeaderVBox"> <property name="visible">True</property> <property name="homogeneous">False</property> - <property name="spacing">5</property> + <property name="spacing">0</property> <child> - <widget class="GtkLabel" id="ViewNameLabel"> + <widget class="GtkHBox" id="HeaderHBox"> <property name="visible">True</property> - <property name="label" translatable="yes"><b>Playlist</b></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkExpander" id="CustomExpander"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <placeholder/> + </child> + + <child> + <widget class="GtkLabel" id="ViewNameLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Library</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="PlaylistHeaderBox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="SearchLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes">Search:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -983,31 +1050,6 @@ <property name="fill">True</property> </packing> </child> - - <child> - <widget class="GtkLabel" id="SearchLabel"> - <property name="visible">True</property> - <property name="label" translatable="yes">Search:</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> </widget> <packing> <property name="padding">0</property> @@ -1017,30 +1059,57 @@ </child> <child> - <widget class="GtkAlignment" id="LibraryAlignment"> + <widget class="GtkVPaned" id="VPane"> <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">0</property> - <property name="right_padding">0</property> + <property name="can_focus">True</property> + <property name="position">200</property> <child> - <widget class="GtkScrolledWindow" id="LibraryContainer"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="shadow_type">GTK_SHADOW_IN</property> - <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + <widget class="GtkVBox" id="DisclosureBox"> + <property name="homogeneous">False</property> + <property name="spacing">0</property> <child> <placeholder/> </child> </widget> + <packing> + <property name="shrink">False</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="LibraryAlignment"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkScrolledWindow" id="LibraryContainer"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> </child> </widget> <packing> Index: src/PlayerInterface.cs =================================================================== RCS file: /cvs/gnome/banshee/src/PlayerInterface.cs,v retrieving revision 1.111 diff -u -r1.111 PlayerInterface.cs --- src/PlayerInterface.cs 6 Dec 2005 11:54:34 -0000 1.111 +++ src/PlayerInterface.cs 7 Dec 2005 18:57:26 -0000 @@ -62,6 +62,9 @@ [Widget] private Gtk.Label LabelStatusBar; [Widget] private HPaned SourceSplitter; [Widget] private Button HeaderCycleButton; + [Widget] private Expander CustomExpander; + [Widget] private Gtk.VBox DisclosureBox; + private PlaylistModel playlistModel; @@ -92,6 +95,8 @@ private CoverArtThumbnail cover_art; private bool incrementedCurrentSongPlayCount; + + private BrowserNoteBook browser_notebook; public Gtk.Window Window { @@ -408,6 +413,14 @@ gxml["SearchLabel"].Sensitive = false; searchEntry.Sensitive = false; + + // Custom Expander and browser related stuff. + CustomExpander = ((Expander)gxml["CustomExpander"]); + DisclosureBox = ((VBox)gxml["DisclosureBox"]); + CustomExpander.Activated += OnExpandAreaClicked; + browser_notebook = new BrowserNoteBook(); + browser_notebook.QueryTransactionComplete += OnQueryTransactionComplete; + DisclosureBox.Add(browser_notebook); // Repeat/Shuffle buttons @@ -548,6 +561,11 @@ } } + private void OnQueryTransactionComplete(object o, QueryTransactionEventArgs args) + { + playlistModel.LoadFromQuery(args.query); + } + private void ConnectToLibraryTransactionManager() { Core.Library.TransactionManager.ExecutionStackChanged += OnLTMExecutionStackChanged; @@ -774,6 +792,17 @@ handled = true; } break; + case Gdk.Key.F5: + if((args.Event.State & Gdk.ModifierType.ControlMask) != 0) { + if (CustomExpander.Expanded) { + CustomExpander.Expanded = false; + } else { + CustomExpander.Expanded = true; + } + OnExpandAreaClicked(this, new EventArgs()); + handled = true; + } + break; case Gdk.Key.space: if(!searchEntry.HasFocus) { PlayPause(); @@ -1223,9 +1252,15 @@ searchEntry.CancelSearch(false); if(source.Type == SourceType.Library) { - playlistModel.LoadFromLibrary(); + if(!browser_notebook.BrowserHasFilter()) { + playlistModel.LoadFromLibrary(); + } playlistModel.Source = source; - + browser_notebook.Active(true); + Application.Invoke(delegate { + browser_notebook.BrowserFilter(); + }); + (gxml["ViewNameLabel"] as Label).Markup = String.Format(Catalog.GetString("<b>{0}'s Music Library</b>"), GLib.Markup.EscapeText(Core.Instance.UserFirstName)); @@ -1235,15 +1270,17 @@ DapSource dap_source = source as DapSource; playlistModel.LoadFromDapSource(dap_source); UpdateDapDiskUsageBar(dap_source); + browser_notebook.Active(false); } else if(source.Type == SourceType.AudioCd) { playlistModel.Clear(); playlistModel.Source = source; - AudioCdSource cdSource = source as AudioCdSource; playlistModel.LoadFromAudioCdSource(cdSource); + browser_notebook.Active(false); } else { playlistModel.LoadFromPlaylist(source.Name); playlistModel.Source = source; + browser_notebook.Active(false); } (gxml["ViewNameLabel"] as Label).Markup = @@ -1657,8 +1694,8 @@ } catch(Exception) {} } } - - transaction.Finished += OnLibraryTrackRemoveFinished; + + transaction.Finished += OnLibraryTrackRemoveFinished; transaction.Register(); } @@ -1666,6 +1703,9 @@ { playlistView.QueueDraw(); sourceView.QueueDraw(); + Application.Invoke(delegate { + browser_notebook.BrowserUpdate(); + }); } private void OnItemPropertiesActivate(object o, EventArgs args) @@ -2463,6 +2503,7 @@ } playlistModel.AddTrack(ti); + browser_notebook.BrowserUpdate(); }); } } catch(Entagged.Audioformats.Exceptions.CannotReadException) { @@ -2472,6 +2513,18 @@ Console.WriteLine(Catalog.GetString("Cannot Import: {0} ({1}, {2})"), args.FileName, e.GetType(), e.Message); args.ReturnMessage = Catalog.GetString("Scanning") + "..."; + } + } + + private void OnExpandAreaClicked (object sender, EventArgs args) + { + if (CustomExpander.Expanded) + { + DisclosureBox.Visible = true; + } + else + { + DisclosureBox.Visible = false; } } } Index: src/PlaylistModel.cs =================================================================== RCS file: /cvs/gnome/banshee/src/PlaylistModel.cs,v retrieving revision 1.25 diff -u -r1.25 PlaylistModel.cs --- src/PlaylistModel.cs 4 Dec 2005 20:08:54 -0000 1.25 +++ src/PlaylistModel.cs 7 Dec 2005 18:57:27 -0000 @@ -58,7 +58,7 @@ public Source Source; public event EventHandler Updated; - + public static int NextUid { get { @@ -153,7 +153,7 @@ loader.HaveTrackInfo += OnLoaderHaveTrackInfo; Core.Library.TransactionManager.Register(loader); } - + public void LoadFromLibrary() { ClearModel(); @@ -164,6 +164,14 @@ SyncPlayingIter(); RaiseUpdated(this, new EventArgs()); + } + + public void LoadFromQuery(Statement query) + { + ClearModel(); + SqlQueryTransaction loader = new SqlQueryTransaction(query); + loader.HaveTrackInfo += OnLoaderHaveTrackInfo; + Core.Library.TransactionManager.Register(loader); } public void LoadFromDapSource(DapSource dapSource) Index: src/Makefile.am =================================================================== RCS file: /cvs/gnome/banshee/src/Makefile.am,v retrieving revision 1.75 diff -u -r1.75 Makefile.am --- src/Makefile.am 6 Dec 2005 11:54:34 -0000 1.75 +++ src/Makefile.am 7 Dec 2005 18:57:28 -0000 @@ -63,6 +63,8 @@ ExceptionDialog.cs \ VersionInformationDialog.cs \ LogCoreViewer.cs \ + ArtistAlbumBrowser.cs \ + BrowserNoteBook.cs \ FileSystemMonitor/Watcher.cs \ FileSystemMonitor/Inotify.cs \ FileSystemMonitor/Watch.cs \ Index: src/LibraryTransactions.cs =================================================================== RCS file: /cvs/gnome/banshee/src/LibraryTransactions.cs,v retrieving revision 1.30 diff -u -r1.30 LibraryTransactions.cs --- src/LibraryTransactions.cs 26 Nov 2005 02:09:56 -0000 1.30 +++ src/LibraryTransactions.cs 7 Dec 2005 18:57:32 -0000 @@ -485,7 +485,7 @@ abstract public class TrackRemoveTransaction : LibraryTransaction { - public ArrayList RemoveQueue; + public ArrayList RemoveQueue; public TrackRemoveTransaction() { @@ -496,7 +496,7 @@ public class LibraryTrackRemoveTransaction : TrackRemoveTransaction { - public override string Name + public override string Name { get { return Catalog.GetString("Library Track Remove"); @@ -528,6 +528,7 @@ currentCount = 0; totalCount = 0; Core.Library.Db.Execute(query); + } } @@ -768,4 +769,64 @@ return count; } } + + public class SqlQueryTransaction : LibraryTransaction + { + private string sql; + public event HaveTrackInfoHandler HaveTrackInfo; + + public override string Name { + get { + return Catalog.GetString("Library Track Loader"); + } + } + + public SqlQueryTransaction(string sql) + { + showStatus = false; + this.sql = sql; + } + + public SqlQueryTransaction(Statement sql) : this(sql.ToString()) + { + + } + + public override void Run() + { + statusMessage = Catalog.GetString("Processing"); + FilterSql(); + } + + private void RaiseTrackInfo(TrackInfo ti) + { + statusMessage = String.Format( + Catalog.GetString("Loading {0} - {1} ..."), + ti.Artist, ti.Title); + currentCount++; + + HaveTrackInfoHandler handler = HaveTrackInfo; + if(handler != null) { + HaveTrackInfoArgs args = new HaveTrackInfoArgs(); + args.TrackInfo = ti; + handler(this, args); + } + } + + + private void FilterSql() + { + IDataReader reader = Core.Library.Db.Query(sql); + while(reader.Read() && !cancelRequested) { + DateTime startStamp = DateTime.Now; + int tid = Convert.ToInt32(reader[0]); + TrackInfo ti = Core.Library.Tracks[tid] as TrackInfo; + + if(ti != null) { + RaiseTrackInfo(ti); + UpdateAverageDuration(startStamp); + } + } + } + } } Index: src/QueryBuilder.cs =================================================================== RCS file: /cvs/gnome/banshee/src/QueryBuilder.cs,v retrieving revision 1.4 diff -u -r1.4 QueryBuilder.cs --- src/QueryBuilder.cs 31 Aug 2005 15:54:46 -0000 1.4 +++ src/QueryBuilder.cs 7 Dec 2005 18:57:32 -0000 @@ -35,490 +35,520 @@ namespace Banshee { - // --- Query Filter Operations --- - - public class ComboBoxUtil - { - public static string GetActiveString(ComboBox box) - { - TreeIter iter; - if(!box.GetActiveIter(out iter)) - return null; - - return (string)box.Model.GetValue(iter, 0); - } - } - - // --- Base QueryMatch Class --- + // --- Query Filter Operations --- + + public class ComboBoxUtil + { + public static string GetActiveString(ComboBox box) + { + TreeIter iter; + if(!box.GetActiveIter(out iter)) + return null; + + return (string)box.Model.GetValue(iter, 0); + } + } + + // --- Base QueryMatch Class --- - public abstract class QueryMatch - { - public string Column, Filter, Value1, Value2; - - public abstract string FilterValues(); - public abstract void UpdateValues(); - - public abstract Widget DisplayWidget - { - get; - } - - public abstract string [] ValidOperations - { - get; - } - - protected static HBox BuildRangeBox(Widget a, Widget b) - { - HBox box = new HBox(); - box.Spacing = 5; - a.Show(); - box.PackStart(a, true, true, 0); - - Label label = new Label(" to "); - label.Show(); - box.PackStart(label, false, false, 0); - - b.Show(); - box.PackStart(b, true, true, 0); - - box.Show(); - - return box; - } - } - - // --- Base QueryBuilderModel Class --- - - public abstract class QueryBuilderModel : IEnumerable - { - private Hashtable fields; - private Hashtable columnLookup; - private Hashtable orderMap; - - public QueryBuilderModel() - { - fields = new Hashtable(); - columnLookup = new Hashtable(); - orderMap = new Hashtable(); - } - - public Type this [string index] - { - get { - return (Type)fields[index]; - } - } - - public IEnumerator GetEnumerator() - { - return fields.Keys.GetEnumerator(); - } - - public void AddField(string name, string column, Type matchType) - { - fields[name] = matchType; - columnLookup[name] = column; - } - - public void AddOrder(string name, string map) - { - orderMap[name] = map; - } - - public string GetOrder(string name) - { - return (string)orderMap[name]; - } - - public string GetColumn(string name) - { - return (string)columnLookup[name]; - } - - public abstract string [] LimitCriteria - { - get; - } - - public ICollection OrderCriteria - { - get { - return orderMap.Keys; - } - } - } - - // --- Query Builder Widgets + public abstract class QueryMatch + { + public string Column, Filter, Value1, Value2; + + public abstract string FilterValues(); + public abstract void UpdateValues(); + + public abstract Widget DisplayWidget + { + get; + } + + public abstract string [] ValidOperations + { + get; + } + + protected static HBox BuildRangeBox(Widget a, Widget b) + { + HBox box = new HBox(); + box.Spacing = 5; + a.Show(); + box.PackStart(a, true, true, 0); + + Label label = new Label(" to "); + label.Show(); + box.PackStart(label, false, false, 0); + + b.Show(); + box.PackStart(b, true, true, 0); + + box.Show(); + + return box; + } + } + + // --- Base QueryBuilderModel Class --- + + public abstract class QueryBuilderModel : IEnumerable + { + private Hashtable fields; + private Hashtable columnLookup; + private Hashtable orderMap; + + public QueryBuilderModel() + { + fields = new Hashtable(); + columnLookup = new Hashtable(); + orderMap = new Hashtable(); + } + + public Type this [string index] + { + get { + return (Type)fields[index]; + } + } + + public IEnumerator GetEnumerator() + { + return fields.Keys.GetEnumerator(); + } + + public void AddField(string name, string column, Type matchType) + { + fields[name] = matchType; + columnLookup[name] = column; + } + + public void AddOrder(string name, string map) + { + orderMap[name] = map; + } + + public string GetOrder(string name) + { + return (string)orderMap[name]; + } + + public string GetColumn(string name) + { + return (string)columnLookup[name]; + } + + public abstract string [] LimitCriteria + { + get; + } + + public ICollection OrderCriteria + { + get { + return orderMap.Keys; + } + } + } + + // --- Query Builder Widgets - public class QueryBuilderMatchRow : HBox - { - private VBox widgetBox; - private ComboBox fieldBox, opBox; - private Widget valueBox; - private QueryBuilderModel model; - private QueryMatch match; - private Button buttonAdd; - private Button buttonRemove; - - public event EventHandler AddRequest; - public event EventHandler RemoveRequest; - - private bool canDelete; - - public QueryBuilderMatchRow(QueryBuilderModel model) : base() - { - this.model = model; - - Spacing = 5; - - fieldBox = ComboBox.NewText(); - fieldBox.Changed += OnFieldComboBoxChanged; - PackStart(fieldBox, false, false, 0); - fieldBox.Show(); - - opBox = ComboBox.NewText(); - opBox.Changed += OnOpComboBoxChanged; - PackStart(opBox, false, false, 0); - opBox.Show(); - - widgetBox = new VBox(); - widgetBox.Show(); - PackStart(widgetBox, true, true, 0); - - foreach(string fieldName in model) { - fieldBox.AppendText(fieldName); - } - - Select(0); - - Image imageRemove = new Image("gtk-remove", IconSize.Button); - buttonRemove = new Button(imageRemove); - buttonRemove.Show(); - buttonRemove.Clicked += OnButtonRemoveClicked; - imageRemove.Show(); - PackStart(buttonRemove, false, false, 0); - - Image imageAdd = new Image("gtk-add", IconSize.Button); - buttonAdd = new Button(imageAdd); - buttonAdd.Show(); - buttonAdd.Clicked += OnButtonAddClicked; - imageAdd.Show(); - PackStart(buttonAdd, false, false, 0); - - canDelete = true; - } - - private void Select(int index) - { - TreeIter iter; - - if(!fieldBox.Model.IterNthChild(out iter, index)) - return; + public class QueryBuilderMatchRow : HBox + { + private VBox widgetBox; + private ComboBox fieldBox, opBox; + private Widget valueBox; + private QueryBuilderModel model; + private QueryMatch match; + private Button buttonAdd; + private Button buttonRemove; + + public event EventHandler AddRequest; + public event EventHandler RemoveRequest; + + private bool canDelete; + + public QueryBuilderMatchRow(QueryBuilderModel model) : base() + { + this.model = model; + + Spacing = 5; + + fieldBox = ComboBox.NewText(); + fieldBox.Changed += OnFieldComboBoxChanged; + PackStart(fieldBox, false, false, 0); + fieldBox.Show(); + + opBox = ComboBox.NewText(); + opBox.Changed += OnOpComboBoxChanged; + PackStart(opBox, false, false, 0); + opBox.Show(); + + widgetBox = new VBox(); + widgetBox.Show(); + PackStart(widgetBox, true, true, 0); + + foreach(string fieldName in model) { + fieldBox.AppendText(fieldName); + } + + Select(0); + + Image imageRemove = new Image("gtk-remove", IconSize.Button); + buttonRemove = new Button(imageRemove); + buttonRemove.Show(); + buttonRemove.Clicked += OnButtonRemoveClicked; + imageRemove.Show(); + PackStart(buttonRemove, false, false, 0); + + Image imageAdd = new Image("gtk-add", IconSize.Button); + buttonAdd = new Button(imageAdd); + buttonAdd.Show(); + buttonAdd.Clicked += OnButtonAddClicked; + imageAdd.Show(); + PackStart(buttonAdd, false, false, 0); + + canDelete = true; + } + + private void Select(int index) + { + TreeIter iter; + + if(!fieldBox.Model.IterNthChild(out iter, index)) + return; - fieldBox.SetActiveIter(iter); - } - - private void Select(TreeIter iter) - { - string fieldName = (string)fieldBox.Model.GetValue(iter, 0); - - Type matchType = model[fieldName]; - match = Activator.CreateInstance(matchType) as QueryMatch; - - while(opBox.Model.IterNChildren() > 0) - opBox.RemoveText(0); + fieldBox.SetActiveIter(iter); + } + + private void Select(TreeIter iter) + { + string fieldName = (string)fieldBox.Model.GetValue(iter, 0); + + Type matchType = model[fieldName]; + match = Activator.CreateInstance(matchType) as QueryMatch; + + while(opBox.Model.IterNChildren() > 0) + opBox.RemoveText(0); - foreach(string op in match.ValidOperations) - opBox.AppendText(op); - - TreeIter opIterFirst; - if(!opBox.Model.IterNthChild(out opIterFirst, 0)) - throw new Exception("Field has no operations"); - - match.Column = fieldName; - - opBox.SetActiveIter(opIterFirst); - } - - private void OnFieldComboBoxChanged(object o, EventArgs args) - { - TreeIter iter; - fieldBox.GetActiveIter(out iter); - Select(iter); - } - - private void OnOpComboBoxChanged(object o, EventArgs args) - { - TreeIter iter; - opBox.GetActiveIter(out iter); - string opName = (string)opBox.Model.GetValue(iter, 0); - - match.Filter = opName; - - widgetBox.Foreach(WidgetBoxForeachRemoveChild); - widgetBox.Add(match.DisplayWidget); - } - - private void WidgetBoxForeachRemoveChild(Widget widget) - { - widgetBox.Remove(widget); - } - - private void OnButtonAddClicked(object o, EventArgs args) - { - EventHandler handler = AddRequest; - if(handler != null) - handler(this, new EventArgs()); - } - - private void OnButtonRemoveClicked(object o, EventArgs args) - { - EventHandler handler = RemoveRequest; - if(handler != null) - handler(this, new EventArgs()); - } - - public bool CanDelete - { - set { - canDelete = value; - buttonRemove.Sensitive = value; - } - } - - public string Query - { - get { - match.Column = - model.GetColumn(ComboBoxUtil.GetActiveString(fieldBox)); - match.Filter = ComboBoxUtil.GetActiveString(opBox); - return match.FilterValues(); - } - } - } + foreach(string op in match.ValidOperations) + opBox.AppendText(op); + + TreeIter opIterFirst; + if(!opBox.Model.IterNthChild(out opIterFirst, 0)) + throw new Exception("Field has no operations"); + + match.Column = fieldName; + + opBox.SetActiveIter(opIterFirst); + } + + private void OnFieldComboBoxChanged(object o, EventArgs args) + { + TreeIter iter; + fieldBox.GetActiveIter(out iter); + Select(iter); + } + + private void OnOpComboBoxChanged(object o, EventArgs args) + { + TreeIter iter; + opBox.GetActiveIter(out iter); + string opName = (string)opBox.Model.GetValue(iter, 0); + + match.Filter = opName; + + widgetBox.Foreach(WidgetBoxForeachRemoveChild); + widgetBox.Add(match.DisplayWidget); + } + + private void WidgetBoxForeachRemoveChild(Widget widget) + { + widgetBox.Remove(widget); + } + + private void OnButtonAddClicked(object o, EventArgs args) + { + EventHandler handler = AddRequest; + if(handler != null) + handler(this, new EventArgs()); + } + + private void OnButtonRemoveClicked(object o, EventArgs args) + { + EventHandler handler = RemoveRequest; + if(handler != null) + handler(this, new EventArgs()); + } + + public bool CanDelete + { + set { + canDelete = value; + buttonRemove.Sensitive = value; + } + } + + public string Query + { + get { + match.Column = + model.GetColumn(ComboBoxUtil.GetActiveString(fieldBox)); + match.Filter = ComboBoxUtil.GetActiveString(opBox); + return match.FilterValues(); + } + } + } - public class QueryBuilderMatches : VBox - { - private QueryBuilderModel model; - - public QueryBuilderMatches(QueryBuilderModel model) : base() - { - this.model = model; - CreateRow(false); - } - - public void CreateRow(bool canDelete) - { - QueryBuilderMatchRow row = new QueryBuilderMatchRow(model); - row.Show(); - PackStart(row, false, false, 0); - row.CanDelete = canDelete; - row.AddRequest += OnRowAddRequest; - row.RemoveRequest += OnRowRemoveRequest; - } - - public void OnRowAddRequest(object o, EventArgs args) - { - CreateRow(true); - UpdateCanDelete(); - } - - public void OnRowRemoveRequest(object o, EventArgs args) - { - Remove(o as Widget); - UpdateCanDelete(); - } - - public void UpdateCanDelete() - { - ((QueryBuilderMatchRow)Children[0]).CanDelete = Children.Length > 1; - } - - public string BuildQuery(string join) - { - string query = null; - for(int i = 0, n = Children.Length; i < n; i++) { - QueryBuilderMatchRow match = Children[i] as QueryBuilderMatchRow; - query += " " + match.Query + " "; - if(i < n - 1) - query += join; - } - - return query; - } - } + public class QueryBuilderMatches : VBox + { + private QueryBuilderModel model; + + public QueryBuilderMatches(QueryBuilderModel model) : base() + { + this.model = model; + CreateRow(false); + } + + public void CreateRow(bool canDelete) + { + QueryBuilderMatchRow row = new QueryBuilderMatchRow(model); + row.Show(); + PackStart(row, false, false, 0); + row.CanDelete = canDelete; + row.AddRequest += OnRowAddRequest; + row.RemoveRequest += OnRowRemoveRequest; + } + + public void OnRowAddRequest(object o, EventArgs args) + { + CreateRow(true); + UpdateCanDelete(); + } + + public void OnRowRemoveRequest(object o, EventArgs args) + { + Remove(o as Widget); + UpdateCanDelete(); + } + + public void UpdateCanDelete() + { + ((QueryBuilderMatchRow)Children[0]).CanDelete = Children.Length > 1; + } + + public string BuildQuery(string join) + { + string query = null; + for(int i = 0, n = Children.Length; i < n; i++) { + QueryBuilderMatchRow match = Children[i] as QueryBuilderMatchRow; + query += " " + match.Query + " "; + if(i < n - 1) + query += join; + } + + return query; + } + } + + public class QueryStatusArgs : EventArgs + { + public bool QueryStatus; + } + + public delegate void QueryStatusChangedHandler(object o, QueryStatusArgs args); - public class QueryBuilder : VBox - { - private QueryBuilderModel model; - - private CheckButton matchCheckBox; - private ComboBox matchLogicCombo; - private QueryBuilderMatches matchesBox; - private Label matchLabelFollowing; - - private CheckButton limitCheckBox; - private Entry limitEntry; - private ComboBox limitComboBox; - private ComboBox orderComboBox; - - public QueryBuilder(QueryBuilderModel model) : base() - { - this.model = model; - - matchesBox = new QueryBuilderMatches(model); - matchesBox.Spacing = 5; - matchesBox.Show(); - - Alignment matchesAlignment = new Alignment(0.0f, 0.0f, 1.0f, 1.0f); - matchesAlignment.Show(); - matchesAlignment.SetPadding(10, 10, 10, 10); - matchesAlignment.Add(matchesBox); - - Frame matchesFrame = new Frame(null); - matchesFrame.Show(); - matchesFrame.Add(matchesAlignment); - - matchesFrame.LabelWidget = BuildMatchHeader(); - - PackStart(matchesFrame, true, true, 0); - PackStart(BuildLimitFooter(), false, false, 0); - } - - private HBox BuildMatchHeader() - { - HBox matchHeader = new HBox(); - matchHeader.Show(); - - matchCheckBox = new CheckButton("Match"); - matchCheckBox.Show(); - matchCheckBox.Toggled += OnMatchCheckBoxToggled; - matchHeader.PackStart(matchCheckBox, false, false, 0); - - matchLogicCombo = ComboBox.NewText(); - matchLogicCombo.AppendText("all"); - matchLogicCombo.AppendText("any"); - matchLogicCombo.Show(); - matchLogicCombo.Active = 0; - matchHeader.PackStart(matchLogicCombo, false, false, 0); - - matchLabelFollowing = new Label("of the following:"); - matchLabelFollowing.Show(); - matchLabelFollowing.Xalign = 0.0f; - matchHeader.PackStart(matchLabelFollowing, true, true, 0); - - matchHeader.Spacing = 5; - - matchCheckBox.Active = false; - OnMatchCheckBoxToggled(matchCheckBox, null); - - return matchHeader; - } - - private HBox BuildLimitFooter() - { - HBox limitFooter = new HBox(); - limitFooter.Show(); - limitFooter.Spacing = 5; - - limitCheckBox = new CheckButton("Limit to"); - limitCheckBox.Show(); - limitCheckBox.Toggled += OnLimitCheckBoxToggled; - limitFooter.PackStart(limitCheckBox, false, false, 0); - - limitEntry = new Entry(); - limitEntry.Show(); - limitEntry.SetSizeRequest(50, -1); - limitFooter.PackStart(limitEntry, false, false, 0); - - limitComboBox = ComboBox.NewText(); - limitComboBox.Show(); - foreach(string criteria in model.LimitCriteria) - limitComboBox.AppendText(criteria); - limitComboBox.Active = 0; - limitFooter.PackStart(limitComboBox, false, false, 0); - - Label orderLabel = new Label("selected by"); - orderLabel.Show(); - limitFooter.PackStart(orderLabel, false, false, 0); - - orderComboBox = ComboBox.NewText(); - orderComboBox.Show(); - foreach(string order in model.OrderCriteria) - orderComboBox.AppendText(order); - orderComboBox.Active = 0; - limitFooter.PackStart(orderComboBox, false, false, 0); - - limitCheckBox.Active = false; - OnLimitCheckBoxToggled(limitCheckBox, null); - - return limitFooter; - } - - private void OnMatchCheckBoxToggled(object o, EventArgs args) - { - matchesBox.Sensitive = matchCheckBox.Active; - matchLogicCombo.Sensitive = matchCheckBox.Active; - matchLabelFollowing.Sensitive = matchCheckBox.Active; - } - - private void OnLimitCheckBoxToggled(object o, EventArgs args) - { - limitEntry.Sensitive = limitCheckBox.Active; - limitComboBox.Sensitive = limitCheckBox.Active; - } - - public bool MatchesEnabled - { - get { - return matchCheckBox.Active; - } - } - - public string MatchQuery - { - get { - return matchesBox.BuildQuery( - ComboBoxUtil.GetActiveString(matchLogicCombo) == "any" ? - "OR" : - "AND" - ); - } - } - - public int LimitNumber - { - get { - try { - return Convert.ToInt32(limitEntry.Text); - } catch(Exception) { - return 0; - } - } - } - - public string LimitCriteria - { - get { - return ComboBoxUtil.GetActiveString(limitComboBox); - } - } - - public bool Limit - { - get { - return limitCheckBox.Active; - } - } - - public string OrderBy - { - get { - return - model.GetOrder(ComboBoxUtil.GetActiveString(orderComboBox)); - } - } - } + public class QueryBuilder : VBox + { + private QueryBuilderModel model; + + private CheckButton matchCheckBox; + private ComboBox matchLogicCombo; + private QueryBuilderMatches matchesBox; + private Label matchLabelFollowing; + + private CheckButton limitCheckBox; + private Entry limitEntry; + private ComboBox limitComboBox; + private ComboBox orderComboBox; + + private Button searchButton; + + public event QueryStatusChangedHandler QueryStatusChanged; + + public QueryBuilder(QueryBuilderModel model) : base() + { + this.model = model; + + matchesBox = new QueryBuilderMatches(model); + matchesBox.Spacing = 5; + matchesBox.Show(); + + Alignment matchesAlignment = new Alignment(0.0f, 0.0f, 1.0f, 1.0f); + matchesAlignment.Show(); + matchesAlignment.SetPadding(10, 10, 10, 10); + matchesAlignment.Add(matchesBox); + + Frame matchesFrame = new Frame(null); + matchesFrame.Show(); + matchesFrame.Add(matchesAlignment); + + matchesFrame.LabelWidget = BuildMatchHeader(); + + PackStart(matchesFrame, true, true, 0); + PackStart(BuildLimitFooter(), false, false, 0); + } + + private HBox BuildMatchHeader() + { + HBox matchHeader = new HBox(); + matchHeader.Show(); + + matchCheckBox = new CheckButton("Match"); + matchCheckBox.Show(); + matchCheckBox.Toggled += OnMatchCheckBoxToggled; + matchHeader.PackStart(matchCheckBox, false, false, 0); + + matchLogicCombo = ComboBox.NewText(); + matchLogicCombo.AppendText("all"); + matchLogicCombo.AppendText("any"); + matchLogicCombo.Show(); + matchLogicCombo.Active = 0; + matchHeader.PackStart(matchLogicCombo, false, false, 0); + + matchLabelFollowing = new Label("of the following:"); + matchLabelFollowing.Show(); + matchLabelFollowing.Xalign = 0.0f; + matchHeader.PackStart(matchLabelFollowing, true, true, 0); + + matchHeader.Spacing = 5; + + matchCheckBox.Active = false; + OnMatchCheckBoxToggled(matchCheckBox, null); + + return matchHeader; + } + + private HBox BuildLimitFooter() + { + HBox limitFooter = new HBox(); + limitFooter.Show(); + limitFooter.Spacing = 5; + + limitCheckBox = new CheckButton("Limit to"); + limitCheckBox.Show(); + limitCheckBox.Toggled += OnLimitCheckBoxToggled; + limitFooter.PackStart(limitCheckBox, false, false, 0); + + limitEntry = new Entry(); + limitEntry.Show(); + limitEntry.SetSizeRequest(50, -1); + limitFooter.PackStart(limitEntry, false, false, 0); + + limitComboBox = ComboBox.NewText(); + limitComboBox.Show(); + foreach(string criteria in model.LimitCriteria) + limitComboBox.AppendText(criteria); + limitComboBox.Active = 0; + limitFooter.PackStart(limitComboBox, false, false, 0); + + Label orderLabel = new Label("selected by"); + orderLabel.Show(); + limitFooter.PackStart(orderLabel, false, false, 0); + + orderComboBox = ComboBox.NewText(); + orderComboBox.Show(); + foreach(string order in model.OrderCriteria) + orderComboBox.AppendText(order); + orderComboBox.Active = 0; + limitFooter.PackStart(orderComboBox, false, false, 0); + + searchButton = new Button("Search"); + searchButton.Clicked += OnSearchButtonClicked; + searchButton.Show(); + limitFooter.PackStart(searchButton, true, true, 0); + + limitCheckBox.Active = false; + OnLimitCheckBoxToggled(limitCheckBox, null); + + return limitFooter; + } + + private void OnSearchButtonClicked(object o, EventArgs args) + { + QueryStatusChangedHandler handler = QueryStatusChanged; + QueryStatusArgs statusArgs = new QueryStatusArgs(); + statusArgs.QueryStatus = matchCheckBox.Active; + if(handler != null) + handler(o, statusArgs); + } + + private void OnMatchCheckBoxToggled(object o, EventArgs args) + { + matchesBox.Sensitive = matchCheckBox.Active; + matchLogicCombo.Sensitive = matchCheckBox.Active; + matchLabelFollowing.Sensitive = matchCheckBox.Active; + QueryStatusChangedHandler handler = QueryStatusChanged; + QueryStatusArgs statusArgs = new QueryStatusArgs(); + statusArgs.QueryStatus = matchCheckBox.Active; + if(handler != null) + handler(o, statusArgs); + } + + private void OnLimitCheckBoxToggled(object o, EventArgs args) + { + limitEntry.Sensitive = limitCheckBox.Active; + limitComboBox.Sensitive = limitCheckBox.Active; + } + + public bool MatchesEnabled + { + get { + return matchCheckBox.Active; + } + } + + public string MatchQuery + { + get { + return matchesBox.BuildQuery( + ComboBoxUtil.GetActiveString(matchLogicCombo) == "any" ? + "OR" : + "AND" + ); + } + } + + public int LimitNumber + { + get { + try { + return Convert.ToInt32(limitEntry.Text); + } catch(Exception) { + return 0; + } + } + } + + public string LimitCriteria + { + get { + return ComboBoxUtil.GetActiveString(limitComboBox); + } + } + + public bool Limit + { + get { + return limitCheckBox.Active; + } + } + + public string OrderBy + { + get { + return + model.GetOrder(ComboBoxUtil.GetActiveString(orderComboBox)); + } + } + } } Index: src/QueryBuilderModel.cs =================================================================== RCS file: /cvs/gnome/banshee/src/QueryBuilderModel.cs,v retrieving revision 1.4 diff -u -r1.4 QueryBuilderModel.cs --- src/QueryBuilderModel.cs 17 Nov 2005 09:16:46 -0000 1.4 +++ src/QueryBuilderModel.cs 7 Dec 2005 18:57:32 -0000 @@ -30,289 +30,330 @@ using System; using GLib; using Gtk; -using Sql; using System.Collections; +using Sql; using Banshee.Widgets; + namespace Banshee { - public sealed class QueryFilterOperation - { - public const string Is = "is"; - public const string IsNot = "is not"; - public const string IsLessThan = "is less than"; - public const string IsGreaterThan = "is greater than"; - public const string Contains = "contains"; - public const string DoesNotContain = "does not contain"; - public const string StartsWith = "starts with"; - public const string EndsWith = "ends with"; - public const string IsBefore = "is before"; - public const string IsAfter = "is after"; - public const string IsInTheRange = "is in the range"; - } - - public sealed class QuerySelectedByCriteria - { - public const string Random = "Random"; - public const string Album = "Album"; - public const string Artist = "Artist"; - public const string Genre = "Genre"; - public const string SongName = "Song Name"; - public const string HighestRating = "Highest Rating"; - public const string LowestRating = "Lowest Rating"; - public const string LeastOftenPlayed = "Least Often Played"; - public const string MostOftenPlayed = "Most Often Played"; - public const string MostRecentlyAdded = "Most Recently Added"; - public const string LeastRecentlyAdded = "Least Recently Added"; - } - - public sealed class QueryLimitCriteria - { - public const string Songs = "songs"; - public const string Minutes = "minutes"; - public const string Hours = "hours"; - } - - // --- Query Match String --- - - public class QueryMatchString : QueryMatch - { - private Entry dispEntry; + public sealed class QueryFilterOperation + { + public const string Is = "is"; + public const string IsNot = "is not"; + public const string IsLessThan = "is less than"; + public const string IsGreaterThan = "is greater than"; + public const string Contains = "contains"; + public const string DoesNotContain = "does not contain"; + public const string StartsWith = "starts with"; + public const string EndsWith = "ends with"; + public const string IsBefore = "is before"; + public const string IsAfter = "is after"; + public const string IsInTheRange = "is in the range"; + } + + public sealed class QuerySelectedByCriteria + { + public const string Random = "Random"; + public const string Album = "Album"; + public const string Artist = "Artist"; + public const string Genre = "Genre"; + public const string SongName = "Song Name"; + public const string HighestRating = "Highest Rating"; + public const string LowestRating = "Lowest Rating"; + public const string LeastOftenPlayed = "Least Often Played"; + public const string MostOftenPlayed = "Most Often Played"; + public const string MostRecentlyAdded = "Most Recently Added"; + public const string LeastRecentlyAdded = "Least Recently Added"; + } + + public sealed class QueryLimitCriteria + { + public const string Songs = "songs"; + public const string Minutes = "minutes"; + public const string Hours = "hours"; + } + + // --- Query Match String --- + + public class QueryMatchString : QueryMatch + { + private Entry dispEntry; + + public override string FilterValues() + { + UpdateValues(); + + string pv = Statement.EscapeQuotes(Value1); + + switch(Filter) { + case QueryFilterOperation.Is: + return Column + " = '" + pv + "'"; + case QueryFilterOperation.IsNot: + return Column + " != '" + pv + "'"; + case QueryFilterOperation.Contains: + return Column + " LIKE '%" + pv + "%'"; + case QueryFilterOperation.DoesNotContain: + return Column + " NOT LIKE '%" + pv + "%'"; + case QueryFilterOperation.StartsWith: + return Column + " LIKE '" + pv + "%'"; + case QueryFilterOperation.EndsWith: + return Column + " LIKE '%" + pv + "'"; + } + + return null; + } + + public override void UpdateValues() + { + if(dispEntry == null) + throw new Exception("Display Widget was never Set"); + + Value1 = dispEntry.Text; + } + + public override Widget DisplayWidget + { + get { + if(dispEntry == null) { + dispEntry = new Entry(); + dispEntry.Show(); + } + + return dispEntry; + } + } + + public override string [] ValidOperations + { + get { + string [] validOperations = { + QueryFilterOperation.Contains, + QueryFilterOperation.Is, + QueryFilterOperation.IsNot, + QueryFilterOperation.DoesNotContain, + QueryFilterOperation.StartsWith, + QueryFilterOperation.EndsWith + }; - public override string FilterValues() - { - UpdateValues(); - - string pv = Statement.EscapeQuotes(Value1); - - switch(Filter) { - case QueryFilterOperation.Is: - return Column + " = '" + pv + "'"; - case QueryFilterOperation.IsNot: - return Column + " != '" + pv + "'"; - case QueryFilterOperation.Contains: - return Column + " LIKE '%" + pv + "%'"; - case QueryFilterOperation.DoesNotContain: - return Column + " NOT LIKE '%" + pv + "%'"; - case QueryFilterOperation.StartsWith: - return Column + " LIKE '" + pv + "%'"; - case QueryFilterOperation.EndsWith: - return Column + " LIKE '%" + pv + "'"; - } - - return null; - } - - public override void UpdateValues() - { - if(dispEntry == null) - throw new Exception("Display Widget was never Set"); - - Value1 = dispEntry.Text; - } - - public override Widget DisplayWidget - { - get { - if(dispEntry == null) { - dispEntry = new Entry(); - dispEntry.Show(); - } - - return dispEntry; - } - } - - public override string [] ValidOperations - { - get { - string [] validOperations = { - QueryFilterOperation.Is, - QueryFilterOperation.IsNot, - QueryFilterOperation.Contains, - QueryFilterOperation.DoesNotContain, - QueryFilterOperation.StartsWith, - QueryFilterOperation.EndsWith - }; + return validOperations; + } + } + } + + // --- Query Match Date --- + + public class QueryMatchDate : QueryMatch + { + private DateButton dateButton1; + private DateButton dateButton2; + private HBox rangeBox; - return validOperations; - } - } - } - - // --- Query Match Date --- - - public class QueryMatchDate : QueryMatch - { - private DateButton dateButton1; - private DateButton dateButton2; - private HBox rangeBox; + public override string FilterValues() + { + UpdateValues(); + + string pv = Statement.EscapeQuotes(Value1), pv2 = Value2; + if(pv2 != null) + pv2 = Statement.EscapeQuotes(Value1); + + switch(Filter) { + case QueryFilterOperation.Is: + return Column + " = '" + pv + "'"; + case QueryFilterOperation.IsNot: + return Column + " != '" + pv + "'"; + case QueryFilterOperation.IsBefore: + return Column + " < '" + pv + "'"; + case QueryFilterOperation.IsAfter: + return Column + " > '" + pv + "'"; + case QueryFilterOperation.IsInTheRange: + return "(" + Column + " >= '" + pv + "' AND " + + Column + " <= '" + pv2 + "')"; + } + + return null; + } + + public override void UpdateValues() + { + if(dateButton1 == null) + throw new Exception("Display Widget was never Set"); + + Value1 = dateButton1.Date.ToString("yyyy-MM-dd"); + + if(dateButton2 != null) + Value2 = dateButton2.Date.ToString("yyyy-MM-dd"); + } + + public override Widget DisplayWidget + { + get { + if(dateButton1 == null) { + dateButton1 = new DateButton("<Select Date>"); + dateButton1.Show(); + } + + if(Filter != QueryFilterOperation.IsInTheRange) { + if(rangeBox != null && dateButton2 != null) { + rangeBox.Remove(dateButton1); + rangeBox.Remove(dateButton2); + + dateButton2.Destroy(); + dateButton2 = null; + rangeBox.Destroy(); + rangeBox = null; + } + + return dateButton1; + } + + if(dateButton2 == null) { + dateButton2 = new DateButton("<Select Date>"); + dateButton2.Show(); + } + + rangeBox = BuildRangeBox(dateButton1, dateButton2); + return rangeBox; + } + } + + public override string [] ValidOperations + { + get { + string [] validOperations = { + QueryFilterOperation.Is, + QueryFilterOperation.IsNot, + QueryFilterOperation.IsBefore, + QueryFilterOperation.IsAfter, + QueryFilterOperation.IsInTheRange + }; - public override string FilterValues() - { - UpdateValues(); - - string pv = Statement.EscapeQuotes(Value1), pv2 = Value2; - if(pv2 != null) - pv2 = Statement.EscapeQuotes(Value1); - - switch(Filter) { - case QueryFilterOperation.Is: - return Column + " = '" + pv + "'"; - case QueryFilterOperation.IsNot: - return Column + " != '" + pv + "'"; - case QueryFilterOperation.IsBefore: - return Column + " < '" + pv + "'"; - case QueryFilterOperation.IsAfter: - return Column + " > '" + pv + "'"; - case QueryFilterOperation.IsInTheRange: - return "(" + Column + " >= '" + pv + "' AND " - + Column + " <= '" + pv2 + "')"; - } - - return null; - } - - public override void UpdateValues() - { - if(dateButton1 == null) - throw new Exception("Display Widget was never Set"); - - Value1 = dateButton1.Date.ToString("yyyy-MM-dd"); - - if(dateButton2 != null) - Value2 = dateButton2.Date.ToString("yyyy-MM-dd"); - } - - public override Widget DisplayWidget - { - get { - if(dateButton1 == null) { - dateButton1 = new DateButton("<Select Date>"); - dateButton1.Show(); - } - - if(Filter != QueryFilterOperation.IsInTheRange) { - if(rangeBox != null && dateButton2 != null) { - rangeBox.Remove(dateButton1); - rangeBox.Remove(dateButton2); - - dateButton2.Destroy(); - dateButton2 = null; - rangeBox.Destroy(); - rangeBox = null; - } - - return dateButton1; - } - - if(dateButton2 == null) { - dateButton2 = new DateButton("<Select Date>"); - dateButton2.Show(); - } - - rangeBox = BuildRangeBox(dateButton1, dateButton2); - return rangeBox; - } - } - - public override string [] ValidOperations - { - get { - string [] validOperations = { - QueryFilterOperation.Is, - QueryFilterOperation.IsNot, - QueryFilterOperation.IsBefore, - QueryFilterOperation.IsAfter, - QueryFilterOperation.IsInTheRange - }; + return validOperations; + } + } + } + + public class TracksQueryModel : QueryBuilderModel + { + private Hashtable fields; + + public TracksQueryModel() : base() + { + AddField("Title", "Title", typeof(QueryMatchString)); + AddField("Artist", "Artist", typeof(QueryMatchString)); + AddField("Date Added", "DateAdded", typeof(QueryMatchDate)); + + AddOrder(QuerySelectedByCriteria.Random, "RAND()"); + AddOrder(QuerySelectedByCriteria.Album, "Album"); + AddOrder(QuerySelectedByCriteria.Artist, "Artist"); + AddOrder(QuerySelectedByCriteria.Genre, "Genre"); + AddOrder(QuerySelectedByCriteria.SongName, "Title"); + AddOrder(QuerySelectedByCriteria.HighestRating, "Rating DESC"); + AddOrder(QuerySelectedByCriteria.LowestRating, "Rating ASC"); + AddOrder(QuerySelectedByCriteria.LeastOftenPlayed, "PlayCount ASC"); + AddOrder(QuerySelectedByCriteria.MostOftenPlayed, "PlayCount DESC"); + AddOrder(QuerySelectedByCriteria.MostRecentlyAdded, "DateAdded DESC"); + AddOrder(QuerySelectedByCriteria.LeastRecentlyAdded, "DateAdded ASC"); + } - return validOperations; - } - } - } - - public class TracksQueryModel : QueryBuilderModel - { - private Hashtable fields; - - public TracksQueryModel() : base() - { - AddField("Title", "Title", typeof(QueryMatchString)); - AddField("Artist", "Artist", typeof(QueryMatchString)); - AddField("Date Added", "DateAdded", typeof(QueryMatchDate)); - - AddOrder(QuerySelectedByCriteria.Random, "RAND()"); - AddOrder(QuerySelectedByCriteria.Album, "Album"); - AddOrder(QuerySelectedByCriteria.Artist, "Artist"); - AddOrder(QuerySelectedByCriteria.Genre, "Genre"); - AddOrder(QuerySelectedByCriteria.SongName, "Title"); - AddOrder(QuerySelectedByCriteria.HighestRating, "Rating DESC"); - AddOrder(QuerySelectedByCriteria.LowestRating, "Rating ASC"); - AddOrder(QuerySelectedByCriteria.LeastOftenPlayed, "PlayCount ASC"); - AddOrder(QuerySelectedByCriteria.MostOftenPlayed, "PlayCount DESC"); - AddOrder(QuerySelectedByCriteria.MostRecentlyAdded, "DateAdded DESC"); - AddOrder(QuerySelectedByCriteria.LeastRecentlyAdded, "DateAdded ASC"); - } + public override string [] LimitCriteria + { + get { + string [] criteria = { + QueryLimitCriteria.Songs, + QueryLimitCriteria.Minutes, + QueryLimitCriteria.Hours + }; + + return criteria; + } + } + } - public override string [] LimitCriteria - { - get { - string [] criteria = { - QueryLimitCriteria.Songs, - QueryLimitCriteria.Minutes, - QueryLimitCriteria.Hours - }; - - return criteria; - } - } - } + public class SqlBuilderUI + { + private QueryBuilder builder; + private TracksQueryModel model; + + private string built_query; + public event EventHandler SearchButtonClicked; + + + public string Query + { + get { + return built_query; + } + set { + built_query = value; + } + } + + public SqlBuilderUI() + { + Window win = new Window("SQL Builder"); + win.Show(); + win.BorderWidth = 10; + win.Resizable = false; + + VBox box = new VBox(); + box.Show(); + win.Add(box); + box.Spacing = 10; + + model = new TracksQueryModel(); + builder = new QueryBuilder(model); + builder.Show(); + builder.Spacing = 4; + + box.PackStart(builder, true, true, 0); + + Button btn = new Button("Generate Query"); + btn.Show(); + box.PackStart(btn, false, false, 0); + btn.Clicked += OnButtonClicked; + } + + public SqlBuilderUI(VBox parentVBox) + { + VBox box = new VBox(); + box.Show(); + parentVBox.Add(box); + box.Spacing = 10; + + model = new TracksQueryModel(); + builder = new QueryBuilder(model); + builder.QueryStatusChanged += OnQueryStatusChanged; + builder.Show(); + builder.Spacing = 4; + + box.PackStart(builder, true, true, 0); + + } + + private void OnButtonClicked(object o, EventArgs args) + { + string query = "SELECT * FROM Tracks"; + + query += builder.MatchesEnabled ? + " WHERE" + builder.MatchQuery : " "; + + query += "ORDER BY " + builder.OrderBy; + + if(builder.Limit && builder.LimitNumber > 0) + query += " LIMIT " + builder.LimitNumber; + + built_query = query; - public class SqlBuilderUI - { - private QueryBuilder builder; - private TracksQueryModel model; - - public SqlBuilderUI() - { - Window win = new Window("SQL Builder"); - win.Show(); - win.BorderWidth = 10; - win.Resizable = false; - - VBox box = new VBox(); - box.Show(); - win.Add(box); - box.Spacing = 10; - - model = new TracksQueryModel(); - builder = new QueryBuilder(model); - builder.Show(); - builder.Spacing = 4; - - box.PackStart(builder, true, true, 0); - - Button btn = new Button("Generate Query"); - btn.Show(); - box.PackStart(btn, false, false, 0); - btn.Clicked += OnButtonClicked; - } - - private void OnButtonClicked(object o, EventArgs args) - { - string query = "SELECT * FROM Tracks"; - - query += builder.MatchesEnabled ? - " WHERE" + builder.MatchQuery : " "; - - query += "ORDER BY " + builder.OrderBy; - - if(builder.Limit && builder.LimitNumber > 0) - query += " LIMIT " + builder.LimitNumber; - - Console.WriteLine(query); - } - } + EventHandler handler = SearchButtonClicked; + if(handler != null) + handler(this, new EventArgs()); + } + + private void OnQueryStatusChanged(object o, QueryStatusArgs args) + { + OnButtonClicked(this, new EventArgs()); + } + } } --- src/ArtistAlbumBrowser.cs 2005-12-07 21:16:18.000000000 +0200 +++ src/ArtistAlbumBrowser.cs 2005-12-07 20:54:03.000000000 +0200 @@ -0,0 +1,319 @@ +// created on 11/19/2005 at 1:26 AM by Aydemir Ulas Sahin + +using System; +using System.Data; +using System.IO; +using System.Collections; +using GLib; +using Gtk; +using Mono.Unix; +using Sql; + +namespace Banshee +{ + public class ArtistAlbumBrowser : VBox + { + private Gtk.HBox main_box = new Gtk.HBox(); + private Gtk.ScrolledWindow artist_scroller = new Gtk.ScrolledWindow(); + private Gtk.ScrolledWindow album_scroller = new Gtk.ScrolledWindow(); + private Gtk.TreeView artist_tree = new Gtk.TreeView(); + private Gtk.TreeView album_tree = new Gtk.TreeView(); + + private ListStore artist_store; + private ListStore album_store; + + private string artist_selected = "none"; + private string album_selected = "none"; + + private int artist_selection_index = 0; + private int album_selection_index = 0; + + private Statement query; + private string library_query; + + private int [] ResetSelectPath = {0}; + + public event EventHandler BrowserClicked; + + public ArtistAlbumBrowser() : base() + { + BuildUI(); + Init(); + } + + public string Query + { + get { + return library_query; + } + set { + library_query = value; + } + } + + private void BuildUI() + { + artist_scroller.HscrollbarPolicy = PolicyType.Automatic; + artist_scroller.VscrollbarPolicy = PolicyType.Always; + artist_scroller.ShadowType = ShadowType.EtchedIn; + artist_scroller.BorderWidth = 3; + + album_scroller.HscrollbarPolicy = PolicyType.Automatic; + album_scroller.VscrollbarPolicy = PolicyType.Always; + album_scroller.ShadowType = ShadowType.EtchedIn; + album_scroller.BorderWidth = 3; + + artist_tree.HeadersVisible = true; + artist_tree.AppendColumn(Catalog.GetString("Artist"), new CellRendererText(), "text", 0); + artist_store = CreateStore(); + artist_tree.Model = artist_store; + artist_tree.CursorChanged += OnArtistCursorChanged; + artist_scroller.Add(artist_tree); + + album_tree.HeadersVisible = true; + album_tree.AppendColumn(Catalog.GetString("Album"), new CellRendererText(), "text", 0); + album_store = CreateStore(); + album_tree.Model = album_store; + album_tree.CursorChanged += OnAlbumCursorChanged; + album_scroller.Add(album_tree); + + main_box.PackStart(artist_scroller, true, true, 0); + main_box.PackStart(album_scroller, true, true, 0); + + PackStart(main_box, true, true, 0); + Spacing = 5; + + } + + private void Init() + { + query = new Select("Tracks", new Statement("DISTINCT Artist")) + new OrderBy("Artist", OrderDirection.Asc); + PopulateArtistStore(false); + PopulateAlbumStore(false); + + artist_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + album_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + + this.ShowAll(); + } + + public void Reset() + { + query = new Select("Tracks", new Statement("DISTINCT Artist")) + new OrderBy("Artist", OrderDirection.Asc) ; + artist_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + artist_tree.ScrollToPoint(0,0); + album_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + album_tree.ScrollToPoint(0,0); + } + + public void Update(bool isFiltered) + { + query = new Select("Tracks", new Statement("DISTINCT Artist")) + new OrderBy("Artist", OrderDirection.Asc); + PopulateArtistStore(true); + PopulateAlbumStore(true); + if(isFiltered) { + RunQuery(); + } + } + + public bool FilterStatus() + { + if(artist_selection_index == 0 && album_selection_index == 0) { + return false; + } else { + return true; + } + } + + private ListStore CreateStore() + { + ListStore store = new ListStore(typeof(string)); + + //We dont need a sort for this view but in case anyone asks... + /* + store.SetSortFunc(1, delegate(TreeModel model, TreeIter a, TreeIter b) { + try { + String a_string = (String)store.GetValue(a, 1); + String b_string = (String)store.GetValue(b, 1); + return String.Compare(a_string, b_string); + } catch(Exception) { + return 0; + } + }); + store.SetSortColumnId(1, SortType.Descending); + */ + return store; + } + + private void PopulateArtistStore(bool isUpdate) + { + artist_store.Clear(); + ArrayList artistlist = new ArrayList(); + try + { + IDataReader reader = Core.Library.Db.Query(query); + while(reader.Read()) + { + artistlist.Add((reader[0] as string)); + } + } catch(Exception) { + + } + + AddEntry("All", true, artist_store); + foreach(string entry in artistlist) { + AddEntry(entry, false, artist_store); + } + if (isUpdate) { + UpdateArtistList(); + } + else { + ResetArtistList(); + } + + } + + private void PopulateAlbumStore(bool isUpdate) + { + album_store.Clear(); + ArrayList albumlist = new ArrayList(); + if (artist_selection_index == 0) + { + query = new Select("Tracks", new Statement("DISTINCT AlbumTitle, Artist")) + + new OrderBy("AlbumTitle", OrderDirection.Asc); + } + else + { + query = new Select("Tracks", new Statement("DISTINCT AlbumTitle, Artist")) + + new Where(new Compare("Artist", Op.EqualTo, artist_selected)) + + new OrderBy("AlbumTitle", OrderDirection.Asc); + } + + try + { + IDataReader reader = Core.Library.Db.Query(query); + while(reader.Read()) + { + albumlist.Add((reader[0] as string)); + } + + } catch(Exception) { + + } + + AddEntry("All", true, album_store); + foreach(string entry in albumlist) { + AddEntry(entry, false, album_store); + } + if (isUpdate) { + UpdateAlbumList(); + } + else { + ResetAlbumList(); + } + + } + + private void UpdateAlbumList() + { + int [] UpdateSelectPath = {album_selection_index }; + album_tree.Selection.SelectPath(new TreePath(UpdateSelectPath)); + } + + private void UpdateArtistList() + { + int [] UpdateSelectPath = {artist_selection_index }; + artist_tree.Selection.SelectPath(new TreePath(UpdateSelectPath)); + } + + private void ResetAlbumList() + { + album_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + TreePath[] treeIndex = album_tree.Selection.GetSelectedRows(); + album_selection_index = treeIndex[0].Indices[0]; + } + + private void ResetArtistList() + { + artist_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + TreePath[] treeIndex = artist_tree.Selection.GetSelectedRows(); + artist_selection_index = treeIndex[0].Indices[0]; + } + + private void AddEntry(string entry, bool prepend,ListStore store) + { + TreeIter iter = prepend ? store.Insert(0) : store.Append(); + store.SetValue(iter, 0, entry); + } + + private void OnArtistCursorChanged(object o, EventArgs args) + { + TreeIter iter; + if(!artist_tree.Selection.GetSelected(out iter)) { + return; + } + + object artist_name = artist_store.GetValue(iter, 0); + artist_selected = (artist_name as string); + + TreePath[] treeIndex = artist_tree.Selection.GetSelectedRows(); + artist_selection_index = treeIndex[0].Indices[0]; + + if(artist_name == null) { + return; + } + + PopulateAlbumStore(false); + RunQuery(); + } + + private void OnAlbumCursorChanged(object o, EventArgs args) + { + TreeIter iter; + if(!album_tree.Selection.GetSelected(out iter)) { + return; + } + + object album_name = album_store.GetValue(iter, 0); + album_selected = (album_name as string); + + TreePath[] treeIndex = album_tree.Selection.GetSelectedRows(); + album_selection_index = treeIndex[0].Indices[0]; + + if(album_name == null) { + return; + } + + RunQuery(); + } + + private void RunQuery() + { + query = new Select("Tracks"); + if (artist_selection_index != 0) + { + query += new Where( + + new Compare("Artist", Op.EqualTo, artist_selected)); + } + + if (album_selection_index != 0) + { + query += new Where( + + new Compare("AlbumTitle", Op.EqualTo, album_selected)); + } + + if (artist_selection_index != 0 && album_selection_index != 0) + { + query = new Select("Tracks") + new Where( new Compare("AlbumTitle", Op.EqualTo, album_selected) + + new And() + new Compare("Artist", Op.EqualTo, artist_selected)); + } + + library_query = query.ToString(); + + EventHandler handler = BrowserClicked; + if(handler != null) + handler(this, new EventArgs()); + } + + } +} --- src/BrowserNoteBook.cs 2005-12-07 21:16:28.000000000 +0200 +++ src/BrowserNoteBook.cs 2005-12-07 20:18:02.000000000 +0200 @@ -0,0 +1,94 @@ +// created on 11/19/2005 at 1:26 AM by Aydemir Ulas Sahin + +using System; +using GLib; +using Gtk; +using Mono.Unix; +using Sql; + +namespace Banshee +{ + public delegate void QueryTransactionHandler(object o, QueryTransactionEventArgs args); + + public class QueryTransactionEventArgs : EventArgs + { + public Statement query; + } + + public class BrowserNoteBook : Notebook + { + private SqlBuilderUI query_builder; + private ArtistAlbumBrowser browser; + private VBox queryHolder = new VBox(); + + public event QueryTransactionHandler QueryTransactionComplete; + + public BrowserNoteBook() : base() + { + BuildUI(); + } + + private void BuildUI() + { + browser = new ArtistAlbumBrowser(); + browser.HeightRequest = 150; + browser.BrowserClicked += OnBrowserClicked; + query_builder = new SqlBuilderUI(queryHolder); + query_builder.SearchButtonClicked += OnSearchButtonClicked; + + AppendPage(browser, new Gtk.Label(Catalog.GetString("Browser"))); + AppendPage(queryHolder, new Gtk.Label(Catalog.GetString("Query"))); + + ShowAll(); + } + + private void OnSearchButtonClicked(object o, EventArgs args) + { + string query = query_builder.Query; + Statement query_statement = new Statement(query); + QueryLibrary(query_statement); + } + + private void OnBrowserClicked(object o, EventArgs args) + { + string query = browser.Query; + Statement query_statement = new Statement(query); + QueryLibrary(query_statement); + } + + public void QueryLibrary(Statement query) + { + QueryTransactionHandler handler = QueryTransactionComplete; + QueryTransactionEventArgs query_args = new QueryTransactionEventArgs(); + query_args.query = query; + if(handler != null) + handler(this, query_args); + + } + + public void BrowserReset() + { + browser.Reset(); + } + + public void BrowserUpdate() + { + browser.Update(false); + } + + public void BrowserFilter() + { + browser.Update(true); + } + + public bool BrowserHasFilter() + { + return browser.FilterStatus(); + } + + public void Active(bool status) + { + this.Sensitive = status; + } + } +}