Skip to main content

This CGIT instance is deprecated, and repositories have been moved to Gitlab or Github. See the repository descriptions for specific locations.

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2022-02-01 13:53:59 +0000
committerEike Stepper2022-02-02 07:47:11 +0000
commit7ab597b9d57d2e4bfbb886a9500a3047e7513689 (patch)
tree779c46f4892e27df218e2c20703d515378a74f40 /plugins/org.eclipse.emf.cdo.ui/src/org
parentc23959f69d7c13f97c9a2100dba5f75f992c207d (diff)
downloadcdo-7ab597b9d57d2e4bfbb886a9500a3047e7513689.tar.gz
cdo-7ab597b9d57d2e4bfbb886a9500a3047e7513689.tar.xz
cdo-7ab597b9d57d2e4bfbb886a9500a3047e7513689.zip
[578512] [UI] Provide an exemplary view for CDORemoteTopics
https://bugs.eclipse.org/bugs/show_bug.cgi?id=578512
Diffstat (limited to 'plugins/org.eclipse.emf.cdo.ui/src/org')
-rw-r--r--plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/bundle/OM.java15
-rw-r--r--plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java103
-rw-r--r--plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/CDORemoteTopicsView.java758
-rw-r--r--plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/UserInfo.java539
-rw-r--r--plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOEventHandler.java16
-rw-r--r--plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOTopicProvider.java134
6 files changed, 1562 insertions, 3 deletions
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/bundle/OM.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/bundle/OM.java
index 57256e95e3..6e841ac14e 100644
--- a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/bundle/OM.java
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/bundle/OM.java
@@ -11,6 +11,7 @@
*/
package org.eclipse.emf.cdo.internal.ui.bundle;
+import org.eclipse.emf.cdo.internal.ui.views.UserInfo;
import org.eclipse.emf.cdo.ui.CDOEditorOpener;
import org.eclipse.emf.cdo.ui.CDOLabelDecorator;
import org.eclipse.emf.cdo.ui.OverlayImage;
@@ -72,6 +73,18 @@ public abstract class OM
public static final OMPreference<Long> PREF_LOCK_TIMEOUT = //
PREFS.init("PREF_LOCK_TIMEOUT", 10000L); //$NON-NLS-1$
+ public static final OMPreference<Boolean> PREF_TOPICS_LINK_WITH_EDITOR = //
+ PREFS.init("PREF_TOPICS_LINK_WITH_EDITOR", false); //$NON-NLS-1$
+
+ public static final OMPreference<String> PREF_USER_FIRST_NAME = //
+ PREFS.init("PREF_USER_FIRST_NAME", ""); //$NON-NLS-1$
+
+ public static final OMPreference<String> PREF_USER_LAST_NAME = //
+ PREFS.init("PREF_USER_LAST_NAME", ""); //$NON-NLS-1$
+
+ public static final OMPreference<String> PREF_USER_DISPLAY_NAME = //
+ PREFS.init("PREF_USER_DISPLAY_NAME", System.getProperty("user.name")); //$NON-NLS-1$
+
private static Boolean historySupportAvailable;
private static Boolean compareSupportAvailable;
@@ -148,11 +161,13 @@ public abstract class OM
protected void doStart() throws Exception
{
CDOEditorOpener.Registry.INSTANCE.activate();
+ UserInfo.Manager.INSTANCE.activate();
}
@Override
protected void doStop() throws Exception
{
+ UserInfo.Manager.INSTANCE.deactivate();
CDOEditorOpener.Registry.INSTANCE.deactivate();
}
}
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java
index 2519dd2a29..f5950fce67 100644
--- a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java
@@ -14,12 +14,15 @@ package org.eclipse.emf.cdo.internal.ui.editor;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDOState;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.model.CDOPackageInfo;
import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.internal.ui.CDOContentProvider;
import org.eclipse.emf.cdo.internal.ui.RunnableViewerRefresh;
@@ -37,6 +40,7 @@ import org.eclipse.emf.cdo.ui.CDOEditorInput3;
import org.eclipse.emf.cdo.ui.CDOEventHandler;
import org.eclipse.emf.cdo.ui.CDOInvalidRootAgent;
import org.eclipse.emf.cdo.ui.CDOLabelProvider;
+import org.eclipse.emf.cdo.ui.CDOTopicProvider;
import org.eclipse.emf.cdo.ui.CDOTreeExpansionAgent;
import org.eclipse.emf.cdo.ui.shared.SharedIcons;
import org.eclipse.emf.cdo.util.CDOURIUtil;
@@ -50,6 +54,7 @@ import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
import org.eclipse.net4j.util.AdapterUtil;
import org.eclipse.net4j.util.ReflectUtil;
import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.collection.ConcurrentArray;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.OMPlatform;
import org.eclipse.net4j.util.om.trace.ContextTracer;
@@ -193,13 +198,14 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Eike Stepper
- * @generated
+ * @generated not
*/
-public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProvider, ISelectionProvider, IMenuListener, IViewerProvider
+public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProvider, ISelectionProvider, IMenuListener, IViewerProvider, CDOTopicProvider
{
/**
* The filters for file extensions supported by the editor.
@@ -529,6 +535,17 @@ public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProv
protected final AtomicBoolean pagesCreated = new AtomicBoolean();
+ protected final ConcurrentArray<Listener> topicListeners = new ConcurrentArray<>()
+ {
+ @Override
+ protected Listener[] newArray(int length)
+ {
+ return new Listener[length];
+ }
+ };
+
+ protected Topic currentTopic;
+
/**
* Handles activation of the editor or it's associated views.
* <!-- begin-user-doc --> <!-- end-user-doc -->
@@ -1381,6 +1398,12 @@ public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProv
eventHandler = new CDOEventHandler(view, selectionViewer)
{
@Override
+ protected void viewTargetChanged(CDOBranchPoint branchPoint)
+ {
+ setCurrentTopic(createTopic());
+ }
+
+ @Override
protected void objectInvalidated(InternalCDOObject cdoObject)
{
if (CDOUtil.isLegacyObject(cdoObject))
@@ -1467,6 +1490,8 @@ public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProv
fireDirtyPropertyChange();
}
};
+
+ setCurrentTopic(createTopic());
}
catch (RuntimeException ex)
{
@@ -2261,6 +2286,80 @@ public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProv
setInputWithNotify(editorInput);
}
+ private void setCurrentTopic(Topic topic)
+ {
+ if (!Objects.equals(currentTopic, topic))
+ {
+ if (currentTopic != null)
+ {
+ for (Listener listener : topicListeners.get())
+ {
+ listener.topicRemoved(this, currentTopic);
+ }
+ }
+
+ currentTopic = topic;
+
+ if (currentTopic != null)
+ {
+ for (Listener listener : topicListeners.get())
+ {
+ listener.topicAdded(this, currentTopic);
+ }
+ }
+ }
+ }
+
+ private Topic createTopic()
+ {
+ if (view != null)
+ {
+ ResourceSet resourceSet = view.getResourceSet();
+ Resource resource = resourceSet.getResources().get(0);
+
+ if (resource instanceof CDOResource)
+ {
+ CDOResource cdoResource = (CDOResource)resource;
+ String path = cdoResource.getPath();
+
+ CDOBranch branch = view.getBranch();
+ long timeStamp = view.getTimeStamp();
+
+ String id = "org.eclipse.emf.cdo.resource" + path + "/" + branch.getID() + "/" + timeStamp;
+ Image image = getTitleImage();
+ String text = path + " [" + branch.getName() + "/" + CDOCommonUtil.formatTimeStamp(timeStamp) + "]";
+ String description = text;
+
+ return new Topic(view.getSession(), id, image, text, description);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Topic[] getTopics()
+ {
+ if (currentTopic != null)
+ {
+ return new Topic[] { currentTopic };
+ }
+
+ return new Topic[0];
+ }
+
+ @Override
+ public void addTopicListener(Listener listener)
+ {
+ topicListeners.add(listener);
+ }
+
+ @Override
+ public void removeTopicListener(Listener listener)
+ {
+ topicListeners.remove(listener);
+ }
+
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/CDORemoteTopicsView.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/CDORemoteTopicsView.java
new file mode 100644
index 0000000000..2161b119de
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/CDORemoteTopicsView.java
@@ -0,0 +1,758 @@
+/*
+ * Copyright (c) 2007-2013, 2015, 2016, 2019, 2020 Eike Stepper (Loehne, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.internal.ui.views;
+
+import org.eclipse.emf.cdo.internal.ui.bundle.OM;
+import org.eclipse.emf.cdo.internal.ui.views.UserInfo.Manager.UserChangedEvent;
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionManager;
+import org.eclipse.emf.cdo.session.remote.CDORemoteTopic;
+import org.eclipse.emf.cdo.ui.CDOTopicProvider;
+import org.eclipse.emf.cdo.ui.CDOTopicProvider.Listener;
+import org.eclipse.emf.cdo.ui.CDOTopicProvider.Topic;
+import org.eclipse.emf.cdo.ui.shared.SharedIcons;
+
+import org.eclipse.net4j.ui.shared.CollapseAllAction;
+import org.eclipse.net4j.ui.shared.ExpandAllAction;
+import org.eclipse.net4j.ui.shared.LinkWithEditorAction;
+import org.eclipse.net4j.util.AdapterUtil;
+import org.eclipse.net4j.util.container.IContainerEvent;
+import org.eclipse.net4j.util.container.IContainerEventVisitor;
+import org.eclipse.net4j.util.event.EventUtil;
+import org.eclipse.net4j.util.event.IEvent;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.ILifecycleEvent;
+import org.eclipse.net4j.util.lifecycle.ILifecycleEvent.Kind;
+import org.eclipse.net4j.util.om.OMPlatform;
+import org.eclipse.net4j.util.ui.GlobalPartAdapter;
+import org.eclipse.net4j.util.ui.UIUtil;
+import org.eclipse.net4j.util.ui.views.ContainerItemProvider;
+
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.provider.ItemProvider;
+import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
+import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.InputDialog;
+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.ITreeSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ISetSelectionTarget;
+import org.eclipse.ui.part.ViewPart;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author Eike Stepper
+ */
+public class CDORemoteTopicsView extends ViewPart implements ISelectionProvider, ISetSelectionTarget
+{
+ public static final String ID = "org.eclipse.emf.cdo.ui.CDORemoteTopicsView"; //$NON-NLS-1$
+
+ private static final boolean LOCAL_USER_INFO_HIDE = //
+ OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.ui.CDORemoteTopicsView.LOCAL_USER_INFO_HIDE"); //$NON-NLS-1$
+
+ private static final boolean LOCAL_USER_INFO_DISABLE = //
+ OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.ui.CDORemoteTopicsView.LOCAL_USER_INFO_DISABLE"); //$NON-NLS-1$
+
+ private static final boolean EXPAND_SELECTION = //
+ OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.ui.CDORemoteTopicsView.EXPAND_SELECTION"); //$NON-NLS-1$
+
+ private AutoCloseable userInfoListener;
+
+ private final Map<Topic, CDORemoteTopic> remoteTopics = new HashMap<>();
+
+ private final Map<CDORemoteTopic, RemoteTopicItem> remoteTopicItems = new HashMap<>();
+
+ private GlobalPartAdapter workbenchListener = new GlobalPartAdapter(false)
+ {
+ @Override
+ public void partOpened(IWorkbenchPartReference partRef)
+ {
+ CDOTopicProvider provider = getTopicProvider(partRef);
+ if (provider != null)
+ {
+ addTopics(provider.getTopics());
+ provider.addTopicListener(topicListener);
+ }
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPartReference partRef)
+ {
+ CDOTopicProvider provider = getTopicProvider(partRef);
+ if (provider != null)
+ {
+ provider.removeTopicListener(topicListener);
+ removeTopics(provider.getTopics());
+ }
+ }
+
+ @Override
+ public void partActivated(IWorkbenchPartReference partRef)
+ {
+ if (linkWithEditor)
+ {
+ CDOTopicProvider provider = getTopicProvider(partRef);
+ if (provider != null)
+ {
+ selectTopics(provider.getTopics());
+ }
+ }
+ }
+ };
+
+ private final Listener topicListener = new Listener()
+ {
+ @Override
+ public void topicAdded(CDOTopicProvider provider, Topic topic)
+ {
+ addTopics(topic);
+ }
+
+ @Override
+ public void topicRemoved(CDOTopicProvider provider, Topic topic)
+ {
+ removeTopics(topic);
+ }
+ };
+
+ private final AdapterFactory adapterFactory = new ComposedAdapterFactory();
+
+ private final ItemProvider root = new ItemProvider(adapterFactory);
+
+ private Shell shell;
+
+ private TreeViewer viewer;
+
+ private boolean linkWithEditor = OM.PREF_TOPICS_LINK_WITH_EDITOR.getValue();
+
+ private boolean inEditorActivation;
+
+ private ISelectionChangedListener selectionListener = event -> {
+ if (!inEditorActivation)
+ {
+ ITreeSelection selection = (ITreeSelection)event.getSelection();
+ IActionBars bars = getViewSite().getActionBars();
+ selectionChanged(bars, selection);
+ }
+ };
+
+ public CDORemoteTopicsView()
+ {
+ }
+
+ public Shell getShell()
+ {
+ return shell;
+ }
+
+ public TreeViewer getViewer()
+ {
+ return viewer;
+ }
+
+ @Override
+ public void setFocus()
+ {
+ viewer.getControl().setFocus();
+ }
+
+ @Override
+ public void dispose()
+ {
+ workbenchListener.dispose();
+
+ try
+ {
+ userInfoListener.close();
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+
+ super.dispose();
+ }
+
+ @Override
+ public ISelection getSelection()
+ {
+ if (viewer != null)
+ {
+ return viewer.getSelection();
+ }
+
+ return StructuredSelection.EMPTY;
+ }
+
+ @Override
+ public void setSelection(ISelection selection)
+ {
+ if (viewer != null)
+ {
+ viewer.setSelection(selection);
+ }
+ }
+
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener)
+ {
+ if (viewer != null)
+ {
+ viewer.addSelectionChangedListener(listener);
+ }
+ }
+
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener)
+ {
+ if (viewer != null)
+ {
+ viewer.removeSelectionChangedListener(listener);
+ }
+ }
+
+ @Override
+ public void selectReveal(ISelection selection)
+ {
+ if (viewer != null)
+ {
+ viewer.setSelection(selection, true);
+ }
+ }
+
+ @Override
+ public final void createPartControl(Composite parent)
+ {
+ shell = parent.getShell();
+ Composite composite = UIUtil.createGridComposite(parent, 1);
+
+ Control control = createUI(composite);
+ control.setLayoutData(UIUtil.createGridData());
+
+ hookContextMenu();
+ hookDoubleClick();
+ contributeToActionBars();
+
+ workbenchListener.register(true);
+ getSite().setSelectionProvider(this);
+ }
+
+ protected Control createUI(Composite parent)
+ {
+ AdapterFactoryLabelProvider labelProvider = new AdapterFactoryLabelProvider(adapterFactory);
+ labelProvider.setFireLabelUpdateNotifications(true);
+
+ viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+ viewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
+ viewer.setLabelProvider(labelProvider);
+ viewer.setComparator(new ViewerComparator());
+ viewer.setInput(root);
+ viewer.addSelectionChangedListener(selectionListener);
+
+ userInfoListener = EventUtil.addListener(UserInfo.Manager.INSTANCE, UserChangedEvent.class, e -> {
+ CDORemoteSession remoteSession = e.getRemoteSession();
+ if (remoteSession != null)
+ {
+ for (Object topic : root.getChildren())
+ {
+ for (Object child : ((RemoteTopicItem)topic).getChildren())
+ {
+ RemoteMemberItem member = (RemoteMemberItem)child;
+ if (member.getRemoteSession() == remoteSession)
+ {
+ UIUtil.asyncExec(() -> member.updateText());
+ }
+ }
+ }
+ }
+ });
+
+ return viewer.getControl();
+ }
+
+ protected void hookDoubleClick()
+ {
+ viewer.addDoubleClickListener(e -> doubleClicked(((ITreeSelection)viewer.getSelection()).getFirstElement()));
+ }
+
+ protected void hookContextMenu()
+ {
+ MenuManager manager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ manager.setRemoveAllWhenShown(true);
+ manager.addMenuListener(m -> fillContextMenu(m, (ITreeSelection)viewer.getSelection()));
+
+ Menu menu = manager.createContextMenu(viewer.getControl());
+ viewer.getControl().setMenu(menu);
+ getViewSite().registerContextMenu(manager, viewer);
+ }
+
+ protected void contributeToActionBars()
+ {
+ IActionBars bars = getViewSite().getActionBars();
+ fillLocalPullDown(bars.getMenuManager());
+ fillLocalToolBar(bars.getToolBarManager());
+ }
+
+ protected void fillLocalPullDown(IMenuManager manager)
+ {
+ }
+
+ protected void fillLocalToolBar(IToolBarManager manager)
+ {
+ if (!LOCAL_USER_INFO_HIDE)
+ {
+ UserInfo localUser = UserInfo.Manager.INSTANCE.getLocalUser();
+
+ manager.add(new Action(localUser.getDisplayName(), IAction.AS_PUSH_BUTTON)
+ {
+ {
+ setToolTipText("Change Local Name");
+ setEnabled(!LOCAL_USER_INFO_DISABLE);
+ }
+
+ @Override
+ public void run()
+ {
+ String displayName = localUser.getDisplayName();
+ InputDialog dialog = new InputDialog(getShell(), getToolTipText(), "Local name:", displayName, null);
+ if (dialog.open() == InputDialog.OK)
+ {
+ String newDisplayName = dialog.getValue();
+ UserInfo.Manager.INSTANCE.changeLocalUser(localUser.getFirstName(), localUser.getLastName(), newDisplayName);
+
+ newDisplayName = localUser.getDisplayName(); // Could be different if it was originally empty.
+ if (!Objects.equals(newDisplayName, displayName))
+ {
+ setText(newDisplayName);
+
+ if (manager instanceof ToolBarManager)
+ {
+ ToolBar toolBar = ((ToolBarManager)manager).getControl();
+ toolBar.getParent().pack(true);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ manager.add(new ExpandAllAction(viewer));
+ manager.add(new CollapseAllAction(viewer));
+
+ LinkWithEditorAction linkAction = new LinkWithEditorAction()
+ {
+ @Override
+ protected void linkWithEditorChanged(boolean linkWithEditor)
+ {
+ CDORemoteTopicsView.this.linkWithEditor = linkWithEditor;
+ OM.PREF_TOPICS_LINK_WITH_EDITOR.setValue(linkWithEditor);
+ }
+ };
+
+ linkAction.setChecked(linkWithEditor);
+ manager.add(linkAction);
+ }
+
+ protected void fillContextMenu(IMenuManager manager, ITreeSelection selection)
+ {
+ }
+
+ protected void selectionChanged(IActionBars bars, ITreeSelection selection)
+ {
+ if (linkWithEditor)
+ {
+ Object element = selection.getFirstElement();
+ if (element instanceof RemoteMemberItem)
+ {
+ element = ((RemoteMemberItem)element).getParent();
+ }
+
+ if (element instanceof RemoteTopicItem)
+ {
+ RemoteTopicItem remoteTopicItem = (RemoteTopicItem)element;
+ Topic topic = remoteTopicItem.getTopic();
+
+ IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ if (!activatePart(topic, activePage.getEditorReferences()))
+ {
+ activatePart(topic, activePage.getViewReferences());
+ }
+ }
+ }
+ }
+
+ protected void doubleClicked(Object object)
+ {
+ if (object instanceof ContainerItemProvider.ErrorElement)
+ {
+ try
+ {
+ UIUtil.getActiveWorkbenchPage().showView(UIUtil.ERROR_LOG_ID);
+ }
+ catch (PartInitException ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+ else if (object != null && viewer.isExpandable(object))
+ {
+ if (viewer.getExpandedState(object))
+ {
+ viewer.collapseToLevel(object, TreeViewer.ALL_LEVELS);
+ }
+ else
+ {
+ viewer.expandToLevel(object, 1);
+ }
+ }
+ }
+
+ /**
+ * @since 3.1
+ */
+ protected void refreshPressed()
+ {
+ UIUtil.refreshViewer(viewer);
+ }
+
+ /**
+ * @since 3.3
+ */
+ protected void collapseAllPressed()
+ {
+ getViewer().collapseAll();
+ }
+
+ protected void closeView()
+ {
+ UIUtil.syncExec(getDisplay(), () -> {
+ getSite().getPage().hideView(CDORemoteTopicsView.this);
+ dispose();
+ });
+ }
+
+ protected Display getDisplay()
+ {
+ Display display = viewer.getControl().getDisplay();
+ if (display == null)
+ {
+ display = UIUtil.getDisplay();
+ }
+
+ return display;
+ }
+
+ public Topic getTopic(CDORemoteTopic remoteTopic)
+ {
+ synchronized (remoteTopics)
+ {
+ RemoteTopicItem item = remoteTopicItems.get(remoteTopic);
+ return item == null ? null : item.getTopic();
+ }
+ }
+
+ protected void addTopics(Topic... topics)
+ {
+ synchronized (remoteTopics)
+ {
+ for (Topic topic : topics)
+ {
+ CDORemoteTopic remoteTopic = remoteTopics.computeIfAbsent(topic, k -> {
+ CDOSession session = topic.getSession();
+ String id = topic.getId();
+
+ CDORemoteSessionManager remoteSessionManager = session.getRemoteSessionManager();
+ return remoteSessionManager.subscribeTopic(id);
+ });
+
+ RemoteTopicItem item = remoteTopicItems.computeIfAbsent(remoteTopic, k -> {
+ RemoteTopicItem newItem = new RemoteTopicItem(topic, remoteTopic);
+ root.getChildren().add(newItem);
+ return newItem;
+ });
+
+ item.reference();
+ }
+ }
+ }
+
+ protected void removeTopics(Topic... topics)
+ {
+ synchronized (remoteTopics)
+ {
+ for (Topic topic : topics)
+ {
+ CDORemoteTopic remoteTopic = remoteTopics.get(topic);
+ if (remoteTopic != null)
+ {
+ RemoteTopicItem item = remoteTopicItems.get(remoteTopic);
+ if (item.dereference() == 0)
+ {
+ remoteTopic.unsubscribe();
+
+ remoteTopics.remove(topic);
+ remoteTopicItems.remove(remoteTopic);
+ root.getChildren().remove(item);
+ }
+ }
+ }
+ }
+ }
+
+ protected void selectTopics(Topic... topics)
+ {
+ List<RemoteTopicItem> items = new ArrayList<>();
+
+ synchronized (remoteTopics)
+ {
+ for (Topic topic : topics)
+ {
+ CDORemoteTopic remoteTopic = remoteTopics.get(topic);
+ if (remoteTopic != null)
+ {
+ RemoteTopicItem item = remoteTopicItems.get(remoteTopic);
+ if (item != null)
+ {
+ items.add(item);
+ }
+ }
+ }
+ }
+
+ try
+ {
+ inEditorActivation = true;
+
+ IStructuredSelection selection = new StructuredSelection(items);
+ viewer.setSelection(selection);
+
+ if (EXPAND_SELECTION)
+ {
+ viewer.setExpandedElements(items.toArray());
+ }
+ }
+ finally
+ {
+ inEditorActivation = false;
+ }
+ }
+
+ private static CDOTopicProvider getTopicProvider(IWorkbenchPartReference partRef)
+ {
+ IWorkbenchPart part = partRef.getPart(true);
+ return AdapterUtil.adapt(part, CDOTopicProvider.class);
+ }
+
+ private static boolean activatePart(Topic selectedTopic, IWorkbenchPartReference[] partRefs)
+ {
+ for (IWorkbenchPartReference partRef : partRefs)
+ {
+ CDOTopicProvider topicProvider = getTopicProvider(partRef);
+ if (topicProvider != null)
+ {
+ for (Topic topic : topicProvider.getTopics())
+ {
+ if (Objects.equals(topic, selectedTopic))
+ {
+ partRef.getPage().activate(partRef.getPart(true));
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private class Item extends ItemProvider
+ {
+ protected Item()
+ {
+ super(CDORemoteTopicsView.this.adapterFactory);
+ }
+
+ /**
+ * Work around EMF bug 578508.
+ */
+ @Override
+ public void setText(Object object, String text)
+ {
+ this.text = text;
+
+ fireNotifyChanged(new ItemProviderNotification(Notification.SET, null, text, Notification.NO_INDEX, false, false, true)
+ {
+ @Override
+ public boolean isLabelUpdate()
+ {
+ return isLabelUpdate;
+ }
+ });
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class RemoteTopicItem extends Item implements IListener
+ {
+ private final Topic topic;
+
+ private final CDORemoteTopic remoteTopic;
+
+ private int refCount;
+
+ public RemoteTopicItem(Topic topic, CDORemoteTopic remoteTopic)
+ {
+ this.topic = topic;
+ this.remoteTopic = remoteTopic;
+
+ setImage(topic.getImage());
+ setText(topic.getText());
+
+ List<RemoteMemberItem> children = new ArrayList<>();
+ for (CDORemoteSession remoteSession : remoteTopic.getRemoteSessions())
+ {
+ RemoteMemberItem item = new RemoteMemberItem(remoteSession);
+ children.add(item);
+ }
+
+ getChildren().addAll(children);
+ remoteTopic.addListener(this);
+ }
+
+ public Topic getTopic()
+ {
+ return topic;
+ }
+
+ @Override
+ public void notifyEvent(IEvent event)
+ {
+ if (event instanceof ILifecycleEvent)
+ {
+ ILifecycleEvent e = (ILifecycleEvent)event;
+ if (e.getKind() == Kind.DEACTIVATED)
+ {
+ dispose();
+ }
+ }
+ else if (event instanceof IContainerEvent)
+ {
+ @SuppressWarnings("unchecked")
+ IContainerEvent<CDORemoteSession> e = (IContainerEvent<CDORemoteSession>)event;
+
+ UIUtil.asyncExec(() -> e.accept(new IContainerEventVisitor<>()
+ {
+ @Override
+ public void added(CDORemoteSession remoteSession)
+ {
+ RemoteMemberItem item = new RemoteMemberItem(remoteSession);
+ getChildren().add(item);
+ }
+
+ @Override
+ public void removed(CDORemoteSession remoteSession)
+ {
+ for (Iterator<Object> it = getChildren().iterator(); it.hasNext();)
+ {
+ RemoteMemberItem item = (RemoteMemberItem)it.next();
+ if (item.getRemoteSession() == remoteSession)
+ {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }));
+ }
+ }
+
+ @Override
+ public void dispose()
+ {
+ remoteTopic.removeListener(this);
+ }
+
+ private void reference()
+ {
+ ++refCount;
+ }
+
+ private int dereference()
+ {
+ return --refCount;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class RemoteMemberItem extends Item
+ {
+ private final CDORemoteSession remoteSession;
+
+ public RemoteMemberItem(CDORemoteSession remoteSession)
+ {
+ this.remoteSession = remoteSession;
+
+ setImage(SharedIcons.getImage(SharedIcons.OBJ_PERSON));
+ updateText();
+ }
+
+ public void updateText()
+ {
+ UserInfo userInfo = UserInfo.Manager.INSTANCE.getRemoteUser(remoteSession);
+ setText(userInfo.getDisplayName());
+ }
+
+ public CDORemoteSession getRemoteSession()
+ {
+ return remoteSession;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/UserInfo.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/UserInfo.java
new file mode 100644
index 0000000000..0d39f15f32
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/views/UserInfo.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2021 Eike Stepper (Loehne, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.internal.ui.views;
+
+import org.eclipse.emf.cdo.internal.ui.bundle.OM;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSession;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionRequest;
+import org.eclipse.emf.cdo.session.remote.CDORemoteSessionRequest.GlobalRequestHandler;
+
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.event.Event;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.OMPlatform;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * @author Eike Stepper
+ */
+public final class UserInfo
+{
+ private static final String UNKNOWN = "Unknown";
+
+ private String firstName;
+
+ private String lastName;
+
+ private String displayName;
+
+ UserInfo()
+ {
+ this(null, null, null);
+ }
+
+ public UserInfo(String firstName, String lastName, String displayName)
+ {
+ change(firstName, lastName, displayName);
+ }
+
+ public String getFirstName()
+ {
+ return firstName;
+ }
+
+ public String getLastName()
+ {
+ return lastName;
+ }
+
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "UserInfo[" + displayName + "]";
+ }
+
+ void change(String firstName, String lastName, String displayName)
+ {
+ this.firstName = StringUtil.isEmpty(firstName) ? UNKNOWN : firstName;
+ this.lastName = StringUtil.isEmpty(lastName) ? UNKNOWN : lastName;
+ this.displayName = StringUtil.isEmpty(displayName) ? UNKNOWN : displayName;
+ }
+
+ void change(UserInfo userInfo)
+ {
+ change(userInfo.getFirstName(), userInfo.getLastName(), userInfo.getDisplayName());
+ }
+
+ byte[] serialize()
+ {
+ String string = firstName + StringUtil.NL + lastName + StringUtil.NL + displayName;
+ return string.getBytes(StandardCharsets.UTF_8);
+ }
+
+ void deserialize(byte[] data)
+ {
+ String string = new String(data, StandardCharsets.UTF_8);
+ StringTokenizer tokenizer = new StringTokenizer(string, StringUtil.NL);
+ change(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Manager extends Lifecycle
+ {
+ public static final UserInfo.Manager INSTANCE = new Manager();
+
+ private final Map<CDORemoteSession, UserInfo> remoteUsers = new HashMap<>();
+
+ private UserInfoStorage localUserInfoStorage;
+
+ private UserInfo localUser;
+
+ private GlobalRequestHandler userInfoRequestHandler;
+
+ private GlobalRequestHandler userInfoNotificationHandler;
+
+ private Manager()
+ {
+ }
+
+ public synchronized UserInfo getLocalUser()
+ {
+ return localUser;
+ }
+
+ public void changeLocalUser(String firstName, String lastName, String displayName)
+ {
+ UserInfo userInfo;
+ CDORemoteSession[] remoteSessions;
+
+ synchronized (this)
+ {
+ if (localUser != null)
+ {
+ localUser.change(firstName, lastName, displayName);
+ }
+
+ userInfo = localUser;
+ remoteSessions = remoteUsers.keySet().toArray(new CDORemoteSession[remoteUsers.size()]);
+ }
+
+ if (userInfo != null)
+ {
+ if (localUserInfoStorage instanceof UserInfoStorage.Writable)
+ {
+ UserInfoStorage.Writable writable = (UserInfoStorage.Writable)localUserInfoStorage;
+
+ try
+ {
+ writable.saveUserInfo(userInfo);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+
+ fireEvent(new UserChangedEvent(null, userInfo));
+
+ byte[] data = userInfo.serialize();
+ for (CDORemoteSession remoteSession : remoteSessions)
+ {
+ new CDORemoteSessionRequest(UserInfoNotificationHandler.TYPE, data) //
+ .send(remoteSession);
+ }
+ }
+ }
+
+ public synchronized UserInfo getRemoteUser(CDORemoteSession remoteSession)
+ {
+ return remoteUsers.computeIfAbsent(remoteSession, k -> {
+ UserInfo userInfo = new UserInfo();
+
+ new CDORemoteSessionRequest(UserInfoRequestHandler.TYPE) //
+ .onResponse(data -> changeRemoteUser(remoteSession, data)) //
+ .send(remoteSession);
+
+ return userInfo;
+ });
+ }
+
+ void changeRemoteUser(CDORemoteSession remoteSession, byte[] data)
+ {
+ UserInfo userInfo;
+ synchronized (this)
+ {
+ userInfo = remoteUsers.get(remoteSession);
+ if (userInfo != null)
+ {
+ userInfo.deserialize(data);
+ }
+ }
+
+ if (userInfo != null)
+ {
+ fireEvent(new UserChangedEvent(remoteSession, userInfo));
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ localUserInfoStorage = UserInfoStorage.Factory.get(IPluginContainer.INSTANCE);
+ localUser = localUserInfoStorage == null ? null : localUserInfoStorage.loadUserInfo();
+
+ if (localUser == null)
+ {
+ localUser = new UserInfo();
+ }
+
+ userInfoRequestHandler = new UserInfoRequestHandler();
+ userInfoNotificationHandler = new UserInfoNotificationHandler();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ userInfoNotificationHandler.deactivate();
+ userInfoNotificationHandler = null;
+
+ userInfoRequestHandler.deactivate();
+ userInfoRequestHandler = null;
+
+ localUser = null;
+ super.doDeactivate();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public final class UserChangedEvent extends Event
+ {
+ private static final long serialVersionUID = 1L;
+
+ private final CDORemoteSession remoteSession;
+
+ private final UserInfo userInfo;
+
+ protected UserChangedEvent(CDORemoteSession remoteSession, UserInfo userInfo)
+ {
+ super(Manager.this);
+ this.remoteSession = remoteSession;
+ this.userInfo = userInfo;
+ }
+
+ @Override
+ public UserInfo.Manager getSource()
+ {
+ return (UserInfo.Manager)super.getSource();
+ }
+
+ public CDORemoteSession getRemoteSession()
+ {
+ return remoteSession;
+ }
+
+ public UserInfo getUserInfo()
+ {
+ return userInfo;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class UserInfoRequestHandler extends GlobalRequestHandler
+ {
+ private static final String TYPE = "org.eclipse.emf.cdo.ui.UserInfo";
+
+ public UserInfoRequestHandler()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ protected byte[] createResponse(CDORemoteSession sender, byte[] request)
+ {
+ UserInfo userInfo = INSTANCE.getLocalUser();
+ return userInfo.serialize();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class UserInfoNotificationHandler extends GlobalRequestHandler
+ {
+ private static final String TYPE = "org.eclipse.emf.cdo.ui.UserInfoChanged";
+
+ public UserInfoNotificationHandler()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ protected byte[] createResponse(CDORemoteSession sender, byte[] request)
+ {
+ INSTANCE.changeRemoteUser(sender, request);
+ return null;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public interface UserInfoStorage
+ {
+ public UserInfo loadUserInfo() throws IOException;
+
+ /**
+ * @author Eike Stepper
+ */
+ public interface Writable extends UserInfoStorage
+ {
+ public void saveUserInfo(UserInfo userInfo) throws IOException;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory
+ {
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.ui.userInfoStorages";
+
+ private static final String DEFAULT_TYPE = OMPlatform.INSTANCE.getProperty("org.eclipse.emf.cdo.ui.UserInfoStorageFactory.DEFAULT_TYPE",
+ PreferencesUserInfoStorage.Factory.TYPE);
+
+ private static final String DEFAULT_DESCRIPTION = OMPlatform.INSTANCE.getProperty("org.eclipse.emf.cdo.ui.UserInfoStorageFactory.DEFAULT_DESCRIPTION");
+
+ public Factory(String type)
+ {
+ super(PRODUCT_GROUP, type);
+ }
+
+ @Override
+ public abstract UserInfoStorage create(String description) throws ProductCreationException;
+
+ public static UserInfoStorage get(IManagedContainer container)
+ {
+ return get(container, null);
+ }
+
+ public static UserInfoStorage get(IManagedContainer container, String type)
+ {
+ return get(container, type, null);
+ }
+
+ public static UserInfoStorage get(IManagedContainer container, String type, String description)
+ {
+ if (type == null)
+ {
+ type = DEFAULT_TYPE;
+ }
+
+ if (description == null)
+ {
+ description = DEFAULT_DESCRIPTION;
+ }
+
+ return container.getElementOrNull(PRODUCT_GROUP, type, description);
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class PreferencesUserInfoStorage implements UserInfoStorage.Writable
+ {
+ public PreferencesUserInfoStorage()
+ {
+ }
+
+ @Override
+ public UserInfo loadUserInfo() throws IOException
+ {
+ String firstName = OM.PREF_USER_FIRST_NAME.getValue();
+ String lastName = OM.PREF_USER_LAST_NAME.getValue();
+ String displayName = OM.PREF_USER_DISPLAY_NAME.getValue();
+ return new UserInfo(firstName, lastName, displayName);
+ }
+
+ @Override
+ public void saveUserInfo(UserInfo userInfo) throws IOException
+ {
+ OM.PREF_USER_FIRST_NAME.setValue(userInfo.getFirstName());
+ OM.PREF_USER_LAST_NAME.setValue(userInfo.getLastName());
+ OM.PREF_USER_DISPLAY_NAME.setValue(userInfo.getDisplayName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Factory extends UserInfoStorage.Factory
+ {
+ public static final String TYPE = "preferences";
+
+ public Factory()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ public UserInfoStorage create(String description) throws ProductCreationException
+ {
+ return new PreferencesUserInfoStorage();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class HomeUserInfoStorage implements UserInfoStorage.Writable
+ {
+ private static final String PROP_FIRST_NAME = "firstName";
+
+ private static final String PROP_LAST_NAME = "lastName";
+
+ private static final String PROP_DISPLAY_NAME = "displayName";
+
+ private static final File FILE = new File(OM.BUNDLE.getUserLocation(), "user.properties");
+
+ public HomeUserInfoStorage()
+ {
+ FILE.getParentFile().mkdirs();
+ }
+
+ @Override
+ public UserInfo loadUserInfo() throws IOException
+ {
+ if (FILE.isFile())
+ {
+ FileInputStream in = null;
+
+ try
+ {
+ in = new FileInputStream(FILE);
+
+ Properties properties = new Properties();
+ properties.load(in);
+
+ String firstName = properties.getProperty(PROP_FIRST_NAME);
+ String lastName = properties.getProperty(PROP_LAST_NAME);
+ String displayName = properties.getProperty(PROP_DISPLAY_NAME);
+
+ return new UserInfo(firstName, lastName, displayName);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ }
+ finally
+ {
+ IOUtil.close(in);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void saveUserInfo(UserInfo userInfo) throws IOException
+ {
+ OutputStream out = null;
+
+ try
+ {
+ out = new FileOutputStream(FILE);
+
+ Properties properties = new Properties();
+ properties.setProperty(PROP_FIRST_NAME, userInfo.getFirstName());
+ properties.setProperty(PROP_LAST_NAME, userInfo.getLastName());
+ properties.setProperty(PROP_DISPLAY_NAME, userInfo.getDisplayName());
+ properties.store(out, "Local user information");
+ }
+ catch (IOException ex)
+ {
+ OM.LOG.error(ex);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+
+ public static void saveProperties(File folder, String fileName, Properties properties, String comment)
+ {
+ OutputStream out = null;
+
+ try
+ {
+ folder.mkdirs();
+
+ File file = new File(folder, fileName);
+ out = new FileOutputStream(file);
+
+ properties.store(out, comment);
+ }
+ catch (IOException ex)
+ {
+ OM.LOG.error(ex);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Factory extends UserInfoStorage.Factory
+ {
+ public static final String TYPE = "home";
+
+ public Factory()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ public UserInfoStorage create(String description) throws ProductCreationException
+ {
+ return new HomeUserInfoStorage();
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOEventHandler.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOEventHandler.java
index d997a6ae8c..440c97a433 100644
--- a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOEventHandler.java
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOEventHandler.java
@@ -14,6 +14,7 @@ package org.eclipse.emf.cdo.ui;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDOState;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.internal.ui.ItemsProcessor;
import org.eclipse.emf.cdo.internal.ui.bundle.OM;
import org.eclipse.emf.cdo.session.CDOSession;
@@ -24,6 +25,7 @@ import org.eclipse.emf.cdo.view.CDOObjectHandler;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
import org.eclipse.emf.cdo.view.CDOViewLocksChangedEvent;
+import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent;
import org.eclipse.net4j.util.container.IContainerDelta;
import org.eclipse.net4j.util.container.IContainerEvent;
@@ -88,7 +90,12 @@ public class CDOEventHandler
@Override
public void notifyEvent(IEvent event)
{
- if (event instanceof CDOViewInvalidationEvent)
+ if (event instanceof CDOViewTargetChangedEvent)
+ {
+ CDOViewTargetChangedEvent e = (CDOViewTargetChangedEvent)event;
+ viewTargetChanged(e.getBranchPoint());
+ }
+ else if (event instanceof CDOViewInvalidationEvent)
{
CDOViewInvalidationEvent e = (CDOViewInvalidationEvent)event;
// Remove detached object from selection, could incur into unwanted exceptions
@@ -375,6 +382,13 @@ public class CDOEventHandler
{
}
+ /**
+ * @since 4.12
+ */
+ protected void viewTargetChanged(CDOBranchPoint branchPoint)
+ {
+ }
+
protected void viewClosed()
{
}
diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOTopicProvider.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOTopicProvider.java
new file mode 100644
index 0000000000..3b6d26603a
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/ui/CDOTopicProvider.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2022 Eike Stepper (Loehne, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.ui;
+
+import org.eclipse.emf.cdo.session.CDOSession;
+
+import org.eclipse.net4j.util.StringUtil;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.util.Objects;
+
+/**
+ * @author Eike Stepper
+ * @since 4.12
+ */
+public interface CDOTopicProvider
+{
+ public Topic[] getTopics();
+
+ public void addTopicListener(Listener listener);
+
+ public void removeTopicListener(Listener listener);
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Topic
+ {
+ private final CDOSession session;
+
+ private final String id;
+
+ private final Image image;
+
+ private final String text;
+
+ private final String description;
+
+ public Topic(CDOSession session, String id, Image image, String text, String description)
+ {
+ this.session = session;
+ this.id = id;
+ this.image = image;
+ this.text = text;
+ this.description = description;
+ }
+
+ public CDOSession getSession()
+ {
+ return session;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public Image getImage()
+ {
+ return image;
+ }
+
+ public String getText()
+ {
+ return text;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(session, id);
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+
+ if (obj == null)
+ {
+ return false;
+ }
+
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+
+ Topic other = (Topic)obj;
+ return Objects.equals(session, other.session) && Objects.equals(id, other.id);
+ }
+
+ @Override
+ public String toString()
+ {
+ String string = session.getRepositoryInfo().getName() + "/" + id;
+
+ String userID = session.getUserID();
+ if (!StringUtil.isEmpty(userID))
+ {
+ string = userID + "@" + string;
+ }
+
+ return string;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public interface Listener
+ {
+ public void topicAdded(CDOTopicProvider provider, Topic topic);
+
+ public void topicRemoved(CDOTopicProvider provider, Topic topic);
+ }
+}

Back to the top