001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.util.log;
017:
018: import com.google.gwt.core.ext.UnableToCompleteException;
019: import com.google.gwt.dev.util.log.TreeItemLogger.LogEvent;
020:
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.custom.SashForm;
023: import org.eclipse.swt.dnd.Clipboard;
024: import org.eclipse.swt.dnd.TextTransfer;
025: import org.eclipse.swt.dnd.Transfer;
026: import org.eclipse.swt.events.DisposeEvent;
027: import org.eclipse.swt.events.DisposeListener;
028: import org.eclipse.swt.events.KeyAdapter;
029: import org.eclipse.swt.events.KeyEvent;
030: import org.eclipse.swt.events.SelectionEvent;
031: import org.eclipse.swt.events.SelectionListener;
032: import org.eclipse.swt.events.TreeEvent;
033: import org.eclipse.swt.events.TreeListener;
034: import org.eclipse.swt.graphics.Color;
035: import org.eclipse.swt.layout.FillLayout;
036: import org.eclipse.swt.widgets.Composite;
037: import org.eclipse.swt.widgets.Display;
038: import org.eclipse.swt.widgets.Text;
039: import org.eclipse.swt.widgets.Tree;
040: import org.eclipse.swt.widgets.TreeItem;
041:
042: import java.io.PrintWriter;
043: import java.io.StringWriter;
044:
045: /**
046: * SWT widget containing a tree logger.
047: */
048: public class TreeLoggerWidget extends Composite implements
049: TreeListener, SelectionListener {
050:
051: private boolean autoScroll;
052:
053: private final Text details;
054:
055: private final TreeItemLogger logger;
056:
057: private final Tree tree;
058:
059: public TreeLoggerWidget(Composite parent) {
060: super (parent, SWT.NONE);
061:
062: setLayout(new FillLayout());
063:
064: // The sash (aka "splitter").
065: //
066: SashForm sash = new SashForm(this , SWT.VERTICAL);
067:
068: // The tree.
069: //
070: tree = new Tree(sash, SWT.BORDER | SWT.SHADOW_IN);
071: tree.setLinesVisible(false);
072: tree.addSelectionListener(this );
073: tree.setFocus();
074: tree.addKeyListener(new KeyAdapter() {
075: @Override
076: public void keyPressed(KeyEvent e) {
077: if (e.keyCode == 'c' && e.stateMask == SWT.CTRL) {
078: // Copy subtree to clipboard.
079: //
080: copyTreeSelectionToClipboard(tree);
081: }
082: }
083: });
084:
085: logger = new TreeItemLogger();
086:
087: // The detail
088: details = new Text(sash, SWT.MULTI | SWT.WRAP | SWT.READ_ONLY
089: | SWT.BORDER | SWT.V_SCROLL);
090: final Color detailsBgColor = new Color(null, 255, 255, 255);
091: details.setBackground(detailsBgColor);
092: details.addDisposeListener(new DisposeListener() {
093: public void widgetDisposed(DisposeEvent arg0) {
094: detailsBgColor.dispose();
095: }
096: });
097:
098: sash.setWeights(new int[] { 80, 20 });
099:
100: initLogFlushTimer(parent.getDisplay());
101: }
102:
103: public void collapseAll() {
104: TreeItem[] items = tree.getItems();
105: for (int i = 0, n = items.length; i < n; ++i) {
106: TreeItem item = items[i];
107: collapseAll(item);
108: }
109: if (items.length > 0) {
110: tree.setSelection(new TreeItem[] { items[0] });
111: }
112: }
113:
114: public void collapseAll(TreeItem from) {
115: TreeItem[] items = from.getItems();
116: for (int i = 0, n = items.length; i < n; ++i) {
117: TreeItem item = items[i];
118: collapseAll(item);
119: }
120: from.setExpanded(false);
121: }
122:
123: public void expandAll() {
124: TreeItem[] items = tree.getItems();
125: for (int i = 0, n = items.length; i < n; ++i) {
126: TreeItem item = items[i];
127: expandAll(item);
128: }
129: if (items.length > 0) {
130: tree.setSelection(new TreeItem[] { items[0] });
131: }
132: }
133:
134: public void expandAll(TreeItem from) {
135: from.setExpanded(true);
136: TreeItem[] items = from.getItems();
137: for (int i = 0, n = items.length; i < n; ++i) {
138: TreeItem item = items[i];
139: expandAll(item);
140: }
141: }
142:
143: public boolean getAutoScroll() {
144: return autoScroll;
145: }
146:
147: public AbstractTreeLogger getLogger() {
148: return logger;
149: }
150:
151: public void removeAll() {
152: tree.removeAll();
153: details.setText("");
154: }
155:
156: public void setAutoScroll(boolean autoScroll) {
157: this .autoScroll = autoScroll;
158: }
159:
160: public synchronized void treeCollapsed(TreeEvent treeEvent) {
161: }
162:
163: public synchronized void treeExpanded(TreeEvent treeEvent) {
164: }
165:
166: public void widgetDefaultSelected(SelectionEvent event) {
167: }
168:
169: public void widgetSelected(SelectionEvent event) {
170: syncDetailsPane((TreeItem) event.item);
171: }
172:
173: protected void appendTreeItemText(PrintWriter result,
174: TreeItem[] items, int depth) {
175: for (int i = 0; i < items.length; i++) {
176: TreeItem item = items[i];
177: for (int j = 0; j < depth; j++) {
178: result.print(" ");
179: }
180: result.println(item.getText());
181: TreeItem[] children = item.getItems();
182: if (children != null) {
183: appendTreeItemText(result, children, depth + 1);
184: }
185: }
186: }
187:
188: protected void copyTreeSelectionToClipboard(Tree tree) {
189: TreeItem[] selected = tree.getSelection();
190: StringWriter sw = new StringWriter();
191: PrintWriter pw = new PrintWriter(sw, false);
192: if (selected != null) {
193: appendTreeItemText(pw, selected, 0);
194: }
195: pw.close();
196: Clipboard cb = new Clipboard(tree.getDisplay());
197:
198: final Object[] cbText = new Object[] { sw.toString() };
199: final Transfer[] cbFormat = new Transfer[] { TextTransfer
200: .getInstance() };
201: cb.setContents(cbText, cbFormat);
202: }
203:
204: private final void initLogFlushTimer(final Display display) {
205: final int flushDelay = 1000;
206:
207: display.timerExec(flushDelay, new Runnable() {
208: public void run() {
209: if (tree.isDisposed()) {
210: return;
211: }
212:
213: if (logger.uiFlush(tree)) {
214: // Sync to the end of the tree.
215: //
216: if (autoScroll) {
217: TreeItem lastItem = findLastVisibleItem(tree);
218: if (lastItem != null) {
219: tree
220: .setSelection(new TreeItem[] { lastItem });
221: tree.showItem(lastItem);
222: expandAllChildren(lastItem);
223: syncDetailsPane(lastItem);
224: }
225: }
226: }
227:
228: display.timerExec(flushDelay, this );
229: }
230:
231: private void expandAllChildren(TreeItem item) {
232: item.setExpanded(true);
233: for (int i = 0, n = item.getItemCount(); i < n; ++i) {
234: expandAllChildren(item.getItem(i));
235: }
236: }
237:
238: private TreeItem findLastVisibleItem(Tree tree) {
239: int n = tree.getItemCount() - 1;
240: if (n > 0) {
241: TreeItem item = tree.getItem(n);
242: if (item.getExpanded()) {
243: return findLastVisibleItem(item);
244: } else {
245: return item;
246: }
247: } else {
248: return null;
249: }
250: }
251:
252: private TreeItem findLastVisibleItem(TreeItem item) {
253: int n = item.getItemCount() - 1;
254: if (n > 0) {
255: TreeItem child = item.getItem(n);
256: if (child.getExpanded()) {
257: return findLastVisibleItem(child);
258: }
259: }
260: return item;
261: }
262: });
263: }
264:
265: private void syncDetailsPane(TreeItem item) {
266: // Try to get a LogEvent from the item's custom data.
267: //
268: TreeItemLogger.LogEvent logEvent = null;
269: Object testLogEvent = item.getData();
270: if (testLogEvent instanceof TreeItemLogger.LogEvent) {
271: logEvent = (LogEvent) testLogEvent;
272: }
273:
274: // Build a detail string.
275: //
276: StringBuffer sb = new StringBuffer();
277:
278: // Show the message type.
279: //
280: if (logEvent != null && logEvent.type != null) {
281: sb.append("[");
282: sb.append(logEvent.type.getLabel());
283: sb.append("] ");
284: }
285:
286: // Show the item text.
287: //
288: sb.append(item.getText());
289: sb.append("\n");
290:
291: // Show the exception info for anything other than "UnableToComplete".
292: //
293: if (logEvent != null && logEvent.caught != null) {
294: if (!(logEvent.caught instanceof UnableToCompleteException)) {
295: String stackTrace = AbstractTreeLogger
296: .getStackTraceAsString(logEvent.caught);
297: sb.append(stackTrace);
298: }
299: }
300:
301: details.setText(sb.toString());
302: }
303: }
|