net.officefloor.demo.chat.client.ChatWidget.java Source code

Java tutorial

Introduction

Here is the source code for net.officefloor.demo.chat.client.ChatWidget.java

Source

/*
 * OfficeFloor - http://www.officefloor.net
 * Copyright (C) 2005-2013 Daniel Sagenschneider
 *
 * This program 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 net.officefloor.demo.chat.client;

import java.util.LinkedList;
import java.util.List;

import net.officefloor.plugin.gwt.comet.api.OfficeFloorComet;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;

/**
 * {@link Widget} providing Chat.
 * 
 * @author Daniel Sagenschneider
 */
public class ChatWidget extends VerticalPanel {

    /**
     * {@link ConversationSubscription} publisher.
     */
    private final ConversationSubscription publisher = OfficeFloorComet
            .createPublisher(ConversationSubscription.class);

    /**
     * Message {@link TextBox}.
     */
    private final TextBox messageText = new TextBox();

    /**
     * Listing of users that are currently typing.
     */
    private final List<String> typingUserNames = new LinkedList<String>();

    /**
     * Flag indicating if the user is typing.
     */
    private boolean isTyping = false;

    /**
     * User name of the user.
     */
    private String userName = null;

    /**
     * Allows specifying the user name.
     * 
     * @param userName
     *            User name.
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * Initiate.
     */
    public ChatWidget() {

        // Provide means to send a message
        HorizontalPanel messagePanel = new HorizontalPanel();
        // messagePanel.setSpacing(10);
        messagePanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
        this.add(messagePanel);

        // Provide message text box
        this.messageText.setStylePrimaryName("message");
        messagePanel.add(this.messageText);
        this.messageText.addFocusHandler(new FocusHandler() {
            @Override
            public void onFocus(FocusEvent event) {
                ChatWidget.this.ensureHaveUserName();
            }
        });

        // Provide send button for message
        Button sendButton = new Button("send");
        sendButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                ChatWidget.this.sendChatMessage();
            }
        });
        messagePanel.add(sendButton);

        // Provide messages in scroll area
        VerticalPanel messagesPanel = new VerticalPanel();
        messagesPanel.setSize("100%", "100%");
        ScrollPanel scrollMessagesPanel = new ScrollPanel(messagesPanel);
        scrollMessagesPanel.setStylePrimaryName("messages");
        this.add(scrollMessagesPanel);

        // Provide label to display typing users
        final Label typingLabel = new Label();
        typingLabel.setStylePrimaryName("typing");
        messagesPanel.add(typingLabel);
        typingLabel.setVisible(false); // initially hidden as no typing users

        // List to contain chat messages
        Cell<ChatMessage> chatCell = new AbstractCell<ChatMessage>() {
            @Override
            public void render(Context context, ChatMessage value, SafeHtmlBuilder sb) {
                sb.appendHtmlConstant("<span><b>");
                sb.appendEscaped(value.getUserName());
                sb.appendHtmlConstant("</b>");
                sb.appendEscaped(" > ");
                sb.appendEscaped(value.getMessage());
                sb.appendHtmlConstant("</span>");
            }
        };
        CellList<ChatMessage> chatList = new CellList<ChatMessage>(chatCell);
        messagesPanel.add(chatList);

        // Add the data
        final ListDataProvider<ChatMessage> chatEntries = new ListDataProvider<ChatMessage>();
        chatEntries.addDataDisplay(chatList);

        // Handle listening for messages
        OfficeFloorComet.subscribe(ConversationSubscription.class, new ConversationSubscription() {
            @Override
            public void message(ChatMessage message) {

                // Determine if typing notification
                String userName = message.getUserName();
                if ((ChatWidget.this.userName == null) || (!(ChatWidget.this.userName.equals(userName)))) {
                    // Not user so provide notification of typing
                    if (message.isTyping()) {
                        // Include user as typing
                        if (!(ChatWidget.this.typingUserNames.contains(userName))) {
                            ChatWidget.this.typingUserNames.add(userName);
                        }
                    } else {
                        // User has stopped typing
                        ChatWidget.this.typingUserNames.remove(userName);
                    }
                }

                // Display typing notification
                if (ChatWidget.this.typingUserNames.size() == 0) {
                    // No typing users
                    typingLabel.setVisible(false);

                } else {
                    // Display the typing users
                    StringBuilder typingUsers = new StringBuilder();
                    boolean isFirst = true;
                    for (String typingUserName : ChatWidget.this.typingUserNames) {
                        if (!isFirst) {
                            typingUsers.append(", ");
                        }
                        isFirst = false;
                        typingUsers.append(typingUserName);
                    }
                    typingUsers.append(" ... (typing)");
                    typingLabel.setText(typingUsers.toString());
                    typingLabel.setVisible(true);
                }

                // Add the message (if one provided)
                String text = message.getMessage();
                if (text != null) {
                    List<ChatMessage> list = chatEntries.getList();
                    if (list.size() == 0) {
                        list.add(message);
                    } else {
                        list.add(0, message);
                    }
                }
            }
        }, null);

        // Handle submitting a message
        this.messageText.addKeyDownHandler(new KeyDownHandler() {
            @Override
            public void onKeyDown(KeyDownEvent event) {

                // Obtain the message text and cursor position
                String message = ChatWidget.this.messageText.getText();
                int cursorPosition = ChatWidget.this.messageText.getCursorPos();

                // Handle various scenarios of keys for sending/notification
                switch (event.getNativeKeyCode()) {
                case KeyCodes.KEY_ENTER:
                    ChatWidget.this.sendChatMessage();
                    break;

                case KeyCodes.KEY_BACKSPACE:
                    if (ChatWidget.this.isTyping && (message.length() == 1) && (cursorPosition == 1)) {
                        // No longer typing as deleting last character
                        ChatWidget.this.sendChatMessage(false, null);
                    }
                    break;

                case KeyCodes.KEY_DELETE:
                    if (ChatWidget.this.isTyping && (message.length() == 1) && (cursorPosition == 0)) {
                        // No longer typing as deleting last character
                        ChatWidget.this.sendChatMessage(false, null);
                    }
                    break;

                case KeyCodes.KEY_ALT:
                case KeyCodes.KEY_CTRL:
                case KeyCodes.KEY_DOWN:
                case KeyCodes.KEY_END:
                case KeyCodes.KEY_ESCAPE:
                case KeyCodes.KEY_HOME:
                case KeyCodes.KEY_LEFT:
                case KeyCodes.KEY_PAGEDOWN:
                case KeyCodes.KEY_PAGEUP:
                case KeyCodes.KEY_RIGHT:
                case KeyCodes.KEY_SHIFT:
                case KeyCodes.KEY_UP:
                    // Do nothing
                    break;

                default:
                    if ((!ChatWidget.this.isTyping) && (message.length() == 0)) {
                        // Started typing a message
                        ChatWidget.this.sendChatMessage(true, null);
                    }
                    break;
                }
            }
        });
    }

    /**
     * Sends the {@link ChatMessage}.
     * 
     * @param isTyping
     *            Indicates if user is typing.
     * @param message
     *            Message text.
     */
    private void sendChatMessage(boolean isTyping, String message) {

        // Ensure have the user name
        this.ensureHaveUserName();

        // Sends the chat message
        this.publisher.message(new ChatMessage(this.userName, isTyping, message));

        // Flag whether typing
        this.isTyping = isTyping;
    }

    /**
     * Sends the {@link ChatMessage} with message text.
     */
    private void sendChatMessage() {

        // Ensure have message to send
        String message = this.messageText.getText();
        if ((message == null) || (message.trim().length() == 0)) {
            return;
        }

        // Send the chat message (user has stopped typing)
        this.sendChatMessage(false, message);
        this.messageText.setText(""); // clear as sent
    }

    /**
     * Ensures that have user name.
     */
    private void ensureHaveUserName() {

        // Determine if user name already specified
        if ((this.userName != null) && (this.userName.trim().length() > 0)) {
            return; // user name specified
        }

        // No user name so obtain the user name
        final DialogBox userDialog = new DialogBox(false, true);
        HorizontalPanel userPanel = new HorizontalPanel();
        userPanel.setSpacing(10);
        userPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
        userDialog.add(userPanel);
        userPanel.add(new Label("Enter name for chat"));
        final TextBox nameText = new TextBox();
        nameText.addKeyDownHandler(new KeyDownHandler() {
            @Override
            public void onKeyDown(KeyDownEvent event) {
                if (KeyCodes.KEY_ENTER == event.getNativeKeyCode()) {
                    ChatWidget.this.enterChatName(nameText, userDialog);
                }
            }
        });
        userPanel.add(nameText);
        Button okButton = new Button("ok");
        userPanel.add(okButton);
        okButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                ChatWidget.this.enterChatName(nameText, userDialog);
            }
        });
        userDialog.center();
        nameText.setFocus(true);
    }

    /**
     * Enters the chat name.
     * 
     * @param nameText
     *            {@link TextBox} containing the name.
     * @param userDialog
     *            {@link DialogBox} to obtain the name.
     */
    private void enterChatName(TextBox nameText, DialogBox userDialog) {

        // Ensure user name provided
        String name = nameText.getText();
        if ((name == null) || (name.trim().length() == 0)) {
            Window.alert("Must provide name!");
            return;
        }

        // Name provided
        this.userName = name;
        userDialog.hide();

        // Provide focus for writing message
        this.messageText.setFocus(true);
    }

}