package org.xmind.ui.internal.notes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Color;
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.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.xmind.core.Core;
import org.xmind.core.IBoundary;
import org.xmind.core.IRelationship;
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.MindMapMessages;
import org.xmind.ui.internal.actions.DeleteNotesAction;
import org.xmind.ui.internal.e4models.IModelConstants;
import org.xmind.ui.internal.utils.E4Utils;
import org.xmind.ui.mindmap.MindMapUI;
import org.xmind.ui.resources.ColorUtils;
import org.xmind.ui.resources.FontUtils;
import org.xmind.ui.util.MindMapUtils;
import org.xmind.ui.util.TextFormatter;
public class SheetNotesViewer
implements INotesContentViewer, ICoreEventListener {
// private static final int TRUNCATE_LENGTH = 50;
private static final int EXTRA_WIDTH = 28;
private ISheet sheet;
private IBoundary boundary;
private IRelationship relationship;
private Composite composite;
// private ToolBarManager toolBarManager = null;
private ScrolledComposite sc;
private Composite contentComposite;
private List<Composite> textComposites = new ArrayList<Composite>();
private List<Control> textControls = new ArrayList<Control>();
private Map<Composite, ITopic> map = new HashMap<Composite, ITopic>();
private Composite currentTextComposite;
private IGraphicalEditor editor;
private int scClientWidth = 0;
private DeleteNotesAction deleteNotesAction;
private ICoreEventRegister notesEventRegister;
private ICoreEventRegister titleEventRegister;
private Color originalColor;
private Color hoverColor = ColorUtils.getColor("#f9f9f9"); //$NON-NLS-1$
private Color selectColor = ColorUtils.getColor("#f5f5f5"); //$NON-NLS-1$
private Listener filter;
//storage the control of each note, used to handle mouseEnter and mouseClick event
private List<Control> controls = new ArrayList<Control>();
private ResourceManager resources;
public SheetNotesViewer(IGraphicalEditor editor) {
this.editor = editor;
}
public Control createControl(Composite parent) {
resetCollections();
composite = new Composite(parent, SWT.NONE);
resources = new LocalResourceManager(JFaceResources.getResources(),
composite);
composite.setBackground(
parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
createContentArea(composite);
// composite.addDisposeListener(new DisposeListener() {
// public void widgetDisposed(DisposeEvent e) {
// handleControlDispose(e);
// }
// });
return composite;
}
// private void handleControlDispose(DisposeEvent e) {
// if (toolBarManager != null) {
// toolBarManager.dispose();
// toolBarManager = null;
// }
// }
private void resetCollections() {
if (textComposites != null) {
textComposites.clear();
}
if (textControls != null) {
textControls.clear();
}
if (map != null) {
map.clear();
}
if (controls != null) {
controls.clear();
}
}
private void createContentArea(Composite parent) {
// Control toolBar = createToolBar(parent);
// if (toolBar != null) {
// createSeparator(parent);
// }
contentComposite = createContentComposite(parent);
deleteNotesAction = new DeleteNotesAction(this);
//add mouse enter filter
addMouseFilter();
contentComposite.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
removeMouseFilter();
}
});
}
// private Control createToolBar(Composite parent) {
// toolBarManager = new ToolBarManager(SWT.FLAT);
// deleteNotesAction = new DeleteNotesAction(this);
// toolBarManager.add(deleteNotesAction);
// parent.addListener(SWT.Resize, new Listener() {
// public void handleEvent(Event event) {
// toolBarManager.update(true);
// }
// });
// ToolBar toolBar = toolBarManager.createControl(parent);
// toolBar.setBackground(parent.getDisplay().getSystemColor(
// SWT.COLOR_WIDGET_BACKGROUND));
// toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
//
// return toolBar;
// }
// private void createSeparator(Composite parent) {
// Label sep = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
// sep.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
// sep.setBackground(parent.getDisplay().getSystemColor(
// SWT.COLOR_WIDGET_BACKGROUND));
// }
private Composite createContentComposite(Composite parent) {
sc = new ScrolledComposite(parent, SWT.V_SCROLL);
// sc.setAlwaysShowScrollBars(true);
sc.setLayoutData(new GridData(GridData.FILL_BOTH));
sc.setBackground(parent.getBackground());
sc.setExpandHorizontal(true);
sc.setMinSize(SWT.DEFAULT, SWT.DEFAULT);
Composite composite = new Composite(sc, SWT.NONE);
composite.setBackground(parent.getBackground());
GridLayout gridLayout = new GridLayout(1, false);
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 10;
gridLayout.marginTop = 0;
gridLayout.verticalSpacing = 10;
gridLayout.horizontalSpacing = 0;
composite.setLayout(gridLayout);
sc.setContent(composite);
sc.getVerticalBar().setIncrement(17);
return composite;
}
private void showAllNotes(Composite parent) {
sc.setExpandVertical(false);
List<ITopic> topics = NotesUtils.getAllTopicsWithNotes(sheet);
if (topics.size() == 0) {
sc.setExpandVertical(true);
createNullContent(parent);
return;
}
for (int i = 0; i < topics.size() - 1; i++) {
showLabelAndNote(parent, topics.get(i));
createSeparatorLine(parent);
}
showLabelAndNote(parent, topics.get(topics.size() - 1));
if (sc.getClientArea().width > 0) {
for (Control textControl : textControls) {
((GridData) textControl.getLayoutData()).widthHint = sc
.getClientArea().width - EXTRA_WIDTH;
}
}
sc.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event event) {
sc.getDisplay().asyncExec(new Runnable() {
public void run() {
if (sc.isDisposed() || contentComposite.isDisposed()) {
return;
}
if (sc.getClientArea().width > 0) {
if (scClientWidth != sc.getClientArea().width) {
scClientWidth = sc.getClientArea().width;
for (Control textControl : textControls) {
if (textControl != null
&& !textControl.isDisposed()) {
((GridData) textControl
.getLayoutData()).widthHint = sc
.getClientArea().width
- EXTRA_WIDTH;
}
}
contentComposite.pack();
}
}
}
});
}
});
}
private void createNullContent(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setBackground(composite.getParent().getBackground());
composite.setLayoutData(
new GridData(SWT.CENTER, SWT.CENTER, true, true));
GridLayout layout = new GridLayout(1, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.verticalSpacing = 20;
composite.setLayout(layout);
Label label = new Label(composite, SWT.NONE);
label.setBackground(label.getParent().getBackground());
label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
label.setImage((Image) resources
.get(MindMapUI.getImages().get("notes-empty-bg.png", true))); //$NON-NLS-1$
Composite composite2 = new Composite(composite, SWT.NONE);
composite2.setBackground(composite2.getParent().getBackground());
composite2.setLayoutData(
new GridData(SWT.CENTER, SWT.CENTER, false, false));
GridLayout layout2 = new GridLayout(1, false);
layout2.marginWidth = 0;
layout2.marginHeight = 0;
layout2.verticalSpacing = 0;
composite2.setLayout(layout2);
Label label2 = new Label(composite2, SWT.NONE);
label2.setBackground(label2.getParent().getBackground());
label2.setForeground(ColorUtils.getColor("#aaaaaa")); //$NON-NLS-1$
label2.setLayoutData(
new GridData(SWT.CENTER, SWT.CENTER, false, false));
label2.setText(""); //$NON-NLS-1$
label2.setFont(
FontUtils.getRelativeHeight(JFaceResources.DEFAULT_FONT, 2));
Label label3 = new Label(composite2, SWT.NONE);
label3.setBackground(label3.getParent().getBackground());
label3.setForeground(ColorUtils.getColor("#aaaaaa")); //$NON-NLS-1$
label3.setLayoutData(
new GridData(SWT.CENTER, SWT.CENTER, false, false));
label3.setText(MindMapMessages.Comments_FirstAdd_text);
label3.setFont(
FontUtils.getRelativeHeight(JFaceResources.DEFAULT_FONT, 2));
}
private void addMouseFilter() {
Display.getCurrent().addFilter(SWT.MouseEnter, getFilter());
Display.getCurrent().addFilter(SWT.MouseExit, getFilter());
}
private void removeMouseFilter() {
Display.getCurrent().removeFilter(SWT.MouseEnter, getFilter());
Display.getCurrent().removeFilter(SWT.MouseExit, getFilter());
}
private Listener getFilter() {
if (filter == null) {
filter = new Listener() {
public void handleEvent(Event event) {
if (contentComposite.isDisposed()) {
return;
}
if (event.widget instanceof Control) {
Control mouseControl = null;
for (Control control : controls) {
if (control == null || control.isDisposed()) {
continue;
}
boolean isAncestor = isAncestorOf(
(Composite) control,
(Control) event.widget);
if (isAncestor) {
mouseControl = control;
break;
}
}
if (mouseControl != null
&& !mouseControl.isDisposed()) {
switch (event.type) {
case SWT.MouseEnter:
if (mouseControl.getBackground()
.equals(originalColor)) {
setRecursiveBackgroundColor(mouseControl,
hoverColor, null);
((Composite) mouseControl).layout();
}
break;
case SWT.MouseExit:
if (mouseControl.getBackground()
.equals(hoverColor)) {
setRecursiveBackgroundColor(mouseControl,
originalColor, null);
((Composite) mouseControl).layout();
}
break;
}
}
}
}
};
}
return filter;
}
private boolean isAncestorOf(Control composite, Control control) {
if (control == composite) {
return true;
}
Composite parent = control.getParent();
while (parent != null && parent != composite
&& !(parent instanceof Shell)) {
parent = parent.getParent();
}
return parent == composite;
}
private void showLabelAndNote(Composite parent, ITopic topic) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setBackground(parent.getBackground());
originalColor = composite.getBackground();
composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
GridLayout layout = new GridLayout(1, false);
layout.marginWidth = 5;
layout.marginLeft = 0;
layout.marginRight = 0;
layout.marginHeight = 8;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 5;
composite.setLayout(layout);
createTopicLabel(composite, topic);
createSingleNotes(composite, topic);
createContextMenu(composite, topic);
controls.add(composite);
}
//recursion add contextMenu
private void createContextMenu(Composite control, ITopic topic) {
setRecursionContextMenu(control, getContextMenu(control, topic));
}
private void setRecursionContextMenu(Control control, Menu contextMenu) {
if (control != null && !control.isDisposed()) {
control.setMenu(contextMenu);
}
if (control instanceof Composite) {
Control[] children = ((Composite) control).getChildren();
for (Control child : children) {
setRecursionContextMenu(child, contextMenu);
}
}
}
private Menu getContextMenu(Control control, final ITopic topic) {
MenuManager menuManager = new MenuManager();
menuManager.add(new Action(MindMapMessages.Notes_Edit_text) {
@Override
public void run() {
MindMapUtils.reveal(editor, topic);
}
});
menuManager.add(new Action(MindMapMessages.Notes_Delete_text) {
@Override
public void run() {
DeleteNotesAction deleteNotesAction2 = new DeleteNotesAction(
SheetNotesViewer.this);
deleteNotesAction2.setSelection(new StructuredSelection(topic));
if (deleteNotesAction2.isEnabled()) {
deleteNotesAction2.run();
}
}
});
return menuManager.createContextMenu(control.getShell());
}
private void createSeparatorLine(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setBackground(parent.getBackground());
composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
GridLayout layout = new GridLayout(1, false);
layout.marginWidth = 9;
layout.marginLeft = 14;
layout.marginRight = 0;
layout.marginHeight = 0;
composite.setLayout(layout);
Label sep = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
sep.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
sep.setBackground(ColorConstants.black);
}
private void createTopicLabel(Composite parent, ITopic topic) {
Composite c = new Composite(parent, SWT.NONE);
c.setBackground(parent.getBackground());
c.setLayoutData(new GridData(GridData.FILL_BOTH));
GridLayout layout = new GridLayout(2, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.horizontalSpacing = 3;
c.setLayout(layout);
Label imageLabel = new Label(c, SWT.LEFT);
GridData data1 = new GridData(SWT.LEFT, SWT.CENTER, false, false);
imageLabel.setLayoutData(data1);
imageLabel.setBackground(c.getBackground());
Image image = (Image) resources
.get(MindMapUI.getImages().getTopicIcon(topic, true));
imageLabel.setImage(image);
Label label = new Label(c, SWT.LEFT | SWT.HORIZONTAL);
label.setBackground(parent.getBackground());
label.setForeground(ColorUtils.getColor("#515151")); //$NON-NLS-1$
GridData data = new GridData(GridData.FILL_BOTH);
data.horizontalIndent = 0;
label.setLayoutData(data);
label.setFont(FontUtils.getBold(
FontUtils.getRelativeHeight(JFaceResources.DEFAULT_FONT, 1)));
label.setText(TextFormatter.removeNewLineCharacter(topic.getTitleText())
+ ":"); //$NON-NLS-1$s
}
private void createSingleNotes(Composite parent, ITopic topic) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setBackground(parent.getBackground());
GridData data = new GridData(GridData.FILL_BOTH);
data.horizontalIndent = 18;
composite.setLayoutData(data);
GridLayout layout = new GridLayout(1, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
composite.setLayout(layout);
Control text = createText(composite, topic);
final Composite composite2 = parent;
textComposites.add(composite2);
map.put(composite2, topic);
textControls.add(text);
final MouseListener mouseListener = new MouseAdapter() {
public void mouseDown(MouseEvent e) {
if (composite2 == null || composite2.isDisposed()) {
return;
}
if (!composite2.isFocusControl()) {
composite2.forceFocus();
}
}
public void mouseDoubleClick(MouseEvent e) {
reveal(composite2);
}
};
final KeyListener keyListener = new KeyListener() {
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN) {
moveDown();
} else if (e.keyCode == SWT.ARROW_UP) {
moveUp();
} else if (e.keyCode == SWT.CR) {
reveal(currentTextComposite);
} else if (e.keyCode == SWT.DEL) {
deleteNotesAction.run();
}
}
public void keyReleased(KeyEvent e) {
}
};
FocusListener focusListener = new FocusListener() {
public void focusLost(FocusEvent e) {
setRecursiveBackgroundColor(composite2, originalColor, null);
composite2.removeKeyListener(keyListener);
setCurrentTextComposite(null);
}
public void focusGained(FocusEvent e) {
setRecursiveBackgroundColor(composite2, selectColor, null);
composite2.addKeyListener(keyListener);
sc.showControl(composite2);
setCurrentTextComposite(composite2);
}
};
addMosuseListener(composite2, mouseListener);
composite2.addFocusListener(focusListener);
}
private void setRecursiveBackgroundColor(Control control, Color background,
Control excludeControl) {
if (control == excludeControl) {
return;
}
control.setBackground(background);
if (control instanceof Composite) {
Control[] children = ((Composite) control).getChildren();
for (Control child : children) {
setRecursiveBackgroundColor(child, background, excludeControl);
}
}
}
private void setCurrentTextComposite(Composite composite) {
if (composite != currentTextComposite) {
currentTextComposite = composite;
updateDeleteAction();
}
}
private void updateDeleteAction() {
ITopic topic = getCurrentTopic(currentTextComposite);
deleteNotesAction.setSelection(
topic == null ? null : new StructuredSelection(topic));
}
private StyledText createText(Composite parent, ITopic topic) {
StyledText text = new StyledText(parent,
SWT.MULTI | SWT.WRAP | SWT.READ_ONLY);
text.setBackground(parent.getBackground());
text.setLayoutData(new GridData(GridData.FILL_BOTH));
text.setEnabled(false);
text.setFont(
FontUtils.getRelativeHeight(JFaceResources.DEFAULT_FONT, 0));
RichDocumentNotesAdapter adapter = new RichDocumentNotesAdapter(topic);
// text.setText(truncate(adapter.getDocument().get()));
text.setText(adapter.getDocument().get());
text.setForeground(ColorUtils.getColor("#9a9a9a")); //$NON-NLS-1$
// text.setEnabled(false);
return text;
}
// private String truncate(String text) {
// return text == null ? null
// : (text.length() <= TRUNCATE_LENGTH ? text
// : text.substring(0, TRUNCATE_LENGTH) + "..."); //$NON-NLS-1$
// }
private void reveal(Composite composite) {
MindMapUtils.reveal(editor, getCurrentTopic(composite));
SafeRunner.run(new SafeRunnable() {
@Override
public void run() throws Exception {
E4Utils.showPart(IModelConstants.COMMAND_SHOW_MODEL_PART,
PlatformUI.getWorkbench().getActiveWorkbenchWindow(),
IModelConstants.PART_ID_NOTES, null,
IModelConstants.PART_STACK_ID_RIGHT);
}
});
}
private void addMosuseListener(Control c, MouseListener ml) {
c.addMouseListener(ml);
if (c instanceof Composite) {
for (final Control cc : ((Composite) c).getChildren()) {
addMosuseListener(cc, ml);
}
}
}
private void moveDown() {
if (currentTextComposite == null) {
return;
}
int index = textComposites.indexOf(currentTextComposite);
if (index < 0 || index >= textComposites.size() - 1) {
return;
}
textComposites.get(index + 1).forceFocus();
}
private void moveUp() {
if (currentTextComposite == null) {
return;
}
int index = textComposites.indexOf(currentTextComposite);
if (index <= 0 || index > textComposites.size() - 1) {
return;
}
textComposites.get(index - 1).forceFocus();
}
private ITopic getCurrentTopic(Composite currentComposite) {
if (map == null || map.size() == 0) {
return null;
}
return map.get(currentComposite);
}
public void setInput(Object input) {
if (input instanceof ISheet || input instanceof IBoundary
|| input instanceof IRelationship) {
ISheet sheet = null;
if (input instanceof ISheet) {
if (this.sheet == input)
return;
sheet = (ISheet) input;
} else if (input instanceof IBoundary) {
if (this.boundary == input)
return;
unhookBoundary();
this.boundary = (IBoundary) input;
sheet = boundary.getOwnedSheet();
hookBoundary();
} else if (input instanceof IRelationship) {
if (this.relationship == input)
return;
unhookRelationship();
this.relationship = (IRelationship) input;
sheet = relationship.getOwnedSheet();
hookRelationship();
}
if (sheet != this.sheet) {
unhookSheet();
unhookTitle();
this.sheet = sheet;
hookTitle();
hookSheet();
update();
}
}
}
private void update() {
if (contentComposite.isDisposed()) {
return;
}
resetCollections();
contentComposite.setRedraw(false);
Control[] children = contentComposite.getChildren();
for (Control child : children) {
child.dispose();
}
showAllNotes(contentComposite);
contentComposite.pack();
contentComposite.layout(true, true);
contentComposite.setRedraw(true);
}
private void hookSheet() {
if (notesEventRegister == null)
notesEventRegister = new CoreEventRegister(
sheet.getAdapter(ICoreEventSupport.class), this);
notesEventRegister.register(Core.TopicNotes);
}
private void unhookSheet() {
if (notesEventRegister != null) {
notesEventRegister.unregisterAll();
notesEventRegister = null;
}
}
private void hookBoundary() {
if (notesEventRegister == null)
notesEventRegister = new CoreEventRegister(
boundary.getAdapter(ICoreEventSupport.class), this);
notesEventRegister.register(Core.TopicNotes);
}
private void unhookBoundary() {
if (notesEventRegister != null) {
notesEventRegister.unregisterAll();
notesEventRegister = null;
}
}
private void hookRelationship() {
if (notesEventRegister == null)
notesEventRegister = new CoreEventRegister(
relationship.getAdapter(ICoreEventSupport.class), this);
notesEventRegister.register(Core.TopicNotes);
}
private void unhookRelationship() {
if (notesEventRegister != null) {
notesEventRegister.unregisterAll();
notesEventRegister = null;
}
}
private void hookTitle() {
if (titleEventRegister == null)
titleEventRegister = new CoreEventRegister(
sheet.getAdapter(ICoreEventSupport.class), this);
titleEventRegister.register(Core.TitleText);
}
private void unhookTitle() {
if (titleEventRegister != null) {
titleEventRegister.unregisterAll();
titleEventRegister = null;
}
}
public void handleCoreEvent(final CoreEvent event) {
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
public void run() {
String eventType = event.getType();
if (Core.TopicNotes.equals(eventType)
|| Core.TitleText.endsWith(eventType)) {
update();
}
}
});
}
public void dispose() {
unhookSheet();
unhookTitle();
composite.dispose();
}
public void setEditor(IGraphicalEditor editor) {
this.editor = editor;
}
public IGraphicalEditor getEditor() {
return editor;
}
public Control getControl() {
return composite;
}
}