1 /*******************************************************************************
2 * Copyright (c) 2007, 2010, 2018 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.ui;
14 import org.eclipse.jface.resource.ImageDescriptor;
15 import org.eclipse.jface.viewers.ISelection;
16 import org.eclipse.swt.widgets.Display;
17 import org.eclipse.swt.widgets.Widget;
18 import org.eclipse.ui.PlatformUI;
19 import org.simantics.DatabaseJob;
20 import org.simantics.Simantics;
21 import org.simantics.db.Resource;
22 import org.simantics.db.Session;
23 import org.simantics.db.common.primitiverequest.Adapter;
24 import org.simantics.db.common.utils.Logger;
25 import org.simantics.db.common.utils.RequestUtil;
26 import org.simantics.db.exception.DatabaseException;
27 import org.simantics.db.management.ISessionContext;
28 import org.simantics.db.management.ISessionContextProvider;
29 import org.simantics.db.management.ISessionContextProviderSource;
30 import org.simantics.project.IProject;
31 import org.simantics.project.ProjectKeys;
32 import org.simantics.utils.datastructures.Arrays;
33 import org.simantics.utils.ui.BundleUtils;
34 import org.simantics.utils.ui.ISelectionUtils;
35 import org.simantics.utils.ui.SWTUtils;
39 public class SimanticsUI {
41 public static final String PLUGIN_ID = "org.simantics.ui";
44 * The maximum amount of time in milliseconds to wait for the execution of a
45 * database request to start when the request is executed synchronously in
46 * the UI thread. The timeout counting starts from the moment the request is
47 * first scheduled into the database {@link Session}. The purpose is to
48 * prevent synchronous UI thread database requests from locking the whole UI
52 * The default value is 20. The default value can be customized at class
53 * load time by setting the system property
54 * <code>simantics.ui.request.start.timeout</code> to the desired value at
59 public static final long UI_THREAD_REQUEST_START_TIMEOUT;
61 * The maximum amount of time in milliseconds to wait for the execution of a
62 * database request to complete when the request is executed synchronously
63 * in the UI thread. The timeout counting starts from the moment the request
64 * execution is scheduled. The purpose is to prevent synchronous UI thread
65 * database requests from locking the whole UI thread up.
68 * The default value is 50. The default value can be customized at class
69 * load time by setting the system property
70 * <code>simantics.ui.request.execution.timeout</code> to the desired value
75 public static final long UI_THREAD_REQUEST_EXECUTION_TIMEOUT;
79 * The default value is 100. The default value can be customized at class
80 * load time by setting the system property
81 * <code>simantics.ui.request.execution.timeout.long</code> to the desired
82 * value at JVM startup.
86 public static final long UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG;
89 UI_THREAD_REQUEST_START_TIMEOUT = parseLongProperty("simantics.ui.request.start.timeout", 500L);
90 UI_THREAD_REQUEST_EXECUTION_TIMEOUT = parseLongProperty("simantics.ui.request.exec.timeout", 50L);
91 UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG = parseLongProperty("simantics.ui.request.exec.timeout.long", 100L);
95 * Information of the currently open database session for the Simantics UI.
96 * Contains just the vital information to connect to the database. Is
97 * <code>null</code> when there is no open database session.
99 private static ISessionContextProviderSource providerSource = null;
102 // * TODO: support different contexts
103 // * @deprecated no replacement
106 // public static void undo() {
108 // PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().undo(
109 // IOperationHistory.GLOBAL_UNDO_CONTEXT, null, null);
110 // } catch (ExecutionException e) {
111 // // TODO Auto-generated catch block
112 // e.printStackTrace();
117 // * TODO: support different contexts
118 // * @deprecated no replacement
121 // public static void redo() {
123 // PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().redo(
124 // IOperationHistory.GLOBAL_UNDO_CONTEXT, null, null);
125 // } catch (ExecutionException e) {
126 // // TODO Auto-generated catch block
127 // e.printStackTrace();
132 * Only for use in application startup code such as the workbench window
133 * advisor. Must be invoked before calling any other methods in this class.
135 * @param manager the ISessionManager to be used by the application
136 * @throw IllegalArgumentException if manager is <code>null</code>
138 public static void setSessionContextProviderSource(ISessionContextProviderSource source) {
140 throw new IllegalArgumentException("null provider source");
141 providerSource = source;
145 * Asserts that the current context provider source has been initialized
146 * before allowing access to it.
148 * @return current context provider source
150 public static ISessionContextProviderSource getProviderSource() {
151 if (providerSource == null)
152 throw new IllegalStateException(
153 "providerSource must be initialized by the application before using SimanticsUI");
154 return providerSource;
158 * Close and remove the current session contexts of the UI. Afterwards
159 * getSessionContext will return <code>null</code>.
161 * Not for client use, only for internal purposes.
163 public static synchronized void closeSessions() {
164 ISessionContextProviderSource source = providerSource;
167 for (ISessionContextProvider p : source.getAll()) {
168 ISessionContext ctx = p.setSessionContext(null);
176 * @return <code>true</code> if the session manager contains the specified
179 public static synchronized boolean isInUse(ISessionContext ctx) {
180 for (ISessionContextProvider p : getProviderSource().getAll()) {
181 if (p.getSessionContext() == ctx)
188 * @param project the project to check
190 * @return <code>true</code> if the session manager contains an
191 * ISessionContext that contains a reference to the specified
192 * project, disregarding the excluded ISessionContexts listed
194 public static synchronized boolean isInUse(IProject project, ISessionContext... excluding) {
195 for (ISessionContextProvider p : getProviderSource().getAll()) {
196 ISessionContext ctx = p.getSessionContext();
198 if (Arrays.indexOf(excluding, ctx) == -1) {
199 if (ctx.getHint(ProjectKeys.KEY_PROJECT) == project)
208 // * Looks if there is an ISessionContextProvider within the Simantics workbench
209 // * that is currently using a ProCore database server at the specified
212 // * @param address the address to look for connections to
213 // * @return <code>null</code> if there is currently no session in use to the
214 // * specified address.
216 // public static synchronized ISessionContext findSessionTo(ServerAddress address) {
217 // if (address == null)
218 // throw new IllegalArgumentException("null address");
219 // for (ISessionContextProvider provider : getProviderSource().getAll()) {
220 // ISessionContext ctx = provider.getSessionContext();
221 // if (ctx != null) {
222 // ServerAddress addr = ctx.getAddress();
223 // if (address.equals(addr))
231 * Returns the session context provider for the specified handle if one
232 * exists. Workbench windows (IWorkbenchWindow) are currently used as
235 * @param handle the handle associated with the requested session context
237 * @return <code>null</code> if there is no session associated to the
240 public static ISessionContextProvider getSessionContextProvider(Object handle) {
241 return getProviderSource().get(handle);
245 * Returns the database session context associated with the currently active
246 * workbench window. This method should be used to retrieve session contexts
247 * only when the client is sure that the correct workbench window has focus.
250 * If the client knows the workbench window it is working with, but it isn't
251 * sure that the correct workbench window has focus, use
252 * {@link #getSessionContext(Object)} instead.
255 * @return the session context associated with the currently active
256 * workbench window or <code>null</code> if the active window has no
260 public static ISessionContext getSessionContext() {
261 return Simantics.getSessionContext();
265 * Returns the database session context associated with the specified
266 * handle. Workbench windows (IWorkbenchWindow) are currently used as
267 * handles. This method should be used to retrieve session contexts in cases
268 * where the workbench window is known, but the thread of execution is such
269 * that the client cannot be certain that the same workbench window has
272 * @return the session context associated with the specified handle
275 public static ISessionContext getSessionContext(Object handle) {
276 return getSessionContextProvider(handle).getSessionContext();
280 * Associates the specified ISessionContext with the currently active
281 * workbench window. To remove an ISessionContext association from the
282 * active workbench window, specify <code>null</code> as ctx.
285 * After invoking this method you should be able to retrieve the same
286 * ISessionContext through {@link #getSessionContext()}, provided that the
287 * same workbench window has focus at that time.
290 * @param ctx the new UI database session context or <code>null</code> to
291 * replace the current UI session with no session.
292 * @return The previous session context if one existed, otherwise
293 * <code>null</code>. If the specified <code>ctx</code> matched the
294 * current session context (<code>null</code> or
295 * <code>non-null</code>), null is also returned and nothing is
298 public static synchronized ISessionContext setSessionContext(ISessionContext ctx) {
299 return Simantics.getSessionContextProvider().setSessionContext(ctx);
303 * Associates the specified ISessionContext with the specified handle
307 * Currently IWorkbenchWindow's are used as handles. This implies
308 * that each workbench window can only have one active ISessionContext bound
309 * to it. After invoking this method with a valid workbench window handle
310 * you should be able to retrieve the same ISessionContext through
311 * {@link #getSessionContext(Object)} with the same workbench window
312 * specified as the handle.
315 * @param handle the handle to associate the specified ISessionContext with.
316 * @param ctx the new UI database session context or <code>null</code> to
317 * replace the current UI session with no session.
318 * @return The previous session context if one existed, otherwise
319 * <code>null</code>. If the specified <code>ctx</code> matched the
320 * current session context (<code>null</code> or
321 * <code>non-null</code>), null is also returned and nothing is
324 public static synchronized ISessionContext setSessionContext(Object handle, ISessionContext ctx) {
325 ISessionContextProvider provider = getProviderSource().get(handle);
326 if (provider != null)
327 return provider.setSessionContext(ctx);
332 * @return the currently open and active project for the specified database
333 * session or <code>null</code> if there is no current project
336 public static IProject peekProject(ISessionContext ctx) {
339 return ctx.getHint(ProjectKeys.KEY_PROJECT);
343 * TODO: refactor this out of here
345 * @param imageFilePath
348 public static ImageDescriptor getImageDescriptor(String imageFilePath) {
349 return BundleUtils.getImageDescriptorFromPlugin(PLUGIN_ID, imageFilePath);
353 * TODO: [Tuukka] I'm really unsure this belongs here.
357 * @param assignableFrom
360 public static <T> T filterSingleSelection(ISelection sel, Class<T> assignableFrom) {
362 T result = ISelectionUtils.filterSingleSelection(sel, assignableFrom);
366 Resource resource = ISelectionUtils.filterSingleSelection(sel, Resource.class);
367 if(resource == null) return null;
370 return Simantics.getSession().syncRequest(new Adapter<T>(resource, assignableFrom));
371 } catch (DatabaseException e) {
372 Logger.defaultLogError(e);
378 public static <T> T filterSingleWorkbenchSelection(Class<T> assignableFrom) {
379 return filterSingleSelection(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection(), assignableFrom);
383 public static void asyncExecSWT(final Widget widget, final Runnable runnable) {
384 SWTUtils.asyncExec(widget, delayedExecSWT(null, widget, runnable));
387 public static void asyncExecSWT(final Display display, final Runnable runnable) {
388 SWTUtils.asyncExec(display, delayedExecSWT(display, null, runnable));
391 private static Runnable delayedExecSWT(final Display display, final Widget widget, final Runnable runnable) {
392 if (display == null && widget == null)
393 throw new IllegalArgumentException("both display and widget are null");
395 return new Runnable() {
398 if (display != null && display.isDisposed())
400 if (widget != null && widget.isDisposed())
402 if (DatabaseJob.inProgress()) {
403 Display d = display != null ? display : widget.getDisplay();
404 d.timerExec(50, this);
412 private static long parseLongProperty(String propertyName, long defaultValue) {
413 String value = System.getProperty(propertyName, null);
415 return value != null ? Long.parseLong(value) : defaultValue;
416 } catch (NumberFormatException e) {
421 public static boolean isLinuxGTK() {
422 String ws = System.getProperty("osgi.ws");
423 return ws != null && "gtk".equals(ws);