Keyboard accessibility thru Command Framework

Keyboard shortcuts is usually much speedier than reaching out your mouse, moving it, pointing it to something and clicking. But there are some things which cannot be done that easily by keyboard shortcuts. For me, one of them is finding out a closed project and open it. Unfortunately, I've quite a large set of projects in my workspace and try to keep most them closed, when not used. In addition to that in the Package Explorer, I've the 'Closed Projects' filter on. So if I need to open a project, I've use the pull down menu, uncheck 'Closed Projects' navigate thru the working sets to find the right project and double click it. To enable keyboard access to this regular task, I decided to make use of Commands Framework.

The solution is to add a parameterized command, and in the values, I compute the projects which are closed. So when I press the awesome shortcut (Ctrl+3) it would display me the list of closed projects. With few keys, I can navigate to the project I want and open it. Lets see how to do it. First step is the command with the parameter:

<extension point="org.eclipse.ui.commands">
   <command
            defaultHandler="com.eclipse_tips.handlers.OpenProjectHandler"
            id="com.eclipse-tips.openProject.command"
            name="Open Project">
      <commandParameter
               id="com.eclipse-tips.openProject.projectNameParameter"
               name="Name"
               optional="false"
               values="com.eclipse_tips.handlers.ProjectNameParameterValues">
      </commandParameter>
   </command>
</extension>

And the handler:

public Object execute(ExecutionEvent event) throws ExecutionException {
 String projectName = event.getParameter("com.eclipse-tips.openProject.projectNameParameter");
 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
 IProject project = root.getProject(projectName);
 try {
  project.open(null);
 } catch (CoreException e) {
  throw new ExecutionException("Error occured while open project", e);
 }
 return null;
}

For the parameter values, I look for closed projects and return them:

public Map<String, String> getParameterValues() {

 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
 IProject[] projects = root.getProjects();
 Map<String, String> paramValues = new HashMap<String, String>();
 for (IProject project : projects) {
  if (project.exists() && !project.isOpen()) {
   paramValues.put(project.getName(), project.getName());
  }

 }
 return paramValues;
}


So finally, When I press Ctrl+3 and type OPN, I get the list:

 

This idea can be extended to provide keyboard accessibility to many functionalities. Say in an RCP mail application, you can add a command like 'Go To Mail' with parameter as the Subject/Sender:


Hmmm, if only the 'Built On Eclipse' mail app that I *have* to use, knows the existence of threads other than the UI thread :-(




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , | 1 comments

Subtask in ProgressMonitors

Here is a trivial tip. When inspecting a bug, I found a interesting thing on Progress Monitors. We know monitor.worked() increments the progress bar, but how do we change the text to update the current subtask? The initial text is set by the beginTask() method and it should be called only once. I digged into the IProgressMonitor and found the subtask() method:

IRunnableWithProgress operation = new IRunnableWithProgress() {

 public void run(IProgressMonitor monitor) {

  monitor.beginTask("Main task running ...", 5);
  for (int i = 0; i < 5; i++) {
   monitor.subTask("Subtask # " + i + " running.");
   runSubTask(new SubProgressMonitor(monitor, 1), i);
  }
 }

};


Now the question is what happens when the runSubTask() method sets another subTask on the SubProgressMonitor?

private void runSubTask(IProgressMonitor monitor, int subTaskId) {

 monitor.beginTask("Sub task running", 10);
  for (int i = 0; i < 10; i++) {
   monitor.subTask("Inside subtask, " + i + " out of 10");
   // do something here ...
   monitor.worked(1);
   if (monitor.isCanceled())
    throw new OperationCanceledException();
 }
  monitor.done();
 }

}

Basically the SubProgressMonitor's subTask() overwrites the parent's subTask(). Thats the default behaviour. You can customize it with the style bits provided in the SubProgressMonitor:

If you want to append the SubProgressMonitor's subTask info, use the style PREPEND_MAIN_LABEL_TO_SUBTASK:
new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK), i);


Else if you want to ignore it altogher then use the SUPPRESS_SUBTASK_LABEL style:

new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), i);




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , | 0 comments

Search menu in Mac - the implementation

In the previous entry I mentioned about the Search Menu in Mac. Lets see what that search menu contains:


 

The first item is the "Clear" menu. It clears the recent search history. Next comes the recent searches. If you want to call it as "Search History", you can do it in the title field menu item. The title field & clear menu item are visible only when there is at least one entry in the recent searches. The menu can also have custom items, where you can include you own action items. Lets see how to add all these in an SWT app:


Text searchText = new Text(shell, SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
  SearchFieldSupport searchFieldSupport = new SearchFieldSupport(searchText);
  Menu menu = new Menu(searchText);
  
  MenuItem customItem = new MenuItem(menu, SWT.NONE);
  customItem.setText("Custom Action");
  customItem.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    MessageDialog.openInformation(shell, "Search Field", "Custom action is done here");
   };
  });
  
  MenuItem sep1 = new MenuItem(menu, SWT.SEPARATOR);
  
  MenuItem recentMenuItem = new MenuItem(menu, SWT.NONE);
  recentMenuItem.setText("Search History");
  SearchFieldSupport.setRecentSearchesTitle(recentMenuItem);
  
  // for the search history
  MenuItem recentsMenuItem = new MenuItem(menu, SWT.PUSH);
  SearchFieldSupport.setRecentSearches(recentsMenuItem);
  
  MenuItem sep2 = new MenuItem(menu, SWT.SEPARATOR);

  MenuItem clearMenuItem = new MenuItem(menu, SWT.PUSH);
  clearMenuItem.setText("Clear History");
  SearchFieldSupport.setClearRecents(clearMenuItem);

  searchFieldSupport.setMenu(menu);


Here is the result:

Its almost perfect except that when we clear the search history and see the menu, it looks like this:


Its because the separators are still shown even when there is no recent menu items. The hack is to set the separator as a resentSearches menu item:

MenuItem sep2 = new MenuItem(menu, SWT.SEPARATOR);
  SearchFieldSupport.setRecentSearchesTitle(sep2);

The Search Field also supports a custom menu item which tells that there is no recent search items. This menu item is automatically hidden when there search history is not empty. We can add that also before the second separator:

MenuItem recentsMenuItem = new MenuItem(menu, SWT.PUSH);
  SearchFieldSupport.setRecentSearches(recentsMenuItem);
  
  MenuItem noRecentMenuItem = new MenuItem(menu, SWT.NONE);
  noRecentMenuItem.setText("No Search History");
  SearchFieldSupport.setNoRecentSearches(noRecentMenuItem);
  
  MenuItem sep2 = new MenuItem(menu, SWT.SEPARATOR);
  SearchFieldSupport.setRecentSearchesTitle(sep2);

Now we are all set:


The last missing piece of the puzzle, the SearchFieldSupport class:

package org.eclipse.ui.cocoa.ext;

import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.cocoa.ext.internal.NSSearchField;

/**
 * 
 * @author Prakash G.R.
 * 
 */
public class SearchFieldSupport implements KeyListener, DisposeListener {
 
 private final NSSearchField nsSearchField;
 private final Text text;

 public SearchFieldSupport(Text text) {
  this.text = text;
  text.addKeyListener(this);
  text.addDisposeListener(this);
  nsSearchField = new NSSearchField(text);
 }

 public Text getText() {
  return text;
 }

 public String[] getRecentSearchStrings() {
  return nsSearchField.getRecentSearches();
 }
 
 public void setRecentSearchStrings(String[] recentSearches) {
  nsSearchField.setRecentSearches(recentSearches);
 }

 public void setMenu(Menu menu) {
  nsSearchField.setMenu(menu);
 }

 public static void setNoRecentSearches(MenuItem menuItem) {
  NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldNoRecentsMenuItemTag);
 }

 public static void setClearRecents(MenuItem menuItem) {
  NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldClearRecentsMenuItemTag);
 }

 public static void setRecentSearchesTitle(MenuItem menuItem) {
  NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldRecentsTitleMenuItemTag);
 }

 public static void setRecentSearches(MenuItem menuItem) {
  NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldRecentsMenuItemTag);
 }

 public void widgetDisposed(DisposeEvent e) {
  text.removeKeyListener(this);
 }

 public void keyPressed(KeyEvent e) {
  // do nothing
 }

 public void keyReleased(KeyEvent e) {
  if (e.keyCode == '\r') {
   String[] recentSearchStrings = getRecentSearchStrings();
   int oldSize = recentSearchStrings.length;
   String[] newSearchStrings = new String[recentSearchStrings.length + 1];
   System.arraycopy(recentSearchStrings, 0, newSearchStrings, 0, recentSearchStrings.length);
   newSearchStrings[oldSize] = text.getText();
   setRecentSearchStrings(newSearchStrings);
   text.setSelection(0, text.getText().length());
  }
 }

}

and the custom NSSearchField class:

package org.eclipse.ui.cocoa.ext.internal;

import java.lang.reflect.Field;

import org.eclipse.swt.internal.cocoa.NSArray;
import org.eclipse.swt.internal.cocoa.NSMenu;
import org.eclipse.swt.internal.cocoa.NSMenuItem;
import org.eclipse.swt.internal.cocoa.NSMutableArray;
import org.eclipse.swt.internal.cocoa.NSString;
import org.eclipse.swt.internal.cocoa.OS;
import org.eclipse.swt.internal.cocoa.id;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Text;

/**
 * @author Prakash G.R.
 * 
 */
@SuppressWarnings("restriction")
public class NSSearchField extends org.eclipse.swt.internal.cocoa.NSSearchField {

 public static final int NSSearchFieldRecentsTitleMenuItemTag = 1000;
 public static final int NSSearchFieldRecentsMenuItemTag = 1001;
 public static final int NSSearchFieldClearRecentsMenuItemTag = 1002;
 public static final int NSSearchFieldNoRecentsMenuItemTag = 1003;

 private static final int /* long */sel_setSearchMenuTemplate = OS.sel_registerName("setSearchMenuTemplate:");
 private static final int /* long */sel_setTag = OS.sel_registerName("setTag:");
 private static final int /* long */sel_setRecentSearches = OS.sel_registerName("setRecentSearches:");

 public NSSearchField(id id) {
  super(id);
 }

 public NSSearchField(Text text) {
  super(text.view);
 }

 public void setMenu(Menu menu) {
  try {
   Field field = Menu.class.getDeclaredField("nsMenu");
   field.setAccessible(true);

   NSMenu nsMenu = (NSMenu) field.get(menu);

   OS.objc_msgSend(this.id, sel_setSearchMenuTemplate, nsMenu.id);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 public static boolean setTag(MenuItem menuItem, int tag) {
  try {
   Field field = MenuItem.class.getDeclaredField("nsItem");
   field.setAccessible(true);

   NSMenuItem nsMenuItem = (NSMenuItem) field.get(menuItem);
   OS.objc_msgSend(nsMenuItem.id, sel_setTag, tag);

   // no action for titles
   if (tag == NSSearchFieldRecentsTitleMenuItemTag || tag == NSSearchFieldNoRecentsMenuItemTag) {
    nsMenuItem.setAction(0);
   }
   return true;
  } catch (Exception e) {
   return false;
  }
 }

 public String[] getRecentSearches() {
  NSArray recentSearches = super.recentSearches();
  String[] recentSearchStrings = new String[recentSearches.count()];
  for (int i = 0; i < recentSearchStrings.length; i++) {
   recentSearchStrings[i] = (new NSString(recentSearches.objectAtIndex(i))).getString();
  }
  return recentSearchStrings;
 }

 public void setRecentSearches(String[] recentSearchStrings) {
  
  NSMutableArray recentSearches = NSMutableArray.arrayWithCapacity(recentSearchStrings.length);
  for (String aRecentSearcb : recentSearchStrings) {
   NSString nsString = NSString.stringWith(aRecentSearcb);
   recentSearches.addObject(nsString);
  }
  OS.objc_msgSend(this.id, sel_setRecentSearches, recentSearches.id);
  
 }
}

Enjoy!




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , , , | 1 comments

Search menu in Mac

In Mac applications, the little search box can have a pull down menu (similar to a View's pull down menu). It can have recent searches, user contributed menu entries, a menu item for clearing the recent searches etc. Here is the search box of Tweetie (the twitter app that I use):


Eclipse by default offers the SWT.SEARCH style which will bring this search box instead of the default text box. But there is no option to add the menu. For today's SWT-Cocoa hack, I tried to enhance the Search box with the search menu and here is the result:

The code is so ugly that I can't post it in public yet. Will post it with refinements tomorrow.

Update: The implementation is available here.




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , | 0 comments

Badge label on Mac Dock icon

I'm passionate on both Mac and Eclipse. Although Eclipse runs well on a Mac, its not a marriage made in heaven. Its really hard to get the complete Mac experience with a portable UI tool kit. If you think of features like this one, its actually endless list. One such thing is the badge on the Dock Icon. In Mac, the Dock icon can have a badge - like the number of unread mails in your inbox. How much effort does it takes to add a badge to our RCP mail sample? Not much. Since SWT doesn't have a NSDockTile, we need a NSDockTile class with two methods, one to get the DockTile for the application and the other to set the badge on it (there is an easier way by using the Mac Generator tool, but that will be a patch to SWT itself, which I wanted to avoid):

/**
 * @author Prakash G.R. (grprakash@gmail.com) 
 * 
 */
@SuppressWarnings("restriction")
public class NSDockTile extends NSResponder {

 private static final int sel_setBadgeLabel_ = OS.sel_registerName("setBadgeLabel:");
 private static final int sel_dockTile_ = OS.sel_registerName("dockTile");
 private static final int sel_display_ = OS.sel_registerName("display");

 public NSDockTile(int id) {
  super(id);
 }

 public static NSDockTile getApplicationDockTile() {
  NSApplication sharedApplication = NSApplication.sharedApplication();
  int id = OS.objc_msgSend(sharedApplication.id, sel_dockTile_);
  NSDockTile dockTile = new NSDockTile(id);
  return dockTile;
 }

 public void setBadgeLabel(String badgeLabel) {
   NSString nsBadgeLabel = NSString.stringWith(badgeLabel);
   OS.objc_msgSend(this.id, sel_setBadgeLabel_, nsBadgeLabel.id);
   OS.objc_msgSend(this.id, sel_display_);
 }
}

And a two line code to set the badge:

NSDockTile nsDockTile = NSDockTile.getApplicationDockTile();
nsDockTile.setBadgeLabel("6");

There you go in the dock icon and in the minimized window:


In case you were wondering how the GMail icon for the application is set, here is the code for that:

ImageDescriptor imageDescriptor = ImageDescriptor.createFromURL(iconUrl);
Image image = imageDescriptor.createImage();
NSApplication app = NSApplication.sharedApplication();
app.setApplicationIconImage(image.handle);

Oh, BTW, I've just started learning Objective C, Cocoa & related things. So stay tuned, you will see more Mac related posts here. Also what is your favourite Mac feature that you are missing in Eclipse?




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , , | 5 comments

Creating a custom Property View

In an RCP application, you might need a Properties View, which shows only the properties of a specific view or a set of views. But the generic Properties View will show from all the other views that support it. Armed up with the knowledge of how a PageBookView works, lets see how to hack the Properties View to listen only to a specific view.

The isImportant() method is the one which decides whether to create an IPage for the specific IWorkbenchPart or not. The idea is to override that method and return false for all the workbenchPart that we are not interested in. Lets create the view first:

<view
            class="com.eclipse_tips.views.CustomPropertiesView"
            icon="icons/sample.gif"
            id="com.eclipse-tips.views.customePropertiesView"
            name="My Properties View">
</view>

The CustomPropertiesView should extend PropertySheet and override the isImportant():

public class CustomPropertiesView extends PropertySheet {

 @Override
 protected boolean isImportant(IWorkbenchPart part) {
  if (part.getSite().getId().equals(IPageLayout.ID_PROJECT_EXPLORER))
   return true;
  return false;
 }
}

In this case, I'm making the view only to respond to Project Explorer and ignore other views. Here is the CustomPropertyView in action:
 
 

If you are on 3.5 or above, you would see the Pin Action in your Custom Properties View. If you don't want the Pin action in your properties view, there is no way to prevent the PropertySheet to adding the action. The action is added to both tool bar and menu in the createControl() method. Only way to get rid of the action is to remove it after the PropertySheet adds it:

@Override
 public void createPartControl(Composite parent) {
  
  super.createPartControl(parent);
  IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
  IContributionItem[] items = menuManager.getItems();
  for (IContributionItem iContributionItem : items) {
   if(iContributionItem instanceof ActionContributionItem) {
    if(((ActionContributionItem) iContributionItem).getAction() instanceof PinPropertySheetAction) {
     menuManager.remove(iContributionItem);
     break;
    }
   }
  }

  IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
  items = toolBarManager.getItems();
  for (IContributionItem iContributionItem : items) {
   if(iContributionItem instanceof ActionContributionItem) {
    if(((ActionContributionItem) iContributionItem).getAction() instanceof PinPropertySheetAction)) {
     toolBarManager.remove(iContributionItem);
     break;
    }
   }
  }
 }

And don't forget to override the isPinned() method:

@Override
 public boolean isPinned() {
  return false;
 }

There you go:




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , | 0 comments

How to create a PageBookView

Think of Properties View. It displays the properties of the selected element in the active part. Whenever the selection changes or the active part changes, it tracks them and displays the properties (unless you used the 'Pin to selection' feature available from 3.5) There are many views like this, which update themselves when the active part changes. Outline view, Templates view, GEF Palette view, etc. If you want to create such view, its not a tough job - Eclipse provides you all the basic features in the class PageBookView. All you need is to extend this class and fill the void by implementing the abstract methods. Thats what we are going to see in this tip. The use case is to create a ActivePartTrackerView, which will display the name of the current active workbench part.

First lets create a View:

<extension
         point="org.eclipse.ui.views">
      <view
            class="com.eclipse_tips.views.SelectionView"
            icon="icons/sample.gif"
            id="com.eclipse-tips.views.pagebookview"
            name="Selection Provider View">
      </view>
   </extension>


The contents of the PageBookView is arranged in pages (IPage). There can be multiple pages, each of them is associated with a corresponding IWorkbenchPart. When the associated part becomes active, the PageBookView automatically switches to the respective page. When a PageBookView is created, it asks for a bootstrap part by calling getBootstrapPart() method. When no bootstrap part is found, it uses a default page. That default page is also shown when there are no pages for the currently active part. Lets start by creating this default page. To do that we need to implement the createDefaultPage() method, which returns the default page. Lets use the MessagePage which simply displays a string.

@Override
 protected IPage createDefaultPage(PageBook book) {
  MessagePage messagePage = new MessagePage();
  initPage(messagePage);
  messagePage.setMessage("No interested in this part");
  messagePage.createControl(book);
  return messagePage;
 } 


Now our SelectionView is up and running, except that its showing a static content not creating any IPages for the active parts. Before we create an IPage, how to determine whether to create an IPage for a given IWorkbenchPart or ignore it? Its by the isImportant() method. Lets say, we want to respond only to the parts that are contributed by the Platform UI.

@Override
 protected boolean isImportant(IWorkbenchPart part) {
  return part.getSite().getPluginId().startsWith("org.eclipse.ui");
 }

So if a Package Explorer(contributed by JDT) or the Manifest Editor(contributed by PDE) is the active part, then our SelectionView will not create any page and use the default page. If its a TextEditor or Project Explorer, then it will create the page and show it.

To create an IPage for a given part, we need to override the doCreatePage() method. Unlike the createDefaultPage() method this doesn't return a IPage, rather a PageRec. Why? This Page Record stores additional information - the associated workbench part and action bars:

@Override
 protected PageRec doCreatePage(IWorkbenchPart part) {
  MessagePage messagePage = new MessagePage();
  initPage(messagePage);
  messagePage.setMessage("Page for "+part.getTitle());
  messagePage.createControl(getPageBook());
  return new PageRec(part, messagePage);
 }

 
 

When you are switching between the TextEditor and the ProjectExplore view, the PageBookView will track and find the page for the active part and automatically show it. When the page is no longer needed (the associated part is closed), it would call the doDestroyPage(). This is where you would ideally remove any listeners and dispose of the resources.

So the whole class goes here:

package com.eclipse_tips.views;

import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.IPage;
import org.eclipse.ui.part.MessagePage;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.PageBookView;

/**
 * @author Prakash G.R.
 *
 */
public class ActivePartTrackerView extends PageBookView {

 @Override
 protected IPage createDefaultPage(PageBook book) {
  MessagePage messagePage = new MessagePage();
  initPage(messagePage);
  messagePage.setMessage("No interested in this part");
  messagePage.createControl(book);
  return messagePage;
 }

 @Override
 protected PageRec doCreatePage(IWorkbenchPart part) {
  MessagePage messagePage = new MessagePage();
  initPage(messagePage);
  messagePage.setMessage("Page for "+part.getTitle());
  messagePage.createControl(getPageBook());
  return new PageRec(part, messagePage);
 }

 @Override
 protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
  pageRecord.page.dispose();
 }

 @Override
 protected IWorkbenchPart getBootstrapPart() {
  IWorkbenchPage page = getSite().getPage();
  if(page != null) {
   // check whether the active part is important to us
   IWorkbenchPart activePart = page.getActivePart();
   return isImportant(activePart)?activePart:null;
  }
  return null;
 }

 @Override
 protected boolean isImportant(IWorkbenchPart part) {
  return part.getSite().getPluginId().startsWith("org.eclipse.ui");
 }

}




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , | 1 comments

Sheet support in SWT

For those who have downloaded 3.5 M7 would have seen Sheets support has been added to SWT and Platform UI has used the API wherever applicable. For those who are wondering what a Sheet means, its an eye-candy in Mac. Here is a sample of a MessageDialog shown with and without Sheet style:



(in case you didn't notice the obvious, I'm back on a Mac :-P)

So what is a Sheet?

A Sheet is essentially a Dialog, which is tied to a parent window. It acts as a Modal Dialog, so the user cannot perform any operations on a window until the Dialog is dismissed. (He is free to work on other windows) The dialog is

  • always attached with the window until dismissed
  • placed in the center of the window
  • moves along when the window is moved
To Sheet or not to Sheet?

  • Use Sheets when the interaction is very short like the question "Is Eclipse a cool product?  ". Opening a 15 step Wizard to choose your life partner is not right place to use Sheet.
  • Do not use Sheets where the user might still need to interact with the Window (like copy something from window and paste it in the Sheet). Since Sheets are Window Modal, it doesn't allow the user to interact with the associated Window
  • Sheets are not Application Modal. So if you want a dialog to be Application Modal, don' use Sheets
  • Unlike normal Dialogs, Sheets do not have title. Avoid Sheets where title provides valuable information.
  • Do not use nested Sheets (says Apple's UI guidelines)
How do I use Sheet?

Simple. Set the SWT.Sheet style to the Shell of the dialog. In case you are using the MessageDialog, pass on the style bit as the last argument to the MessageDialog.open() method




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Commands Part 7: Adding standard commands

In the earlier installments, we have seen adding our commands to menus and toolbars. But what about the standard menu items like Cut, Copy, Paste etc? We'll see how to add these to a context menu of the Sample View.

Create the Sample View through the PDE's Extension Point Wizard. In the SampleView class, notice these lines:


private void hookContextMenu() {

// rest of the code...
getSite().registerContextMenu(menuMgr, viewer);
}

private void fillContextMenu(IMenuManager manager) {

//rest of the code
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}


The first one registers this context menu, so that other plugins can contribute their commands to it. The second one adds a separator, which serves as a place holder/location where the contribution goes. So the SampleView is ready to accept the contributions, lets now contribute:

   <extension
point="org.eclipse.ui.menus">
<menuContribution
locationURI="popup:com.eclipse_tips.commands.part7.views.SampleView?after=additions">
<command commandId="org.eclipse.ui.edit.cut"/>
<command commandId="org.eclipse.ui.edit.copy"/>
<command commandId="org.eclipse.ui.edit.paste"/>
</menuContribution>
</extension>


The id for the context menu, is by default the View's id, and we add our contribution after the separator. We can add any command, either the Platform defined or our own. Result:


Now we have the menu items, images, shortcut keys everything in place (you ought to love the command framework :-)), except that the items are not enabled. Its because there is no handler for these commands for the given context. There are multiple ways to contribute the handler, since we are adding this to our view, lets use the activePartId variable:

   <extension
point="org.eclipse.ui.handlers">
<handler
class="com.eclipse_tips.commands.part7.handlers.CutHandler"
commandId="org.eclipse.ui.edit.cut">
<activeWhen>
<with
variable="activePartId">
<equals
value="com.eclipse_tips.commands.part7.views.SampleView">
</equals>
</with>
</activeWhen>
</handler>


Other commands also have similar handlers. Now:


There you go. In the similar way you can add all the standard commands to any menu/context menu you prefer. Just make sure that you have the handler for the commands with the right activeWhen & enabledWhen expression.

The standard command Ids can be found in the IWorkbenchCommandConstants class (available in 3.5)





Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in Labels: , , , | 2 comments

[Off Topic] Thank you!

 

When Google Web Tookit was released to the public, I played with it and realized that an Eclipse plug-in could simply few things. Since I didn't find any, I started learning Eclipse plug-in development to create one. I've fallen in love with Eclipse and its been a wonderful journey since then. I created this blog to publish the tips I've learnt. Shifted myself from server-side-runtime to IDE. I was the first guy to offer Eclipse Plug-in development training in India. With few like-minded-friends, we even started a company to do this training. The clientele includes several small companies and big companies as well. When IBM extended the Eclipse team in Asia, I was the first hire and moved to Eclipse Platform UI team. Last week my committer election successfully concluded and I've become a committer now! Its one of my happiest moments and this blog has been partially responsible for all of this. I've learnt so many things just for the sake of publishing tips.

I really thank all of the readers of this blog. Your comments, emails, queries kept me going. Thanks!




Tired of unwanted and irrelevant results in Google? Try Eclipse Search, the Customized Seach Engine that gives you only relevant results - Powered by Google! It has plugins for Eclipse and Firefox/IE



Posted in | 10 comments