eclipse:eclipse_rcp
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
eclipse:eclipse_rcp [2016/07/15 07:16] – skipidar | eclipse:eclipse_rcp [2023/11/01 07:15] (current) – ↷ Links adapted because of a move operation skipidar | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Eclipse - Rich Client Platform (RCP) ====== | ====== Eclipse - Rich Client Platform (RCP) ====== | ||
+ | |||
+ | ==== Documents ==== | ||
+ | ^What ^Where^ | ||
+ | |1. Introduction | [[http:// | ||
+ | |2. Tutorial | [[http:// | ||
+ | |3. Tutorial | [[http:// | ||
+ | |4. Tutorial | [[http:// | ||
+ | |||
+ | ==== Fallpits ==== | ||
+ | * Die Annotations wie @Inject und Lifecylce-Annotations werden nicht getriggert, solange sie betroffenen Klassen nicht eingebunden werden in der Application.e4xmi | ||
+ | * When defining a keybinding - do not forget to define a context where the binding will worl, as described [[http:// | ||
+ | * When using Declarative Services - the services may only be injected by **Interface** not by Implementation class | ||
+ | * **Annotations** | ||
+ | * There only can by ONE method per lifeCycle annotation: @PostCreate, | ||
+ | * **Inheritance** | ||
+ | * The GUI is now created in // | ||
+ | * Given part classes A,B. B inherits from A. Both implement an @onFocus annotated method " | ||
===== Eclipse RCP common===== | ===== Eclipse RCP common===== | ||
Line 188: | Line 205: | ||
{{http:// | {{http:// | ||
+ | |||
+ | ==== Dependencies Management in Eclipse ==== | ||
+ | |||
+ | Documentation: | ||
+ | |||
+ | ^Location ^Documentation^ | ||
+ | |**feature.xml**|< | ||
+ | |**plugin.xml**|< | ||
+ | [[http:// | ||
+ | [[http:// | ||
+ | </ | ||
+ | |**MANIFEST.MF**|< | ||
+ | |||
+ | |||
+ | Eclipse Dependencies have different Levels: | ||
+ | |||
+ | ^Level ^ Describtion ^ Configuration File^ | ||
+ | |OSGI| The OSGI is a specification, | ||
+ | |Eclipse RCP| Eclipse Introduces Concepts like **Product, Feature, Application, | ||
+ | |||
+ | |||
+ | {{http:// | ||
+ | |||
+ | |OSGI Level dependencies| All OSGI dependencies - are dependencies that are defined in **MANIFEST.MF**' | ||
+ | |Describes Product in Extension Point| The Product.xml is just a configuration files to enable forms to manage icons, and stuff. The framework retrieves the product not from this file, but from an **an associated plugin.xml**. There the product describtion is saved under an **extension point org.eclipse.core.runtime.products** |{{http:// | ||
+ | |Contains any plugin which will be used in Product.| Plugins/ | ||
+ | |Depends on foreign features| Dependencies to features/ | ||
+ | |||| | ||
+ | |||
+ | |||
+ | === Libs dependencies in Project === | ||
+ | If a project requires an external libraries | ||
+ | * create a new **plugin-project** and add those libraries to the subfolder, e.g. " | ||
+ | * add the " | ||
+ | * and **export all packages** which should be visible from outside | ||
+ | |||
+ | This will allow to check in the libs to the version control and make the project completely portable, avoiding problems with unavailable external repos. So it can be checked out even years later and compiled again! | ||
+ | |||
+ | {{http:// | ||
+ | |||
+ | === Storing Libs in a local repository === | ||
+ | A local p2 repository can be set up, by exporting a project. | ||
+ | The local repository can then be references from the product as | ||
+ | < | ||
+ | " | ||
+ | </ | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | |||
+ | Using a p2-repository will allow Maven-Tycho (the headless build plugin) to resolve dependencies automatically. | ||
+ | [[https:// | ||
+ | |||
+ | |||
+ | === Tool === | ||
+ | |||
+ | To analyse the dependencies there is a tool: Plug-in Dependency Visualization | ||
+ | Here is a p2 repository to install the tool which is aplugin: http:// | ||
+ | |||
+ | Open the View "Graph Plug-In Dependency" | ||
+ | |||
+ | {{http:// | ||
==== Dependency resolutions ==== | ==== Dependency resolutions ==== | ||
Line 1114: | Line 1193: | ||
{{http:// | {{http:// | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Download the Eclipse RCP updatesite ==== | ||
+ | |||
+ | To install Eclipse plugins offline you have to download the updatesite. | ||
+ | Here is an example to download instasearch. | ||
+ | |||
+ | < | ||
+ | set eclipse_home=" | ||
+ | set updatesite=http:// | ||
+ | set folder=d: | ||
+ | |||
+ | |||
+ | %eclipse_home% -application org.eclipse.equinox.p2.artifact.repository.mirrorApplication -source %updatesite% -destination %folder% | ||
+ | %eclipse_home% -application org.eclipse.equinox.p2.metadata.repository.mirrorApplication -source %updatesite% -destination %folder% | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Eclipse RCP 4 ===== | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | <sxh java> | ||
+ | //create and open an MPart from class A. Which methods are triggered? | ||
+ | |||
+ | class A{ | ||
+ | | ||
+ | | ||
+ | // is triggered! postConstruct supports inheritance | ||
+ | } | ||
+ | | ||
+ | | ||
+ | // NOT triggered! onFocus in only triggered on the top of the inheritance levels! | ||
+ | } | ||
+ | } | ||
+ | class B extends A{ | ||
+ | | ||
+ | | ||
+ | // is triggered! postConstruct supports inheritance | ||
+ | } | ||
+ | | ||
+ | | ||
+ | // is triggered, because B is the top level inheritance class | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | * Injections will only be processed in classes, which are referenced in Application Model. \\ (see [[http:// | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Target Platform ==== | ||
+ | |||
+ | <fc # | ||
+ | - When adding stuff from the platform \\ <fc # | ||
+ | |||
+ | A custom Target Platform contains all the artifacts, which teh projects depends on. (libs) | ||
+ | |||
+ | == IDE Independent Target Platform == | ||
+ | Just copy the **plugins** dir form your IDE to a libs dir and point the target platform to this direction. | ||
+ | This will protect the project from IDE changes and uncontrolled installation of incompatible plugins. | ||
+ | |||
+ | {{http:// | ||
+ | |||
+ | == Sources of packages, bundles and libs == | ||
+ | |||
+ | All p2 update sites are listed here: https:// | ||
+ | |||
+ | |Site|Feature|Bundles| | ||
+ | |< | ||
+ | http:// | ||
+ | The swt packages are located inside the Eclipse RCP Feature. | ||
+ | </ | ||
+ | * org.eclipse.swt | ||
+ | * org.eclipse.jface | ||
+ | </ | ||
+ | |E4 Downloads - http:// | ||
+ | * E4 CSS Spy | ||
+ | * E4 Event Spy | ||
+ | * Eclipse E4 Tools | ||
+ | </ | ||
+ | |Nattable 1.3.0 - http:// | ||
+ | * NatTable Core | ||
+ | </ | ||
+ | |< | ||
+ | http:// | ||
+ | The 3rd party libs a located inside the ORBIT Project' | ||
+ | [[http:// | ||
+ | </ | ||
+ | * org.apache.commons.logging | ||
+ | </ | ||
+ | |< | ||
+ | http:// | ||
+ | </ | ||
+ | * org.eclipse.e4.emf.xpath | ||
+ | </ | ||
+ | |< | ||
+ | http:// | ||
+ | </ | ||
+ | * org.eclipse.emf.ecore | ||
+ | </ | ||
+ | |< | ||
+ | http:// | ||
+ | </ | ||
+ | * org.eclipse.ui.forms | ||
+ | </ | ||
+ | == Portability | ||
+ | To keep the libs locally in a project and to have it portable between different workstations | ||
+ | (e.g. by keeping projects in dropbox) | ||
+ | |||
+ | The **Eclipse Projects** are usually kept **on dropbox**, but the **workspace** is located on the local machine, because it uses **absolute paths**. | ||
+ | So the problem is, set the path to the libs workspace independent. | ||
+ | |||
+ | This can be achieved by using eclipse variables. | ||
+ | But there is no special variable which always points to the targetPlatform location. (**target_home** var points to the first entry in target_platform) | ||
+ | |||
+ | The solution is to import the **target_platform project** into the workspace and use the variable, | ||
+ | which contains an absolute path to the target_platform location. | ||
+ | From there you can locate the subdirectories with libs. | ||
+ | |||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | {{http:// | ||
+ | {{http:// | ||
+ | |||
+ | The Project **de.vogella.e4.rcp.target** is imported, and so it may be used to locate libs, which are located in subdirectories | ||
+ | |||
+ | == Example Setup == | ||
+ | Here is a setp of a target platform, which contains all the necessary plugins / features to run a product. | ||
+ | |||
+ | {{http:// | ||
+ | ==== Tools ==== | ||
+ | ^Tool ^Where ^ Screenshot ^ | ||
+ | | **Eclipse e4 Tools** | ||
+ | http:// | ||
+ | |**CssSpy** - Can inspect the application structure, display the css rules. \\ You should assign a hotkey to "open CSS Spy" action, e.g. **ALT+SHIFT+F5** \\ \\ < | ||
+ | - Add the e4 tooling repository to the target. Don't check the " | ||
+ | - Add Plugin org.eclipse.e4.tools.css.spy to your application dependencies. | ||
+ | - Add command <fc # | ||
+ | </ | ||
+ | |**Plug-In Inspector** - Can inspect active screen element, to show which plugin has contributed the current view.. \\ Hotkey: **ALT+SHIFT+F1** | OnBoard in Eclipse 4 |{{http:// | ||
+ | | **Menu-Inspector** can inspect which menu was contributed by which plugin \\ Hotkey: **ALT+SHIFT+F2** and menu click \\ \\ Useful to find plugins, which contribute functionality (e.g. about dialog). | Onboard in Eclipse e4 | {{http:// | ||
+ | |**Live-Editor** - Can CHANGE active application' | ||
+ | - Add Plugins: | ||
+ | - org.eclipse.e4.tools.emf.liveeditor, | ||
+ | - org.eclipse.e4.tools.emf.ui, | ||
+ | - org.eclipse.e4.tools.emf.ui.script.js + | ||
+ | - transitive dependencies to your application dependencies. | ||
+ | - Add command <fc # | ||
+ | </ | ||
+ | |**Context-Spy** - Is able to inspect the Eclipse Context \\ Hotkey: **ALT+SHIFT+F10**. \\ \\ < | ||
+ | - Add Plugins: | ||
+ | - org.eclipse.e4.tools.context.spy, | ||
+ | - org.eclipse.e4.tools.spy | ||
+ | </ | ||
+ | ==== Glossar ==== | ||
+ | |||
+ | * **Application model** - Since Version 4 of the the application layout/ | ||
+ | * **Context** is the space, where the OSGI Injections data comes from. Objects can have theis own Context, which is connected to the parent-context. (hierarchically) First the own context is searched, than the parent-context. This allows to avoid collusions. | ||
+ | * **Context Variables** (String/ | ||
+ | * **Annotations** - there are lifecycle annotations and Behavior Annotations in Eclipse. You can annotate a class or method to tell Eclipse, what automagically to do with the class or when to call a method. | ||
+ | * **Command-Handlers** - class with an // | ||
+ | handlers this is doomed to fail miserably because the handler is created | ||
+ | in a completely different context then it is executed!</ | ||
+ | ==== useful Paths and Data ==== | ||
+ | ^Path ^ Code^ Example^ | ||
+ | |Workspace Location| <sxh java> | ||
+ | |Bundle path|< | ||
+ | import org.eclipse.core.runtime.FileLocator; | ||
+ | import org.eclipse.core.runtime.Path; | ||
+ | import org.eclipse.swt.SWT; | ||
+ | import org.eclipse.swt.SWTError; | ||
+ | import org.eclipse.swt.browser.Browser; | ||
+ | import org.eclipse.swt.widgets.Composite; | ||
+ | import org.osgi.framework.Bundle; | ||
+ | import org.osgi.framework.BundleContext; | ||
+ | import org.osgi.framework.FrameworkUtil; | ||
+ | |||
+ | |||
+ | Bundle bundle = FrameworkUtil.getBundle(this.getClass()); | ||
+ | |||
+ | String pathIndexString = "/ | ||
+ | Path pathToIndex | ||
+ | |||
+ | // | ||
+ | URL indexUrl = bundle.getEntry(pathIndexString); | ||
+ | System.out.println(" | ||
+ | |||
+ | URL url = FileLocator.find(bundle, | ||
+ | URL fileUrl = null; | ||
+ | try { | ||
+ | // | ||
+ | fileUrl = FileLocator.toFileURL(url); | ||
+ | } catch (IOException e1) { | ||
+ | e1.printStackTrace(); | ||
+ | } | ||
+ | System.out.println(" | ||
+ | </ | ||
+ | |System Data|< | ||
+ | java.version Java Runtime Environment version | ||
+ | java.vendor Java Runtime Environment vendor | ||
+ | java.vendor.url Java vendor URL | ||
+ | java.home Java installation directory | ||
+ | java.vm.specification.version Java Virtual Machine specification version | ||
+ | java.vm.specification.vendor Java Virtual Machine specification vendor | ||
+ | java.vm.specification.name Java Virtual Machine specification name | ||
+ | java.vm.version Java Virtual Machine implementation version | ||
+ | java.vm.vendor Java Virtual Machine implementation vendor | ||
+ | java.vm.name Java Virtual Machine implementation name | ||
+ | java.specification.version Java Runtime Environment specification version | ||
+ | java.specification.vendor Java Runtime Environment specification vendor | ||
+ | java.specification.name Java Runtime Environment specification name | ||
+ | java.class.version Java class format version number | ||
+ | java.class.path Java class path | ||
+ | java.library.path List of paths to search when loading libraries | ||
+ | java.io.tmpdir Default temp file path | ||
+ | java.compiler Name of JIT compiler to use | ||
+ | java.ext.dirs Path of extension directory or directories | ||
+ | os.name Operating system name | ||
+ | os.arch Operating system architecture | ||
+ | os.version Operating system version | ||
+ | file.separator File separator ("/" | ||
+ | path.separator Path separator (":" | ||
+ | line.separator Line separator (" | ||
+ | user.name User's account name | ||
+ | user.home User's home directory | ||
+ | user.dir User's current working directory</ | ||
+ | |||
+ | ==== Eclipse RCP Product ==== | ||
+ | The product allows to customize your application a little: | ||
+ | * set an own icon for the launcher (*.exe file) | ||
+ | * set the name for the launcher | ||
+ | |||
+ | == Launcher Icons and tycho == | ||
+ | Here is how the icon for the launcher is set. | ||
+ | When using **tycho** - the maven plugin, which is able to build rcp in headless mode - the **paths to the images** are set **relative to the workspace**. Otherwise tycho will not fid them. \\ | ||
+ | You should use *.bmp files with alpha channel. The Tool [[http:// | ||
+ | You should not use *.ico since there are bugs in tycho and not every *.ico file will work. [[http:// | ||
+ | |||
+ | {{http:// | ||
+ | |||
+ | == Icons for windows == | ||
+ | Since E4 default Window-Icons are set via the e4Model. | ||
+ | {{http:// | ||
+ | |||
+ | There is a possibility to set default Window-Icons for Windows without icons in code | ||
+ | < | ||
+ | private void setDefaults() { | ||
+ | Image icon16 = UtilsCommonResource.getCoreIcon(ImageSize.Size16x16); | ||
+ | Image icon24 = UtilsCommonResource.getCoreIcon(ImageSize.Size24x24); | ||
+ | Image icon32 = UtilsCommonResource.getCoreIcon(ImageSize.Size32x32); | ||
+ | Image icon48 = UtilsCommonResource.getCoreIcon(ImageSize.Size48x48); | ||
+ | Image icon256 = UtilsCommonResource.getCoreIcon(ImageSize.Size256x256); | ||
+ | Window.setDefaultImages(new Image[] {icon16, icon24, icon32, icon48, icon256 }); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | == Customize the product to store the data inside the AppData folder == | ||
+ | The product may store the data inside the user's AppData Folder. | ||
+ | To do that some properties, which define the storage location for the setting must be set inside the product. | ||
+ | |||
+ | < | ||
+ | osgi.configuration.area - $APPDATA$/ | ||
+ | osgi.instance.area - $APPDATA$/ | ||
+ | </ | ||
+ | |||
+ | As the result the above properties ae redirected into the user's directory. | ||
+ | You can write the log files to that directory by including the // | ||
+ | |||
+ | and evaluating the path inside the log4j appender | ||
+ | |||
+ | <sxh java> | ||
+ | /** | ||
+ | * Unverändert der RollingFileAppender, | ||
+ | * aber erlaubt die Angabe der Logdatei in File-URI-Notation.< | ||
+ | * Dies wird notwendig, wenn die Logdatei über eine System-Property | ||
+ | * definiert wird, die von Eclipse/ | ||
+ | * <br> | ||
+ | * Beispiel: log4j.appender.XXX.FileUri=${osgi.instance.area}/ | ||
+ | | ||
+ | * @author xsb | ||
+ | */ | ||
+ | public class ExtendedRollingFileAppender extends RollingFileAppender { | ||
+ | |||
+ | public void setFileUri(String fileUri) { | ||
+ | | ||
+ | URI uri = URI.create(fileUri); | ||
+ | if (!" | ||
+ | throw new IllegalArgumentException(" | ||
+ | } | ||
+ | super.setFile(uri.getRawSchemeSpecificPart()); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | here you can learn more about theses properties: \\ | ||
+ | http:// | ||
+ | |||
+ | |||
+ | {{http:// | ||
+ | ==== Customization Runtimeparameters ==== | ||
+ | |||
+ | There are multiple ways to configure an RCP Product. | ||
+ | Here is a full list of parameters, which can be passed to Eclipse RCP Products: | ||
+ | http:// | ||
+ | |||
+ | |||
+ | ===< | ||
+ | |||
+ | Should only contain launch arguments. | ||
+ | |||
+ | < | ||
+ | -startup | ||
+ | plugins/ | ||
+ | --launcher.library | ||
+ | plugins/ | ||
+ | -vm | ||
+ | C:\Programm Files\Java\JDK\1.5\bin\javaw.exe | ||
+ | -vmargs | ||
+ | -Xms40m | ||
+ | -Xmx512m | ||
+ | -Duser.timezone=Europe/ | ||
+ | -Djna.library.path=" | ||
+ | </ | ||
+ | |||
+ | ==vmarguments== | ||
+ | |||
+ | When adding vmarguments to the file | ||
+ | * you have to prepend **-vmargs** to all parameters | ||
+ | * you have to be prepended a **D** to each parameter. Like: **D**property=value | ||
+ | < | ||
+ | -Duser.language=es | ||
+ | -Dequinox.scr.waitTimeOnBlock=1 | ||
+ | -Duser.timezone=" | ||
+ | </ | ||
+ | |||
+ | |||
+ | === / | ||
+ | |||
+ | |||
+ | Can hold any parameter, but should hold only VM arguments. For organisation' | ||
+ | The form is **parameter=value** | ||
+ | < | ||
+ | #This configuration file was written by: org.eclipse.equinox.internal.frameworkadmin.equinox.EquinoxFwConfigFileParser | ||
+ | #Mon Oct 14 17:21:58 CEST 2013 | ||
+ | eclipse.p2.profile=DefaultProfile | ||
+ | osgi.framework=file\: | ||
+ | equinox.use.ds=true | ||
+ | osgi.bundles=reference\: | ||
+ | org.eclipse.equinox.simpleconfigurator.configUrl=file\: | ||
+ | eclipse.product=de.ivu.fare.rcp.core.bundle.product | ||
+ | osgi.splashPath=platform\:/ | ||
+ | osgi.framework.extensions=reference\: | ||
+ | osgi.bundles.defaultStartLevel=4 | ||
+ | eclipse.p2.data.area=@config.dir/ | ||
+ | eclipse.application=org.eclipse.e4.ui.workbench.swt.E4Application | ||
+ | fare.serverhost=localhost | ||
+ | fare.serverport=4447 | ||
+ | osgi.nl=es_CO | ||
+ | fare.user=sov | ||
+ | fare.passwd=sovsovsov | ||
+ | </ | ||
+ | |||
+ | Equinox (which is Elcipse implementation of OSGI API) options may be added to config.ini. | ||
+ | |||
+ | All Equinox runtime optins are listed here: {{http:// | ||
+ | |||
+ | Here are some of them: | ||
+ | |||
+ | < | ||
+ | // how many miliseconds should equinox wait until the service is ready. Default is 10.000. This pparameter may force equinox to launch much faster! | ||
+ | equinox.scr.waitTimeOnBlock=1 | ||
+ | |||
+ | // logs execution time in ms | ||
+ | equinox.ds.perf=true | ||
+ | |||
+ | // prints the trace-logs on the console | ||
+ | equinox.ds.print=true | ||
+ | |||
+ | // debugmode on | ||
+ | equinox.ds.debug=true | ||
+ | |||
+ | // which start level should the bundles have on default? This should be larger than the launch-level of your product, otherwise the fragments will wait for core, which is not started yet. | ||
+ | // check the product > configuration > Start level | ||
+ | osgi.bundles.defaultStartLevel=4 | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===Runtime Parameters=== | ||
+ | Can pass runtime arguments or vmarguments. The vmarguments must come as last parameter, because all aruments after are treated as Java VM Arguments. \\ | ||
+ | The Java VM Arguments must be separated by a **=** sign. | ||
+ | |||
+ | The vmargument proper.ty=value is passed as as runtimeparameter as " | ||
+ | |||
+ | < | ||
+ | fare.exe | ||
+ | fare.exe | ||
+ | fare.exe | ||
+ | </ | ||
+ | |||
+ | <fc # | ||
+ | |||
+ | - Die Vm-Arguments **fangen mit D an** und kommen **als letztes nach -vmargs** nach allen anderen argumenten: | ||
+ | - Bei OSGI Argumenten wird das **Präfix " | ||
+ | |||
+ | |||
+ | ===Run Configuration=== | ||
+ | Use run configuration during the development to set VM Args: | ||
+ | |||
+ | {{http:// | ||
+ | ==== Application Model ==== | ||
+ | The [[eclipse: | ||
+ | {{http:// | ||
+ | |||
+ | It's parts are describedd here: | ||
+ | |||
+ | === PartDescriptors === | ||
+ | Can define Part-Templates. | ||
+ | The templates will be used to create predefined MParts: | ||
+ | <sxh java> | ||
+ | @Inject | ||
+ | MPartStack mainStack; | ||
+ | @Inject | ||
+ | IEclipseContext stackEclipseContext; | ||
+ | ... | ||
+ | IEclipseContext newContext = EclipseContextFactory.create(); | ||
+ | newContext.setParent(stackEclipseContext); | ||
+ | |||
+ | MPart part = partService.createPart(" | ||
+ | part.setContext(newContext); | ||
+ | |||
+ | mainStack.getChildren().add(part); | ||
+ | </ | ||
+ | ==== API ==== | ||
+ | |||
+ | The e4 Static classes, like services are listed here: [[http:// | ||
+ | |||
+ | == Basic Eclipse entrypoints | ||
+ | ^Command ^What^ | ||
+ | | [[http:// | ||
+ | | [[http:// | ||
+ | |||
+ | == UI related Classes, Interfaces, Factories and Services == | ||
+ | |**MApplication**|Describes the application object. Can be used for example to add new windows to your application| | ||
+ | |interface **MWindow**| Represents a Window in your application. Can be used for example to add new windows to your application \\ This interface is used by **MApplication** to work with Windows inside of the EMF structure. < | ||
+ | * getMainMenu | ||
+ | * getX | ||
+ | * getY | ||
+ | * getWidth | ||
+ | * getHeight | ||
+ | * getWindows | ||
+ | * getSharedElements | ||
+ | |||
+ | **ACHTUNG: | ||
+ | |||
+ | < | ||
+ | MWindow mWindow = findElements.get(0); | ||
+ | mWindow.setToBeRendered(true); | ||
+ | mWindow.setVisible(true); | ||
+ | |||
+ | /* | ||
+ | * ACHTUNG HACK: As soon as an MWindow is closed - it's children are set to | ||
+ | * " | ||
+ | * So Set the Children back to " | ||
+ | */ | ||
+ | for (MWindowElement child : mWindow.getChildren()) { | ||
+ | child.setToBeRendered(true); | ||
+ | child.setVisible(true); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | {{http:// | ||
+ | To connect the windows hierarchically - add the window to a parent window. Then the child window will always be in front its parent. They will come to front together | ||
+ | |||
+ | </ | ||
+ | |interface **MTrimmedWindow**| Represents a Window in your application. The underlying SWT shell has been created with the SWT.SHELL_TRIM attribute which means, it has a tile, a minimize, maximize and resize button.| | ||
+ | |interface **MPerspective**| Object for the perspective model element..| | ||
+ | |interface **MPart** extends **MUIElements**| Are used by the MBasicFactory to create Parts. | | ||
+ | |interface **MDirtyable**| Are used by the MBasicFactory to create Parts. | | ||
+ | |interface **MPartDescriptor**| MPartDescriptor is a template for new Parts. You define in your application model a PartDescriptor. A new Part based on this PartDescriptor can be created via the EPartService and shown its showPart() method. Are used by the MBasicFactory to create Parts. < | ||
+ | * getMenus | ||
+ | * getToolbar | ||
+ | * isCloseable | ||
+ | * getDescription | ||
+ | </ | ||
+ | |**MBasicFactory**|Factory, | ||
+ | |||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory; | ||
+ | |||
+ | MBasicFactory.INSTANCE.createTrimmedWindow(); | ||
+ | MBasicFactory.INSTANCE.createTrimBar(); | ||
+ | MBasicFactory.INSTANCE.createPart(); | ||
+ | MBasicFactory.INSTANCE.createWindow(); | ||
+ | MBasicFactory.INSTANCE.createPartSashContainer(); | ||
+ | MBasicFactory.INSTANCE.createPartStack(); | ||
+ | ... | ||
+ | </ | ||
+ | |**EModelService**| A very important class, which allows manipulating the EMF Model. \\< | ||
+ | * tags | ||
+ | * id | ||
+ | * Class | ||
+ | Can **move()** or **clone()** Parts. \\ | ||
+ | Can find find the nearest parent context <sxh java> | ||
+ | </ | ||
+ | |**EPartService**|< | ||
+ | * Can switch perspectives. | ||
+ | * Can activate Parts <sxh java> | ||
+ | * Can hide Parts | ||
+ | * Can retrieve all dirty Parts | ||
+ | </ | ||
+ | |**ECommandService**| Can be injected. Finds Commands. | | ||
+ | |**CommandManager**| Can be injected. Finds Commands AND is able to add execution listeners to them. | | ||
+ | |**EHandlerService**|< | ||
+ | <sxh java> | ||
+ | @Inject | ||
+ | private IEclipseContext eclipseContext; | ||
+ | |||
+ | @Inject | ||
+ | private ECommandService commandService; | ||
+ | |||
+ | @Inject | ||
+ | private EHandlerService handlerService; | ||
+ | |||
+ | | ||
+ | treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { | ||
+ | |||
+ | @Override | ||
+ | public void selectionChanged(SelectionChangedEvent event) { | ||
+ | IStructuredSelection selection = (IStructuredSelection) event.getSelection(); | ||
+ | for (Iterator<?> | ||
+ | Module module = (Module) iterator.next(); | ||
+ | |||
+ | ParameterizedCommand command = commandService.createCommand(" | ||
+ | null); | ||
+ | handlerService.executeHandler(command, | ||
+ | |||
+ | } | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |**IEventLoopAdvisor**|< | ||
+ | |**MenuManagerRenderer**|< | ||
+ | Merges MenuItems from MenuContributions with the given menu. | ||
+ | <sxh java> | ||
+ | // somehow find a menu in Application.e4xmi e.g. by using EModelService.class | ||
+ | org.eclipse.e4.ui.model.application.ui.menu.MMenu mmenu; | ||
+ | |||
+ | MenuManagerRenderer mmr = new MenuManagerRenderer(); | ||
+ | mmr.init(eclipseContext); | ||
+ | ContextInjectionFactory.inject(mmr, | ||
+ | |||
+ | // this checks the e4 model for contributions with same id and merges them to the mmenu | ||
+ | mmr.processContributions(mmenu, | ||
+ | </ | ||
+ | |||
+ | |||
+ | == Eclipse e4 Context related API == | ||
+ | ^Command ^What^ | ||
+ | |context.modify, | ||
+ | |context.runAndTrack| registers a runnable, which is called on every context change.| | ||
+ | |context.getActive(MyObjectClass)| searches the given class in the whole active context chain. This is a possibility to search for objects in the hierarchy, below the own context. | | ||
+ | |**IContextFunction**| Context can contain Functions, which will operate on the context values.< | ||
+ | //Creation | ||
+ | slideContextObjects.get(item).set(" | ||
+ | | ||
+ | @Override | ||
+ | public Object compute(IEclipseContext context) { | ||
+ | if(context.get(" | ||
+ | return 1; | ||
+ | } | ||
+ | return 2; | ||
+ | } | ||
+ | }); | ||
+ | | ||
+ | //usage | ||
+ | slideContextObjects.get(" | ||
+ | </ | ||
+ | |**ContextInjectionFactory**|< | ||
+ | <sxh java> | ||
+ | ManipulateModelhandler man = new ManipulateModelhandler(); | ||
+ | |||
+ | // | ||
+ | //the man should allready have an own iEclipseContext to copy the data in | ||
+ | ContextInjectionFactory.inject(man, | ||
+ | |||
+ | // | ||
+ | ContextInjectionFactory.make(TestPart.class, | ||
+ | |||
+ | man.execute();</ | ||
+ | |**EclipseContextFactory**| A factory for creating context instances | ||
+ | <sxh java> | ||
+ | MPart newMPart = MBasicFactory.INSTANCE.createPart(); | ||
+ | |||
+ | MPartStack mainStack = (MPartStack) eModelService.find(STACKID, | ||
+ | mainStack.getChildren().add(newMPart); | ||
+ | | ||
+ | //nearest context of the stack | ||
+ | IEclipseContext eclipseContext = eModelService.getContainingContext(mainStack); | ||
+ | |||
+ | // eclipseContext should be the Context of mainStack, which is the parent of newPart in model | ||
+ | IEclipseContext newContext = EclipseContextFactory.create(); | ||
+ | newContext.setParent(eclipseContext); | ||
+ | newMPart.setContext(newContext); | ||
+ | </ | ||
+ | |**EclipseContext.activate()**, | ||
+ | <sxh java> | ||
+ | main(){ | ||
+ | IEclipseContext parentContext = EclipseContextFactory.create(); | ||
+ | IEclipseContext context = EclipseContextFactory.create(); | ||
+ | context.setParent(parentContext); | ||
+ | | ||
+ | context.set(" | ||
+ | context.activate(); | ||
+ | | ||
+ | parentContext.getActive(" | ||
+ | } | ||
+ | | ||
+ | @Optional | ||
+ | @Inject | ||
+ | void monitorMyObject(@Active @Named(OBJECTNAME) MyObject newMyObject){ | ||
+ | ... | ||
+ | } | ||
+ | | ||
+ | </ | ||
+ | |Naming the Context for Debugging| < | ||
+ | <sxh java> | ||
+ | // setting the EclipseContext name which will be displayed by debugger (toString Method) | ||
+ | eclipseContextForEditor.set(EclipseContext.DEBUG_STRING, | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | == Useful Classes and Methods== | ||
+ | |||
+ | ^Command ^What^ | ||
+ | |[[http:// | ||
+ | |[[http:// | ||
+ | <sxh java> | ||
+ | public class NodeListFactory implements IAdapterFactory { | ||
+ | /** The actual conversion to a Node */ | ||
+ | public Object getAdapter(Object list, Class clazz) { | ||
+ | if (clazz == Node.class && list instanceof List) { | ||
+ | Element root = new Element(" | ||
+ | Iterator it = list.iterator(); | ||
+ | while(it.hasNext()) { | ||
+ | Element item = new Element(" | ||
+ | item.appendChild(it.next().toString()); | ||
+ | root.appendChild(item); | ||
+ | } | ||
+ | return root; | ||
+ | } else { | ||
+ | return null; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** Teach the platform to convertLists into Nodes. The Adaptor will return IAdaptable instances which will have to be casted manually.*/ | ||
+ | Platform.getAdapterManager().registerAdapters( new NodeListFactory(), | ||
+ | |||
+ | /** Use the mechanism to cast List to Nodes*/ | ||
+ | Node getNodeFrom(IAdaptable list) { | ||
+ | Object adaptable = list.getAdapter(Node.class); | ||
+ | if (adaptable != null) { | ||
+ | Node node = (Node)adaptable; | ||
+ | return node; | ||
+ | } | ||
+ | return null; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |**IEventBroker**|< | ||
+ | |||
+ | **ACHTUNG: | ||
+ | The IEventBrokerTopics may not contain any cahracters: | ||
+ | |. | **NOT ALLOWED**| | ||
+ | |_| ALLOWED| | ||
+ | |Big characters | ALLOWED| | ||
+ | |||
+ | There is a possibility to use hierarchies in Topics: | ||
+ | |||
+ | <sxh java> | ||
+ | public static final String TOPIC_TODO_ALLTOPICS = | ||
+ | " | ||
+ | | ||
+ | public static final String TOPIC_TODO_NEW = | ||
+ | " | ||
+ | | ||
+ | public static final String TOPIC_TODO_DELETE = | ||
+ | " | ||
+ | | ||
+ | public static final String TOPIC_TODO_UPDATE = | ||
+ | " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | More about the usage [[http:// | ||
+ | Eclipse 4 used the EventAdmin to communicate between Bundles. [[http:// | ||
+ | |||
+ | |||
+ | **Observer Problems** | ||
+ | The current event processing in Eclipse is centered on the listener mechanism (also commonly described as the " | ||
+ | |||
+ | |||
+ | I use " | ||
+ | |||
+ | The " | ||
+ | |||
+ | |||
+ | **IEventBroker vs. Observer** | ||
+ | The main difference to the observer is that an intermediary is introduced between the sender and the receiver: events are published to the Event Broker which dispatches them to the listeners subscribed to this event type. | ||
+ | |||
+ | In this approach listeners can subscribe and unsubscribe as they please, regardless if the particular event source exists. It means that there is one implementation that everybody can use without the need to write additional code. It also means that no extra processing will be done for events that do not happen. And we'll have no need for multitude of listener interfaces specific to each event. | ||
+ | |||
+ | So, is this the best thing since sliced bread? Well, it does have some downsides. The event broker becomes a rather sensitive point of the system. It has to perform well both in CPU timing and memory allocations. And the broker itself better be robust. | ||
+ | |||
+ | <sxh java> | ||
+ | //register listener | ||
+ | IEventBroker eventBroker = (IEventBroker) eclipseContext.get( | ||
+ | IEventBroker.class.getName()); | ||
+ | eventBroker.subscribe(IUIEvents.ElementContainer.Topic, | ||
+ | new EventHandler() { | ||
+ | public void handleEvent(Event event) { | ||
+ | if (event.getProperty(IUIEvents.EventTags.AttName) | ||
+ | .equals(IUIEvents.ElementContainer.ActiveChild)) { | ||
+ | Object newPart = event.getProperty(IUIEvents.EventTags.NewValue); | ||
+ | if (newPart instanceof MPart) { | ||
+ | // do something | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | }, /* headless */ false); | ||
+ | | ||
+ | |||
+ | //sending Events | ||
+ | Map data = new HashMap(); | ||
+ | map.put(IUIEvents.EventTags.AttName, | ||
+ | map.put(IUIEvents.EventTags.Element, | ||
+ | map.put(IUIEvents.EventTags.OldValue, | ||
+ | map.put(IUIEvents.EventTags.NewValue, | ||
+ | map.put(IUIEvents.EventTags.Type, | ||
+ | |||
+ | IEventBroker eventBroker = (IEventBroker) eclipseContext.get( | ||
+ | IEventBroker.class.getName()); | ||
+ | eventBroker.send(IUIEvents.ElementContainer.Topic, | ||
+ | |||
+ | //To receive EventBroker Notifications in a specific method - use the shortcut | ||
+ | private static final String EVENT_TOPIC_ID = " | ||
+ | @Inject | ||
+ | @Optional | ||
+ | public void handleEvent(@UIEventTopic(EVENT_TOPIC_ID) final EventBrokerModuleData eventBrokerModuleData, | ||
+ | @Optional final EditorPage page, @Optional final IEclipseContext eclipseContext) { | ||
+ | //LOGIC GOES HERE | ||
+ | } | ||
+ | |||
+ | |||
+ | </ | ||
+ | |||
+ | Some Fallpits: | ||
+ | |||
+ | <sxh java> | ||
+ | //1. ACHTUNG: sending Platform Objects doesn' | ||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | |||
+ | //here this method is not triggered | ||
+ | @Inject | ||
+ | @Optional | ||
+ | public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EventBroker b) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | |||
+ | //2. ACHTUNG: sending " | ||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | |||
+ | |||
+ | //3. ACHTUNG: subscribing for Interfaces does not work. Just classes. | ||
+ | @Inject | ||
+ | @Optional | ||
+ | //public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) IEclipseContext c) { | ||
+ | public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EclipseContext c) { | ||
+ | |||
+ | |||
+ | |||
+ | //4. receiving Object results in an unexpected behaviour! Sometimes it reacts on everything, sometims on nothing. | ||
+ | @Inject | ||
+ | @Optional | ||
+ | public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) Object o) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | //5. sending and receiving String - works always. Test using Strings. | ||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | |||
+ | @Inject | ||
+ | @Optional | ||
+ | public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) String s) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | //6. sending and receiving Objects created by new - works | ||
+ | eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, | ||
+ | |||
+ | @Inject | ||
+ | @Optional | ||
+ | public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EventRepaintTable r) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | The class which receives the **EventBroker** notifications is not coppled with the EventBroker. | ||
+ | It may be destroyed by the Garbadge Collector if it is not stored somewhere. | ||
+ | |||
+ | <sxh java> | ||
+ | // Fallpit garbadge collector | ||
+ | class SomeClass{ | ||
+ | |||
+ | SomeClass(){ | ||
+ | | ||
+ | /* this anonymous class is created and stored nowhere. | ||
+ | * It is intended to handle EVENTID_CREATE_DOMAINOBJECT events, but | ||
+ | * It will be destroyed by the garbadge collector and events wont be received | ||
+ | | ||
+ | new MyEventHandlerListener(){ | ||
+ | | ||
+ | | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | | **UISynchronize** |< | ||
+ | |||
+ | @Inject | ||
+ | UISynchronize uisynchronize | ||
+ | |||
+ | .. | ||
+ | |||
+ | uiSynchronizer.asyncExec(new Runnable() { | ||
+ | @Override | ||
+ | public void run() { | ||
+ | //do something on UI Thread | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | == OSGI Budnles Related API == | ||
+ | |||
+ | |**IContributionFactory**| A factory , which can create an Object by ContributorURI. Can be used to create an Object to do something with it, before it will be connected to a Part. < | ||
+ | <sxh java> | ||
+ | @Inject | ||
+ | IEclipseContext iEclipseContext | ||
+ | |||
+ | @Inject | ||
+ | MApplication mApplication; | ||
+ | ... | ||
+ | |||
+ | Object partimplementation = IContributionFactory.create(" | ||
+ | MPart part = (MPart) eModelService.find(" | ||
+ | part.setObject(partimplementation); | ||
+ | | ||
+ | //its the same like doing | ||
+ | MPart part = (MPart) eModelService.find(" | ||
+ | part.setContributionURI(" | ||
+ | |||
+ | </ | ||
+ | |**FrameworkUtil**| This can retrieve a Bundle by class. < | ||
+ | <sxh java> | ||
+ | FrameworkUtil.getBundle(MyHandler.class); | ||
+ | </ | ||
+ | |||
+ | <sxh java> | ||
+ | Class<?> | ||
+ | |||
+ | // getting the bundle | ||
+ | Bundle b1 = Platform.getBundle(" | ||
+ | Bundle b = FrameworkUtil.getBundle(this.getClass()); | ||
+ | |||
+ | // getting the extension point by name. Now looking for the module contributed by the | ||
+ | // current Bundle? How? Can't get the right Bundle! | ||
+ | IExtensionPoint extPoint = Platform.getExtensionRegistry().getExtensionPoint(" | ||
+ | IExtensionPoint extPointMod = Platform.getExtensionRegistry().getExtensionPoint( | ||
+ | " | ||
+ | | ||
+ | //getting the extensionpoint by bundle? | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== Injection Cycle ==== | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |< 100% 50% >| | ||
+ | ^Step ^Fallpits ^ | ||
+ | | 1. run constructor code |< | ||
+ | \\ You cannot use of them inside the constructor. | ||
+ | No parametrized constructors are allowed, where you need a **special** object as a parameter. (e.g. concrete parrent composite)\\ | ||
+ | Workaround - is to create a special context, where you put in the concrete object and where you inject these objects into the constructor. | ||
+ | <sxh java> | ||
+ | c2 = context.createChild(); | ||
+ | c2.set( Composite.class, | ||
+ | ContextInjectionFactory.make(TableToolbar.class, | ||
+ | </ | ||
+ | | 2. do field injection | ||
+ | | 3. do method injection | | | ||
+ | | 4. run @PostConstruct | | | ||
+ | |||
+ | |||
+ | ==== Using Annotations to Inject Objects from Context ==== | ||
+ | |||
+ | Add own Objects to the context, then inject it from there into own Objects | ||
+ | |||
+ | **FALLPUTS: | ||
+ | - method, which is invokes should be PUBLIC | ||
+ | - when using own annotation - it should have @Retention(value=RetentionPolicy.RUNTIME) | ||
+ | |||
+ | <sxh java> | ||
+ | |||
+ | //ACHTUNG: Retention is important, when using methods at runtime | ||
+ | @Retention(value=RetentionPolicy.RUNTIME) | ||
+ | @Inherited | ||
+ | @Target(value={java.lang.annotation.ElementType.METHOD}) | ||
+ | public @interface MyPostGuiCreate { | ||
+ | |||
+ | } | ||
+ | |||
+ | public class InjectableObject { | ||
+ | void talk(){ | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public class InjectContextHere { | ||
+ | //ACHTUNG: method should be public to be reached by reflections | ||
+ | @MyPostGuiCreate | ||
+ | public void myPostGuiCreate(@Named(" | ||
+ | injectableObject.talk(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @PostConstruct | ||
+ | void postConstruct(){ | ||
+ | |||
+ | // create a context | ||
+ | IEclipseContext context = EclipseContextFactory.create(); | ||
+ | |||
+ | // put some stuff into it | ||
+ | InjectableObject injectableObject = new InjectableObject(); | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | |||
+ | // create the object, which we will inject stuff into | ||
+ | InjectContextHere injectContextInThisObject = new InjectContextHere(); | ||
+ | |||
+ | //now use the annotation | ||
+ | ContextInjectionFactory.invoke(injectContextInThisObject, | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | ==== IPartListener and EPartService ==== | ||
+ | The // | ||
+ | Following hooks are available and are triggered in the following order. | ||
+ | |||
+ | ^ ^ ^ | ||
+ | | partVisible | Is triggered when a Part: is created / closed, loses / gains focus | | ||
+ | | partHidden | When part loses focus, | ||
+ | | partDeactivated | When part loses its focus, when it is closed | | ||
+ | | partBroughtToTop | | | ||
+ | | partActivated | Is triggered when a Part: is created, | | ||
+ | |||
+ | == Concrete cycles == | ||
+ | |||
+ | == new Part opening cycle == | ||
+ | |partVisible| | ||
+ | |partBroughtToTop| | ||
+ | |UILifeCycle.BRINGTOTOP <fc # | ||
+ | |partActivated| | ||
+ | |@Focus <fc # | ||
+ | |UILifeCycle.ACTIVATE <fc # | ||
+ | |||
+ | == explicite selection (by click) of Part which was inactive == | ||
+ | |partVisible| | ||
+ | |partBroughtToTop| | ||
+ | |@Focus <fc # | ||
+ | |UILifeCycle.BRINGTOTOP <fc # | ||
+ | |partActivated| | ||
+ | |UILifeCycle.ACTIVATE <fc # | ||
+ | |||
+ | == implicite unselection of a Part, through selection of another == | ||
+ | |partHidden</ | ||
+ | |partDeactivated| | ||
+ | |||
+ | == Part closing cycle == | ||
+ | |partDeactivated| | ||
+ | |||
+ | |||
+ | |||
+ | Additionally an artificaial event, when the part is detached may be implemented by using the following: | ||
+ | * every time the part is dragged to a new Window the hook **partVisible** is triggered. | ||
+ | * when a // | ||
+ | |||
+ | <sxh java> | ||
+ | |||
+ | // Part listener | ||
+ | partService.addPartListener(new IPartListener() { | ||
+ | |||
+ | |||
+ | @Override | ||
+ | public void partVisible(MPart part) { | ||
+ | if(isDetached(part)){ | ||
+ | // part is detached now! | ||
+ | } | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | } | ||
+ | | ||
+ | private boolean isDetached(MPart part){ | ||
+ | boolean isDeatached = false; | ||
+ | MWindow mWindow = part.getContext().get(MWindow.class); | ||
+ | |||
+ | /* Every Part has an MWindow somewhere in the e4 Model hierarchy. | ||
+ | * In DETACHED Parts the Parent of the MWindow is NULL. | ||
+ | * In NON detached Parts the Parent is not null and has the type " | ||
+ | if(mWindow.getParent()==null || !MApplication.class.isAssignableFrom(mWindow.getParent().getClass())){ | ||
+ | isDeatached = true; | ||
+ | } | ||
+ | return isDeatached; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Annotations for Injections ==== | ||
+ | Dependency Injections and annotations are describes [[http:// | ||
+ | |||
+ | ^OSGI Annotations^ Meaning^ | ||
+ | |@Inject |Can be applied to constructor, | ||
+ | - The @Inject annotated **constructor** will be called, when the object of current type is needed by the pltform. | ||
+ | - Then the @Inject annotated **fields** which are are filled with data from the Context. | ||
+ | - Then the @Inject annotated **methods** are exectued, which suppose to do initialization. (called initializer methods) | ||
+ | </ | ||
+ | @Inject | ||
+ | private void getActivePart(@Named(IServiceConstants.ACTIVE_PART) MPart part) { | ||
+ | activePart = part; | ||
+ | } | ||
+ | </ | ||
+ | |@Optional \\ @Inject | @Optional is used together with @Inject. This Annotation tells the system, that this field is not required for the current class. However an @Option annotated Field will be filled by the system later, as soon, as the Object becomes available in the context. This feature can be used if an Object is not available on creation, but will be available in the later part of teh lifecycle. <sxh java> | ||
+ | class X{ | ||
+ | @Optional | ||
+ | @Inject | ||
+ | EHandler eHandler; //the creation of class will not fail, if the EHandler is not yet available. However if the Object will be created in the context later it will magically appear here - and myFunction will be able to use eHandler. | ||
+ | ... | ||
+ | void myFunction(){ | ||
+ | //use eHandler; | ||
+ | } | ||
+ | </ | ||
+ | |@Named| Can be applied to **classes** and **methods**. Makes the system retrieve a concrete object from the context, chosen by a name-key. \\ If the object was put into the context, without a name-key, then the object' | ||
+ | * @Inject @Named (IServiceConstants.ACTIVE_SHELL) Shell shell; - active shell | ||
+ | * @Inject @Named (IServiceConstants.ACTIVE_PART) Shell shell; - active part | ||
+ | * @Inject @Named (IServiceConstants.ACTIVE_SELECTION) Shell shell; - active selection | ||
+ | * @Inject @Named (IServiceConstants.SELECTION) Shell shell; - (inactive?) selection | ||
+ | </ | ||
+ | |@Creatable| Can be applied to **classes**. Makes system initialize the Object of this type automatically, | ||
+ | |@Inject @Preference(nodePath=" | ||
+ | |@Active|< | ||
+ | <sxh java> | ||
+ | @Execute | ||
+ | public void execute(@Active MPart activePart) { | ||
+ | ... | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Further, when annotating a Value by @Active this value gets reinjected, every time when a content in the active context-chain changes!. | ||
+ | <sxh java> | ||
+ | |||
+ | ... | ||
+ | @Active | ||
+ | MyObject myObject; | ||
+ | |||
+ | void method(EclipseContext newContextWithMyObject){ | ||
+ | ... | ||
+ | | ||
+ | } | ||
+ | |||
+ | void method(EclipseContext childContextWithMyObject){ | ||
+ | ... | ||
+ | // | ||
+ | | ||
+ | } </ | ||
+ | |||
+ | |||
+ | <fc # | ||
+ | |||
+ | |||
+ | ^LifecyleAnnotation^ Meaning^ | ||
+ | |1. @PostConstruct |Is called after the class is constructed and the field and method injection has been performed. Create the GUI here. \\ SUPPORTS INHERITANCE. Is called on multiple inheritance levels if present!| | ||
+ | |2. @PostContextCreate|< | ||
+ | |3. @ProcessAdditions|< | ||
+ | |4. @ProcessRemovals|< | ||
+ | * method will be called after the @ProcessAdditions calls are done**</ | ||
+ | |5. UIEvents.UILifeCycle.APP_STARTUP_COMPLETE |< | ||
+ | <sxh java> | ||
+ | @Inject | ||
+ | @Optional | ||
+ | public void appIsRunning(@UIEventTopic(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE ) | ||
+ | Event event) { | ||
+ | // Do something | ||
+ | } | ||
+ | </ | ||
+ | |5+. UIEvents.UILifeCycle.ACTIVATE |< | ||
+ | |||
+ | <sxh java> | ||
+ | IEventBroker broker = (IEventBroker) iEclipseContext.get(IEventBroker.class.getName()); | ||
+ | broker.subscribe(UIEvents.UILifeCycle.ACTIVATE, | ||
+ | @Override | ||
+ | public void handleEvent(Event event) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | // alternative annotation: you can inject the UIEventTopic Event | ||
+ | @Inject | ||
+ | @Optional | ||
+ | public void partActivation(@UIEventTopic(UIEvents.UILifeCycle.ACTIVATE) Event event, MApplication application) { | ||
+ | // retrieve data from event | ||
+ | MPart activePart = (MPart) event.getProperty(UIEvents.EventTags.ELEMENT); | ||
+ | } | ||
+ | </ | ||
+ | |5+. UIEvents.UILifeCycle.BRINGTOTOP|< | ||
+ | <sxh java> | ||
+ | IEventBroker broker = (IEventBroker) iEclipseContext.get(IEventBroker.class.getName()); | ||
+ | broker.subscribe(UIEvents.UILifeCycle.BRINGTOTOP, | ||
+ | @Override | ||
+ | public void handleEvent(Event event) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | }); | ||
+ | }); | ||
+ | </ | ||
+ | |5+. UIEvents.UILifeCycle.PERSPECTIVE_OPENED|< | ||
+ | <sxh java> | ||
+ | broker.subscribe(UIEvents.UILifeCycle.PERSPECTIVE_OPENED, | ||
+ | @Override | ||
+ | public void handleEvent(Event event) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | }); | ||
+ | </ | ||
+ | |5+. UIEvents.UILifeCycle.PERSPECTIVE_SAVED|< | ||
+ | <sxh java> | ||
+ | broker.subscribe(UIEvents.UILifeCycle.PERSPECTIVE_SAVED, | ||
+ | @Override | ||
+ | public void handleEvent(Event event) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | }); | ||
+ | </ | ||
+ | |5+. @Focus |Indicates that this method should be called, once the Part gets the focus. It is required to set the focus on one user interface control otherwise certain workbench functionality does not work. \\ <fc # | ||
+ | |5+ @PreSave |Is called before the application model is saved. You can modify the model before it is persisted. | | ||
+ | |5+ @Persist |Is called if a save request on the Part is triggered. Can be used to save the data of the Part. | | ||
+ | |6. @PersistState |Is called before the model object is disposed, so that the Part can save its state. | | ||
+ | |7. @PreDestroy | Is called before the class is destroyed. Can be used to clean up resources. | | ||
+ | ==== Handler Annotations ==== | ||
+ | Sources: http:// | ||
+ | |@Execute| This method will be Executed when the handelr is started| | ||
+ | |@CanExecute|Mehtod annotated by this must return true, in order for the Handler' | ||
+ | ==== Eclipse Context Lifecycle ==== | ||
+ | |||
+ | == notes == | ||
+ | - Command Handlers are created in a different context, than in which they are executed. So can't use injections in there. | ||
+ | ==== Commands with Parameters ==== | ||
+ | |||
+ | To send a Command with a parameter it should be defined in the Applicationmodel. | ||
+ | {{http:// | ||
+ | |||
+ | The matching handler will receive the command and the // | ||
+ | The command parameter can be injected using //@Named// annotation with the id of the command in it. \\ | ||
+ | <fc # | ||
+ | <sxh java> | ||
+ | @Execute | ||
+ | public void execute(@Optional @Named(" | ||
+ | ... | ||
+ | | ||
+ | @CanExecute | ||
+ | public boolean canExecute(){ | ||
+ | return true; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | To execute the command programmatically (with parameters) do the following: | ||
+ | <sxh java> | ||
+ | ECommandService commandService = eclipseContext.get(ECommandService.class); | ||
+ | EHandlerService handlerService = eclipseContext.get(EHandlerService.class); | ||
+ | |||
+ | /* | ||
+ | * The map is filled with [String: | ||
+ | * Can not pass any parameterkey here. Only those, which were | ||
+ | * defined in model, as a Command Parameter. | ||
+ | */ | ||
+ | Map< | ||
+ | parameters.put(" | ||
+ | module.getModuleId()); | ||
+ | |||
+ | Command commandNoParms = commandService.getCommand(" | ||
+ | ParameterizedCommand commandParametrized = ParameterizedCommand.generateCommand( | ||
+ | commandNoParms, | ||
+ | |||
+ | handlerService.executeHandler(commandParametrized); | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Declarative Services in e4 alias. Context Functions==== | ||
+ | By using declarative Services you can make an Iterface availabel, for injections in <fc # | ||
+ | A Factory is registered in xml, to return implementations of an Interface. | ||
+ | |||
+ | ==Fallpits== | ||
+ | - this funcionality requires following bundles. Otherwise the serivecs wont be found. | ||
+ | - org.eclipse.core.runtime | ||
+ | - org.eclipse.equinox.ds | ||
+ | - org.eclipse.equinox.util | ||
+ | - org.eclipse.osgi.services | ||
+ | - And < | ||
+ | - the service must be registered in the **MANIFEST.MF** file as < | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | Infos: | ||
+ | * http:// | ||
+ | * http:// | ||
+ | * http:// | ||
+ | * http:// | ||
+ | |||
+ | |< 100% 40% 60% >| | ||
+ | ^Step ^ Describtion^ | ||
+ | |1. edit MANIFEST.MF| register xml in MANIFEST.MF by adding the following lines <sxh java> | ||
+ | Service-Component: | ||
+ | Bundle-ActivationPolicy: | ||
+ | </ | ||
+ | |2. Dependencies| Require Bundles <sxh java> | ||
+ | org.eclipse.equinox.util | ||
+ | org.eclipse.equinox.ds - Declarative service | ||
+ | </ | ||
+ | |3. Create Interface, Implementation, | ||
+ | |||
+ | public interface IContextHellower { | ||
+ | void sayHello(); | ||
+ | } | ||
+ | |||
+ | // you can add @Singleton to the implementing class. Then only one instance of the service will be created. | ||
+ | @Singleton | ||
+ | public class ContextHellower implements IContextHellower { | ||
+ | @Override | ||
+ | public void sayHello() { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public class ContextHellowerCreationFunction extends ContextFunction { | ||
+ | @Override | ||
+ | public Object compute(IEclipseContext context) { | ||
+ | return ContextInjectionFactory.make(ContextHellower.class, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |4. Create an XML: OSGI-INF/ | ||
+ | <?xml version=" | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | <provide interface=" | ||
+ | </ | ||
+ | < | ||
+ | value=" | ||
+ | </ | ||
+ | </ | ||
+ | |5. Inject | <sxh java> | ||
+ | @Inject | ||
+ | public PlaygroundPart(IContextHellower contextHellower) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | </ | ||
+ | ==== Adding a non model object to the model ==== | ||
+ | If you need to use injections or other context features inside of a class, which isn't in the model - there are several ways to do so. | ||
+ | Then this class will be injected into the Context AND YOU WILL BE ABLE TO USE INJECTIONS INSIDE THIS CLASS! | ||
+ | |||
+ | | **IEclipseContext.runtrack(Runnable)** | The runnable is called once on PostConstruct. IEclipseContext is passed to it. After that it is called every time when one of the context object which Runnable has used - changes. < | ||
+ | public TableCallbackHandler(IEclipseContext context) { | ||
+ | |||
+ | context.runAndTrack(new RunAndTrack() { | ||
+ | |||
+ | @Override | ||
+ | public boolean changed(IEclipseContext context) { | ||
+ | System.out.println(" | ||
+ | activeEditorPage = context.get(EditorPage.class); | ||
+ | return true; // makes the method to be called again, when EditorPage.class changes | ||
+ | // in context | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | </ | ||
+ | | **@Creatable** | Annotate the class by @Creatable. It can then be created by atomatically, | ||
+ | @Creatable | ||
+ | public class TableCallbackHandler { | ||
+ | |||
+ | EditorPage activeEditorPage; | ||
+ | @Inject | ||
+ | public TableCallbackHandler() { | ||
+ | |||
+ | @Inject | ||
+ | public void onFocusChange(EditorPage page) { | ||
+ | System.out.println(" | ||
+ | this.activeEditorPage = page; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | | ||
+ | ... | ||
+ | TableCallbackHandler t = ContextInjectionFactory.make(TableCallbackHandler.class, | ||
+ | </ | ||
+ | |||
+ | If the automaticall creation has to be done programatically - don't forget to put the newly created object into the context. It is not done by calling make only. | ||
+ | <sxh java> | ||
+ | if (!eclipseContext.containsKey(TableCallbackHandler.class)) { | ||
+ | Object handler = ContextInjectionFactory.make(TableCallbackHandler.class, | ||
+ | eclipseContext.set(TableCallbackHandler.class.getName(), | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Using Menu in Applicaiton.e4xmi | ||
+ | |||
+ | === Menu === | ||
+ | |||
+ | The menu in Applicaiton.e4xmi is represented by **MMenu**. \\ | ||
+ | The menu may be rendered to SWT widgets as following: | ||
+ | |||
+ | <sxh java> | ||
+ | |||
+ | /** | ||
+ | * Same as the {@link # | ||
+ | * Usable to flattern a menu object | ||
+ | * | ||
+ | * @param parentMenu - the parent menu | ||
+ | * @param menuElement - the element | ||
+ | */ | ||
+ | private void renderMenuSkipFirstLevel(Menu parentMenu, MMenu menuElement){ | ||
+ | for(MMenuElement childMenuElement : menuElement.getChildren()){ | ||
+ | renderMenu(parentMenu, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Method which iterates all {@link MenuContribution} children and renders them | ||
+ | * @param parentMenu - the menu which should be used as the parent for rendered {@link MenuItem} objects | ||
+ | * @param mMenuContribution - the element from the e4 model which contains menu items | ||
+ | */ | ||
+ | public void renderMenu(Menu parentMenu, MMenuContribution mMenuContribution){ | ||
+ | for(MMenuElement menuElement : mMenuContribution.getChildren()){ | ||
+ | renderMenu(parentMenu, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Renders the given {@link MenuElement} as a {@link Menu} and {@link MenuItem} structure. | ||
+ | * | ||
+ | * @param parentMenu - the menu which should be used as the parent for rendered {@link MenuItem} objects | ||
+ | * @param menuElement - the element from the e4 model which represents a menuItem | ||
+ | */ | ||
+ | @SuppressWarnings({ " | ||
+ | public void renderMenu(Menu parentMenu, MMenuElement menuElement){ | ||
+ | // not null | ||
+ | Assert.isNotNull(parentMenu); | ||
+ | |||
+ | if(menuElement instanceof MHandledItem){ | ||
+ | MHandledItem item = (MHandledItem) menuElement; | ||
+ | MCommand mcommand = item.getCommand(); | ||
+ | String commandId = mcommand.getElementId(); | ||
+ | |||
+ | final ParameterizedCommand paramCommand = commandService.createCommand(commandId, | ||
+ | |||
+ | // swt | ||
+ | MenuItem menuItem = new MenuItem(parentMenu, | ||
+ | menuItem.setText(item.getLabel()); | ||
+ | menuItem.addSelectionListener(new SelectionAdapter() { | ||
+ | @Override | ||
+ | public void widgetSelected(SelectionEvent e) { | ||
+ | handlerService.executeHandler(paramCommand, | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | }else if(menuElement instanceof MMenuSeparator){ | ||
+ | MMenuSeparator item = (MMenuSeparator) menuElement; | ||
+ | |||
+ | // swt | ||
+ | new MenuItem(parentMenu, | ||
+ | |||
+ | |||
+ | }else if(menuElement instanceof MDirectMenuItem){ | ||
+ | MDirectMenuItem item = (MDirectMenuItem) menuElement; | ||
+ | String contributionUri = item.getContributionURI(); | ||
+ | |||
+ | final Object handler = contributionFactory.create(contributionUri, | ||
+ | // swt | ||
+ | MenuItem menuItem = new MenuItem(parentMenu, | ||
+ | menuItem.setText(item.getLabel()); | ||
+ | menuItem.addSelectionListener(new SelectionAdapter() { | ||
+ | @Override | ||
+ | public void widgetSelected(SelectionEvent e) { | ||
+ | ContextInjectionFactory.invoke(handler, | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | |||
+ | }else if(menuElement instanceof MDynamicMenuContribution){ | ||
+ | DynamicMenuContributionImpl item = (DynamicMenuContributionImpl) menuElement; | ||
+ | String contributionUri = item.getContributionURI(); | ||
+ | |||
+ | final Object handler = contributionFactory.create(contributionUri, | ||
+ | |||
+ | // swt | ||
+ | MenuItem menuItem = new MenuItem(parentMenu, | ||
+ | menuItem.setText(item.getLabel()); | ||
+ | menuItem.addSelectionListener(new SelectionAdapter() { | ||
+ | @Override | ||
+ | public void widgetSelected(SelectionEvent e) { | ||
+ | ContextInjectionFactory.invoke(handler, | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | |||
+ | }else if(menuElement instanceof MMenu){ | ||
+ | MMenu menu = (MMenu) menuElement; | ||
+ | |||
+ | // swt | ||
+ | MenuItem mnItemSubmenu = new MenuItem(parentMenu, | ||
+ | mnItemSubmenu.setText(menu.getLabel()); | ||
+ | String iconUri = menu.getIconURI(); | ||
+ | |||
+ | Menu swtSubMenu = new Menu(mnItemSubmenu); | ||
+ | mnItemSubmenu.setMenu(swtSubMenu); | ||
+ | |||
+ | for(MMenuElement subElement : ((MMenu) menuElement).getChildren()){ | ||
+ | renderMenu(swtSubMenu, | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Menu Contributions === | ||
+ | A menu contribution allows adding | ||
+ | The menu contribution may e.g. be created in another e4-model-plugin. | ||
+ | |||
+ | For that: | ||
+ | |||
+ | - Create a main-menu, here with id **de.mine.e4.styleinspector.menu.context.basic** \\ {{http:// | ||
+ | - Create a MenuContriibution and use Parent-Id to point to the main-menu **de.mine.e4.styleinspector.menu.context.basic** \\ {{http:// | ||
+ | |||
+ | |||
+ | Retrieving the Menu may be done as following. It is assumed that a Menu-contribution ID_CONTEXTMENU_CONTRIB | ||
+ | |||
+ | <sxh java> | ||
+ | public static final String ID_CONTEXTMENU_CONTRIB = " | ||
+ | public static final String ID_CONTEXTMENU = " | ||
+ | |||
+ | |||
+ | // ACHTUNG: modelService.findElements does not find the **MenuContributions**. Have to iterate. | ||
+ | // inspect the model and find the Menus | ||
+ | MMenu mmenu = null; | ||
+ | for(MMenuContribution mmc : app.getMenuContributions()){ | ||
+ | if(!mmc.getElementId().equals(ID_CONTEXTMENU_CONTRIB)){ | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | List< | ||
+ | if(!listMMenu.isEmpty()){ | ||
+ | mmenu = listMMenu.get(0); | ||
+ | } | ||
+ | break; | ||
+ | } | ||
+ | |||
+ | // assemble the menu, consider the Menu-Contributions with the same parent-Id | ||
+ | MenuManagerRenderer mmr = getMenuManagerRenderer(); | ||
+ | mmr.processContributions(mmenu, | ||
+ | </ | ||
+ | ==== UI Toolkits in Eclipse 4 ==== | ||
+ | |||
+ | ^Toolkit ^Example ^ | ||
+ | |< | ||
+ | * **TM alias Toolkit Model** - Das Toolkit Model (TM) an EMF-Modell to generate UI-Layouts. | ||
+ | * Based on **EMF** - is an Eclipse own toolkit project which __takes UML__ like model (for modeling data) and __generates java code automatically__. \\ [[http:// | ||
+ | * Based on **XMI** - is an XML based format, to save Object-metadata. It is a way to define 4th meta-level: \\ 1)Applicationmodel \\ 2)UML \\ 3)UML metamodel \\ 4)Meta-Metamodel - metamodel for UML | ||
+ | * Based on XML. | ||
+ | </ | ||
+ | |< | ||
+ | * **XWT** - Eclipse XML Window Toolkit. It takes declarative XML like models and generates __UI layouts and widgets.__ \\ During runtime the XWT reads the XML model and converts it to Java's SWT Wdgets. \\ There is a special Editor for this. | ||
+ | * Based on XML. | ||
+ | </ | ||
+ | < | ||
+ | <Shell xmlns=" | ||
+ | xmlns: | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | <Button text=" | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== CSS ==== | ||
+ | |||
+ | == Fallpits == | ||
+ | * Use // | ||
+ | * **ACHTUNG**: | ||
+ | * The widget do **NOT** have to be a **part of the e4 model** to styled successfully | ||
+ | |||
+ | ^What ^Where^ | ||
+ | |There is a cure presentation about CSS | [[http:// | ||
+ | |There is a list of CSS Tags| [[http:// | ||
+ | |Examples about using CSS Tags| [[http:// | ||
+ | |Style It! The Eclipse 4 Styling Tutorial| [[https:// | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | == adding a CSS file to the application == | ||
+ | <sxh xml> | ||
+ | <?xml version=" | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | == Enable a new look and feel (rounded corners) == | ||
+ | The howto is [[http:// | ||
+ | |||
+ | {{http:// | ||
+ | |||
+ | == Possibilities of CSS in e4 == | ||
+ | Here is a short list of customizable properties. | ||
+ | More properties are [[http:// | ||
+ | ^Java-Property ^CSS Attribute^ | ||
+ | |**Class** | maps to **Selector**| | ||
+ | |**Background** | maps to <sxh css> | ||
+ | background-color: | ||
+ | background-color: | ||
+ | background-image: | ||
+ | |border|< | ||
+ | |cursor|< | ||
+ | |font|< | ||
+ | font: italic 12 bold " | ||
+ | font-style: italic; | ||
+ | font-size: 12; | ||
+ | font-weight: | ||
+ | font-family: | ||
+ | |color|< | ||
+ | |||
+ | ^class ^ widget ^ | ||
+ | |<sxh css> | ||
+ | .MTrimBar { | ||
+ | background-color: | ||
+ | }</ | ||
+ | |<sxh css> | ||
+ | .MTrimmedWindow { | ||
+ | background-color: | ||
+ | }</ | ||
+ | |Patterns can be defined as bg, e.g. [[http:// | ||
+ | < | ||
+ | name=" | ||
+ | value=" | ||
+ | </ | ||
+ | |||
+ | }<sxh css> | ||
+ | .MTrimmedWindow { | ||
+ | background-image: | ||
+ | |||
+ | }</ | ||
+ | |It is possible to select the active(focused) tabs, by using the " | ||
+ | /* match ALL Tabs, match ALL part stacks | ||
+ | CTabItem, CTabFolder{ | ||
+ | background-color: | ||
+ | } | ||
+ | |||
+ | /* OVERRIDES TWO GENERAL RULES ABOVE */ | ||
+ | /* match Active part stack */ | ||
+ | CTabFolder.active{ | ||
+ | background-color: | ||
+ | } | ||
+ | /* match tabs in ACTIVE(focused) part stack */ | ||
+ | CTabFolder.active CTabItem{ | ||
+ | background-color: | ||
+ | } | ||
+ | /* match SELECTED tab in ACTIVE(focused) part stack */ | ||
+ | CTabFolder.active CTabItem: | ||
+ | background-color: | ||
+ | }</ | ||
+ | |||
+ | |||
+ | == Classes and ids== | ||
+ | |||
+ | <sxh java> | ||
+ | //VOGELLA | ||
+ | // IStylingEngine was injected | ||
+ | // via @Inject | ||
+ | |||
+ | Label label = new Label(parent, | ||
+ | Text text = new Text(parent, | ||
+ | |||
+ | // Set the ID, must be unique in the same window | ||
+ | IStylingEngine.setID(label, | ||
+ | |||
+ | // | ||
+ | label.setData(" | ||
+ | " | ||
+ | |||
+ | //or set class | ||
+ | label.setData(" | ||
+ | " | ||
+ | |||
+ | // Set the class, can be used several times | ||
+ | IStylingEngine.setClassname(text, | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | //ECLIPSE CON | ||
+ | |||
+ | //set class | ||
+ | WidgetElement.setCSSClass(widget, | ||
+ | |||
+ | // set id | ||
+ | WidgetElement.setID(widget, | ||
+ | </ | ||
+ | |||
+ | |||
+ | Eclipse Tags are mapped to classes in css. | ||
+ | |||
+ | < | ||
+ | dashboardPart.getTags().add(" | ||
+ | dashboardPart.getTags().add(" | ||
+ | </ | ||
+ | |||
+ | <sxh css> | ||
+ | /* match all dashboard parts: color of content pane */ | ||
+ | *[class=' | ||
+ | background-color: | ||
+ | } | ||
+ | /* match tabs of dashboard parts: color of tab */ | ||
+ | CTabItem[class=" | ||
+ | tab-color: rgb(255, | ||
+ | } | ||
+ | /* match tabs of dashboard parts in ACTIVE(focused) part stack: color of tab */ | ||
+ | CTabFolder.active CTabItem[class=" | ||
+ | tab-color: rgb(255, | ||
+ | } | ||
+ | |||
+ | /* match cashier dashboard parts: color of content pane */ | ||
+ | *[class=' | ||
+ | background-color: | ||
+ | } | ||
+ | /* match tabs of cashier dashboard parts: color of tab */ | ||
+ | CTabItem[class=" | ||
+ | tab-color: rgb(106, | ||
+ | } | ||
+ | /* match tabs of cashier dashboard parts in ACTIVE(focused) part stack: color of tab */ | ||
+ | CTabFolder.active CTabItem[class=" | ||
+ | tab-color: rgb(106, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | == PseudoElements == | ||
+ | |< | ||
+ | <sxh css> | ||
+ | Shell: | ||
+ | Text:focus { background: parchment; } | ||
+ | CTabItem: | ||
+ | CTabFolder: | ||
+ | </ | ||
+ | Psedoelements can be inspected with the css spy : \\ | ||
+ | {{http:// | ||
+ | |||
+ | |||
+ | == Extend CSS by own properties == | ||
+ | Add the following to the plugin.xml, as [[http:// | ||
+ | |||
+ | <sxh xml> | ||
+ | <!-- define the new property --> | ||
+ | < | ||
+ | < | ||
+ | composite=" | ||
+ | handler=" | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <sxh java> | ||
+ | //define a Handler | ||
+ | public class XXXPropertyHandler implements ICSSPropertyHandler { | ||
+ | public boolean applyCSSProperty(Object element, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { | ||
+ | ... | ||
+ | } | ||
+ | | ||
+ | public String retrieveCSSProperty(Object widget, String pseudo, CSSEngine engine) throws Exception { | ||
+ | return ...; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ==== Scripting ==== | ||
+ | |||
+ | The Model can be edited live by using the Live Editor, see [[eclipse: | ||
+ | Import the necessary plugins and use **ALT+Shift+F9** in your application. | ||
+ | |||
+ | |||
+ | {{http:// | ||
+ | |||
+ | ^Objects ^Methods ^Describtion ^ | ||
+ | |mainObject| All members of the element, chosen in Live Editor| The most importan Object| | ||
+ | |log| debug(Objects) \\ error(Object)| Logger Object | | ||
+ | |swt| newColor(Color) \\ newText(Composite, | ||
+ | |eclipseContext| get(String name) | here you can enter the current EclipseObject | | ||
+ | |service| getStyleEngine() \\ getPartService() \\ getModelService()| Can get Services | | ||
+ | |di| newInstance(String, | ||
+ | |||
+ | **Example: | ||
+ | <sxh javascript> | ||
+ | mainObject.getWidget().setBackground(swt.newColor("# | ||
+ | var meinpart = service.getModelService().find(" | ||
+ | var meinuri = meinpart.getContributionURI(); | ||
+ | log.debug(meinuri); | ||
+ | </ | ||
+ | ==== Model Processor ==== | ||
+ | |||
+ | <sxh java> | ||
+ | package de.mine.experiments.modelprocessor.processors; | ||
+ | |||
+ | import javax.inject.Inject; | ||
+ | |||
+ | import org.eclipse.e4.core.di.annotations.Execute; | ||
+ | import org.eclipse.e4.ui.model.application.MApplication; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MWindow; | ||
+ | |||
+ | /** | ||
+ | * A processor, registerd in extension Point: org.eclipse.e4.workbench.model | ||
+ | * Changes the existing model, as soon as the bundle , in which this processor is registered is loaded. | ||
+ | | ||
+ | * @author skip | ||
+ | * | ||
+ | */ | ||
+ | public class Processor { | ||
+ | |||
+ | @Inject | ||
+ | MApplication application; | ||
+ | |||
+ | @Execute | ||
+ | public void execute(){ | ||
+ | MWindow existingWindow = application.getChildren().get(0); | ||
+ | MTrimmedWindow newWindow = MBasicFactory.INSTANCE.createTrimmedWindow(); | ||
+ | |||
+ | existingWindow.setX(300); | ||
+ | existingWindow.setY(300); | ||
+ | |||
+ | newWindow.setWidth(200); | ||
+ | newWindow.setHeight(existingWindow.getHeight()); | ||
+ | |||
+ | newWindow.setY(existingWindow.getY()); | ||
+ | newWindow.setX(existingWindow.getX()+existingWindow.getWidth()); | ||
+ | |||
+ | application.getChildren().add(newWindow); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <sxh xml> | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | | ||
+ | < | ||
+ | beforefragment=" | ||
+ | class=" | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ==== Persistence ==== | ||
+ | |||
+ | ==Model== | ||
+ | |||
+ | The Model changes are persisted into the file\\ | ||
+ | < | ||
+ | ...runtime-EclipseApplication\.metadata\.plugins\org.eclipse.e4.workbench\workbench.xmi | ||
+ | </ | ||
+ | |||
+ | ==Arguments== | ||
+ | Run configurations can have different, persistence relevant arguments: | ||
+ | ^Argument ^ Meaning ^ | ||
+ | |**-clearPersistedState**|< | ||
+ | |**-persistState**|< | ||
+ | |||
+ | |||
+ | In code you can check, whether the -clearPersistedState | ||
+ | <sxh> | ||
+ | @Inject | ||
+ | @Named(E4Workbench.CLEAR_PERSISTED_STATE) | ||
+ | private boolean clearPersistedState; | ||
+ | </ | ||
+ | |||
+ | ==@PersistState== | ||
+ | |||
+ | To trigger persistence you should use the annotation **@PersistState**, | ||
+ | ==== TAGS ==== | ||
+ | The Tags are defined and explained inside of the Interface **org.eclipse.e4.ui.workbench.IPresentationEngine** | ||
+ | The following Tags can be added to the Parts: | ||
+ | < | ||
+ | NoTitle - hide part title | ||
+ | NoClose - prevents parts from being closed (no lcose button) | ||
+ | NoAutoCollapse - prevents MPartStack from autocollapse, | ||
+ | NoMove - prevents parts from being dragged out from stack | ||
+ | Minimized | ||
+ | Maximized | ||
+ | MinimizedByZoom | ||
+ | Horizontal | ||
+ | Vertical | ||
+ | e4_disabled_icon_image_key | ||
+ | e4_override_icon_image_key | ||
+ | e4_override_title_tool_tip_key | ||
+ | Animations Enabled | ||
+ | </ | ||
+ | |||
+ | ==== OSGI ==== | ||
+ | === ClassLoaders ==== | ||
+ | Every Eclipse Bundle has an own class loader. | ||
+ | The mechanism is described here: http:// | ||
+ | |||
+ | === Logging in OSGI ==== | ||
+ | |||
+ | **Achtung** | ||
+ | On default there already are OSGI Listeners, which redirect the Exceptions to STDout. \\ | ||
+ | So e.g. OSGI Messaging (EventBroker) Exceptions should be visible in the Eclipse Console and Log. \\ | ||
+ | But sometimes the Exception-messages just do not appear there, until Eclipse is restarted. | ||
+ | |||
+ | |Source: | https:// | ||
+ | |||
+ | OSGI implements a Fascade, behind which any framework like log4j may be used. | ||
+ | This is done, because logging is done per Bundle, which is not possible with other logging frameworks. | ||
+ | |||
+ | There are **LogService** and **LogReaderService** which may be retrieved inside the **Activator**. | ||
+ | Listeners may be installed to the LogReaderService. | ||
+ | |||
+ | Here is the summary of what is done below: | ||
+ | |||
+ | - you retrieve the **LogReaderService** from the Platform | ||
+ | - you add an own **LogService** | ||
+ | - inside the LogService you can log int oyour own file (e.g. using SLF4j) | ||
+ | |||
+ | |||
+ | The OSGI Platform will do the following: | ||
+ | - Some event will occur, e.g. because of an Exception | ||
+ | - The Eception will be bundled into an **LogEntry** | ||
+ | - The class **LogTracker** will be triggered | ||
+ | - LogTracker will pass the events on to platform services **org.osgi.service.log.LogService** and **org.eclipse.equinox.log.ExtendedLogService** (extends LogReaderService) | ||
+ | - the platform service org.eclipse.equinox.log.ExtendedLogService will pass the event to your **LogService** | ||
+ | |||
+ | <sxh java> | ||
+ | |||
+ | import org.osgi.service.log.LogEntry; | ||
+ | import org.osgi.service.log.LogListener; | ||
+ | import org.osgi.service.log.LogService; | ||
+ | import org.slf4j.Logger; | ||
+ | import org.slf4j.LoggerFactory; | ||
+ | |||
+ | public class OSGILogListenerImpl implements LogListener { | ||
+ | |||
+ | private static final Logger LOG = LoggerFactory.getLogger(OSGILogListenerImpl.class); | ||
+ | |||
+ | @Override | ||
+ | public void logged(LogEntry entry) { | ||
+ | if (entry.getMessage() != null) { | ||
+ | switch (entry.getLevel()) { | ||
+ | case LogService.LOG_ERROR: | ||
+ | LOG.error(" | ||
+ | break; | ||
+ | case LogService.LOG_WARNING: | ||
+ | LOG.warn(" | ||
+ | break; | ||
+ | case LogService.LOG_DEBUG: | ||
+ | LOG.debug(" | ||
+ | break; | ||
+ | case LogService.LOG_INFO: | ||
+ | LOG.info(" | ||
+ | break; | ||
+ | |||
+ | default: | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | import org.osgi.framework.BundleActivator; | ||
+ | import org.osgi.framework.BundleContext; | ||
+ | import org.osgi.framework.ServiceReference; | ||
+ | import org.osgi.service.log.LogReaderService; | ||
+ | |||
+ | /** | ||
+ | * This class should be registerd as the Bundle Activator for a bundle, which is loaded early (low **osgi start level**) | ||
+ | */ | ||
+ | public class OSGILogActivator implements BundleActivator { | ||
+ | |||
+ | private OSGILogListenerImpl osgiLogListener = new OSGILogListenerImpl(); | ||
+ | |||
+ | @SuppressWarnings({" | ||
+ | private LogReaderService getReaderService(BundleContext context) { | ||
+ | LogReaderService readerService = null; | ||
+ | ServiceReference readerRef = context.getServiceReference(LogReaderService.class.getName()); | ||
+ | if (readerRef != null) { | ||
+ | readerService = (LogReaderService) context.getService(readerRef); | ||
+ | } | ||
+ | return readerService; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void start(BundleContext context) throws Exception { | ||
+ | LogReaderService readerService = getReaderService(context); | ||
+ | if (readerService != null) { | ||
+ | readerService.addLogListener(osgiLogListener); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void stop(BundleContext context) throws Exception { | ||
+ | LogReaderService readerService = getReaderService(context); | ||
+ | if (readerService != null) { | ||
+ | readerService.removeLogListener(osgiLogListener); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | In Eclipse you should use **ExtendedLogReaderService** to register to platform events, like errors on initialization of bundles. | ||
+ | |||
+ | {{http:// | ||
+ | The listener-Bundle should be loaded as early as possible, in an own bundle which has a low **osgi start level** | ||
+ | This setting can be set within the runtim configuration or the product configuration. | ||
+ | {{http:// | ||
+ | |||
+ | ==== Fragments ==== | ||
+ | A plugin may contribute any element to the **Application.e4xml** | ||
+ | - Create a Model Fragment (every one may contribute to one part of Model-Element) | ||
+ | - Featurename is the name of the element, how it is called inside ApplicationModel.e4xml: | ||
+ | |||
+ | {{http:// | ||
+ | {{http:// | ||
+ | |||
+ | ==== Startup faster ==== | ||
+ | Add following to the VM arguments | ||
+ | * in product as VM Argument -Dequinox.scr.waitTimeOnBlock=1 | ||
+ | * as runtime parameter: -vmargs -Dequinox.scr.waitTimeOnBlock=1 | ||
+ | * in Runtime configuration as VM Argument: -Dequinox.scr.waitTimeOnBlock=1 | ||
+ | < | ||
+ | equinox.scr.waitTimeOnBlock=1 | ||
+ | </ | ||
+ | |||
+ | ==== ToolControl in Window Trim ==== | ||
+ | Icons in the Toolbar are difficult to center. | ||
+ | They are not created as children of Toolbar but as children of Composites, which are children of the ToolBar. | ||
+ | |||
+ | To set a fixed size to the composite and to center wom widget, like progressbar within do: | ||
+ | |||
+ | <sxh java> | ||
+ | |||
+ | @PostComposite | ||
+ | public void createWidget(Composite parent){ | ||
+ | // centering nested composite | ||
+ | Composite centering = new Composite(parent, | ||
+ | centering.setBounds(0, | ||
+ | centering.setLayout(new GridLayout()); | ||
+ | // centering.setLayoutData(new GridData(SWT.CENTER, | ||
+ | centering.setLayout(new GridLayout(1, | ||
+ | |||
+ | // progressbar | ||
+ | progressBar = new ProgressBar(centering, | ||
+ | progressBar.setMaximum(progressBar.getMaximum()); | ||
+ | GridData gd_progressBar = new GridData(SWT.CENTER, | ||
+ | gd_progressBar.widthHint = 60; | ||
+ | progressBar.setLayoutData(gd_progressBar); | ||
+ | |||
+ | // listener | ||
+ | ToolItemSelectedListener listener = new ToolItemSelectedListener(); | ||
+ | |||
+ | // cursor | ||
+ | centering.setCursor(cursor); | ||
+ | |||
+ | // do the logic | ||
+ | centering.addMouseListener(listener); | ||
+ | progressBar.addMouseListener(listener); | ||
+ | |||
+ | // tooltip here | ||
+ | String toolTip = MessagesUtils.getNlsMessage(Messages.class, | ||
+ | progressBar.setToolTipText(toolTip); | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== Platform Files ==== | ||
+ | |||
+ | |**platform.xml**|< | ||
+ | |||
+ | In your Eclipse configuration directory in the org.eclipse.update folder, there should be a file named platform.xml. This is the file that the Update Configurator uses to determine the rest of the bundles that are known to your system. | ||
+ | |||
+ | The file is organized into sites. You should see a site with the location of platform:/ | ||
+ | |||
+ | If your bundle exists in an enabled site that is in the platform.xml file, then everything should be ok. Note that the sites can list either features or plug-ins so you may need to know which feature your plug-in belongs to, in order to figure out if it should be known. See: https:// | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Eclipse RCP Project with Maven dependencies==== | ||
+ | Maven dependencies can not be added to Eclipse RCP projects driectly. | ||
+ | It is possible to create a special RCP projects, which would resolve Maven dependencies automatically and share them with other plugins, which depend on them. | ||
+ | |||
+ | Several things have to be done by using **maven-felix-plugin** | ||
+ | * add jars to **classpath** | ||
+ | * **export** all packages | ||
+ | * set **Eclipse-BundlePolicy** to **global** (means that missing Classes will be looked up in all available packages) | ||
+ | * set **Bundle-ActivationPolicy** to **lazy** | ||
+ | |||
+ | |||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | | ||
+ | <!-- Add maven dependencies here --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | <!-- Make maven-bundle-plugins share all maven depenencies by adding jars to classpath and exporting all packages --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | |||
+ | </ | ||
+ | | ||
+ | </ | ||
+ | </ | ||
+ | | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Versioning ==== | ||
+ | |||
+ | Eclipse bundles and features have version numbers of the form major.minor.micro.qualifier | ||
+ | |||
+ | < | ||
+ | 1.0.0.SNAPSHOT | ||
+ | 15.1.0.FINAL | ||
+ | </ | ||
+ | |||
+ | Source: http:// | ||
+ | |||
+ | You can modify the qualifier by different plugins. | ||
+ | E.g. tycho-packaging-plugin modifies it for tycho builds: | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | makes the qualifier to be the following **after deploy** (**not inside the target folder**) | ||
+ | |||
+ | < | ||
+ | 1.0.0.revvodoo1-20150906-2225 | ||
+ | </ | ||
+ | ====== UnitTesting ====== | ||
+ | |||
+ | Read here about Jubula, Q7: [[: | ||
+ | |||
+ | |||
+ | ====== Examples by code ====== | ||
+ | |||
+ | |||
+ | ===== Creating a new Part ===== | ||
+ | |||
+ | Add the following POJO into the Class URI field inside of Application.e4xml to create a new part. | ||
+ | |||
+ | {{http:// | ||
+ | |||
+ | <sxh java> | ||
+ | import javax.annotation.PostConstruct; | ||
+ | import javax.inject.Inject; | ||
+ | |||
+ | import org.eclipse.e4.ui.di.Focus; | ||
+ | import org.eclipse.swt.SWT; | ||
+ | import org.eclipse.swt.layout.FillLayout; | ||
+ | import org.eclipse.swt.widgets.Button; | ||
+ | import org.eclipse.swt.widgets.Composite; | ||
+ | |||
+ | public class PartSwt { | ||
+ | |||
+ | // @Inject annotated constructor will be used to create the Part | ||
+ | @Inject | ||
+ | public PartSwt(){ | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | // will be called with, to fill the new part with content | ||
+ | @Inject | ||
+ | @PostConstruct | ||
+ | void onCreate(Composite partCanvas){ | ||
+ | partCanvas.setLayout(new FillLayout()); | ||
+ | Button b = new Button(partCanvas, | ||
+ | b.setText(" | ||
+ | partCanvas.pack(); | ||
+ | } | ||
+ | |||
+ | // will be called every time, when the new part retrieves focus | ||
+ | @Focus | ||
+ | void onFocus(){ | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | ===== Manipulate the GUI programmatically ===== | ||
+ | |||
+ | <sxh java> | ||
+ | package de.vogella.e4.rcp.wizard.handlers; | ||
+ | |||
+ | import java.util.ArrayList; | ||
+ | import java.util.Calendar; | ||
+ | import java.util.List; | ||
+ | |||
+ | import javax.inject.Inject; | ||
+ | import javax.inject.Named; | ||
+ | |||
+ | import org.eclipse.e4.core.contexts.IEclipseContext; | ||
+ | import org.eclipse.e4.core.di.annotations.Execute; | ||
+ | import org.eclipse.e4.ui.model.application.MApplication; | ||
+ | import org.eclipse.e4.ui.model.application.commands.MHandler; | ||
+ | import org.eclipse.e4.ui.model.application.ui.MUIElement; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MPart; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MPartStack; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow; | ||
+ | import org.eclipse.e4.ui.model.application.ui.basic.MWindow; | ||
+ | import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem; | ||
+ | import org.eclipse.e4.ui.model.application.ui.menu.MMenu; | ||
+ | import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; | ||
+ | import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; | ||
+ | import org.eclipse.e4.ui.workbench.modeling.EModelService; | ||
+ | import org.eclipse.e4.ui.workbench.modeling.EPartService; | ||
+ | import org.eclipse.swt.widgets.Display; | ||
+ | import org.eclipse.swt.widgets.MenuItem; | ||
+ | |||
+ | public class ManipulateModelhandler { | ||
+ | |||
+ | /* | ||
+ | * Application representation. | ||
+ | * Represents the top level EMF Container, entrypoint for getting all the EMF-Model Data: | ||
+ | * - Addons | ||
+ | * - Binding Context | ||
+ | * - Binding Tables | ||
+ | * - Handlers | ||
+ | * ... | ||
+ | | ||
+ | * Can retrieve active parts and everything, what is related to the model. | ||
+ | */ | ||
+ | @Inject | ||
+ | private MApplication mapplication; | ||
+ | | ||
+ | /* | ||
+ | * Service, responsible for | ||
+ | * - finding the parts of the EMF Model by Tag, Id, Class | ||
+ | * - manipulating the EMF Model, bringing them to the top etc. | ||
+ | */ | ||
+ | @Inject | ||
+ | private EModelService eModelService; | ||
+ | |||
+ | //service, responsible for manipulating the model | ||
+ | @Inject | ||
+ | private EPartService ePartService; | ||
+ | | ||
+ | |||
+ | @Execute | ||
+ | private void execute() { | ||
+ | |||
+ | |||
+ | //1. mapplication : FINDING APPLICATION DATA CONTRIBUTED IN Application.e4xml | ||
+ | mapplication.getHandlers(); | ||
+ | mapplication.getRootContext(); | ||
+ | mapplication.getSelectedElement(); | ||
+ | mapplication.getToolBarContributions(); | ||
+ | mapplication.getMenuContributions(); | ||
+ | |||
+ | System.out.println(" | ||
+ | for(MHandler handler : mapplication.getHandlers()){ | ||
+ | handler.getCommand().getCommandName(); | ||
+ | System.out.println(" | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | //2. eModelService : FINDING MODEL PARTS - use with MUIElement, MWindow | ||
+ | |||
+ | // Find objects by ID | ||
+ | List< | ||
+ | System.out.println(" | ||
+ | |||
+ | |||
+ | // Find objects by type | ||
+ | List< | ||
+ | System.out.println(" | ||
+ | |||
+ | |||
+ | // Find objects by tags | ||
+ | List< | ||
+ | tags.add(" | ||
+ | List< | ||
+ | System.out.println(" | ||
+ | |||
+ | // Get the MWindow and change its size | ||
+ | List< | ||
+ | MWindow mWindow = windows.get(0); | ||
+ | mWindow.setWidth(mWindow.getWidth()-10); | ||
+ | |||
+ | |||
+ | |||
+ | /* ATTENTION: RootContext contains is the context, in which the commands can be activated, | ||
+ | * like " | ||
+ | // | ||
+ | |||
+ | IEclipseContext iEclipseContext1 = mapplication.getContext(); | ||
+ | IEclipseContext iEclipseContext2 = eModelService.getActivePerspective(mWindow).getContext(); | ||
+ | |||
+ | //get MTrimmedWindow from the context or by eModelService. Injecting MTrimmedWindow will fail | ||
+ | MTrimmedWindow mTrimmedWindow1 = iEclipseContext1.get(MTrimmedWindow.class); | ||
+ | MTrimmedWindow mTrimmedWindow2 = iEclipseContext2.get(MTrimmedWindow.class); | ||
+ | MTrimmedWindow mTrimmedWindow3 = eModelService.findElements(mapplication, | ||
+ | |||
+ | |||
+ | |||
+ | //3. eModelService : MANIPULATING MODEL PARTS - use with MUIElement, MWindow | ||
+ | |||
+ | //find " | ||
+ | MMenu mMenuDemo = null; | ||
+ | for( MMenuElement menu : mTrimmedWindow2.getMainMenu().getChildren()){ | ||
+ | System.out.println(" | ||
+ | |||
+ | if(menu.getElementId()!= null && menu.getElementId().equals(" | ||
+ | mMenuDemo = (MMenu) menu; | ||
+ | }else{ | ||
+ | ((MUIElement)menu).setToBeRendered(false); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | //EDIT: adding menu entries doesn' | ||
+ | //add an Exit menu-Entry | ||
+ | if(mMenuDemo != null){ | ||
+ | MDirectMenuItem newExitItem = MMenuFactory.INSTANCE.createDirectMenuItem(); | ||
+ | newExitItem.setLabel(" | ||
+ | //ACHTUNG: not setContributORURI!!!! | ||
+ | newExitItem.setContributionURI(" | ||
+ | |||
+ | mMenuDemo.getChildren().add(newExitItem); | ||
+ | |||
+ | newExitItem.setVisible(true); | ||
+ | newExitItem.setToBeRendered(true); | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | //moving parts around | ||
+ | MUIElement partPlayground = eModelService.find(" | ||
+ | MUIElement partDetails = eModelService.find(" | ||
+ | |||
+ | //move the part to a new parent | ||
+ | if (partPlayground.getParent() != partDetails.getParent()){ | ||
+ | // | ||
+ | eModelService.move(partPlayground, | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | //3. MBasicFactory : CREATING MODEL PARTS | ||
+ | MBasicFactory.INSTANCE.createTrimmedWindow(); | ||
+ | MBasicFactory.INSTANCE.createTrimBar(); | ||
+ | MBasicFactory.INSTANCE.createPart(); | ||
+ | MBasicFactory.INSTANCE.createWindow(); | ||
+ | MBasicFactory.INSTANCE.createPartStack(); | ||
+ | MBasicFactory.INSTANCE.createPartSashContainer(); | ||
+ | |||
+ | //create a new Part, attach it near playground | ||
+ | MPart newMPart = MBasicFactory.INSTANCE.createPart(); | ||
+ | newMPart.setLabel(" | ||
+ | newMPart.setParent(partPlayground.getParent()); | ||
+ | |||
+ | //create a new Window | ||
+ | MWindow mWindow = MBasicFactory.INSTANCE.createWindow(); | ||
+ | mapplication.getChildren().add(mWindow); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | private void wait(int sec){ | ||
+ | int start = Calendar.getInstance().get(Calendar.SECOND); | ||
+ | |||
+ | while(Calendar.getInstance().get(Calendar.SECOND) - start <sec){ | ||
+ | Display.getCurrent().readAndDispatch(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Create a new MWindow, with a custom Shell ===== | ||
+ | The Shell for the MWindow is created by the **WBWRenderer.class** in context of the window. | ||
+ | Here we inject a custom, extended WBWRenderer into the context. | ||
+ | |||
+ | <sxh java> | ||
+ | MWindow mWindow = MBasicFactory.INSTANCE.createWindow(); | ||
+ | app.getChildren().add(mWindow); | ||
+ | mWindow.getTags().add(WBWRendererCustomTags.WINDOWTAG_CLOSERESIZE); | ||
+ | |||
+ | mWindow.setRenderer(new WBWRendererCustomTags()); | ||
+ | Shell s = (Shell) mWindow.getWidget(); | ||
+ | |||
+ | stylingEngine.setClassname(s, | ||
+ | stylingEngine.setId(s, | ||
+ | if (cssClassName != null) { | ||
+ | stylingEngine.setClassname(s, | ||
+ | } | ||
+ | |||
+ | for (Control c : s.getChildren()) { | ||
+ | c.setVisible(false); | ||
+ | } | ||
+ | s.pack(); | ||
+ | return s; | ||
+ | </ | ||
+ | |||
+ | ===== Loading Image from Bundle ===== | ||
+ | |||
+ | <sxh java> | ||
+ | Label labelMessages = new Label(parent, | ||
+ | |||
+ | Bundle bundle = FrameworkUtil.getBundle(ControlTrimbarMessages.class); | ||
+ | URL fileURLImage = bundle.getEntry("/ | ||
+ | |||
+ | URL fileUrl = null; | ||
+ | try { | ||
+ | fileUrl = FileLocator.toFileURL(fileURLImage); | ||
+ | } catch (IOException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | |||
+ | Image image = ImageDescriptor.createFromURL(fileUrl).createImage(); | ||
+ | labelMessages.setImage(image); | ||
+ | </ | ||
+ | ====== FAQ ====== | ||
+ | |||
+ | < | ||
+ | - Welche klassen haben einen Kontext? | ||
+ | -> die meissten model Klasen haben einen context | ||
+ | - wie ist die Kontext hierarchie? Ist es die Gleiche, wie im EMF Model? Context vom Parent Window ist das Parent-kontext von unterwindows? | ||
+ | -> in einem der Tutorials gibts eine uebersicht. | ||
+ | - wie kann man ein MWindow entfernen? (dispose) | ||
+ | -> man kann auf das zugrundeliegende Widget zugreifen. Dieses muss man casten und disposen. | ||
+ | - muss man einen Kontext initialisieren? | ||
+ | -> ja , neu erstellte Model-Objekte haben einen null-Context | ||
+ | </ |
eclipse/eclipse_rcp.1468566988.txt.gz · Last modified: (external edit)