package org.reldb.dbrowser.ui.log; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.widgets.ToolItem; 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.events.ShellAdapter; import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Rectangle; import org.reldb.dbrowser.loading.Loading; import org.reldb.dbrowser.ui.IconLoader; import org.reldb.dbrowser.ui.preferences.Preferences; public class LogWin { private static final int threadLoadMax = 10; private static LogWin window; protected static Shell shell; private StyledText textLog; private Color red; private Color black; private Color blue; private static class Message { String msg; Color color; public Message(String msg, Color color) { this.msg = msg; this.color = color; } public Message() { this.msg = null; this.color = null; } public boolean isNull() { return this.msg == null && this.color == null; } } private BlockingQueue<Message> messageQueue; private boolean running = true; private FileDialog saveTextDialog; private static final String rectPrefName = "logwin.rect"; protected LogWin(Composite parent) { messageQueue = new LinkedBlockingQueue<Message>(); shell = new Shell(parent.getDisplay()); shell.setText("Rel System Log"); shell.setLayout(new FormLayout()); shell.addShellListener(new ShellAdapter() { @Override public void shellClosed(ShellEvent e) { e.doit = false; shell.setVisible(false); } }); shell.addListener(SWT.Move, new Listener() { @Override public void handleEvent(Event event) { Preferences.setPreference(rectPrefName, shell.getBounds()); } }); shell.addListener(SWT.Resize, new Listener() { @Override public void handleEvent(Event event) { Preferences.setPreference(rectPrefName, shell.getBounds()); } }); red = new Color(shell.getDisplay(), 200, 0, 0); black = new Color(shell.getDisplay(), 0, 0, 0); blue = new Color(shell.getDisplay(), 0, 0, 128); ToolBar toolBar = new ToolBar(shell, SWT.FLAT | SWT.RIGHT); FormData fd_toolBar = new FormData(); fd_toolBar.top = new FormAttachment(0); fd_toolBar.left = new FormAttachment(0); toolBar.setLayoutData(fd_toolBar); ToolItem tltmClear = new ToolItem(toolBar, SWT.NONE); tltmClear.setToolTipText("Clear"); tltmClear.setImage(IconLoader.loadIcon("clearIcon")); tltmClear.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { textLog.setText(""); } }); ToolItem tltmSave = new ToolItem(toolBar, SWT.NONE); tltmSave.setToolTipText("Save"); tltmSave.setImage(IconLoader.loadIcon("saveIcon")); tltmSave.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (saveTextDialog == null) { saveTextDialog = new FileDialog(shell, SWT.SAVE); saveTextDialog.setFilterPath(System.getProperty("user.home")); saveTextDialog.setFilterExtensions(new String[] {"*.txt", "*.*"}); saveTextDialog.setFilterNames(new String[] {"Text", "All Files"}); saveTextDialog.setText("Save Output"); saveTextDialog.setOverwrite(true); } String fname = saveTextDialog.open(); if (fname == null) return; try { BufferedWriter f = new BufferedWriter(new FileWriter(fname)); f.write(textLog.getText()); f.close(); output("Saved " + fname, blue); } catch (IOException ioe) { output(ioe.toString(), red); } } }); textLog = new StyledText(shell, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI | SWT.H_SCROLL); textLog.setEditable(false); FormData fd_textLog = new FormData(); fd_textLog.bottom = new FormAttachment(100); fd_textLog.right = new FormAttachment(100); fd_textLog.top = new FormAttachment(toolBar); fd_textLog.left = new FormAttachment(0); textLog.setLayoutData(fd_textLog); new Thread(new Runnable() { @Override public void run() { while (running) { // wait for data to show up Message awaitedEntry; try { awaitedEntry = messageQueue.take(); } catch (InterruptedException e1) { continue; } if (parent.isDisposed() || parent.getDisplay().isDisposed()) { running = false; return; } parent.getDisplay().syncExec(new Runnable() { @Override public void run() { if (!textLog.isDisposed()) { try { Message message = awaitedEntry; int threadLoadCount = 0; do { if (message.isNull()) { running = false; return; } else { cull(); StyleRange styleRange = new StyleRange(); styleRange.start = textLog.getCharCount(); styleRange.length = message.msg.length(); styleRange.fontStyle = SWT.NORMAL; styleRange.foreground = message.color; textLog.append(message.msg); textLog.setStyleRange(styleRange); } if (++threadLoadCount > threadLoadMax) { // exit every so often, because staying in syncExec too long causes UI lag return; } } while ((message = messageQueue.poll(100, TimeUnit.MILLISECONDS)) != null); textLog.setCaretOffset(textLog.getCharCount()); textLog.setSelection(textLog.getCaretOffset(), textLog.getCaretOffset()); } catch (InterruptedException e) { return; } } } }); } } }).start(); } /** * Open the window. * @param parent */ public static void open() { if (shell.isVisible()) return; Rectangle rect = Preferences.getPreferenceRectangle(rectPrefName); if (rect.height > 0 && rect.width > 0) shell.setBounds(rect); shell.open(); shell.layout(); } /** * Close the window. */ private void close() { shell.close(); } public void dispose() { close(); red.dispose(); black.dispose(); blue.dispose(); } private void cull() { if (textLog.getText().length() > 1000000) textLog.setText("[...]\n" + textLog.getText().substring(10000)); } private void output(String s, Color color) { messageQueue.add(new Message(s, color)); } private static Interceptor outInterceptor; private static Interceptor errInterceptor; public static void install(Composite parent) { window = new LogWin(parent); class LogMessages implements Logger { public void log(String s) { Loading.action(s); window.output(s, window.black); } }; class LogErrors implements Logger { public void log(String s) { window.output(s, window.red); } }; outInterceptor = new Interceptor(System.out, new LogMessages()); outInterceptor.attachOut(); errInterceptor = new Interceptor(System.err, new LogErrors()); errInterceptor.attachErr(); } public static void remove() { outInterceptor.detachOut(); errInterceptor.detachErr(); window.messageQueue.add(new Message()); window.messageQueue.clear(); window.messageQueue.add(new Message()); window.dispose(); } }