[ease/plugins] Added the "Import Media" dialog.



commit 04488dfb4486cc9444cf7c6ab244593dc6b84b53
Author: Nate Stedman <natesm gmail com>
Date:   Sun Aug 22 08:39:57 2010 -0400

    Added the "Import Media" dialog.

 data/ui/editor-window.ui                  |   13 ++
 data/ui/import-widget.ui                  |  178 +++++++++++++++++++++++++++++
 ease-core/ease-import-widget.vala         |  116 +++++++++++++------
 ease-core/ease-plugin-import-service.vala |   81 +++++++-------
 ease-core/sourcelist/source-list.vala     |    5 +-
 ease/Makefile.am                          |    1 +
 ease/ease-editor-window.vala              |    7 +
 ease/ease-import-dialog.vala              |   45 +++++++
 ease/ease-import-oca-media.vala           |   27 ++++-
 ease/ease-import-oca-service.vala         |   21 +++-
 10 files changed, 412 insertions(+), 82 deletions(-)
---
diff --git a/data/ui/editor-window.ui b/data/ui/editor-window.ui
index 0fcfc95..fbb8471 100644
--- a/data/ui/editor-window.ui
+++ b/data/ui/editor-window.ui
@@ -247,6 +247,19 @@
                   </object>
                 </child>
                 <child>
+                  <object class="GtkSeparatorMenuItem" id="&lt;separator&gt;">
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkMenuItem" id="menuitem6">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Media from the _Web</property>
+                    <property name="use_underline">True</property>
+                    <signal name="activate" handler="ease_editor_window_on_insert_web_media"/>
+                  </object>
+                </child>
+                <child>
                   <object class="GtkSeparatorMenuItem" id="menuitem3">
                     <property name="visible">True</property>
                   </object>
diff --git a/data/ui/import-widget.ui b/data/ui/import-widget.ui
new file mode 100644
index 0000000..ff665d1
--- /dev/null
+++ b/data/ui/import-widget.ui
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkEventBox" id="root">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkAlignment" id="alignment1">
+        <property name="visible">True</property>
+        <property name="top_padding">4</property>
+        <property name="bottom_padding">4</property>
+        <property name="left_padding">4</property>
+        <property name="right_padding">4</property>
+        <child>
+          <object class="GtkVBox" id="vbox">
+            <property name="visible">True</property>
+            <property name="spacing">4</property>
+            <child>
+              <object class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <property name="spacing">4</property>
+                <child>
+                  <object class="GtkEntry" id="search">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x2022;</property>
+                    <property name="secondary_icon_stock">gtk-clear</property>
+                    <property name="secondary_icon_sensitive">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="search-button">
+                    <property name="label">gtk-find</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="use_stock">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="icon-window">
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkIconView" id="icon-view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="spin-align">
+                <property name="xscale">0</property>
+                <property name="yscale">0</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="no-results">
+                <property name="xscale">0</property>
+                <property name="yscale">0</property>
+                <child>
+                  <object class="GtkVBox" id="no-results-box">
+                    <property name="visible">True</property>
+                    <property name="spacing">10</property>
+                    <child>
+                      <object class="GtkImage" id="image2">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-dialog-warning</property>
+                        <property name="icon-size">6</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">No Results Found</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox2">
+                <property name="visible">True</property>
+                <property name="spacing">4</property>
+                <child>
+                  <object class="GtkProgressBar" id="progress-bar"/>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="close">
+                    <property name="label">gtk-close</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="use_stock">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="pack_type">end</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="insert">
+                    <property name="label" translatable="yes">_Insert</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="image">image1</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="pack_type">end</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">end</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="stock">gtk-add</property>
+  </object>
+</interface>
diff --git a/ease-core/ease-import-widget.vala b/ease-core/ease-import-widget.vala
index 806bf1b..573d270 100644
--- a/ease-core/ease-import-widget.vala
+++ b/ease-core/ease-import-widget.vala
@@ -1,14 +1,17 @@
 public class Ease.ImportWidget : Gtk.Alignment
 {
+	private const string UI_FILE_PATH = "import-widget.ui";
+	private const double DARK_FACTOR = 1.1;
+	
 	/**
 	 * Primary icon view for display of results.
 	 */
 	internal Gtk.IconView icons;
 	
 	/**
-	 * Scrolled window for icon view.
+	 * Container of the icons.
 	 */
-	internal Gtk.ScrolledWindow icons_scroll;
+	internal Gtk.Widget icons_container;
 	
 	/**
 	 * Search field.
@@ -26,67 +29,114 @@ public class Ease.ImportWidget : Gtk.Alignment
 	internal Gtk.ProgressBar progress;
 	
 	/**
-	 * Alignment placing progress bar at the bottom.
+	 * Spinner displayed while REST call is being made.
 	 */
-	internal Gtk.Alignment progress_align;
+	internal Gtk.Spinner spinner;
 	
 	/**
-	 * Spinner displayed while REST call is being made.
+	 * Container of the spinner.
 	 */
-	internal Gtk.Spinner spinner;
+	internal Gtk.Widget spinner_container;
 	
 	/**
-	 * Alignment containing the spinner.
+	 * Message displayed when no results are found
 	 */
-	internal Gtk.Alignment spinner_align;
+	internal Gtk.Widget no_results;
 	
 	/**
-	 * Main VBox for packing widgets.
+	 * The ImportService associated with this widget.
 	 */
-	internal Gtk.VBox main_vbox;
+	private Plugin.ImportService service;
 	
 	/**
 	 * Size of the spinner
 	 */
 	private const int SPINNER_SIZE = 40;
 
-	internal ImportWidget(Plugin.ImportService service)
+	public ImportWidget(Plugin.ImportService serv)
 	{
+		service = serv;
+		service.widget = this;
+		set_padding(0, 0, 0, 0);
+		
+		// darken the background
+		map_event.connect((self, event) => {
+			var color = style.bg[Gtk.StateType.NORMAL];
+			color.red = 0;
+			modify_bg(Gtk.StateType.NORMAL, color);
+			return false;
+		});
+		
+		// load the ui from GtkBuilder
+		var builder = new Gtk.Builder();
+		try
+		{
+			builder.add_from_file(data_path(Path.build_filename(Temp.UI_DIR,
+				                                                UI_FILE_PATH)));
+		}
+		catch (Error e) { error("Error loading UI: %s", e.message); }
+		
 		// search field
-		search = new Gtk.Entry();
-		search.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
-		                               "gtk-clear");
-		search.icon_press.connect (() => search.text = "");
+		search = builder.get_object("search") as Gtk.Entry;
+		search.icon_press.connect(() => search.text = "");
+		search.activate.connect(() => button.activate());
+		search.expose_event.connect(set_bg);
 		
 		// search button
-		button = new Gtk.Button.from_stock("gtk-find");
+		button = builder.get_object("search-button") as Gtk.Button;
 		button.clicked.connect(service.run);
+		button.expose_event.connect(set_bg);
 		
 		// progress
-		progress = new Gtk.ProgressBar();
-		progress_align = new Gtk.Alignment(0, 1, 1, 0);
-		progress_align.add(progress);
+		progress = builder.get_object("progress-bar") as Gtk.ProgressBar;
 		
 		// spinner
 		spinner = new Gtk.Spinner();
-		spinner_align = new Gtk.Alignment(0.5f, 0.5f, 0, 0);
-		spinner_align.add(spinner);
+		spinner.visible = true;
 		spinner.set_size_request(SPINNER_SIZE, SPINNER_SIZE);
+		spinner_container = builder.get_object("spin-align") as Gtk.Widget;
+		(spinner_container as Gtk.Bin).add(spinner);
 		
 		// icon view
-		icons = new Gtk.IconView();
-		icons_scroll = new Gtk.ScrolledWindow(null, null);
-		icons_scroll.add_with_viewport(icons);
-		icons_scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS);
+		icons = builder.get_object("icon-view") as Gtk.IconView;
+		icons_container = builder.get_object("icon-window") as Gtk.Widget;
+		
+		// no results
+		no_results = builder.get_object("no-results") as Gtk.Widget;
+		
+		// add
+		var root = builder.get_object("root") as Gtk.EventBox;
+		root.expose_event.connect(set_bg);
+		add(root);
+	}
+	
+	private bool set_bg(Gtk.Widget root, Gdk.EventExpose event)
+	{
+		// lighten or darken the background
+		var color = root.style.bg[Gtk.StateType.NORMAL];
+		
+		// darken if it would overflow
+		if (color.red * DARK_FACTOR > 65535)
+		{
+			color.red = (uint16)(color.red / DARK_FACTOR);
+			color.blue = (uint16)(color.blue / DARK_FACTOR);
+			color.green = (uint16)(color.green / DARK_FACTOR);
+		}
+		
+		// otherwise, lighten
+		else
+		{
+			color.red = (uint16)(color.red * DARK_FACTOR);
+			color.blue = (uint16)(color.blue * DARK_FACTOR);
+			color.green = (uint16)(color.green * DARK_FACTOR);
+		}
+		
+		// set the background color
+		root.modify_bg(Gtk.StateType.NORMAL, color);
 		
-		// pack search field and button
-		var hbox = new Gtk.HBox(false, 5);
-		hbox.pack_start(search, true, true, 0);
-		hbox.pack_start(button, false, false, 0);
+		// only do this once
+		root.expose_event.disconnect(set_bg);
 		
-		// pack top and bottom
-		main_vbox = new Gtk.VBox(false, 5);
-		main_vbox.pack_start(hbox, false, false, 0);
-		add(main_vbox);
+		return false;
 	}
 }
diff --git a/ease-core/ease-plugin-import-service.vala b/ease-core/ease-plugin-import-service.vala
index 1645c23..0ca8e91 100644
--- a/ease-core/ease-plugin-import-service.vala
+++ b/ease-core/ease-plugin-import-service.vala
@@ -45,7 +45,7 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 	/**
 	 * The widget for this service.
 	 */
-	private ImportWidget widget;
+	internal ImportWidget widget;
 	
 	/**
 	 * The size of the list to download.
@@ -97,16 +97,16 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 		proxy = create_proxy();
 		call = create_call(proxy, widget.search.text);
 		
-		// remove the icons, if needed
-		if (widget.icons_scroll.get_parent() == widget.main_vbox)
-		{
-			widget.main_vbox.remove(widget.icons_scroll);
-		}
+		// remove the results
+		widget.icons_container.visible = false;
+		widget.no_results.visible = false;
 		
 		// display the spinner
-		widget.main_vbox.pack_end(widget.spinner_align, true, true, 0);
 		widget.spinner.start();
-		widget.spinner_align.show_all();
+		widget.spinner_container.visible = true;
+		
+		// reset the progress bar
+		widget.progress.set_fraction(0);
 		
 		// run the call
 		try { call.run_async(on_call_finish, this); }
@@ -120,23 +120,11 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 	 */
 	private void on_call_finish(Rest.ProxyCall call)
 	{
-		// remove the widget.spinner
-		if (widget.spinner_align.get_parent() == widget.main_vbox)
-		{
-			widget.main_vbox.remove(widget.spinner_align);
-		}
+		// remove the spinner
+		widget.spinner_container.visible = false;
 		widget.spinner.stop();
 		
-		// add the icon view
-		widget.main_vbox.pack_start(widget.icons_scroll, true, true, 0);
-		widget.icons_scroll.show_all();
-		
-		// add the widget.progress
-		widget.main_vbox.pack_end(widget.progress_align, false, false, 0);
-		widget.progress_align.show_all();
-		
-		// create list and model
-		model = new Gtk.ListStore(2, typeof(Gdk.Pixbuf), typeof(string));
+		// create list
 		images_list = new Gee.LinkedList<ImportMedia?>();
 		
 		// parse the image data (done by subclasses)
@@ -145,20 +133,36 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 		// remember the list size for the widget.progress bar
 		list_size = images_list.size;
 		
-		// set icons
-		widget.icons.set_model(model);
-		widget.icons.text_column = Column.TEXT;
-		widget.icons.pixbuf_column = Column.PIXBUF;
-		
-		// if threads are supported, get the pixbufs in a thread
-		if (Thread.supported())
+		if (list_size > 0)
 		{
-			try { Thread.create(threaded_get_pixbufs, false); }
-			catch { threaded_get_pixbufs(); }
+			// add the icon view
+			widget.icons_container.visible = true;
+		
+			// add the progress
+			widget.progress.visible = true;
+			
+			// create model
+			model = new Gtk.ListStore(2, typeof(Gdk.Pixbuf), typeof(string));
+		
+			// set icons
+			widget.icons.set_model(model);
+			widget.icons.text_column = Column.TEXT;
+			widget.icons.pixbuf_column = Column.PIXBUF;
+		
+			// if threads are supported, get the pixbufs in a thread
+			if (Thread.supported())
+			{
+				try { Thread.create(threaded_get_pixbufs, false); }
+				catch { threaded_get_pixbufs(); }
+			}
+			else
+			{
+				threaded_get_pixbufs();
+			}
 		}
 		else
 		{
-			threaded_get_pixbufs();
+			widget.no_results.visible = true;
 		}
 	}
 	
@@ -207,10 +211,7 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 		// otherwise, remove the widget.progress bar and return
 		lock (widget)
 		{
-			if (widget.progress_align.get_parent() == widget.main_vbox)
-			{
-				widget.main_vbox.remove(widget.progress_align);
-			}
+			widget.progress.visible = false;
 		}
 		return null;
 	}
@@ -229,9 +230,9 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 		catch (Error e)
 		{
 			filestream = null;
-			error ("Couldn't read distant file : %s", e.message);
+			critical("Couldn't read distant file : %s", e.message);
 		}
-		assert (filestream != null);
+		assert(filestream != null);
 		Gdk.Pixbuf pix;
 		try
 		{
@@ -241,7 +242,7 @@ public abstract class Ease.Plugin.ImportService : GLib.Object
 		}
 		catch (Error e)
 		{
-			error ("Couldn't create pixbuf from file: %s", e.message);
+			critical("Couldn't create pixbuf from file: %s", e.message);
 			pix = null;
 		}
 		return pix;
diff --git a/ease-core/sourcelist/source-list.vala b/ease-core/sourcelist/source-list.vala
index 63d5b25..325bdf4 100644
--- a/ease-core/sourcelist/source-list.vala
+++ b/ease-core/sourcelist/source-list.vala
@@ -84,7 +84,7 @@ public class Source.List : Gtk.Alignment
 	 * @param linked_bin The Gtk.Bin to link this Source.View with.
 	 */
 	public List(Gtk.Bin linked_bin)
-	{
+	{	
 		// create widgets
 		scroll = new Gtk.ScrolledWindow(null, null);
 		box = new Gtk.VBox(false, GROUP_PADDING);
@@ -133,7 +133,8 @@ public class Source.List : Gtk.Alignment
 			
 				// add the new child
 				bin.add(sender.widget);
-				bin.show_all();
+				bin.show();
+				sender.widget.show();
 			}
 			
 			// select the sender
diff --git a/ease/Makefile.am b/ease/Makefile.am
index 8cfccd1..9818e1f 100644
--- a/ease/Makefile.am
+++ b/ease/Makefile.am
@@ -8,6 +8,7 @@ ease_SOURCES = \
 	ease-editor-embed.vala \
 	ease-editor-window.vala \
 	ease-handle.vala \
+	ease-import-dialog.vala \
 	ease-import-oca-media.vala \
 	ease-import-oca-service.vala \
 	ease-inspector-element-pane.vala \
diff --git a/ease/ease-editor-window.vala b/ease/ease-editor-window.vala
index 92206ce..c5db0b5 100644
--- a/ease/ease-editor-window.vala
+++ b/ease/ease-editor-window.vala
@@ -553,6 +553,13 @@ internal class Ease.EditorWindow : Gtk.Window
 	}
 	
 	[CCode (instance_pos = -1)]
+	internal void on_insert_web_media(Gtk.Widget sender)
+	{
+		var dialog = new ImportDialog();
+		dialog.run();
+	}
+	
+	[CCode (instance_pos = -1)]
 	internal void on_insert_rectangle(Gtk.Widget sender)
 	{
 		var rect = new ShapeElement(ShapeType.RECTANGLE);
diff --git a/ease/ease-import-dialog.vala b/ease/ease-import-dialog.vala
new file mode 100644
index 0000000..f13cc72
--- /dev/null
+++ b/ease/ease-import-dialog.vala
@@ -0,0 +1,45 @@
+/*  Ease, a GTK presentation application
+    Copyright (C) 2010 Nate Stedman
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+internal class Ease.ImportDialog : Gtk.Window
+{
+	internal ImportDialog()
+	{
+		title = _("Import Media");
+		set_default_size(640, 480);
+		
+		// create the source list
+		var view = new Source.View();
+		var group = new Source.Group(_("Images"));
+		view.add_group(group);
+		view.show_all();
+		
+		Plugin.ImportService service = new OCAService();
+		var item = new Source.Item.from_stock_icon(
+			"OpenClipArt", "gtk-go-down", new ImportWidget(service));
+		group.add_item(item);
+		
+		add(view);
+		view.show_all();
+		item.select();
+	}
+	
+	internal void run()
+	{
+		show();
+	}
+}
diff --git a/ease/ease-import-oca-media.vala b/ease/ease-import-oca-media.vala
index f7c1b87..d9d4838 100644
--- a/ease/ease-import-oca-media.vala
+++ b/ease/ease-import-oca-media.vala
@@ -1,7 +1,24 @@
-public class Ease.OCAMedia : Plugin.ImportMedia
+/*  Ease, a GTK presentation application
+    Copyright (C) 2010 Nate Stedman
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+internal class Ease.OCAMedia : Plugin.ImportMedia
 {
-	public string link;
-	public string creator;
-	public string description;
-	public string license;
+	internal string link;
+	internal string creator;
+	internal string description;
+	internal string license;
 }
diff --git a/ease/ease-import-oca-service.vala b/ease/ease-import-oca-service.vala
index e595b35..d47c40a 100644
--- a/ease/ease-import-oca-service.vala
+++ b/ease/ease-import-oca-service.vala
@@ -1,4 +1,21 @@
-public class Ease.OCAService : Plugin.ImportService
+/*  Ease, a GTK presentation application
+    Copyright (C) 2010 Nate Stedman
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+internal class Ease.OCAService : Plugin.ImportService
 {	
 	private const string REST_URL =
 		"http://www.openclipart.org/media/feed/rss/";;
@@ -16,7 +33,7 @@ public class Ease.OCAService : Plugin.ImportService
 		return call;
 	}
 	
-	public override void parse_data(string data)
+	internal override void parse_data(string data)
 	{	
 		Xml.Parser.init();
 		



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]