001: /*
002: This library is free software; you can redistribute it and/or
003: modify it under the terms of the GNU General Public
004: License as published by the Free Software Foundation; either
005: version 2 of the license, or (at your option) any later version.
006: */
007:
008: package org.gjt.jclasslib.browser.config.classpath;
009:
010: import org.gjt.jclasslib.browser.BrowserMDIFrame;
011: import org.gjt.jclasslib.util.GUIHelper;
012: import org.gjt.jclasslib.util.ProgressDialog;
013:
014: import javax.swing.*;
015: import javax.swing.event.TreeSelectionEvent;
016: import javax.swing.event.TreeSelectionListener;
017: import javax.swing.tree.DefaultTreeModel;
018: import javax.swing.tree.TreePath;
019: import java.awt.*;
020: import java.awt.event.*;
021:
022: /**
023: Classpath browser that shows a tree of the contents of a
024: <tt>ClasspathComponent</tt>.
025:
026: @author <a href="mailto:jclasslib@ej-technologies.com">Ingo Kegel</a>
027: @version $Revision: 1.1 $ $Date: 2003/08/18 08:10:15 $
028: */
029: public class ClasspathBrowser extends JDialog implements
030: ActionListener, ClasspathChangeListener {
031:
032: private static final int DIALOG_WIDTH = 450;
033: private static final int DIALOG_HEIGHT = 450;
034:
035: private BrowserMDIFrame frame;
036: private ClasspathComponent classpathComponent;
037:
038: private JLabel lblTitle;
039: private JTree tree;
040: private JScrollPane scpTree;
041: private JButton btnSetup;
042: private JButton btnSync;
043: private JButton btnOk;
044: private JButton btnCancel;
045:
046: private ProgressDialog progressDialog;
047: private boolean resetOnNextMerge;
048: private boolean needsMerge;
049:
050: private String selectedClassName;
051:
052: /**
053: * Constructor.
054: * @param frame the parent frame.
055: * @param classpathComponent the classpath component to display initially.
056: * @param title the disalog title.
057: * @param setupVisible if the <i>setup classpath</i> button should be visible.
058: */
059: public ClasspathBrowser(BrowserMDIFrame frame,
060: ClasspathComponent classpathComponent, String title,
061: boolean setupVisible) {
062: super (frame);
063: this .frame = frame;
064:
065: setClasspathComponent(classpathComponent);
066: setupControls(title, setupVisible);
067: setupComponent();
068: setupEventHandlers();
069: }
070:
071: public void actionPerformed(ActionEvent event) {
072: Object source = event.getSource();
073: if (source == btnCancel) {
074: doCancel();
075: } else if (source == btnOk) {
076: doOk();
077: } else if (source == btnSetup) {
078: doSetup();
079: } else if (source == btnSync) {
080: doSync(true);
081: }
082:
083: }
084:
085: public void classpathChanged(ClasspathChangeEvent event) {
086: needsMerge = true;
087: if (event.isRemoval()) {
088: resetOnNextMerge = true;
089: }
090: }
091:
092: public void setVisible(boolean visible) {
093: if (visible) {
094: selectedClassName = null;
095: }
096: super .setVisible(visible);
097: }
098:
099: /**
100: * Get the name of the selected class.
101: * @return the name
102: */
103: public String getSelectedClassName() {
104: return selectedClassName;
105: }
106:
107: /**
108: * Set the new classpath component to be displayed by this dialog.
109: * The previous content will be cleared.
110: * @param classpathComponent the new classpath component.
111: */
112: public void setClasspathComponent(
113: ClasspathComponent classpathComponent) {
114: if (this .classpathComponent != null) {
115: this .classpathComponent.removeClasspathChangeListener(this );
116: }
117: this .classpathComponent = classpathComponent;
118: if (classpathComponent != null) {
119: classpathComponent.addClasspathChangeListener(this );
120: }
121: resetOnNextMerge = true;
122: needsMerge = true;
123: clear();
124: }
125:
126: /**
127: * Clear the current contents of the dialog. The tree will not be synchronized
128: * automatically on the next <tt>setVisible</tt>.
129: */
130: public void clear() {
131: if (tree != null) {
132: tree.setModel(new DefaultTreeModel(new ClassTreeNode()));
133: }
134: }
135:
136: private void setupControls(String title, boolean setupVisible) {
137:
138: lblTitle = new JLabel(title);
139: tree = new JTree(new ClassTreeNode());
140: tree.setRootVisible(false);
141: tree.setShowsRootHandles(true);
142: tree.putClientProperty("JTree.lineStyle", "Angled");
143: scpTree = new JScrollPane(tree);
144:
145: btnSetup = new JButton("Setup classpath");
146: btnSetup.setVisible(setupVisible);
147: btnSync = new JButton("Synchronize");
148: btnOk = new JButton("Ok");
149: btnOk.setEnabled(false);
150: btnCancel = new JButton("Cancel");
151: btnOk.setPreferredSize(btnCancel.getPreferredSize());
152:
153: progressDialog = new ProgressDialog(this , null,
154: "Scanning classpath ...");
155:
156: }
157:
158: private void setupComponent() {
159:
160: Container contentPane = getContentPane();
161: contentPane.setLayout(new GridBagLayout());
162: GridBagConstraints gc = new GridBagConstraints();
163: gc.gridx = 0;
164: gc.gridy = 0;
165: gc.insets = new Insets(5, 5, 0, 5);
166: gc.weightx = 1;
167: gc.anchor = GridBagConstraints.NORTHWEST;
168: contentPane.add(lblTitle, gc);
169: gc.gridy++;
170:
171: gc.weighty = 1;
172: gc.insets.top = 0;
173: gc.fill = GridBagConstraints.BOTH;
174: contentPane.add(scpTree, gc);
175: gc.gridy++;
176: gc.fill = GridBagConstraints.HORIZONTAL;
177: gc.weighty = 0;
178: gc.insets.top = 3;
179: gc.insets.bottom = 5;
180: contentPane.add(createButtonBox(), gc);
181: getRootPane().setDefaultButton(btnOk);
182:
183: setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
184: setModal(true);
185: setTitle("Choose a class");
186: GUIHelper.centerOnParentWindow(this , getOwner());
187: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
188:
189: }
190:
191: private Box createButtonBox() {
192:
193: Box box = Box.createHorizontalBox();
194: box.add(btnSetup);
195: box.add(btnSync);
196: box.add(Box.createHorizontalGlue());
197: box.add(btnOk);
198: box.add(btnCancel);
199:
200: return box;
201: }
202:
203: private void setupEventHandlers() {
204:
205: btnCancel.addActionListener(this );
206: btnOk.addActionListener(this );
207: btnSetup.addActionListener(this );
208: btnSync.addActionListener(this );
209:
210: addWindowListener(new WindowAdapter() {
211: public void windowClosing(WindowEvent event) {
212: doCancel();
213: }
214: });
215: KeyStroke keyStroke = KeyStroke.getKeyStroke(
216: KeyEvent.VK_ESCAPE, 0);
217: Object key = new Object();
218:
219: JComponent contentPane = (JComponent) getContentPane();
220: contentPane.getInputMap(
221: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
222: keyStroke, key);
223: contentPane.getActionMap().put(key, new AbstractAction() {
224: public void actionPerformed(ActionEvent event) {
225: doCancel();
226: }
227: });
228:
229: addComponentListener(new ComponentAdapter() {
230: public void componentShown(ComponentEvent event) {
231: conditionalUpdate();
232: }
233: });
234:
235: tree.addTreeSelectionListener(new TreeSelectionListener() {
236: public void valueChanged(TreeSelectionEvent event) {
237: checkTreeSelection();
238: }
239: });
240:
241: tree.addMouseListener(new MouseAdapter() {
242: public void mouseClicked(MouseEvent event) {
243: if (event.getClickCount() == 2
244: && isValidDoubleClickPath(event)) {
245: doOk();
246: }
247: }
248: });
249:
250: }
251:
252: private void conditionalUpdate() {
253: if (needsMerge) {
254: doSync(resetOnNextMerge);
255: }
256: }
257:
258: private boolean isValidDoubleClickPath(MouseEvent event) {
259:
260: TreePath locationPath = tree.getPathForLocation(event.getX(),
261: event.getY());
262: TreePath selectionPath = tree.getSelectionPath();
263: if (selectionPath == null || locationPath == null
264: || !selectionPath.equals(locationPath)) {
265: return false;
266: }
267: ClassTreeNode lastPathComponent = (ClassTreeNode) selectionPath
268: .getLastPathComponent();
269: return !lastPathComponent.isPackageNode();
270: }
271:
272: private void checkTreeSelection() {
273:
274: TreePath selectionPath = tree.getSelectionPath();
275: boolean enabled = false;
276: if (selectionPath != null) {
277: ClassTreeNode classTreeNode = (ClassTreeNode) selectionPath
278: .getLastPathComponent();
279: enabled = !classTreeNode.isPackageNode();
280: }
281: btnOk.setEnabled(enabled);
282: }
283:
284: private void doOk() {
285:
286: StringBuffer buffer = new StringBuffer();
287: TreePath selectionPath = tree.getSelectionPath();
288: for (int i = 1; i < selectionPath.getPathCount(); i++) {
289: if (buffer.length() > 0) {
290: buffer.append('/');
291: }
292: buffer.append(selectionPath.getPathComponent(i).toString());
293: }
294: selectedClassName = buffer.toString();
295:
296: setVisible(false);
297: }
298:
299: private void doCancel() {
300: setVisible(false);
301: }
302:
303: private void doSetup() {
304: frame.getActionSetupClasspath().actionPerformed(
305: new ActionEvent(this , 0, null));
306: conditionalUpdate();
307: }
308:
309: private void doSync(final boolean reset) {
310:
311: final DefaultTreeModel model = reset ? new DefaultTreeModel(
312: new ClassTreeNode()) : (DefaultTreeModel) tree
313: .getModel();
314: Runnable mergeTask = new Runnable() {
315: public void run() {
316: if (classpathComponent != null) {
317: classpathComponent.mergeClassesIntoTree(model,
318: reset);
319: }
320: }
321: };
322:
323: progressDialog.setRunnable(mergeTask);
324: progressDialog.setVisible(true);
325:
326: if (reset) {
327: tree.setModel(model);
328: }
329: tree.expandPath(new TreePath(model.getRoot()));
330: resetOnNextMerge = false;
331: needsMerge = false;
332: }
333:
334: }
|