gzz-commits
[Top][All Lists]
Advanced

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

[Gzz-commits] fenfire ./TODO-loom org/fenfire/loom/Cursor.jav...


From: Benja Fallenstein
Subject: [Gzz-commits] fenfire ./TODO-loom org/fenfire/loom/Cursor.jav...
Date: Sat, 15 Mar 2003 16:58:00 -0500

CVSROOT:        /cvsroot/fenfire
Module name:    fenfire
Changes by:     Benja Fallenstein <address@hidden>      03/03/15 16:57:59

Modified files:
        .              : TODO-loom 
        org/fenfire/loom: Cursor.java Cursor.test Loom.java 
                          ModelUtil.java ModelUtil.test RDFVocab.java 
                          SimpleView.test WheelView.test 
Added files:
        org/fenfire/loom: PropertiesBox.java PropertiesBox.test 
                          PropertySetSelector.java 
                          PropertySetSelector.test ViewSettings.java 
                          ViewSettings.test 

Log message:
        Refactor

CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/TODO-loom.diff?tr1=1.44&tr2=1.45&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/PropertiesBox.java?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/PropertiesBox.test?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/PropertySetSelector.java?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/PropertySetSelector.test?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/ViewSettings.java?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/ViewSettings.test?rev=1.1
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/Cursor.java.diff?tr1=1.9&tr2=1.10&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/Cursor.test.diff?tr1=1.8&tr2=1.9&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/Loom.java.diff?tr1=1.36&tr2=1.37&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/ModelUtil.java.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/ModelUtil.test.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/RDFVocab.java.diff?tr1=1.2&tr2=1.3&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/SimpleView.test.diff?tr1=1.9&tr2=1.10&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/fenfire/fenfire/org/fenfire/loom/WheelView.test.diff?tr1=1.14&tr2=1.15&r1=text&r2=text

Patches:
Index: fenfire/TODO-loom
diff -u fenfire/TODO-loom:1.44 fenfire/TODO-loom:1.45
--- fenfire/TODO-loom:1.44      Thu Mar 13 16:18:32 2003
+++ fenfire/TODO-loom   Sat Mar 15 16:57:59 2003
@@ -6,30 +6,31 @@
 
 
 soon:
-    - factor almost everything from Loom.java 
-      out into tested code
     - doc test system
 
+    - clip instead of truncate text
+
 
 0.1: Useful RDF browser
     - Beta release
     - Full release
 
-later (or now if somebody wants to):
-
-    - clip instead of truncate text
+0.2: Useful RDF editor
+    - editing structure and text content
+    - factor almost everything from Loom.java 
+      out into tested code
     - Copy URI / copy literal text to clipboard
+    - saving
+
     - fix bug: when there are two equal literals connected
       to a node, clicking on one may focus the other.
-
     - fix bug: when Cursor.java has an insane rotation
       (too high or too small), it should fix itself
       when move() is called.
 
-    - draw UML diagrams similar to Gzz "Core APIs"
 
-    - editing structure and text content
-    - saving
+later, or now if somebody wants to:
+    - draw UML diagrams similar to Gzz "Core APIs"
 
     - move into own Savannah project
 
@@ -40,12 +41,11 @@
 
     - load RDF from the Web ('semantic web browsing')
 
-    - views proportional to window size?
-
-    + WebStart deployment of Loom?
+    + views proportional to window size?
     + in Wheel view, have a minimum angle, so that nodes
       aren't shown arbitrarily close. Figure out how:
       we cannot always show all nodes any more, then.
+    + WebStart deployment of Loom?
 
 humppake:
     - Optional node view showing Resources as green ovals
Index: fenfire/org/fenfire/loom/Cursor.java
diff -u fenfire/org/fenfire/loom/Cursor.java:1.9 
fenfire/org/fenfire/loom/Cursor.java:1.10
--- fenfire/org/fenfire/loom/Cursor.java:1.9    Tue Mar 11 17:52:22 2003
+++ fenfire/org/fenfire/loom/Cursor.java        Sat Mar 15 16:57:59 2003
@@ -35,6 +35,10 @@
  */
 public class Cursor {
 
+    /** The namespace prefix mappings used to render URIs.
+     */
+    public NamespaceMap names;
+
     /** The focused node, usually shown in the middle of the screen. 
      *  "Where you are."
      */
@@ -65,18 +69,20 @@
     /** Create a new cursor without giving it a position yet.
      */
     public Cursor(Comparator subjOrder, Comparator objOrder,
-                 StatementSelector statementSelector) {
+                 StatementSelector statementSelector,
+                 NamespaceMap names) {
        this.subjOrder = subjOrder;
        this.objOrder = objOrder;
        this.statementSelector = statementSelector;
+       this.names = names;
     }
 
     /** Create a new cursor and give it a focus.
      */
     public Cursor(Comparator subjOrder, Comparator objOrder,
                  StatementSelector statementSelector,
-                 Resource focus) {
-       this(subjOrder, objOrder, statementSelector);
+                 NamespaceMap names, Resource focus) {
+       this(subjOrder, objOrder, statementSelector, names);
        set(focus);
     }
 
@@ -84,8 +90,9 @@
      */
     public Cursor(Comparator subjOrder, Comparator objOrder,
                  StatementSelector statementSelector,
+                 NamespaceMap names,
                  Resource focus, int dir, RDFNode rotation) {
-       this(subjOrder, objOrder, statementSelector);
+       this(subjOrder, objOrder, statementSelector, names);
        set(focus, dir, rotation);
     }
 
Index: fenfire/org/fenfire/loom/Cursor.test
diff -u fenfire/org/fenfire/loom/Cursor.test:1.8 
fenfire/org/fenfire/loom/Cursor.test:1.9
--- fenfire/org/fenfire/loom/Cursor.test:1.8    Tue Mar 11 17:52:22 2003
+++ fenfire/org/fenfire/loom/Cursor.test        Sat Mar 15 16:57:59 2003
@@ -63,31 +63,31 @@
     for i in range(3,10):
         n[2].addProperty(p, n[i])
 
-    c = org.fenfire.loom.Cursor(subjectCmp, objectCmp, selector)
+    c = org.fenfire.loom.Cursor(subjectCmp, objectCmp, selector, None)
 
 
 def testConstructors():
     sc, oc, sel = subjectCmp, objectCmp, selector
     
-    c = org.fenfire.loom.Cursor(sc,oc,sel)
+    c = org.fenfire.loom.Cursor(sc,oc,sel,None)
     assert (c.getSubjectOrder(), c.getObjectOrder(),
             c.getStatementSelector()) == (sc, oc, sel)
     assert c.focus is None
     assert c.rotation == 0
 
-    c = org.fenfire.loom.Cursor(sc,oc,sel,n[2])
+    c = org.fenfire.loom.Cursor(sc,oc,sel,None,n[2])
     assert (c.getSubjectOrder(), c.getObjectOrder(),
             c.getStatementSelector()) == (sc, oc, sel)
     assert c.focus == n[2]
     assert c.rotation == 0
 
-    c = org.fenfire.loom.Cursor(sc,oc,sel,n[2],1,n[8])
+    c = org.fenfire.loom.Cursor(sc,oc,sel,None,n[2],1,n[8])
     assert (c.getSubjectOrder(), c.getObjectOrder(),
             c.getStatementSelector()) == (sc, oc, sel)
     assert c.focus == n[2]
     assert c.rotation == 2
 
-    c = org.fenfire.loom.Cursor(sc,oc,sel,n[2],-1,n[1])
+    c = org.fenfire.loom.Cursor(sc,oc,sel,None,n[2],-1,n[1])
     assert (c.getSubjectOrder(), c.getObjectOrder(),
             c.getStatementSelector()) == (sc, oc, sel)
     assert c.focus == n[2]
Index: fenfire/org/fenfire/loom/Loom.java
diff -u fenfire/org/fenfire/loom/Loom.java:1.36 
fenfire/org/fenfire/loom/Loom.java:1.37
--- fenfire/org/fenfire/loom/Loom.java:1.36     Thu Mar 13 16:18:37 2003
+++ fenfire/org/fenfire/loom/Loom.java  Sat Mar 15 16:57:59 2003
@@ -39,76 +39,53 @@
 import com.hp.hpl.mesa.rdf.jena.model.*;
 import com.hp.hpl.mesa.rdf.jena.mem.*;
 
-/** A sample app loading an RDF file and showing part of it.
- *  Currently used to verify visually that the tests really work.
- *  May evolve into the main Loom app in the future :-)
+/** The main Fenfire Loom application.
  */
 public class Loom {
 
-    protected View[] views;
-    int viewcur = 0;
+    protected ViewSettings view;
 
     protected ColorScheme colors;
 
     protected Model model;
+    protected Cursor cursor;
     protected RDFVocab rdf;
-    protected NamespaceMap names;
 
-    protected Cursor cursor;
+    protected PropertiesBox propertiesBox = new PropertiesBox();
 
-    protected boolean isNumberProp(Property p) {
-       return p.toString().startsWith(
-            "http://www.w3.org/1999/02/22-rdf-syntax-ns#_";);
-    }
-
-    public void load(File file) throws RDFException, 
-                                      IOException, 
+    public void load(File file) throws RDFException, IOException, 
                                       org.xml.sax.SAXException {
        model = new ModelMem();
        rdf = new RDFVocab(model);
 
        model.read(new java.io.FileReader(file), "");
 
-       names = new NamespaceMap();
+       NamespaceMap names = new NamespaceMap();
        names.loadMappings(new java.io.FileReader(file));
 
-       Comparator compareByAbbrev = names.getComparator();
+       initialize(names);
+    }
 
-       Collection classes = new TreeSet(compareByAbbrev);
+    /** Initialize the cursor and PUI after a model has been loaded.
+     */
+    protected void initialize(NamespaceMap names) throws RDFException, 
IOException,
+                                                        
org.xml.sax.SAXException {
+       Collection classes = new TreeSet(names.getComparator());
        classes.addAll(ModelUtil.getClasses(model));
        classes = new ArrayList(classes);
-       
-       properties = new ArrayList(ModelUtil.getProperties(model));
-       Collections.sort(properties, compareByAbbrev);
-
-       numericPropertiesAccepted = true;
-       acceptedProperties = new HashSet(properties);
-
-       propList.setMultipleMode(true);
-       propList.removeAll();
-       propList.add("rdf:_n properties");
-       propList.select(0);
-       int i=1;
-       for(Iterator iter=properties.iterator(); iter.hasNext();) {
-           Property p = (Property)iter.next();
-           propList.add(names.getAbbrev(p.toString()));
-           propList.select(i);
-           i++;
-       }
 
+       propertiesBox.setModel(model, names);
+       
        Statement stmt = model.listStatements().next();
        cursor = 
            new Cursor(SimpleOrder.subjOrder, SimpleOrder.objOrder, 
-                      new StatementSelector.SimpleSelector() {
-                          protected boolean accept(Statement s) {
-                              Property p = s.getPredicate();
-                              if(isNumberProp(p))
-                                  return numericPropertiesAccepted;
-                              return acceptedProperties.contains(p);
-                          }
-                      },
+                      propertiesBox.getStatementSelector(), names,
                       stmt.getSubject(), 1, stmt.getObject());
 
+       List properties = 
+           new ArrayList(ModelUtil.getNonNumericProperties(model));
+       Collections.sort(properties, names.getComparator());
+
        showClassMenu.removeAll();
        showClassBy.clear();
        classes.add(null);
@@ -116,7 +93,7 @@
            final Resource cls = (Resource)j.next();
            Menu menu;
            if(cls != null)
-               menu = new Menu(names.getAbbrev(cls.toString()));
+               menu = new Menu(cursor.names.getAbbrev(cls.toString()));
            else
                menu = new Menu("All others");
            showClassMenu.add(menu);
@@ -131,7 +108,7 @@
            MenuItem sep = new MenuItem("-"); menu.add(sep);
            for(Iterator k=properties.iterator(); k.hasNext();) {
                final Property prop = (Property)k.next();
-               MenuItem mi = new MenuItem("by 
"+names.getAbbrev(prop.toString()));
+               MenuItem mi = new MenuItem("by 
"+cursor.names.getAbbrev(prop.toString()));
                menu.add(mi);
                mi.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent _) {
@@ -146,12 +123,7 @@
        }
     }
 
-    protected List properties;
-    protected Set acceptedProperties;
-    protected boolean numericPropertiesAccepted;
-
     protected Frame propFrame = new Frame("Properties");
-    protected java.awt.List propList = new java.awt.List();
 
     protected Dialog goTo;
     protected TextField goToText;
@@ -238,33 +210,22 @@
 
        mWheel.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent _) {
-                   viewcur = 0;
+                   view.setWheelView();
                    AbstractUpdateManager.chg();
                }
            });
 
        mSimple.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent _) {
-                   viewcur = 1;
+                   view.setSimpleView();
                    AbstractUpdateManager.chg();
                }
            });
 
        propFrame.setBounds(350, 300, 350, 200);
-       propFrame.add(propList);
-       propList.addItemListener(new ItemListener() {
+       propFrame.add(propertiesBox);
+       propertiesBox.addItemListener(new ItemListener() {
                public void itemStateChanged(ItemEvent e) {
-                   numericPropertiesAccepted = false;
-                   acceptedProperties = new HashSet();
-
-                   int[] indexes = propList.getSelectedIndexes();
-                   for(int i=0; i<indexes.length; i++) {
-                       if(indexes[i] == 0)
-                           numericPropertiesAccepted = true;
-                       else
-                           
acceptedProperties.add(properties.get(indexes[i]-1));
-                   }
-
                    // the rotation cannot be sanely kept
                    // when properties are added or removed
                    cursor.rotation = 0; 
@@ -275,7 +236,7 @@
        final GraphicsAPI api = GraphicsAPI.getInstance();
 
        final TextStyle style = api.getTextStyle("Serif", 0, 12);
-       final NodeView nodeView = new NodeView() {
+       view = new ViewSettings(new NodeView() {
                public void render(VobScene sc, int into, Nodespec spec) {
                    RDFNode node = spec.node;
                    Property prop = spec.prop;
@@ -295,7 +256,7 @@
                    String s = node.toString();
                    boolean isByClass = false;
                    if(node instanceof Resource) {
-                       s = names.getAbbrev(s);
+                       s = cursor.names.getAbbrev(s);
                        String t = s;
 
                        try {
@@ -315,7 +276,7 @@
                                if(byStmt.getObject() instanceof Literal)
                                    s = byStmt.getString();
                                else
-                                   s = 
names.getAbbrev(byStmt.getObject().toString());
+                                   s = 
cursor.names.getAbbrev(byStmt.getObject().toString());
                            
                                isByClass = true;
                            }
@@ -340,7 +301,7 @@
 
                    if(prop != null) {
                        s = prop.toString();
-                       s = names.getAbbrev(s);
+                       s = cursor.names.getAbbrev(s);
                        if(s.length() > 15) s = s.substring(s.length()-15);
                        if(dir > 0)
                            x = 0;
@@ -352,9 +313,7 @@
                        sc.map.put(new TextVob(style, s), propCs);
                    }
                }
-           };
-       views = new View[] { new WheelView(nodeView), 
-                            new SimpleView(nodeView) };
+           });
        final Shower s = new Shower() {
                Screen screen;
                public void setScreen(Screen s) {
@@ -367,7 +326,7 @@
 
                    if(model == null) return sc;
 
-                   views[viewcur].render(sc, 0, cursor);
+                   view.getView().render(sc, 0, cursor);
 
                    if(lastFocus != null) {
                        DefaultVobMatcher lm = (DefaultVobMatcher)old.matcher;
@@ -402,14 +361,14 @@
                        System.exit(0);
                    if(model == null) return;
 
-                   if(viewcur == 0) {
+                   if(view.isWheelView()) {
                        if(s.equals("i") || s.equals("Up") ||
                           s.equals(",") || s.equals("Down")) return;
                        if(s.equals("m") || s.equals("n") || s.equals("o"))
                            s = "Up";
                        else if(s.equals("u") || s.equals("."))
                            s = "Down";
-                   } else {
+                   } else if(view.isSimpleView()) {
                        if(s.equals("u") || s.equals("o"))
                            s = "Up";
                        else if(s.equals("m") || s.equals("n") || s.equals("."))
@@ -445,10 +404,9 @@
                                                  stmt.getPredicate(), 1);
                        cursor.move(1);
                    } else if(s.equals("PgUp")) // previous view wanted
-                       viewcur = (viewcur-1+views.length) 
-                           % views.length;
+                       view.changeView(-1);
                    else if(s.equals("PgDown")) // next view wanted
-                       viewcur = (viewcur+1) % views.length;
+                       view.changeView(1);
 
                    AbstractUpdateManager.chg();
                }
Index: fenfire/org/fenfire/loom/ModelUtil.java
diff -u fenfire/org/fenfire/loom/ModelUtil.java:1.2 
fenfire/org/fenfire/loom/ModelUtil.java:1.3
--- fenfire/org/fenfire/loom/ModelUtil.java:1.2 Tue Mar 11 17:52:22 2003
+++ fenfire/org/fenfire/loom/ModelUtil.java     Sat Mar 15 16:57:59 2003
@@ -51,8 +51,6 @@
     }
 
     /** Get the set of all properties of all statements in a model.
-     *  Omitted from this are the 'numeric' properties:
-     *  rdf:_1, rdf:_2 etc.
      */
     public static Set getProperties(Model m) throws RDFException {
        Set properties = new HashSet();
@@ -61,10 +59,21 @@
        for(StmtIterator iter = m.listStatements(); iter.hasNext();) {
            Statement stmt = iter.next();
            Property p = stmt.getPredicate();
-           if(!rdf.isNumericProperty(p))
-               properties.add(p);
+           properties.add(p);
        }
 
        return properties;
+    }
+
+    /**
+     */
+    public static Set getNonNumericProperties(Model m) 
+       throws RDFException {
+
+       Set s = getProperties(m);
+       for(Iterator i=s.iterator(); i.hasNext();)
+           if(RDFVocab.isNumericProperty((Property)i.next()))
+               i.remove();
+       return s;
     }
 }
Index: fenfire/org/fenfire/loom/ModelUtil.test
diff -u fenfire/org/fenfire/loom/ModelUtil.test:1.2 
fenfire/org/fenfire/loom/ModelUtil.test:1.3
--- fenfire/org/fenfire/loom/ModelUtil.test:1.2 Tue Mar 11 17:52:22 2003
+++ fenfire/org/fenfire/loom/ModelUtil.test     Sat Mar 15 16:57:59 2003
@@ -67,8 +67,11 @@
 
 def testGetProperties():
 
-    def get():
-        set = org.fenfire.loom.ModelUtil.getProperties(model)
+    def get(numeric):
+       if numeric:
+            set = org.fenfire.loom.ModelUtil.getProperties(model)
+        else:
+            set = org.fenfire.loom.ModelUtil.getNonNumericProperties(model)
         ps = [p, q, r, rdf._(3), rdf._(7)]
 
         iter = set.iterator()
@@ -77,24 +80,26 @@
     
         return [x for x in ps if set.contains(x)]
 
-    assert get() == []
+    assert get(0) == get(1) == []
 
     n[2].addProperty(p, n[5])
 
-    assert get() == [p]
+    assert get(0) == get(1) == [p]
 
     n[2].addProperty(rdf._(7), n[3])
     n[4].addProperty(q, n[8])
     n[5].addProperty(q, n[2])
     n[5].addProperty(r, n[2])
 
-    assert get() == [p, q, r]
+    assert get(0) == [p, q, r]
+    assert get(1) == [p, q, r, rdf._(7)]
 
     n[2].removeProperties()
 
-    assert get() == [q, r]
+    assert get(0) == get(1) == [q, r]
 
     n[0].addProperty(rdf._(3), n[3])
     n[0].addProperty(rdf._(7), n[7])
 
-    assert get() == [q, r]
+    assert get(0) == [q, r]
+    assert get(1) == [q, r, rdf._(3), rdf._(7)]
Index: fenfire/org/fenfire/loom/RDFVocab.java
diff -u fenfire/org/fenfire/loom/RDFVocab.java:1.2 
fenfire/org/fenfire/loom/RDFVocab.java:1.3
--- fenfire/org/fenfire/loom/RDFVocab.java:1.2  Tue Mar 11 17:52:22 2003
+++ fenfire/org/fenfire/loom/RDFVocab.java      Sat Mar 15 16:57:59 2003
@@ -47,7 +47,7 @@
        return model.getProperty(namespace, "_"+n);
     }
 
-    public boolean isNumericProperty(Property p) {
+    public static boolean isNumericProperty(Property p) {
        return p.toString().startsWith(namespace + "_");
     }
 
Index: fenfire/org/fenfire/loom/SimpleView.test
diff -u fenfire/org/fenfire/loom/SimpleView.test:1.9 
fenfire/org/fenfire/loom/SimpleView.test:1.10
--- fenfire/org/fenfire/loom/SimpleView.test:1.9        Tue Mar 11 17:52:22 2003
+++ fenfire/org/fenfire/loom/SimpleView.test    Sat Mar 15 16:57:59 2003
@@ -68,7 +68,8 @@
     vs = getvs()
     cursor = org.fenfire.loom.Cursor(Cmp(lambda stmt: stmt.getSubject()),
                              Cmp(lambda stmt: stmt.getObject()),
-                             
org.fenfire.loom.StatementSelector.SimpleSelector())
+                             
org.fenfire.loom.StatementSelector.SimpleSelector(),
+                             None)
     cursor.set(r)
     view.render(vs, 0, cursor)
 
@@ -100,7 +101,7 @@
     cursor = org.fenfire.loom.Cursor(Cmp(lambda stmt: stmt.getSubject()),
                              Cmp(lambda stmt: stmt.getObject()),
                              
org.fenfire.loom.StatementSelector.SimpleSelector(),
-                             r, 1, s)
+                             None, r, 1, s)
     assert cursor.rotation == 0
     view.render(vs, 0, cursor)
 
Index: fenfire/org/fenfire/loom/WheelView.test
diff -u fenfire/org/fenfire/loom/WheelView.test:1.14 
fenfire/org/fenfire/loom/WheelView.test:1.15
--- fenfire/org/fenfire/loom/WheelView.test:1.14        Thu Mar 13 12:49:29 2003
+++ fenfire/org/fenfire/loom/WheelView.test     Sat Mar 15 16:57:59 2003
@@ -106,7 +106,7 @@
     cursor = org.fenfire.loom.Cursor(Cmp(lambda stmt: stmt.getSubject()),
                              Cmp(lambda stmt: stmt.getObject()),
                              
org.fenfire.loom.StatementSelector.SimpleSelector(),
-                             r, 1, s)
+                             None, r, 1, s)
     assert cursor.rotation == 0
     view.render(vs, 0, cursor)
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]