001: /*
002: * Copyright (c) 2000, Jacob Smullyan.
003: *
004: * This is part of SkunkDAV, a WebDAV client. See http://skunkdav.sourceforge.net/
005: * for the latest version.
006: *
007: * SkunkDAV is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License as published
009: * by the Free Software Foundation; either version 2, or (at your option)
010: * any later version.
011: *
012: * SkunkDAV is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with SkunkDAV; see the file COPYING. If not, write to the Free
019: * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
020: * 02111-1307, USA.
021: */
022:
023: package org.skunk.dav.client.gui;
024:
025: import java.awt.BorderLayout;
026: import java.awt.Color;
027: import java.awt.Component;
028: import java.awt.Dimension;
029: import java.awt.Font;
030: import java.awt.Point;
031: import java.awt.event.WindowAdapter;
032: import java.awt.event.WindowEvent;
033: import java.beans.PropertyVetoException;
034: import java.util.ArrayList;
035: import java.util.Iterator;
036: import java.util.ListIterator;
037: import javax.swing.Box;
038: import javax.swing.JComponent;
039: import javax.swing.JFrame;
040: import javax.swing.JLabel;
041: import javax.swing.JPanel;
042: import javax.swing.JTabbedPane;
043: import javax.swing.JTextField;
044: import javax.swing.SwingConstants;
045: import javax.swing.UIManager;
046: import javax.swing.event.ChangeEvent;
047: import javax.swing.event.ChangeListener;
048: import org.skunk.dav.client.gui.editor.DAVEditor;
049: import org.skunk.trace.Debug;
050:
051: public class ExplorerView implements View {
052: private JFrame appFrame;
053: private JTabbedPane tabPane = null;
054: private ArrayList dockedBuffers = new ArrayList();
055: private ArrayList dockedStatusComponents = new ArrayList();
056: private AppContext appContext;
057: private Buffer focussedBuffer = null;
058: private JTextField statusField;
059: private JLabel locationField;
060: private Box statusBar;
061:
062: public ExplorerView(AppContext appContext) {
063: this .appContext = appContext;
064: initComponents();
065: }
066:
067: public final AppContext getAppContext() {
068: return this .appContext;
069: }
070:
071: public Component getComponent() {
072: return appFrame;
073: }
074:
075: private void initComponents() {
076: Explorer ex = new Explorer();
077: getAppContext().getConfigurator().configure(ex);
078: ExplorerMenuBar menubar = ExplorerMenuBar.getMenuBar(ex);
079: initStatusBar();
080: appFrame = new JFrame("SkunkDAV "
081: + ResourceManager
082: .getMessage(ResourceManager.RELEASE_VERSION));
083: appFrame.addWindowListener(new WindowAdapter() {
084: public void windowClosing(WindowEvent woo) {
085: System.exit(0);
086: }
087: });
088: appFrame.setJMenuBar(menubar);
089: dock(ex);
090: appFrame.pack();
091: appFrame.setVisible(true);
092: }
093:
094: private void initStatusBar() {
095: statusField = new JTextField();
096: statusField.setEnabled(false);
097: Font defaultFont = new Font("Serif", Font.PLAIN, 10);
098: statusField.setFont(defaultFont);
099: statusField.setBackground(UIManager
100: .getColor("Label.background"));
101: statusField.setForeground(Color.black);
102: dockedStatusComponents.add(statusField);
103:
104: locationField = new JLabel();
105: locationField.setFont(new Font("SansSerif", Font.PLAIN, 12));
106: locationField.setForeground(Color.black);
107: dockedStatusComponents.add(locationField);
108:
109: statusBar = Box.createHorizontalBox();
110: StateMonitor.registerProperty(locationField, "text",
111: StateProperties.CARET_POSITION,
112: new StateMonitor.Mapper() {
113: public Object getTargetValue(Object applicationValue) {
114: if (applicationValue instanceof TextPoint) {
115: TextPoint p = (TextPoint) applicationValue;
116: return ResourceManager.getMessage(
117: ResourceManager.LINE_AND_COLUMN, p
118: .getCoordinates());
119: } else
120: return "";
121: }
122:
123: public boolean canMapValue(Object applicationValue) {
124: return true;
125: }
126: }, new StateMonitor.DomainMatcher() {
127: public boolean domainMatches(Object domainKey) {
128: return domainKey != null
129: && domainKey.equals(ExplorerView.this );
130: }
131: });
132: layoutStatusBarComponents();
133: }
134:
135: private void layoutStatusBarComponents() {
136: statusBar.removeAll();
137: for (Iterator it = dockedStatusComponents.iterator(); it
138: .hasNext();) {
139: statusBar.add((JComponent) it.next());
140: if (it.hasNext())
141: statusBar.add(Box.createHorizontalGlue());
142: }
143: statusBar.invalidate();
144:
145: if (appFrame != null) {
146: Dimension d = appFrame.getSize();
147: appFrame.pack();
148: appFrame.setSize(d);
149: appFrame.repaint();
150: }
151: }
152:
153: private void resetStatusBar() {
154: if (!dockedStatusComponents.contains(statusField)) {
155: swapStatus((JComponent) dockedStatusComponents.get(0),
156: statusField);
157: }
158: }
159:
160: /**
161: * add the buffer to the application container widget
162: */
163: public void dock(Buffer buffer) {
164: if (!dockedBuffers.contains(buffer))
165: dockedBuffers.add(buffer);
166: buffer.docking();
167: resetStatusBar();
168: redoLayout();
169: focus(buffer);
170: }
171:
172: /**
173: * remove the buffer from the application container widget
174: */
175: public void undock(Buffer buffer) {
176: if (buffer instanceof Explorer) {
177: Debug.trace(this , Debug.DP3,
178: "attempting to undock an Explorer");
179: return;
180: } else {
181: Debug.trace(this , Debug.DP3, "undocking " + buffer);
182: }
183: if (dockedBuffers.contains(buffer)) {
184: int index = dockedBuffers.indexOf(buffer);
185: dockedBuffers.remove(buffer);
186: buffer.undocking();
187: resetStatusBar();
188: redoLayout();
189: //show the nearest buffer to the one that was removed.
190: if (index >= dockedBuffers.size())
191: index--;
192: focus((Buffer) dockedBuffers.get(index));
193:
194: }
195: }
196:
197: /**
198: * @return list of current docked buffers
199: */
200: public Iterator getDockedBuffers() {
201: return dockedBuffers.listIterator();
202: }
203:
204: /**
205: * ensure that the specified docked buffer is visible and has focus
206: */
207: public void focus(Buffer buffer) {
208: if (dockedBuffers.size() > 1
209: && getAppContext().getDockMode().equals(
210: DockMode.TABBED_PANE_MODE)) {
211: tabPane.setSelectedComponent(buffer.getComponent());
212: }
213: focussedBuffer = buffer;
214: }
215:
216: /**
217: * @return the buffer that has focus, or null if there are no buffers
218: */
219: public Buffer getFocussedBuffer() {
220: return focussedBuffer;
221: }
222:
223: /**
224: * attempt to set for the given buffer the given visibility.
225: * Depending on the dock mode, certain settings of this property
226: * may be vetoed (e.g., in EMACS_MODE, at least one buffer must
227: * be visible at all times, so setting the only visible buffer
228: * invisible would not be permitted.) A change of visibility
229: * may entail a change to the focusedBuffer property, but it may
230: * not change the visibility of another buffer. (Hence, for
231: * single-buffer dock modes (like TABBED_PANE_MODE, if restricted to one frame)
232: * the setVisible method should throw an UnsupportedOperationException.)
233: * Preconditions: the buffer must be docked and non-null.
234: *
235: */
236: public void setVisible(Buffer buffer, boolean visible)
237: throws UnsupportedOperationException, PropertyVetoException {
238: if (getAppContext().getDockMode().equals(
239: DockMode.TABBED_PANE_MODE))
240: throw new UnsupportedOperationException(
241: "setVisible not supported for DockMode.TABBED_PANE_MODE");
242: }
243:
244: /**
245: * @return whether the given buffer is visible
246: */
247: public boolean isVisible(Buffer buffer) {
248: if (getAppContext().getDockMode().equals(
249: DockMode.TABBED_PANE_MODE))
250: return buffer.equals(focussedBuffer);
251: //no other modes implemented yet
252: return false;
253: }
254:
255: public void dispose() {
256: appFrame.dispose();
257: StateMonitor.unregisterProperty(locationField, "text",
258: StateProperties.CARET_POSITION);
259: }
260:
261: public void showStatus(String message) {
262: statusField.setText(message);
263: }
264:
265: /**
266: * add a component to the status bar
267: */
268: public void dockStatus(JComponent statusComponent) {
269: swapStatus(statusField, statusComponent);
270: }
271:
272: /**
273: * remove a component from the status bar
274: */
275: public void undockStatus(JComponent statusComponent) {
276: swapStatus(statusComponent, statusField);
277: }
278:
279: private void swapStatus(JComponent firstComp, JComponent secondComp) {
280: if (dockedStatusComponents.contains(firstComp)) {
281: int index = dockedStatusComponents.indexOf(firstComp);
282: dockedStatusComponents.remove(firstComp);
283: dockedStatusComponents.add(index, secondComp);
284: layoutStatusBarComponents();
285: } else
286: Debug.trace(this , Debug.DP3,
287: "Component {0} not status-docked, cannot swap",
288: firstComp);
289: }
290:
291: private String getBufferName(Buffer buffer) {
292: String bs = buffer.getName();
293: if (buffer instanceof DAVEditor) {
294: if (((DAVEditor) buffer).isDirty()) {
295: return new StringBuffer(bs).append('*').toString();
296: }
297: }
298: return bs;
299: }
300:
301: public void bufferDirtinessChanged(Buffer buffer) {
302: int index = tabPane.indexOfComponent(buffer.getComponent());
303: if (index != -1) {
304: tabPane.setTitleAt(index, getBufferName(buffer));
305: }
306: if (buffer.equals(focussedBuffer)
307: && buffer instanceof DAVEditor) {
308: StateMonitor.setProperty(
309: StateProperties.EDITOR_IN_FOCUS_AND_DIRTY,
310: new Boolean(((DAVEditor) buffer).isDirty()), null);
311: }
312: }
313:
314: private void redoLayout() {
315: locationField.setText("");
316: if (dockedBuffers.size() == 1) {
317: Debug.trace(this , Debug.DP3, "only one buffer docked");
318: JPanel basePanel = new JPanel(new BorderLayout());
319: basePanel.add(statusBar, BorderLayout.SOUTH);
320: JComponent jc = ((Buffer) dockedBuffers.get(0))
321: .getComponent();
322: basePanel.add(jc, BorderLayout.CENTER);
323: appFrame.setContentPane(basePanel);
324: } else {
325: if (getAppContext().getDockMode().equals(
326: DockMode.TABBED_PANE_MODE)) {
327: Debug.trace(this , Debug.DP3, "adding tab");
328: if (tabPane == null) {
329: tabPane = new JTabbedPane(SwingConstants.BOTTOM);
330: tabPane.addChangeListener(new ChangeListener() {
331: public void stateChanged(ChangeEvent cheese) {
332: setFocussedBufferFromTabPane();
333: resetStatusBar();
334: }
335: });
336: tabPane.setBackground(UIManager
337: .getColor("Label.background"));
338: tabPane.setOpaque(true);
339: }
340: Dimension d0 = appFrame.getContentPane()
341: .getPreferredSize();
342: Dimension d1 = appFrame.getContentPane().getSize();
343: Dimension d = (d1 == null) ? d0 : d1;
344: tabPane.removeAll();
345: for (ListIterator lit = dockedBuffers.listIterator(); lit
346: .hasNext();) {
347: Buffer buffy = (Buffer) lit.next();
348: tabPane.addTab(getBufferName(buffy), buffy
349: .getComponent());
350: }
351: tabPane.setPreferredSize(d);
352: JPanel basePanel = new JPanel(new BorderLayout());
353: basePanel.add(statusBar, BorderLayout.SOUTH);
354: basePanel.add(tabPane, BorderLayout.CENTER);
355: basePanel.setPreferredSize(d);
356: appFrame.setContentPane(basePanel);
357: }
358: }
359: appFrame.invalidate();
360: appFrame.validate();
361: appFrame.repaint();
362: }
363:
364: private void setFocussedBufferFromTabPane() {
365: Component c = tabPane.getSelectedComponent();
366: for (Iterator it = getDockedBuffers(); it.hasNext();) {
367: Buffer buffy = (Buffer) it.next();
368: JComponent jc = buffy.getComponent();
369: if (jc.equals(c)) {
370: focussedBuffer = buffy;
371: boolean haveAnEditor = buffy instanceof DAVEditor;
372: StateMonitor.setProperty(
373: StateProperties.EDITOR_IN_FOCUS, new Boolean(
374: haveAnEditor), null);
375: StateMonitor
376: .setProperty(
377: StateProperties.EDITOR_IN_FOCUS_AND_DIRTY,
378: new Boolean(haveAnEditor
379: && ((DAVEditor) buffy)
380: .isDirty()), null);
381: break;
382: }
383: }
384: }
385: }
386:
387: /* $Log: ExplorerView.java,v $
388: /* Revision 1.13 2001/07/22 07:04:24 smulloni
389: /* changed the handling of release number.
390: /*
391: /* Revision 1.12 2000/12/19 22:36:05 smulloni
392: /* adjustments to preamble.
393: /*
394: /* Revision 1.11 2000/12/19 19:22:22 smulloni
395: /* some tweaks: ExitAction now cleans up the app's buffers. The app now does
396: /* not resize every time a buffer is added or removed.
397: /*
398: /* Revision 1.10 2000/12/04 23:51:16 smulloni
399: /* added ImageViewer; fixed word in SimpleTextEditor
400: /*
401: /* Revision 1.9 2000/12/03 23:53:26 smulloni
402: /* added license and copyright preamble to java files.
403: /*
404: /* Revision 1.8 2000/12/01 16:25:52 smullyan
405: /* improvements to look and feel; fixed NPE in DAVFile; new actions for text
406: /* editor
407: /*
408: /* Revision 1.7 2000/11/29 23:16:04 smullyan
409: /* adding first rough cut of search capability to the text editor. View
410: /* is being updated to allow components to be docked into the status bar.
411: /*
412: /* Revision 1.6 2000/11/28 00:01:38 smullyan
413: /* added a status bar/minibuffer, with a location field showing the current line and
414: /* column number (for the SimpleTextEditor and kin only).
415: /*
416: /* Revision 1.5 2000/11/22 00:11:27 smullyan
417: /* editor now locks and unlocks, more or less appropriately.
418: /*
419: /* Revision 1.4 2000/11/20 23:30:21 smullyan
420: /* more editor integration work.
421: /*
422: /* Revision 1.3 2000/11/18 04:36:04 smullyan
423: /* work on StateMonitor and related functionality.
424: /*
425: /* Revision 1.2 2000/11/17 20:25:05 smullyan
426: /* new SaveAction; a StateMonitor being added to handle application state.
427: /*
428: /* Revision 1.1 2000/11/16 20:45:18 smullyan
429: /* the start of editor integration.
430: /* */
|