/*
Jreepad - personal information manager.
Copyright (C) 2004 Dan Stowell
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 2
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.
The full license can be read online here:
http://www.gnu.org/copyleft/gpl.html
*/
package jreepad;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
import javax.swing.text.DefaultEditorKit;
import jreepad.editor.ArticleView;
import jreepad.editor.ContentChangeListener;
import jreepad.editor.HtmlViewer;
import jreepad.editor.PlainTextEditor;
import jreepad.editor.TableViewer;
import jreepad.editor.TextileViewer;
import edu.stanford.ejalbert.BrowserLauncher;
import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
import jreepad.ui.FontHelper;
import jreepad.editor.EditPopupHandler;
import jreepad.editor.TextTransferHandler;
import javax.swing.text.JTextComponent;
import java.util.ResourceBundle;
public class JreepadView
extends Box {
private static JreepadPrefs prefs;
private JreepadNode root;
private JreepadNode currentNode;
private JreepadTreeModel treeModel;
private TreeView tree;
private JScrollPane treeView;
private JScrollPane articlePane;
// editorPane is supposed to represent the pane currently displayed/edited - so it's the one
// to refer to when you're doing GUI-related stuff
// It will be equal to one of the content-type-specific panes. Need to set the content of BOTH of these...
// private JEditorPane editorPane;
private PlainTextEditor editorPanePlainText;
private HtmlViewer editorPaneHtml;
private TextileViewer editorPaneTextile;
private TableViewer editorPaneCsv;
private EditPopupHandler editPopupHandler = new EditPopupHandler();
private TextTransferHandler textTransferHandler = new TextTransferHandler();
private ArticleView currentArticleView;
// Undo features
// protected UndoManager undoMgr;
private JSplitPane splitPane;
private JreepadSearcher searcher;
// Things concerned with the "undo" function
//OLD ATTEMPT private JreepadNode oldRootForUndo, oldCurrentNodeForUndo;
//OLD ATTEMPT private TreePath[] oldExpandedPaths;
//OLD ATTEMPT private TreePath oldSelectedPath;
public JreepadView()
{
this(new JreepadTreeModel());
}
public JreepadView(JreepadTreeModel treeModel)
{
super(BoxLayout.X_AXIS);
treeView = new JScrollPane();
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setResizeWeight(0.5);
if(getPrefs().dividerLocation > 0)
{
splitPane.setDividerLocation(getPrefs().dividerLocation);
}
splitPane.addPropertyChangeListener("lastDividerLocation", new java.beans.PropertyChangeListener()
{
public void propertyChange(java.beans.PropertyChangeEvent evt)
{
// System.out.println(evt.getPropertyName());
getPrefs().dividerLocation = splitPane.getDividerLocation();
// System.out.println("New divider location = " + getPrefs().dividerLocation);
}
}
);
this.treeModel = treeModel;
root = (JreepadNode)treeModel.getRoot();
treeModel.addTreeModelListener(new JreepadTreeModelListener());
tree = new TreeView(treeModel);
searcher = new JreepadSearcher(root);
// undoMgr = new UndoManager();
//Listen for when the selection changes.
tree.addTreeSelectionListener(new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent e)
{
JreepadNode node = (JreepadNode)
tree.getLastSelectedPathComponent();
if (node == null) return;
// UNDO DEVELOPMENT:
// System.out.println("TreeSelectionListener:valueChanged");
// undoMgr.discardAllEdits();
setCurrentNode(node);
}
});
tree.addKeyListener(new KeyAdapter(){public void keyPressed(KeyEvent kee) {
int key = kee.getKeyCode();
switch(key)
{
case KeyEvent.VK_ENTER:
addNodeBelow();
break;
case KeyEvent.VK_F2:
editNodeTitleAction();
break;
}
// System.out.println("Tree detected a keypress: " + kee.getKeyText(kee.getKeyCode()) + " (key code "+ kee.getKeyCode()+")");
}});
treeView.setViewportView(tree);
initEditPopupHandler();
editorPanePlainText = new PlainTextEditor(root.getArticle());
initTextComponent((JTextComponent)editorPanePlainText.getComponent());
editorPaneHtml = new HtmlViewer(root.getArticle());
initTextComponent((JTextComponent)editorPaneHtml.getComponent());
editorPaneTextile = new TextileViewer(root.getArticle());
initTextComponent((JTextComponent)editorPaneTextile.getComponent());
editorPaneCsv = new TableViewer(root.getArticle());
currentArticleView = editorPanePlainText;
editorPanePlainText.setContentChangeListener(new ContentChangeListener() {
public void contentChanged()
{
JreepadView.this.treeModel.setContentSaved(false);
}
});
articlePane = new JScrollPane(editorPanePlainText.getComponent(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
/* XXX Is this really needed?
articleView.addComponentListener(new ComponentListener()
{
public void componentResized(ComponentEvent e)
{
editorPanePlainText.setMaximumSize(new Dimension(articleView.getViewport().getWidth(), Integer.MAX_VALUE));
editorPanePlainText.setSize(articleView.getViewport().getViewSize());
editorPaneHtml.setMaximumSize(new Dimension(articleView.getViewport().getWidth(), Integer.MAX_VALUE));
editorPaneHtml.setSize(articleView.getViewport().getViewSize());
}
public void componentMoved(ComponentEvent e){}
public void componentHidden(ComponentEvent e){}
public void componentShown(ComponentEvent e){}
}
);
articleView.getViewport().addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
editorPanePlainText.setPreferredSize(articleView.getViewport().getExtentSize());
editorPanePlainText.setMaximumSize(new Dimension(articleView.getViewport().getWidth(), Integer.MAX_VALUE));
editorPanePlainText.setSize(articleView.getViewport().getViewSize());
editorPaneHtml.setPreferredSize(articleView.getViewport().getExtentSize());
editorPaneHtml.setMaximumSize(new Dimension(articleView.getViewport().getWidth(), Integer.MAX_VALUE));
editorPaneHtml.setSize(articleView.getViewport().getViewSize());
}
}
);
*/
setCurrentNode(root);
setViewBoth();
tree.setSelectionRow(0);
}
private void initEditPopupHandler() {
ResourceBundle lang = ResourceBundle.getBundle(
"jreepad.lang.JreepadStrings");
editPopupHandler.addActionItem(new DefaultEditorKit.CopyAction(),
lang.getString("MENUITEM_COPY"),
EditPopupHandler.ITEM_COPY);
editPopupHandler.addActionItem(new DefaultEditorKit.CutAction(),
lang.getString("MENUITEM_CUT"),
EditPopupHandler.ITEM_CUT);
editPopupHandler.addActionItem(new DefaultEditorKit.PasteAction(),
lang.getString("MENUITEM_PASTE"),
EditPopupHandler.ITEM_PASTE);
}
private void initTextComponent(JTextComponent textComp) {
textComp.addMouseListener(editPopupHandler);
textComp.setTransferHandler(textTransferHandler);
textComp.setDragEnabled(true);
}
public static final int CHANGE_ARTICLE_FONT = 1;
public static final int CHANGE_TREE_FONT = 2;
public void changeFont(int direction,
int comptype) {
if (comptype == CHANGE_ARTICLE_FONT && currentArticleView != null) {
currentArticleView.updateFont(direction);
}
if (comptype == CHANGE_TREE_FONT && tree != null) {
FontHelper.updateFont(tree, direction);
}
}
public void setViewMode(int mode) {
// System.out.println("-------------------------------------------------------------");
// System.out.println("editorPane size: " + editorPane.getSize());
// System.out.println("articleView size: " + articleView.getSize());
// System.out.println("articleView viewport size: " + articleView.getViewport().getSize());
// System.out.println();
switch(mode)
{
case JreepadPrefs.VIEW_BOTH:
setViewBoth();
break;
case JreepadPrefs.VIEW_TREE:
setViewTreeOnly();
break;
case JreepadPrefs.VIEW_ARTICLE:
setViewArticleOnly();
break;
default:
System.err.println("Invalid argument to JreepadView.setViewMode()!");
return;
}
setSize(getSize());
currentArticleView.getComponent().setPreferredSize(articlePane.getViewport().getExtentSize());
currentArticleView.getComponent().setSize(articlePane.getViewport().getExtentSize());
validate();
repaint();
// System.out.println("editorPane size: " + editorPane.getSize());
// System.out.println("articleView size: " + articleView.getSize());
// System.out.println("articleView viewport size: " + articleView.getViewport().getSize());
// System.out.println();
// System.out.println();
}
private void setViewBoth()
{
ensureCorrectArticleRenderMode();
splitPane.setLeftComponent(treeView);
splitPane.setRightComponent(articlePane);
this.add(splitPane);
// editorPane.setSize(articleView.getSize());
// editorPane.setSize(articleView.getViewport().getViewSize());
getPrefs().viewWhich = JreepadPrefs.VIEW_BOTH;
}
private void setViewTreeOnly()
{ this.remove(splitPane);
this.remove(articlePane);
this.add(treeView);
treeView.setSize(getSize());
getPrefs().viewWhich = JreepadPrefs.VIEW_TREE;
}
private void setViewArticleOnly()
{
this.remove(splitPane);
this.remove(treeView);
ensureCorrectArticleRenderMode();
this.add(articlePane);
articlePane.setSize(getSize());
getPrefs().viewWhich = JreepadPrefs.VIEW_ARTICLE;
}
private void setCurrentNode(JreepadNode n)
{
// This should stop the caret jumping to the end of the text when we press Save.
if (currentNode == n)
return;
ArticleView oldView = currentArticleView;
oldView.lockEdits(); // Deactivate the caret-listener, effectively - ALSO DEACTIVATES UNDO-STORAGE
currentNode = n;
ensureCorrectArticleRenderMode();
oldView.unlockEdits(); // Reactivate the caret listener - ALSO REACTIVATES UNDO-STORAGE
}
public JTree getTree()
{
return tree;
}
public JreepadNode getCurrentNode()
{
return currentNode;
}
public String getTreepadNodeUrl()
{
StringBuffer ret = new StringBuffer("\"node:/");
Object[] p = tree.getLeadSelectionPath().getPath();
for(int i=0; i<p.length; i++)
ret.append("/" + ((JreepadNode)p[i]).getTitle());
return ret.toString() + "\"";
}
public void indentCurrentNode()
{
if(currentNode.equals(root))
{
notForRootNode();
return;
}
TreePath parentPath = tree.getSelectionPath().getParentPath();
int pos = currentNode.getIndex();
if(pos<1) return;
//DEL storeForUndo();
JreepadNode newParent = ((JreepadNode)currentNode.getParent().getChildAt(pos-1));
if(currentNode.indent())
{
treeModel.reload(currentNode.getParent().getParent());
parentPath = parentPath.pathByAddingChild(newParent);
TreePath myPath = parentPath.pathByAddingChild(currentNode);
// Now use scrollPathToVisible() or scrollRowToVisible() to make sure it's visible
tree.scrollPathToVisible(myPath);
tree.setSelectionPath(myPath);
}
}
public void outdentCurrentNode()
{
if(currentNode.equals(root))
{
notForRootNode();
return;
}
TreePath parentPath = tree.getSelectionPath().getParentPath();
if(parentPath==null) return;
TreePath parentParentPath = parentPath.getParentPath();
if(parentParentPath==null) return;
//DEL storeForUndo();
if(currentNode.outdent())
{
TreePath myPath = parentParentPath.pathByAddingChild(currentNode);
treeModel.reload(currentNode.getParent());
// Now use scrollPathToVisible() or scrollRowToVisible() to make sure it's visible
tree.scrollPathToVisible(myPath);
tree.setSelectionPath(myPath);
}
}
public void moveCurrentNodeUp()
{
TreePath nodePath = tree.getSelectionPath();
if(currentNode.equals(root))
{
notForRootNode();
return;
}
//DEL storeForUndo();
currentNode.moveUp();
treeModel.reload(currentNode.getParent());
tree.setSelectionPath(nodePath);
}
public void moveCurrentNodeDown()
{
TreePath nodePath = tree.getSelectionPath();
if(currentNode.equals(root))
{
notForRootNode();
return;
}
//DEL storeForUndo();
currentNode.moveDown();
treeModel.reload(currentNode.getParent());
tree.setSelectionPath(nodePath);
}
private void notForRootNode()
{
// FIXME: If there are no child nodes, assume the user needs some advice about adding nodes
if(root.isLeaf())
JOptionPane.showMessageDialog(this,
JreepadViewer.lang.getString("MSG_ONLY_ON_CHILDNODES"), JreepadViewer.lang.getString("TITLE_ONLY_ON_CHILDNODES") ,
JOptionPane.INFORMATION_MESSAGE);
else
return;
// JOptionPane.showMessageDialog(this,
// "The root node is currently selected - you can only perform this operation on child nodes.", "Root node is selected" ,
// JOptionPane.INFORMATION_MESSAGE);
}
public void insertDate()
{
if(currentNode.getArticle().getArticleMode() != JreepadArticle.ARTICLEMODE_ORDINARY)
return; // May want to fix this later - allow other modes to have the date inserted...
//DEL storeForUndo();
String theDate = JreepadArticle.getCurrentDate();
editorPanePlainText.insertText(theDate);
}
public JreepadNode addNodeAbove()
{
int index = currentNode.getIndex();
if(index==-1)
{
notForRootNode();
return null;
}
if(tree.getSelectionPath()==null)
return null;
//DEL storeForUndo();
TreePath parentPath = tree.getSelectionPath().getParentPath();
JreepadNode parent = currentNode.getParentNode();
JreepadNode ret = parent.addChild(index);
treeModel.nodesWereInserted(parent, new int[]{index});
TreePath newPath = (parentPath.pathByAddingChild(ret));
if(newPath!=null)
tree.startEditingAtPath(newPath);
return ret;
}
public JreepadNode addNodeBelow()
{
int index = currentNode.getIndex();
if(index==-1)
{
notForRootNode();
return null;
}
if(tree.getSelectionPath()==null)
return null;
//DEL storeForUndo();
TreePath parentPath = tree.getSelectionPath().getParentPath();
JreepadNode parent = currentNode.getParentNode();
JreepadNode ret = parent.addChild(index+1);
treeModel.nodesWereInserted(parent, new int[]{index+1});
tree.startEditingAtPath(parentPath.pathByAddingChild(ret));
return ret;
}
public JreepadNode addNode()
{
//DEL storeForUndo();
JreepadNode ret = currentNode.addChild();
TreePath nodePath = tree.getSelectionPath();
treeModel.nodesWereInserted(currentNode, new int[]{currentNode.getIndex(ret)});
// tree.setSelectionPath(nodePath.pathByAddingChild(ret));
tree.scrollPathToVisible(nodePath.pathByAddingChild(ret));
tree.startEditingAtPath(nodePath.pathByAddingChild(ret));
return ret;
}
public JreepadNode removeNode()
{
JreepadNode parent = (JreepadNode)currentNode.getParent();
TreePath parentPath = tree.getSelectionPath().getParentPath();
if(parent != null)
{
//DEL storeForUndo();
int index = parent.getIndex(currentNode);
JreepadNode ret = parent.removeChild(index);
tree.setSelectionPath(parentPath);
treeModel.nodesWereRemoved(parent, new int[]{index}, new Object[]{ret});
repaint();
return ret;
}
else
return null;
}
public void sortChildren()
{
//DEL storeForUndo();
currentNode.sortChildren();
treeModel.reload(currentNode);
// System.out.println(currentNode.toFullString());
}
public void sortChildrenRecursive()
{
//DEL storeForUndo();
currentNode.sortChildrenRecursive();
treeModel.reload(currentNode);
// System.out.println(currentNode.toFullString());
}
public void returnFocusToTree()
{
tree.requestFocus();
}
public void expandAllCurrentNode()
{
tree.expandAll(currentNode, tree.getLeadSelectionPath());
}
public void collapseAllCurrentNode()
{
tree.collapseAll(currentNode, tree.getLeadSelectionPath());
}
public TreePath[] getAllExpandedPaths()
{
return tree.getAllExpandedPaths();
}
// THIS FUNCTION SEEMS TO HAVE NO EFFECT, ON MY MACHINE AT LEAST! WHAT'S GOING ON?
public void expandPaths(TreePath[] paths)
{
tree.expandPaths(paths);
}
// Functions and inner class for searching nodes
public boolean performSearch(String inNodes, String inArticles, int searchWhat // 0=selected, 1=all
, boolean orNotAnd, boolean caseSensitive, int maxResults)
{
switch(searchWhat)
{
case 0: // search selected node
searcher.performSearch(inNodes, inArticles, tree.getSelectionPath(), orNotAnd, caseSensitive, maxResults);
break;
default: // case 1==search whole tree
searcher.performSearch(inNodes, inArticles, new TreePath(root), orNotAnd, caseSensitive, maxResults);
break;
}
return true;
}
public JreepadSearcher.JreepadSearchResult[] getSearchResults()
{
return searcher.getSearchResults();
}
public void addChildrenFromTextFiles(File[] inFiles) throws IOException
{
//DEL storeForUndo();
for(int i=0; i<inFiles.length; i++)
getCurrentNode().addChildFromTextFile(new InputStreamReader(new FileInputStream(inFiles[i]), getPrefs().getEncoding())
, inFiles[i].getName());
treeModel.reload(currentNode);
tree.expandPath(tree.getSelectionPath());
}
public void addChild(JreepadNode newKid)
{
//DEL storeForUndo();
getCurrentNode().add(newKid);
treeModel.reload(currentNode);
tree.expandPath(tree.getSelectionPath());
}
public void addChildrenFromListTextFile(InputStreamReader inFile) throws IOException
{
//DEL storeForUndo();
BufferedReader bReader = new BufferedReader(inFile);
String curLine;
while((curLine = bReader.readLine())!=null)
if(curLine.trim().length() > 0)
getCurrentNode().add(new JreepadNode(curLine.trim(), ""));
treeModel.reload(currentNode);
tree.expandPath(tree.getSelectionPath());
}
public static JreepadPrefs getPrefs()
{
return prefs;
}
public static void setPrefs(JreepadPrefs thesePrefs)
{
prefs = thesePrefs;
prefs.save();
}
// Stuff concerned with linking
public void webSearchTextSelectedInArticle()
{
// JComponent treeOrArticle;
String url = currentArticleView.getSelectedText();
if(url==null || url.length()==0)
url = currentNode.getTitle();
if((url == null) && (currentNode.getArticle().getArticleMode()==JreepadArticle.ARTICLEMODE_ORDINARY))
url = editorPanePlainText.selectWordUnderCursor();
if(url==null || !(url.length()>0))
url = currentNode.getTitle();
webSearchText(url);
}
public void webSearchText(String text)
{
openURL("http://" + getPrefs().webSearchPrefix + text + getPrefs().webSearchPostfix);
}
public void openURLSelectedInArticle()
{
String url = currentArticleView.getSelectedText();
if((url == null) && (currentNode.getArticle().getArticleMode()==JreepadArticle.ARTICLEMODE_ORDINARY))
url = editorPanePlainText.selectWordUnderCursor();
openURL(url);
}
public static boolean isPureWord(String in)
{
char[] c = in.toCharArray();
for(int i=0; i<c.length; i++)
if(c[i]==':' || c[i]=='/' || c[i]=='[' || c[i]==']')
return false;
return true;
}
public static boolean isWikiWord(String in)
{
if(in.length()>4 && in.startsWith("[[") && in.endsWith("]]"))
return true;
char[] c = in.toCharArray();
int uppers = 0;
boolean currentlyUpper = false;
for(int i=0; i<c.length; i++)
if(!Character.isLetter(c[i]))
return false;
else if(i==0 && !Character.isUpperCase(c[i]))
return false;
else
if(currentlyUpper && Character.isLowerCase(c[i]))
{
currentlyUpper = false;
uppers++;
}
else if(!currentlyUpper && Character.isUpperCase(c[i]))
{
currentlyUpper = true;
}
return uppers>1;
}
public void openURL(String url)
{
if(url==null || url=="")
return;
url = url.trim();
// Wiki-like links
if(isWikiWord(url))
{
followWikiLink(url, prefs.wikiBehaviourActive);
return;
}
// if(url.length()>4 && url.startsWith("[[") && url.endsWith("]]"))
// {
// followWikiLink(url.substring(2, url.length()-2));
// return;
// }
if(isPureWord(url))
{
if(prefs.defaultSearchMode == 0)
webSearchText(url);
else
followWikiLink(url, false);
return;
}
// Strip quotes off
if(url.length()>2 && url.startsWith("\"") && url.endsWith("\""))
url = url.substring(1, url.length()-1);
// Treepad node:// links
if(url.startsWith("node://"))
{
if(!followTreepadInternalLink(url))
JOptionPane.showMessageDialog(this,
JreepadViewer.lang.getString("MSG_NODE_NOT_FOUND"),
JreepadViewer.lang.getString("TITLE_NODE_NOT_FOUND"),
JOptionPane.ERROR_MESSAGE);
return;
}
// It's probably a web-link, so let's do something to it and then try and launch it
/*
// NOTE:
// I haven't been able to get this file:// method to work, on Windows 2000 or on Mac OSX.
// So I'm disactivating it for now.
// Firstly we use Kami's method for attempting to open file:// links
if(url.startsWith("file://"))
{
url = getPrefs().openLocation.getParentFile().getPath() + System.getProperty("file.separator")+ url.substring(7);
try
{
BrowserLauncher.openURL(url.toString());
}
catch(IOException err)
{
JOptionPane.showMessageDialog(this, "I/O error while opening URL:\n"+url+"\n\nThe \"BrowserLauncher\" used to open a URL is an open-source Java library \nseparate from Jreepad itself - i.e. a separate Sourceforge project. \nIt may be a good idea to submit a bug report to\nhttp://sourceforge.net/projects/browserlauncher\n\nIf you do, please remember to supply information about the operating system\nyou are using - which type, and which version.", "Error" , JOptionPane.ERROR_MESSAGE);
}
}
else
{
*/
char[] curl = url.toCharArray();
StringBuffer surl = new StringBuffer();
for(int i=0; i<curl.length; i++)
if(curl[i]==' ')
surl.append("%20");
else
surl.append(curl[i]);
try
{
new BrowserLauncher(null).openURLinBrowser(surl.toString());
}
catch (BrowserLaunchingInitializingException e)
{
displayBrowserLauncherException(e, surl.toString());
}
catch (BrowserLaunchingExecutionException e)
{
displayBrowserLauncherException(e, surl.toString());
}
catch (UnsupportedOperatingSystemException e)
{
displayBrowserLauncherException(e, surl.toString());
}
// }
}
private void displayBrowserLauncherException(Exception e, String url)
{
JOptionPane.showMessageDialog(this, "Error while opening URL:\n" + url + "\n"
+ e.getMessage() + "\n\n"
+ "The \"BrowserLauncher\" used to open a URL is an open-source Java library \n"
+ "separate from Jreepad itself - i.e. a separate Sourceforge project. \n"
+ "It may be a good idea to submit a bug report to\n"
+ "http://browserlaunch2.sourceforge.net/\n\n"
+ "If you do, please remember to supply information about the operating system\n"
+ "you are using - which type, and which version.", "Error",
JOptionPane.ERROR_MESSAGE);
}
public boolean followTreepadInternalLink(String url)
{
url = url.substring(7);
// Split it at slashes, and then add each one to the new TreePath object as we go
Vector pathNames = new Vector();
StringBuffer buf = new StringBuffer();
char[] curl = url.toCharArray();
for(int i=0; i<curl.length; i++)
if(curl[i]=='/')
{
pathNames.add(buf.toString());
buf = new StringBuffer();
}
else
buf.append(curl[i]);
if(buf.length()>0)
pathNames.add(buf.toString());
// System.out.println(pathNames);
// OK, so we've got the names into an array. Now how do we actually follow the path?
if(pathNames.size()<1 || !((String)pathNames.get(0)).equals(root.getTitle()))
return false;
TreePath goTo = new TreePath(root);
JreepadNode nextNode = root;
for(int i=1; i<pathNames.size(); i++)
{
nextNode = nextNode.getChildByTitle((String)pathNames.get(i));
if(nextNode == null)
return false;
goTo = goTo.pathByAddingChild(nextNode);
}
tree.setSelectionPath(goTo);
tree.scrollPathToVisible(goTo);
return true;
}
// End of: stuff concerned with linking
// Searching (for wikilike action)
public void followWikiLink(String text, boolean noNeedToConfirm)
{
if(text.length()>4 && text.startsWith("[[") && text.endsWith("]]"))
text = text.substring(2, text.length()-2);
TreePath tp = findNearestNodeTitled(text);
if(tp == null)
{
if(noNeedToConfirm || JOptionPane.showConfirmDialog(this, JreepadViewer.lang.getString("TITLE_NODE_NOT_FOUND_PROMPT_CREATE"), JreepadViewer.lang.getString("MSG_NODE_NOT_FOUND") , JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE)
== JOptionPane.YES_OPTION)
{
JreepadNode newNode;
TreePath newPath;
newNode = new JreepadNode(text, "");
addChild(newNode);
TreePath leadPath = tree.getLeadSelectionPath();
if(leadPath != null)
newPath = leadPath.pathByAddingChild(newNode);
else
newPath = new TreePath(newNode);
// Now we need to select it... how do we do that?
tree.setSelectionPath(newPath);
tree.scrollPathToVisible(newPath);
}
}
else
tree.setSelectionPath(tp);
}
public TreePath findNearestNodeTitled(String text)
{
TreePath curPath = tree.getLeadSelectionPath();
TreePath tp;
while(curPath != null && curPath.getPathCount()>0)
{
tp = findChildTitled(text, curPath);
if(tp!=null)
return tp;
// Else try again but using the parent...
curPath = curPath.getParentPath();
}
return null;
}
public TreePath findChildTitled(String text)
{
return findChildTitled(text, tree.getLeadSelectionPath());
}
public TreePath findChildTitled(String text, TreePath pathToNode)
{
JreepadNode myNode = (JreepadNode)pathToNode.getLastPathComponent();
JreepadNode myChild;
TreePath childPath;
for(int i=0; i<myNode.getChildCount(); i++)
{
myChild = (JreepadNode)myNode.getChildAt(i);
childPath = pathToNode.pathByAddingChild(myChild);
if(myChild.getTitle().equals(text))
return childPath;
else
{ // Ask the child to search its descendents
childPath = findChildTitled(text, childPath);
if(childPath!=null)
return childPath;
}
}
return null;
}
// End of: Searching (for wikilike action)
// public void setTreeFont(Font f)
// {
// ((DefaultTreeCellRenderer)tree.getCellRenderer()).setFont(f);
// }
// public void setArticleFont(Font f)
// {
// editorPane.setFont(f);
// }
public void wrapContentToCharWidth(int charWidth)
{
//DEL storeForUndo();
currentNode.getArticle().wrapContentToCharWidth(charWidth);
currentArticleView.reloadArticle();
}
public void stripAllTags()
{
//DEL storeForUndo();
currentNode.getArticle().stripAllTags();
currentArticleView.reloadArticle();
}
public void setArticleMode(int newMode)
{
ArticleView oldView = currentArticleView;
oldView.lockEdits(); // Disables store-for-undo
currentNode.getArticle().setArticleMode(newMode);
ensureCorrectArticleRenderMode();
currentArticleView.getComponent().repaint();
oldView.unlockEdits(); // Re-enables store-for-undo
}
public void ensureCorrectArticleRenderMode()
{
switch(currentNode.getArticle().getArticleMode())
{
case JreepadArticle.ARTICLEMODE_ORDINARY:
currentArticleView = editorPanePlainText;
break;
case JreepadArticle.ARTICLEMODE_HTML:
currentArticleView = editorPaneHtml;
break;
case JreepadArticle.ARTICLEMODE_TEXTILEHTML:
currentArticleView = editorPaneTextile;
break;
case JreepadArticle.ARTICLEMODE_CSV:
currentArticleView = editorPaneCsv;
break;
default:
System.err.println("getEditorPaneComponent() says: JreepadNode.getArticleMode() returned an unrecognised value");
currentArticleView = null;
}
currentArticleView.setArticle(currentNode.getArticle());
articlePane.setViewportView(currentArticleView.getComponent());
}
public void editNodeTitleAction()
{
if(getPrefs().viewWhich == JreepadPrefs.VIEW_ARTICLE)
{
setViewMode(JreepadPrefs.VIEW_BOTH);
}
tree.startEditingAtPath(tree.getSelectionPath());
}
class JreepadTreeModelListener implements TreeModelListener
{
public void treeNodesChanged(TreeModelEvent e)
{
treeModel.setContentSaved(false);
tree.repaint();
}
public void treeNodesInserted(TreeModelEvent e)
{
treeModel.setContentSaved(false);
tree.expandPath(e.getTreePath());
tree.scrollPathToVisible(e.getTreePath());
tree.repaint();
}
public void treeNodesRemoved(TreeModelEvent e)
{
treeModel.setContentSaved(false);
tree.repaint();
}
public void treeStructureChanged(TreeModelEvent e)
{
treeModel.setContentSaved(false);
tree.repaint();
}
} // End of: class JreepadTreeModelListener
}