/*
Copyright (C) 2011 monte
This file is part of PSP NetParty.
PSP NetParty is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pspnetparty.client.swt.message;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.hyperlink.HyperlinkManager;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import pspnetparty.client.swt.PlayClient;
import pspnetparty.client.swt.SwtUtils;
import pspnetparty.client.swt.config.IniAppearance;
import pspnetparty.lib.FixedSizeList;
import pspnetparty.lib.Utility;
import pspnetparty.lib.constants.AppConstants;
public class LogViewer {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss");
private FixedSizeList<IMessage> messageList;
private PlayClient application;
private Composite container;
private SourceViewer sourceViewer;
private StyledText logWidget;
private CompositeRuler ruler;
private TimestampRulerColumn timestampRulerColumn;
private NameRulerColumn nameRulerColumn;
private String lineDelimiter;
private ArrayList<StyleRange> styleRanges = new ArrayList<StyleRange>();
public LogViewer(Composite parent, int size, PlayClient application) {
this.application = application;
messageList = new FixedSizeList<IMessage>(size);
ruler = new CompositeRuler();
container = new Composite(parent, SWT.BORDER);
container.setLayout(new FillLayout());
sourceViewer = new SourceViewer(container, ruler, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION | SWT.WRAP);
Document document = new Document();
sourceViewer.setDocument(document);
logWidget = sourceViewer.getTextWidget();
timestampRulerColumn = new TimestampRulerColumn();
nameRulerColumn = new NameRulerColumn();
ruler.addDecorator(0, timestampRulerColumn);
ruler.addDecorator(1, nameRulerColumn);
logWidget.setEditable(false);
logWidget.setLineSpacing(1);
logWidget.setMargins(3, 2, 3, 2);
application.initLogControl(logWidget);
logWidget.addControlListener(new ControlListener() {
@Override
public void controlResized(ControlEvent e) {
ruler.update();
}
@Override
public void controlMoved(ControlEvent e) {
}
});
lineDelimiter = logWidget.getLineDelimiter();
HyperlinkManager manager = new HyperlinkManager(HyperlinkManager.LONGEST_REGION_FIRST);
IHyperlinkPresenter presenter = new IHyperlinkPresenter() {
@Override
public void uninstall() {
}
@Override
public void showHyperlinks(IHyperlink[] hyperlinks) throws IllegalArgumentException {
}
@Override
public void install(ITextViewer textViewer) {
}
@Override
public void hideHyperlinks() {
}
@Override
public boolean canShowMultipleHyperlinks() {
return false;
}
};
manager.install(sourceViewer, presenter, new IHyperlinkDetector[] { new URLHyperlinkDetector() }, SWT.NONE);
applyAppearance();
Menu menu = new Menu(parent.getShell(), SWT.POP_UP);
logWidget.setMenu(menu);
MenuItem menuCopyLog = new MenuItem(menu, SWT.PUSH);
menuCopyLog.setText("ログをコピー");
menuCopyLog.addListener(SWT.Selection, new Listener() {
private Date date = new Date();
@Override
public void handleEvent(Event event) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < messageList.size(); i++) {
IMessage log = messageList.get(i);
date.setTime(log.getTimestamp());
sb.append(DATE_FORMAT.format(date));
if (Utility.isEmpty(log.getName())) {
sb.append(' ');
} else {
sb.append(" <").append(log.getName()).append("> ");
}
sb.append(log.getMessage());
sb.append(AppConstants.NEW_LINE);
}
LogViewer.this.application.putClipboard(sb.toString());
}
});
new MenuItem(menu, SWT.SEPARATOR);
MenuItem menuClearLog = new MenuItem(menu, SWT.PUSH);
menuClearLog.setText("ログをクリア");
menuClearLog.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
messageList.clear();
if (!styleRanges.isEmpty()) {
styleRanges.clear();
StyleRange[] ranges = new StyleRange[0];
logWidget.setStyleRanges(ranges);
}
logWidget.setText("");
ruler.update();
}
});
}
public void applyAppearance() {
IniAppearance appearance = application.getAppearance();
timestampRulerColumn.setColor(appearance.getColorLogTimestampRulerBG(), appearance.getColorLogTimestampRulerFG());
timestampRulerColumn.resizeWidth(appearance.getLogTimestampRulerWidth());
nameRulerColumn.setColor(appearance.getColorLogNameRulerBG(), appearance.getColorLogNameRulerFG());
nameRulerColumn.resizeWidth(appearance.getLogNameRulerWidth());
int offset = 0;
ArrayList<StyleRange> newStyles = new ArrayList<StyleRange>(messageList.size());
for (int i = 0; i < messageList.size(); i++) {
styleRanges.clear();
IMessage message = messageList.get(i);
message.configureStyle(styleRanges, appearance);
for (StyleRange range : styleRanges) {
range.start += offset;
newStyles.add(range);
}
offset += message.length() + lineDelimiter.length();
}
if (!newStyles.isEmpty()) {
StyleRange[] ranges = newStyles.toArray(new StyleRange[newStyles.size()]);
logWidget.setStyleRanges(ranges);
}
}
public Control getControl() {
return container;
}
public void appendMessage(final IMessage message) {
try {
if (SwtUtils.isNotUIThread()) {
SwtUtils.DISPLAY.asyncExec(new Runnable() {
@Override
public void run() {
appendMessage(message);
}
});
return;
}
int lineCount = logWidget.getLineCount();
int topLineIndex = logWidget.getTopIndex();
int bottomLineIndex = logWidget.getLineIndex(logWidget.getSize().y);
boolean scrollLastLine = lineCount - bottomLineIndex < 2;
IMessage removed = messageList.add(message);
if (removed != null) {
int length = removed.length() + lineDelimiter.length();
logWidget.replaceTextRange(0, length, "");
}
int offset = logWidget.getCharCount();
if (offset > 0)
logWidget.append(lineDelimiter);
logWidget.append(message.getMessage());
styleRanges.clear();
message.configureStyle(styleRanges, application.getAppearance());
for (StyleRange range : styleRanges) {
if (offset > 0)
range.start += offset + lineDelimiter.length();
// System.out.println(range);
logWidget.setStyleRange(range);
}
if (scrollLastLine) {
logWidget.setTopIndex(lineCount);
ruler.update();
} else if (removed != null && topLineIndex > 1) {
logWidget.setTopIndex(topLineIndex - 2);
ruler.update();
}
} catch (SWTException e) {
} catch (RuntimeException e) {
e.fillInStackTrace();
}
}
private class TimestampRulerColumn extends BaseRulerColumn {
private Date date = new Date();
private TimestampRulerColumn() {
super(logWidget, 3);
}
@Override
protected String getLabel(int line) {
if (messageList.size() == 0)
return null;
IMessage message = messageList.get(line);
date.setTime(message.getTimestamp());
return DATE_FORMAT.format(date);
}
void resizeWidth(int width) {
setWidth(width);
}
}
private class NameRulerColumn extends BaseRulerColumn {
private NameRulerColumn() {
super(logWidget, 3);
}
@Override
protected String getLabel(int line) {
if (messageList.size() == 0)
return null;
IMessage message = messageList.get(line);
return message.getName();
}
void resizeWidth(int width) {
setWidth(width);
}
}
}