]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.ui/src/org/simantics/ui/SimanticsUI.java
Merge "Fix column width issues on HiDPI displays. KeyTiSelection fixes."
[simantics/platform.git] / bundles / org.simantics.ui / src / org / simantics / ui / SimanticsUI.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010, 2018 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.ui;
13
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;
36
37 /**
38  */
39 public class SimanticsUI {
40
41     public static final String                   PLUGIN_ID      = "org.simantics.ui";
42
43     /**
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
49      * thread up.
50      *
51      * <p>
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
55      * JVM startup.
56      * 
57      * @see RequestUtil
58      */
59     public static final long UI_THREAD_REQUEST_START_TIMEOUT;
60     /**
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.
66      *
67      * <p>
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
71      * at JVM startup.
72      * 
73      * @see RequestUtil
74      */
75     public static final long UI_THREAD_REQUEST_EXECUTION_TIMEOUT;
76     /**
77      *
78      * <p>
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.
83      * 
84      * @see RequestUtil
85      */
86     public static final long UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG;
87
88     static {
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);
92     }
93
94     /**
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.
98      */
99     private static ISessionContextProviderSource providerSource = null;
100
101 //    /**
102 //     * TODO: support different contexts
103 //     * @deprecated no replacement
104 //     */
105 //    @Deprecated
106 //    public static void undo() {
107 //        try {
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();
113 //        }
114 //    }
115 //
116 //    /**
117 //     * TODO: support different contexts
118 //     * @deprecated no replacement
119 //     */
120 //    @Deprecated
121 //    public static void redo() {
122 //        try {
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();
128 //        }
129 //    }
130
131     /**
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.
134      * 
135      * @param manager the ISessionManager to be used by the application
136      * @throw IllegalArgumentException if manager is <code>null</code>
137      */
138     public static void setSessionContextProviderSource(ISessionContextProviderSource source) {
139         if (source == null)
140             throw new IllegalArgumentException("null provider source");
141         providerSource = source;
142     }
143
144     /**
145      * Asserts that the current context provider source has been initialized
146      * before allowing access to it.
147      * 
148      * @return current context provider source
149      */
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;
155     }
156
157     /**
158      * Close and remove the current session contexts of the UI. Afterwards
159      * getSessionContext will return <code>null</code>.
160      * 
161      * Not for client use, only for internal purposes.
162      */
163     public static synchronized void closeSessions() {
164         ISessionContextProviderSource source = providerSource;
165         if (source == null)
166             return;
167         for (ISessionContextProvider p : source.getAll()) {
168             ISessionContext ctx = p.setSessionContext(null);
169             if (ctx != null) {
170                 ctx.dispose();
171             }
172         }
173     }
174
175     /**
176      * @return <code>true</code> if the session manager contains the specified
177      *         session context
178      */
179     public static synchronized boolean isInUse(ISessionContext ctx) {
180         for (ISessionContextProvider p : getProviderSource().getAll()) {
181             if (p.getSessionContext() == ctx)
182                 return true;
183         }
184         return false;
185     }
186
187     /**
188      * @param project the project to check
189      * @param excluding
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
193      */
194     public static synchronized boolean isInUse(IProject project, ISessionContext... excluding) {
195         for (ISessionContextProvider p : getProviderSource().getAll()) {
196             ISessionContext ctx = p.getSessionContext();
197             if (ctx != null) {
198                 if (Arrays.indexOf(excluding, ctx) == -1) {
199                     if (ctx.getHint(ProjectKeys.KEY_PROJECT) == project)
200                         return true;
201                 }
202             }
203         }
204         return false;
205     }
206
207 //    /**
208 //     * Looks if there is an ISessionContextProvider within the Simantics workbench
209 //     * that is currently using a ProCore database server at the specified
210 //     * address.
211 //     * 
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.
215 //     */
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))
224 //                    return ctx;
225 //            }
226 //        }
227 //        return null;
228 //    }
229
230     /**
231      * Returns the session context provider of the curretly active workbench
232      * window. This method will always return a valid session context provider.
233      * 
234      * @return a valid ISessionContextProvider
235      */
236     @Deprecated
237     public static ISessionContextProvider getSessionContextProvider() {
238         return Simantics.getSessionContextProvider();
239     }
240
241     /**
242      * Returns the session context provider for the specified handle if one
243      * exists. Workbench windows (IWorkbenchWindow) are currently used as
244      * handles.
245      * 
246      * @param handle the handle associated with the requested session context
247      *        provider
248      * @return <code>null</code> if there is no session associated to the
249      *         specified handle
250      */
251     public static ISessionContextProvider getSessionContextProvider(Object handle) {
252         return getProviderSource().get(handle);
253     }
254
255     /**
256      * Returns the database session context associated with the currently active
257      * workbench window. This method should be used to retrieve session contexts
258      * only when the client is sure that the correct workbench window has focus.
259      * 
260      * <p>
261      * If the client knows the workbench window it is working with, but it isn't
262      * sure that the correct workbench window has focus, use
263      * {@link #getSessionContext(Object)} instead.
264      * </p>
265      * 
266      * @return the session context associated with the currently active
267      *         workbench window or <code>null</code> if the active window has no
268      *         session context
269      */
270     @Deprecated
271     public static ISessionContext getSessionContext() {
272         return Simantics.getSessionContext();
273     }
274
275     /**
276      * Returns the database session context associated with the specified
277      * handle. Workbench windows (IWorkbenchWindow) are currently used as
278      * handles. This method should be used to retrieve session contexts in cases
279      * where the workbench window is known, but the thread of execution is such
280      * that the client cannot be certain that the same workbench window has
281      * focus.
282      * 
283      * @return the session context associated with the specified handle
284      *         (IWorkbenchWindow)
285      */
286     public static ISessionContext getSessionContext(Object handle) {
287         return getSessionContextProvider(handle).getSessionContext();
288     }
289
290     /**
291      * Associates the specified ISessionContext with the currently active
292      * workbench window. To remove an ISessionContext association from the
293      * active workbench window, specify <code>null</code> as ctx.
294      * 
295      * <p>
296      * After invoking this method you should be able to retrieve the same
297      * ISessionContext through {@link #getSessionContext()}, provided that the
298      * same workbench window has focus at that time.
299      * </p>
300      * 
301      * @param ctx the new UI database session context or <code>null</code> to
302      *        replace the current UI session with no session.
303      * @return The previous session context if one existed, otherwise
304      *         <code>null</code>. If the specified <code>ctx</code> matched the
305      *         current session context (<code>null</code> or
306      *         <code>non-null</code>), null is also returned and nothing is
307      *         done.
308      */
309     public static synchronized ISessionContext setSessionContext(ISessionContext ctx) {
310         return getSessionContextProvider().setSessionContext(ctx);
311     }
312
313     /**
314      * Associates the specified ISessionContext with the specified handle
315      * object.
316      * 
317      * <p>
318      * Currently IWorkbenchWindow's are used as handles. This implies
319      * that each workbench window can only have one active ISessionContext bound
320      * to it. After invoking this method with a valid workbench window handle
321      * you should be able to retrieve the same ISessionContext through
322      * {@link #getSessionContext(Object)} with the same workbench window
323      * specified as the handle.
324      * </p>
325      * 
326      * @param handle the handle to associate the specified ISessionContext with.
327      * @param ctx the new UI database session context or <code>null</code> to
328      *        replace the current UI session with no session.
329      * @return The previous session context if one existed, otherwise
330      *         <code>null</code>. If the specified <code>ctx</code> matched the
331      *         current session context (<code>null</code> or
332      *         <code>non-null</code>), null is also returned and nothing is
333      *         done.
334      */
335     public static synchronized ISessionContext setSessionContext(Object handle, ISessionContext ctx) {
336         ISessionContextProvider provider = getProviderSource().get(handle);
337         if (provider != null)
338             return provider.setSessionContext(ctx);
339         return null;
340     }
341
342     /**
343      * Returns the database Session bound to the currently active workbench
344      * window.
345      * 
346      * <p>
347      * This method should only be invoked in cases where it is certain that the
348      * correct workbench window has focus or it is the latest of all workbench
349      * windows to have had focus. Basically any invocation from the SWT UI
350      * thread is safe, since because in those cases the currently active
351      * workbench window is generally known. Instead invocations from any other
352      * thread should be carefully considered. The rule of thumb is that if you
353      * cannot be sure that the correct workbench window has focus, you should
354      * always get a hold of the Session to be used in some other manner.
355      * </p>
356      * 
357      * <p>
358      * The method always returns a non-null Session or produces an
359      * IllegalStateException if a Session was not attainable.
360      * </p>
361      * 
362      * @return the Session bound to the currently active workbench window
363      * @throws IllegalStateException if no Session was available
364      */
365     @Deprecated
366     public static Session getSession() {
367         return Simantics.getSession();
368     }
369         
370     /**
371      * Returns the database Session bound to the currently active workbench
372      * window. Differently from {@link #getSession()}, this method returns
373      * <code>null</code> if there is no current Session available.
374      * 
375      * <p>
376      * This method should only be invoked from the SWT UI thread. Check the
377      * explanations given in {@link #getSession()}. The same applies to this
378      * method also.
379      * </p>
380      * 
381      * @return the Session bound to the currently active workbench window or
382      *         <code>null</code>
383      */
384     @Deprecated
385     public static Session peekSession() {
386         return Simantics.peekSession();
387     }
388
389     /**
390      * @return the currently open and active project as an IProject or
391      *         <code>null</code> if there is no active session or project
392      */
393     @Deprecated
394     public static IProject peekProject() {
395         return Simantics.peekProject();
396     }
397
398     /**
399      * @return the currently open and active project for the specified database
400      *         session or <code>null</code> if there is no current project
401      */
402     @Deprecated
403     public static IProject peekProject(ISessionContext ctx) {
404         if (ctx == null)
405             return null;
406         return ctx.getHint(ProjectKeys.KEY_PROJECT);
407     }
408
409     /**
410      * @return the currently open and active project as an IProject
411      * @throws IllegalStateException if there is no currently active database
412      *         session, which also means there is no active project at the
413      *         moment
414      */
415     @Deprecated
416     public static IProject getProject() {
417         return Simantics.getProject();
418     }
419
420     /**
421      * TODO: refactor this out of here
422      * 
423      * @param imageFilePath
424      * @return
425      */
426     public static ImageDescriptor getImageDescriptor(String imageFilePath) {
427         return BundleUtils.getImageDescriptorFromPlugin(PLUGIN_ID, imageFilePath);
428     }
429
430     /**
431      * TODO: [Tuukka] I'm really unsure this belongs here.
432      * 
433      * @param <T>
434      * @param sel
435      * @param assignableFrom
436      * @return
437      */
438     public static <T> T filterSingleSelection(ISelection sel, Class<T> assignableFrom) {
439
440         T result = ISelectionUtils.filterSingleSelection(sel, assignableFrom);
441         if (result != null)
442             return result;
443
444         Resource resource = ISelectionUtils.filterSingleSelection(sel, Resource.class);
445         if(resource == null) return null;
446         
447         try {
448             return getSession().syncRequest(new Adapter<T>(resource, assignableFrom));
449         } catch (DatabaseException e) {
450             Logger.defaultLogError(e);
451             return null;
452         }
453         
454     }
455     
456     public static <T> T filterSingleWorkbenchSelection(Class<T> assignableFrom) {
457         return filterSingleSelection(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection(), assignableFrom);
458     }
459
460
461     public static void asyncExecSWT(final Widget widget, final Runnable runnable) {
462         SWTUtils.asyncExec(widget, delayedExecSWT(null, widget, runnable));
463     }
464
465     public static void asyncExecSWT(final Display display, final Runnable runnable) {
466         SWTUtils.asyncExec(display, delayedExecSWT(display, null, runnable));
467     }
468
469     private static Runnable delayedExecSWT(final Display display, final Widget widget, final Runnable runnable) {
470         if (display == null && widget == null)
471             throw new IllegalArgumentException("both display and widget are null");
472
473         return new Runnable() {
474             @Override
475             public void run() {
476                 if (display != null && display.isDisposed())
477                     return;
478                 if (widget != null && widget.isDisposed())
479                     return;
480                 if (DatabaseJob.inProgress()) {
481                     Display d = display != null ? display : widget.getDisplay();
482                     d.timerExec(50, this);
483                     return;
484                 }
485                 runnable.run();
486             }
487         };
488     }
489
490     private static long parseLongProperty(String propertyName, long defaultValue) {
491         String value = System.getProperty(propertyName, null);
492         try {
493             return value != null ? Long.parseLong(value) : defaultValue;
494         } catch (NumberFormatException e) {
495             return defaultValue;
496         }
497     }
498     
499     public static boolean isLinuxGTK() {
500         String ws = System.getProperty("osgi.ws");
501         return ws != null && "gtk".equals(ws);
502     }
503
504 }