package eu.jucy.gui.texteditor; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import logger.LoggerFactory; import org.apache.log4j.Logger; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.plugin.AbstractUIPlugin; import uc.IHasUser; import uc.IHub; import uc.IUser; import uc.IHasUser.IMultiUser; import uc.files.MagnetLink; import uc.files.filelist.FileListFile; import uc.files.filelist.IOwnFileList; import uc.files.filelist.IOwnFileList.AddedFile; import uc.protocols.hub.FeedType; import uihelpers.SUIJob; import uihelpers.SelectionProviderIntermediate; import eu.jucy.gui.Application; import eu.jucy.gui.ApplicationWorkbenchWindowAdvisor; import eu.jucy.gui.IImageKeys; import eu.jucy.gui.Lang; import eu.jucy.gui.UCMessageEditor; import eu.jucy.gui.UCWorkbenchPart; import eu.jucy.gui.itemhandler.SendScreenHandler.ScreenShotContributions; import eu.jucy.gui.texteditor.hub.ItemSelectionProvider; /** * * holds common functionality of Hub and PM editor * protected fields not set by this must be set by implementing classes * -> hard to extend... * * @author Quicksilver * */ public abstract class UCTextEditor extends UCMessageEditor { public static final String TEXT_POPUP_ID = "eu.jucy.gui.texteditor"; private static final long NOT_CHATTED_TIME = 60 * 60 *1000; private static final Logger logger = LoggerFactory.make(); protected SendingWriteline sendingWriteline; protected LabelViewer feedLabelViewer; protected StyledTextViewer textViewer; protected boolean messagesWaiting = false; protected TextUserSelectionprovider tus; protected final SelectionProviderIntermediate spi = new SelectionProviderIntermediate(); private final Map<IUser,Long> recentlyChatted = new HashMap<IUser, Long>(); /** * first cleans up added user then adds the latest again.. * @param usr - true if the user wasn't present before.. */ protected boolean put(IUser usr) { long removeAllBefore = System.currentTimeMillis() - NOT_CHATTED_TIME; synchronized (recentlyChatted) { Iterator<Entry<IUser,Long>> it = recentlyChatted.entrySet().iterator(); while (it.hasNext()) { Entry<IUser,Long> e = it.next(); if (e.getValue() < removeAllBefore) { it.remove(); userRemovedFromRecentChatted(e.getKey()); } } if (recentlyChatted.size() > 1000) { //protection against floods removeAll(); } if (recentlyChatted.put(usr, System.currentTimeMillis()) == null) { userAddedToRecentChatted(usr); return true; } return false; } } protected boolean contains(IUser usr) { synchronized (recentlyChatted) { Long chattedLast= recentlyChatted.get(usr); if (chattedLast == null) { return false; } else { return chattedLast > System.currentTimeMillis() - NOT_CHATTED_TIME; } } } protected void removeAll() { synchronized (recentlyChatted) { for (IUser usr: new ArrayList<IUser>(recentlyChatted.keySet())) { recentlyChatted.remove(usr); userRemovedFromRecentChatted(usr); } } } protected void userRemovedFromRecentChatted(IUser usr) {} protected void userAddedToRecentChatted(IUser usr) {} /** * * @param usr * @param join */ protected void showJoinsParts(IUser usr,boolean join) { String joinmes = "*** "+String.format(join?Lang.UserJoins:Lang.UserParts,usr.getNick())+" ***"; appendText( joinmes , usr,MessageType.JOINPART); Long lastChatted; if (!join && (lastChatted = recentlyChatted.get(usr)) != null) { recentlyChatted.put(usr, lastChatted-(NOT_CHATTED_TIME/3) ); } } public UCTextEditor() {} public SendingWriteline getSendingWriteline() { return sendingWriteline; } public Text getWriteline() { return sendingWriteline.getWriteline(); } protected void makeTextActions() { tus = new TextUserSelectionprovider(getText(),getHub()); spi.addSelectionProvider(getText(), tus); getSite().setSelectionProvider(spi); UCWorkbenchPart.createContextPopups(getSite(), TEXT_POPUP_ID, tus, getText()); DropTarget target = new DropTarget(getText(), DND.DROP_DEFAULT | DND.DROP_MOVE); target.setTransfer(new Transfer[] { FileTransfer.getInstance() }); target.addDropListener(new MagnetDropAdapter(false)); DropTarget target2 = new DropTarget(getSendingWriteline().getWriteline(), DND.DROP_DEFAULT | DND.DROP_MOVE); target2.setTransfer(new Transfer[] { FileTransfer.getInstance() }); target2.addDropListener(new MagnetDropAdapter(true)); } protected void addedFile(FileListFile file,boolean append,boolean addedOutsideShare) { MagnetLink ml = new MagnetLink(file); if (append) { getSendingWriteline().getWriteline().append(ml.toString()); } else { getSendingWriteline().send(ml.toString()); } } public abstract void storedPM(IUser receiver,String message,boolean me); protected abstract void setTitleImage(); public void statusMessage(final String message, int severity) { appendText( "*** " +message,null,MessageType.STATUS); changeLabel(message,severity); } protected void changeLabel(final String message, final int severity) { new SUIJob(feedLabelViewer.getLabel()) { public void run() { FeedType ft; switch(severity) { case 1: ft = FeedType.WARN; break; case 2: ft = FeedType.ERROR; break; default: ft = FeedType.NONE; } feedLabelViewer.addFeedMessage(ft,message); } }.scheduleOrRun(); } public void appendText(String text,IUser usr,MessageType type) { appendText(text, usr,System.currentTimeMillis(),type); } /** * * @param text - fully formatted text to append (except textmodificators) * @param usr - usr associated with text.. potentially null * @param received - timestamp text is associated with * @param message - true if that append is a message and not just some text * (if true put will be called) */ public void appendText(final String text,final IUser usr,final long received , final MessageType type) { new SUIJob(textViewer.getText()) { public void run() { if (usr != null && !getHub().getSelf().equals(usr) && MessageType.CHAT.equals(type)) { put(usr); } textViewer.addMessage(text,usr,new Date(received),type); } }.scheduleOrRun(); } @Override public void partActivated() { super.partActivated(); getText().redraw(); messagesWaiting = false; setTitleImage(); } public void replaceSelectedText(String replacement,String expectedSelection) { textViewer.replaceSelection(replacement, expectedSelection); } public abstract IHub getHub(); public void dispose() { removeAll(); super.dispose(); } public void getContributionItems(List<IContributionItem> items) { super.getContributionItems(items); MenuManager mm = new MenuManager(Lang.SendScreen ,AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, IImageKeys.SCREENSHOT_ICON) ,null); mm.add(new ScreenShotContributions()); items.add( mm ); } /** * * @param f - the file that should be dropped * @param append - if true -> append to writeline else -> send directly.. */ public void dropFile(File f,final boolean append) { if (f.isFile()) { IOwnFileList iof = ApplicationWorkbenchWindowAdvisor.get().getFilelist(); UCTextEditor uct = UCTextEditor.this; IUser droppedFor = uct instanceof IHasUser && ! (uct instanceof IMultiUser) ? ((IHasUser)UCTextEditor.this).getUser() :null; iof.immediatelyAddFile(f, true,droppedFor, new AddedFile() { @Override public void addedFile(final FileListFile file,final boolean addedOutsideShare) { new SUIJob(getText()) { @Override public void run() { UCTextEditor.this.addedFile(file,append,addedOutsideShare); } }.schedule(); } }); } } private final class MagnetDropAdapter extends DropTargetAdapter { private final boolean append; public MagnetDropAdapter(boolean append) { this.append = append; } public void drop(DropTargetEvent event) { String fileList[] = null; FileTransfer ft = FileTransfer.getInstance(); if (ft.isSupportedType(event.currentDataType)) { fileList = (String[])event.data; for (String file:fileList) { File f = new File(file); UCTextEditor.this.dropFile(f,append); } } } } /** * selects user if a nick is under the mousepointer * otherwise selects the text currently selected * * @author Quicksilver * */ public static class TextUserSelectionprovider extends ItemSelectionProvider implements ISelectionProvider { private final StyledText text; private final IHub hub; private final Point mousepos = new Point(0,0); /** * * @param text a text on which selections happen * @param hub where the users appearing in that text are from */ public TextUserSelectionprovider(StyledText text,IHub hub) { this.text = text; this.hub = hub; addListeners(); setSelection(null); } private void addListeners() { text.addMouseListener(new MouseAdapter() { public void mouseDown(MouseEvent e) { mousepos.x = e.x; mousepos.y = e.y; IUser usr = getUsrFromPosition(mousepos.x,mousepos.y); logger.debug("slection set to: "+ (usr!= null?usr.toString():"empty")); if (usr != null) { setSelection(usr); } else { setSelection(text.getSelectionText()); } } }); } /** * gets user by MousePosition in styled Text.. * * @return null if none found */ protected IUser getUsrFromPosition(int x, int y) { int cursorPos ; try { cursorPos = text.getOffsetAtLocation(new Point(x,y)); } catch (IllegalArgumentException iae) { return null; //break .. don't know how to do this diffently.. } String tFound = text.getText(); int space = tFound.lastIndexOf(' ', cursorPos); int smaller = tFound.lastIndexOf('<', cursorPos); int smaller2 = tFound.lastIndexOf('\n', cursorPos); int begin = Math.max(space, smaller); begin = Math.max(smaller2, begin); int space2 = tFound.indexOf(' ', cursorPos); int bigger = tFound.indexOf('>', cursorPos); int bigger2 = tFound.indexOf('\n', cursorPos); int end = smallestNonnegative(space2, bigger); end = smallestNonnegative(end, bigger2); // logger.debug("spacebegin:"+space+" <begin:"+smaller+"\n" // +" spaceend:"+space2+" >ende:"+bigger +"\nstart:"+begin+" end:"+end); if (begin != -1 && end != -1 && begin < end) { String nick = tFound.substring(begin+1, end); logger.debug("nick found: "+nick); return hub.getUserByNick(nick); } return null; } private static int smallestNonnegative(int a ,int b) { if (a < 0 || b < 0) { return Math.max(a, b); } else { return Math.min(a, b); } } } }