Pages

Tuesday, 23 November 2010

Remove the Progress Monitor Part from Jface Wizard

Many a times the Jface wizards have posed an annoying problem to me... Even after setting the needsProgressMonitor attribute to false, there is some extra space left at the bottom of the wizard dialog which contains the invisible progress monitor part.

I finally managed a hack that removes that part, here is the code that you need to use -

WizardDialog dialog = new WizardDialog(null, wizard) {
           
            @Override
            protected Control createDialogArea(Composite parent) {
                Control ctrl = super.createDialogArea(parent);
                getProgressMonitor();
                return ctrl;
            }
           
            @Override
            protected IProgressMonitor getProgressMonitor() {
                ProgressMonitorPart monitor = (ProgressMonitorPart) super.getProgressMonitor();
                GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
                gridData.heightHint = 0;
                monitor.setLayoutData(gridData);
                monitor.setVisible(false);
                return monitor;
            }
   };

This safely removes the extra space from the bottom of the wizard. Though you will not be able to use the progress monitor from this point onwards in the wizard where this code is placed.

Thursday, 3 June 2010

Delete Logs action on Eclipse Problems View

The standard Eclipse Errors view has a provision for deleting all the logs by simply using the Delete Logs action available in the view tool bar as shown -

But there is no such provision available for the Eclipse Problems View. If you are planning to utilize the problems view in your product then this might be a big headache. I encountered the same thing and decided to contribute one of my own action for doing this job.

Extension Point Definition -





Extension Point Details -

The class DeleteLogsAction looks as follows -

public class DeleteLogsAction extends Action implements IViewActionDelegate {

    private IViewPart view;

    private final static String DIALOG_TITLE = "Confirm Delete";

    private final static String DIALOG_MESSAGE = "Are you sure you want to delete all logged events?";

    private final static String MARKER_TYPE = IMarker.PROBLEM;

    private final static int DEPTH = IResource.DEPTH_INFINITE;

    private final static boolean INCLUDE_SUB_TYPES = true;

    public DeleteLogsAction() {
        this.setImageDescriptor(Activator.getImageDescriptor("\\icons\\remove.gif"));
        this.setDisabledImageDescriptor(Activator.getImageDescriptor("\\icons\\remove_disabled.gif"));
    }

    @Override
    public void init(IViewPart view) {
        this.view = view;
    }

    @Override
    public void run(IAction action) {
        try
        {
            if(ResourcesPlugin.getWorkspace() != null)
            {
                IWorkspace workSpace = ResourcesPlugin.getWorkspace();
                if(workSpace.getRoot() != null)
                {
                    IWorkspaceRoot root = workSpace.getRoot();
                    IMarker[] markers = root.findMarkers(MARKER_TYPE, INCLUDE_SUB_TYPES, DEPTH);
                    if(markers != null && markers.length > 0)
                    {
                        boolean confirm = MessageDialog.openConfirm(view.getSite().getShell(), DIALOG_TITLE,
                                DIALOG_MESSAGE);

                        if(confirm)
                        {
                            root.deleteMarkers(MARKER_TYPE, INCLUDE_SUB_TYPES, DEPTH);
                        }
                        else
                        {
                            return;
                        }
                    }
                }
            }
        }
        catch ( CoreException e )
        {
            PALogger.logError(Activator.PLUGIN_ID, "Failed to delete the problem markers.", e);
        }
    }

    @Override
    public void selectionChanged(IAction action, ISelection selection) {
    }

Once this part is done, You will be able to see the new action in the problems view -

Run editor in event loop

We have only seen dialogs in a modal mode but recently I came across a requirement where we had to open an editor from a UI thread and then hold the Job until that editor was either closed or a button 'continue' was pressed upon it.


The challenge here was halting/suspending the job until some user action has been received. So here is the trick that I applied towards this problem -

  • Get the active workbench window -
 IWorkbenchWindow dw = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
  •  Get the active page -
IWorkbenchPage page = dw.getActivePage();        
  • Open the editor -
IEditorPart part = IDE.openEditor(page, editorInput, true);
  • Use the following method to keep the job suspended until the editorpart was disposed -
runEventLoop(page, part.getEditorInput(), monitor);

private void runEventLoop(IWorkbenchPage page, IEditorInput editorInput, ProgressMonitor monitor) {
        Display display = Display.getCurrent();
        if(page != null && editorInput != null)
        {
            while (page.findEditor(editorInput) != null)
            {
                IEditorPart part = page.findEditor(editorInput);
                if(!monitor.isCanceled())
                {
                    if(!display.readAndDispatch())
                    {
                        display.sleep();
                    }
                }
                else
                {
                    if(part.getSite() != null && part.getSite().getPage() != null)
                    {
                        part.getSite().getPage().closeEditor(part, false);
                    }
                }
            }
            display.update();
        }
    }

So, as long as the editor part is received on page.findEditor(editorInput) line the display would sleep and hence our job will sit dummy. As soon as the editor part is not found, the while loop will exit updating the display in the process.

Change types in Eclipse Problems View using EMF

An interesting problem that I recently faced was to change the type for the messages appearing in the Type column of Eclipse Problems view. I am working with EMF plugins at the moment and had to come up with a solution that suits the EMF environment. By default any validation message in EMF has a type of 'EMF Problem' associated with it but in certain situations we need to change that to meet our business needs. So, here is what you need to do -

  • Define your own Resource Markers in some plugin as shown -


  • Override the validate action present in the ActionBarContributor class of your EMF editor plugin. By default it points to the "org.eclipse.emf.edit.ui.action.ValidateAction", create your own class extending this one.
  • In the extended class, override the method "Diagnostic validate(final IProgressMonitor progressMonitor)" in the following manner -
protected Diagnostic validate(final IProgressMonitor progressMonitor) 

    /* Overridden the utility to supply the new Marker ID */
    eclipseResourcesUtil = EMFPlugin.IS_RESOURCES_BUNDLE_AVAILABLE ?
     new EclipseResourcesUtil() {
               @Override
                protected String getMarkerID() {
                 return "New Resource Marker ID" ;
                 }
       } : null;
               /*Changes finished*/
  }
  • When you will now execute the validate action, the custom type specified while defining your own resource marker shall be shown instead of 'EMF Problem'.

Stop Connection from moving when Nodes are moved

For this to take place we need to understand the concept of fixedconnectionAnchor. Try to extend the class AbstractConnectionAnchor and override the methods getLocation() and the getOwner() method to suit to your needs.

Then go to the editpart you wish the connection to stick to and just write the following code -

public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connEditPart) {
     if (fixedAnchor == null) 
       {
         fixedAnchor = new FixedConnectionAnchor(getNodeFigure());
         getNodeFigure().addAncestorListener(fixedAnchor);
       }
  return fixedAnchor;
}

public ConnectionAnchor getTargetConnectionAnchor(Request request) {
    if (fixedAnchor == null) 
      {
        fixedAnchor = new FixedConnectionAnchor(getNodeFigure());
        getNodeFigure().addAncestorListener(fixedAnchor);
      }
  return fixedAnchor;
}

Cascaded menus in GMF

To get nested menus while dragging a connection out from a node in GMF we need to make our own command that extends PopupMenuCommand.

The Steps will be -
1. Override the method doExecuteWithResult().
2. Inside the method we would need to call another method createPopupMenu().
3. To populate the contents of the popupmenu we need the connection and endMenu contents.
4. To get the endMenu Contents write a method similar to :-

protected List getEndMenuContent(Object connectionItem)
{
  ModelingAssistantService mInstance = ModelingAssistantService.getInstance();
  if(connectionItem instanceof IElementType)
   {
     IElementType connectionType = (IElementType) connectionItem;
     List returneMe = new ArrayList();
     List menuContent = mInstance.getTypesForTarget(getKnownEnd(), connectionType);
     for (int i = 0; i <>
      {
         if(menuContent.get(i) instanceof IElementType)
                     returneMe.add(menuContent.get(i));
         else {
               returneMe.add(new PopupMenu.CascadingMenu("{name of the catrgory}", new PopupMenu(
               (List) (menuContent.get(i)), getConnectionAndEndLabelProvider(menuContent.get(i)))));
              }
      }
      returneMe.add(EXISTING_ELEMENT);
      return returneMe;
 }
return Collections.EMPTY_LIST;
}

Once you are done with these stpes then go ahead with the normal 
procedure to call this command in the ContainerNodeEditPolicy of your projetct.