package eu.jucy.gui.texteditor;
import helpers.GH;
import helpers.SizeEnum;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import logger.LoggerFactory;
import org.apache.log4j.Logger;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import eu.jucy.gui.Application;
import eu.jucy.gui.ApplicationWorkbenchWindowAdvisor;
import eu.jucy.gui.GUIPI;
import eu.jucy.gui.GuiAppender;
import eu.jucy.gui.GuiHelpers;
import eu.jucy.gui.IImageKeys;
import eu.jucy.gui.Lang;
import eu.jucy.gui.favhub.FavHubEditor;
import eu.jucy.gui.search.OpenSearchEditorHandler;
import eu.jucy.gui.texteditor.StyledTextViewer.ControlReplacement;
import eu.jucy.gui.texteditor.StyledTextViewer.Message;
import eu.jucy.gui.texteditor.StyledTextViewer.TextReplacement;
import uc.DCClient;
import uc.FavHub;
import uc.IHub;
import uc.PI;
import uc.files.MagnetLink;
import uc.files.downloadqueue.AbstractDownloadQueueEntry;
import uc.files.downloadqueue.AbstractDownloadFinished;
import uihelpers.SUIJob;
public class URLTextModificator implements ITextModificator {
private static final Logger logger = LoggerFactory.make();
// private static final char URL_CHAR = '\uFFFC';
private static Image IMAGE_URL_ICON,IMAGE_SEARCH_ICON;
private static char[] OPENING_DELIMTER = new char[]{'\"','<','(','{','[','\''},
CLOSING_DELIMITER = new char[]{'\"','>',')','}',']','\''};
private static Image getImageURLIcon() {
if (IMAGE_URL_ICON == null) {
IMAGE_URL_ICON = AbstractUIPlugin.imageDescriptorFromPlugin(
Application.PLUGIN_ID, IImageKeys.VIEWIMAGEICON).createImage();
}
return IMAGE_URL_ICON;
}
private static Image getImageSearchIcon() {
if (IMAGE_SEARCH_ICON == null) {
IMAGE_SEARCH_ICON = AbstractUIPlugin.imageDescriptorFromPlugin(
Application.PLUGIN_ID, IImageKeys.SEARCH_16).createImage();
}
return IMAGE_SEARCH_ICON;
}
public static final String ID = "eu.jucy.gui.URLTextModificator";
private static final String URLENDING = "\\w[\\S]*[\\S&&[^<>\"\\)]]";
// "[\\w\\p{L}\\-_]+(\\.[\\w\\p{L}\\-_]+)+([\\w\\p{L}\\-\\.,@?^=%&:/~\\+#\\(\\)]*[\\w\\p{L}\\-\\@?^=%&/~\\+#])?";
private static final String URL = "(http|ftp|https):\\/\\/"+URLENDING;
private final AbstractLinkType[] LINK_TYPES = new AbstractLinkType[]{
new HTTPLink(),new MagLink(),new HubLink()};
private static final Pattern ANY_URL = Pattern.compile(
"((?:"+URL+")|(?:"+MagnetLink.MagnetURI+")|(?:"+HubLink.HL_PAT+"))");
private static final String[] IMAGE_ENDINGS = new String[] {".png",".jpg",".bmp", ".gif"} ;
private StyledText text;
private StyledTextViewer viewer;
public void init(StyledText st,StyledTextViewer viewer, IHub hub) {
if (st.isDisposed()) {
throw new IllegalStateException("can't init on disposed Text: "+hub.getName()+" "+hub.getFavHub().getHubaddy());
}
this.viewer = viewer;
this.text = st;
text.setBackgroundMode(SWT.INHERIT_FORCE);
}
public void dispose() {}
// public String modifyMessage(String message, Message original, boolean pm) {
// if (message.indexOf(URL_CHAR) != -1 ) {
// message = message.replace(URL_CHAR, ' '); //replace invalid chars..
// }
//
// Matcher m = ANY_URL.matcher(message);
// int minimumSearchpos = 0;
// while (minimumSearchpos < message.length() && m.find(minimumSearchpos)) {
// String uri = m.group();
// AbstractLinkType alt = getMatching(uri);
//
// int start = m.start();
// logger.debug("found image URI: "+uri+" "+uri.length() );
// message = message.substring(0, start)+URL_CHAR
// +(alt.hasImageAfterURI(uri)?" ":"")+message.substring(m.end());
// m = ANY_URL.matcher(message);
//
// minimumSearchpos = start+2;
// }
//
// return message;
// }
public void getMessageModifications(Message original, boolean pm,List<TextReplacement> replacement) {
String message = original.getMessage();
Matcher m = ANY_URL.matcher(message);
int minimumSearchpos = 0;
while (minimumSearchpos < message.length() && m.find(minimumSearchpos)) {
int start = m.start();
String u = m.group();
for (int i=0; i < OPENING_DELIMTER.length;i++) {
if (message.charAt(start-1) == OPENING_DELIMTER[i] && u.charAt(u.length()-1) == CLOSING_DELIMITER[i]) {
u = u.substring(0,u.length()-1);
break;
}
}
final String uri = u;
int end = start+uri.length();
AbstractLinkType alt = getMatching(uri);
replacement.add(new ControlReplacement(start,uri) {
@Override
public Control createControl(StyledText createOn,float[] ascent) {
AbstractLinkType alt = getMatching(this.replacedText);
String linkText = alt.getTextReplacement(this.replacedText);
Link link = new Link(createOn, SWT.NONE);
link.setBackground( GUIPI.getColor(GUIPI.urlModCol));
link.setFont(GUIPI.getFont(GUIPI.urlModFont));
link.setData(this.replacedText);
link.setToolTipText(GuiHelpers.escapeMnemonics(this.replacedText));
link.setText("<a>"+linkText+"</a>");
ascent[0]= 0.8f;
Menu menu = new Menu(link);
MenuItem mi = new MenuItem(menu,SWT.PUSH);
mi.setData(this.replacedText);
mi.setText(Lang.CopyAddressToClipboard);
mi.addSelectionListener(adapter);
link.setMenu(menu);
link.addListener (SWT.Selection, new Listener () {
public void handleEvent(Event event) {
logger.debug("Selection: " + event.text+ " "+event.widget.getData());
String uri = (String)event.widget.getData();
getMatching(uri).execute(uri);
}
});
return link;
}
});
if (alt.hasImageAfterURI(uri)) {
replacement.add(new ControlReplacement(end,"") {
public void apply(StyledText st,List<StyleRange> toAdd,List<ObjectPoint<Image>> imagePoints,List<ObjectPoint<Control>> controlPoints ,int positionInText,Message message) {
float[] ascent = new float[] {2f/3f};
Control c = createControl(st,ascent);
final ObjectPoint<Control> op = ObjectPoint.create(positionInText,replacedText, ascent[0], c, toAdd);
c.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
String uri = (String)e.widget.getData();
getMatching(uri).executeImageClick(uri,op,URLTextModificator.this);
}
});
controlPoints.add(op);
}
@Override
public Control createControl(StyledText createOn,float[] ascent) {
AbstractLinkType alt = getMatching(uri);
Label lab = new Label(createOn,SWT.NONE);
Image img = alt.getImageAfterURI(uri);
lab.setImage(img);
lab.setData(uri);
ascent[0]=2f/3f;
return lab;
}
});
}
logger.debug("found image URI: "+uri+" "+uri.length() );
minimumSearchpos = end;
}
}
private static final SelectionAdapter adapter = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String uri = (String)e.widget.getData();
GuiHelpers.copyTextToClipboard(uri);
}
};
// public void getStyleRange(String message, int start,
// Message originalMessage, List<StyleRange> ranges, List<ObjectPoint<Image>> images) {
//
// Matcher m = ANY_URL.matcher(originalMessage.getMessage());
// int messagePos = 0;
// int charPos = 0;
// while (m.find(messagePos)) {
// String foundURI = m.group();
// AbstractLinkType alt = getMatching(foundURI);
// String linkText = alt.getTextReplacement(foundURI);
//
// int posOfURLChar = message.indexOf(URL_CHAR,charPos);
// int posURI = posOfURLChar+start; // to full text..
// Link link = new Link(text, SWT.NONE);
// link.setBackground( GUIPI.getColor(GUIPI.urlModCol));
// link.setFont(GUIPI.getFont(GUIPI.urlModFont));
// link.setData(foundURI);
// link.setToolTipText(foundURI);
// link.setText("<a>"+linkText+"</a>");
//
// viewer.addControl(link,posURI, 0.8f);
// Menu menu = new Menu(link);
// MenuItem mi = new MenuItem(menu,SWT.PUSH);
// mi.setData(foundURI);
// mi.setText("Copy URI to Clipboard");
// mi.addSelectionListener(adapter);
// link.setMenu(menu);
//
// link.addListener (SWT.Selection, new Listener () {
// public void handleEvent(Event event) {
// logger.debug("Selection: " + event.text+ " "+event.widget.getData());
// String uri = (String)event.widget.getData();
// getMatching(uri).execute(uri);
// }
// });
//
// if (alt.hasImageAfterURI(foundURI)) {
// logger.debug("added image: "+foundURI);
// addLabelImage(posURI+1,foundURI);
// }
//
// messagePos = m.end();
// charPos = posOfURLChar+1;
// }
// }
void addLabelImage(int pos,String uri) {
AbstractLinkType alt = getMatching(uri);
Label lab = new Label(text,SWT.NONE);
Image img = alt.getImageAfterURI(uri);
lab.setImage(img);
lab.setData(uri);
final ObjectPoint<Control> op = viewer.addControl(lab, pos,"", 2f/3f);
lab.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
String uri = (String)e.widget.getData();
getMatching(uri).executeImageClick(uri,op,URLTextModificator.this);
}
});
}
void addLabelReplacementImage(int pos,String uri,final Image img) {
Label lab = new Label(text,SWT.NONE);
lab.setImage(img);
lab.setData(uri);
final ObjectPoint<Control> op = viewer.addControl(lab, pos,"", 2f/3f);
lab.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
String uri = (String)e.widget.getData();
addLabelImage(op.x,uri);
text.redraw();
}
});
lab.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
img.dispose();
}
});
}
public AbstractLinkType getMatching(String uri) {
for (AbstractLinkType alt: LINK_TYPES) {
Matcher m = alt.getLinkPat().matcher(uri);
if (m.matches()) {
return alt;
}
}
throw new IllegalStateException();
}
public static abstract class AbstractLinkType {
private final Pattern linkPat;
private AbstractLinkType(String linkPat) {
super();
this.linkPat = Pattern.compile(linkPat);
}
public abstract void execute(String matched);
public String getTextReplacement(String matched) {
return matched;
}
/**
*
* @param uri - provided URI
* @return an image if there should be an image put after the URI ...
*/
public Image getImageAfterURI(String uri) {
return null;
}
public boolean hasImageAfterURI(String uri) {
return getImageAfterURI(uri) != null;
}
/**
* executed if the image after the URI was clicked instead of the URI itself
* @param uri - the URI before the image..
*/
public void executeImageClick(String uri,ObjectPoint<Control> point,URLTextModificator mod) {}
public Pattern getLinkPat() {
return linkPat;
}
}
private static class HTTPLink extends AbstractLinkType {
private HTTPLink() {
super(URL);
}
@Override
public void execute(String matched) {
try {
URL url = new URL(matched);
IWorkbenchBrowserSupport browserSupport =
PlatformUI.getWorkbench().getBrowserSupport();
IWebBrowser browser = browserSupport.createBrowser("myid");
browser.openURL(url);
} catch (IOException ioe) {
logger.warn(ioe,ioe);
} catch (PartInitException io2) {
logger.warn(io2,io2);
}
}
// @Override
// public void getStyleRanges(List<StyleRange> ranges, int start, int length,String matched, List<ObjectPoint<Image>> images) {
// ranges.add(getURLRange(start, length, GUIPI.getColor(GUIPI.urlModCol)) );
// }
@Override
public void executeImageClick(String uri,ObjectPoint<Control> point,URLTextModificator mod) {
logger.debug("Image url icon pressed: "+uri);
try {
new GraphicalFileDownloader(new URL(uri), point,mod).start();
} catch (MalformedURLException e) {
logger.warn(e,e);
}
}
public boolean hasImageAfterURI(String uri) {
for (String ending: IMAGE_ENDINGS) {
if (uri.toLowerCase().endsWith(ending)) {
return true;
}
}
return false;
}
@Override
public Image getImageAfterURI(String uri) {
for (String ending: IMAGE_ENDINGS) {
if (uri.toLowerCase().endsWith(ending)) {
return getImageURLIcon();
}
}
return null;
}
}
private static class MagLink extends AbstractLinkType {
private MagLink() {
super(MagnetLink.MagnetURI);
}
@Override
public void execute(String matched) {
MagnetLink magnetLink = MagnetLink.parse(matched);
if (magnetLink != null) {
if (magnetLink.isComplete()) {
magnetLink.download();
logger.log(GuiAppender.GUI,String.format(Lang.AddedFileViaMagnet,magnetLink.getName()));
} else {
String search = null;
if (magnetLink.getTTHRoot() != null) {
search = magnetLink.getTTHRoot().toString();
} else if (magnetLink.get(MagnetLink.KEYWORD_TOPIC) != null) {
search = magnetLink.get(MagnetLink.KEYWORD_TOPIC);
}
if (!GH.isNullOrEmpty(search)) {
OpenSearchEditorHandler.openSearchEditor(
PlatformUI.getWorkbench().getActiveWorkbenchWindow()
, search);
}
}
}
}
@Override
public void executeImageClick(final String uri,
final ObjectPoint<Control> point,final URLTextModificator mod) {
MagnetLink magnetLink = MagnetLink.parse(uri);
if (magnetLink != null) {
if (magnetLink.isComplete()) {
point.obj.setEnabled(false);
DCClient dcc = ApplicationWorkbenchWindowAdvisor.get();
File target = dcc.getFilelist().getFile(magnetLink.getTTHRoot());
if (target != null && target.isFile()) {
openFile(target,uri,point,mod);
} else {
target = new File(PI.getTempPath(),magnetLink.getName());
if (target.isFile()) {
openFile(target,uri,point,mod);
} else {
AbstractDownloadQueueEntry adqe = magnetLink.download(target);
if (adqe != null) {
adqe.addDoAfterDownload(new AbstractDownloadFinished() {
public void finishedDownload(final File f) {
new SUIJob() {
@Override
public void run() {
openFile(f,uri,point,mod);
f.deleteOnExit();
}
}.schedule();
}
});
logger.log(GuiAppender.GUI,String.format(Lang.AddedFileViaMagnet,magnetLink.getName()));
}
}
}
} else if (magnetLink.get(MagnetLink.KEYWORD_TOPIC) != null) {
execute(uri);
}
}
}
private void openFile(File f,String uri,ObjectPoint<Control> point,URLTextModificator mod) {
try {
ImageData imgda = ImageDescriptor.createFromURL(f.toURI().toURL()).getImageData();
Image img = GraphicalFileDownloader.scaleIfNeeded(imgda);
mod.addLabelReplacementImage(point.x, uri, img);
} catch (Exception e) {
logger.log(GuiAppender.GUI,"Download failed: "+e,e);
}
}
@Override
public Image getImageAfterURI(String uri) {
MagnetLink magnetLink = MagnetLink.parse(uri);
if (magnetLink != null) {
if (magnetLink.isComplete()) {
String end = "."+magnetLink.getEnding();
logger.debug("found ending: "+end);
for (String ending: IMAGE_ENDINGS) {
if (end.equalsIgnoreCase(ending)) {
return getImageURLIcon();
}
}
} else if (magnetLink.get(MagnetLink.KEYWORD_TOPIC) != null) {
return getImageSearchIcon();
}
}
return null;
}
@Override
public String getTextReplacement(String matched) {
MagnetLink ml = MagnetLink.parse(matched);
if (ml.isComplete()) {
return String.format("%s (%s)",ml.getName(),SizeEnum.getReadableSize(ml.getSize()));
} else if (ml.get(MagnetLink.KEYWORD_TOPIC) != null) {
return String.format("%s ",ml.getName() == null?ml.get(MagnetLink.KEYWORD_TOPIC):ml.getName());
} else {
return matched;
}
}
}
private static class HubLink extends AbstractLinkType {
private static final String HL_PAT = "((?:dchub)|(?:nmdc)|(?:adc))s?:\\/\\/"+URLENDING;
private static final Image FAVHUB_ICON = AbstractUIPlugin.imageDescriptorFromPlugin(
Application.PLUGIN_ID, IImageKeys.FAVHUBS).createImage();
private HubLink() {
super(HL_PAT);
}
@Override
public void execute(String matched) {
new FavHub(matched).connect(ApplicationWorkbenchWindowAdvisor.get());
}
@Override
public void executeImageClick(String uri, ObjectPoint<Control> point,
URLTextModificator mod) {
FavHub fh = new FavHub(uri);
fh.addToFavHubs(ApplicationWorkbenchWindowAdvisor.get().getFavHubs());
GuiHelpers.executeCommand(FavHubEditor.OPEN_FAVHUBS_COMMAND_ID,
Collections.<String,String>emptyMap());
}
@Override
public Image getImageAfterURI(String uri) {
return FAVHUB_ICON;
}
@Override
public boolean hasImageAfterURI(String uri) {
return !ApplicationWorkbenchWindowAdvisor.get().getFavHubs().contains(uri);
}
}
}