Skip to content

Mediaplayer Tutorial

semanticsoft edited this page Jan 11, 2013 · 6 revisions

#About In this tutorial we start creating the first Vaaclipse project - the Mediaplayer Demo. You will create step by step mymediaplayer project that exactly matches to original mediaplayer demo.

Creating application project

Select "File" → "New" → "Other...". In the "New" dialog find Eclipse 4 group and select Eclipse 4 Vaadin Application Project (this wizard would be enabled only if you have installed E4 Tooling and Vaaclipse Eclipse plugins as explained in the Getting Started guide).

Type the project name: org.semanticsoft.vaaclipsedemo.mymediaplayer. You can also choose some working set (I have selected the vaaclipse working set). Complete 3 wizard steps to create mymediaplayer project:

First step

Second step

Third step

After finishing these steps the project org.semanticsoft.vaaclipsedemo.mymediaplayer will be created. Now open product file org.semanticsoft.vaaclipsedemo.mymediaplayer.product and click the link "Launch an Eclipse application" or "Launch an Eclipse application in Debug mode" in Testing section of Overview tab. Wait until the swing window "Vaaclipse Server" appears. Then open your browser and follow the link http://localhost:8080/mymediaplayer. You see the login page, just click on login button to login to created demo. You see the simple default application generated for you by project wizard. It consists of menu, toptrimbar with single toolbar and the main area spliting on two parts. Left partstack contains one part called "First part" and right partstack contains "Second part".

Model editor

Now go to the project and open file Application.e4xmi. This file contains the model of application that you have seen. You use the Model Editor to edit the model. The left part of Model Editor contains the hierarchy of model elements, the right part - the selected model element's property editor. The Windows section contains the list of windows presented in application. Our application has one window with label "org.semanticsoft.vaaclipsedemo.mediaplayer". Expand the Window entry. You see the Main Menu entry containing the windows's menu, TrimBars entry containing the list of trimbars presented in this window (now there is one trimbar - top trimbar) and Controls section containing Perspective Stack with single perspective. This perspective contains PartSashContainer element with orientation = "Horizontal" splitting the area in horizontal direction. PartSashContainer is similar to vaadin split panels, but can contain more than two childs. Given PartSashContainer has two childs - two part stacks that you see in demo. Each part stack contains one child part. Part stacks are similar to vaadin TabSheet.

Now terminate demo by pressing Shutdown button on server window (or using eclipse) and select PartSashContainer item. Change the Orientation property to Vertical and start demo again. You see that the main area is splitted in vertical direction. Restore previous value of Orientation property (Horizontal). Select the first child PartStack in model editor and change the Container Data property to 30. Select the second child PartStack in model editor and change the Container Data property to 70. Save model and restart application. So Container Data property of PartSashContainer's children contains the weight of childs.

Now select the Trimmed Window and open Supplementary tab. You see the "mainWindow" tag added to this element. The window containing this tag becomes the main window in vaadin terms, i.e. it is stretched on entire page. The application should contain at least one main window.

#Starting creation the application model Copy org.semanticsoft.vaaclipsedemo.mediaplayer/icons folder to org.semanticsoft.vaaclipsedemo.mymediaplayer. Now we can use these icons in our application.

Select Perspective element in model editor and set property values: Edit perspective

You may restart demo and detect changes in perspective button.

Now create 4 empty java classes:

  • org.semanticsoft.vaaclipsedemo.mymediaplayer.views.PlayerView
  • org.semanticsoft.vaaclipsedemo.mymediaplayer.views.MediaLibraryView
  • org.semanticsoft.vaaclipsedemo.mymediaplayer.views.PlaylistView
  • org.semanticsoft.vaaclipsedemo.mymediaplayer.views.MediaInfoView

View classes

Do right click on Shared Elements section of TrimmedWindow and select Add Part.

Add part

Edit created part as in the screenshot below.

Edit player part

We created Player part. Repeate this procedure 3 times and create Media Library, Media Info, Playlist parts.

Edit media library part

Edit media info part

Edit playlist part

Do right click on Complex perspective Controls section and add two PartSashContainer elements.

Add PartSashContainer

Then add PartStack element.

Add PartStack

Edit created elements:

Edit first PartSashContainer

Edit second PartStackContainer

Edit PartStack

Add two children PartStack elements to the first PartSashContainer element (...partsashcontainer.0). Change the properties of added stacks:

Edit PartStack

Edit PartStack

Add one children PartStack element to the second PartStackContainer element (...partsashcontainer.1).

Now there are 4 PartStack elements in Complex perspective. Add exactly one Placeholder element to each PartStack element:

Edit PartStack

Each placeholder element should reference the shared part, so you should have 4 placeholder refrencing 4 shared parts. Using Find button set the Reference property in a placeholder element properties editor page to relevant shared part.

Edit PartStack

Select relevant part from list and press OK. The Reference property of Placeholder element now points the selected part.

After peforming all operations the Complex perspective Controls section should look like screenshot below:

Complex perspective controls

Run mymediaplayer application. If you've done everything correctly, you should see this screen:

Mymediaplayer application

Domain model

It's time to write some code. First we need to implement the domain model of our application. Copy packages org.semanticsoft.vaaclipsedemo.mediaplayer.model and org.semanticsoft.vaaclipsedemo.mediaplayer.service from mediaplayer demo as packages org.semanticsoft.vaaclipsedemo.mymediaplayer.model and org.semanticsoft.vaaclipsedemo.mymediaplayer.service to mymediaplayer demo. Look at the domain model. It is very simple. There are two entities - Media and MediaCategory that share the same interface MediaEntry. Media represents any type of remote media, MediaCategory is a collection of Media (pattern Composite).

We need to setup the model. The good place for this operation is processor. Create class org.semanticsoft.vaaclipsedemo.mymediaplayer.processors.MediaLibrarySetupProcessor with content:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.processors;

import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Execute;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaCategory;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaLibrary;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Playlist;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.service.MediaService;

public class MediaLibrarySetupProcessor
{
	@Execute
	public void setup(IEclipseContext context)
	{
		MediaLibrary library = new MediaLibrary();
		
		//Eclipse
		MediaCategory eclipse = new MediaCategory();
		eclipse.setName("Eclipse");
		
		Media eclipseIntro = new Media();
		eclipseIntro.setName("Introduction into Eclipse E4");
		eclipseIntro.setUri("http://www.youtube.com/v/hAGhny7bcCs");
		eclipseIntro.setDescription("San Francisco Java User Group hosted an event on April 13th, 2010 with Lars Vogel, " +
				"a committer on the Eclipse e4 project, who gave a talk on the next generation of the Eclipse Platform. " +
				"We had two speakers that evening, this is the first of the two presentations.");
		eclipse.addMedia(eclipseIntro);
		
		//Vaadin
		MediaCategory vaadin = new MediaCategory();
		vaadin.setName("Vaadin");
		
		Media vaadinIntro = new Media();
		vaadinIntro.setName("Introduction into vaadin");
		vaadinIntro.setUri("http://www.youtube.com/v/W-mp5E-T88o");
		vaadinIntro.setDescription("The video describes vaadin idea: rich web applications in plain Java without plugins or JavaScript");
		vaadin.addMedia(vaadinIntro);
		
		library.addCategory(eclipse);
		library.addCategory(vaadin);
		
		context.set(MediaLibrary.class, library);
		context.set(Playlist.class, new Playlist());
                MediaService mediaService = ContextInjectionFactory.make(MediaService.class, context);
		context.set(MediaService.class, mediaService);
	}
}

In code above we create MediaLibrary instance with sample content and set it to context:

context.set(MediaLibrary.class, library);

Also we add to context Playlist instance and MediaService instance. Pay attention to the way the instance of MediaService created:

MediaService mediaService = ContextInjectionFactory.make(MediaService.class, context);

We use ContextInjectionFactory to create MediaService instance because we want some dependencies from context be injected into MediaService object. Open MediaService class and take a look on a mediaLib field. It is annotated with javax.inject.Inject annotation:

public class MediaService
{
	@Inject
	MediaLibrary mediaLib;
        //...
}

ContextInjectionFactory inject the MediaLibrary instance that we set to context in couple lines of code above in MediaService instance. So we avoid directly configuring MediaService instance. This was the example of Eclipse 4 dependency injection.

So our model will be created and injected into context if MediaLibrarySetupProcessor.setup method is called. It should be called on start of user workbench (i.e. in each user session as well as each user has own model instance). How to tell Eclipse 4 to call setup method on workbench start? It is very easy to do with processors. Open plugin.xml and go to Extensions tab. Press Add button and in extension point filter field type org.eclipse.e4.workbench.model.

Select extension point

Press Finish. The extension point org.eclipse.e4.workbench.model is added to the list. Call the context menu on the added extension point and select New -> processor.

extpoint-add-processor

Select the added processor entry and in Extension Element Details area press Browse button and select our MediaLibrarySetupProcessor class.

Media Library processor

Now the MediaLibrary will be created on workbench start and we can access it using dependency injection.

#Media Library part OK, we have instantiated and accessible model. It is time to display this model to user. We already have created MediaLibrary part, but it is empty. Let's fill it with some content. Open MediaLibraryView and fill it with code below:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.views;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.core.contexts.IEclipseContext;
import org.semanticsoft.vaaclipse.publicapi.resources.BundleResource;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaCategory;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaLibrary;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.service.MediaService;

import com.vaadin.data.Container;
import com.vaadin.data.Container.Hierarchical;
import com.vaadin.data.Item;
import com.vaadin.data.util.HierarchicalContainer;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.Panel;
import com.vaadin.ui.Tree;
import com.vaadin.ui.Tree.TreeDragMode;
import com.vaadin.ui.VerticalLayout;

public class MediaLibraryView {
	
	private static final String NAME_PROP = "name";
	private static final String ICON_PROP = "icon";
	private static final String OBJECT_PROP = "object";
	
	private Panel panel;
	private Tree tree;

	@Inject
	MediaLibrary mediaLibrary;
	
	@Inject
	MediaService mediaService;
	
	private HierarchicalContainer container;
	
	@PostConstruct
	public void postConstruct(VerticalLayout parent)
	{
		panel = new Panel();
		panel.setSizeFull();
		parent.addComponent(panel);
		
		createMediaLibraryTree();
	}
	
	private void createMediaLibraryTree()
	{
		tree = new Tree();
		tree.setDragMode(TreeDragMode.NODE);
		tree.setSizeFull();
		tree.setImmediate(true);
		panel.addComponent(tree);

		container = createMediaLibraryDataSource();
		tree.setContainerDataSource(container);
		
		// Set tree to show the 'name' property as caption for items
		tree.setItemCaptionPropertyId(NAME_PROP);
		tree.setItemIconPropertyId(ICON_PROP);
		
		// Expand whole tree
		for (Object id : tree.rootItemIds())
		{
			tree.expandItemsRecursively(id);
		}
	}
		
	private HierarchicalContainer createMediaLibraryDataSource()
	{
		HierarchicalContainer container = new HierarchicalContainer();
		container.addContainerProperty(NAME_PROP, String.class, "No Name");
		container.addContainerProperty(ICON_PROP, ThemeResource.class, null);
		container.addContainerProperty(OBJECT_PROP, Object.class, null);
		fillContainer(mediaLibrary, null, "", container);
		return container;
	}
	
	private void fillContainer(MediaCategory category, Item categoryItem, String categoryPath, HierarchicalContainer container)
	{
		for (MediaCategory childCategory : category.getCategories())
		{
			String childCategoryPath = categoryPath + "/" + childCategory.getName();
			Item childCategoryItem = container.addItem(childCategoryPath);
			if (!(category instanceof MediaLibrary))
				container.setParent(childCategoryPath, categoryPath);
			setupCategory(childCategory, childCategoryItem);
			fillContainer(childCategory, childCategoryItem, childCategoryPath, container);
		}
		
		for (Media media : category.getMediaList())
		{
			String mediaPath = categoryPath + "/" + media.getUri();
			Item mediaItem = container.addItem(mediaPath);
			if (!(category instanceof MediaLibrary))
				container.setParent(mediaPath, categoryPath);
			container.setChildrenAllowed(mediaPath, false);
			setupItem(media, mediaItem);
		}
	}

	private void setupCategory(MediaCategory childCategory, Item childCategoryItem)
	{
		childCategoryItem.getItemProperty(NAME_PROP).setValue(childCategory.getName());
		childCategoryItem.getItemProperty(ICON_PROP).setValue(BundleResource.valueOf("platform:/plugin/org.semanticsoft.vaaclipsedemo.mymediaplayer/icons/mediacategory.png"));
		childCategoryItem.getItemProperty(OBJECT_PROP).setValue(childCategory);
	}

	private void setupItem(Media media, Item mediaItem)
	{
		mediaItem.getItemProperty(NAME_PROP).setValue(media.getName());
		mediaItem.getItemProperty(ICON_PROP).setValue(BundleResource.valueOf("platform:/plugin/org.semanticsoft.vaaclipsedemo.mymediaplayer/icons/media.png"));
		mediaItem.getItemProperty(OBJECT_PROP).setValue(media);
	}
}

Let's take a look at code above. You have already known how to use javax.inject.Inject annotation with Eclipse 4 context. By @Inject here injected MediaLibrary and MediaService dependencies. Then take a look on a postConstruct method. It is annotated with annotation javax.annotation.PostConstruct, so method postConstruct will be called after the instance of MediaLibraryView class will be created by runtime. We can place here any initialization code. Note that we can inject dependencies also using this method parameters. In this case parameter parent with type VerticalLayout is injected from context. But what object parent is? This is the vaadin container component, the parent of MediaLibraryView. We create the panel with MediaLibraryView content and add this component to the parent:

@PostConstruct
public void postConstruct(VerticalLayout parent)
{
    panel = new Panel();
    panel.setSizeFull();
    parent.addComponent(panel);
		
    createMediaLibraryTree();
}

The method createMediaLibraryTree create the tree with MediaLibrary content.

Run the demo. You should to see the Media Libaray view with library content.

#Player part It is time to implement the playing of media. We already have the Player part (it is located in center of the screen as you remember), but it is empty. Now we are going to implement playing the selected media. The idea is simple: the user select a media (in the media library or anywhere els) and the selected media is loaded into the player.

Create the class org.semanticsoft.vaaclipsedemo.mymediaplayer.constants.MediaConstants:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.constants;

public class MediaConstants {
    public static final String mediaEntrySelected = "MediaEntrySelectedEvent";
    public static final String autoPlay = "AutoPlayProperty";	
}

Then open the PlayerView class and fill it with code below:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.views;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.extensions.EventUtils;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.constants.MediaConstants;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.VerticalLayout;

public class PlayerView
{
	@Inject
	MPart part;
	
	private Media media;
	
	private HorizontalLayout layout = new HorizontalLayout();
	private Embedded e;
	
	private EventHandler mediaSelectedHandler = new EventHandler() {
		
		@Override
		public void handleEvent(Event event) {
			Object data = event.getProperty(EventUtils.DATA);
			if (data instanceof Media){
				Boolean autoplay = (Boolean) event.getProperty(MediaConstants.autoPlay);
				if (autoplay == null)
					autoplay = false;
				setMedia((Media) data, autoplay);
				part.setLabel(((Media) data).getName());
			}
			
		}
	};

	@Inject
	public PlayerView(VerticalLayout parent)
	{
		layout.setSizeFull();
		parent.addComponent(layout);
	}
	
	@PostConstruct
	public void pc(IEventBroker b){
		b.subscribe(MediaConstants.mediaEntrySelected, mediaSelectedHandler);
	}
	
	public Media getMedia()
	{
		return media;
	}
	
	public void setMedia(Media media, boolean autoplay)
	{
		this.media = media;
		
		if (e == null)
		{
			e = new Embedded();
	        e.setMimeType("application/x-shockwave-flash");
	        e.setParameter("allowFullScreen", "true");
			layout.addComponent(e);
		}
		
		e.setSizeFull();
		e.setSource(new ExternalResource(media.getUri() + (autoplay ? "&autoplay=1" : "") ));
	}
	
	@PreDestroy
	public void pd(IEventBroker broker){
		broker.unsubscribe(mediaSelectedHandler);
	}
}

You can see our familiars here - the annotation Inject and PostConstruct. But there are some new things. First, Inject annotation is used not only with field (part field). Also there is the constructor is annotated with Inject annotation. This constructor is used by runtime for instantiating PlayerView. The PlayerView parent vaadin component is received to contructor. Second, we can see the new type of annotation - javax.annotation.PreDestroy. The method annotated with this annotation (pd) is called by runtime after current part was disposed. You can do some cleaning here.

How does it work? Runtime creates the PlayerView instance using constructor annotated with @Inject. It receive the parent vaadin component to this constructor. We are going to use the HorizontalLayout component for our PlayerView, so we add it to parent component:

parent.addComponent(layout);

After PlayerView has been created runtime call the method annotated with PostConstruct annotation. So, method pc is called and IEventBroker service is received in parameters. Then we use event broker to subscribe on event mediaEntrySelected:

b.subscribe(MediaConstants.mediaEntrySelected, mediaSelectedHandler);

When event mediaEntrySelected is occur, the mediaSelectedHandler is called by event broker.

private EventHandler mediaSelectedHandler = new EventHandler() {		

    @Override
    public void handleEvent(Event event) {
        Object data = event.getProperty(EventUtils.DATA);
	if (data instanceof Media){
	    Boolean autoplay = (Boolean) event.getProperty(MediaConstants.autoPlay);
            if (autoplay == null)
	        autoplay = false;
	    setMedia((Media) data, autoplay);
	    part.setLabel(((Media) data).getName());
	}
			
    }
};

The event EventUtils.DATA property contains the data attached to this event. In our case it should be a Media object. Event may contain other properties. In this case there are one another property MediaConstants.autoPlay. If this property has the "true" value, then the selected media should be played by PlayerView after loading. The setMedia method insert the selected media into layout using vaadin Embedded object.

OK, now we have the Player View displaying the selected media. But currently we have no ways to select media in our application. It is time to implement the selection of media in MediaLibraryView. Open the MediaLibraryView class do the following changes. First, inject the IEventBroker field:

@Inject
IEventBroker broker;

Then at any place of createMediaLibraryTree method add the tree listener:

tree.addListener(new ItemClickEvent.ItemClickListener() {

    private static final long serialVersionUID = 1L;

    public void itemClick(final ItemClickEvent event)
    {
        if (event.getButton() == ItemClickEvent.BUTTON_LEFT)
        {
            Item item = event.getItem();
            Object object = item.getItemProperty(OBJECT_PROP).getValue();
	    if (object != null && object instanceof MediaEntry)
	    {
	         MediaEntry media = (MediaEntry)object;	
		 mediaLibrary.setSelectedMediaEntry(media);
		 broker.send(MediaConstants.mediaEntrySelected, media);
	    }
        }
    }
});

It is very simple: when a user selects a media entry in a tree, we do two things:

  1. Do the appropriate model change - set selected media in media library
  2. Send mediaEntrySelected event with selected media.

So when user select a media in MediaLibrarView, the PlayerView load the selected media. Run the application and check that it works.

Media Info View

The Media info view should display the information about the selected media. It can be implemented very similar to PlayerView. The Media Info functionality is divided into two classes because the part of it functionality will be used later in Media Editor. Create new class org.semanticsoft.vaaclipsedemo.mymediaplayer.views.MediaInfoBase:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.views;

import org.eclipse.e4.core.contexts.IEclipseContext;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

public abstract class MediaInfoBase
{

	protected Media media;
	private HorizontalLayout layout = new HorizontalLayout();
	private GridLayout grid;

	public MediaInfoBase(VerticalLayout parent, IEclipseContext context)
	{
		layout.setSizeFull();
		parent.addComponent(layout);
	}
	
	public Media getMedia()
	{
		return media;
	}
	
	public void setMedia(Media media)
	{
		this.media = media;
		
		if (grid == null)
		{
			grid = new GridLayout(2, 3);
			
			int k = 0;
			
			grid.addComponent(new Label("Name: "), 0, k);
			Component nc = getNameComponent();
			nc.setWidth("100%");
			grid.addComponent(nc, 1, k++);
			
			Component uc = getUriComponent();
			if (uc != null)
			{
				grid.addComponent(new Label("URI: "), 0, k);
				uc.setWidth("100%");
				grid.addComponent(uc, 1, k++);
			}
			
			grid.addComponent(new Label("Description: "), 0, k);
			Component dc = getDescriptionComponent();
			dc.setSizeFull();
			grid.addComponent(dc, 1, k);
			layout.addComponent(grid);
			
			grid.setColumnExpandRatio(0, 20);
			grid.setColumnExpandRatio(1, 80);
			
			grid.setRowExpandRatio(k, 100);
			grid.setRowExpandRatio(--k, 10);
			if (--k >= 0)
				grid.setRowExpandRatio(k, 10);
			
			grid.setSizeFull();
		}
		
		insertMedia(media);
	}
	
	protected abstract void insertMedia(Media media);

	protected abstract Component getNameComponent();
	
	protected abstract Component getDescriptionComponent();
	
	protected abstract Component getUriComponent();
}

Then open the MediaInfoView class and fill it with:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.views;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;

import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.extensions.EventUtils;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.constants.MediaConstants;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;

import com.vaadin.data.util.ObjectProperty;
import com.vaadin.ui.Component;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

public class MediaInfoView extends MediaInfoBase
{
	private Label name = new Label();
	private Label uri = new Label();
	private Label description = new Label("", Label.CONTENT_XHTML);

	private EventHandler meidaSelectedHandler = new EventHandler() {
		
		@Override
		public void handleEvent(Event event) {
			Object data = event.getProperty(EventUtils.DATA);
			if (data instanceof Media){
				setMedia((Media) data);
			}
		}
	};
	
	@Inject
	public MediaInfoView(VerticalLayout parent)
	{
		super(parent, context);
	}
	
	@PostConstruct
	public void pc(IEventBroker broker){
		broker.subscribe(MediaConstants.mediaEntrySelected, meidaSelectedHandler);
	}
	
	protected void insertMedia(Media media)
	{
		name.setPropertyDataSource(new ObjectProperty<String>(media.getName(), String.class));
		uri.setPropertyDataSource(new ObjectProperty<String>(media.getUri(), String.class));
		description.setPropertyDataSource(new ObjectProperty<String>(media.getDescription(), String.class));
	}

	protected Component getNameComponent()
	{
		return this.name;
	}
	
	protected Component getDescriptionComponent()
	{
		return this.description;
	}
	
	protected Component getUriComponent()
	{
		return this.uri;
	}
	
	@PreDestroy
	public void pd(IEventBroker broker){
		broker.unsubscribe(meidaSelectedHandler);
	}
}

The constructor annotated with @Inject is used for creation MediaInvoView insance. We use pc method annotated with PostConstruct annotation to subcribe on media selected event and pd method annotated with PreDestory method to unsubsribe. When media selected event is occur, the setMedia method of base class is called and media information is displayed. Start the application and check how it works. If user select a media in media library, the media is loaded in PlayerView and media information is displayed in MediaInfoView.

#Commands and handlers: remove a media from library It is time to improve our application with commands. Now we are going to create the removing selected media entry (media or category) from media library.

Create new class org.semanticsoft.vaaclipsedemo.mymediaplayer.handlers.medialib.RemoveSelectedEntryFromLibrary:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.handlers.medialib;

import org.eclipse.e4.core.di.annotations.CanExecute;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.constants.MediaConstants;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaCategory;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaEntry;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaLibrary;

public class RemoveSelectedEntryFromLibrary
{
	@CanExecute
	public boolean canExecute(MediaLibrary medialib)
	{
		return medialib.getSelectedMediaEntry() != null;
	}
	
	@Execute
	public void remove(MediaLibrary medialib, IEventBroker eventBroker)
	{
		MediaEntry selectedMediaEntry = medialib.getSelectedMediaEntry();
		if (selectedMediaEntry != null)
		{
			MediaCategory parent = selectedMediaEntry.getParent();
			parent.removeMediaEntry(selectedMediaEntry);
			
			eventBroker.send(MediaConstants.mediaEntryRemoved, selectedMediaEntry);
		}
		
	}
}

This is handler class. It is used to execute commands. When the associated command is executed (or DirectItem associated with this handler selected), runtime check that this handler can be executed calling method annotated with @CanExecute annotation and if it return true, it call the method annotated with @Execute annotation. In our example RemoveSelectedEntryFromLibrary handler can be executed only if there are any selected media entry (media or category) in media library. Note that @CanExecute is used not only for checking execution possibility. If this method return false the appropriate menu or toolbar item will be disabled. So this is fine way for dealing with item enablements. In our case items that use this handler will be enabled only if there are some media entry selected in media library.

And last note - after removing media entry from library handler sends the mediaEntryRemoved event. So add the event header to MediaConstants class:

public static final String mediaEntryRemoved = "MediaEntryRemoved";

OK, we have a handler class, it is time to do some operations with model. Open the application model. Right click on the Commands entry and select Add -> Command.

Add command

Edit command:

Edit remove from media library command

Add handler:

Add handler

Select added handler and fill Command field using appropriate Find button. Select the removeFromLibrary command created in previous step:

Select command

Then set Class URI using appropriate "Find..." button. Select class RemoveSelectedEntryFromLibrary. The handler should look like this:

Edit handler

OK, we have a handler and command, how we can execute command? We can assign command to menu item or toolbar item. Now we are going to create the part toolbar with appropriate item. Find MediaLibrary part in Shared Elements section of Trimmed Window and select it. Check the ToolBar checkbox in part editor:

Add part toolbar

Add handler tool item to added toolbar:

Add handled tool item

Edit added tool item as shown below. Assign removeFromLibrary command to this tool item using "Find..." button and selecting removeFromLibrary command from list.

Edit handled tool item

Save model and run application. At first there are no selected entry in media library, so our tool item is disabled. After selecting media or category in media library our item is become enabled. Click on it. The selected media entry is not removed from media library? It is removed, but MediaLibraryView doesn't update itself as far as we don't handle the mediaEntryRemoved that triggered by RemoveSelectedEntryFromLibrary handler:

eventBroker.send(MediaConstants.mediaEntryRemoved, selectedMediaEntry);

Open MediaLibraryView and add mediaEntryRemoved event handling in the same way as you have added mediaEntrySelected event handling. Add mediaEntryRemoved handler field:

private EventHandler mediaEntryRemoved = new EventHandler() {				

    @Override
    public void handleEvent(Event event) {
        Object data = event.getProperty(EventUtils.DATA);
        if (data instanceof MediaEntry)
        {
            MediaEntry mediaEntry = (MediaEntry) data;
            String id = mediaService.getId(mediaEntry);
            container.removeItemRecursively(id);
        }
    }
};

Then subsribe on mediaEntryRemoved event in postConstruct method:

@PostConstruct
public void postConstruct(VerticalLayout parent)
{
    panel = new Panel();
    panel.setSizeFull();
    parent.addComponent(panel);
		
    createMediaLibraryTree();
   //add this line to subsribe on remove event:
    broker.subscribe(MediaConstants.mediaEntryRemoved, mediaEntryRemoved);
}

Don't forget create method preDestroy and unsubscribe from mediaEntryRemoved event:

@PreDestroy
public void preDestory()
{
    broker.unsubscribe(mediaEntryRemoved);
}

Run application and try to remove media or catalog. Now deleted media entry is disappear from MediaLibraryView. Yes, it steel selected in PlayerView and MediaInfoView but this is not criminal - it is not necessary the selected media to be presented in media library. Later we add the tool control to load media directly from url.

#Editors: edit selected media We are going to implement the editing media properties using Eclipse 4 editors infrastructure. First, we need to create the editor area. Open model editor and go to TrimmedWindow SharedElements section. Right click it and choose Add Child -> Area. Maybe there are no such item in your version of tooling. For example, I have no such item. If you have find it, click and add Area element. Otherwise you need to add Area element editing model file by hands (it is model editor bug). Don't worry, it is just xml file. First, close model editor. Then right click on Application.e4xmi and choose Open With -> Xml Editor. Then find place where sharedElements located and add yet another one:

<sharedElements xsi:type="advanced:Area" xmi:id="_QWERMOsAEeGrI5NvCZeHUA" elementId="org.eclipse.ui.editorss"/>

Save the file and close Application.e4xmi. Then open it again with Eclipse 4 Model Editor. In SharedElelments section should appear the added Area with elementId=org.eclipse.ui.editorss (the element id of this area is very important). The tutorial branch for readers with this model editor bug is end.

Select the Added area element and add child PartStack.

Now find the second PartSashContainer element containing single PartStack (selected in screenshot below) and add the child placeholder component.

Add area placeholder.

Select the added placeholder and using "Find..." button select reference to shared Area.

Now we should set the container data of both sibling childs. Set the container data of PartStack to 70 and the container data of Placeholder containing Area to 30. There are one another problem in my version of model editor - no the container data field in placeholder editor. If you have the same problem, you can easily fix it by editing model file by hands as described above. Just open the model with XML editor and find the placeholder containing Area. We have insert the Area with xmi:id="_QWERMOsAEeGrI5NvCZeHUA", so just find element that has property ref="_QWERMOsAEeGrI5NvCZeHUA":

<children xsi:type="basic:PartSashContainer" xmi:id="_IbI74FBUEeKPt6_TR92GRw" elementId="org.semanticsoft.vaaclipsedemo.mymediaplayer.partsashcontainer.1" containerData="60">
            <children xsi:type="basic:PartStack" xmi:id="_JsQE0FBcEeKPt6_TR92GRw" elementId="org.semanticsoft.vaaclipsedemo.mymediaplayer.partstack.player" containerData="70">
              <children xsi:type="advanced:Placeholder" xmi:id="_SES4YFBdEeKPt6_TR92GRw" elementId="org.semanticsoft.vaaclipsedemo.mymediaplayer.placeholder.player" ref="_BesyYFBNEeKPt6_TR92GRw"/>
            </children>
            <!-- This is placeholder you search:-->
            <children xsi:type="advanced:Placeholder" xmi:id="_9xXYkFFKEeKPt6_TR92GRw" elementId="org.semanticsoft.vaaclipsedemo.mymediaplayer.placeholder.0" ref="_QWERMOsAEeGrI5NvCZeHUA"/>
          </children>

Add attribute containerData="30" to finded placeholder element:

<children xsi:type="advanced:Placeholder" xmi:id="_9xXYkFFKEeKPt6_TR92GRw" elementId="org.semanticsoft.vaaclipsedemo.mymediaplayer.placeholder.0" ref="_QWERMOsAEeGrI5NvCZeHUA" containerData="30"/>

Save model file and close xml editor. Reopen the file in Model Editor (important - you should close the Model Editor and open it again, otherwise you lose the changes done in xml editor!).

Start the application. You should see the editor area below the Player part.

Now we have the editor area. Now we are going to implement the edit operation. Add editSelectedMedia command and handlers as you do above for remove media, create the HandledToolItem in MediaLibraryView toolbar and assign it the created command, then create new class org.semanticsoft.vaaclipsedemo.mymediaplayer.handlers.medialib.EditSelectedMedia.

command

handler

Handled Tool Item

package org.semanticsoft.vaaclipsedemo.mymediaplayer.handlers.medialib;

import org.eclipse.e4.core.di.annotations.CanExecute;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.model.application.ui.basic.MInputPart;
import org.semanticsoft.e4extension.service.EPartServiceExt;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaLibrary;

public class EditSelectedMedia
{	
	@CanExecute
	public boolean canExecute(MediaLibrary medialib)
	{
		return medialib.getSelectedMediaEntry() instanceof Media;
	}
	
	@Execute
	public void play(MediaLibrary medialib, EPartServiceExt partServiceExt)
	{
		if (medialib.getSelectedMediaEntry() instanceof Media)
		{
			Media selectedInPlaylist = (Media) medialib.getSelectedMediaEntry();
			MInputPart part = partServiceExt.openUri(selectedInPlaylist.getUri());
			part.setLabel(selectedInPlaylist.getName());
		}
	}
}

As you can see handler associated with this class can be executed only when there are any selected Media (not category!). Next, see on import

import org.semanticsoft.e4extension.service.EPartServiceExt;

This service extends the Eclipse 4 service org.eclipse.e4.ui.workbench.modeling.EPartService with usefull editor operations. It allows open any URI in editor area (area with elementId="org.eclipse.ui.editorss") using the editor associated with uri type. This works like file associations in operation system with next difference: instead of files we have more general concept - content URI. So you ask the service "open for edit" some uri and the service find the appropriate editor as the OS find the appropriate program for the file extension. If this URI already opened in editor with given type, system just activate this editor. It is the same as you open a file in package explorer - if this file has been already opened, eclipse just switch to it. If there are no opened editors with this type, runtime create new editor of this type and put the URI into it. How to create assication between URI and the editor type? You can do it using EditorPartDescriptor element. Two things you need to know about EditorPartDescriptor element:

  • It is the editor part template. New editor parts are initialized from this template. For example, if you specify the icon URI in EditorPartDescriptor element, the created editor part will have this icon URI.
  • It contains the URI filter to match URIs associated to this EditorPartDescriptor element. It has a form of regex filtering URIs. The matched URIs will be opened using this EditorPartDescriptor.

Now you will create the EditorPartDescriptor element describing the media editor. In model editor find the section Editor Part Descriptors and add the EditorPartDescriptor. Edit the added element as in the screenshot below.

Editor Descriptor

As you can see the created descriptor has a URI filter string ".*", so it matches any URIs. We have only one type of URIs in our application. If there were any types of content, then we would do distinguish the URIs using more specialized regex expression.

Also there are field "Part Adding Logic". This field specifies the class containig algorithm for adding the created editor parts. Part adding logic gives reply on a question: "where will be the opened editors located?". As you can see the default value of this field is DefaultPartAddingLogic. This logic finds the area with elementId="org.eclipse.ui.editorss" and add the area into this area using certain algorithm. You can change this behaviour adding own adding logic.

Also there are class URI to the contribution class MediaInfoEditor. As well as the PartEditorDescriptor element is a template for editior parts, it contains the initial values of part properties. A label, tooltip and class URI properties are the template properties for editor parts produced by this descriptor.

Now create class org.semanticsoft.vaaclipsedemo.mymediaplayer.editors.MediaInfoEditor:

package org.semanticsoft.vaaclipsedemo.mymediaplayer.editors;

import javax.inject.Inject;

import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.di.Persist;
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
import org.eclipse.e4.ui.model.application.ui.basic.MInputPart;
import org.semanticsoft.vaaclipse.publicapi.editor.SavePromptSetup;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.constants.MediaConstants;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.service.MediaService;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.views.MediaInfoBase;

import com.vaadin.data.util.ObjectProperty;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

public class MediaInfoEditor extends MediaInfoBase
{
	@Inject
	IEventBroker eventBroker;
	
	private MInputPart inputPart;
	
	@Inject
	public MediaInfoEditor(VerticalLayout parent, IEclipseContext context, MediaService service, MInputPart inputPart, SavePromptSetup promptProvider)
	{
		super(parent, context);
		this.inputPart = inputPart;
		Media media = service.findMedia(inputPart.getInputURI());
		if (media != null)
		{
			setMedia(media);
		}
		
		promptProvider.setCaption("Save media");
		promptProvider.setMessage(String.format("Media %s has been modified. Save changes?", media.getName()));
		
		this.name.setImmediate(true);
		this.description.setImmediate(true);
		
		this.name.addListener(textChangeListener);
		this.description.addListener(textChangeListener);
	}
	
	@Inject
	private MDirtyable dirtable;

	private TextField name = new TextField();
	private TextField description = new TextField();
	private TextChangeListener textChangeListener = new TextChangeListener() {
		
		@Override
		public void textChange(TextChangeEvent event)
		{
			if (!dirtable.isDirty())
				dirtable.setDirty(true);
		}
	};
	
	@Override
	protected Component getNameComponent()
	{
		return this.name;
	}
	
	@Override
	protected Component getDescriptionComponent()
	{
		return this.description;
	}
	
	@Override
	protected Component getUriComponent()
	{
		return null;
	}

	protected void insertMedia(Media media)
	{
		name.setPropertyDataSource(new ObjectProperty<String>(media.getName(), String.class));
		description.setPropertyDataSource(new ObjectProperty<String>(media.getDescription(), String.class));
	}
	
	@Persist
	public void persist()
	{
		String newName = name.getValue().toString();
		
		this.inputPart.setLabel(newName);
		
		this.media.setName(newName);
		this.media.setDescription(description.getValue().toString());
		
		dirtable.setDirty(false);
		
		eventBroker.send(MediaConstants.mediaEntryChanged, this.media);
	}
}

You know that constructor annotated with @Inject annotation is used for creation of contribution object. You can see that MInpuPart received into constructor. InputPart is the extended version of Part. It has an input URI - the URI of content opened with this editor. So we can fetch the input uri using inputPart.getInputURI(). Then the media object for this URI is finded and inserted into editor using setMedia method.

Now take a look to this field:

@Inject
private MDirtyable dirtable;

This object is used to report about edit status. If the document contains unsaved changes, dirtable.isDirty() should return true and false otherwise. So when new changes appear we set the dirty status to true:

if (!dirtable.isDirty())
    dirtable.setDirty(true);

Now the runtime knows about status of our document. It displays the "*" sign before the part label and display save dialog when user close the unsaved document. Take a look at persist method. It is annotated with @Persist annotation, so runtime knows how to save the part. Yes, it just call this method.

@Persist
public void persist()
{
    this.media.setName(name.getValue().toString());
    this.media.setUri(uri.getValue().toString());
    this.media.setDescription(description.getValue().toString());
		
    dirtable.setDirty(false);
		
    eventBroker.send(MediaConstants.mediaEntryChanged, this.media);
}

We save the content of part. In our case we simple read the media properties from text fields and set into media object. In real application you can perform storing the media object for example in database. Don't forget call the dirtable.setDirty(false) if its peformed succesfully.

Last thing what we are doing in this method - change the mediaEntryChanged event. By the way, add the event header for this event in MediaConstants:

public static final String mediaEntryChanged = "MediaEntryChanged";

Of course, we should handle this event to update the views displaying changed media. It can be displayed by MediaLibraryView and MediaInfoView.

Add this field to MediaLibraryView:

private EventHandler mediaEntryChangedHandler = new EventHandler() {
		
    @Override
    public void handleEvent(Event event) {
         Object data = event.getProperty(EventUtils.DATA);
             if (data instanceof MediaEntry){
                 MediaEntry media = (MediaEntry) data;
		 String id = mediaService.getId(media);
		 Item item = container.getItem(id);
		 item.getItemProperty(NAME_PROP).setValue(media.getName());
         }
    }
};

Subscribe on mediaEntryChangedEvent adding this line to MediaLibraryView.postCostruct method :

broker.subscribe(MediaConstants.mediaEntryChanged, mediaEntryChangedHandler);

and don't forget to unsubscribe in preDestroy:

broker.unsubscribe(mediaEntryChangedHandler);

Add this field to MediaInfoView:

private EventHandler mediaChangedHandler = new EventHandler() {
			
			@Override
			public void handleEvent(Event event) {
				Object data = event.getProperty(EventUtils.DATA);
				if (data instanceof Media){
					insertMedia((Media) data);
				}
				
			}
		};

Then adjust pd and pc methods to subsribte and unsubsribe from this event.

OK, now you are ready to test editors. Run the application and select a media in the library. Click on Edit tool item, the selected media should be opened in editor area. Change the media name or description and try to close the editor. The save dialog appears. Choose Yes - the media data should be updated in both views.

#Add a media entry to library The adding a media etnry (media or category) to library is similar to removing. Add the following to MediaConstants:

public static final String mediaEntryAdded = "MediaEntryAdded";

Copy classes AddMediaEntryBasic, AddMedia, AddCategory from package org.semanticsoft.vaaclipsedemo.mediaplayer.handlers.medialib of project mediaplayer to appropriate package of mymediaplayer project. The class AddMediaEntryBasic contains the common part of AddMedia and AddCategory. The most interesting in this classes the addMedia method of AddMedia handler. As you know, runtime executes this method when handler AddMedia executed. Take a look at the parameter mediaUri:

@Execute
public void addMedia(@Optional final String mediaUri, MediaLibrary mediaLibrary, final MWindow window)
{
    init(mediaLibrary, window);
		
    if (mediaUri != null)
    {
        addMediaToLibrary(window, parentCategory, mediaUri);
    }
    else
    {
        //----
        createAndShowDlg(window, "New media", "Media url:");
    }
}

It is annotated with @Optional annotation, so it can be null. This is media uri to be added. We can inject some media uri in context before handler executing - and handler is add the media with injected uri. If mediaUri is null, handler asks user enter the uri of media by creating input dialog. We will use later this feature of AddMedia handler.

If user enters the existing media, the notification is displayed. Otherwise the media is added to library.

Media media = mediaService.findMedia(uri);
if (media != null)
{
    ((Window)window.getWidget()).showNotification("Media with this uri exists in media library", Notification.TYPE_WARNING_MESSAGE);
    return;
}
else
{
    media = new Media();
    media.setName("No name");
    media.setUri(uri);
    media.setDescription("");
    category.addMedia(media);
    eventBroker.send(MediaConstants.mediaEntrySelected, media);
			
    eventBroker.send(MediaConstants.mediaEntryAdded, media);
    MInputPart part = partServiceExt.openUri(media.getUri());
    part.setLabel(media.getName());
}

At last the added media is opened for editing using EPartServiceExt.openUri method. User can enter the name and description of the new media and save it.

As in the case with media removing, open the Model Editor and add commans addMedia and addCategory and appropriate handlers. Add one another Handled Tool Item to MediaLibraryView toolbar and specify the created command. Finally, add the mediaEntryAdded event handling in MediaLibraryView in the same way as we have done it for media entry removing.

private EventHandler mediaEntryAdded = new EventHandler() {
			
    @Override
    public void handleEvent(Event event) {
        Object data = event.getProperty(EventUtils.DATA);
        if (data instanceof MediaEntry){
	    MediaEntry mediaEntry = (MediaEntry) data;
	    MediaCategory parent = mediaEntry.getParent();
	    String id = mediaService.getId(mediaEntry);
	    String parentid = mediaService.getId(parent);
	    Item item = container.addItem(id);
	    container.setParent(id, parentid);
            setupMediaEntry(mediaEntry, item);
        }
    }
};

...

@PostConstruct
public void postConstruct(VerticalLayout parent, IEclipseContext context)
{
    panel = new Panel();
    panel.setSizeFull();
    parent.addComponent(panel);
		
    createMediaLibraryTree();
		
    broker.subscribe(MediaConstants.mediaEntryChanged, mediaEntryChangedHandler);
    broker.subscribe(MediaConstants.mediaEntryAdded, mediaEntryAdded); //add this line
    broker.subscribe(MediaConstants.mediaEntryRemoved, mediaEntryRemoved);
}

...

@PreDestroy
public void preDestory()
{
    broker.unsubscribe(mediaEntryChangedHandler);
    broker.unsubscribe(mediaEntryAdded); //add this line
    broker.unsubscribe(mediaEntryRemoved);
}

If you have problems with completing this task, please, see the mediaplayer demo.

#Tool control Now we are going to create the tool control AddToLibraryToolControl. What tool control is? Tool control is the counterpart of parts for toolbars and trimbars. They have a contribution like parts. Toolbars consist of tool items, tool control content can be any vaadin component. Now we are going to use this feature to design AddMedia tool control. The AddMedia tool control consists of the text field and two buttons - Play Media button to play media with URI entered into text field and Add To Library button to add this media to library. So using this tool control user can play media directly by URL without adding it to the library. Also this tool control helps him to watch video before adding to the library.

Expand TrimBars section of TrimmedWindow. There are one trimbar element - top trimbar with one child toolbar created by project wizard. Select and remove this toolbar. Add ToolControl element to top trimbar (call context menu, right click, Add -> Tool Control). Create class org.semanticsoft.vaaclipsedemo.mymediaplayer.toolcontrols.AddToLibraryToolControl and edit the added ToolControl as shown below.

Edit tool control

package org.semanticsoft.vaaclipsedemo.mymediaplayer.toolcontrols;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.CanExecute;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.semanticsoft.vaaclipse.publicapi.resources.BundleResource;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.constants.MediaConstants;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.handlers.medialib.AddMedia;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.Media;
import org.semanticsoft.vaaclipsedemo.mymediaplayer.model.MediaLibrary;

import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.Notification;

public class AddToLibraryToolControl
{
    private Button playMediaButton;
    private TextField textField;
	
    @Inject
    IEventBroker broker;
	
    @Inject
    MediaLibrary mediaLibrary;
	
    @Inject
    IEclipseContext context;
	
    @Inject
    MWindow window;

    @PostConstruct
    public void postConstruct(ComponentContainer cc)
    {
        playMediaButton = new Button("Play media: ");
        playMediaButton.setIcon(BundleResource.valueOf("platform:/plugin/org.semanticsoft.vaaclipsedemo.mymediaplayer/icons/watch.png"));
        playMediaButton.setDescription("Play media");
        textField = new TextField();
        textField.setWidth("20em");
        textField.setValue("http://www.youtube.com/v/0417pQz7iIk");

	playMediaButton.addListener(new ClickListener() {

	    @Override
	    public void buttonClick(ClickEvent event)
	    {
	        if (textField.getValue() == null)
		    return;		
                String uri = textField.getValue().toString().trim();
		if ((uri.isEmpty()))
		    return;

		Media media = new Media();
		media.setName("No name");
		media.setUri(uri);
		media.setDescription("");
		broker.send(MediaConstants.mediaEntrySelected, media);
	    }
        });
		
        Button addToLibraryButton = new Button("Add...");
	addToLibraryButton.setIcon(BundleResource.valueOf("platform:/plugin/org.semanticsoft.vaaclipsedemo.mymediaplayer/icons/add.png"));
	addToLibraryButton.setDescription("Add media to library");
	addToLibraryButton.addListener(new ClickListener() {
			
	@Override
	public void buttonClick(ClickEvent event)
	{
	    if (textField.getValue() == null)
	        return;
				
	    String uri = textField.getValue().toString().trim();
	    if ((uri.isEmpty()))
		return;
	    	
            //Don't forget create and use local context, not touch the context of current control!
            IEclipseContext localContext = context.createChild();
            localContext.set(String.class, uri); //media uri
            Object addMediaHandler = ContextInjectionFactory.make(AddMedia.class, localContext);
	    if ((Boolean) ContextInjectionFactory.invoke(addMediaHandler, CanExecute.class, localContext, true))
	    ContextInjectionFactory.invoke(addMediaHandler, Execute.class, localContext);
			}
		});
		
	    cc.addComponent(playMediaButton);
	    cc.addComponent(textField);
	    cc.addComponent(addToLibraryButton);
	}
}

When user click button Play the new empty media created and selected:

Media media = new Media();
media.setName("No name");
media.setUri(uri);
media.setDescription("");
broker.send(MediaConstants.mediaEntrySelected, media);

When user click button AddToLibrary, the AddMedia handler is executed. We created it above, so we can reuse it now. But before executing this handler, we create the child context and set the media uri from text field:

IEclipseContext localContext = context.createChild();
localContext.set(String.class, uri); //media uri

You have designed AddMedia method try media uri from context before ask it from user. If there are some media uri in context, it simple add this uri to library without showing input dialog. So we create child context with media uri and then create and execute AddMedia handler.

Object addMediaHandler = ContextInjectionFactory.make(AddMedia.class, localContext);
ContextInjectionFactory.invoke(addMediaHandler, Execute.class, localContext);

Clone this wiki locally