package cn.ieclipse.smartim.console; import cn.ieclipse.smartim.IMHistoryManager; import cn.ieclipse.smartim.IMWindowFactory; import cn.ieclipse.smartim.SmartClient; import cn.ieclipse.smartim.actions.*; import cn.ieclipse.smartim.common.*; import cn.ieclipse.smartim.idea.EditorUtils; import cn.ieclipse.smartim.model.IContact; import cn.ieclipse.smartim.model.impl.AbstractContact; import cn.ieclipse.smartim.settings.SmartIMSettings; import cn.ieclipse.smartim.settings.StyleConfPanel; import cn.ieclipse.smartim.views.IMPanel; import cn.ieclipse.util.BareBonesBrowserLaunch; import cn.ieclipse.util.EncodeUtils; import cn.ieclipse.util.StringUtils; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.ui.JBSplitter; import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.JTextComponent; import javax.swing.text.ViewFactory; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.net.URL; import java.util.List; /** * Created by Jamling on 2017/7/1. */ public abstract class IMChatConsole extends SimpleToolWindowPanel { public static final String ENTER_KEY = "\n"; protected IContact contact; protected String uin; protected IMPanel imPanel; public IMChatConsole(IContact target, IMPanel imPanel) { super(false, false); this.contact = target; this.uin = target.getUin(); this.imPanel = imPanel; initUI(); new Thread() { public void run() { loadHistories(); } }.start(); } public SmartClient getClient() { return imPanel.getClient(); } public abstract void loadHistory(String raw); public abstract void post(final String msg); public File getHistoryDir() { if (getClient() != null) { return getClient().getWorkDir(IMHistoryManager.HISTORY_NAME); } File dir = IMWindowFactory.getDefault().getWorkDir().getAbsoluteFile(); return new File(dir, IMHistoryManager.HISTORY_NAME); } public String getHistoryFile() { return EncodeUtils.getMd5(contact.getName()); } public String getUin() { return uin; } public String trimMsg(String msg) { if (msg.endsWith(ENTER_KEY)) { return msg; } return msg + ENTER_KEY; } public void loadHistories() { SmartClient client = getClient(); if (client != null) { List<String> ms = IMHistoryManager.getInstance().load(getHistoryDir(), getHistoryFile()); int size = ms.size(); for (int i = 0; i < size; i++) { String raw = ms.get(i); if (!IMUtils.isEmpty(raw)) { try { loadHistory(raw); } catch (Exception e) { error("历史消息记录:" + raw); } } } } } public void clearHistories() { IMHistoryManager.getInstance().clear(getHistoryDir(), getHistoryFile()); historyWidget.setText(""); } public void clearUnread() { if (contact != null && contact instanceof AbstractContact) { ((AbstractContact)contact).clearUnRead(); imPanel.notifyUpdateContacts(0, true); } } public boolean hideMyInput() { return false; } public boolean checkClient(SmartClient client) { if (client == null || client.isClose()) { error("连接已关闭"); return false; } if (!client.isLogin()) { error("请先登录"); return false; } return true; } public void send(final String input) { SmartClient client = getClient(); if (!checkClient(client)) { return; } String name = client.getAccount().getName(); String msg = formatInput(name, input); if (!hideMyInput()) { insertDocument(msg); IMHistoryManager.getInstance().save(getHistoryDir(), getHistoryFile(), msg); } new Thread() { @Override public void run() { post(input); } }.start(); } public void sendWithoutPost(final String msg, boolean raw) { if (!hideMyInput()) { String name = getClient().getAccount().getName(); insertDocument(raw ? msg : formatInput(name, msg)); IMHistoryManager.getInstance().save(getHistoryDir(), getHistoryFile(), msg); } } public void sendFile(final String file) { new Thread() { public void run() { uploadLock = true; try { sendFileInternal(file); } catch (Exception e) { LOG.error("发送文件失败 : " + e); LOG.sendNotification("发送文件失败", String.format("文件:%s(%s)", file, e.getMessage())); error(String.format("发送文件失败:%s(%s)", file, e.getMessage())); } finally { uploadLock = false; } } }.start(); } protected void sendFileInternal(final String file) throws Exception { } protected String encodeInput(String input) { return StringUtils.encodeXml(input); } // 组装成我输入的历史记录,并显示在聊天窗口中 protected String formatInput(String name, String msg) { return IMUtils.formatHtmlMyMsg(System.currentTimeMillis(), name, msg); } public void error(Throwable e) { error(e == null ? "null" : e.toString()); } public void error(final String msg) { insertDocument(String.format("<div class=\"error\">%s</div>", msg)); } private void createUIComponents() { } public void write(final String msg) { insertDocument(msg); } protected JBSplitter splitter; protected ChatHistoryPane top; protected ChatInputPane bottom; protected JEditorPane historyWidget; protected JTextComponent inputWidget; protected JButton btnSend; public void initUI() { top = new ChatHistoryPane(); bottom = new ChatInputPane(); historyWidget = top.getEditorPane(); inputWidget = bottom.getTextPane(); btnSend = bottom.getBtnSend(); btnSend.setVisible(SmartIMSettings.getInstance().getState().SHOW_SEND); btnSend.addActionListener(new SendAction()); inputWidget.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Send"); inputWidget.getActionMap().put("Send", btnSend.getAction()); splitter = new JBSplitter(true); splitter.setSplitterProportionKey("chat.splitter.key"); splitter.setFirstComponent(top.getPanel()); splitter.setSecondComponent(bottom.getPanel()); setContent(splitter); splitter.setPreferredSize(new Dimension(-1, 200)); splitter.setProportion(0.85f); inputWidget.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); if (SmartIMSettings.getInstance().getState().KEY_SEND.equals(SwingUtils.key2string(e))) { String input = inputWidget.getText(); if (!input.isEmpty()) { inputWidget.setText(""); send(input); } e.consume(); } } }); initToolBar(); initHistoryWidget(); } protected void initToolBar() { DefaultActionGroup group = new DefaultActionGroup(); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("SmartQQ", group, false); // toolbar.getComponent().addFocusListener(createFocusListener()); toolbar.setTargetComponent(this); setToolbar(toolbar.getComponent()); initToolBar(group); } protected void initToolBar(DefaultActionGroup group) { group.add(new SendImageAction(this)); group.add(new SendFileAction(this)); // group.add(new SendProjectFileAction(this)); group.add(new SendProjectFileAction2(this)); group.add(new ScrollLockAction(this)); group.add(new ClearHistoryAction(this)); } public class SendAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { String input = inputWidget.getText(); if (!input.isEmpty()) { inputWidget.setText(""); send(input); } } } protected boolean scrollLock = false; protected boolean uploadLock = false; public boolean enableUpload() { return !uploadLock; } public void setScrollLock(boolean scrollLock) { this.scrollLock = scrollLock; } public boolean isScrollLock() { return scrollLock; } protected void initHistoryWidget() { HTMLEditorKit kit = new HTMLEditorKit() { @Override public ViewFactory getViewFactory() { return new WrapHTMLFactory(); } }; final StyleSheet styleSheet = kit.getStyleSheet(); styleSheet.addRule("body {text-align: left; overflow-x: hidden;}"); styleSheet.addRule(".my {font-size: 1 em; font-style: italic; float: left;}"); styleSheet.addRule("div.error {color: red;}"); styleSheet.addRule("img {max-width: 100%; display: block;}"); styleSheet.addRule(".sender {display: inline; float: left;}"); styleSheet.addRule(".content {display: inline-block; white-space: pre-wrap; padding-left: 4px;}"); styleSheet.addRule(".br {height: 1px; line-height: 1px; min-height: 1px;}"); RestUtils.loadStyleAsync(styleSheet); File f = StyleConfPanel.getCssFile(); try { if (f.exists()) { URL url = f.toURI().toURL(); styleSheet.importStyleSheet(url); } else { LOG.error("$idea.config.path not exists, smartim will use default css"); } } catch (Exception e) { LOG.error("加载SmartIM消息CSS失败", e); } HTMLDocument doc = (HTMLDocument)kit.createDefaultDocument(); String initText = String .format("<html><head></head><body><div class=\"welcome\">%s</div></body></html>", imPanel.getWelcome()); historyWidget.setContentType("text/html"); historyWidget.setEditorKit(kit); historyWidget.setDocument(doc); historyWidget.setText(initText); historyWidget.setEditable(false); historyWidget.setBackground(null); historyWidget.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { String desc = e.getDescription(); if (!StringUtils.isEmpty(desc)) { hyperlinkActivated(desc); } } } }); } protected boolean hyperlinkActivated(String desc) { if (desc.startsWith("user://")) { String user = desc.substring(7); try { inputWidget.getDocument().insertString(inputWidget.getCaretPosition(), "@" + user + " ", null); } catch (Exception e) { } } else if (desc.startsWith("code://")) { String code = desc.substring(7); int pos = code.lastIndexOf(':'); String file = code.substring(0, pos); int line = Integer.parseInt(code.substring(pos + 1).trim()); if (line > 0) { line--; } // TODO open file in editor and located to line EditorUtils.openFile(file, line); } else { BareBonesBrowserLaunch.openURL(desc); } return false; } protected void insertDocument(final String msg) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { HTMLEditorKit kit = (HTMLEditorKit)historyWidget.getEditorKit(); HTMLDocument doc = (HTMLDocument)historyWidget.getDocument(); // historyWidget.getDocument().insertString(len - offset, // trimMsg(msg), null); // Element root = doc.getDefaultRootElement(); // Element body = root.getElement(1); // doc.insertBeforeEnd(body, msg); int pos = historyWidget.getCaretPosition(); kit.insertHTML(doc, doc.getLength(), msg, 0, 0, null); historyWidget.setCaretPosition(scrollLock ? pos : doc.getLength()); } catch (Exception e) { e.printStackTrace(); } } }); } }