/* * Copyright 2000-2006 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.communicator.idea.toolWindow; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.ScreenUtil; import com.intellij.util.ui.tree.WideSelectionTreeUI; import jetbrains.communicator.commands.SendMessageCommand; import jetbrains.communicator.core.Pico; import jetbrains.communicator.core.commands.UserCommand; import jetbrains.communicator.core.dispatcher.LocalMessageDispatcher; import jetbrains.communicator.core.dispatcher.Message; import jetbrains.communicator.core.users.User; import jetbrains.communicator.idea.IDEAFacade; import jetbrains.communicator.idea.IdeaLocalMessage; import jetbrains.communicator.idea.actions.BaseAction; import jetbrains.communicator.util.KirTree; import jetbrains.communicator.util.StringUtil; import jetbrains.communicator.util.TreeUtils; import jetbrains.communicator.util.UIUtil; import org.picocontainer.MutablePicoContainer; import javax.swing.*; import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import static com.intellij.util.ui.UIUtil.isUnderAquaLookAndFeel; /** * @author Kir */ public class UserTree extends KirTree { private final LocalMessageDispatcher myLocalMessageDispatcher; private boolean myDelivered; private final MyTreeUI myUi; private final TreeDragListener myDragListener; public UserTree(LocalMessageDispatcher localMessageDispatcher) { IDEAFacade.installIdeaTreeActions(this); myLocalMessageDispatcher = localMessageDispatcher; setEditable(true); getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0), "startEditing"); myDragListener = new TreeDragListener(); myUi = createUI(); setUI((BasicTreeUI)myUi); if (Pico.isLocalTesting()) { addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { super.mousePressed(e); if (e.getClickCount() == 3) { throw new RuntimeException(); } } }); } } private MyTreeUI createUI() { if (SystemInfo.isMac && isUnderAquaLookAndFeel()) { return new MyWideSelectionTreeUIImpl(); } return new MyBasicTreeUIImpl(); } @Override protected boolean isCustomUI() { return true; } public void addNotify() { super.addNotify(); addMouseListener(myDragListener); addMouseMotionListener(myDragListener); } public void removeNotify() { removeMouseListener(myDragListener); removeMouseMotionListener(myDragListener); if (ScreenUtil.isStandardAddRemoveNotify(this)) UIUtil.removeListenersToPreventMemoryLeak(myUi.getRendererPane()); super.removeNotify(); } public void updateUI() { super.updateUI(); if (myUi != null) { setUI((BasicTreeUI)myUi); } } public String getToolTipText(MouseEvent e) { TreePath pathForLocation = getPathForLocation(e.getX(), e.getY()); if (pathForLocation != null) { Object userObject = TreeUtils.getUserObject(pathForLocation); if (userObject instanceof User) { User user = (User) userObject; StringBuffer result = new StringBuffer(); if (!user.getName().equals(user.getDisplayName())) { result.append(StringUtil.getMsg("user.tooltip", user.getName())).append("\n"); } Message[] pendingMessages = myLocalMessageDispatcher.getPendingMessages(user); if (pendingMessages.length > 0) { IdeaLocalMessage ideaLocalMessage = (IdeaLocalMessage) pendingMessages[0]; result.append(ideaLocalMessage.getTitle()); if (result.length() > 0 && ideaLocalMessage.getComment().length() > 0) { result.append(": "); } result.append(ideaLocalMessage.getComment()); } return result.length() == 0 ? super.getToolTipText(e) : result.toString() ; } } return super.getToolTipText(e); } @SuppressWarnings({"RefusedBequest"}) protected void onDblClick(TreePath path, Object pathComponent, MouseEvent e) { Object userObject = TreeUtils.getUserObject(path); if (userObject instanceof User && !myDelivered) { e.consume(); invokeSendMessageAction(); } } protected void onEnter() { super.onEnter(); invokeSendMessageAction(); } protected void onClick(TreePath path, Object pathComponent, MouseEvent e) { super.onClick(path, pathComponent, e); if (e.isShiftDown() || e.isControlDown() || e.isAltDown()) return; myDelivered = false; Object userObject = TreeUtils.getUserObject(pathComponent); if (userObject instanceof User) { User user = (User) userObject; deliverLocalMessage(user, path, e); } } private void deliverLocalMessage(User user, TreePath path, MouseEvent e) { Message[] pendingMessages = myLocalMessageDispatcher.getPendingMessages(user); if (pendingMessages.length > 0) { IdeaLocalMessage message = (IdeaLocalMessage) pendingMessages[0]; if (myLocalMessageDispatcher.sendNow(user, message)) { myDelivered = true; e.consume(); ((MyTreeUI) getUI()).invalidatePath(path); treeDidChange(); } } } protected void invokeSendMessageAction() { MutablePicoContainer container = BaseAction.getContainer(this); UserCommand command = Pico.getCommandManager().getCommand(SendMessageCommand.class, container); if (command.isEnabled()) { command.execute(); } } public TreePath getPathForUser(User user) { TreeNode node = TreeUtils.findNodeWithObject((DefaultMutableTreeNode) getModel().getRoot(), user); return TreeUtils.getPathFromRoot(node); } public TreePath expandUserNode(User user) { TreePath objectPath = getPathForUser(user); if (objectPath != null) { expandPath(objectPath.getParentPath()); } return objectPath; } public interface MyTreeUI { void invalidatePath(TreePath path); Container getRendererPane(); } public static class MyBasicTreeUIImpl extends BasicTreeUI implements MyTreeUI { public void invalidatePath(TreePath path) { treeState.invalidatePathBounds(path); } public Container getRendererPane() { return rendererPane; } } public static class MyWideSelectionTreeUIImpl extends WideSelectionTreeUI implements MyTreeUI { public void invalidatePath(TreePath path) { treeState.invalidatePathBounds(path); } public Container getRendererPane() { return rendererPane; } } }