cc.warlock.rcp.ui.WarlockText.java Source code

Java tutorial

Introduction

Here is the source code for cc.warlock.rcp.ui.WarlockText.java

Source

/**
 * Warlock, the open-source cross-platform game client
 *  
 * Copyright 2008, Warlock LLC, and individual contributors as indicated
 * by the @authors tag. 
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package cc.warlock.rcp.ui;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import cc.warlock.core.client.IClientSettings;
import cc.warlock.core.client.IWarlockClient;
import cc.warlock.core.client.IWarlockClientConnectListener;
import cc.warlock.core.client.IWarlockClientViewer;
import cc.warlock.core.client.IWarlockFont;
import cc.warlock.core.client.IWarlockPattern;
import cc.warlock.core.client.IWarlockStyle;
import cc.warlock.core.client.WarlockClientRegistry;
import cc.warlock.core.client.WarlockColor;
import cc.warlock.core.client.WarlockString;
import cc.warlock.core.client.WarlockStringMarker;
import cc.warlock.core.client.WarlockStyle;
import cc.warlock.core.client.settings.ClientSettings;
import cc.warlock.core.client.settings.PresetStyleConfigurationProvider;
import cc.warlock.core.client.settings.WindowConfigurationProvider;
import cc.warlock.core.settings.IWarlockSetting;
import cc.warlock.core.settings.IWarlockSettingListener;
import cc.warlock.rcp.configuration.GameViewConfiguration;
import cc.warlock.rcp.ui.client.SWTWarlockClientConnectListener;
import cc.warlock.rcp.ui.client.SWTWarlockSettingListener;
import cc.warlock.rcp.util.ColorUtil;
import cc.warlock.rcp.util.FontUtil;
import cc.warlock.rcp.util.RCPUtil;
import cc.warlock.rcp.util.SoundPlayer;

/**
 * This is an extension of the StyledText widget which has special support for
 *  embedding of arbitrary Controls/Links
 * @author Marshall
 */
public class WarlockText {

    private class WindowSettingsListener implements IWarlockSettingListener {
        WindowConfigurationProvider provider;
        IWarlockSettingListener listener = new SWTWarlockSettingListener(this);

        public WindowSettingsListener(WindowConfigurationProvider provider) {
            this.provider = provider;
            provider.addListener(listener);
        }

        public void settingChanged(IWarlockSetting setting) {
            loadSettings();
        }

        public void remove() {
            provider.removeListener(listener);
        }
    }

    private IWarlockClient client;
    private StyledText textWidget;
    private Cursor handCursor, defaultCursor;
    private int lineLimit = 5000;
    private int doScrollDirection = SWT.DOWN;
    private boolean ignoreEmptyLines = true;
    private Font monoFont = null;
    private LinkedList<WarlockStringMarker> markers = new LinkedList<WarlockStringMarker>();
    private IWarlockClientConnectListener clientListener = new SWTWarlockClientConnectListener(
            new IWarlockClientConnectListener() {
                @Override
                public void clientConnected(IWarlockClient client) {
                }

                @Override
                public void clientDisconnected(IWarlockClient client) {
                }

                @Override
                public void clientSettingsLoaded(IWarlockClient client) {
                    loadSettings();
                }
            });
    private WindowSettingsListener settingListener;
    final private String streamName;

    public WarlockText(Composite parent, IWarlockClientViewer viewer, String streamName) {
        this.streamName = streamName;

        textWidget = new StyledText(parent, SWT.V_SCROLL);

        textWidget.setEditable(false);
        textWidget.setWordWrap(true);
        textWidget.setIndent(1);
        textWidget.setCaret(null);

        Display display = parent.getDisplay();
        handCursor = new Cursor(display, SWT.CURSOR_HAND);
        defaultCursor = parent.getCursor();

        RCPUtil.addTextContextMenu(textWidget, viewer, streamName);

        textWidget.addMouseMoveListener(new MouseMoveListener() {
            public void mouseMove(MouseEvent e) {
                try {
                    if (!textWidget.isDisposed() && textWidget.isVisible()) {
                        Point point = new Point(e.x, e.y);
                        int offset = textWidget.getOffsetAtLocation(point);
                        StyleRange range = textWidget.getStyleRangeAtOffset(offset);
                        if (range != null && range.data != null) {
                            textWidget.setCursor(handCursor);
                            return;
                        }
                        textWidget.setCursor(defaultCursor);
                    }
                } catch (IllegalArgumentException ex) {
                    // swallow -- this happens if the mouse cursor moves to an
                    // area not covered by the imaginary rectangle surround the
                    // current text
                    textWidget.setCursor(defaultCursor);
                }
            }
        });

        textWidget.addMouseListener(new MouseListener() {
            public void mouseDoubleClick(MouseEvent e) {
            }

            public void mouseDown(MouseEvent e) {
            }

            public void mouseUp(MouseEvent e) {
                try {
                    Point point = new Point(e.x, e.y);
                    int offset = textWidget.getOffsetAtLocation(point);
                    StyleRange range = textWidget.getStyleRangeAtOffset(offset);
                    if (range != null && range.data != null && range.data instanceof Runnable) {
                        ((Runnable) range.data).run();
                    }
                } catch (IllegalArgumentException ex) {
                    // swallow -- see note above
                }
            }
        });
    }

    public void selectAll() {
        textWidget.selectAll();
    }

    public void copy() {
        textWidget.copy();
    }

    public void pageUp() {
        if (isAtBottom()) {
            textWidget.setCaretOffset(textWidget.getCharCount());
        }
        textWidget.invokeAction(ST.PAGE_UP);
    }

    public void pageDown() {
        textWidget.invokeAction(ST.PAGE_DOWN);
    }

    public void clearText() {
        textWidget.setText("");
        markers.clear();
    }

    public void setLineLimit(int limit) {
        lineLimit = limit;
    }

    public void appendRaw(String string) {
        boolean atBottom = isAtBottom();

        textWidget.append(string);

        if (atBottom)
            scrollToEnd();
    }

    private Pattern newlinePattern = Pattern.compile("\r?\n");

    private void removeEmptyLines(int offset) {
        int line = textWidget.getLineAtOffset(offset);
        int start = textWidget.getOffsetAtLine(line);
        int end = textWidget.getCharCount();
        if (start >= end)
            return;
        String str = textWidget.getTextRange(start, end - start);
        Matcher m = newlinePattern.matcher(str);

        int lineStart = 0;
        while (m.find(lineStart)) {
            if (lineStart == m.start()) {
                int matchPos = start + m.start();
                int matchLen = m.end() - m.start();
                // Add the newline marker. We give it an initial length of 1
                //   so it gets added correctly into the tree of markers
                WarlockStringMarker marker = new WarlockStringMarker(new WarlockStyle("newline"), matchPos,
                        matchPos + matchLen);
                this.addInternalMarker(marker, markers);

                // then remove the newline from the text
                textWidget.replaceTextRange(matchPos, matchLen, "");
                // and shrink down the newline marker because the actual newline is no longer there.
                marker.setEnd(matchPos);
                WarlockStringMarker.updateMarkers(-matchLen, marker, markers);
                // Recursive call. if this could be a tail call, that would be awesome.
                removeEmptyLines(start);
                break;
            } else {
                lineStart = m.end();
            }
        }
    }

    private void restoreNewlines(int offset, Collection<WarlockStringMarker> markerList) {
        for (Iterator<WarlockStringMarker> iter = markerList.iterator(); iter.hasNext();) {
            WarlockStringMarker marker = iter.next();

            Collection<WarlockStringMarker> subList = marker.getSubMarkers();
            if (subList != null)
                restoreNewlines(offset, subList);

            // check to make sure we're a newline in the appropriate area
            if (marker.getStart() < offset)
                continue;
            String name = marker.getName();
            if (name == null || !name.equals("newline"))
                continue;

            // check if we're an empty line
            if (marker.getStart() == 0 || textWidget.getTextRange(marker.getStart() - 1, 1).equals("\n"))
                continue;

            // we're not an empty line, put us back into action
            textWidget.replaceTextRange(marker.getStart(), 0, "\n");
            // TODO: this should actually just affect markers after us... oh well.
            WarlockStringMarker.updateMarkers(1, marker, markers);
            iter.remove();
        }
    }

    private Collection<StyleRange> mergeStyleRangeLists(Collection<StyleRange> list1,
            Collection<StyleRange> list2) {
        LinkedList<StyleRange> resultList = new LinkedList<StyleRange>();
        for (StyleRange style : list1) {
            resultList.add(style);
        }

        mergeLoop: for (StyleRange mergingStyle : list2) {
            if (mergingStyle == null)
                continue;
            for (ListIterator<StyleRange> iter = resultList.listIterator(); iter.hasNext();) {
                StyleRange style = iter.next();
                if (style == null)
                    continue;

                // if the mergeStyle came before the current style, add the
                //   mergeStyle before it and go to the next mergeStyle
                if (style.start >= mergingStyle.start + mergingStyle.length) {
                    iter.previous();
                    iter.add(mergingStyle);
                    continue mergeLoop;
                }

                // If the mergeStyle came after the current style, continue on
                if (mergingStyle.start >= style.start + style.length)
                    continue;

                iter.remove();

                int subStart;

                if (style.start < mergingStyle.start) {
                    // Add the style before they overlap
                    StyleRange newStyle = (StyleRange) style.clone();
                    newStyle.length = mergingStyle.start - style.start;
                    iter.add(newStyle);

                    subStart = mergingStyle.start;
                } else if (mergingStyle.start < style.start) {
                    // Add the style before they overlap
                    StyleRange newStyle = (StyleRange) mergingStyle.clone();
                    newStyle.length = style.start - mergingStyle.start;
                    iter.add(newStyle);

                    subStart = style.start;
                } else {
                    subStart = style.start;
                }

                int subEnd;
                if (style.start + style.length < mergingStyle.start + mergingStyle.length) {
                    subEnd = style.start + style.length;
                } else {
                    subEnd = mergingStyle.start + mergingStyle.length;
                }

                StyleRange newStyle = this.mergeStyleRanges(style, mergingStyle);
                newStyle.start = subStart;
                newStyle.length = subEnd - subStart;
                iter.add(newStyle);

                if (style.start + style.length < mergingStyle.start + mergingStyle.length) {
                    int length = mergingStyle.start + mergingStyle.length - subEnd;
                    mergingStyle.start = subEnd;
                    mergingStyle.length = length;
                    continue;
                } else if (mergingStyle.start + mergingStyle.length < style.start + style.length) {
                    StyleRange endStyle = (StyleRange) style.clone();
                    endStyle.start = subEnd;
                    endStyle.length = style.start + style.length - subEnd;
                    iter.add(endStyle);
                }
                // else both styles end at the same time

                // We matched a style and inserted the new style, so we're done
                continue mergeLoop;
            }

            resultList.add(mergingStyle);
        }

        return resultList;
    }

    private void showStyles(Collection<StyleRange> styles, int start, int end) {

        try {
            Collection<StyleRange> finalList = mergeStyleRangeLists(styles, getHighlights(start, end));

            for (StyleRange style : finalList) {
                textWidget.setStyleRange(style);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Collection<StyleRange> getHighlights(int start, int end) {
        ArrayList<StyleRange> highlightList = new ArrayList<StyleRange>();
        if (client == null)
            return highlightList;

        String text = textWidget.getTextRange(start, end - start);

        for (Iterator<? extends IWarlockPattern> iter = client.getHighlightsIterator(); iter.hasNext();) {
            IWarlockPattern highlight = iter.next();
            if (highlight == null)
                continue;
            Pattern p;
            try {
                p = highlight.getPattern();
            } catch (PatternSyntaxException e) {
                continue;
            }
            if (p == null)
                continue;
            Matcher matcher = p.matcher(text);

            while (matcher.find()) {
                MatchResult result = matcher.toMatchResult();

                IWarlockStyle style = highlight.getStyle();
                int highlightStart = result.start() + start;
                int highlightLength = result.end() - result.start();
                if (style.isFullLine()) {
                    int lineNum = textWidget.getLineAtOffset(highlightStart);
                    highlightStart = textWidget.getOffsetAtLine(lineNum);
                    if (lineNum + 1 >= textWidget.getLineCount())
                        highlightLength = end - highlightStart;
                    else
                        highlightLength = textWidget.getOffsetAtLine(lineNum + 1) - highlightStart;
                }

                StyleRange styleRange = warlockStyleToStyleRange(style, highlightStart, highlightLength);
                if (styleRange == null)
                    continue;
                highlightList.add(styleRange);

                try {
                    if (style.getSound() != null && !style.getSound().equals("")) {
                        SoundPlayer.play(style.getSound());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        return highlightList;
    }

    private void getMarkerStyles(WarlockStringMarker marker, StyleRange baseStyle,
            Collection<StyleRange> resultStyles) {
        int pos = marker.getStart();
        for (WarlockStringMarker subMarker : marker.getSubMarkers()) {

            int nextPos = subMarker.getStart();

            StyleRange styleRange = mergeStyleRanges(baseStyle,
                    warlockStyleToStyleRange(marker.getStyle(), pos, nextPos - pos));

            if (nextPos > pos)
                resultStyles.add(styleRange);

            getMarkerStyles(subMarker, styleRange, resultStyles);

            pos = subMarker.getEnd();
        }

        if (marker.getEnd() > pos) {
            StyleRange styleRange = mergeStyleRanges(baseStyle,
                    warlockStyleToStyleRange(marker.getStyle(), pos, marker.getEnd() - pos));
            resultStyles.add(styleRange);
        }
    }

    public void append(WarlockString wstring) {
        boolean atBottom = isAtBottom();

        int offset = textWidget.getCharCount();
        textWidget.append(wstring.toString());
        /* Break up the ranges and merge overlapping styles because SWT only
         * allows 1 style per section
         */
        LinkedList<StyleRange> finishedStyles = new LinkedList<StyleRange>();
        for (WarlockStringMarker strMarker : wstring.getStyles()) {
            WarlockStringMarker marker = strMarker.copy(offset);
            addComponentMarker(marker, marker);
            getMarkerStyles(marker, new StyleRange(), finishedStyles);
        }
        showStyles(finishedStyles, offset, textWidget.getCharCount());

        postTextChange(atBottom, offset);
    }

    private void addComponentMarker(WarlockStringMarker marker, WarlockStringMarker topLevel) {
        if (marker.getComponentName() != null) {
            this.addMarker(marker);
            IWarlockStyle baseStyle = topLevel.getBaseStyle(marker);
            if (baseStyle != null)
                marker.setStyle(baseStyle);
        } else {
            for (WarlockStringMarker subMarker : marker.getSubMarkers()) {
                addComponentMarker(subMarker, topLevel);
            }
        }
    }

    private StyleRange warlockStyleToStyleRange(IWarlockStyle style, int start, int length) {
        StyleRange styleRange = new StyleRange();

        WarlockColor foreground = style.getForegroundColor();
        WarlockColor background = style.getBackgroundColor();
        boolean underline = style.isUnderline();
        boolean fullLine = style.isFullLine();

        IClientSettings settings = null;
        if (client != null)
            settings = client.getClientSettings();
        if (settings == null)
            settings = ClientSettings.getGlobalClientSettings();
        IWarlockStyle presetStyle = PresetStyleConfigurationProvider.getProvider(settings)
                .getStyle(style.getName());
        if (presetStyle != null) {
            if (background == null || background.isDefault())
                background = presetStyle.getBackgroundColor();
            if (foreground == null || foreground.isDefault())
                foreground = presetStyle.getForegroundColor();
            underline |= presetStyle.isUnderline();
            fullLine |= presetStyle.isFullLine();
        }

        if (style.isMonospace()) {
            styleRange.font = monoFont;
        }

        styleRange.fontStyle = SWT.NORMAL;
        if (style.isBold())
            styleRange.fontStyle |= SWT.BOLD;
        if (style.isItalic())
            styleRange.fontStyle |= SWT.ITALIC;
        styleRange.underline = underline;

        if (foreground != null && !foreground.isDefault())
            styleRange.foreground = ColorUtil.warlockColorToColor(foreground);
        if (background != null && !background.isDefault())
            styleRange.background = ColorUtil.warlockColorToColor(background);

        styleRange.start = start;
        styleRange.length = length;

        if (fullLine)
            textWidget.setLineBackground(textWidget.getLineAtOffset(styleRange.start), 1, styleRange.background);
        if (style.getAction() != null)
            styleRange.data = style.getAction();

        return styleRange;
    }

    private StyleRange mergeStyleRanges(StyleRange style1, StyleRange style2) {
        if (style1 == null)
            return style2;
        if (style2 == null)
            return style1;

        StyleRange newStyle = new StyleRange(style1);

        newStyle.start = style2.start;
        newStyle.length = style2.length;
        if (style2.font != null)
            newStyle.font = style2.font;
        if (style2.background != null)
            newStyle.background = style2.background;
        if (style2.foreground != null)
            newStyle.foreground = style2.foreground;
        if (style2.fontStyle != SWT.NORMAL)
            newStyle.fontStyle = style2.fontStyle;
        if (style2.strikeout)
            newStyle.strikeout = true;
        if (style2.underline)
            newStyle.underline = true;

        if (style2.data != null)
            newStyle.data = style2.data;

        return newStyle;
    }

    public boolean isAtBottom() {
        return textWidget.getLinePixel(textWidget.getLineCount()) <= textWidget.getClientArea().height;
    }

    private void postTextChange(boolean atBottom, int offset) {

        if (ignoreEmptyLines) {
            removeEmptyLines(offset);
            restoreNewlines(offset, markers);
        }

        constrainLineLimit(atBottom);

        if (atBottom)
            scrollToEnd();
    }

    public void scrollToEnd() {
        if (doScrollDirection == SWT.DOWN)
            textWidget.setTopPixel(textWidget.getTopPixel() + textWidget.getLinePixel(textWidget.getLineCount()));
    }

    // this function removes the first "delta" amount of characters
    private void updateMarkers(int delta) {
        for (Iterator<WarlockStringMarker> iter = markers.iterator(); iter.hasNext();) {
            WarlockStringMarker marker = iter.next();

            // If the marker is moved off the beginning, remove it
            if (marker.getEnd() + delta < 0) {
                iter.remove();
                continue;
            }
            // move us accordingly
            marker.move(delta);
        }
    }

    public void addInternalMarker(WarlockStringMarker marker, LinkedList<WarlockStringMarker> markerList) {
        ListIterator<WarlockStringMarker> iter = markerList.listIterator();
        try {
            while (true) {
                if (!iter.hasNext()) {
                    iter.add(marker);
                    break;
                }
                WarlockStringMarker cur = iter.next();
                if (cur.getEnd() > marker.getStart()) {
                    if (marker.getEnd() > cur.getStart()) {
                        addInternalMarker(marker, cur.getSubMarkers());
                        return;
                    }

                    iter.previous();
                    iter.add(marker);
                    break;
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    public void addMarker(WarlockStringMarker marker) {
        ListIterator<WarlockStringMarker> iter = markers.listIterator();
        try {
            while (true) {
                if (!iter.hasNext()) {
                    iter.add(marker);
                    break;
                }
                WarlockStringMarker cur = iter.next();
                if (cur.getEnd() > marker.getStart()) {
                    if (marker.getEnd() > cur.getStart()) {
                        throw new Exception("Bad marker!");
                    }

                    iter.previous();
                    iter.add(marker);
                    break;
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    public void replaceMarker(String name, WarlockString text) {
        WarlockStringMarker marker = null;
        IWarlockStyle baseStyle = null;
        for (WarlockStringMarker subMarker : markers) {
            marker = getMarkerByComponent(name, subMarker);
            if (marker != null) {
                baseStyle = subMarker.getBaseStyle(marker);
                break;
            }
        }
        if (marker == null)
            return;

        int start = marker.getStart();
        int length = marker.getEnd() - start;
        boolean atBottom = isAtBottom();
        textWidget.replaceTextRange(start, length, text.toString());
        marker.clear();
        int newLength = text.length();
        marker.setEnd(start + newLength);
        WarlockStringMarker.updateMarkers(newLength - length, marker, markers);

        // Add the new styles to the existing marker
        for (WarlockStringMarker newMarker : text.getStyles()) {
            marker.addMarker(newMarker.copy(start));
        }

        /* Break up the ranges and merge overlapping styles because SWT only
         * allows 1 style per section
         */
        WarlockStringMarker markerWithStyle = marker.clone();
        markerWithStyle.setStyle(baseStyle);
        LinkedList<StyleRange> newStyles = new LinkedList<StyleRange>();
        getMarkerStyles(markerWithStyle, new StyleRange(), newStyles);
        showStyles(newStyles, marker.getStart(), marker.getEnd());

        postTextChange(atBottom, start);
    }

    private WarlockStringMarker getMarkerByComponent(String componentName, WarlockStringMarker marker) {
        String myName = marker.getComponentName();
        if (myName != null && myName.equals(componentName))
            return marker;

        for (WarlockStringMarker subMarker : marker.getSubMarkers()) {
            WarlockStringMarker result = getMarkerByComponent(componentName, subMarker);
            if (result != null)
                return result;
        }
        return null;
    }

    private void constrainLineLimit(boolean atBottom) {
        // 'status' is a pointer that allows us to change the object in our parent..
        // in this method... it is intentional.
        if (lineLimit > 0) {
            int lines = textWidget.getLineCount();
            if (lines > lineLimit) {
                int linesToRemove = lines - lineLimit;
                int charsToRemove = textWidget.getOffsetAtLine(linesToRemove);
                if (atBottom) {
                    textWidget.replaceTextRange(0, charsToRemove, "");
                    updateMarkers(-charsToRemove);
                } else {
                    int pixelsToRemove = textWidget.getLinePixel(linesToRemove);
                    textWidget.replaceTextRange(0, charsToRemove, "");
                    updateMarkers(-charsToRemove);
                    if (pixelsToRemove < 0)
                        textWidget.setTopPixel(-pixelsToRemove);
                }
            }
        }
    }

    public void setScrollDirection(int dir) {
        if (dir == SWT.DOWN || dir == SWT.UP)
            doScrollDirection = dir;
        // TODO: Else throw an error
    }

    public void setIgnoreEmptyLines(boolean ignoreLines) {
        this.ignoreEmptyLines = ignoreLines;
    }

    public StyledText getTextWidget() {
        return textWidget;
    }

    public void setClient(IWarlockClient client) {
        if (this.client != null)
            WarlockClientRegistry.removeWarlockClientListener(clientListener);
        if (settingListener != null)
            settingListener.remove();

        this.client = client;

        // if client already has settings, we'll unintentially load them twice
        loadSettings();

        if (client != null)
            WarlockClientRegistry.addWarlockClientListener(clientListener);
    }

    protected IWarlockClient getClient() {
        return client;
    }

    private void loadSettings() {
        if (settingListener != null)
            settingListener.remove();

        if (textWidget.isDisposed())
            return;

        IClientSettings settings = null;
        if (client != null)
            settings = client.getClientSettings();

        if (settings == null)
            settings = ClientSettings.getGlobalClientSettings();

        WindowConfigurationProvider provider = WindowConfigurationProvider.getProvider(settings);
        settingListener = new WindowSettingsListener(provider);

        // Set to defaults first, then try window settings later
        Color background = ColorUtil.warlockColorToColor(provider.getWindowBackground(streamName));
        Color foreground = ColorUtil.warlockColorToColor(provider.getWindowForeground(streamName));
        IWarlockFont font = provider.getWindowFont(streamName);

        textWidget.setBackground(background);
        textWidget.setForeground(foreground);

        if (font.isDefaultFont()) {
            String defaultFontFace = GameViewConfiguration.getProvider(settings).getDefaultFontFace();
            int defaultFontSize = GameViewConfiguration.getProvider(settings).getDefaultFontSize();
            textWidget.setFont(new Font(Display.getDefault(), defaultFontFace, defaultFontSize, SWT.NORMAL));
        } else {
            textWidget.setFont(FontUtil.warlockFontToFont(font));
        }

        IWarlockFont columnFont = provider.getWindowMonoFont(streamName);
        if (columnFont == null || columnFont.isDefaultFont()) {
            this.monoFont = JFaceResources.getFont(JFaceResources.TEXT_FONT);
        } else {
            String fontFace = columnFont.getFamilyName();
            int fontSize = columnFont.getSize();
            this.monoFont = new Font(getTextWidget().getDisplay(), fontFace, fontSize, SWT.NORMAL);
        }
    }

    public String getName() {
        return streamName;
    }
}