package org.limewire.ui.swing.friends.chat; import java.awt.BorderLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.util.concurrent.ScheduledExecutorService; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.JTextComponent; import org.jdesktop.application.Resource; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import org.limewire.ui.swing.action.CopyAction; import org.limewire.ui.swing.action.CutAction; import org.limewire.ui.swing.action.DeleteAction; import org.limewire.ui.swing.action.PasteAction; import org.limewire.ui.swing.action.PopupUtil; import org.limewire.ui.swing.action.SelectAllAction; import org.limewire.ui.swing.util.GuiUtils; import org.limewire.collection.Periodic; import org.limewire.friend.api.ChatState; import org.limewire.friend.api.FriendException; import org.limewire.friend.api.MessageWriter; class ResizingInputPanel extends JPanel implements Displayable { private static final Log LOG = LogFactory.getLog(ResizingInputPanel.class); private static final int DELAY_IN_MILLIS_FOR_CHAT_PAUSED = 3000; private final JTextArea text; private final MessageWriter writer; private final ChatStateManager chatStateManager; @Resource(key="ChatInputPanel.textFont") private Font inputTextFont; public ResizingInputPanel(MessageWriter writer, ScheduledExecutorService schedExecService) { super(new BorderLayout()); this.writer = writer; GuiUtils.assignResources(this); chatStateManager = new ChatStateManager(schedExecService); text = new JTextArea(); text.setFont(inputTextFont); text.setRows(2); text.setWrapStyleWord(true); text.setLineWrap(true); text.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "sendMessage"); text.getActionMap().put("sendMessage", new SendMessage()); text.getDocument().addDocumentListener(chatStateManager); text.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); JPopupMenu popup = PopupUtil.addPopupMenus(text, new CutAction(text), new CopyAction(text), new PasteAction(), new DeleteAction(text)); popup.addSeparator(); popup.add(new SelectAllAction()); final JScrollPane scrollPane = new JScrollPane(text, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.getViewport().addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (text.getText().length() > 0 && scrollPane.getVerticalScrollBar().isVisible()) { resizeTextBox(3); } } }); scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0)); add(scrollPane); } private void resizeTextBox(int rowCount) { if (text.getRows() != rowCount) { text.setRows(rowCount); revalidate(); } } public void dispose() { chatStateManager.cancelPendingChatStateUpdates(); } @Override public void handleDisplay() { text.requestFocusInWindow(); } public JTextComponent getInputComponent() { return text; } private class SendMessage extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { try { String message = text.getText(); if (message.trim().length() > 0) { writer.writeMessage(message); text.setText(""); resizeTextBox(2); chatStateManager.updateChatState(ChatState.active); } } catch (FriendException e1) { LOG.error("Unable to write message", e1); } } } private class ChatStateManager implements DocumentListener { private final Periodic chatPausedTimer; private volatile ChatState currentInputChatState = ChatState.active; ChatStateManager(ScheduledExecutorService schedExecService) { chatPausedTimer = new Periodic(new Runnable() { public void run() { if (currentInputChatState == ChatState.composing) { updateChatState(ChatState.paused); } } }, schedExecService); } void updateChatState(ChatState state) { if (currentInputChatState != state) { currentInputChatState = state; try { writer.setChatState(currentInputChatState); } catch (FriendException e) { LOG.error("Unable to set chat state", e); } } } void cancelPendingChatStateUpdates() { chatPausedTimer.unschedule(); } @Override public void changedUpdate(DocumentEvent e) { updateChatStateComposing(); } @Override public void insertUpdate(DocumentEvent e) { updateChatStateComposing(); } @Override public void removeUpdate(DocumentEvent e) { if (e.getDocument().getLength() == 0) { updateChatState(ChatState.active); } else { updateChatStateComposing(); } } private void updateChatStateComposing() { // update chat state to "composing", if necessary updateChatState(ChatState.composing); chatPausedTimer.rescheduleIfLater(DELAY_IN_MILLIS_FOR_CHAT_PAUSED); } } }