001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.ui;
038:
039: import java.util.List;
040: import java.util.ArrayList;
041: import java.util.LinkedList;
042: import java.util.Enumeration;
043: import java.io.*;
044:
045: import javax.swing.*;
046: import javax.swing.event.*;
047: import javax.swing.table.*;
048: import javax.swing.text.*;
049: import javax.swing.plaf.*;
050: import javax.swing.plaf.basic.BasicToolTipUI;
051: import java.awt.event.*;
052: import java.awt.font.*;
053: import java.awt.*;
054:
055: import edu.rice.cs.drjava.config.OptionConstants;
056: import edu.rice.cs.drjava.model.DocumentRegion;
057: import edu.rice.cs.drjava.model.SingleDisplayModel;
058: import edu.rice.cs.drjava.model.debug.*;
059: import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
060: import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException;
061: import edu.rice.cs.drjava.config.*;
062: import edu.rice.cs.drjava.DrJava;
063: import edu.rice.cs.util.swing.Utilities;
064: import edu.rice.cs.util.UnexpectedException;
065: import edu.rice.cs.util.StringOps;
066: import edu.rice.cs.util.swing.RightClickMouseAdapter;
067:
068: /**
069: * Panel for displaying regions in a list sorted by time of creation.
070: * This class is a swing view class and hence should only be accessed from the event-handling thread.
071: * @version $Id$
072: */
073: public abstract class RegionsListPanel<R extends DocumentRegion>
074: extends TabbedPanel {
075: protected JPanel _leftPane;
076:
077: protected JList _list;
078: protected DefaultListModel _listModel;
079: protected String _title;
080:
081: protected final SingleDisplayModel _model;
082: protected final MainFrame _frame;
083:
084: protected JPanel _buttonPanel;
085:
086: /** Constructs a new panel to display regions in a list.
087: * This is swing view class and hence should only be accessed from the event-handling thread.
088: * @param frame the MainFrame
089: * @param title title of the pane
090: */
091: public RegionsListPanel(MainFrame frame, String title) {
092: super (frame, title);
093: _title = title;
094: this .setLayout(new BorderLayout());
095:
096: _frame = frame;
097: _model = frame.getModel();
098:
099: this .removeAll(); // override the behavior of TabbedPanel
100:
101: // remake closePanel
102: _closePanel = new JPanel(new BorderLayout());
103: _closePanel.add(_closeButton, BorderLayout.NORTH);
104:
105: _leftPane = new JPanel(new BorderLayout());
106: _setupRegionList();
107:
108: this .add(_leftPane, BorderLayout.CENTER);
109:
110: _buttonPanel = new JPanel(new BorderLayout());
111: _setupButtonPanel();
112: this .add(_buttonPanel, BorderLayout.EAST);
113: updateButtons();
114:
115: // Setup the color listeners.
116: _setColors(_list);
117:
118: _list.addMouseListener(new RegionMouseAdapter());
119: }
120:
121: /** Quick helper for setting up color listeners. */
122: private static void _setColors(Component c) {
123: new ForegroundColorListener(c);
124: new BackgroundColorListener(c);
125: }
126:
127: /** Close the pane. */
128: protected void _close() {
129: super ._close();
130: updateButtons();
131: }
132:
133: /** Creates the region list. */
134: private void _setupRegionList() {
135: _listModel = new DefaultListModel();
136: _list = new JList(_listModel) {
137: public String getToolTipText(MouseEvent evt) {
138: // Get item
139: int index = locationToIndex(evt.getPoint());
140:
141: @SuppressWarnings("unchecked")
142: RegionListUserObj<R> node = (RegionListUserObj<R>) getModel()
143: .getElementAt(index);
144: R r = node.region();
145: String tooltip = null;
146:
147: OpenDefinitionsDocument doc = r.getDocument();
148: doc.acquireReadLock();
149: try {
150: int lnr = doc.getLineOfOffset(r.getStartOffset()) + 1;
151: int startOffset = doc.getOffset(lnr - 3);
152: if (startOffset < 0) {
153: startOffset = 0;
154: }
155: int endOffset = doc.getOffset(lnr + 3);
156: if (endOffset < 0) {
157: endOffset = doc.getLength() - 1;
158: }
159:
160: // convert to HTML (i.e. < to < and > to > and newlines to <br>)
161: String s = doc.getText(startOffset, endOffset
162: - startOffset);
163:
164: // this highlights the actual region in red
165: int rStart = r.getStartOffset() - startOffset;
166: if (rStart < 0) {
167: rStart = 0;
168: }
169: int rEnd = r.getEndOffset() - startOffset;
170: if (rEnd > s.length()) {
171: rEnd = s.length();
172: }
173: if ((rStart <= s.length()) && (rEnd >= rStart)) {
174: String t1 = StringOps.encodeHTML(s.substring(0,
175: rStart));
176: String t2 = StringOps.encodeHTML(s.substring(
177: rStart, rEnd));
178: String t3 = StringOps.encodeHTML(s
179: .substring(rEnd));
180: s = t1 + "<font color=#ff0000>" + t2
181: + "</font>" + t3;
182: } else {
183: s = StringOps.encodeHTML(s);
184: }
185: tooltip = "<html><pre>" + s + "</pre></html>";
186: } catch (javax.swing.text.BadLocationException ble) {
187: tooltip = null; /* just don't give a tool tip */
188: } finally {
189: doc.releaseReadLock();
190: }
191: return tooltip;
192: }
193: };
194: _list.getSelectionModel().setSelectionMode(
195: ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
196: _list.addListSelectionListener(new ListSelectionListener() {
197: public void valueChanged(ListSelectionEvent e) {
198: updateButtons();
199: }
200: });
201: _list.addKeyListener(new KeyAdapter() {
202: public void keyPressed(KeyEvent e) {
203: if (e.getKeyCode() == KeyEvent.VK_ENTER) {
204: performDefaultAction();
205: }
206: }
207: });
208: _list.setFont(DrJava.getConfig().getSetting(
209: OptionConstants.FONT_DOCLIST));
210:
211: _leftPane.add(new JScrollPane(_list));
212: ToolTipManager.sharedInstance().registerComponent(_list);
213: }
214:
215: /** Update button state and text. Should be overridden if additional buttons are added besides "Go To", "Remove" and "Remove All". */
216: protected void updateButtons() {
217: }
218:
219: /** Action performed when the Enter key is pressed. Should be overridden. */
220: protected void performDefaultAction() {
221: }
222:
223: /** Creates the buttons for controlling the regions. Should be overridden. */
224: protected JComponent[] makeButtons() {
225: return new JComponent[0];
226: }
227:
228: /** Creates the buttons for controlling the regions. */
229: private void _setupButtonPanel() {
230: JPanel mainButtons = new JPanel();
231: JPanel emptyPanel = new JPanel();
232: JPanel closeButtonPanel = new JPanel(new BorderLayout());
233: GridBagLayout gbLayout = new GridBagLayout();
234: GridBagConstraints c = new GridBagConstraints();
235: mainButtons.setLayout(gbLayout);
236:
237: JComponent[] buts = makeButtons();
238:
239: closeButtonPanel.add(_closeButton, BorderLayout.NORTH);
240: for (JComponent b : buts) {
241: mainButtons.add(b);
242: }
243: mainButtons.add(emptyPanel);
244:
245: c.fill = GridBagConstraints.HORIZONTAL;
246: c.anchor = GridBagConstraints.NORTH;
247: c.gridwidth = GridBagConstraints.REMAINDER;
248: c.weightx = 1.0;
249:
250: for (JComponent b : buts) {
251: gbLayout.setConstraints(b, c);
252: }
253:
254: c.fill = GridBagConstraints.BOTH;
255: c.anchor = GridBagConstraints.SOUTH;
256: c.gridheight = GridBagConstraints.REMAINDER;
257: c.weighty = 1.0;
258:
259: gbLayout.setConstraints(emptyPanel, c);
260:
261: _buttonPanel.add(mainButtons, BorderLayout.CENTER);
262: _buttonPanel.add(closeButtonPanel, BorderLayout.EAST);
263: }
264:
265: /** Gets the currently selected regions in the region list, or an empty array if no regions are selected.
266: * @return list of selected regions in the list
267: */
268: protected ArrayList<R> getSelectedRegions() {
269: ArrayList<R> regs = new ArrayList<R>();
270: int[] indices = _list.getSelectedIndices();
271: if (indices != null) {
272: for (int index : indices) {
273: @SuppressWarnings("unchecked")
274: RegionListUserObj<R> userObj = ((RegionListUserObj<R>) _listModel
275: .elementAt(index));
276: R r = userObj.region();
277: regs.add(r);
278: }
279: }
280: return regs;
281: }
282:
283: /** Go to region. */
284: protected void goToRegion() {
285: ArrayList<R> r = getSelectedRegions();
286: if (r.size() == 1) {
287: RegionListUserObj<R> userObj = getUserObjForRegion(r.get(0));
288: if (userObj != null) {
289: _list.ensureIndexIsVisible(_listModel.indexOf(userObj));
290: }
291: _frame.scrollToDocumentAndOffset(r.get(0).getDocument(), r
292: .get(0).getStartOffset(), false);
293: }
294: }
295:
296: /** @return the usser object in the list associated with the region, or null if not found */
297: protected RegionListUserObj<R> getUserObjForRegion(R r) {
298: for (int i = 0; i < _listModel.size(); ++i) {
299: @SuppressWarnings("unchecked")
300: RegionListUserObj<R> userObj = (RegionListUserObj<R>) _listModel
301: .get(i);
302: if ((userObj.region().getStartOffset() == r
303: .getStartOffset())
304: && (userObj.region().getEndOffset() == r
305: .getEndOffset())
306: && (userObj.region().getDocument().equals(r
307: .getDocument()))) {
308: return userObj;
309: }
310: }
311: return null;
312: }
313:
314: /** Add a region to the list. Must be executed in event thread.
315: * @param r the region
316: * @param index the index where the region should be inserted
317: */
318: public void addRegion(final R r, final int index) {
319: // Only change GUI from event-dispatching thread
320: Runnable doCommand = new Runnable() {
321: public void run() {
322: // edu.rice.cs.drjava.model.AbstractGlobalModel.log.log("RegionsListPanel.addRegion: in list were...");
323: // for(int i=0;i<_listModel.getSize();++i) { edu.rice.cs.drjava.model.AbstractGlobalModel.log.log("\t"+_listModel.elementAt(i)); }
324:
325: String name = "";
326: try {
327: name = r.getDocument().getQualifiedClassName();
328: } catch (ClassNameNotFoundException cnnfe) {
329: name = r.getDocument().toString();
330: }
331:
332: RegionListUserObj<R> userObj = makeRegionListUserObj(r);
333: _listModel.add(index, userObj);
334: _list.ensureIndexIsVisible(_listModel.indexOf(userObj));
335:
336: updateButtons();
337: }
338: };
339: Utilities.invokeLater(doCommand);
340: }
341:
342: /** Remove a region from the tree. Must be executed in event thread.
343: * @param r the region
344: */
345: public void removeRegion(final R r) {
346: // Only change GUI from event-dispatching thread
347: Runnable doCommand = new Runnable() {
348: public void run() {
349: String name = "";
350: try {
351: name = r.getDocument().getQualifiedClassName();
352: } catch (ClassNameNotFoundException cnnfe) {
353: name = r.getDocument().toString();
354: }
355:
356: for (int i = 0; i < _listModel.size(); ++i) {
357: @SuppressWarnings("unchecked")
358: RegionListUserObj<R> userObj = (RegionListUserObj<R>) _listModel
359: .get(i);
360: if (userObj.region() == r) {
361: _listModel.removeElementAt(i);
362: break;
363: }
364: }
365:
366: updateButtons();
367: }
368: };
369: Utilities.invokeLater(doCommand);
370: }
371:
372: /** Remove all regions for this document from the tree. Must be executed in event thread.
373: */
374: public void removeRegions(final OpenDefinitionsDocument odd) {
375: // Only change GUI from event-dispatching thread
376: Runnable doCommand = new Runnable() {
377: public void run() {
378: String name = "";
379: try {
380: name = odd.getQualifiedClassName();
381: } catch (ClassNameNotFoundException cnnfe) {
382: name = odd.toString();
383: }
384:
385: for (int i = 0; i < _listModel.size(); ++i) {
386: @SuppressWarnings("unchecked")
387: RegionListUserObj<R> userObj = (RegionListUserObj<R>) _listModel
388: .get(i);
389:
390: if (userObj.region().getDocument().equals(odd)) {
391: _listModel.removeElementAt(i);
392: }
393: }
394:
395: updateButtons();
396: }
397: };
398: Utilities.invokeLater(doCommand);
399: }
400:
401: /** Factory method to create user objects put in the list.
402: * If subclasses extend RegionListUserObj, they need to override this method. */
403: protected RegionListUserObj<R> makeRegionListUserObj(R r) {
404: return new RegionListUserObj<R>(r);
405: }
406:
407: /** Class that gets put into the list. The toString() method determines what's displayed in the three. */
408: protected static class RegionListUserObj<R extends DocumentRegion> {
409: protected R _region;
410:
411: public int lineNumber() {
412: return _region.getDocument().getLineOfOffset(
413: _region.getStartOffset()) + 1;
414: }
415:
416: public R region() {
417: return _region;
418: }
419:
420: public RegionListUserObj(R r) {
421: _region = r;
422: }
423:
424: public String toString() {
425: final StringBuilder sb = new StringBuilder();
426: _region.getDocument().acquireReadLock();
427: try {
428: sb.append(_region.getDocument().toString());
429: sb.append(':');
430: sb.append(lineNumber());
431: try {
432: sb.append(": ");
433: int length = Math.min(120, _region.getEndOffset()
434: - _region.getStartOffset());
435: sb.append(_region.getDocument().getText(
436: _region.getStartOffset(), length).trim());
437: } catch (BadLocationException bpe) { /* ignore, just don't display line */
438: }
439: } finally {
440: _region.getDocument().releaseReadLock();
441: }
442: return sb.toString();
443: }
444:
445: public boolean equals(Object other) {
446: @SuppressWarnings("unchecked")
447: RegionListUserObj<R> o = (RegionListUserObj<R>) other;
448: return (o.region().getDocument().equals(region()
449: .getDocument()))
450: && (o.region().getStartOffset() == region()
451: .getStartOffset())
452: && (o.region().getEndOffset() == region()
453: .getEndOffset());
454: }
455: }
456:
457: /**
458: * Mouse adapter for the region tree.
459: */
460: protected class RegionMouseAdapter extends RightClickMouseAdapter {
461: protected void _popupAction(MouseEvent e) {
462: // TODO: add popup
463: }
464:
465: public void mousePressed(MouseEvent e) {
466: super .mousePressed(e);
467: if (SwingUtilities.isLeftMouseButton(e)
468: && e.getClickCount() == 2) {
469: performDefaultAction();
470: }
471: }
472: }
473:
474: }
|