2
* @(#)BasicFavoritesNavigatorUI.java 1.17 06/10/30
4
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7
* This code is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License version 2 only, as
9
* published by the Free Software Foundation. Sun designates this
10
* particular file as subject to the "Classpath" exception as provided
11
* by Sun in the LICENSE file that accompanied this code.
13
* This code is distributed in the hope that it will be useful, but WITHOUT
14
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16
* version 2 for more details (a copy is included in the LICENSE file that
17
* accompanied this code).
19
* You should have received a copy of the GNU General Public License version
20
* 2 along with this work; if not, write to the Free Software Foundation,
21
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24
* CA 95054 USA or visit www.sun.com if you need additional information or
28
package javax.help.plaf.basic;
31
import javax.help.plaf.HelpNavigatorUI;
32
import javax.help.plaf.HelpUI;
33
import javax.help.event.HelpModelListener;
34
import javax.help.event.HelpModelEvent;
35
import java.util.EventObject;
36
import java.util.Enumeration;
37
import java.util.Hashtable;
38
import java.util.Vector;
39
import java.util.Locale;
40
import java.util.HashMap;
42
import javax.swing.plaf.ComponentUI;
43
import javax.swing.tree.*;
44
import javax.swing.event.*;
46
import java.awt.event.*;
47
import java.io.Reader;
48
import java.io.Serializable;
50
import java.net.URLConnection;
51
import java.beans.PropertyChangeEvent;
52
import java.beans.PropertyChangeListener;
53
import java.awt.event.ComponentEvent;
54
import javax.help.Map.ID;
55
import java.text.Collator;
56
import java.text.RuleBasedCollator;
57
import java.net.MalformedURLException;
58
import java.util.HashSet;
59
import javax.help.event.HelpSetListener;
60
import javax.help.event.HelpSetEvent;
61
import java.awt.Cursor;
62
import java.awt.image.BufferedImage;
63
import java.awt.geom.Rectangle2D.Float;
64
import java.awt.geom.Rectangle2D;
65
import java.awt.geom.AffineTransform;
66
import java.awt.datatransfer.*;
67
import java.awt.dnd.*;
68
import java.awt.dnd.peer.*;
69
import java.io.IOException;
72
* The default UI for JHelpNavigator of type Favorites
74
* @author Richard Gregor
75
* @version 1.17 10/30/06
78
public class BasicFavoritesNavigatorUI extends HelpNavigatorUI
79
implements HelpModelListener, TreeSelectionListener, HelpSetListener,
80
PropertyChangeListener, TreeModelListener, Serializable {
82
protected JHelpFavoritesNavigator favorites;
83
protected JScrollPane sp;
84
protected FavoritesNode topNode;
86
private String contentTitle;
87
protected RuleBasedCollator rbc;
88
protected String oldText;
89
protected DefaultMutableTreeNode currentFindNode;
90
protected Action addAction;
91
protected Action removeAction;
92
protected Action cutAction;
93
protected Action pasteAction;
94
protected Action copyAction;
95
protected Action folderAction;
96
protected JPopupMenu popup;
98
private HashMap dataMap = new HashMap();
99
// node wich will contain parsed favorites data
100
private FavoritesNode favNode = null;
101
// node which contents will be saved
102
private FavoritesNode rootNode = null;
103
private Locale locale = null;
104
private FavoritesNode selectedNode = null;
105
private FavoritesItem selectedItem = null;
106
private TreePath selectedTreePath = null;
107
private Map.ID selectedID = null;
108
private JMenuItem newFolderMI = null;
109
private JMenuItem addMI = null;
110
private JSeparator separatorMI = null;
111
private JMenuItem cutMI = null;
112
private JMenuItem copyMI = null;
113
private JMenuItem pasteMI = null;
114
private JMenuItem removeMI = null;
115
private HashMap hsMap = null;
116
//HasMap with visible node from favNode as key and its related node from rootNode as a value
117
private HashMap connections = new HashMap();
119
* Vector of nodes used in CutAction
121
private Vector nodeClipboard = new Vector();
123
static private boolean on1dot3 = false;
127
// Test if method introduced in 1.3 or greater is available.
128
Class klass = Class.forName("javax.swing.InputVerifier");
129
on1dot3 = (klass != null);
130
} catch (ClassNotFoundException e) {
136
public static ComponentUI createUI(JComponent x) {
137
return new BasicFavoritesNavigatorUI((JHelpFavoritesNavigator) x);
140
* Creates BasicFavoritesUI for JHelpFavoritesNavigator
142
public BasicFavoritesNavigatorUI(JHelpFavoritesNavigator b) {
143
ImageIcon icon = getImageIcon(b.getNavigatorView());
147
setIcon(UIManager.getIcon("FavoritesNav.icon"));
151
public void installUI(JComponent c) {
154
locale = HelpUtilities.getLocale(c);
155
addAction = new AddAction();
156
removeAction = new RemoveAction();
157
folderAction = new FolderAction();
158
favorites = (JHelpFavoritesNavigator)c;
159
HelpModel model = favorites.getModel();
161
favorites.setLayout(new BorderLayout());
162
favorites.addPropertyChangeListener(this);
164
model.addHelpModelListener(this); // for our own changes
165
model.addPropertyChangeListener(this); // for HelpSet change
166
HelpSet helpSet = model.getHelpSet();
168
helpSet.addHelpSetListener(this);
171
topNode = new FavoritesNode(new FavoritesItem("Favorites"));
173
// Use drag and drop if available
174
tree = new FavoritesTree(topNode);
176
tree = new JTree(topNode);
178
tree.setEditable(true);
179
tree.addMouseListener(new PopupListener());
181
cutAction = new CutAction();
182
copyAction = new CopyAction();
183
pasteAction = new PasteAction();
185
popup = new JPopupMenu();
187
newFolderMI = new JMenuItem((String)folderAction.getValue(folderAction.NAME));
188
newFolderMI.addActionListener(folderAction);
189
popup.add(newFolderMI);
191
addMI = new JMenuItem((String)addAction.getValue(addAction.NAME));
192
addMI.addActionListener(addAction);
195
separatorMI = new JSeparator();
196
popup.add(separatorMI);
198
cutMI = new JMenuItem((String)cutAction.getValue(cutAction.NAME));
199
cutMI.addActionListener(cutAction);
200
cutMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,java.awt.Event.CTRL_MASK));
203
copyMI = new JMenuItem((String)copyAction.getValue(copyAction.NAME));
204
copyMI.addActionListener(copyAction);
205
copyMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,java.awt.Event.CTRL_MASK));
208
pasteMI = new JMenuItem((String)pasteAction.getValue(pasteAction.NAME));
209
pasteMI.addActionListener(pasteAction);
210
pasteMI.setEnabled(false);
211
pasteMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,java.awt.Event.CTRL_MASK));
214
removeMI = new JMenuItem((String) removeAction.getValue(removeAction.NAME));
215
removeMI.addActionListener(removeAction);
219
tree.getModel().addTreeModelListener(this);
220
tree.addTreeSelectionListener(this);
222
TreeSelectionModel tsm = tree.getSelectionModel();
223
tsm.addTreeSelectionListener(this);
225
tree.setShowsRootHandles(true);
226
tree.setRootVisible(false);
228
setCellRenderer(favorites.getNavigatorView(), tree);
230
sp = new JScrollPane();
231
sp.getViewport().add(tree);
233
favorites.add("Center", sp);
239
* Sets the desired cell renderer on this tree. This is exposed for redefinition
242
protected void setCellRenderer(NavigatorView view, JTree tree) {
243
tree.setCellRenderer(new BasicFavoritesCellRenderer());
246
public void uninstallUI(JComponent c) {
247
debug("uninstallUI");
248
HelpModel model = favorites.getModel();
250
favorites.removePropertyChangeListener(this);
251
TreeSelectionModel tsm = tree.getSelectionModel();
252
tsm.removeTreeSelectionListener(this);
253
favorites.setLayout(null);
254
favorites.removeAll();
257
model.removeHelpModelListener(this);
263
public Dimension getPreferredSize(JComponent c) {
265
return new Dimension(200,100);
269
public Dimension getMinimumSize(JComponent c) {
270
return new Dimension(100,100);
273
public Dimension getMaximumSize(JComponent c) {
274
return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
278
private void reloadData() {
281
//needs refactoring - sholudn't parse each time!!! - only once
283
if (favorites.getModel() == null) {
287
// remove all children
288
topNode.removeAllChildren();
290
FavoritesView view = (FavoritesView) favorites.getNavigatorView();
296
favNode = view.getDataAsTree();
297
rootNode = favNode.getDeepCopy();
299
classifyNode(favNode);
300
while (favNode.getChildCount() > 0) {
301
// this looks strange but as you add a child to the top node
302
// it removes the child from the favNode so the getFirstChild
303
// will always get the first child
304
topNode.add((DefaultMutableTreeNode)favNode.getFirstChild());
307
// reload the tree data
308
((DefaultTreeModel)tree.getModel()).reload();
310
setVisibility(topNode);
315
* Classifies the node. Determines wheter child will be visible or not according to node's HelpSet.
316
* Hides node without children when its HelpSet is not loaded
318
* @param node The FavoritesNode
320
private void classifyNode(FavoritesNode node){
321
debug("classifyNode");
326
HelpModel model = favorites.getModel();
328
node.removeAllChildren();
331
HelpSet masterHelpSet = model.getHelpSet();
332
if(masterHelpSet == null){
333
node.removeAllChildren();
336
hsMap = new HashMap();
337
hsMap.put(masterHelpSet.getTitle(), masterHelpSet);
339
fillHelpSetTitles(masterHelpSet);
341
// tags node's children wheter they are visible or not
342
classifyChildren(node);
347
* Fills HashMap with actual HelpSets and their titles
349
* @param masterHelpSet The master HelpSet
351
private void fillHelpSetTitles(HelpSet masterHelpSet){
352
for(Enumeration helpSets = masterHelpSet.getHelpSets();helpSets.hasMoreElements();){
353
HelpSet hs = (HelpSet) helpSets.nextElement();
355
hsMap.put(hs.getTitle(), hs);
356
debug(" fill title: "+hs.getTitle());
357
fillHelpSetTitles(hs);
363
* Classifies children of node
365
* @param node The FavoritesNode
367
private void classifyChildren(FavoritesNode node){
368
debug("classifyChildren: "+node);
373
boolean skipChild = true;
375
for(Enumeration children = node.preorderEnumeration();children.hasMoreElements();){
376
FavoritesNode chnode = (FavoritesNode)children.nextElement();
378
// Skip the first entry because it is node and we don't need
379
// to classify the node only it's children
385
// Add this child node to the root node.
387
// Make a copy of the child node for the rootNode
388
FavoritesNode copy = chnode.getDeepCopy();
390
// establish the connection between the chnode and the copy
391
connections.put(chnode, copy);
392
// properly connect it to the childs corresponding parent in the
394
FavoritesNode rootParent = (FavoritesNode)connections.get(chnode.getParent());
395
if (rootParent == null) {
396
rootParent = rootNode;
398
rootParent.add(copy);
400
// Now on to seeing if the item
401
FavoritesItem item = (FavoritesItem)chnode.getUserObject();
402
debug("classify item: "+ item);
405
debug("item is null : fillDataMap");
408
String target = item.getTarget();
409
String hsTitle = item.getHelpSetTitle();
410
if(!hsMap.containsKey(hsTitle)){
411
if(chnode.getVisibleChildCount() == 0){
412
if(item.emptyInitState() && item.isFolder()){
413
debug("empty init state");
416
//chnode.removeFromParent();
417
item.setVisible(false);
423
debug("target is null:fillDataMap");
429
id = Map.ID.create(target, (HelpSet)hsMap.get(hsTitle));
430
}catch(BadIDException ep){
431
debug(ep.getMessage());
435
debug("put to the dataMap: "+item);
436
dataMap.put(item,id);
440
Vector toRemove = new Vector();
441
Enumeration nodesToRem = node.breadthFirstEnumeration();
442
while(nodesToRem.hasMoreElements()) {
443
FavoritesNode fn = (FavoritesNode)nodesToRem.nextElement();
445
debug ("remove node:" +(FavoritesItem)fn.getUserObject());
446
toRemove.addElement(fn);
449
for (int i=0; i < toRemove.size(); i++) {
450
debug("removing " + toRemove.elementAt(i));
452
node.remove((DefaultMutableTreeNode) toRemove.elementAt(i));
453
} catch (IllegalArgumentException iae) {
454
// ignore - supernode is already removed
461
* Expands entry path and entry itself( when entry is not empty) for specific id
463
* @param target The target of entry
466
private void expand(String target){
467
debug("expand called");
468
//find all nodes with certain id
469
Enumeration nodes = findNodes(target).elements();
470
DefaultMutableTreeNode node = null;
472
while(nodes.hasMoreElements()){
473
node = (DefaultMutableTreeNode)nodes.nextElement();
474
debug("expandPath :"+node);
475
if(node.getChildCount() > 0){
476
DefaultMutableTreeNode child =(DefaultMutableTreeNode) node.getFirstChild();
477
TreePath path = new TreePath(child.getPath());
478
tree.makeVisible(path);
481
TreeNode[] treeNode = node.getPath();
482
TreePath path = new TreePath(treeNode);
483
//tree.scrollPathToVisible(path);
484
tree.makeVisible(path);
490
* Returns all nodes with certain id
492
* @param target The target of entry
495
private Vector findNodes(String target){
496
Enumeration nodes = topNode.preorderEnumeration();
497
DefaultMutableTreeNode node = null;
498
Vector nodeFound = new Vector();
500
while(nodes.hasMoreElements()){
501
node = (DefaultMutableTreeNode)nodes.nextElement();
502
debug(" node :"+ node.toString());
504
FavoritesItem favoritesItem = (FavoritesItem)node.getUserObject();
505
if(favoritesItem == null)
506
debug("favoritesItem is null");
508
//Map.ID id = favoritesItem.getID();
509
Map.ID id = (Map.ID)dataMap.get(favoritesItem);
511
debug("id name :"+id.id);
512
debug("target :"+target);
513
Map.ID itemID = null;
515
itemID = Map.ID.create(target,favorites.getModel().getHelpSet());
517
catch(BadIDException exp){
518
System.err.println("Not valid ID :"+target );
521
if(id.equals(itemID))
522
nodeFound.addElement(node);
532
* Collapses entry specified by id. If entry is empty collapses it's parent.
534
* @param target The target of entry
537
private void collapse(String target){
538
Enumeration nodes = findNodes(target).elements();
539
DefaultMutableTreeNode node = null;
540
debug("collapse called");
542
while(nodes.hasMoreElements()){
543
node = (DefaultMutableTreeNode)nodes.nextElement();
544
if(node.getChildCount() > 0){
545
TreeNode[] treeNode = node.getPath();
546
TreePath path = new TreePath(treeNode);
547
tree.collapsePath(path);
548
tree.collapseRow(tree.getRowForPath(path));
551
DefaultMutableTreeNode parent =(DefaultMutableTreeNode) node.getParent();
552
TreePath path = new TreePath(parent.getPath());
553
tree.collapseRow(tree.getRowForPath(path));
558
// Make all nodes visible
560
private void setVisibility(DefaultMutableTreeNode node) {
561
tree.expandPath(new TreePath(node.getPath()));
562
if (! node.isLeaf()) {
563
int max = node.getChildCount();
564
for (int i=0; i<max; i++) {
565
setVisibility((DefaultMutableTreeNode)node.getChildAt(i));
571
* Processes and idChanged event. In this navigator works with url too.
573
* @param e The HelpModelEvent
576
public void idChanged(HelpModelEvent e) {
577
debug("idChanged("+e+")");
581
contentTitle = e.getHistoryName();
582
URL nurl = e.getURL();
585
nURL = nurl.toExternalForm();
589
String currentURL = null;
590
String currentName = null;
591
FavoritesItem item = null;
593
HelpModel helpModel = favorites.getModel();
596
if (e.getSource() != helpModel) {
597
debug("Internal inconsistency!");
598
debug(" "+e.getSource()+" != "+helpModel);
599
throw new Error("Internal error");
602
TreePath s = tree.getSelectionPath();
604
Object o = s.getLastPathComponent();
605
// should require only a TreeNode
606
if (o instanceof DefaultMutableTreeNode) {
607
DefaultMutableTreeNode tn = (DefaultMutableTreeNode) o;
608
item = (FavoritesItem) tn.getUserObject();
610
currentID = (Map.ID) dataMap.get(item);
611
currentURL = item.getURLSpec();
612
currentName = item.getName();
614
if((currentName != null) && (currentName.equals(contentTitle))){
615
if(currentID != null)
616
if(currentID.equals(id))
618
if(currentURL != null)
619
if(currentURL.equals(nURL))
625
DefaultMutableTreeNode node = null;
627
node = findID(topNode, id);
629
node = findURL(topNode, nURL);
633
// node doesn't exist. Need to clear the selection.
634
tree.clearSelection();
637
TreePath path = new TreePath(node.getPath());
638
tree.expandPath(path);
639
tree.setSelectionPath(path);
640
tree.scrollPathToVisible(path);
643
private DefaultMutableTreeNode findID(DefaultMutableTreeNode node, ID id) {
644
debug("findID: ("+id+")");
645
debug(" node: "+node);
651
FavoritesItem item = (FavoritesItem) node.getUserObject();
652
Map.ID itemID = (Map.ID) dataMap.get(item);
653
if (itemID != null) {
655
debug(" testID: "+testID);
656
if (testID != null && testID.equals(id)) {
660
int size = node.getChildCount();
661
for (int i=0; i<size ; i++) {
662
DefaultMutableTreeNode tmp =
663
(DefaultMutableTreeNode) node.getChildAt(i);
664
DefaultMutableTreeNode test = findID(tmp, id);
672
* Finds the subnode with certain url and name
674
private DefaultMutableTreeNode findURL(DefaultMutableTreeNode node, String urlSpec){
675
debug(" findURL: "+ urlSpec);
680
for(Enumeration children = node.children(); children.hasMoreElements();){
681
DefaultMutableTreeNode child = (DefaultMutableTreeNode)children.nextElement();
682
FavoritesItem childItem = (FavoritesItem) child.getUserObject();
683
String childName = childItem.getName();
684
String childURL = childItem.getURLSpec();
685
if(urlSpec.equals(childURL))
688
findURL(child,urlSpec);
694
* Finds the subnode with certain id and name
697
public void valueChanged(TreeSelectionEvent e) {
699
selectedTreePath = e.getNewLeadSelectionPath();
700
if (selectedTreePath == null) {
705
(FavoritesNode)selectedTreePath.getLastPathComponent();
706
if (selectedNode != null){
707
selectedItem = (FavoritesItem)selectedNode.getUserObject();
708
selectedID = (Map.ID)dataMap.get(selectedItem);
711
HelpModel helpModel = favorites.getModel();
712
HelpSet hs = helpModel.getHelpSet();
714
debug("ValueChanged: "+e);
715
debug(" model: "+helpModel);
716
if(helpModel == null)
718
// send selected items into navigator
719
TreeItem[] items = null;
720
TreePath[] paths = tree.getSelectionPaths();
722
removeAction.setEnabled(true);
723
items = new TreeItem[paths.length];
724
for (int i = 0; i < paths.length; i++) {
725
if (paths[i] != null) {
726
DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[i].getLastPathComponent();
727
FavoritesItem favItem = (FavoritesItem) node.getUserObject();
728
// need to normalize this item so it looks like a real TreeItem
730
String target = favItem.getTarget();
731
if (target != null) {
732
ID id = Map.ID.create(favItem.getTarget(), hs);
735
} catch (BadIDException ee) {
737
items[i] = (TreeItem) favItem;
741
removeAction.setEnabled(false);
742
pasteAction.setEnabled(false);
743
pasteMI.setEnabled(false);
746
favorites.setSelectedItems(items);
748
// change current id only if one item is selected
749
if (items != null && items.length == 1) {
750
FavoritesItem item = (FavoritesItem) items[0];
752
Map.ID itemID = (Map.ID) dataMap.get(item);
754
debug("itemID: "+itemID);
756
// helpmodel.setCurrentID(item.getID());
757
helpModel.setCurrentID(itemID, item.getName(), (JHelpNavigator)favorites);
758
} catch (InvalidHelpSetContextException ex) {
759
System.err.println("BadID: "+item.getID());
763
if(item.getURLSpec() != null){
765
URL url = new URL(item.getURLSpec());
766
helpModel.setCurrentURL(url,item.getName(),(JHelpNavigator)favorites);
767
}catch(MalformedURLException ep){
768
System.err.println(ep);
776
public void propertyChange(PropertyChangeEvent event) {
777
debug("propertyChange: " + event.getSource() + " " +
778
event.getPropertyName());
780
if (event.getSource() == favorites) {
781
String changeName = event.getPropertyName();
782
if (changeName.equals("helpModel")) {
783
debug("model changed");
786
} else if (changeName.equals("font")) {
787
debug("Font change");
788
Font newFont = (Font)event.getNewValue();
789
tree.setFont(newFont);
790
RepaintManager.currentManager(tree).markCompletelyDirty(tree);
791
} else if(changeName.equals("expand")){
792
debug("Expand change");
793
expand((String)event.getNewValue());
794
} else if(changeName.equals("collapse")){
795
debug("Collapse change");
796
collapse((String)event.getNewValue());
797
} else if(changeName.equals("navigatorChange")){
798
debug("Navigator change");
799
tree.clearSelection();
802
// changes to UI property?
804
}else if (favorites != null){
805
if (event.getSource() == favorites.getModel()) {
806
String changeName = event.getPropertyName();
807
if( changeName.equals("helpSet")) {
814
public void helpSetAdded(HelpSetEvent ev){
815
debug("HelpSet added");
819
public void helpSetRemoved(HelpSetEvent ev){
820
debug("HelpSet removed");
825
* Saves favorites to the file
827
public void saveFavorites(){
828
FavoritesView view = (FavoritesView)favorites.getNavigatorView();
829
view.saveFavorites(rootNode);
832
public void treeStructureChanged(javax.swing.event.TreeModelEvent treeModelEvent) {
833
debug("tree structure changed");
836
public void treeNodesInserted(javax.swing.event.TreeModelEvent treeModelEvent) {
837
debug("node inserted");
839
FavoritesNode parent = (FavoritesNode) treeModelEvent.getTreePath().getLastPathComponent();
840
Object nodes[] = treeModelEvent.getChildren();
841
int[] indices = treeModelEvent.getChildIndices();
842
int firstIndex = indices[0];
843
debug("index first "+firstIndex);
844
int lastIndex = (indices.length + firstIndex) - 1;
845
FavoritesNode rootParent = (FavoritesNode)connections.get(parent);
846
if(rootParent == null)
847
rootParent = rootNode;
849
debug("root parent "+rootParent);
850
//nodes were inserted into first place in parent
852
if(rootParent.getChildCount() == 0)
855
for(Enumeration en = rootParent.children(); en.hasMoreElements();){
856
FavoritesNode node = (FavoritesNode)en.nextElement();
857
if(node.isVisible()){
858
debug("is visible : "+node);
859
place = rootParent.getIndex(node);
864
//insert nodes in node place
866
for(int j = nodes.length - 1; j >= 0; j--){
867
FavoritesNode copy = ((FavoritesNode) nodes[j]).getDeepCopy();
868
rootParent.insert((DefaultMutableTreeNode)copy,place);
869
connections.put((FavoritesNode)nodes[j],copy);
873
}else if (firstIndex > 0){
874
//find what is before
875
FavoritesNode nodeBefore = (FavoritesNode)parent.getChildAt(firstIndex - 1);
876
FavoritesNode rootNode = (FavoritesNode)connections.get(nodeBefore);
877
place = rootParent.getIndex(rootNode)+1;
878
for(int k = nodes.length - 1; k >= 0; k--){
879
FavoritesNode copyNode = ((FavoritesNode)nodes[k]).getDeepCopy();
880
rootParent.insert((DefaultMutableTreeNode)copyNode,place);
881
connections.put((FavoritesNode)nodes[k],copyNode);
886
public void treeNodesRemoved(javax.swing.event.TreeModelEvent treeModelEvent) {
887
debug("nodes removed");
888
Object nodes[] = treeModelEvent.getChildren();
889
for(int i = 0; i < nodes.length; i++){
890
FavoritesNode originalNode = (FavoritesNode)nodes[i];
891
FavoritesNode remNode = (FavoritesNode)connections.get(originalNode);
893
remNode.removeFromParent();
897
public void treeNodesChanged(javax.swing.event.TreeModelEvent treeModelEvent) {
898
debug("node changed");
899
TreeCellEditor editor = tree.getCellEditor();
900
Object newName = editor.getCellEditorValue();
902
if((newName instanceof String) && selectedItem != null){
905
Map.ID itemID = (Map.ID)dataMap.get(selectedItem);
906
//remove old data from dataMap
907
dataMap.remove(selectedItem);
908
FavoritesNode fromRootNode = (FavoritesNode)connections.get(getSelectedNode());
909
//change name of old Item
910
selectedItem.setName((String)newName);
911
selectedNode.setUserObject(selectedItem);
912
if(fromRootNode != null){
913
FavoritesItem fromRootItem = (FavoritesItem) fromRootNode.getUserObject();
914
fromRootItem.setName((String)newName);
916
//put data to the dataMap
917
dataMap.put(selectedItem,itemID);
923
* Returns the selected node
926
public FavoritesNode getSelectedNode() {
934
public class AddAction extends AbstractAction{
937
super(HelpUtilities.getString(locale, "favorites.add"), SwingHelpUtilities.getImageIcon(BasicFavoritesNavigatorUI.class,"images/addToFav.gif"));
940
public void actionPerformed(ActionEvent ev){
943
String target = null;
944
String hstitle = null;
945
FavoritesItem favorite = null;
946
HelpModel helpModel = favorites.getModel();
947
HelpSet hs = helpModel.getHelpSet();
949
Map.ID ID = helpModel.getCurrentID();
950
URL url = helpModel.getCurrentURL();
953
hstitle = ID.hs.getTitle();
956
// need to determine the helpset title by looking at the
957
// starting URL of the
958
hstitle = getHelpSetTitle(hs, url);
959
if (hstitle == null) {
960
hstitle= hs.getTitle();
963
String urlSpec = null;
965
urlSpec = url.toExternalForm();
967
favorite = new FavoritesItem(contentTitle, target, urlSpec,
968
hstitle, Locale.getDefault());
969
dataMap.put(favorite,ID);
971
FavoritesNode node = new FavoritesNode(favorite);
972
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
973
model.insertNodeInto(node, topNode, topNode.getChildCount());
975
TreePath path = new TreePath(node.getPath());
976
tree.expandPath(path);
977
tree.setSelectionPath(path);
978
tree.scrollPathToVisible(path);
984
* retuns a helpset title for a given url
986
private String getHelpSetTitle(HelpSet hs, URL url) {
987
URL baseURL = hs.getHelpSetURL();
988
String urlExternal = url.toExternalForm();
989
String baseURLExternal = baseURL.toExternalForm();
990
if (urlExternal.startsWith(baseURLExternal)) {
991
return hs.getTitle();
993
Enumeration helpsets = hs.getHelpSets();
995
while (helpsets.hasMoreElements()) {
996
HelpSet testHS = (HelpSet) helpsets.nextElement();
997
title = getHelpSetTitle(testHS, url);
1008
* Returns the Add action
1011
public Action getAddAction(){
1016
* RemoveAction class.
1019
public class RemoveAction extends AbstractAction{
1021
public RemoveAction(){
1022
super(HelpUtilities.getString(locale, "favorites.remove"), SwingHelpUtilities.getImageIcon(BasicFavoritesNavigatorUI.class,"images/remove.gif"));
1025
public void actionPerformed(ActionEvent ev){
1027
DefaultMutableTreeNode node = null;
1028
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
1029
TreePath[] paths = tree.getSelectionPaths();
1030
for(int i = 0 ; i < paths.length; i++){
1031
if(paths[i] != null){
1032
node = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
1033
model.removeNodeFromParent(node);
1035
FavoritesItem item = (FavoritesItem)node.getUserObject();
1036
dataMap.remove(item);
1045
* Returns the RemoveAction object
1047
public Action getRemoveAction(){
1048
return removeAction;
1052
* FolderAction class.
1055
public class FolderAction extends AbstractAction{
1057
public FolderAction(){
1058
super(HelpUtilities.getString(locale, "favorites.folder"), SwingHelpUtilities.getImageIcon(BasicFavoritesNavigatorUI.class,"images/folder.gif"));
1061
public void actionPerformed(ActionEvent ev){
1062
FavoritesItem favoriteFolder = new FavoritesItem(HelpUtilities.getString(locale, "favorites.folder"));
1063
favoriteFolder.setAsFolder();
1064
FavoritesNode node = new FavoritesNode(favoriteFolder);
1065
TreePath nodePath = tree.getSelectionPath();
1066
TreeNode parent = null;
1067
if(nodePath == null)
1070
FavoritesNode selNode = (FavoritesNode)nodePath.getLastPathComponent();
1071
parent = selNode.getParent();
1073
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
1074
model.insertNodeInto(node,(DefaultMutableTreeNode)parent,parent.getChildCount());
1075
TreePath path = new TreePath(node.getPath());
1076
tree.expandPath(path);
1077
tree.setSelectionPath(path);
1078
tree.scrollPathToVisible(path);
1084
* Returns the FolderAction object
1086
public Action getFolderAction(){
1087
return folderAction;
1093
public class CutAction extends AbstractAction{
1096
super(HelpUtilities.getString(locale,"favorites.cut"));
1099
public void actionPerformed(ActionEvent ev){
1101
DefaultMutableTreeNode node = null;
1102
nodeClipboard.removeAllElements();
1103
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
1104
TreePath[] paths = tree.getSelectionPaths();
1105
for(int i = 0 ; i < paths.length; i++){
1106
if(paths[i] != null){
1107
node = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
1109
FavoritesItem item = (FavoritesItem)node.getUserObject();
1110
nodeClipboard.add(node);
1112
model.removeNodeFromParent(node);
1116
pasteMI.setEnabled(true);
1121
* Returns the CutAction object
1123
public Action getCutAction(){
1128
* PasteAction class.
1130
public class PasteAction extends AbstractAction{
1132
public PasteAction(){
1133
super(HelpUtilities.getString(locale,"favorites.paste"));
1136
public void actionPerformed(ActionEvent ev){
1139
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
1140
TreePath path = tree.getSelectionPath();
1141
FavoritesNode node = (FavoritesNode)path.getLastPathComponent();
1143
if(node.getAllowsChildren()){
1144
for(Enumeration nodes = nodeClipboard.elements(); nodes.hasMoreElements();){
1145
model.insertNodeInto((DefaultMutableTreeNode)nodes.nextElement(),(DefaultMutableTreeNode)node,node.getChildCount());
1149
DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
1152
int index = parent.getIndex(node);
1153
for(Enumeration en = nodeClipboard.elements(); en.hasMoreElements();index++){
1154
model.insertNodeInto((DefaultMutableTreeNode)en.nextElement(),parent,index);
1164
* Returns the PasteAction object
1166
public Action getPasteAction(){
1173
public class CopyAction extends AbstractAction{
1175
public CopyAction(){
1176
super(HelpUtilities.getString(locale,"favorites.copy"));
1179
public void actionPerformed(ActionEvent ev){
1182
DefaultMutableTreeNode node = null;
1183
nodeClipboard.removeAllElements();
1184
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
1185
TreePath[] paths = tree.getSelectionPaths();
1186
for(int i = 0 ; i < paths.length; i++){
1187
if(paths[i] != null){
1188
node = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
1190
FavoritesItem item = (FavoritesItem)node.getUserObject();
1191
FavoritesNode copy = ((FavoritesNode)node).getDeepCopy();
1192
nodeClipboard.add(copy);
1197
pasteMI.setEnabled(true);
1202
* Returns the CopyAction Object
1204
public Action getCopyAction(){
1209
* PopupListener class
1211
public class PopupListener extends MouseAdapter{
1213
public void mousePressed(MouseEvent e){
1217
public void mouseReleased(MouseEvent e){
1221
private void maybeShowPopup(MouseEvent e) {
1222
TreePath path = tree.getSelectionPath();
1223
TreePath clickPath = tree.getPathForLocation(e.getX(),e.getY());
1224
if (e.isPopupTrigger()) {
1225
if (path != null && path.equals(clickPath)) {
1226
separatorMI.setVisible(true);
1227
cutMI.setVisible(true);
1228
copyMI.setVisible(true);
1229
pasteMI.setVisible(true);
1230
removeMI.setVisible(true);
1232
separatorMI.setVisible(false);
1233
cutMI.setVisible(false);
1234
copyMI.setVisible(false);
1235
pasteMI.setVisible(false);
1236
removeMI.setVisible(false);
1238
popup.show(e.getComponent(), e.getX(), e.getY());
1244
* Class for JTree supported D&D features.
1246
public class FavoritesTree extends JTree implements DragGestureListener, DropTargetListener, DragSourceListener {
1248
protected Map.ID selectedID = null;
1250
private DragSource dragSource = null;
1251
private DragSourceContext dragSourceContext = null;
1252
private Point cursorLocation = null;
1253
private TreePath pathSource;
1254
private BufferedImage ghostImage;
1255
private Point offset = new Point();
1256
private Point ptLast = new Point();
1257
private Rectangle2D ghostRect = new Rectangle2D.Float();
1260
* Data object of selected FavoritesNode
1262
private Map.ID hashCandidate;
1266
private Cursor dndCursor;
1269
* Creates FavoritesTree
1271
* @param root The root node of the tree
1273
public FavoritesTree(FavoritesNode root) {
1278
dragSource = DragSource.getDefaultDragSource();
1280
DragGestureRecognizer dgr =
1281
dragSource.createDefaultDragGestureRecognizer(this,DnDConstants.ACTION_COPY_OR_MOVE,this);
1283
dgr.setSourceActions((dgr.getSourceActions()) &~ (InputEvent.BUTTON3_MASK));
1285
DropTarget dropTarget = new DropTarget(this, this);
1287
Toolkit tk = this.getToolkit();
1288
if(tk.getBestCursorSize(16,16).equals(new Dimension(64,64)))
1289
dndCursor = (Cursor) UIManager.get("HelpDnDCursor");
1290
if(dndCursor == null)
1291
debug("cursor is null");
1292
putClientProperty("JTree.lineStyle", "None");
1295
public void dragGestureRecognized(DragGestureEvent e) {
1296
FavoritesNode dragNode = getSelectedNode();
1297
if (dragNode != null) {
1299
ghostImage = createGhostImage(e);
1300
Transferable transferable = (Transferable) dragNode.getUserObject();
1301
hashCandidate = (Map.ID)dataMap.get((FavoritesItem)transferable);
1303
Cursor cursor = DragSource.DefaultCopyDrop;
1304
int action = e.getDragAction();
1305
if(action == DnDConstants.ACTION_MOVE){
1306
debug("action move");
1307
cursor = DragSource.DefaultMoveDrop;
1310
dragSource.startDrag(e,dndCursor,ghostImage,new Point(5,5), transferable,this);
1314
private BufferedImage createGhostImage(DragGestureEvent e){
1315
debug("createGhostImage");
1317
BufferedImage ghostImage = null;
1318
Point ptDragOrigin = e.getDragOrigin();
1319
TreePath path = getPathForLocation(ptDragOrigin.x, ptDragOrigin.y);
1323
Rectangle raPath = getPathBounds(path);
1324
offset.setLocation(ptDragOrigin.x-raPath.x, ptDragOrigin.y-raPath.y);
1326
JLabel lbl = (JLabel) getCellRenderer().getTreeCellRendererComponent
1327
(this,path.getLastPathComponent(),false, isExpanded(path),getModel().isLeaf(path.getLastPathComponent()),0,false);
1328
lbl.setSize((int)raPath.getWidth(), (int)raPath.getHeight());
1330
ghostImage = new BufferedImage((int)raPath.getWidth(), (int)raPath.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
1331
Graphics2D g2 = ghostImage.createGraphics();
1333
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0.5f));
1336
Icon icon = lbl.getIcon();
1337
int nStartOfText = (icon == null) ? 0 : icon.getIconWidth()+lbl.getIconTextGap();
1338
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 0.5f));
1339
g2.setPaint(new GradientPaint(nStartOfText, 0, SystemColor.controlShadow,
1340
getWidth(), 0, new Color(255,255,255,0)));
1341
g2.fillRect(nStartOfText, 0, getWidth(), ghostImage.getHeight());
1348
public void dragDropEnd(DragSourceDropEvent dsde) {
1349
debug("dragDropEnd");
1352
public void dragEnter(DragSourceDragEvent dsde) {
1357
public void dragOver(DragSourceDragEvent dsde) {
1362
public void dropActionChanged(DragSourceDragEvent dsde) {
1363
debug("dropActionChanged");
1367
public void dragExit(DragSourceEvent dsde) {
1372
* Sets appropriate cursor type according to event
1374
* @param dsde DrageSourceDrageEvent
1376
private void setCursor(DragSourceDragEvent dsde) {
1378
if (cursorLocation == null) return;
1380
TreePath destinationPath =
1381
getPathForLocation(cursorLocation.x, cursorLocation.y);
1383
DragSourceContext dsc = dsde.getDragSourceContext();
1385
if (testDropTarget(destinationPath, selectedTreePath) == null){
1386
dsc.setCursor(DragSource.DefaultCopyDrop);
1390
dsc.setCursor(DragSource.DefaultCopyNoDrop);
1394
public void drop(DropTargetDropEvent e) {
1397
Transferable tr = e.getTransferable();
1399
if (!tr.isDataFlavorSupported( FavoritesItem.FAVORITES_FLAVOR)){
1400
debug("drop rejected not data flavor");
1404
FavoritesItem childInfo = (FavoritesItem) tr.getTransferData( FavoritesItem.FAVORITES_FLAVOR );
1405
Point loc = e.getLocation();
1406
TreePath destinationPath = getPathForLocation(loc.x, loc.y);
1408
final String msg = testDropTarget(destinationPath, selectedTreePath);
1411
debug("Error : "+msg);
1415
FavoritesNode newParent =
1416
(FavoritesNode) destinationPath.getLastPathComponent();
1418
debug("new parent: "+newParent);
1419
//get old parent node
1420
FavoritesNode oldParent = (FavoritesNode) getSelectedNode().getParent();
1422
FavoritesNode selNode = (FavoritesNode) getSelectedNode();
1423
FavoritesItem selItem = (FavoritesItem) selNode.getUserObject();
1425
FavoritesNode newNode = selNode.getDeepCopy();
1427
int action = e.getDropAction();
1428
boolean copyAction = (action == DnDConstants.ACTION_COPY);
1429
debug("copy action: "+ copyAction);
1430
//make new child node
1431
FavoritesNode newChild = new FavoritesNode(childInfo);
1432
debug("new child: "+newChild);
1435
FavoritesNode fromRootNode = (FavoritesNode)connections.get(getSelectedNode());
1436
if(fromRootNode != null)
1437
fromRootNode.removeFromParent();
1438
oldParent.remove(getSelectedNode());
1440
DefaultTreeModel model = (DefaultTreeModel)getModel();
1442
if (!newParent.getAllowsChildren()){
1443
TreeNode parent = newParent.getParent();
1445
int index = parent.getIndex(newParent);
1446
model.insertNodeInto(newNode,(DefaultMutableTreeNode)parent, index+1);
1449
model.insertNodeInto(newNode,newParent,newParent.getChildCount());
1452
e.acceptDrop(DnDConstants.ACTION_COPY);
1453
else e.acceptDrop(DnDConstants.ACTION_MOVE);
1455
catch (java.lang.IllegalStateException ils) {
1456
debug("drop ejected");
1460
e.getDropTargetContext().dropComplete(true);
1462
FavoritesItem newItem = (FavoritesItem) newNode.getUserObject();
1463
dataMap.put(newItem, hashCandidate);
1464
DefaultTreeModel model = (DefaultTreeModel) getModel();
1465
model.reload(oldParent);
1466
model.reload(newParent);
1467
TreePath parentPath = new TreePath(newParent.getPath());
1468
expandPath(parentPath);
1471
catch (IOException io) {
1473
debug("drop rejected" + io);
1475
catch (UnsupportedFlavorException ufe) {
1477
debug("drop rejected: "+ ufe);
1481
public void dragEnter(DropTargetDragEvent e) {
1484
public void dragExit(DropTargetEvent e) {
1485
if (!DragSource.isDragImageSupported()) {
1486
repaint(ghostRect.getBounds());
1490
public void dragOver(DropTargetDragEvent e) {
1492
Point pt = e.getLocation();
1493
if (pt.equals(ptLast))
1498
Point cursorLocationBis = e.getLocation();
1499
TreePath destinationPath = getPathForLocation(cursorLocationBis.x, cursorLocationBis.y);
1501
Graphics2D g2 = (Graphics2D) getGraphics();
1503
if(testDropTarget(destinationPath, selectedTreePath) == null){
1504
e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
1505
if (!DragSource.isDragImageSupported()) {
1506
paintImmediately(ghostRect.getBounds());
1507
ghostRect.setRect(pt.x - offset.x, pt.y - offset.y, ghostImage.getWidth(), ghostImage.getHeight());
1508
g2.drawImage(ghostImage, AffineTransform.getTranslateInstance(ghostRect.getX(), ghostRect.getY()), null);
1514
public void dropActionChanged(DropTargetDragEvent e) {
1518
* Tests whether drop location is valid or not
1520
* @param destination The destination path
1521
* @param dropper The path for the node to be dropped
1522
* @return Null if no problems, otherwise an explanation
1524
private String testDropTarget(TreePath destination, TreePath dropper) {
1526
boolean destinationPathIsNull = destination == null;
1527
if (destinationPathIsNull){
1528
return "Invalid drop location.";
1529
//remove ghostLikeImage
1532
FavoritesNode node = (FavoritesNode) destination.getLastPathComponent();
1534
if (destination.equals(dropper))
1535
return "Destination cannot be same as source";
1538
if ( dropper.isDescendant(destination))
1539
return "Destination node cannot be a descendant.";
1542
if ( dropper.getParentPath().equals(destination))
1543
return "Destination node cannot be a parent.";
1550
* For printf debugging.
1552
private static boolean debug = false;
1553
private static void debug(String str) {
1555
System.out.println("BasicFavoritesNavigatorUI: " + str);