package de.urszeidler.shr5.ecp.views; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.apache.pdfbox.exceptions.CryptographyException; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.util.PDFTextStripper; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.draw2d.ColorConstants; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.preference.IPreferenceNode; import org.eclipse.jface.preference.IPreferencePage; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.preference.PreferenceManager; import org.eclipse.jface.preference.PreferenceNode; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; import org.eclipse.wb.swt.ResourceManager; import org.eclipse.wb.swt.SWTResourceManager; import com.google.common.base.Joiner; import de.urszeidler.commons.Duo; import de.urszeidler.eclipse.shr5.Beschreibbar; import de.urszeidler.eclipse.shr5.Quelle; import de.urszeidler.eclipse.shr5.Shr5Factory; import de.urszeidler.eclipse.shr5.SourceBook; import de.urszeidler.eclipse.shr5.util.AdapterFactoryUtil; import de.urszeidler.shr5.ecp.Activator; import de.urszeidler.shr5.ecp.preferences.PreferenceConstants; import de.urszeidler.shr5.ecp.preferences.SourcebookViewerPreferences; import de.urszeidler.shr5.ecp.util.ShadowrunEditingTools; public class SourceBookView extends ViewPart implements ISelectionListener { protected DataBindingContext m_bindingContext; public static final String ID = "de.urszeidler.shr5.ecp.views.SourceBookView"; //$NON-NLS-1$ private Composite container; private Quelle internalQuelle = Shr5Factory.eINSTANCE.createAutoSoft(); private Quelle selection = Shr5Factory.eINSTANCE.createAutoSoft(); private WritableValue displayedText = new WritableValue("", String.class); private Collection<File> workingFiles = Collections.synchronizedCollection(new HashSet<File>()); private Map<String, String> pageMap = new HashMap<String, String>(); private Map<File, PDDocument> bookMap = new HashMap<File, PDDocument>(); private StyledText styledText; private Label lblImage; private Label lblName; private Action nextAction; private Action prevAction; private Action gotoPageAction; private Composite composite_1; private Composite composite_2; private StackLayout layout; private Link link; public SourceBookView() { } @Override public void init(IViewSite site) throws PartInitException { site.getPage().addSelectionListener(this); super.init(site); } @Override public void dispose() { getSite().getPage().removeSelectionListener(this); super.dispose(); } /** * Create contents of the view part. * * @param parent */ @Override public void createPartControl(Composite parent) { container = new Composite(parent, SWT.NONE); container.setLayout(new GridLayout(1, false)); Composite composite = new Composite(container, SWT.NONE); composite.setLayout(new GridLayout(2, false)); composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); lblImage = new Label(composite, SWT.NONE); GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); layoutData.widthHint = 16; lblImage.setLayoutData(layoutData); lblImage.setText(""); lblName = new Label(composite, SWT.NONE); GridData gd_lblName = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); gd_lblName.widthHint = 168; lblName.setLayoutData(gd_lblName); lblName.setText("sourcebook name"); composite_1 = new Composite(container, SWT.NONE); layout = new StackLayout(); composite_1.setLayout(layout); composite_1.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); styledText = new StyledText(composite_1, SWT.V_SCROLL | SWT.BORDER | SWT.READ_ONLY | SWT.WRAP); composite_2 = new Composite(composite_1, SWT.NONE); composite_2.setBackground(SWTResourceManager.getColor(SWT.COLOR_LIST_BACKGROUND)); composite_2.setLayout(new GridLayout(1, false)); link = new Link(composite_2, SWT.NONE); link.setBackground(SWTResourceManager.getColor(SWT.COLOR_LIST_BACKGROUND)); link.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IPreferencePage page = new SourcebookViewerPreferences(); PreferenceManager mgr = new PreferenceManager(); IPreferenceNode node = new PreferenceNode("1", page); mgr.addToRoot(node); PreferenceDialog dialog = new PreferenceDialog(getSite().getShell(), mgr); dialog.create(); dialog.setMessage(page.getTitle()); dialog.open(); } }); link.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true, 1, 1)); link.setText("The current sourcebook is not configured, \nadd the sourcebook in the <a>preferences</a> \nto extract and display the text here."); layout.topControl = composite_2; composite.update(); composite_1.layout(); createActions(); initializeToolBar(); initializeMenu(); m_bindingContext = initDataBindings(); } /** * Create the actions. */ private void createActions() { // Create the actions { nextAction = new Action() { @Override public void run() { try { internalQuelle.setSrcBook(selection.getSrcBook()); internalQuelle.setPage(Integer.toString(Integer.parseInt(selection.getPage()) + 1)); setQuelle(internalQuelle); } catch (Exception e) { } } }; nextAction.setToolTipText("go to the next page"); nextAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor("de.urszeidler.shr5.ecp", "images/rightarrow.gif")); } { prevAction = new Action() { @Override public void run() { try { internalQuelle.setSrcBook(selection.getSrcBook()); internalQuelle.setPage(Integer.toString(Integer.parseInt(selection.getPage()) - 1)); setQuelle(internalQuelle); } catch (Exception e) { } } }; prevAction.setToolTipText("one page back"); prevAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor("de.urszeidler.shr5.ecp", "images/leftarrow.gif")); prevAction.setDescription(""); } { gotoPageAction = new Action("") { @Override public void run() { try { internalQuelle.setSrcBook(selection.getSrcBook()); int pageNumber = Integer.parseInt(selection.getPage()); InputDialog inputDialog = new InputDialog(getSite().getShell(), "Jump to page", "Jump to page number.", Integer.toString(pageNumber), null); if (inputDialog.open() == org.eclipse.jface.dialogs.Dialog.CANCEL) return; pageNumber = Integer.parseInt(inputDialog.getValue()); internalQuelle.setPage(Integer.toString(pageNumber)); setQuelle(internalQuelle); } catch (Exception e) { } } }; gotoPageAction.setToolTipText("jump to page"); gotoPageAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor("de.urszeidler.shr5.ecp", "images/doc_tab.gif")); } } /** * Initialize the toolbar. */ private void initializeToolBar() { IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager(); toolbarManager.add(prevAction); toolbarManager.add(nextAction); toolbarManager.add(gotoPageAction); } /** * Initialize the menu. */ private void initializeMenu() { IMenuManager menuManager = getViewSite().getActionBars().getMenuManager(); } @Override public void setFocus() { // Set the focus } @Override public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (selection instanceof StructuredSelection) { StructuredSelection ss = (StructuredSelection)selection; Object firstElement = ss.getFirstElement(); if (firstElement instanceof Quelle) { final Quelle q = (Quelle)firstElement; setQuelle(q); } } } /** * @param src */ private void setQuelle(final Quelle src) { SourceBook srcBook = src.getSrcBook(); if (srcBook == null) return; selection = src; Image image = AdapterFactoryUtil.getInstance().getLabelProvider().getImage(srcBook); lblImage.setImage(image); lblName.setText(String.format("%s page %s", AdapterFactoryUtil.getInstance().getLabelProvider().getText(srcBook), src.getPage())); final File file = getFileFromPreferences(srcBook);// if (file == null) { displayedText.setValue(""); layout.topControl = composite_2; composite_1.layout(); return; } layout.topControl = styledText; composite_1.layout(); final String key = src.getSrcBook().getName() + src.getPage(); String text = pageMap.get(key); if (text == null) { startJob(src, file, key); } else { displayedText.setValue(text); processText(text, src); } } /** * @param src * @param file * @param key */ private void startJob(final Quelle src, final File file, final String key) { if (workingFiles.contains(file)) return; Job job = new Job("get page text " + key) { @Override protected IStatus run(IProgressMonitor monitor) { try { PDDocument pdDocument = getpdfDoc(file, monitor); if (pdDocument == null) return Status.OK_STATUS; final String text1 = getTextFromPage(src, pdDocument); pageMap.put(key, text1); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { displayedText.setValue(text1); processText(text1, src); } }); } catch (IOException e) { Activator.logError(e); } return Status.OK_STATUS; } }; job.setUser(false); job.schedule(); } private void processText(String text, Quelle q) { if (q instanceof Beschreibbar) { Beschreibbar b = (Beschreibbar)q; String name = b.getName(); if (name == null || name.isEmpty()) return; String lowerCase = text.toLowerCase(); Duo<Integer, Integer> match = findNearesMatch(name, lowerCase); int indexOf = match.getObjectA(); if (indexOf == -1) return; String substring = lowerCase.substring(0, indexOf); String[] split = substring.split("\n"); StyleRange styleRange = new StyleRange(); styleRange.start = indexOf; styleRange.length = match.getObjectB(); styleRange.fontStyle = SWT.BOLD; styleRange.background = ColorConstants.lightGray; styledText.setStyleRange(styleRange); if (substring.isEmpty()) styledText.setTopIndex(0); else styledText.setTopIndex(split.length); styledText.setSelection(indexOf, indexOf + match.getObjectB()); styledText.showSelection(); } } /** * @param name * @param lowerCase * @return {@link Duo} where a is the index and b is the length */ private Duo<Integer, Integer> findNearesMatch(String name, String lowerCase) { int indexOf = findByName(name, lowerCase); if (indexOf == -1) { String[] split = name.split(" "); if (split.length == 2) { indexOf = findByName(split[0], lowerCase); if (indexOf == -1) return new Duo<Integer, Integer>(findByName(split[1], lowerCase), split[0].length()); else return new Duo<Integer, Integer>(indexOf, split[0].length()); } else if (split.length < 2) return new Duo<Integer, Integer>(-1, split[0].length()); for (int i = split.length - 1; i > 0; i--) { String searchstring = Joiner.on(" ").join(Arrays.copyOfRange(split, 0, i)); indexOf = findByName(searchstring, lowerCase); if (indexOf != -1) return new Duo<Integer, Integer>(indexOf, searchstring.length()); } } return new Duo<Integer, Integer>(indexOf, name.length()); } /** * @param name * @param lowerCase * @return */ private int findByName(String name, String lowerCase) { if (name.length() < 3) return -1; String lowerCaseName = name.toLowerCase(); int indexOf = lowerCase.indexOf(lowerCaseName + ":"); if (indexOf == -1) indexOf = lowerCase.indexOf(lowerCaseName + " :"); if (indexOf == -1) indexOf = lowerCase.indexOf(lowerCaseName); return indexOf; } public static File getFileFromPreferences(SourceBook srcBook) { if (srcBook == null) return null; IPreferenceStore store = Activator.getDefault().getPreferenceStore(); String id2 = ShadowrunEditingTools.getId(srcBook); String string = store.getString(PreferenceConstants.LINKED_SOURCEBOOKS + id2); if (string == null) return null; File file = new File(string); if (file.exists()) return file; return null; } /** * Get the text from the pdf. */ private String getTextFromPage(Quelle q, PDDocument pdDocument2) { IPreferenceStore store = Activator.getDefault().getPreferenceStore(); String id2 = ShadowrunEditingTools.getId(q.getSrcBook()); int offset = store.getInt(PreferenceConstants.LINKED_SOURCEBOOKS_OFFSET + id2); try { int page = Integer.parseInt(q.getPage()); String text; synchronized (pdDocument2) { PDFTextStripper pdfTextStripper = new PDFTextStripper(); pdfTextStripper.setStartPage(page + offset); pdfTextStripper.setEndPage(page + offset); pdfTextStripper.setAddMoreFormatting(true); text = pdfTextStripper.getText(pdDocument2).trim(); } text = text.replaceAll("-\n", ""); text = text.replaceAll("\n\n", "---STOP---"); text = text.replaceAll("\n", " "); text = text.replaceAll("---STOP---", "\n\n"); return text; } catch (IOException e) { Activator.logError(e); } catch (NumberFormatException e) { Activator.logError(e); } return ""; } /** * @param file * @param monitor * @return * @throws IOException */ private PDDocument getpdfDoc(final File file, IProgressMonitor monitor) throws IOException { PDDocument pdDocument = bookMap.get(file); if (pdDocument != null) return pdDocument; if (workingFiles.contains(file)) return null; else workingFiles.add(file); monitor.setTaskName("load ..." + file.getName()); try { pdDocument = PDDocument.load(file); if (pdDocument.isEncrypted()) { monitor.setTaskName("decrypt ..."); pdDocument.decrypt(""); pdDocument.setAllSecurityToBeRemoved(true); } bookMap.put(file, pdDocument); return pdDocument; } catch (IOException e) { Activator.logError(e); return null; } catch (CryptographyException e) { Activator.logError(e); return null; } finally { workingFiles.remove(file); } } protected DataBindingContext initDataBindings() { DataBindingContext bindingContext = new DataBindingContext(); // IObservableValue observeTextStyledTextObserveWidget = WidgetProperties.text(SWT.Modify).observeDelayed(10, styledText); bindingContext.bindValue(observeTextStyledTextObserveWidget, displayedText, null, null); // return bindingContext; } }