Package com.jcloisterzone.ui.controls

Source Code of com.jcloisterzone.ui.controls.ChatPanel

package com.jcloisterzone.ui.controls;

import static com.jcloisterzone.ui.I18nUtils._;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.util.ArrayDeque;
import java.util.Deque;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.BoxView;
import javax.swing.text.ComponentView;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.IconView;
import javax.swing.text.LabelView;
import javax.swing.text.ParagraphView;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleConstants.ColorConstants;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;

import com.jcloisterzone.Player;
import com.jcloisterzone.event.ChatEvent;
import com.jcloisterzone.game.Game;
import com.jcloisterzone.game.PlayerSlot;
import com.jcloisterzone.game.phase.CreateGamePhase;
import com.jcloisterzone.ui.Client;
import com.jcloisterzone.ui.component.TextPrompt;
import com.jcloisterzone.ui.component.TextPrompt.Show;
import com.jcloisterzone.wsio.message.PostChatMessage;

public class ChatPanel extends FakeComponent implements WindowStateListener {

    public static final int CHAT_WIDTH = 250;
    public static final int DISPLAY_MESSAGES_INTERVAL = 9000;

    private JComponent parent; //TODO move to FakeComponent? hack to re-layout from inside class
    private final Game game;

    private boolean forceFocus;
    private boolean messageReceivedWhileIconified;
    private JTextField input;
    private JTextPane messagesPane;
    private final Deque<ReceivedChatMessage> formattedMessages = new ArrayDeque<>();
    private final Timer repaintTimer;


    public ChatPanel(Client client, Game game) {
        super(client);
        this.game = game;
        repaintTimer = new Timer(DISPLAY_MESSAGES_INTERVAL, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                forceFocus = false;
                parent.repaint();
                repaintTimer.stop();
            }
        });
    }


    public void setParent(JComponent parent) {
        this.parent = parent;
    }

    public void activateChat() {
        input.setFocusable(true);
        input.requestFocusInWindow();
        //prevent key event propagate to input - but still not 100%
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                input.requestFocusInWindow();
            }
        });
    }

    private void clean() {
        input.setText("");
        input.setFocusable(false);
        client.requestFocusInWindow();
    }



    @Override
    public void registerSwingComponents(JComponent parent) {
        this.parent = parent;
        input = new JTextField();
        //prevent unintended focus (by window activate etc. - allow focus just on direct click)
        input.setFocusable(false);
        input.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                activateChat();
            }
        });
        input.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    clean();
                }
            }
        });
        input.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String msg = input.getText();
                if (!"".equals(msg)) {
                    forceFocus = true; //prevent panel flashing
                    client.getConnection().send(new PostChatMessage(game.getGameId(), msg));
                    //client.getServer().chatMessage(getSendingPlayer().getIndex(), msg);
                }
                clean();
            }
        });
        input.addFocusListener(new FocusListener() {
            @Override
            public void focusLost(FocusEvent e) {
                if (ChatPanel.this.parent != null) {
                    ChatPanel.this.parent.repaint();
                }
            }

            @Override
            public void focusGained(FocusEvent e) {
                messagesPane.setVisible(true);
                ChatPanel.this.parent.repaint();
            }
        });

        input.setOpaque(false);
        input.setBackground(new Color(255, 255, 255, 8));
        TextPrompt tp = new TextPrompt(_("Type to chat"), input);
        tp.setShow(Show.FOCUS_LOST);
        tp.changeStyle(Font.ITALIC);
        tp.changeAlpha(0.4f);

        messagesPane = new JTextPane();
        messagesPane.setEditorKit(new WrapEditorKit());
        messagesPane.setFocusable(false);
        messagesPane.setOpaque(false);

        parent.add(input);
        parent.add(messagesPane);

        client.addWindowStateListener(this);
    }

    @Override
    public void destroySwingComponents(JComponent parent) {
        parent.remove(input);
        client.removeWindowStateListener(this);
    }

    @Override
    public void windowStateChanged(WindowEvent e) {
        if (e.getOldState() == JFrame.ICONIFIED && messageReceivedWhileIconified) {
            setForceFocus();
            messageReceivedWhileIconified = false;
        }
    }

    @Override
    public void layoutSwingComponents(JComponent parent) {
        input.setBounds(10, parent.getHeight() - 35, CHAT_WIDTH-20, 25);

        messagesPane.setSize(CHAT_WIDTH-20, Short.MAX_VALUE);
        int height = messagesPane.getPreferredSize().height;
        messagesPane.setBounds(10, parent.getHeight() - 30 - height, CHAT_WIDTH-20, height);
    }

    private boolean isFolded() {
        return !forceFocus && !input.hasFocus();
    }

    @Override
    public void paintComponent(Graphics2D g2) {
        int h = parent.getHeight();

        g2.setColor(ControlPanel.PANEL_BG_COLOR);
        if (isFolded()) {
            if (messagesPane.isVisible()) messagesPane.setVisible(false);
            g2.fillRect(0, parent.getHeight() - 45, CHAT_WIDTH, 45);
        } else {
            if (!messagesPane.isVisible()) messagesPane.setVisible(true);
            g2.fillRect(0, 0, CHAT_WIDTH, h);
        }
    }

    public JTextField getInput() {
        return input;
    }

    public JTextPane getMessagesPane() {
        return messagesPane;
    }

    private void setForceFocus() {
        if (repaintTimer.isRunning()) {
            repaintTimer.restart();
        } else {
            forceFocus = true;
            parent.repaint();
            repaintTimer.start();
        }
    }

    /**
     * More then one client can play from one seat. Find local player, prefer
     * active, than human player, latest is AI.
     *
     * @return
     */
    private ReceivedChatMessage createReceivedMessage(ChatEvent ev) {
        String nick = ev.getRemoteClient().getName();
        Color color = Color.DARK_GRAY;

        if (game.isStarted()) {
            Player selected = null, active = game.getActivePlayer();
            for (Player player : game.getAllPlayers()) {
                if (player.getSlot().getClientId().equals(ev.getRemoteClient().getClientId())) {
                    if (selected == null) {
                        selected = player;
                    } else {
                        if (selected.getSlot().getAiClassName() != null
                                && player.getSlot().getAiClassName() == null) {
                            // prefer real user for remote inactive client with
                            // more players
                            selected = player;
                        }
                    }
                    if (player.equals(active)) {
                        selected = player;
                        break;
                    }
                }
            }
            if (selected != null) {
                nick = selected.getNick();
                color = selected.getColors().getFontColor();
            }
        } else {
             PlayerSlot[] slots = ((CreateGamePhase) game.getPhase()).getPlayerSlots();
             for (PlayerSlot slot: slots) {
                 if (ev.getRemoteClient().getClientId().equals(slot.getClientId()) && !slot.getNickname().equals("")) {
                     nick = slot.getNickname();
                     color = slot.getColors().getFontColor();
                     break;
                 }
             }
        }
        return new ReceivedChatMessage(ev, nick, color);
    }

    public void displayChatMessage(ChatEvent ev) {
        ReceivedChatMessage fm = createReceivedMessage(ev);
        formattedMessages.addLast(fm);

        if (client.getState() == JFrame.ICONIFIED) {
            messageReceivedWhileIconified = true;
        } else {
            setForceFocus();
        }

        DefaultStyledDocument doc = new DefaultStyledDocument();
        int offset = 0;
        try {
            for (ReceivedChatMessage msg : formattedMessages) {
                SimpleAttributeSet attrs = new SimpleAttributeSet();
                ColorConstants.setForeground(attrs, msg.color);

                String nick = msg.nickname;
                String text = msg.ev.getText();
                doc.insertString(offset, nick + ": ", attrs);
                offset += nick.length() + 2;
                doc.insertString(offset, text + "\n", null);
                offset += text.length() + 1;
            }
        } catch (BadLocationException e) {
            e.printStackTrace(); //should never happen
        }
        messagesPane.setDocument(doc);
        layoutSwingComponents(parent);
    }


    static class ReceivedChatMessage {
        final ChatEvent ev;
        final String nickname;
        final Color color;
        final long time;

        public ReceivedChatMessage(ChatEvent ev, String nickname, Color color) {
            this.ev = ev;
            this.color = color;
            this.nickname = nickname;
            this.time = System.currentTimeMillis();
        }
    }

    static class WrapEditorKit extends StyledEditorKit {
        ViewFactory defaultFactory=new WrapColumnFactory();
        public ViewFactory getViewFactory() {
            return defaultFactory;
        }

    }

    static class WrapColumnFactory implements ViewFactory {
        public View create(Element elem) {
            String kind = elem.getName();
            if (kind != null) {
                if (kind.equals(AbstractDocument.ContentElementName)) {
                    return new WrapLabelView(elem);
                } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                    return new ParagraphView(elem);
                } else if (kind.equals(AbstractDocument.SectionElementName)) {
                    return new BoxView(elem, View.Y_AXIS);
                } else if (kind.equals(StyleConstants.ComponentElementName)) {
                    return new ComponentView(elem);
                } else if (kind.equals(StyleConstants.IconElementName)) {
                    return new IconView(elem);
                }
            }

            // default to text display
            return new LabelView(elem);
        }
    }

    static class WrapLabelView extends LabelView {
        public WrapLabelView(Element elem) {
            super(elem);
        }

        public float getMinimumSpan(int axis) {
            switch (axis) {
                case View.X_AXIS:
                    return 0;
                case View.Y_AXIS:
                    return super.getMinimumSpan(axis);
                default:
                    throw new IllegalArgumentException("Invalid axis: " + axis);
            }
        }

    }
}
TOP

Related Classes of com.jcloisterzone.ui.controls.ChatPanel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.