/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain Eclipse Public Licensed code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.logging.view; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.net.URI; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.Path; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.FontRegistry; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISharedTextColors; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.OverviewRuler; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.source.VerticalRuler; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.custom.LineStyleEvent; import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.TextChangeListener; import org.eclipse.swt.custom.TextChangedEvent; import org.eclipse.swt.custom.TextChangingEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.internal.editors.text.EditorsPlugin; import org.eclipse.ui.texteditor.AnnotationPreference; import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; import org.eclipse.ui.texteditor.IUpdate; import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import com.aptana.ide.core.IdeLog; import com.aptana.ide.editors.UnifiedEditorsPlugin; import com.aptana.ide.editors.unified.UnifiedColorManager; import com.aptana.ide.editors.unified.UniformResourceMarkerAnnotationModel; import com.aptana.ide.lexer.ILexer; import com.aptana.ide.lexer.Lexeme; import com.aptana.ide.lexer.LexemeList; import com.aptana.ide.logging.ILogResource; import com.aptana.ide.logging.ILogResourceListener; import com.aptana.ide.logging.ILogTailListener; import com.aptana.ide.logging.ILogWatcher; import com.aptana.ide.logging.LogResourceFactory; import com.aptana.ide.logging.LoggingPlugin; import com.aptana.ide.logging.LoggingPreferences; import com.aptana.ide.logging.Messages; import com.aptana.ide.logging.coloring.LoggingColorizer; import com.aptana.ide.logging.coloring.LoggingLexemeManager; import com.aptana.ide.logging.coloring.TokenTypes; import com.aptana.ide.logging.impl.DisplayThreadProxy; import com.aptana.ide.logging.preferences.ILoggingPreferenceListener; import com.aptana.ide.logging.preferences.LoggingStructureProvider; import com.aptana.ide.logging.view.LogView.TextListener; /** * Log tab. * @author Denis Denisenko */ class LogTab { /** * Log source viewer. * @author Denis Denisenko * */ static class LogSourceViewer extends SourceViewer { public LogSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles) { super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles); } /** * Reveals the given range of the visible document. * * @param start the start offset of the range * @param end the end offset of the range */ protected void revealLastLine() { try { IDocument doc = getVisibleDocument(); int endLine= doc.getLineOfOffset(doc.getLength() == 0 ? 0 : doc.getLength() - 1); int startLine = endLine; int top = getTextWidget().getTopIndex(); if (top > -1) { // scroll vertically int bottom= getBottomIndex(getTextWidget()); int lines= bottom - top; // if the widget is not scrollable as it is displaying the entire content // setTopIndex won't have any effect. if (startLine >= top && startLine <= bottom && endLine >= top && endLine <= bottom ) { // do not scroll at all as it is already visible } else { int delta= Math.max(0, lines - (endLine - startLine)); getTextWidget().setTopIndex(startLine - delta/3); updateViewportListeners(INTERNAL); } } } catch (BadLocationException e) { throw new IllegalArgumentException("Illegal text range"); //$NON-NLS-1$ } } /** * Returns the last fully visible line of the widget. The exact semantics of "last fully visible * line" are: * <ul> * <li>the last line of which the last pixel is visible, if any * <li>otherwise, the only line that is partially visible * </ul> * * @param widget the widget * @return the last fully visible line */ public static int getBottomIndex(StyledText widget) { int lastPixel= computeLastVisiblePixel(widget); // bottom is in [0 .. lineCount - 1] int bottom= widget.getLineIndex(lastPixel); // bottom is the first line - no more checking if (bottom == 0) return bottom; int pixel= widget.getLinePixel(bottom); // bottom starts on or before the client area start - bottom is the only visible line if (pixel <= 0) return bottom; int offset= widget.getOffsetAtLine(bottom); int height= widget.getLineHeight(offset); // bottom is not showing entirely - use the previous line if (pixel + height - 1 > lastPixel) return bottom - 1; // bottom is fully visible and its last line is exactly the last pixel return bottom; } /** * Returns the last visible pixel in the widget's client area. * * @param widget the widget * @return the last visible pixel in the widget's client area */ private static int computeLastVisiblePixel(StyledText widget) { int caHeight= widget.getClientArea().height; int lastPixel= caHeight - 1; return lastPixel; } } /** * Document writer. * @LogTab * @author Denis Denisenko */ private static class DocumentWriter extends Writer { /** * Document. */ private IDocument document; /** * DocumentWriter constructor. * @param document - document. */ public DocumentWriter(IDocument document) { this.document = document; } /** * {@inheritDoc} */ @Override public void close() throws IOException { } /** * {@inheritDoc} */ @Override public void flush() throws IOException { } /** * {@inheritDoc} */ @Override public void write(char[] cbuf, int off, int len) throws IOException { try { document.replace(document.getLength(), 0, new String(cbuf, off, len)); } catch (BadLocationException e) { throw new IOException(e.getMessage()); } } } /** * DEFAULT_ENCODING */ private static final String DEFAULT_ENCODING = "cp1251"; //$NON-NLS-1$ /** * MAX_NOTIFICATION_SIZE */ private static final int MAX_NOTIFICATION_SIZE = 1024; /** * Book mark annotation type. */ private static final String BOOKMARK_ANNOTATION_TYPE = "org.eclipse.ui.workbench.texteditor.bookmark"; //$NON-NLS-1$ /** * Default font constant. */ private static final String DEFAULT_FONT = "DEFAULT_TAIL_VIEW_FONT"; //$NON-NLS-1$ /** * Log tab prefix. */ private static final String PREFIX = "LogTab"; //$NON-NLS-1$ /** * Log tab prefix. */ private static final String BOOKMARK_PREFIX = PREFIX + ".BookmarkAction"; //$NON-NLS-1$ /** * The width of the vertical ruler. */ protected final static int VERTICAL_RULER_WIDTH = 12; /** * Max colorization columns. */ protected int _maxColorizingColumns = 512; /** * logView */ private final LogView logView; /** * URI. */ private final URI uri; /** * Tab item. */ private CTabItem item; /** * Ruler. */ private IVerticalRuler ruler; /** * Current global offset. */ private long currentGlobalOffset = 0; /** * Content dependent actions. */ private Set contentDependentActions = new HashSet(); /** * Log watcher. */ private ILogWatcher watcher; /** * Resource. */ private ILogResource resource = null; /** * Menu manager. */ private MenuManager menuMgr; /** * Font registry. */ private FontRegistry fontRegistry = new FontRegistry(); /** * Color registry. */ private ColorRegistry colorRegistry = new ColorRegistry(); /** * Viewer. */ private SourceViewer viewer; /** * Bold caption font. */ private Font boldFont; /** * Normal caption font. */ private Font normalFont; /** * Italic caption font. */ private Font italicFont; /** * Text foreground color. */ private Color textForeground; /** * Status filed that indicates whether tab has unread data available. */ private volatile boolean unreadDataAvailable; /** * Whether resource is available. */ private volatile boolean resourceAvailable; /** * Add Bookmark action. */ private LoggingMarkerRulerAction actionAddBookmark; /** * Annotation model. */ private UniformResourceMarkerAnnotationModel model; /** * Overview ruler. */ private IOverviewRuler overviewRuler; /** * Annotation preferences. */ private MarkerAnnotationPreferences fAnnotationPreferences; /** * Bookmark color. */ private Color bookmarkColor = new Color(Display.getCurrent(), 0, 0, 255); /** * Line style listener. */ private LineStyleListener _lineStyleListener; /** * Logging colorizer. */ private LoggingColorizer _colorizer; /** * Text change listener. */ private TextChangeListener _textChangeListener; /** * Lexer. */ private ILexer lexer; /** * Whether to follow document tail. */ private boolean followTail = true; /** * Document. */ private IDocument document; /** * Colorization preference listener. */ private IPropertyChangeListener colorizationPreferencesListener; /** * Logging preference listener. */ private ILoggingPreferenceListener loggingPreferenceListener; /** * Tail listener. */ private ILogTailListener tailListener; /** * Helper for handling decoration. */ protected SourceViewerDecorationSupport decorationSupport; /** * Logging lexeme manager. */ protected LoggingLexemeManager lexememanager; /** * Composite. */ private Composite composite; /** * Whether the tab supports log erasing. */ private boolean supportsErase; /** * LogView.LogTab constructor. * @param uri - URI. * @param logView - log view that contains this tab. * @param name - tab name. maybe null. * @param supportsErase - whether the tab's log supports log erasing. */ public LogTab(LogView logView, URI uri, String name, boolean supportsErase) { this.logView = logView; this.uri = uri; this.supportsErase = supportsErase; // creating tab item String tabName = name; if (tabName == null) { tabName = getTabName(uri.getPath()); } fAnnotationPreferences= EditorsPlugin.getDefault().getMarkerAnnotationPreferences(); item = createLogTabItem(tabName, uri); createColorizer(); try { resource = LogResourceFactory.createResource(uri); resource.setEncoding(Charset.forName(LoggingPlugin.getDefault().getLoggingPreferences().getDefaultEncoding())); } catch (IOException e) { //e.printStackTrace(new PrintWriter(writer)); e.printStackTrace(new PrintWriter(new DocumentWriter(getDocument()))); return; } initAnnotationModel(); createActions(); hookRulerContextMenu(); initializeFonts(); initializeColors(); getDocument().addDocumentListener(new IDocumentListener() { public void documentAboutToBeChanged(DocumentEvent event) { } public void documentChanged(DocumentEvent event) { } }); LoggingPreferences preferences = LoggingPlugin.getDefault().getLoggingPreferences(); int timeout = preferences.getReadTimeout(); int readBuffer = preferences.getReadBuffer(); int maxBytesPerSecond = (int) ((((long)readBuffer) * 1000l) / ((long)timeout)); watcher = resource .getResourceWatcher(new DisplayThreadProxy(item.getDisplay()), maxBytesPerSecond, readBuffer, timeout, Charset.forName(DEFAULT_ENCODING), LoggingPlugin.getDefault().getLoggingPreferences().getBacklogLines()); //registering tail listener registerTailListener(); //registering resource listener registerResourceListener(); //starting watching watcher.startWatching(); //settign selection to the new tab this.logView.tabFolder.setSelection(item); linkColorer(); } /** * Gets tab URI. * @return tab URI. */ public URI getURI() { return uri; } /** * Gets viewer. * @return viewer. */ public TextViewer getViewer() { return viewer; } /** * Gets tab name. * @return tab name. */ public String getName() { return item.getText(); } /** * Gets whether the tab's log supports log erase. * @return whether the tab's log supports log erase. */ public boolean supportsLogErase() { return this.supportsErase; } /** * Sets tab name. * @param name - name to set. */ public void setName(String name) { item.setText(name); } /** * Closes the tab. */ public void close() { watcher.stopWatching(); watcher.close(); //console.destroy(); try { resource.close(); } catch (IOException e) { IdeLog.logError(LoggingPlugin.getDefault(), com.aptana.ide.logging.view.Messages.LogTab_4, e); } disposeListeners(); if (boldFont != null) { boldFont.dispose(); } if (italicFont != null) { italicFont.dispose(); } bookmarkColor.dispose(); item.dispose(); if (decorationSupport != null) { decorationSupport.dispose(); decorationSupport= null; } } /** * Starts watching. */ public void start() { watcher.startWatching(); } /** * Stops watching. */ public void stop() { watcher.stopWatching(); } /** * Whether watching is on. * @return true if watching, false otherwise. */ public boolean isWatching() { return watcher.watchingStatus(); } /** * Reloads the log. */ public void reload() { watcher.resetWatching(); clear(); watcher.startWatching(); } /** * Gets tab item. * @return tab item. */ public CTabItem getItem() { return item; } /** * Clears tab. */ public void clear() { if (document != null) { document.set(""); //$NON-NLS-1$ } //currentLexemes.clear(); currentGlobalOffset = 0; if (lexememanager != null) { List<String> empty = Collections.emptyList(); lexememanager.dataAvailable(empty); } } /** * Notifies current tab, it is selected. */ public void selected() { //removing "unread data state" if any unreadDataAvailable = false; makeTabBold(false); if (!resourceAvailable) { makeTabShaded(true); } } /** * Clears log file. */ public void clearLogFile() { try { resource.clear(); } catch (IOException e) { MessageDialog.openError(this.getItem().getControl().getShell(), com.aptana.ide.logging.view.Messages.LogTab_2, com.aptana.ide.logging.view.Messages.LogTab_1 //$NON-NLS-2$ + resource.getURI().getPath()); } reload(); } /** * Gets whether unread data is available. * @return whether unread data is available. */ public boolean hasUnreadData() { return unreadDataAvailable; } /** * Sets whether viewer follows document tail. * @param followTail - whether to follow tail. */ public void setFollowTail(boolean followTail) { this.followTail = followTail; } /** * Inverts whether viewer follows document tail. */ public void invertFollowTail() { followTail = !followTail; } /** * Gets selection. * @return selection or null */ String getSelection() { if (viewer == null) { return null; } TextSelection selection = (TextSelection) viewer.getSelection(); if (selection == null) { return null; } try { return getDocument().get(selection.getOffset(), selection.getLength()); } catch(BadLocationException ex) { IdeLog.logError(LoggingPlugin.getDefault(), com.aptana.ide.logging.view.Messages.LogTab_ERR_Exception, ex); return null; } } /** * Refreshes tab viewer. */ void refreshViewer() { viewer.refresh(); } /** * Creates new logger tab item * * @param tabName - * tab name. * @return tab item */ private CTabItem createLogTabItem(String tabName, URI uri) { CTabItem item = new CTabItem(this.logView.tabFolder, SWT.DEFAULT); item.setText(tabName); if (uri != null) { item.setToolTipText(uri.getPath()); } document = new Document(); lexememanager = new LoggingLexemeManager(document, LoggingPlugin.getDefault().getLoggingPreferences()); // creating a view IAnnotationAccess access = new DefaultMarkerAnnotationAccess(); ruler = createVerticalRuler(access); overviewRuler = createOverviewRuler(access); createViewer(item); return item; } /** * Recreates viewer. */ void recreateViewer() { decorationSupport.dispose(); decorationSupport = null; composite.dispose(); viewer = null; LoggingPlugin.getDefault().getLoggingPreferences(). removePreferenceListener(loggingPreferenceListener); createViewer(item); } /** * @param item */ private void createViewer(CTabItem item) { int viewerStyle = 0; if (LoggingPlugin.getDefault().getLoggingPreferences().getWrapping()) { viewerStyle = SWT.V_SCROLL | SWT.WRAP | SWT.BORDER; } else { viewerStyle = SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER; } composite = new Composite(this.logView.tabFolder, SWT.NONE); GridLayout layout = new GridLayout(1, true); layout.marginHeight =3; layout.marginWidth =3; GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); composite.setLayout(layout); composite.setLayoutData(data); composite.setBackground(UnifiedColorManager.getInstance().getColor(new RGB(220, 220, 220))); viewer = new LogSourceViewer(composite, ruler, overviewRuler, true, viewerStyle); viewer.addSelectionChangedListener(new ISelectionChangedListener(){ public void selectionChanged(SelectionChangedEvent event) { // TODO Auto-generated method stub int i = 0; i++; } }); viewer.setDocument(getDocument()); Control control = viewer.getControl(); control.setLayoutData(data); viewer.getTextWidget().setFont(LoggingPlugin.getDefault().getLoggingPreferences().getFont()); item.setControl(composite); //adding logger menu Menu menu = logView.menuMgr.createContextMenu(viewer.getTextWidget()); viewer.getTextWidget().setMenu(menu); TextListener listener = this.logView.new TextListener(viewer); viewer.addTextInputListener(listener); viewer.addTextListener(listener); viewer.activatePlugins(); createSourceViewerDecorationSupport(viewer); bindToColorizationSave(viewer); //PreferenceUtils.registerFontPreference(viewer.getTextWidget(), LoggingPreferences.MAIN_TEXT_FONT_KEY); } /** * Recreates tab item. * @param name - tab name. * @param newPos - new position. */ void recreateItem(String name, int newPos) { item = new CTabItem(this.logView.tabFolder, SWT.DEFAULT, newPos); item.setText(name); item.setControl(composite); } /** * Binds to colorization save. * @param viewer - viewer. */ private void bindToColorizationSave(final SourceViewer viewer) { colorizationPreferencesListener = new IPropertyChangeListener(){ public void propertyChange(PropertyChangeEvent event) { if (LoggingStructureProvider.COLORIZATION_SAVED.equals(event.getProperty())) { createColorizer(); lexememanager.clearCache(); if (viewer != null) { viewer.setRedraw(false); ((StyledText) LogTab.this.viewer.getTextWidget()).removeLineStyleListener(_lineStyleListener); ((StyledText) LogTab.this.viewer.getTextWidget()).addLineStyleListener(_lineStyleListener); viewer.setRedraw(true); } } } }; UnifiedEditorsPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(colorizationPreferencesListener); LoggingPreferences preferences = LoggingPlugin.getDefault().getLoggingPreferences(); loggingPreferenceListener = new ILoggingPreferenceListener(){ public void rulesChanged() { } public void wrappingChanged(boolean wrapping) { recreateViewer(); } public void fontChanged(Font font) { viewer.getTextWidget().setFont(font); } public void textForegroundColorChanged(Color color) { textForeground = color; } }; preferences.addPreferenceListener(loggingPreferenceListener); } /** * Hooks context menu. */ private void hookRulerContextMenu() { menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { logView.setFocus(); fillRulerContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(ruler.getControl()); ruler.getControl().setMenu(menu); logView.getSite().registerContextMenu(menuMgr, viewer.getSelectionProvider()); } /** * Fills context menu. * @param manager - menu manager. */ private void fillRulerContextMenu(IMenuManager manager) { manager.add(actionAddBookmark); // manager.add(actionAdd); // manager.add(actionDelete); // // manager.add(new Separator()); // // manager.add(actionFindReplace); // // manager.add(new Separator()); // // Other plug-ins can contribute there actions here // manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } /** * Creates vertical ruler. * * @param access - annotation access. * * @return the vertical ruler */ protected IVerticalRuler createVerticalRuler(IAnnotationAccess access) { return new VerticalRuler(VERTICAL_RULER_WIDTH, access); } /** * Creates overview ruler. * * @param access - annotation access. * * @return the overview ruler */ protected IOverviewRuler createOverviewRuler(IAnnotationAccess access) { ISharedTextColors sharedColors= EditorsPlugin.getDefault().getSharedTextColors(); IOverviewRuler ruler= new OverviewRuler(access, VERTICAL_RULER_WIDTH, sharedColors); Iterator e= fAnnotationPreferences.getAnnotationPreferences().iterator(); while (e.hasNext()) { AnnotationPreference preference= (AnnotationPreference) e.next(); if (preference.contributesToHeader()) ruler.addHeaderAnnotationType(preference.getAnnotationType()); } String type = BOOKMARK_ANNOTATION_TYPE; ruler.addAnnotationType(type); ruler.addHeaderAnnotationType(type); ruler.setAnnotationTypeLayer(type, 1); //TODO find why default bookmark preferences fail to be loaded correctly. ruler.setAnnotationTypeColor(type, bookmarkColor); return ruler; } /** * Gets tab name by path. * * @param path - * path. * @return tab name. */ private String getTabName(String path) { Path p = new Path(path); return p.lastSegment(); } /** * Makes tab bold. * @param bold - whether to set bold or to remove. */ private void makeTabBold(boolean bold) { Font currentFont = item.getFont(); int style = currentFont.getFontData()[0].getStyle(); if (bold) { if ((style & SWT.BOLD) != 0) { return; } item.setFont(boldFont); //item.getControl().setFont(boldFont); } else { if ((style & SWT.BOLD) == 0) { return; } item.setFont(normalFont); //item.getControl().setFont(normalFont); } } /** * Makes tab shaded. * @param shaded - true if to turn shaded mode on, false otherwise. */ private void makeTabShaded(boolean shaded) { if (shaded) { Font currentFont = item.getFont(); if (currentFont.equals(italicFont)) { return; } item.setFont(italicFont); } else { Font currentFont = item.getFont(); if (!currentFont.equals(italicFont)) { return; } item.setFont(normalFont); } } /** * Initialized tab fonts. */ private void initializeColors() { textForeground = LoggingPlugin.getDefault().getLoggingPreferences().getTextColor(); } /** * Initialized tab fonts. */ private void initializeFonts() { Font normalFont = item.getFont(); //setting normal font this.normalFont = normalFont; //setting bold font FontData data = normalFont.getFontData()[0]; int originalStyle = data.getStyle(); int boldStyle = originalStyle | SWT.BOLD; data.setStyle(boldStyle); this.boldFont = new Font(normalFont.getDevice(), new FontData[] { data }); //setting italic font int italicStyle = originalStyle | SWT.ITALIC; data = normalFont.getFontData()[0]; data.setStyle(italicStyle); this.italicFont = new Font(normalFont.getDevice(), new FontData[] { data }); } private void createActions() { actionAddBookmark = new LoggingMarkerRulerAction(Messages.getResourceBundle(), BOOKMARK_PREFIX, resource, getDocument(), model, ruler, true, IMarker.BOOKMARK, item.getControl().getShell()); actionAddBookmark.update(); } /** * Initializes annotation model. * @return model */ private void initAnnotationModel() { model = new UniformResourceMarkerAnnotationModel(resource); model.connect(getDocument()); ruler.setModel(model); overviewRuler.setModel(model); } /** * Registers resource listener. */ private void registerResourceListener() { watcher.registerListener(new ILogResourceListener() { public void resourceAvailable(boolean available) { resourceAvailable = available; //if resource became unavailable and we're not in bold mode, //shading tab caption. if (!unreadDataAvailable) { if (!resourceAvailable) { makeTabShaded(true); } else { makeTabShaded(false); } } } }); } /** * Registers tail listener. */ private void registerTailListener() { tailListener = new ILogTailListener() { public void dataAvailable(String data, long globalOffset, long globalLength) { updateData(data, globalOffset, globalLength); } public void errorHappened(Throwable th) { IdeLog.logError(LoggingPlugin.getDefault(), com.aptana.ide.logging.view.Messages.LogTab_ERR_FetchTail, th); //th.printStackTrace(new PrintWriter(new DocumentWriter(getDocument()))); } }; watcher.registerListener(tailListener); } /** * Updates content dependent actions. */ private void updateContentDependentActions() { Iterator it = contentDependentActions.iterator(); while (it.hasNext()) { Action action = (Action) it.next(); if (action instanceof IUpdate) { ((IUpdate) action).update(); } } } /** * Marks action as content dependent. * @param action - action to mark. */ void markAsContentDependent(Action action) { contentDependentActions.add(action); } /** * Links colorer. */ private void linkColorer() { if (_lineStyleListener == null) { _lineStyleListener = new LineStyleListener() { /** * @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(org.eclipse.swt.custom.LineStyleEvent) */ public void lineGetStyle(LineStyleEvent e) { //LexemeList lexemeList = currentLexemes; int orgOffset = e.lineOffset; int offset = orgOffset; int extra = 0; int lineLength = e.lineText.length(); // need to get actual offset values in the doc, // as widget offsets do not include potentially folded code if (viewer instanceof ITextViewerExtension5) { ITextViewerExtension5 v5 = (ITextViewerExtension5) viewer; offset = v5.widgetOffset2ModelOffset(e.lineOffset); extra = offset - e.lineOffset; } int maxLineLength = lineLength > _maxColorizingColumns ? _maxColorizingColumns : lineLength; Lexeme[] lexemes = null; int lineNumber; try { lineNumber = document.getLineOfOffset(e.lineOffset); lexemes = lexememanager.getLexemes(lineNumber); } catch (BadLocationException e1) { IdeLog.logError(LoggingPlugin.getDefault(), com.aptana.ide.logging.view.Messages.LogTab_ERR_Exception, e1); } if (lexemes != null && lexemes.length > 0) { Vector styles = new Vector(); _colorizer.createStyles(styles, lexemes, false); StyleRange[] styleResults = (StyleRange[]) styles.toArray(new StyleRange[] {}); // move styles back to actual widget offsets in case of // folding if (extra > 0) { for (int i = 0; i < styleResults.length; i++) { StyleRange range = styleResults[i]; range.start -= extra; } } e.styles = styleResults; } else { StyleRange[] styles = new StyleRange[1]; styles[0] = new StyleRange(e.lineOffset, e.lineText.length(), textForeground, null); e.styles = styles; } } }; } // Repaint lines if the user is making changes if (_textChangeListener == null) { _textChangeListener = new TextChangeListener() { public void textChanging(TextChangingEvent event) { } public void textChanged(TextChangedEvent event) { StyledText text = getViewer().getTextWidget(); redrawFrom(text, text.getLineAtOffset(text.getCaretOffset())); } // Used with complex text change events (tabbing, replacement, // etc.) public void textSet(TextChangedEvent event) { StyledText text = getViewer().getTextWidget(); redrawFrom(text, 0); } private void redrawFrom(StyledText text, int lno) { if (lno < 0 || lno >= text.getLineCount()) { return; } int height = text.getClientArea().height; int width = text.getClientArea().width + text.getHorizontalPixel(); try { text.redraw(0, 0, width, height, true); } catch (Exception e) { // Catch errors in redraw as they may be intermittent and the subsequent redraw will complete // successfully } } }; } getViewer().getTextWidget().addLineStyleListener(_lineStyleListener); getViewer().getTextWidget().getContent().addTextChangeListener(_textChangeListener); } /** * Adds lexeme to the list. * @param lexeme - lexeme to add. * @param lexemes - lexemes */ private void addLexeme(Lexeme lexeme, LexemeList lexemes) { lexemes.add(lexeme); return; } /** * Gets lexer. * @return */ private ILexer getLexer() { return TokenTypes.getLexerFactory().getLexer(); } /** * Finds last recognized regexp lexeme. * @param lexemes - lexemes to search in. * @return last recognized lexeme. */ private Lexeme findLastRecognizedRegexpLexeme(LexemeList lexemes) { if (lexemes.size() == 0) { return null; } for (int i = lexemes.size()-1; i >= 0; i--) { Lexeme currentLexeme = lexemes.get(i); if (TokenTypes.isRegexpType(currentLexeme.getType())) { return currentLexeme; } } return null; } /** * Disposes listeners. */ private void disposeListeners() { UnifiedEditorsPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(colorizationPreferencesListener); LoggingPlugin.getDefault().getLoggingPreferences().removePreferenceListener(loggingPreferenceListener); watcher.removeListener(tailListener); } /** * Creates colorizer. */ private void createColorizer() { _colorizer = new LoggingColorizer(getLexer().getTokenList(TokenTypes.LANGUAGE)); } /** * Gets autobolding preference. * @return autobolding preference. */ private boolean getAutobolding() { return LoggingPlugin.getDefault().getLoggingPreferences().getAutoBolding(); } /** * Gets decoration support. * @param viewer - viewer. * @return decoration support. */ protected void createSourceViewerDecorationSupport(ISourceViewer viewer) { if (decorationSupport == null) { ISharedTextColors sharedColors= EditorsPlugin.getDefault().getSharedTextColors(); decorationSupport= new SourceViewerDecorationSupport(viewer, null, null, sharedColors); configureSourceViewerDecorationSupport(decorationSupport); //decorationSupport.showCursorLine(); decorationSupport.install(LoggingPlugin.getDefault().getPreferenceStore());; } } /** * Configures decoration support. * @param support - support to configure. */ protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { support.setCursorLinePainterPreferenceKeys( LoggingPreferences.CURSORLINE_KEY, LoggingPreferences.CURSORLINE_COLOR_KEY); support.setSymbolicFontName(LoggingPreferences.MAIN_TEXT_FONT_KEY); } /** * @return */ private IDocument getDocument() { return document; } /** * Updates data. * @param data - data to add. * @param append - whether to append, or to replace. */ private void updateData(String data, long globalOffset, long globalLength) { //if this tab is not active, we have an unread data if (!LogTab.this.equals(logView.getActiveTab()) && getAutobolding()) { //setting "unreadDataAvailable" states unreadDataAvailable = true; //making tab caption bold makeTabBold(true); } Integer topIndex = null; Integer bottomIndex = null; int horizontalScrollPixel = -1; ISelection selection = null; if (viewer != null) { //saving horizontal scrolling position horizontalScrollPixel = viewer.getTextWidget().getHorizontalPixel(); //saving selection viewer.setRedraw(false); selection = viewer.getSelection(); topIndex = viewer.getTopIndex(); bottomIndex = viewer.getBottomIndex(); } updateDocumentData(data, globalOffset, globalLength); if (viewer != null) { viewer.setRedraw(true); //viewer.setSelection(new TextSelection(0,0), false); // if (selection != null) // { // viewer.setSelection(selection, false); // } if (followTail) { ((LogSourceViewer) viewer).revealLastLine(); } else { if (topIndex != null) { try { int startOffset = document.getLineOffset(topIndex); int endOffset = document.getLineOffset(bottomIndex) + document.getLineLength(bottomIndex); viewer.revealRange(startOffset, endOffset - startOffset); topIndex = viewer.getTopIndex(); bottomIndex = viewer.getBottomIndex(); } catch (BadLocationException e) { IdeLog.logError(LoggingPlugin.getDefault(), com.aptana.ide.logging.view.Messages.LogTab_ERR_Exception, e); } } } final int horizontalScrollPixel1 = horizontalScrollPixel; //restoring horizontal scrolling position viewer.getTextWidget().setHorizontalPixel(horizontalScrollPixel1); } } /** * Updates document data. * @param data - data. * @param globalOffset - global offset. * @param globalLength - global length. */ private void updateDocumentData(String data, long globalOffset, long globalLength) { try { IDocument document = getDocument(); //applying data long lastGlobalPosition = currentGlobalOffset + document.getLength(); if (globalOffset == 0 && globalLength >= Integer.MAX_VALUE) { document.replace(0, document.getLength(), data); currentGlobalOffset = globalOffset; } else if (globalOffset < currentGlobalOffset) { int diff = (int) (currentGlobalOffset - globalOffset); //checking for part visibility if (diff <= data.length()) { int lengthToReplace = (int) (globalLength - diff); String beginData = data.substring(0, diff); String innrerData = data.substring(diff, data.length()); //replacing inner data replaceInnerData(innrerData, currentGlobalOffset, lengthToReplace, document); //adding begin data document.replace(0, diff, beginData); currentGlobalOffset = globalOffset; } else { //in other case, replacing the whole document document.replace(0, document.getLength(), data); currentGlobalOffset = globalOffset; } } else if (globalOffset >= currentGlobalOffset && globalOffset < lastGlobalPosition) { //replacing inner data replaceInnerData(data, globalOffset, globalLength, document); } else if (globalOffset == lastGlobalPosition) { //adding data to the end of the document document.replace(document.getLength(), 0, data); } else { //replacing whole data with new data document.replace(0, document.getLength(), data); currentGlobalOffset = globalOffset; } //fixing number of lines if needed int numberOfLines = document.getNumberOfLines(); int allowedNumberOfLines = LoggingPlugin.getDefault().getLoggingPreferences().getBacklogLines(); List<String> topLines = new ArrayList<String>(); if (numberOfLines > allowedNumberOfLines) { int toRemoveLinesNum = numberOfLines - allowedNumberOfLines; int offset = document.getLineOffset(toRemoveLinesNum); for (int i = 0; i < toRemoveLinesNum; i++) { int lineOffset = document.getLineOffset(i); int lineLength = document.getLineLength(i); topLines.add(document.get(lineOffset, lineLength)); } currentGlobalOffset += offset; document.replace(0, offset, ""); //$NON-NLS-1$ } if(lexememanager != null) { lexememanager.dataAvailable(topLines); } } catch (Throwable e) { IdeLog.logError(LoggingPlugin.getDefault(), com.aptana.ide.logging.view.Messages.LogTab_8, e); } } /** * Replaces data, when change starts inside the document. * @param data - data to replace with. * @param globalOffset - global offset. * @param globalLength - global length. * @param document - codument. * @throws BadLocationException */ private void replaceInnerData(String data, long globalOffset, long globalLength, IDocument document) throws BadLocationException { int diff = (int) (globalOffset - currentGlobalOffset); int lengthToReplace = (int) globalLength; if (lengthToReplace == Integer.MAX_VALUE || diff + lengthToReplace > document.getLength()) { lengthToReplace = document.getLength() - diff; } document.replace(diff, lengthToReplace, data); } }