Running a custom progressBar on plugin Action

I'll start with some explanations -

I've written a plugin that navigates from a class annotation to a corresponding xml attribute.

since I model the data by myself, I traverse between files TextEditors instances, so I have

to run on the DispatchThread.

some code to illustrate :

public boolean removeErrorTextRangeForElement(PsiElement annoElement, RangeHighlighter rangeHighlighter) {
  FileEditor[] editors = openEditorIfNeeded(annoElement);
  for (FileEditor fileEditor : editors) {
   if (fileEditor instanceof TextEditor) {
    Editor editor = ((TextEditor) fileEditor).getEditor();
    try {
     editor.getMarkupModel().removeHighlighter(rangeHighlighter);
     editor.getMarkupModel().removeAllHighlighters();
    } catch (Throwable e) {
     LOGGER.error("Tried to remove a RangeHighlighter from an Editor that are not related, RangeHL: " + rangeHighlighter + " XmlAttribElement: " + annoElement.getText(), e);
    }
    return true;
   }
  }
  return false;
}

I'm removing any Rangehighlighter associated with the XML attribute element, (The passed RangeHighlighter is a cache saved one from the model...if you

want to see the project's code, check it out here : http://code.google.com/p/annoref/

NOW, for the real problem at hand :

I have an action the performs a a reIndexing of the plugin's model,

While the Plugin reIndex (take a few good seconds) I want to show some sort of a progressBar,

using the Task.Backgroundable isn't possible for me, cause it can not run a thread that needs to interact with that DispatchThread's descendants.

So I thought about making a custom ProgressBar, which is pretty simple,and pop it while the plugin is running.

I just want to know if OpenApi has some way of gettting it's main Frame,

or should I Just use the SwingUtilies invokeLater (or the ApplicationManager which is quite the same)

I want to add my custom JPanel to the frame, and not run a runnable via the .invokeLater, is that the right way to do it?


5 comments
Comment actions Permalink

Am I right understanding that you have a relatively long processing (plugin model reindexing) that is performed from EDT and the problem is that you can't show its progress?

Denis

0
Comment actions Permalink

That is correct.

I've also tried various implementations for ProgressIndicator, but if I'm not mistaken

it works the same way as the Task.Backgroundable.

0
Comment actions Permalink

That doesn't work this way - progress repainting is performed at EDT but you say that EDT is busy executing your custom logic.

So, you need to free up EDT in order to allow progress repainting. That can be achieved by moving custom logic to non-EDT thread and exploiting standard Task.Backgroundable.

Denis

0
Comment actions Permalink

I realize that Progress repainting is done at EDT level, to make things clearer (for you viewer out there)

my current action :


public class SQLRefNavigationResetAction extends AnAction {
private static final Logger LOGGER = Logger.getInstance(SQLRefNavigationResetAction.class.getName());

public void actionPerformed(AnActionEvent event) {
  final Project project = (Project) event.getDataContext().getData(PlatformDataKeys.PROJECT.getName());

     final BackgroundableProcessIndicator progress = new BackgroundableProcessIndicator(project, "RexIndexing xml change...", PerformInBackgroundOption.DEAF, "Cancel SQLRef Indexing", "", true);
  if (ApplicationManager.getApplication().isDispatchThread()) {
   ProgressManager.getInstance().runProcess(new Runnable() {
    @Override
    public void run() {
     try {
      if (!progress.isRunning()) {
       progress.start();
      }
      progress.setFraction(0.1);
      if (project != null) {
       SQLRefApplication.resetProjectClassesAndReferences(project);
       progress.setFraction(0.2);
       SQLRefApplication.initializeManagersForProject(project);
       progress.setFraction(0.3);
       final VirtualFile[] xmlVfToScan = PackageIndex.getInstance(project).getDirectoriesByPackageName("queries", true);
       progress.setFraction(0.4);
       ServiceManager.getService(project, SQLRefDataAccessor.class).lookForConventionalReferencedFile(project, xmlVfToScan);
       progress.setFraction(0.5);
       final VirtualFile[] classVfToScan = PackageIndex.getInstance(project).getDirectoriesByPackageName("com.idi", true);
       progress.setFraction(0.6);
       ServiceManager.getService(project, SQLRefDataAccessor.class).lookForConventionalReferencedFile(project, classVfToScan);
       progress.setFraction(0.7);
       ServiceManager.getService(project, SQLRefDataAccessor.class).findNonCorrelatingSQLRefsInXmlFiles();
       progress.setFraction(0.8);
      }
     } catch (Exception e) {
      LOGGER.error(e);
     }
     progress.setFraction(1.0);
    }
   }, progress);

This works, but the EDT hangs and no progressBar is shown, this code was changed from :

Task.Backgroundable myTask = new Task.Backgroundable(project, "ReIndexing XML for SQLRef Navigation", true) {
    @Override public void run(@NotNull ProgressIndicator indicator) {
     indicator.setText("RexIndexing xml change...");
     indicator.setFraction(0.0);
     try {
      indicator.setFraction(0.1);
      if (project != null) {
       SQLRefApplication.resetProjectClassesAndReferences(project);
       indicator.setFraction(indicator.getFraction() + (0.1));
       SQLRefApplication.initializeManagersForProject(project);
       indicator.setFraction(indicator.getFraction() + (0.1));
       final VirtualFile[] xmlVfToScan = PackageIndex.getInstance(project).getDirectoriesByPackageName("queries", true);
       indicator.setFraction(indicator.getFraction() + (0.1));
       SQLRefApplication.getInstance(project, SQLRefDataAccessor.class).lookForConventionalReferencedFile(project, xmlVfToScan);
       indicator.setFraction(indicator.getFraction() + (0.1));
       final VirtualFile[] classVfToScan = PackageIndex.getInstance(project).getDirectoriesByPackageName("com.idi", true);
       indicator.setFraction(indicator.getFraction() + (0.1));
       SQLRefApplication.getInstance(project, SQLRefDataAccessor.class).lookForConventionalReferencedFile(project, classVfToScan);
       indicator.setFraction(indicator.getFraction() + (0.1));
       SQLRefApplication.getInstance(project, SQLRefDataAccessor.class).findNonCorrelatingSQLRefsInXmlFiles();
       indicator.setFraction(indicator.getFraction() + (0.1));
      }
     } catch (Exception e) {
      System.out.println("e = " + e.getMessage());
     }
     indicator.setFraction(1.0);
    }
   };
   myTask.setCancelText("Stop Indexing For SQLRef").queue();
   myTask.setCancelTooltipText("Terminates current indexing for xml content change");
  }
}

This code fails with a EDT exception.

what I thought of doing, since changing my logic will be quite impossible, is the have a simple swing progressBar, get a JPanel and

just pop it up in another Runnable, that will run under the EDT (using invokeLater), in the code the Action I'll progress the progessBar

instead of the progressIndicator fraction, will that work? (I don't care if the thread will hang during switches, or whatever, as long as the progressBar will

be shown during the other thread operation)


 

0
Comment actions Permalink

Right way to go is to create Task instance and call
'com.intellij.openapi.progress.ProgressManager.getInstance().run(task)' with it. Am I right understanding that the IDE hangs during that? Provide thread dump for that state.

P.S. There is no point in implementing custom progress representation logic as the IDE already has standard facilities for that.

Denis

0

Please sign in to leave a comment.