package org.goko.tools.editor; import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.Supplier; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.commons.collections.CollectionUtils; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.di.UIEventTopic; import org.eclipse.e4.ui.di.UISynchronize; import org.eclipse.e4.ui.workbench.UIEvents; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IFindReplaceTarget; import org.eclipse.jface.text.IUndoManager; import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISharedTextColors; import org.eclipse.jface.text.source.OverviewRuler; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabFolder2Adapter; import org.eclipse.swt.custom.CTabFolderEvent; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.wb.swt.ResourceManager; import org.goko.common.dialog.GkDialog; import org.goko.core.common.exception.GkException; import org.goko.tools.editor.component.GCodeSourceViewer; import org.goko.tools.editor.component.annotation.BasicAnnotationAccess; import org.goko.tools.editor.component.provider.DocumentProviderAdapter; import org.goko.tools.editor.component.provider.IDocumentProvider; import org.goko.tools.editor.component.provider.IDocumentProviderListener; /** * Part for the GCode editor * * @author Psyko * @date 23 mai 2016 */ public class GCodeEditorPart{ /** Main tab folder */ private CTabFolder mainTabFolder; /** Map of Tab item for each IDocumentProvider */ private Map<IDocumentProvider, CTabItem> mapTabItemByDocumentProvider; /** Map of SourceViewer for each IDocumentProvider */ private Map<IDocumentProvider, GCodeSourceViewer> mapSourceViewerByDocumentProvider; /** UI synchronize */ @Inject private UISynchronize uiSynchronize; @Inject private IEventBroker eventBroker; /** Supplier for IFindReplaceTarget */ private Supplier<IFindReplaceTarget> findReplaceTargetSupplier; @Inject public GCodeEditorPart(IEclipseContext context) { this.mapTabItemByDocumentProvider = new HashMap<IDocumentProvider, CTabItem>(); this.mapSourceViewerByDocumentProvider = new HashMap<IDocumentProvider, GCodeSourceViewer>(); this.createFindReplaceTargetSupplier(); context.set(GCodeEditorPart.class, this); } @PostConstruct public void postConstruct(Composite parent) throws FileNotFoundException { mainTabFolder = new CTabFolder(parent, SWT.FLAT); mainTabFolder.setUnselectedCloseVisible(false); mainTabFolder.addCTabFolder2Listener(new TabItemCloseListener()); } @Inject @Optional public void onOpenRequest(@UIEventTopic(GCodeEditorTopic.TOPIC_OPEN_PART_EDITOR) IDocumentProvider provider) throws GkException{ CTabItem targetTab = mapTabItemByDocumentProvider.get(provider); if(targetTab == null){ targetTab = new CTabItem(mainTabFolder, SWT.CLOSE); if(provider.isModifiable()){ targetTab.setText(provider.getDocumentName()); targetTab.setImage(ResourceManager.getPluginImage("org.goko.tools.editor", "resources/icons/document-attribute-g.png")); }else{ targetTab.setText(provider.getDocumentName()+" (Read only)"); targetTab.setImage(ResourceManager.getPluginImage("org.goko.tools.editor", "resources/icons/lock-small.png")); } targetTab.setData(provider); ISharedTextColors sharedColors = new ISharedTextColors(){ @Override public Color getColor(RGB rgb) { return ResourceManager.getColor(rgb); } @Override public void dispose() { ResourceManager.dispose(); } }; IAnnotationAccess access = (IAnnotationAccess) new BasicAnnotationAccess(); IOverviewRuler overviewRuler = new OverviewRuler(access, 12, sharedColors); GCodeSourceViewer viewer = new GCodeSourceViewer(mainTabFolder, overviewRuler, access, SWT.V_SCROLL | SWT.H_SCROLL); mapSourceViewerByDocumentProvider.put(provider, viewer); IDocument document = provider.getDocument(); //viewer.setDocument(document); viewer.setDocumentProvider(provider); // viewer.setEditable(provider.isModifiable()); targetTab.setControl(viewer.getControl()); final CTabItem finalTargetTab = targetTab; IDocumentProviderListener listener = new DocumentProviderAdapter() { @Override public void onDirtyChanged(IDocumentProvider provider) { if(provider.isDirty()){ finalTargetTab.setText(provider.getDocumentName()+"*"); }else{ finalTargetTab.setText(provider.getDocumentName()); } forceHandlerUpdate(); } /** (inheritDoc) * @see org.goko.tools.editor.component.provider.DocumentProviderAdapter#aboutToClose(org.goko.tools.editor.component.provider.IDocumentProvider) */ @Override public void aboutToClose(final IDocumentProvider targetProvider) { uiSynchronize.syncExec(new Runnable() { @Override public void run() { askForSave(getActiveDocumentProvider()); } }); } /** (inheritDoc) * @see org.goko.tools.editor.component.provider.DocumentProviderAdapter#onClosed(org.goko.tools.editor.component.provider.IDocumentProvider) */ @Override public void onClosed(final IDocumentProvider targetProvider) { uiSynchronize.syncExec(new Runnable() { @Override public void run() { closeByDocumentProvider(targetProvider); } }); } }; provider.addDocumentProviderListener(listener); document.addDocumentListener(new IDocumentListener() { /** (inheritDoc) * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ @Override public void documentChanged(DocumentEvent event) { forceHandlerUpdate(); } /** (inheritDoc) * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ @Override public void documentAboutToBeChanged(DocumentEvent event) { } }); mapTabItemByDocumentProvider.put(provider, targetTab); } mainTabFolder.setSelection(targetTab); forceHandlerUpdate(); } public void closeByDocumentProvider(IDocumentProvider provider) { if(provider != null && mapSourceViewerByDocumentProvider.containsKey(provider)){ mapSourceViewerByDocumentProvider.remove(provider); CTabItem tabItem = mapTabItemByDocumentProvider.remove(provider); tabItem.dispose(); provider.removeAllDocumentProviderListener(); } } public IDocumentProvider getActiveDocumentProvider(){ CTabItem currentTab = mainTabFolder.getSelection(); if(currentTab == null){ return null; } return getDocumentProvider(currentTab); } public IDocumentProvider getDocumentProvider(CTabItem tab){ IDocumentProvider provider = (IDocumentProvider) tab.getData(); return provider; } public IFindReplaceTarget getFindReplaceTarget(){ if(getActiveDocumentProvider() != null){ GCodeSourceViewer sourceViewer = mapSourceViewerByDocumentProvider.get(getActiveDocumentProvider()); return sourceViewer.getFindReplaceTarget(); } return null; } public IUndoManager getUndoManager(){ if(getActiveDocumentProvider() != null){ GCodeSourceViewer sourceViewer = mapSourceViewerByDocumentProvider.get(getActiveDocumentProvider()); return sourceViewer.getUndoManager(); } return null; } public void saveActiveDocument(){ IDocumentProvider provider = getActiveDocumentProvider(); try { provider.saveDocument(null); } catch (GkException e) { GkDialog.openDialog(e); } } public boolean isAnyUnsavedDocument(){ Set<IDocumentProvider> providerSet = mapTabItemByDocumentProvider.keySet(); if(CollectionUtils.isNotEmpty(providerSet)){ for (IDocumentProvider documentProvider : providerSet) { if (documentProvider.isDirty()) { return true; } } } return false; } public void saveAllDocument(){ Set<IDocumentProvider> providerSet = mapTabItemByDocumentProvider.keySet(); if(CollectionUtils.isNotEmpty(providerSet)){ for (IDocumentProvider documentProvider : providerSet) { try { documentProvider.saveDocument(null); } catch (GkException e) { GkDialog.openDialog(e); } } } } @Focus public void focus(){ forceHandlerUpdate(); } protected void forceHandlerUpdate(){ eventBroker.send(UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC, UIEvents.ALL_ELEMENT_ID); } protected void askForSave(final IDocumentProvider provider){ if(provider.isDirty()){ uiSynchronize.syncExec(new Runnable() { @Override public void run() { MessageDialog saveDialog = new MessageDialog(null, "Save", null, "File '"+provider.getDocumentName()+"' has unsaved modification. Would you like to save them now ?", MessageDialog.QUESTION_WITH_CANCEL, new String[]{ IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL}, 0 ); int result = saveDialog.open(); if(result == 0){ // YES Button saveActiveDocument(); }else if(result == 1){ // NO button // Don't save but set document not dirty provider.setDirty(false); } } }); } } /** * Listener for tab closing event * @author Psyko * @date 28 mai 2016 */ class TabItemCloseListener extends CTabFolder2Adapter{ /** (inheritDoc) * @see org.eclipse.swt.custom.CTabFolder2Adapter#close(org.eclipse.swt.custom.CTabFolderEvent) */ @Override public void close(CTabFolderEvent event) { IDocumentProvider provider = getActiveDocumentProvider(); if(provider == null){ return; } if(provider.isDirty()){ askForSave(provider); event.doit = !provider.isDirty(); } if(event.doit){ closeByDocumentProvider(provider); } } } /** * Creates the Supplier<IFindReplaceTarget> to make sure the search windows is always on the active document */ void createFindReplaceTargetSupplier(){ findReplaceTargetSupplier = new Supplier<IFindReplaceTarget>() { @Override public IFindReplaceTarget get() { return getFindReplaceTarget(); } }; } /** * @return the findReplaceTargetSupplier */ public Supplier<IFindReplaceTarget> getFindReplaceTargetSupplier() { return findReplaceTargetSupplier; } /** * @param findReplaceTargetSupplier the findReplaceTargetSupplier to set */ public void setFindReplaceTargetSupplier(Supplier<IFindReplaceTarget> findReplaceTargetSupplier) { this.findReplaceTargetSupplier = findReplaceTargetSupplier; } }