package org.xmind.ui.internal.e4models; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.inject.Inject; import org.eclipse.e4.core.commands.ECommandService; import org.eclipse.e4.core.commands.EHandlerService; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.CanExecute; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.services.EContextService; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.internal.E4PartWrapper; import org.eclipse.ui.part.IContributedContentsView; import org.eclipse.ui.services.IServiceLocator; import org.xmind.core.Core; import org.xmind.core.IComment; import org.xmind.core.ISheet; import org.xmind.core.ITopic; import org.xmind.core.event.CoreEvent; import org.xmind.core.event.CoreEventRegister; import org.xmind.core.event.ICoreEventListener; import org.xmind.core.event.ICoreEventRegister; import org.xmind.core.event.ICoreEventSupport; import org.xmind.gef.ui.editor.IGraphicalEditor; import org.xmind.ui.internal.comments.CommentTextViewer; import org.xmind.ui.internal.comments.CommentsPartActionBarContributor; import org.xmind.ui.internal.comments.CommentsSelectionProvider; import org.xmind.ui.internal.comments.ICommentTextViewerContainer; import org.xmind.ui.internal.comments.ICommentsActionBarContributor; import org.xmind.ui.internal.comments.SheetCommentsViewer; import org.xmind.ui.resources.ColorUtils; @SuppressWarnings("restriction") public class CommentsPart extends ViewModelPart implements IContributedContentsView, IPartListener, ISelectionChangedListener, ICoreEventListener, ICommentTextViewerContainer { public static final String PART_ID = "org.xmind.ui.modelPart.comment"; //$NON-NLS-1$ public static final String BG_COLOR = "#ffffff"; //$NON-NLS-1$ private static final String COMMENTS_EDIT_CONTEXT_ID = "org.xmind.ui.context.comments.edit"; //$NON-NLS-1$ private static final String INVALID_COLER = "#f0f0f0"; //$NON-NLS-1$ private static class EActionHandler { private IAction action; public EActionHandler(IAction action) { this.action = action; } @Execute public void execute() { if (action.getStyle() == IAction.AS_CHECK_BOX || action.getStyle() == IAction.AS_RADIO_BUTTON) { action.setChecked(!action.isChecked()); } action.run(); } @CanExecute public boolean canExecute() { return action.isEnabled(); } } private class ContextActivator implements IPartListener { public ContextActivator() { getAdapter(IWorkbenchWindow.class).getActivePage() .addPartListener(this); } public void partActivated(IWorkbenchPart part) { MPart modelPart = CommentsPart.this.getAdapter(MPart.class); Object e4Wrapper = modelPart.getTransientData() .get(E4PartWrapper.E4_WRAPPER_KEY); if (part == e4Wrapper) { activateContext(); } } public void partBroughtToTop(IWorkbenchPart part) { } public void partClosed(IWorkbenchPart part) { getAdapter(IWorkbenchWindow.class).getActivePage() .removePartListener(this); } public void partDeactivated(IWorkbenchPart part) { MPart modelPart = CommentsPart.this.getAdapter(MPart.class); Object e4Wrapper = modelPart.getTransientData() .get(E4PartWrapper.E4_WRAPPER_KEY); if (part == e4Wrapper) { deactivateContext(); } } public void partOpened(IWorkbenchPart part) { } private void activateContext() { contextService.activateContext(COMMENTS_EDIT_CONTEXT_ID); } private void deactivateContext() { contextService.deactivateContext(COMMENTS_EDIT_CONTEXT_ID); } } private class TextAction extends Action { private TextViewer textViewer; private int op; public TextAction(int op) { this.op = op; } public void run() { if (textViewer != null && textViewer.canDoOperation(op)) { textViewer.doOperation(op); contentComposite.pack(); } } public void update(TextViewer textViewer) { this.textViewer = textViewer; setEnabled(textViewer != null && textViewer.canDoOperation(op)); } } private class CommitCommentHandler { @Execute public void execute() { saveComment(); } } @Inject private EHandlerService handlerService; @Inject private EContextService contextService; private CommentsPartActionBarContributor contributor; private ISelectionProvider selectionProvider = new CommentsSelectionProvider(); private List<TextAction> textActions = new ArrayList<TextAction>(7); private IGraphicalEditor contributingEditor; private ICoreEventRegister commentEventRegister; private ICoreEventRegister globalEventRegister; private Control control; private ScrolledComposite sc; private Composite contentComposite; private SheetCommentsViewer contentViewer; private ISheet sheet; private ControlListener controlListener; private CommitCommentHandler commitCommentHandler = new CommitCommentHandler(); private IComment latestCreatedComment; private IComment selectedComment; private IComment editingComment; private boolean modified; private String insertTarget; private Map<String, Object> handlers = new HashMap<String, Object>(); private Map<String, IAction> globalActions = new HashMap<String, IAction>( 7); @Override protected Control doCreateContent(Composite parent) { contributor = new CommentsPartActionBarContributor(this, contributingEditor); control = createControl(parent); activateHandlers(); new ContextActivator(); getAdapter(IWorkbenchWindow.class).getActivePage() .addPartListener(this); return control; } private Composite createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setBackground( (Color) resources.get(ColorUtils.toDescriptor(BG_COLOR))); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); GridLayout gridLayout = new GridLayout(1, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.verticalSpacing = 0; gridLayout.horizontalSpacing = 0; composite.setLayout(gridLayout); contentComposite = createContentComposite(composite); return composite; } private Composite createContentComposite(Composite parent) { sc = new ScrolledComposite(parent, SWT.V_SCROLL); sc.setBackground(parent.getBackground()); sc.setLayoutData(new GridData(GridData.FILL_BOTH)); sc.setExpandHorizontal(true); final Composite composite = new Composite(sc, SWT.NONE); composite.setBackground(composite.getParent().getBackground()); GridLayout gridLayout = new GridLayout(1, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; gridLayout.verticalSpacing = 0; gridLayout.horizontalSpacing = 0; gridLayout.marginBottom = 0; composite.setLayout(gridLayout); sc.setContent(composite); sc.getVerticalBar().setIncrement(17); sc.addControlListener(getControlListener()); return composite; } private ControlListener getControlListener() { if (controlListener == null) { controlListener = new ControlListener() { public void controlMoved(ControlEvent e) { } public void controlResized(ControlEvent e) { e.widget.getDisplay().asyncExec(new Runnable() { public void run() { if (contentComposite != null && !contentComposite.isDisposed()) { contentComposite.pack(); } } }); } }; } return controlListener; } private void setInitialInput() { IEditorPart activeEditor = getAdapter(IWorkbenchWindow.class) .getActivePage().getActiveEditor(); if (activeEditor instanceof IGraphicalEditor) { setContributingEditor((IGraphicalEditor) activeEditor); } else { setInput(null); } } private void setInput(ISheet sheet) { if (this.sheet == sheet) { return; } this.sheet = sheet; unhookSheet(); update(); hookSheet(); } private void hookSheet() { if (sheet != null) { if (commentEventRegister == null) commentEventRegister = new CoreEventRegister(sheet .getOwnedWorkbook().getAdapter(ICoreEventSupport.class), this); commentEventRegister.register(Core.CommentAdd); commentEventRegister.register(Core.CommentRemove); registerGlobalEvent(); } } private void unhookSheet() { if (commentEventRegister != null) { commentEventRegister.unregisterAll(); commentEventRegister = null; } unRegisterGlobalEvent(); } private void registerGlobalEvent() { globalEventRegister = new CoreEventRegister( sheet.getOwnedWorkbook().getAdapter(ICoreEventSupport.class), this); globalEventRegister.register(Core.CommentContent); } private void unRegisterGlobalEvent() { if (globalEventRegister != null) { globalEventRegister.unregisterAll(); globalEventRegister = null; } } private void update() { resetSelectedComment(); updateCompositeEnabled(); updateComments(); setModified(false); setEditingComment(null); setInsertTarget(null); } private void resetSelectedComment() { contributor.selectedCommentChanged(null); } private void updateCompositeEnabled() { sc.setEnabled(sheet != null); if (sheet == null) { contentComposite.getParent().setBackground((Color) resources .get(ColorUtils.toDescriptor(INVALID_COLER))); contentComposite.setBackground((Color) resources .get(ColorUtils.toDescriptor(INVALID_COLER))); } else { contentComposite.getParent().setBackground(control.getBackground()); contentComposite.setBackground(control.getBackground()); } } private void updateComments() { contentComposite.setRedraw(false); clearContent(); selectionProvider.setSelection(null); if (sheet != null) { contentViewer = new SheetCommentsViewer(sheet, contributor, selectionProvider, this, contributingEditor); contentViewer.create(contentComposite); } contentComposite.pack(true); contentComposite.layout(true, true); contentComposite.setRedraw(true); } private void clearContent() { Control[] controls = contentComposite.getChildren(); if (controls != null) { for (Control control : controls) { if (control != null && !control.isDisposed()) { control.dispose(); control = null; } } } } protected boolean postConfiguration(IWorkbenchPart workbenchPart, MPart part) { // prevent PartRenderingEngine's limbo shell. setInitialInput(); super.postConfiguration(workbenchPart, part); IWorkbenchPartSite site = workbenchPart.getSite(); if (site instanceof IViewSite) { IActionBars actionBars = ((IViewSite) site).getActionBars(); if (actionBars == null) { return false; } IServiceLocator serviceLocator = actionBars.getServiceLocator(); if (serviceLocator == null) return false; IEclipseContext eclipseContext = serviceLocator .getService(IEclipseContext.class); eclipseContext.set(ECommandService.class, serviceLocator.getService(ECommandService.class)); eclipseContext.set(EHandlerService.class, serviceLocator.getService(EHandlerService.class)); registerGlobalTextActionHandlers(); return true; } return false; } private void registerGlobalTextActionHandlers() { activateGlobalTextHandler(ActionFactory.UNDO, ITextOperationTarget.UNDO); activateGlobalTextHandler(ActionFactory.REDO, ITextOperationTarget.REDO); activateGlobalTextHandler(ActionFactory.CUT, ITextOperationTarget.CUT); activateGlobalTextHandler(ActionFactory.COPY, ITextOperationTarget.COPY); activateGlobalTextHandler(ActionFactory.PASTE, ITextOperationTarget.PASTE); activateGlobalTextHandler(ActionFactory.SELECT_ALL, ITextOperationTarget.SELECT_ALL); } private void activateGlobalTextHandler(ActionFactory actionFactory, int textOp) { TextAction textAction = new TextAction(textOp); String commandId = actionFactory.getCommandId(); textAction.setActionDefinitionId(commandId); Object handler = new EActionHandler(textAction); handlerService.activateHandler(commandId, handler); handlers.put(commandId, handler); textAction.setId(actionFactory.getId()); IWorkbenchAction workbenchAction = actionFactory .create(getAdapter(IWorkbenchWindow.class)); textAction.setText(workbenchAction.getText()); workbenchAction.dispose(); textActions.add(textAction); globalActions.put(actionFactory.getId(), textAction); } private void unregisterGlobalTextActionHandlers() { if (handlerService != null) { for (Entry<String, Object> entry : handlers.entrySet()) { handlerService.deactivateHandler(entry.getKey(), entry.getValue()); } handlers.clear(); } } public IAction getGlobalAction(String actionId) { return globalActions.get(actionId); } public void updateTextActions(TextViewer textViewer) { if (textViewer != null) { for (TextAction action : textActions) { action.update(textViewer); } } } private void activateHandlers() { handlerService.activateHandler("org.xmind.ui.command.commitComments", //$NON-NLS-1$ commitCommentHandler); } private void deactivateHandlers() { handlerService.deactivateHandler("org.xmind.ui.command.commitComments", //$NON-NLS-1$ commitCommentHandler); } private void saveComment() { if (contentViewer != null) { contentViewer.save(); } } public void setFocus() { if (control != null && !control.isDisposed()) { control.setFocus(); } } private void setContributingEditor(IGraphicalEditor editor) { if (editor == contributingEditor) { return; } if (contributingEditor != null) { ISelectionProvider selectionProvider = contributingEditor.getSite() .getSelectionProvider(); if (selectionProvider != null) selectionProvider.removeSelectionChangedListener(this); } contributingEditor = editor; if (contributingEditor != null) { ISelectionProvider selectionProvider = contributingEditor.getSite() .getSelectionProvider(); if (selectionProvider != null) { selectionProvider.addSelectionChangedListener(this); } } if (getControl().isDisposed()) { return; } if (contentViewer != null) { contentViewer.setTargetEditor(contributingEditor); } if (contributor != null) { contributor.setTargetEditor(contributingEditor); } ISheet sheet = getSheet(contributingEditor); setInput(sheet); } public void partActivated(IWorkbenchPart part) { if (part instanceof IGraphicalEditor) { setContributingEditor((IGraphicalEditor) part); } } public void partBroughtToTop(IWorkbenchPart part) { } public void partClosed(IWorkbenchPart part) { if (part == this.contributingEditor) { setContributingEditor(null); } } public void partDeactivated(IWorkbenchPart part) { } public void partOpened(IWorkbenchPart part) { } public void selectionChanged(SelectionChangedEvent event) { ISheet sheet = getSheet(contributingEditor); setInput(sheet); } private ISheet getSheet(IGraphicalEditor editor) { if (editor != null && editor.getActivePageInstance() != null) { return (ISheet) editor.getActivePageInstance() .getAdapter(ISheet.class); } else { return null; } } public void handleCoreEvent(final CoreEvent event) { final String type = event.getType(); PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { public void run() { if (!contentComposite.isDisposed()) { if (Core.CommentAdd.equals(type) || Core.CommentRemove.equals(type)) { update(); } else if (Core.CommentContent.equals(type)) { IComment comment = (IComment) event.getSource(); if (comment.isOrphan()) { return; } Object source = comment.getOwnedWorkbook() .getElementById(comment.getObjectId()); if ((source instanceof ITopic && ((ITopic) source).getOwnedSheet() == sheet) || source instanceof ISheet && source == sheet) { update(); } } } } }); } public void moveToPreviousTextViewer(CommentTextViewer implementation) { List<CommentTextViewer> implementations = contentViewer .getImplementations(); int index = implementations.indexOf(implementation); if (index <= 0 || index > implementations.size() - 1) { return; } setSelection(new StructuredSelection(implementations.get(index - 1))); } public void moveToNextTextViewer(CommentTextViewer implementation) { List<CommentTextViewer> implementations = contentViewer .getImplementations(); int index = implementations.indexOf(implementation); if (index < 0 || index >= implementations.size() - 1) { return; } setSelection(new StructuredSelection(implementations.get(index + 1))); } private void setSelection(ISelection selection) { selectionProvider.setSelection(selection); } //It can be only used in getAdapter() for findReplaceAction. private CommentTextViewer getImplementation() { if (selectionProvider instanceof CommentsSelectionProvider) { ISelection selection = selectionProvider.getSelection(); if (selection instanceof IStructuredSelection) { Object obj = ((IStructuredSelection) selection) .getFirstElement(); if (obj instanceof CommentTextViewer) { return (CommentTextViewer) obj; } } ISelection oldSelection = ((CommentsSelectionProvider) selectionProvider) .getOldSelection(); if (oldSelection instanceof IStructuredSelection) { Object obj = ((IStructuredSelection) oldSelection) .getFirstElement(); if (obj instanceof CommentTextViewer) { return (CommentTextViewer) obj; } } } return null; } public Composite getContentComposite() { return contentComposite; } public ScrolledComposite getScrolledComposite() { return sc; } public ICommentsActionBarContributor getContributor() { return contributor; } public void setLatestCreatedComment(IComment latestCreatedComment) { this.latestCreatedComment = latestCreatedComment; } public IComment getLatestCreatedComment() { return latestCreatedComment; } public void setSelectedComment(IComment selectedComment) { this.selectedComment = selectedComment; } public IComment getSelectedComment() { return selectedComment; } public Control getControl() { return control; } @Override public void createComment(String objectId) { contentViewer.createNewComment(objectId); } @Override public void cancelCreateComment() { contentViewer.cancelCreateNewComment(); } public void setEditingComment(IComment editingComment) { this.editingComment = editingComment; } public IComment getEditingComment() { return editingComment; } public void setModified(boolean modified) { this.modified = modified; } public boolean isModified() { return modified; } public void setInsertTarget(String objectId) { this.insertTarget = objectId; } public String getInsertTarget() { return insertTarget; } public IWorkbenchPart getContributingPart() { return contributingEditor; } public void dispose() { unhookSheet(); deactivateHandlers(); if (contributor != null) { contributor.dispose(); } if (sc != null && !sc.isDisposed()) { sc.removeControlListener(getControlListener()); } getAdapter(IWorkbenchWindow.class).getActivePage() .removePartListener(this); setContributingEditor(null); unregisterGlobalTextActionHandlers(); super.dispose(); textActions = null; } @Override public <T> T getAdapter(Class<T> adapter) { if (adapter == IContributedContentsView.class) { return adapter.cast(this); } else if (adapter == ITextViewer.class) { return adapter.cast(getImplementation() == null ? null : getImplementation().getTextViewer()); } return super.getAdapter(adapter); } }