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 javax.swing.*;
040: import javax.swing.event.*;
041: import javax.swing.text.JTextComponent;
042: import javax.swing.text.Keymap;
043: import java.awt.*;
044: import java.awt.event.*;
045: import java.util.List;
046: import java.util.ArrayList;
047: import java.util.StringTokenizer;
048: import java.util.NoSuchElementException;
049:
050: import edu.rice.cs.drjava.DrJava;
051: import edu.rice.cs.drjava.config.OptionConstants;
052: import edu.rice.cs.drjava.model.ClipboardHistoryModel;
053: import edu.rice.cs.util.Lambda;
054: import edu.rice.cs.util.StringOps;
055:
056: /** Frame with history of clipboard. */
057: public class ClipboardHistoryFrame extends JFrame {
058: /** Interface for an action to be performed when the user closes the frame,
059: * either by using "OK" or "Cancel".
060: */
061: public static interface CloseAction extends Lambda<Object, String> {
062: public Object apply(String selected);
063: }
064:
065: /** Class to save the frame state, i.e. location and dimensions.*/
066: public static class FrameState {
067: private Dimension _dim;
068: private Point _loc;
069:
070: public FrameState(Dimension d, Point l) {
071: _dim = d;
072: _loc = l;
073: }
074:
075: public FrameState(String s) {
076: StringTokenizer tok = new StringTokenizer(s);
077: try {
078: int x = Integer.valueOf(tok.nextToken());
079: int y = Integer.valueOf(tok.nextToken());
080: _dim = new Dimension(x, y);
081: x = Integer.valueOf(tok.nextToken());
082: y = Integer.valueOf(tok.nextToken());
083: _loc = new Point(x, y);
084: } catch (NoSuchElementException nsee) {
085: throw new IllegalArgumentException(
086: "Wrong FrameState string: " + nsee);
087: } catch (NumberFormatException nfe) {
088: throw new IllegalArgumentException(
089: "Wrong FrameState string: " + nfe);
090: }
091: }
092:
093: public FrameState(ClipboardHistoryFrame comp) {
094: _dim = comp.getSize();
095: _loc = comp.getLocation();
096: }
097:
098: public String toString() {
099: final StringBuilder sb = new StringBuilder();
100: sb.append((int) _dim.getWidth());
101: sb.append(' ');
102: sb.append((int) _dim.getHeight());
103: sb.append(' ');
104: sb.append(_loc.x);
105: sb.append(' ');
106: sb.append(_loc.y);
107: return sb.toString();
108: }
109:
110: public Dimension getDimension() {
111: return _dim;
112: }
113:
114: public Point getLocation() {
115: return _loc;
116: }
117: }
118:
119: /** Clipboard history model */
120: private ClipboardHistoryModel _chm;
121:
122: /** Code for the last button that was pressed.*/
123: private int _buttonPressed;
124:
125: /** Ok button.*/
126: private JButton _okButton;
127:
128: /** Cancel button. */
129: private JButton _cancelButton;
130:
131: /** List with history. */
132: private JList _historyList;
133:
134: /** Text area for that previews the history content. */
135: private JTextArea _previewArea;
136:
137: /** Last frame state. It can be stored and restored. */
138: private FrameState _lastState = null;
139:
140: /** Owner frame. */
141: private MainFrame _mainFrame;
142:
143: /** Close actions for ok and cancel button. */
144: private CloseAction _okAction, _cancelAction;
145:
146: /** Create a new clipboard history frame.
147: * @param owner owner frame
148: * @param title dialog title
149: * @param chm the clipboard history model
150: * @param okAction the action to perform when OK is clicked
151: * @param cancelAction the action to perform when Cancel is clicked
152: */
153: public ClipboardHistoryFrame(MainFrame owner, String title,
154: ClipboardHistoryModel chm, CloseAction okAction,
155: CloseAction cancelAction) {
156: super (title);
157: _chm = chm;
158: _mainFrame = owner;
159: _okAction = okAction;
160: _cancelAction = cancelAction;
161: init();
162: }
163:
164: /** Returns the last state of the frame, i.e. the location and dimension.
165: * @return frame state
166: */
167: public FrameState getFrameState() {
168: return _lastState;
169: }
170:
171: /** Sets state of the frame, i.e. the location and dimension of the frame for the next use.
172: * @param ds State to update to, or {@code null} to reset
173: */
174: public void setFrameState(FrameState ds) {
175: _lastState = ds;
176: if (_lastState != null) {
177: setSize(_lastState.getDimension());
178: setLocation(_lastState.getLocation());
179: validate();
180: }
181: }
182:
183: /** Sets state of the frame, i.e. the location and dimension of the frame for the next use.
184: * @param s State to update to, or {@code null} to reset
185: */
186: public void setFrameState(String s) {
187: try {
188: _lastState = new FrameState(s);
189: } catch (IllegalArgumentException e) {
190: _lastState = null;
191: }
192: if (_lastState != null) {
193: setSize(_lastState.getDimension());
194: setLocation(_lastState.getLocation());
195: validate();
196: } else {
197: Dimension parentDim = (_mainFrame != null) ? _mainFrame
198: .getSize() : getToolkit().getScreenSize();
199: int xs = (int) parentDim.getWidth() / 3;
200: int ys = (int) parentDim.getHeight() / 4;
201: setSize(Math.max(xs, 400), Math.max(ys, 400));
202: MainFrame.setPopupLoc(this , _mainFrame);
203: }
204: }
205:
206: /** Return the code for the last button that was pressed. This will be either JOptionPane.OK_OPTION or
207: * JOptionPane.CANCEL_OPTION.
208: * @return button code
209: */
210: public int getButtonPressed() {
211: return _buttonPressed;
212: }
213:
214: /** Initialize the frame.
215: */
216: private void init() {
217: addWindowListener(new java.awt.event.WindowAdapter() {
218: public void windowClosing(WindowEvent winEvt) {
219: cancelButtonPressed();
220: }
221: });
222: addComponentListener(new java.awt.event.ComponentAdapter() {
223: public void componentResized(ComponentEvent e) {
224: validate();
225: _historyList.ensureIndexIsVisible(_historyList
226: .getSelectedIndex());
227: }
228: });
229:
230: JRootPane rootPane = this .getRootPane();
231: InputMap iMap = rootPane
232: .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
233: iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
234: "escape");
235:
236: ActionMap aMap = rootPane.getActionMap();
237: aMap.put("escape", new AbstractAction() {
238: public void actionPerformed(ActionEvent e) {
239: cancelButtonPressed();
240: }
241: });
242:
243: _historyList = new JList();
244: _historyList
245: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
246: _historyList
247: .addListSelectionListener(new ListSelectionListener() {
248: public void valueChanged(ListSelectionEvent e) {
249: updatePreview();
250: }
251: });
252: _historyList.setFont(DrJava.getConfig().getSetting(
253: OptionConstants.FONT_MAIN));
254: _historyList.setCellRenderer(new DefaultListCellRenderer() {
255: public Component getListCellRendererComponent(JList list,
256: Object value, int index, boolean isSelected,
257: boolean cellHasFocus) {
258: Component c = super .getListCellRendererComponent(list,
259: value, index, isSelected, cellHasFocus);
260: c.setForeground(DrJava.getConfig().getSetting(
261: OptionConstants.DEFINITIONS_NORMAL_COLOR));
262: return c;
263: }
264: });
265: _historyList.addFocusListener(new FocusAdapter() {
266:
267: public void focusLost(FocusEvent e) {
268: if ((e.getOppositeComponent() != _previewArea)
269: && (e.getOppositeComponent() != _okButton)
270: && (e.getOppositeComponent() != _cancelButton)) {
271: _historyList.requestFocus();
272: }
273: }
274: });
275:
276: // buttons
277: _okButton = new JButton("OK");
278: _okButton.addActionListener(new ActionListener() {
279: public void actionPerformed(ActionEvent e) {
280: okButtonPressed();
281: }
282: });
283:
284: _cancelButton = new JButton("Cancel");
285: _cancelButton.addActionListener(new ActionListener() {
286: public void actionPerformed(ActionEvent e) {
287: cancelButtonPressed();
288: }
289: });
290:
291: // put everything together
292: Container contentPane = getContentPane();
293:
294: GridBagLayout layout = new GridBagLayout();
295: contentPane.setLayout(layout);
296:
297: GridBagConstraints c = new GridBagConstraints();
298: c.anchor = GridBagConstraints.NORTHWEST;
299: c.weightx = 1.0;
300: c.weighty = 0.0;
301: c.gridwidth = GridBagConstraints.REMAINDER; // end row
302: c.insets.top = 2;
303: c.insets.left = 2;
304: c.insets.bottom = 2;
305: c.insets.right = 2;
306:
307: c.fill = GridBagConstraints.BOTH;
308: c.weighty = 1.0;
309: contentPane.add(new JScrollPane(_historyList,
310: JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
311: JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), c);
312:
313: _previewArea = new JTextArea("");
314: _previewArea.setEditable(false);
315: _previewArea.setDragEnabled(false);
316: _previewArea.setEnabled(false);
317: _previewArea.setFont(DrJava.getConfig().getSetting(
318: OptionConstants.FONT_MAIN));
319: _previewArea.setDisabledTextColor(DrJava.getConfig()
320: .getSetting(OptionConstants.DEFINITIONS_NORMAL_COLOR));
321: c.weighty = 2.0;
322: contentPane.add(new JScrollPane(_previewArea,
323: JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
324: JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), c);
325:
326: c.anchor = GridBagConstraints.SOUTH;
327:
328: JPanel buttonPanel = new JPanel(new GridBagLayout());
329: GridBagConstraints bc = new GridBagConstraints();
330: bc.insets.left = 2;
331: bc.insets.right = 2;
332: buttonPanel.add(_okButton, bc);
333: buttonPanel.add(_cancelButton, bc);
334:
335: c.weighty = 0.0;
336: contentPane.add(buttonPanel, c);
337:
338: Dimension parentDim = (_mainFrame != null) ? _mainFrame
339: .getSize() : getToolkit().getScreenSize();
340: int xs = (int) parentDim.getWidth() / 3;
341: int ys = (int) parentDim.getHeight() / 4;
342: setSize(Math.max(xs, 400), Math.max(ys, 300));
343: MainFrame.setPopupLoc(this , _mainFrame);
344:
345: updateView();
346: }
347:
348: /** Validates before changing visibility. Only runs in the event thread.
349: * @param b true if frame should be shown, false if it should be hidden.
350: */
351: public void setVisible(boolean b) {
352: assert EventQueue.isDispatchThread();
353: validate();
354: super .setVisible(b);
355: if (b) {
356: _mainFrame.hourglassOn();
357: updateView();
358: _historyList.requestFocus();
359: } else {
360: _mainFrame.hourglassOff();
361: _mainFrame.toFront();
362: }
363: }
364:
365: /** Update the displays based on the model. */
366: private void updateView() {
367: List<String> strs = _chm.getStrings();
368: ListItem[] arr = new ListItem[strs.size()];
369: for (int i = 0; i < strs.size(); ++i)
370: arr[strs.size() - i - 1] = new ListItem(strs.get(i));
371: _historyList.setListData(arr);
372: if (_historyList.getModel().getSize() > 0) {
373: _historyList.setSelectedIndex(0);
374: getRootPane().setDefaultButton(_okButton);
375: _okButton.setEnabled(true);
376: } else {
377: getRootPane().setDefaultButton(_cancelButton);
378: _okButton.setEnabled(false);
379: }
380: updatePreview();
381: }
382:
383: /** Update the preview area based on the model. */
384: private void updatePreview() {
385: String text = "";
386: if (_historyList.getModel().getSize() > 0) {
387: int index = _historyList.getSelectedIndex();
388: if (index != -1) {
389: text = ((ListItem) _historyList.getModel()
390: .getElementAt(_historyList.getSelectedIndex()))
391: .getFull();
392: }
393: }
394:
395: _previewArea.setText(text);
396: _previewArea.setCaretPosition(0);
397: }
398:
399: /** Handle OK button. */
400: private void okButtonPressed() {
401: _lastState = new FrameState(ClipboardHistoryFrame.this );
402: setVisible(false);
403: if (_historyList.getModel().getSize() > 0) {
404: _buttonPressed = JOptionPane.OK_OPTION;
405: String s = ((ListItem) _historyList.getModel()
406: .getElementAt(_historyList.getSelectedIndex()))
407: .getFull();
408: _chm.put(s);
409: _okAction.apply(s);
410: } else {
411: _buttonPressed = JOptionPane.CANCEL_OPTION;
412: Toolkit.getDefaultToolkit().beep();
413: _cancelAction.apply(null);
414: }
415: }
416:
417: /** Handle cancel button. */
418: private void cancelButtonPressed() {
419: _buttonPressed = JOptionPane.CANCEL_OPTION;
420: _lastState = new FrameState(ClipboardHistoryFrame.this );
421: setVisible(false);
422: _cancelAction.apply(null);
423: }
424:
425: /** Keeps a full string, but toString is only the first line. */
426: private static class ListItem {
427: private String full, display;
428:
429: public ListItem(String s) {
430: full = s;
431: int index1 = s.indexOf('\n');
432: if (index1 == -1)
433: index1 = s.length();
434: int index2 = s.indexOf(StringOps.EOL);
435: if (index2 == -1)
436: index2 = s.length();
437: display = s.substring(0, Math.min(index1, index2));
438: }
439:
440: public String getFull() {
441: return full;
442: }
443:
444: public String toString() {
445: return display;
446: }
447:
448: public boolean equals(Object o) {
449: if ((o == null) || !(o instanceof ListItem))
450: return false;
451: return full.equals(((ListItem) o).full);
452: }
453: }
454: }
|