]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/SimanticsUI.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / SimanticsUI.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.ui;\r
13 \r
14 import org.eclipse.jface.resource.ImageDescriptor;\r
15 import org.eclipse.jface.viewers.ISelection;\r
16 import org.eclipse.swt.widgets.Display;\r
17 import org.eclipse.swt.widgets.Widget;\r
18 import org.eclipse.ui.PlatformUI;\r
19 import org.simantics.DatabaseJob;\r
20 import org.simantics.db.Resource;\r
21 import org.simantics.db.Session;\r
22 import org.simantics.db.common.primitiverequest.Adapter;\r
23 import org.simantics.db.common.utils.Logger;\r
24 import org.simantics.db.common.utils.RequestUtil;\r
25 import org.simantics.db.exception.DatabaseException;\r
26 import org.simantics.db.management.ISessionContext;\r
27 import org.simantics.db.management.ISessionContextProvider;\r
28 import org.simantics.db.management.ISessionContextProviderSource;\r
29 import org.simantics.project.IProject;\r
30 import org.simantics.project.ProjectKeys;\r
31 import org.simantics.utils.datastructures.Arrays;\r
32 import org.simantics.utils.ui.BundleUtils;\r
33 import org.simantics.utils.ui.ISelectionUtils;\r
34 import org.simantics.utils.ui.SWTUtils;\r
35 \r
36 /**\r
37  */\r
38 public class SimanticsUI {\r
39 \r
40     public static final String                   PLUGIN_ID      = "org.simantics.ui";\r
41 \r
42     /**\r
43      * The maximum amount of time in milliseconds to wait for the execution of a\r
44      * database request to start when the request is executed synchronously in\r
45      * the UI thread. The timeout counting starts from the moment the request is\r
46      * first scheduled into the database {@link Session}. The purpose is to\r
47      * prevent synchronous UI thread database requests from locking the whole UI\r
48      * thread up.\r
49      *\r
50      * <p>\r
51      * The default value is 20. The default value can be customized at class\r
52      * load time by setting the system property\r
53      * <code>simantics.ui.request.start.timeout</code> to the desired value at\r
54      * JVM startup.\r
55      * \r
56      * @see RequestUtil\r
57      */\r
58     public static final long UI_THREAD_REQUEST_START_TIMEOUT;\r
59     /**\r
60      * The maximum amount of time in milliseconds to wait for the execution of a\r
61      * database request to complete when the request is executed synchronously\r
62      * in the UI thread. The timeout counting starts from the moment the request\r
63      * execution is scheduled. The purpose is to prevent synchronous UI thread\r
64      * database requests from locking the whole UI thread up.\r
65      *\r
66      * <p>\r
67      * The default value is 50. The default value can be customized at class\r
68      * load time by setting the system property\r
69      * <code>simantics.ui.request.execution.timeout</code> to the desired value\r
70      * at JVM startup.\r
71      * \r
72      * @see RequestUtil\r
73      */\r
74     public static final long UI_THREAD_REQUEST_EXECUTION_TIMEOUT;\r
75     /**\r
76      *\r
77      * <p>\r
78      * The default value is 100. The default value can be customized at class\r
79      * load time by setting the system property\r
80      * <code>simantics.ui.request.execution.timeout.long</code> to the desired\r
81      * value at JVM startup.\r
82      * \r
83      * @see RequestUtil\r
84      */\r
85     public static final long UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG;\r
86 \r
87     static {\r
88         UI_THREAD_REQUEST_START_TIMEOUT = parseLongProperty("simantics.ui.request.start.timeout", 500L);\r
89         UI_THREAD_REQUEST_EXECUTION_TIMEOUT = parseLongProperty("simantics.ui.request.exec.timeout", 50L);\r
90         UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG = parseLongProperty("simantics.ui.request.exec.timeout.long", 100L);\r
91     }\r
92 \r
93     /**\r
94      * Information of the currently open database session for the Simantics UI.\r
95      * Contains just the vital information to connect to the database. Is\r
96      * <code>null</code> when there is no open database session.\r
97      */\r
98     private static ISessionContextProviderSource providerSource = null;\r
99 \r
100 //    /**\r
101 //     * TODO: support different contexts\r
102 //     * @deprecated no replacement\r
103 //     */\r
104 //    @Deprecated\r
105 //    public static void undo() {\r
106 //        try {\r
107 //            PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().undo(\r
108 //                    IOperationHistory.GLOBAL_UNDO_CONTEXT, null, null);\r
109 //        } catch (ExecutionException e) {\r
110 //            // TODO Auto-generated catch block\r
111 //            e.printStackTrace();\r
112 //        }\r
113 //    }\r
114 //\r
115 //    /**\r
116 //     * TODO: support different contexts\r
117 //     * @deprecated no replacement\r
118 //     */\r
119 //    @Deprecated\r
120 //    public static void redo() {\r
121 //        try {\r
122 //            PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().redo(\r
123 //                    IOperationHistory.GLOBAL_UNDO_CONTEXT, null, null);\r
124 //        } catch (ExecutionException e) {\r
125 //            // TODO Auto-generated catch block\r
126 //            e.printStackTrace();\r
127 //        }\r
128 //    }\r
129 \r
130     /**\r
131      * Only for use in application startup code such as the workbench window\r
132      * advisor. Must be invoked before calling any other methods in this class.\r
133      * \r
134      * @param manager the ISessionManager to be used by the application\r
135      * @throw IllegalArgumentException if manager is <code>null</code>\r
136      */\r
137     public static void setSessionContextProviderSource(ISessionContextProviderSource source) {\r
138         if (source == null)\r
139             throw new IllegalArgumentException("null provider source");\r
140         providerSource = source;\r
141     }\r
142 \r
143     /**\r
144      * Asserts that the current context provider source has been initialized\r
145      * before allowing access to it.\r
146      * \r
147      * @return current context provider source\r
148      */\r
149     public static ISessionContextProviderSource getProviderSource() {\r
150         if (providerSource == null)\r
151             throw new IllegalStateException(\r
152             "providerSource must be initialized by the application before using SimanticsUI");\r
153         return providerSource;\r
154     }\r
155 \r
156     /**\r
157      * Close and remove the current session contexts of the UI. Afterwards\r
158      * getSessionContext will return <code>null</code>.\r
159      * \r
160      * Not for client use, only for internal purposes.\r
161      */\r
162     public static synchronized void closeSessions() {\r
163         ISessionContextProviderSource source = providerSource;\r
164         if (source == null)\r
165             return;\r
166         for (ISessionContextProvider p : source.getAll()) {\r
167             ISessionContext ctx = p.setSessionContext(null);\r
168             if (ctx != null) {\r
169                 ctx.dispose();\r
170             }\r
171         }\r
172     }\r
173 \r
174     /**\r
175      * @return <code>true</code> if the session manager contains the specified\r
176      *         session context\r
177      */\r
178     public static synchronized boolean isInUse(ISessionContext ctx) {\r
179         for (ISessionContextProvider p : getProviderSource().getAll()) {\r
180             if (p.getSessionContext() == ctx)\r
181                 return true;\r
182         }\r
183         return false;\r
184     }\r
185 \r
186     /**\r
187      * @param project the project to check\r
188      * @param excluding\r
189      * @return <code>true</code> if the session manager contains an\r
190      *         ISessionContext that contains a reference to the specified\r
191      *         project, disregarding the excluded ISessionContexts listed\r
192      */\r
193     public static synchronized boolean isInUse(IProject project, ISessionContext... excluding) {\r
194         for (ISessionContextProvider p : getProviderSource().getAll()) {\r
195             ISessionContext ctx = p.getSessionContext();\r
196             if (ctx != null) {\r
197                 if (Arrays.indexOf(excluding, ctx) == -1) {\r
198                     if (ctx.getHint(ProjectKeys.KEY_PROJECT) == project)\r
199                         return true;\r
200                 }\r
201             }\r
202         }\r
203         return false;\r
204     }\r
205 \r
206 //    /**\r
207 //     * Looks if there is an ISessionContextProvider within the Simantics workbench\r
208 //     * that is currently using a ProCore database server at the specified\r
209 //     * address.\r
210 //     * \r
211 //     * @param address the address to look for connections to\r
212 //     * @return <code>null</code> if there is currently no session in use to the\r
213 //     *         specified address.\r
214 //     */\r
215 //    public static synchronized ISessionContext findSessionTo(ServerAddress address) {\r
216 //        if (address == null)\r
217 //            throw new IllegalArgumentException("null address");\r
218 //        for (ISessionContextProvider provider : getProviderSource().getAll()) {\r
219 //            ISessionContext ctx = provider.getSessionContext();\r
220 //            if (ctx != null) {\r
221 //                ServerAddress addr = ctx.getAddress();\r
222 //                if (address.equals(addr))\r
223 //                    return ctx;\r
224 //            }\r
225 //        }\r
226 //        return null;\r
227 //    }\r
228 \r
229     /**\r
230      * Returns the session context provider of the curretly active workbench\r
231      * window. This method will always return a valid session context provider.\r
232      * \r
233      * @return a valid ISessionContextProvider\r
234      */\r
235     public static ISessionContextProvider getSessionContextProvider() {\r
236         return getProviderSource().getActive();\r
237     }\r
238 \r
239     /**\r
240      * Returns the session context provider for the specified handle if one\r
241      * exists. Workbench windows (IWorkbenchWindow) are currently used as\r
242      * handles.\r
243      * \r
244      * @param handle the handle associated with the requested session context\r
245      *        provider\r
246      * @return <code>null</code> if there is no session associated to the\r
247      *         specified handle\r
248      */\r
249     public static ISessionContextProvider getSessionContextProvider(Object handle) {\r
250         return getProviderSource().get(handle);\r
251     }\r
252 \r
253     /**\r
254      * Returns the database session context associated with the currently active\r
255      * workbench window. This method should be used to retrieve session contexts\r
256      * only when the client is sure that the correct workbench window has focus.\r
257      * \r
258      * <p>\r
259      * If the client knows the workbench window it is working with, but it isn't\r
260      * sure that the correct workbench window has focus, use\r
261      * {@link #getSessionContext(Object)} instead.\r
262      * </p>\r
263      * \r
264      * @return the session context associated with the currently active\r
265      *         workbench window or <code>null</code> if the active window has no\r
266      *         session context\r
267      */\r
268     public static ISessionContext getSessionContext() {\r
269         ISessionContextProvider provider = getSessionContextProvider();\r
270         return provider != null ? provider.getSessionContext() : null;\r
271     }\r
272 \r
273     /**\r
274      * Returns the database session context associated with the specified\r
275      * handle. Workbench windows (IWorkbenchWindow) are currently used as\r
276      * handles. This method should be used to retrieve session contexts in cases\r
277      * where the workbench window is known, but the thread of execution is such\r
278      * that the client cannot be certain that the same workbench window has\r
279      * focus.\r
280      * \r
281      * @return the session context associated with the specified handle\r
282      *         (IWorkbenchWindow)\r
283      */\r
284     public static ISessionContext getSessionContext(Object handle) {\r
285         return getSessionContextProvider(handle).getSessionContext();\r
286     }\r
287 \r
288     /**\r
289      * Associates the specified ISessionContext with the currently active\r
290      * workbench window. To remove an ISessionContext association from the\r
291      * active workbench window, specify <code>null</code> as ctx.\r
292      * \r
293      * <p>\r
294      * After invoking this method you should be able to retrieve the same\r
295      * ISessionContext through {@link #getSessionContext()}, provided that the\r
296      * same workbench window has focus at that time.\r
297      * </p>\r
298      * \r
299      * @param ctx the new UI database session context or <code>null</code> to\r
300      *        replace the current UI session with no session.\r
301      * @return The previous session context if one existed, otherwise\r
302      *         <code>null</code>. If the specified <code>ctx</code> matched the\r
303      *         current session context (<code>null</code> or\r
304      *         <code>non-null</code>), null is also returned and nothing is\r
305      *         done.\r
306      */\r
307     public static synchronized ISessionContext setSessionContext(ISessionContext ctx) {\r
308         return getSessionContextProvider().setSessionContext(ctx);\r
309     }\r
310 \r
311     /**\r
312      * Associates the specified ISessionContext with the specified handle\r
313      * object.\r
314      * \r
315      * <p>\r
316      * Currently IWorkbenchWindow's are used as handles. This implies\r
317      * that each workbench window can only have one active ISessionContext bound\r
318      * to it. After invoking this method with a valid workbench window handle\r
319      * you should be able to retrieve the same ISessionContext through\r
320      * {@link #getSessionContext(Object)} with the same workbench window\r
321      * specified as the handle.\r
322      * </p>\r
323      * \r
324      * @param handle the handle to associate the specified ISessionContext with.\r
325      * @param ctx the new UI database session context or <code>null</code> to\r
326      *        replace the current UI session with no session.\r
327      * @return The previous session context if one existed, otherwise\r
328      *         <code>null</code>. If the specified <code>ctx</code> matched the\r
329      *         current session context (<code>null</code> or\r
330      *         <code>non-null</code>), null is also returned and nothing is\r
331      *         done.\r
332      */\r
333     public static synchronized ISessionContext setSessionContext(Object handle, ISessionContext ctx) {\r
334         ISessionContextProvider provider = getProviderSource().get(handle);\r
335         if (provider != null)\r
336             return provider.setSessionContext(ctx);\r
337         return null;\r
338     }\r
339 \r
340     /**\r
341      * Returns the database Session bound to the currently active workbench\r
342      * window.\r
343      * \r
344      * <p>\r
345      * This method should only be invoked in cases where it is certain that the\r
346      * correct workbench window has focus or it is the latest of all workbench\r
347      * windows to have had focus. Basically any invocation from the SWT UI\r
348      * thread is safe, since because in those cases the currently active\r
349      * workbench window is generally known. Instead invocations from any other\r
350      * thread should be carefully considered. The rule of thumb is that if you\r
351      * cannot be sure that the correct workbench window has focus, you should\r
352      * always get a hold of the Session to be used in some other manner.\r
353      * </p>\r
354      * \r
355      * <p>\r
356      * The method always returns a non-null Session or produces an\r
357      * IllegalStateException if a Session was not attainable.\r
358      * </p>\r
359      * \r
360      * @return the Session bound to the currently active workbench window\r
361      * @throws IllegalStateException if no Session was available\r
362      */\r
363     public static Session getSession() {\r
364         ISessionContext ctx = getSessionContext();\r
365         if (ctx == null)\r
366             throw new IllegalStateException("Session unavailable, no database session open");\r
367         return ctx.getSession();\r
368     }\r
369         \r
370     /**\r
371      * Returns the database Session bound to the currently active workbench\r
372      * window. Differently from {@link #getSession()}, this method returns\r
373      * <code>null</code> if there is no current Session available.\r
374      * \r
375      * <p>\r
376      * This method should only be invoked from the SWT UI thread. Check the\r
377      * explanations given in {@link #getSession()}. The same applies to this\r
378      * method also.\r
379      * </p>\r
380      * \r
381      * @return the Session bound to the currently active workbench window or\r
382      *         <code>null</code>\r
383      */\r
384     public static Session peekSession() {\r
385         ISessionContext ctx = getSessionContext();\r
386         return ctx == null ? null : ctx.peekSession();\r
387     }\r
388 \r
389     /**\r
390      * @return the currently open and active project as an IProject or\r
391      *         <code>null</code> if there is no active session or project\r
392      */\r
393     public static IProject peekProject() {\r
394         ISessionContext ctx = getSessionContext();\r
395         return ctx == null ? null : (org.simantics.project.IProject) ctx.getHint(ProjectKeys.KEY_PROJECT);\r
396     }\r
397 \r
398     /**\r
399      * @return the currently open and active project for the specified database\r
400      *         session or <code>null</code> if there is no current project\r
401      */\r
402     public static IProject peekProject(ISessionContext ctx) {\r
403         if (ctx == null)\r
404             return null;\r
405         return ctx.getHint(ProjectKeys.KEY_PROJECT);\r
406     }\r
407 \r
408     /**\r
409      * @return the currently open and active project as an IProject\r
410      * @throws IllegalStateException if there is no currently active database\r
411      *         session, which also means there is no active project at the\r
412      *         moment\r
413      */\r
414     public static IProject getProject() {\r
415         ISessionContext ctx = getSessionContext();\r
416         if (ctx == null)\r
417             throw new IllegalStateException("No current database session");\r
418         return ctx.getHint(ProjectKeys.KEY_PROJECT);\r
419     }\r
420 \r
421     /**\r
422      * TODO: refactor this out of here\r
423      * \r
424      * @param imageFilePath\r
425      * @return\r
426      */\r
427     public static ImageDescriptor getImageDescriptor(String imageFilePath) {\r
428         return BundleUtils.getImageDescriptorFromPlugin(PLUGIN_ID, imageFilePath);\r
429     }\r
430 \r
431     /**\r
432      * TODO: [Tuukka] I'm really unsure this belongs here.\r
433      * \r
434      * @param <T>\r
435      * @param sel\r
436      * @param assignableFrom\r
437      * @return\r
438      */\r
439     public static <T> T filterSingleSelection(ISelection sel, Class<T> assignableFrom) {\r
440 \r
441         T result = ISelectionUtils.filterSingleSelection(sel, assignableFrom);\r
442         if (result != null)\r
443             return result;\r
444 \r
445         Resource resource = ISelectionUtils.filterSingleSelection(sel, Resource.class);\r
446         if(resource == null) return null;\r
447         \r
448         try {\r
449             return getSession().syncRequest(new Adapter<T>(resource, assignableFrom));\r
450         } catch (DatabaseException e) {\r
451             Logger.defaultLogError(e);\r
452             return null;\r
453         }\r
454         \r
455     }\r
456     \r
457     public static <T> T filterSingleWorkbenchSelection(Class<T> assignableFrom) {\r
458         return filterSingleSelection(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection(), assignableFrom);\r
459     }\r
460 \r
461 \r
462     public static void asyncExecSWT(final Widget widget, final Runnable runnable) {\r
463         SWTUtils.asyncExec(widget, delayedExecSWT(null, widget, runnable));\r
464     }\r
465 \r
466     public static void asyncExecSWT(final Display display, final Runnable runnable) {\r
467         SWTUtils.asyncExec(display, delayedExecSWT(display, null, runnable));\r
468     }\r
469 \r
470     private static Runnable delayedExecSWT(final Display display, final Widget widget, final Runnable runnable) {\r
471         if (display == null && widget == null)\r
472             throw new IllegalArgumentException("both display and widget are null");\r
473 \r
474         return new Runnable() {\r
475             @Override\r
476             public void run() {\r
477                 if (display != null && display.isDisposed())\r
478                     return;\r
479                 if (widget != null && widget.isDisposed())\r
480                     return;\r
481                 if (DatabaseJob.inProgress()) {\r
482                     Display d = display != null ? display : widget.getDisplay();\r
483                     d.timerExec(50, this);\r
484                     return;\r
485                 }\r
486                 runnable.run();\r
487             }\r
488         };\r
489     }\r
490 \r
491     private static long parseLongProperty(String propertyName, long defaultValue) {\r
492         String value = System.getProperty(propertyName, null);\r
493         try {\r
494             return value != null ? Long.parseLong(value) : defaultValue;\r
495         } catch (NumberFormatException e) {\r
496             return defaultValue;\r
497         }\r
498     }\r
499     \r
500     public static boolean isLinuxGTK() {\r
501         String ws = System.getProperty("osgi.ws");\r
502         return ws != null && "gtk".equals(ws);\r
503     }\r
504 \r
505 }\r