/**
* 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 other free and open source software ("FOSS") 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;
import java.io.File;
import java.util.Vector;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextViewerExtension5;
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.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
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.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
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.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import com.aptana.ide.core.FileUtils;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.StringUtils;
import com.aptana.ide.core.ui.CoreUIUtils;
import com.aptana.ide.core.ui.Messages;
import com.aptana.ide.editors.UnifiedEditorsPlugin;
import com.aptana.ide.editors.unified.UnifiedColorManager;
import com.aptana.ide.lexer.ILexer;
import com.aptana.ide.lexer.Lexeme;
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.preferences.ILoggingPreferenceListener;
import com.aptana.ide.logging.preferences.LoggingStructureProvider;
/**
* Log read-only editor.
*
* @author Denis Denisenko
*/
public class LogReadonlyEditor extends TextEditor
{
class ReadOnlySourceViewer extends SourceViewer
{
/**
* ReadOnlySourceViewer constructor.
*
* @param parent -
* parent.
* @param ruler -
* vertical ruler.
* @param styles -
* styles.
*/
public ReadOnlySourceViewer(Composite parent, IVerticalRuler ruler, int styles)
{
super(parent, ruler, styles);
}
/**
* {@inheritDoc}
*/
@Override
protected void createControl(Composite parent, int styles)
{
super.createControl(parent, styles | SWT.READ_ONLY);
this.getTextWidget().setEditable(false);
}
}
/**
* Line style listener.
*/
private LineStyleListener _lineStyleListener;
/**
* Logging colorizer.
*/
private LoggingColorizer _colorizer;
/**
* Logging lexeme manager.
*/
protected LoggingLexemeManager lexememanager;
/**
* Max colorization columns.
*/
protected int _maxColorizingColumns = 512;
/**
* Composite.
*/
private Composite composite;
/**
* Helper for handling decoration.
*/
protected SourceViewerDecorationSupport decorationSupport;
/**
* Text foreground color.
*/
private Color textForeground;
/**
* Viewer.
*/
private SourceViewer viewer;
private ToolBar refreshBar;
private ToolItem refreshItem;
private IFileStore refreshableFile;
private IPropertyChangeListener colorizationPreferencesListener;
/**
* {@inheritDoc}
*/
@Override
public boolean isEditable()
{
return false;
}
/**
* @see org.eclipse.ui.texteditor.AbstractTextEditor#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
*/
public void init(IEditorSite site, IEditorInput input) throws PartInitException
{
super.init(site, input);
if (input instanceof IURIEditorInput)
{
IFileStore fileStore;
try {
fileStore = EFS.getStore(((IURIEditorInput) input).getURI());
if (fileStore.toLocalFile(EFS.NONE, null) == null) {
this.refreshableFile = fileStore;
}
} catch (CoreException e) {
IdeLog.logImportant(LoggingPlugin.getDefault(), StringUtils.EMPTY, e);
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles)
{
return createViewer(parent, ruler);
}
/**
* {@inheritDoc}
*/
@Override
protected void initializeEditor()
{
super.initializeEditor();
createColorizer();
initializeColors();
linkColorer();
}
/**
* @param item
*/
private ISourceViewer createViewer(Composite parent, IVerticalRuler ruler)
{
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(parent, 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)));
if (refreshableFile != null)
{
refreshBar = new ToolBar(composite, SWT.FLAT);
refreshBar.setBackground(UnifiedColorManager.getInstance().getColor(new RGB(220, 220, 220)));
refreshBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
refreshItem = new ToolItem(refreshBar, SWT.PUSH);
refreshItem.setImage(LoggingPlugin.getImage("icons/active/refresh_active.gif")); //$NON-NLS-1$
refreshItem.setToolTipText(Messages.LogReadonlyEditor_TTP_Refresh);
refreshItem.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
refreshItem.setEnabled(false);
Job job = new Job(Messages.LogReadonlyEditor_Job_Refresh)
{
protected IStatus run(IProgressMonitor monitor)
{
try {
final File temp = refreshableFile.toLocalFile(EFS.CACHE, monitor);
UIJob update = new UIJob(Messages.LogReadonlyEditor_Job_Updating)
{
public IStatus runInUIThread(IProgressMonitor monitor)
{
try
{
if (viewer != null && viewer.getTextWidget() != null
&& !viewer.getTextWidget().isDisposed())
{
refreshItem.setEnabled(true);
if (temp != null) {
viewer.getTextWidget().setText(FileUtils.readContent(temp));
}
}
}
catch (Exception e)
{
IdeLog.logError(LoggingPlugin.getDefault(),
Messages.LogReadonlyEditor_ERR_Update);
}
catch (Error e)
{
IdeLog.logError(LoggingPlugin.getDefault(),
Messages.LogReadonlyEditor_ERR_Update);
}
return Status.OK_STATUS;
}
};
update.schedule();
} catch (CoreException e) {
CoreUIUtils.showError(StringUtils.format(
Messages.FileExplorerView_UnableToSaveRemoteFile, new String[] {
refreshableFile.toString() }), e);
}
return Status.OK_STATUS;
}
};
job.schedule();
}
});
}
viewer = new ReadOnlySourceViewer(composite, ruler, viewerStyle);
viewer.addTextInputListener(new ITextInputListener()
{
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput)
{
}
public void inputDocumentChanged(IDocument oldInput, IDocument newInput)
{
lexememanager = new LoggingLexemeManager(newInput, LoggingPlugin.getDefault().getLoggingPreferences());
}
});
Control control = viewer.getControl();
control.setLayoutData(data);
viewer.getTextWidget().setFont(LoggingPlugin.getDefault().getLoggingPreferences().getFont());
viewer.activatePlugins();
createSourceViewerDecorationSupport(viewer);
bindToColorizationSave(/* viewer */);
viewer.getTextWidget().addLineStyleListener(_lineStyleListener);
return viewer;
}
/**
* 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);
ISourceViewer sv = getSourceViewer();
if(sv != null && sv.getTextWidget() != null)
{
StyledText st = sv.getTextWidget();
st.removeLineStyleListener(_lineStyleListener);
st.addLineStyleListener(_lineStyleListener);
}
viewer.setRedraw(true);
}
}
}
};
UnifiedEditorsPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(
colorizationPreferencesListener);
LoggingPreferences preferences = LoggingPlugin.getDefault().getLoggingPreferences();
ILoggingPreferenceListener loggingPreferenceListener = new ILoggingPreferenceListener()
{
public void rulesChanged()
{
}
public void wrappingChanged(boolean wrapping)
{
// TODO handle this
// recreateViewer();
}
public void fontChanged(Font font)
{
if (viewer != null && viewer.getTextWidget() != null && !viewer.getTextWidget().isDisposed())
{
LogReadonlyEditor.this.viewer.getTextWidget().setFont(font);
}
}
public void textForegroundColorChanged(Color color)
{
textForeground = color;
}
};
preferences.addPreferenceListener(loggingPreferenceListener);
}
/**
* Creates colorizer.
*/
private void createColorizer()
{
_colorizer = new LoggingColorizer(getLexer().getTokenList(TokenTypes.LANGUAGE));
}
/**
* 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)
{
if (viewer == null || viewer.getDocument() == null || lexememanager == null)
{
return;
}
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 (getSourceViewer() instanceof ITextViewerExtension5)
{
ITextViewerExtension5 v5 = (ITextViewerExtension5) getSourceViewer();
offset = v5.widgetOffset2ModelOffset(e.lineOffset);
extra = offset - e.lineOffset;
}
int maxLineLength = lineLength > _maxColorizingColumns ? _maxColorizingColumns : lineLength;
Lexeme[] lexemes = null;
int lineNumber;
try
{
lineNumber = viewer.getDocument().getLineOfOffset(e.lineOffset);
lexemes = lexememanager.getLexemes(lineNumber);
}
catch (BadLocationException e1)
{
IdeLog.logError(LoggingPlugin.getDefault(), Messages.LogReadonlyEditor_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;
}
}
};
}
}
/**
* Initialized tab fonts.
*/
private void initializeColors()
{
textForeground = LoggingPlugin.getDefault().getLoggingPreferences().getTextColor();
}
/**
* Gets decoration support.
*
* @param viewer -
* viewer.
*/
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());
}
}
/**
* @see org.eclipse.ui.texteditor.AbstractTextEditor#isDirty()
*/
public boolean isDirty()
{
return false;
}
/**
* 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);
}
/**
* Gets lexer.
*
* @return - lexer
*/
private ILexer getLexer()
{
return TokenTypes.getLexerFactory().getLexer();
}
/**
* Dispose the editor
*/
public void dispose()
{
super.dispose();
UnifiedEditorsPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(
colorizationPreferencesListener);
}
}