[banshee] [MeeGo] cleanup, refactoring, polish
- From: Aaron Bockover <abock src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [banshee] [MeeGo] cleanup, refactoring, polish
- Date: Mon, 29 Mar 2010 17:22:59 +0000 (UTC)
commit a2e20a15b53047d5a1a374b627e2bf632f3892b0
Author: Aaron Bockover <abockover novell com>
Date: Mon Mar 29 13:19:44 2010 -0400
[MeeGo] cleanup, refactoring, polish
Split the view code out into a proper ITrackModelSourceContents,
fix some cosmetic bugs, try to create the toolbar panel connection
earlier on.
Also added an artist filter for now and limit the source selection
to only the music library and its children and the play queue since
other track model sources don't have filters, are confusing, and
don't work well without full UI support (e.g. toolbar).
src/Extensions/Banshee.MeeGo/Banshee.MeeGo.csproj | 1 +
.../Banshee.MeeGo/MediaPanelContents.cs | 96 +++-------
.../Banshee.MeeGo/Banshee.MeeGo/MeeGoPanel.cs | 88 +++++----
.../Banshee.MeeGo/Banshee.MeeGo/MeeGoService.cs | 46 +++--
.../Banshee.MeeGo/MeeGoSourceContents.cs | 200 ++++++++++++++++++++
src/Extensions/Banshee.MeeGo/Makefile.am | 1 +
6 files changed, 305 insertions(+), 127 deletions(-)
---
diff --git a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo.csproj b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo.csproj
index 9fb4413..f6c29e1 100644
--- a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo.csproj
+++ b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo.csproj
@@ -91,5 +91,6 @@
<Compile Include="Banshee.MeeGo\PlaybackBox.cs" />
<Compile Include="Banshee.MeeGo\MeeGoTrackInfoDisplay.cs" />
<Compile Include="Banshee.MeeGo\MeeGoPanel.cs" />
+ <Compile Include="Banshee.MeeGo\MeeGoSourceContents.cs" />
</ItemGroup>
</Project>
diff --git a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MediaPanelContents.cs b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MediaPanelContents.cs
index 1822900..ba06bd4 100644
--- a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MediaPanelContents.cs
+++ b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MediaPanelContents.cs
@@ -4,7 +4,7 @@
// Author:
// Aaron Bockover <abockover novell com>
//
-// Copyright 2009 Novell, Inc.
+// Copyright 2009-2010 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -43,85 +43,51 @@ using Banshee.Collection.Database;
namespace Banshee.MeeGo
{
- public class MediaPanelContents : Table
+ public class MediaPanelContents : VBox
{
private SourceComboBox source_combo_box;
private HBox header_box;
private SearchEntry search_entry;
- private AlbumListView album_view;
- private TerseTrackListView track_view;
+ private MeeGoSourceContents source_contents;
- public MediaPanelContents () : base (1, 2, false)
+ public MediaPanelContents ()
{
BorderWidth = 12;
- RowSpacing = 15;
- ColumnSpacing = 15;
- }
+ Spacing = 12;
- public void BuildViews ()
- {
header_box = new HBox () {
Spacing = 5,
BorderWidth = 5
};
+
header_box.PackStart (source_combo_box = new SourceComboBox (), false, false, 0);
+
var button = new Button (new Image () {
IconSize = (int)IconSize.LargeToolbar,
IconName = "media-player-banshee"
}) {
TooltipText = Catalog.GetString ("Launch the Banshee Media Player")
};
+
button.Clicked += (o, e) => {
ServiceManager.SourceManager.SetActiveSource (ServiceManager.SourceManager.MusicLibrary);
ServiceManager.Get<MeeGoService> ().PresentPrimaryInterface ();
};
- header_box.PackStart (button, false, false, 0);
- header_box.PackStart (search_entry = new SearchEntry () { Ready = true }, true, true, 0);
- Attach (header_box, 0, 1, 0, 1,
- AttachOptions.Fill | AttachOptions.Expand,
- AttachOptions.Shrink,
- 0, 0);
-
- var scroll = new ScrolledWindow () {
- VscrollbarPolicy = PolicyType.Always,
- HscrollbarPolicy = PolicyType.Never,
- ShadowType = ShadowType.None
- };
- scroll.Add (album_view = new AlbumListView ());
- Attach (scroll, 0, 1, 1, 2,
- AttachOptions.Fill | AttachOptions.Expand,
- AttachOptions.Fill | AttachOptions.Expand,
- 0, 0);
-
- var side_box = new VBox () { Spacing = 15 };
-
- side_box.PackStart (new PlaybackBox (), false, false, 0);
-
- scroll = new ScrolledWindow () {
- VscrollbarPolicy = PolicyType.Always,
- HscrollbarPolicy = PolicyType.Never,
- ShadowType = ShadowType.None
- };
- track_view = new TerseTrackListView () {
- IsReorderable = true
- };
- track_view.ColumnController.Insert (new Column (null, "indicator",
- new ColumnCellStatusIndicator (null), 0.05, true, 20, 20), 0);
- track_view.ColumnController.Add (new Column ("Rating", new ColumnCellRating ("Rating", false), 0.15));
- scroll.Add (track_view);
- side_box.PackStart (scroll, true, true, 0);
+ header_box.PackStart (button, false, false, 0);
+ header_box.PackStart (search_entry = new SearchEntry (), true, true, 0);
+ header_box.PackStart (new PlaybackBox (), false, false, 0);
- side_box.PackStart (new MeeGoTrackInfoDisplay () { HeightRequest = 64 }, false, false, 0);
- Attach (side_box, 1, 2, 0, 2,
- AttachOptions.Shrink,
- AttachOptions.Fill | AttachOptions.Expand,
- 0, 0);
+ PackStart (header_box, false, false, 0);
+ PackStart (source_contents = new MeeGoSourceContents (), true, true, 0);
ShowAll ();
source_combo_box.UpdateActiveSource ();
- source_combo_box.Model.Filter = (source) => source is ITrackModelSource;
+ source_combo_box.Model.Filter = (source) =>
+ source == ServiceManager.SourceManager.MusicLibrary ||
+ source.Parent == ServiceManager.SourceManager.MusicLibrary ||
+ source.GetType ().FullName == "Banshee.PlayQueue.PlayQueueSource";
search_entry.Changed += OnSearchEntryChanged;
ServiceManager.SourceManager.ActiveSourceChanged += OnActiveSourceChanged;
}
@@ -141,30 +107,20 @@ namespace Banshee.MeeGo
{
ThreadAssist.ProxyToMain (delegate {
var source = ServiceManager.SourceManager.ActiveSource;
- var track_source = source as ITrackModelSource;
- var filter_source = source as IFilterableSource;
-
- if (track_source == null) {
- return;
- }
- track_view.SetModel (track_source.TrackModel);
+ search_entry.Ready = false;
+ search_entry.CancelSearch ();
+ search_entry.SearchSensitive = source != null && source.CanSearch;
- if (filter_source == null) {
- album_view.Parent.Hide ();
- return;
+ if (source != null && source.FilterQuery != null) {
+ search_entry.Query = source.FilterQuery;
+ search_entry.ActivateFilter ((int)source.FilterType);
}
- foreach (var filter in filter_source.CurrentFilters) {
- var album_filter = filter as DatabaseAlbumListModel;
- if (album_filter != null) {
- album_view.Parent.Show ();
- album_view.SetModel (album_filter);
- return;
- }
- }
+ source_contents.ResetSource ();
+ source_contents.SetSource (source);
- album_view.Parent.Hide ();
+ search_entry.Ready = true;
});
}
diff --git a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoPanel.cs b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoPanel.cs
index 44bab20..7e40b68 100644
--- a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoPanel.cs
+++ b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoPanel.cs
@@ -4,7 +4,7 @@
// Author:
// Aaron Bockover <abockover novell com>
//
-// Copyright 2009 Novell, Inc.
+// Copyright 2009-2010 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -36,55 +36,71 @@ namespace Banshee.MeeGo
{
public class MeeGoPanel : IDisposable
{
- public static MeeGoPanel Instance { get; private set; }
+ private bool waiting_for_embedded;
+ private PanelGtk embedded_panel;
+ private Window window_panel;
- public PanelGtk ToolbarPanel { get; private set; }
- public uint ToolbarPanelWidth { get; private set; }
- public uint ToolbarPanelHeight { get; private set; }
public MediaPanelContents Contents { get; private set; }
- public bool BansheeIsInitialized { get; set; }
public MeeGoPanel ()
{
- if (Instance != null) {
- throw new ApplicationException ("Only one MeeGoPanel instance can exist");
- }
+ var timer = Log.DebugTimerStart ();
- if (ApplicationContext.CommandLine.Contains ("mutter-panel")) {
- BuildPanel ();
- Instance = this;
+ try {
+ waiting_for_embedded = true;
+ embedded_panel = new PanelGtk ("banshee", "media", null, "media-button", true);
+ embedded_panel.ReadyEvent += (o, e) => {
+ lock (this) {
+ waiting_for_embedded = false;
+ BuildContents ();
+ }
+ };
+ } catch (Exception e) {
+ if (!(e is DllNotFoundException)) {
+ Log.Exception ("Could not bind to MeeGo panel", e);
+ }
+ waiting_for_embedded = false;
+ window_panel = new Gtk.Window ("MeeGo Media Panel");
}
+
+ Log.DebugTimerPrint (timer, "MeeGo panel created: {0}");
}
public void Dispose ()
{
}
- private void BuildPanel ()
+ public void BuildContents ()
{
- try {
- ToolbarPanel = new PanelGtk ("banshee", "media", null, "media-button", true);
- ToolbarPanel.ReadyEvent += (o, e) => {
- lock (this) {
- Contents = new MediaPanelContents ();
- Contents.ShowAll ();
- ToolbarPanel.SetChild (Contents);
- if (BansheeIsInitialized) {
- Contents.BuildViews ();
- }
- }
- };
- } catch (Exception e) {
- Log.Exception ("Could not bind to MeeGo panel", e);
- var window = new Gtk.Window ("MeeGo Media Panel");
- window.SetDefaultSize (1000, 500);
- window.WindowPosition = Gtk.WindowPosition.Center;
- window.Add (Contents = new MediaPanelContents ());
- window.ShowAll ();
- GLib.Timeout.Add (1000, () => {
- window.Present ();
- return false;
- });
+ lock (this) {
+ if (waiting_for_embedded) {
+ return;
+ }
+
+ var timer = Log.DebugTimerStart ();
+ Contents = new MediaPanelContents ();
+ Contents.ShowAll ();
+ Log.DebugTimerPrint (timer, "MeeGo panel contents created: {0}");
+
+ if (embedded_panel != null) {
+ embedded_panel.SetChild (Contents);
+ } else if (window_panel != null) {
+ window_panel.Add (Contents);
+ window_panel.SetDefaultSize (1000, 500);
+ window_panel.WindowPosition = WindowPosition.Center;
+ window_panel.Show ();
+ GLib.Timeout.Add (1000, () => {
+ window_panel.Present ();
+ return false;
+ });
+ }
+ }
+ }
+
+ public void Hide ()
+ {
+ if (embedded_panel != null) {
+ embedded_panel.Hide ();
}
}
}
diff --git a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoService.cs b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoService.cs
index cc47c54..a281fb6 100644
--- a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoService.cs
+++ b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoService.cs
@@ -4,7 +4,7 @@
// Authors:
// Aaron Bockover <abockover novell com>
//
-// Copyright 2009 Novell, Inc.
+// Copyright 2009-2010 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -47,14 +47,19 @@ namespace Banshee.MeeGo
private InterfaceActionService interface_action_service;
private SourceManager source_manager;
private PlayerEngineService player;
- private Banshee.Sources.Source now_playing;
+ private Source now_playing;
- public MeeGoService ()
- {
- }
+ private MeeGoPanel panel;
void IExtensionService.Initialize ()
{
+ // We need to create the MeeGo panel connection as soon as possible
+ // to keep mutter-moblin's toolbar from thinking we crashed (timing out).
+ // The contents of the panel will be constructed later on.
+ if (ApplicationContext.CommandLine.Contains ("mutter-panel")) {
+ panel = new MeeGoPanel ();
+ }
+
elements_service = ServiceManager.Get<GtkElementsService> ();
interface_action_service = ServiceManager.Get<InterfaceActionService> ();
source_manager = ServiceManager.SourceManager;
@@ -82,7 +87,8 @@ namespace Banshee.MeeGo
private bool ServiceStartup ()
{
- if (elements_service == null || interface_action_service == null || source_manager == null || player == null) {
+ if (elements_service == null || interface_action_service == null ||
+ source_manager == null || player == null) {
return false;
}
@@ -98,7 +104,7 @@ namespace Banshee.MeeGo
// regular metacity does not seem to like this at all, crashing
// and complaining "Window manager warning: Buggy client sent a
// _NET_ACTIVE_WINDOW message with a timestamp of 0 for 0x2e00020"
- if (MeeGoPanel.Instance != null) {
+ if (panel != null) {
elements_service.PrimaryWindow.Decorated = false;
elements_service.PrimaryWindow.Maximize ();
}
@@ -106,16 +112,11 @@ namespace Banshee.MeeGo
// Set the internal engine volume to 100%
ServiceManager.PlayerEngine.Volume = 100;
- if (MeeGoPanel.Instance == null) {
+ if (panel == null) {
return;
}
- lock (MeeGoPanel.Instance) {
- MeeGoPanel.Instance.BansheeIsInitialized = true;
- if (MeeGoPanel.Instance.Contents != null) {
- MeeGoPanel.Instance.Contents.BuildViews ();
- }
- }
+ panel.BuildContents ();
elements_service.PrimaryWindowClose = () => {
elements_service.PrimaryWindow.Hide ();
@@ -129,13 +130,15 @@ namespace Banshee.MeeGo
};
FindNowPlaying ();
- ServiceManager.PlayerEngine.ConnectEvent (OnPlayerStateChanged, PlayerEvent.StateChange | PlayerEvent.StartOfStream);
+ ServiceManager.PlayerEngine.ConnectEvent (OnPlayerStateChanged,
+ PlayerEvent.StateChange | PlayerEvent.StartOfStream);
}
private void OnPlayerStateChanged (PlayerEventArgs args)
{
var player = ServiceManager.PlayerEngine;
- if (player.CurrentState == PlayerState.Playing && player.CurrentTrack.HasAttribute (TrackMediaAttributes.VideoStream)) {
+ if (player.CurrentState == PlayerState.Playing &&
+ player.CurrentTrack.HasAttribute (TrackMediaAttributes.VideoStream)) {
if (now_playing != null) {
ServiceManager.SourceManager.SetActiveSource (now_playing);
}
@@ -153,8 +156,9 @@ namespace Banshee.MeeGo
}
}
- if (now_playing != null)
+ if (now_playing != null) {
return;
+ }
Banshee.ServiceStack.ServiceManager.SourceManager.SourceAdded += (args) => {
if (now_playing == null && args.Source.UniqueId.Contains ("now-playing")) {
@@ -167,15 +171,15 @@ namespace Banshee.MeeGo
{
elements_service.PrimaryWindow.Maximize ();
elements_service.PrimaryWindow.Present ();
- if (MeeGoPanel.Instance != null && MeeGoPanel.Instance.ToolbarPanel != null) {
- MeeGoPanel.Instance.ToolbarPanel.Hide ();
+ if (panel != null) {
+ panel.Hide ();
}
}
public void Dispose ()
{
- if (MeeGoPanel.Instance != null) {
- MeeGoPanel.Instance.Dispose ();
+ if (panel != null) {
+ panel.Dispose ();
}
interface_action_service = null;
diff --git a/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoSourceContents.cs b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoSourceContents.cs
new file mode 100644
index 0000000..bb67d99
--- /dev/null
+++ b/src/Extensions/Banshee.MeeGo/Banshee.MeeGo/MeeGoSourceContents.cs
@@ -0,0 +1,200 @@
+//
+// MeeGoSourceContents.cs
+//
+// Author:
+// Aaron Bockover <abockover novell com>
+//
+// Copyright 2010 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using Gtk;
+
+using Hyena.Data;
+using Hyena.Data.Gui;
+
+using Banshee.Sources;
+using Banshee.Sources.Gui;
+using Banshee.Collection;
+using Banshee.Collection.Gui;
+
+namespace Banshee.MeeGo
+{
+ public class MeeGoSourceContents : HBox, ITrackModelSourceContents
+ {
+ private ArtistListView artist_view;
+ private AlbumListView album_view;
+ private TerseTrackListView track_view;
+
+ private ISource source;
+ private Dictionary<object, double> model_positions = new Dictionary<object, double> ();
+
+ public MeeGoSourceContents ()
+ {
+ Spacing = 5;
+
+ var side_box = new VBox () {
+ Spacing = 5
+ };
+
+ PackStart (SetupView (artist_view = new ArtistListView ()), false, false, 0);
+ PackStart (SetupView (album_view = new AlbumListView ()), true, true, 0);
+ PackStart (side_box, false, false, 0);
+ side_box.PackStart (SetupView (track_view = new TerseTrackListView ()), true, true, 0);
+ track_view.ColumnController.Insert (new Column (null, "indicator",
+ new ColumnCellStatusIndicator (null), 0.05, true, 20, 20), 0);
+ side_box.PackStart (new MeeGoTrackInfoDisplay () { HeightRequest = 64 }, false, false, 0);
+
+ artist_view.WidthRequest = 150;
+ track_view.WidthRequest = 240;
+ artist_view.DoNotRenderNullModel = true;
+ album_view.DoNotRenderNullModel = true;
+
+ artist_view.SelectionProxy.Changed += OnBrowserViewSelectionChanged;
+ album_view.SelectionProxy.Changed += OnBrowserViewSelectionChanged;
+ }
+
+ private ScrolledWindow SetupView (Widget view)
+ {
+ var scrolled = new ScrolledWindow () {
+ VscrollbarPolicy = PolicyType.Automatic,
+ HscrollbarPolicy = PolicyType.Never,
+ ShadowType = ShadowType.None
+ };
+ scrolled.Add (view);
+ return scrolled;
+ }
+
+ private void OnBrowserViewSelectionChanged (object o, EventArgs args)
+ {
+ // Scroll the raising filter view to the top if "all" is selected
+ var selection = (Hyena.Collections.Selection)o;
+ if (!selection.AllSelected) {
+ return;
+ }
+
+ if (artist_view.Selection == selection) {
+ artist_view.ScrollTo (0);
+ } else if (album_view.Selection == selection) {
+ album_view.ScrollTo (0);
+ }
+ }
+
+#region View<->Model binding
+
+ private void SetModel<T> (IListModel<T> model)
+ {
+ ListView<T> view = FindListView <T> ();
+ if (view != null) {
+ SetModel (view, model);
+ } else {
+ Hyena.Log.DebugFormat ("Unable to find view for model {0}", model);
+ }
+ }
+
+ private void SetModel<T> (ListView<T> view, IListModel<T> model)
+ {
+ if (view.Model != null) {
+ model_positions[view.Model] = view.Vadjustment.Value;
+ }
+
+ if (model == null) {
+ view.SetModel (null);
+ return;
+ }
+
+ if (!model_positions.ContainsKey (model)) {
+ model_positions[model] = 0.0;
+ }
+
+ view.SetModel (model, model_positions[model]);
+ }
+
+ private ListView<T> FindListView<T> ()
+ {
+ foreach (var view in new IListView [] { artist_view, album_view, track_view }) {
+ if (view is ListView<T>) {
+ return (ListView<T>)view;
+ }
+ }
+ return null;
+ }
+
+#endregion
+
+#region ISourceContents
+
+ public bool SetSource (ISource source)
+ {
+ var track_source = source as ITrackModelSource;
+ var filterable_source = source as IFilterableSource;
+ if (track_source == null) {
+ return false;
+ }
+
+ this.source = source;
+
+ SetModel (track_view, track_source.TrackModel);
+
+ if (filterable_source != null && filterable_source.CurrentFilters != null) {
+ foreach (var model in filterable_source.CurrentFilters) {
+ if (model is IListModel<ArtistInfo>) {
+ SetModel (artist_view, (model as IListModel<ArtistInfo>));
+ } else if (model is IListModel<AlbumInfo>) {
+ SetModel (album_view, (model as IListModel<AlbumInfo>));
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void ResetSource ()
+ {
+ source = null;
+ SetModel (track_view, null);
+ SetModel (artist_view, null);
+ SetModel (album_view, null);
+ track_view.HeaderVisible = false;
+ }
+
+ public ISource Source {
+ get { return source; }
+ }
+
+ public Widget Widget {
+ get { return this; }
+ }
+
+#endregion
+
+#region ITrackModelSourceContents
+
+ public IListView<TrackInfo> TrackView {
+ get { return track_view; }
+ }
+
+#endregion
+
+ }
+}
+
diff --git a/src/Extensions/Banshee.MeeGo/Makefile.am b/src/Extensions/Banshee.MeeGo/Makefile.am
index ba7b0bd..6792c4b 100644
--- a/src/Extensions/Banshee.MeeGo/Makefile.am
+++ b/src/Extensions/Banshee.MeeGo/Makefile.am
@@ -9,6 +9,7 @@ SOURCES = \
Banshee.MeeGo/MediaPanelContents.cs \
Banshee.MeeGo/MeeGoPanel.cs \
Banshee.MeeGo/MeeGoService.cs \
+ Banshee.MeeGo/MeeGoSourceContents.cs \
Banshee.MeeGo/MeeGoTrackInfoDisplay.cs \
Banshee.MeeGo/PlaybackBox.cs \
Banshee.MeeGo/SearchEntry.cs \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]