protected boolean askReloadFromDisk(final VirtualFile file, final Document document) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (!isDocumentUnsaved(document)) return true;

    String message = UIBundle.message("file.cache.conflict.message.text", file.getPresentableUrl());
    if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message);
    final DialogBuilder builder = new DialogBuilder((Project) null);
    builder.setCenterPanel(new JLabel(message, Messages.getQuestionIcon(), SwingConstants.CENTER));
    builder.addOkAction().setText(UIBundle.message("file.cache.conflict.load.fs.changes.button"));
    builder
        .addCancelAction()
        .setText(UIBundle.message("file.cache.conflict.keep.memory.changes.button"));
    builder.addAction(
        new AbstractAction(UIBundle.message("file.cache.conflict.show.difference.button")) {
          @Override
          public void actionPerformed(ActionEvent e) {
            String title =
                UIBundle.message(
                    "file.cache.conflict.for.file.dialog.title", file.getPresentableUrl());
            final ProjectEx project =
                (ProjectEx) ProjectLocator.getInstance().guessProjectForFile(file);

            SimpleDiffRequest request = new SimpleDiffRequest(project, title);
            FileType fileType = file.getFileType();
            String fsContent = LoadTextUtil.loadText(file).toString();
            request.setContents(
                new SimpleContent(fsContent, fileType),
                new DocumentContent(project, document, fileType));
            request.setContentTitles(
                UIBundle.message("file.cache.conflict.diff.content.file.system.content"),
                UIBundle.message("file.cache.conflict.diff.content.memory.content"));
            DialogBuilder diffBuilder = new DialogBuilder(project);
            DiffPanelImpl diffPanel =
                (DiffPanelImpl)
                    DiffManager.getInstance()
                        .createDiffPanel(diffBuilder.getWindow(), project, diffBuilder, null);
            diffPanel.getOptions().setShowSourcePolicy(DiffPanelOptions.ShowSourcePolicy.DONT_SHOW);
            diffBuilder.setCenterPanel(diffPanel.getComponent());
            diffBuilder.setDimensionServiceKey("FileDocumentManager.FileCacheConflict");
            diffPanel.setDiffRequest(request);
            diffBuilder
                .addOkAction()
                .setText(UIBundle.message("file.cache.conflict.save.changes.button"));
            diffBuilder.addCancelAction();
            diffBuilder.setTitle(title);
            if (diffBuilder.show() == DialogWrapper.OK_EXIT_CODE) {
              builder.getDialogWrapper().close(DialogWrapper.CANCEL_EXIT_CODE);
            }
          }
        });
    builder.setTitle(UIBundle.message("file.cache.conflict.dialog.title"));
    builder.setButtonsAlignment(SwingConstants.CENTER);
    builder.setHelpId("reference.dialogs.fileCacheConflict");
    return builder.show() == 0;
  }
  public final ActionGroup createPopupGroup(boolean skipHideAction) {
    final DefaultActionGroup group = createGearPopupGroup();
    if (!ToolWindowId.PREVIEW.equals(myInfo.getId())) {
      group.add(myToggleContentUiTypeAction);
    }

    final DefaultActionGroup moveGroup =
        new DefaultActionGroup(UIBundle.message("tool.window.move.to.action.group.name"), true);
    final ToolWindowAnchor anchor = myInfo.getAnchor();
    if (anchor != ToolWindowAnchor.TOP) {
      final AnAction topAction =
          new ChangeAnchorAction(
              UIBundle.message("tool.window.move.to.top.action.name"), ToolWindowAnchor.TOP);
      moveGroup.add(topAction);
    }
    if (anchor != ToolWindowAnchor.LEFT) {
      final AnAction leftAction =
          new ChangeAnchorAction(
              UIBundle.message("tool.window.move.to.left.action.name"), ToolWindowAnchor.LEFT);
      moveGroup.add(leftAction);
    }
    if (anchor != ToolWindowAnchor.BOTTOM) {
      final AnAction bottomAction =
          new ChangeAnchorAction(
              UIBundle.message("tool.window.move.to.bottom.action.name"), ToolWindowAnchor.BOTTOM);
      moveGroup.add(bottomAction);
    }
    if (anchor != ToolWindowAnchor.RIGHT) {
      final AnAction rightAction =
          new ChangeAnchorAction(
              UIBundle.message("tool.window.move.to.right.action.name"), ToolWindowAnchor.RIGHT);
      moveGroup.add(rightAction);
    }
    group.add(moveGroup);

    DefaultActionGroup resize =
        new DefaultActionGroup(ActionsBundle.groupText("ResizeToolWindowGroup"), true);
    resize.add(new ResizeToolWindowAction.Left(myToolWindow, this));
    resize.add(new ResizeToolWindowAction.Right(myToolWindow, this));
    resize.add(new ResizeToolWindowAction.Up(myToolWindow, this));
    resize.add(new ResizeToolWindowAction.Down(myToolWindow, this));
    resize.add(ActionManager.getInstance().getAction("MaximizeToolWindow"));

    group.add(resize);
    if (!skipHideAction) {
      group.addSeparator();
      group.add(new HideAction());
    }
    return group;
  }
 protected JetBrainsTvAction(@NotNull @NonNls final String channel) {
   final String fullProductName = ApplicationNamesInfo.getInstance().getFullProductName();
   getTemplatePresentation().setText(fullProductName + " TV");
   getTemplatePresentation()
       .setDescription(
           UIBundle.message("welcome.screen.jetbrains.tv.action.description", fullProductName));
   myUrl = JETBRAINS_TV_URL + "channel/" + channel;
 }
  @Override
  public void reloadFromDisk(@NotNull final Document document) {
    ApplicationManager.getApplication().assertIsDispatchThread();

    final VirtualFile file = getFile(document);
    assert file != null;

    if (!fireBeforeFileContentReload(file, document)) {
      return;
    }

    final Project project = ProjectLocator.getInstance().guessProjectForFile(file);
    CommandProcessor.getInstance()
        .executeCommand(
            project,
            new Runnable() {
              @Override
              public void run() {
                ApplicationManager.getApplication()
                    .runWriteAction(
                        new ExternalChangeAction.ExternalDocumentChange(document, project) {
                          @Override
                          public void run() {
                            boolean wasWritable = document.isWritable();
                            DocumentEx documentEx = (DocumentEx) document;
                            documentEx.setReadOnly(false);
                            LoadTextUtil.setCharsetWasDetectedFromBytes(file, null);
                            documentEx.replaceText(
                                LoadTextUtil.loadText(file), file.getModificationStamp());
                            documentEx.setReadOnly(!wasWritable);
                          }
                        });
              }
            },
            UIBundle.message("file.cache.conflict.action"),
            null,
            UndoConfirmationPolicy.REQUEST_CONFIRMATION);

    myUnsavedDocuments.remove(document);

    myMultiCaster.fileContentReloaded(file, document);
  }
 private static String getChooserTitle(final FileChooserDescriptor descriptor) {
   final String title = descriptor.getTitle();
   return title != null ? title : UIBundle.message("file.chooser.default.title");
 }
  public boolean execute(CompileContext context) {
    final RunConfiguration runConfiguration =
        CompileStepBeforeRun.getRunConfiguration(context.getCompileScope());
    if (!(runConfiguration instanceof FlexUnitRunConfiguration)) {
      return true;
    }

    final Ref<Boolean> isDumb = new Ref<>(false);
    final RuntimeConfigurationException validationError =
        ApplicationManager.getApplication()
            .runReadAction(
                new NullableComputable<RuntimeConfigurationException>() {
                  public RuntimeConfigurationException compute() {
                    if (DumbService.getInstance(myProject).isDumb()) {
                      isDumb.set(true);
                      return null;
                    }
                    try {
                      runConfiguration.checkConfiguration();
                      return null;
                    } catch (RuntimeConfigurationException e) {
                      return e;
                    }
                  }
                });

    if (isDumb.get()) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("dumb.mode.flex.unit.warning"),
          null,
          -1,
          -1);
      return false;
    }

    if (validationError != null) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("configuration.not.valid", validationError.getMessage()),
          null,
          -1,
          -1);
      return false;
    }

    int flexUnitPort = ServerConnectionBase.getFreePort(FLEX_UNIT_PORT_START, PORTS_ATTEMPT_NUMBER);
    if (flexUnitPort == -1) {
      context.addMessage(
          CompilerMessageCategory.ERROR, FlexBundle.message("no.free.port"), null, -1, -1);
      return false;
    }

    final int socketPolicyPort;
    if (SystemInfo.isWindows
        && ServerConnectionBase.tryPort(SwfPolicyFileConnection.DEFAULT_PORT)) {
      socketPolicyPort = SwfPolicyFileConnection.DEFAULT_PORT;
    } else {
      socketPolicyPort =
          ServerConnectionBase.getFreePort(SWC_POLICY_PORT_START, PORTS_ATTEMPT_NUMBER);
    }

    if (socketPolicyPort == -1) {
      context.addMessage(
          CompilerMessageCategory.ERROR, FlexBundle.message("no.free.port"), null, -1, -1);
      return false;
    }

    final FlexUnitRunnerParameters params =
        ((FlexUnitRunConfiguration) runConfiguration).getRunnerParameters();
    params.setPort(flexUnitPort);
    params.setSocketPolicyPort(socketPolicyPort);

    final Ref<Module> moduleRef = new Ref<>();
    final Ref<FlexBuildConfiguration> bcRef = new Ref<>();
    final Ref<FlexUnitSupport> supportRef = new Ref<>();

    ApplicationManager.getApplication()
        .runReadAction(
            () -> {
              if (DumbService.getInstance(myProject).isDumb()) return;

              try {
                final Pair<Module, FlexBuildConfiguration> moduleAndBC =
                    params.checkAndGetModuleAndBC(myProject);
                moduleRef.set(moduleAndBC.first);
                bcRef.set(moduleAndBC.second);
                supportRef.set(FlexUnitSupport.getSupport(moduleAndBC.second, moduleAndBC.first));
              } catch (RuntimeConfigurationError e) {
                // already checked above, can't happen
                throw new RuntimeException(e);
              }
            });

    final Module module = moduleRef.get();
    final FlexBuildConfiguration bc = bcRef.get();
    final FlexUnitSupport support = supportRef.get();

    if (bc == null || support == null) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("dumb.mode.flex.unit.warning"),
          null,
          -1,
          -1);
      return false;
    }

    final GlobalSearchScope moduleScope = GlobalSearchScope.moduleScope(module);

    StringBuilder imports = new StringBuilder();
    StringBuilder code = new StringBuilder();

    final boolean flexUnit4;
    switch (params.getScope()) {
      case Class:
        {
          final Ref<Boolean> isFlexUnit1Suite = new Ref<>();
          final Ref<Boolean> isSuite = new Ref<>();
          Set<String> customRunners =
              ApplicationManager.getApplication()
                  .runReadAction(
                      new NullableComputable<Set<String>>() {
                        public Set<String> compute() {
                          if (DumbService.getInstance(myProject).isDumb()) return null;
                          Set<String> result = new THashSet<>();
                          final JSClass clazz =
                              (JSClass)
                                  ActionScriptClassResolver.findClassByQNameStatic(
                                      params.getClassName(), moduleScope);
                          collectCustomRunners(result, clazz, support, null);
                          isFlexUnit1Suite.set(support.isFlexUnit1SuiteSubclass(clazz));
                          isSuite.set(support.isSuite(clazz));
                          return result;
                        }
                      });

          if (customRunners == null) {
            context.addMessage(
                CompilerMessageCategory.ERROR,
                FlexBundle.message("dumb.mode.flex.unit.warning"),
                null,
                -1,
                -1);
            return false;
          }
          // FlexUnit4 can't run FlexUnit1 TestSuite subclasses, fallback to FlexUnit1 runner
          flexUnit4 = support.flexUnit4Present && !isFlexUnit1Suite.get();
          generateImportCode(imports, params.getClassName(), customRunners);
          generateTestClassCode(code, params.getClassName(), customRunners, isSuite.get());
        }
        break;

      case Method:
        {
          Set<String> customRunners =
              ApplicationManager.getApplication()
                  .runReadAction(
                      new NullableComputable<Set<String>>() {
                        public Set<String> compute() {
                          if (DumbService.getInstance(myProject).isDumb()) return null;
                          Set<String> result = new THashSet<>();
                          final JSClass clazz =
                              (JSClass)
                                  ActionScriptClassResolver.findClassByQNameStatic(
                                      params.getClassName(), moduleScope);
                          collectCustomRunners(result, clazz, support, null);
                          return result;
                        }
                      });
          if (customRunners == null) {
            context.addMessage(
                CompilerMessageCategory.ERROR,
                FlexBundle.message("dumb.mode.flex.unit.warning"),
                null,
                -1,
                -1);
            return false;
          }

          flexUnit4 = support.flexUnit4Present;
          generateImportCode(imports, params.getClassName(), customRunners);
          generateTestMethodCode(
              code, params.getClassName(), params.getMethodName(), customRunners);
        }
        break;

      case Package:
        {
          final Collection<Pair<String, Set<String>>> classes =
              ApplicationManager.getApplication()
                  .runReadAction(
                      new NullableComputable<Collection<Pair<String, Set<String>>>>() {
                        public Collection<Pair<String, Set<String>>> compute() {
                          if (DumbService.getInstance(myProject).isDumb()) return null;

                          final Collection<Pair<String, Set<String>>> result = new ArrayList<>();
                          JSPackageIndex.processElementsInScopeRecursive(
                              params.getPackageName(),
                              new JSPackageIndex.PackageQualifiedElementsProcessor() {
                                public boolean process(
                                    String qualifiedName,
                                    JSPackageIndexInfo.Kind kind,
                                    boolean isPublic) {
                                  if (kind == JSPackageIndexInfo.Kind.CLASS) {
                                    PsiElement clazz =
                                        ActionScriptClassResolver.findClassByQNameStatic(
                                            qualifiedName, moduleScope);
                                    if (clazz instanceof JSClass
                                        && support.isTestClass((JSClass) clazz, false)) {
                                      Set<String> customRunners = new THashSet<>();
                                      collectCustomRunners(
                                          customRunners, (JSClass) clazz, support, null);
                                      result.add(
                                          Pair.create(
                                              ((JSClass) clazz).getQualifiedName(), customRunners));
                                    }
                                  }
                                  return true;
                                }
                              },
                              moduleScope,
                              myProject);
                          return result;
                        }
                      });

          if (classes == null) {
            context.addMessage(
                CompilerMessageCategory.ERROR,
                FlexBundle.message("dumb.mode.flex.unit.warning"),
                null,
                -1,
                -1);
            return false;
          }

          if (classes.isEmpty()) {
            String message =
                MessageFormat.format("No tests found in package ''{0}''", params.getPackageName());
            context.addMessage(CompilerMessageCategory.WARNING, message, null, -1, -1);
            return false;
          }

          flexUnit4 = support.flexUnit4Present;
          for (Pair<String, Set<String>> classAndRunner : classes) {
            generateImportCode(imports, classAndRunner.first, classAndRunner.second);
            generateTestClassCode(code, classAndRunner.first, classAndRunner.second, false);
          }
        }
        break;
      default:
        flexUnit4 = false;
        assert false : "Unknown scope: " + params.getScope();
    }

    if (!flexUnit4 && bc.isPureAs()) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("cant.execute.flexunit1.for.pure.as.bc"),
          null,
          -1,
          -1);
    }

    String launcherText;
    try {
      launcherText = getLauncherTemplate(bc);
    } catch (IOException e) {
      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
      return false;
    }

    final boolean desktop = bc.getTargetPlatform() == TargetPlatform.Desktop;
    if (desktop) {
      generateImportCode(imports, "flash.desktop.NativeApplication");
    }

    launcherText = replace(launcherText, "/*imports*/", imports.toString());
    launcherText =
        replace(
            launcherText,
            "/*test_runner*/",
            flexUnit4
                ? FlexCommonUtils.FLEXUNIT_4_TEST_RUNNER
                : FlexCommonUtils.FLEXUNIT_1_TEST_RUNNER);
    launcherText = replace(launcherText, "/*code*/", code.toString());
    launcherText = replace(launcherText, "/*port*/", String.valueOf(flexUnitPort));
    launcherText = replace(launcherText, "/*socketPolicyPort*/", String.valueOf(socketPolicyPort));
    launcherText = replace(launcherText, "/*module*/", module.getName());
    if (!bc.isPureAs()) {
      final FlexUnitRunnerParameters.OutputLogLevel logLevel = params.getOutputLogLevel();
      launcherText = replace(launcherText, "/*isLogEnabled*/", logLevel != null ? "1" : "0");
      launcherText =
          replace(
              launcherText,
              "/*logLevel*/",
              logLevel != null
                  ? logLevel.getFlexConstant()
                  : FlexUnitRunnerParameters.OutputLogLevel.All.getFlexConstant());
    }

    final File tmpDir = new File(getPathToFlexUnitTempDirectory(myProject));
    boolean ok = true;
    if (tmpDir.isFile()) ok &= FileUtil.delete(tmpDir);
    if (!tmpDir.isDirectory()) ok &= tmpDir.mkdirs();
    if (!ok) {
      final String message =
          UIBundle.message(
              "create.new.folder.could.not.create.folder.error.message",
              FileUtil.toSystemDependentName(tmpDir.getPath()));
      context.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
      return false;
    }

    final String fileName =
        FlexCommonUtils.FLEX_UNIT_LAUNCHER
            + FlexCommonUtils.getFlexUnitLauncherExtension(bc.getNature());
    final File launcherFile = new File(tmpDir, fileName);
    FileUtil.delete(launcherFile);

    try {
      FileUtil.writeToFile(launcherFile, launcherText);
    } catch (IOException e) {
      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
      return false;
    }

    context.putUserData(FILES_TO_DELETE, Collections.singletonList(launcherFile.getPath()));
    return true;
  }
 public String getTooltipText() {
   return isReadonlyApplicable()
       ? UIBundle.message("read.only.attr.panel.double.click.to.toggle.attr.tooltip.text")
       : null;
 }
    private void setOrientation(boolean isVerticalSplit) {
      removeAll();

      if (!myShowDividerControls) {
        return;
      }

      int xMask = isVerticalSplit ? 1 : 0;
      int yMask = isVerticalSplit ? 0 : 1;

      Icon glueIcon = isVerticalSplit ? AllIcons.General.SplitGlueV : AllIcons.General.SplitCenterH;
      int glueFill = isVerticalSplit ? GridBagConstraints.VERTICAL : GridBagConstraints.HORIZONTAL;
      add(
          new JLabel(glueIcon),
          new GridBagConstraints(
              0,
              0,
              1,
              1,
              0,
              0,
              isVerticalSplit ? GridBagConstraints.EAST : GridBagConstraints.NORTH,
              glueFill,
              new Insets(0, 0, 0, 0),
              0,
              0));
      JLabel splitDownlabel =
          new JLabel(isVerticalSplit ? AllIcons.General.SplitDown : AllIcons.General.SplitRight);
      splitDownlabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      splitDownlabel.setToolTipText(
          isVerticalSplit
              ? UIBundle.message("splitter.down.tooltip.text")
              : UIBundle.message("splitter.right.tooltip.text"));
      new ClickListener() {
        @Override
        public boolean onClick(@NotNull MouseEvent e, int clickCount) {
          if (myInnerComponent != null) {
            final int income =
                myVerticalSplit ? myInnerComponent.getHeight() : myInnerComponent.getWidth();
            if (myIsFirst) {
              setFirstSize(myFirstSize + income);
            } else {
              setLastSize(myLastSize + income);
            }
          }
          return true;
        }
      }.installOn(splitDownlabel);

      add(
          splitDownlabel,
          new GridBagConstraints(
              isVerticalSplit ? 1 : 0,
              isVerticalSplit ? 0 : 5,
              1,
              1,
              0,
              0,
              GridBagConstraints.CENTER,
              GridBagConstraints.NONE,
              new Insets(0, 0, 0, 0),
              0,
              0));
      //
      add(
          new JLabel(glueIcon),
          new GridBagConstraints(
              2 * xMask,
              2 * yMask,
              1,
              1,
              0,
              0,
              GridBagConstraints.CENTER,
              glueFill,
              new Insets(0, 0, 0, 0),
              0,
              0));
      JLabel splitCenterlabel =
          new JLabel(
              isVerticalSplit ? AllIcons.General.SplitCenterV : AllIcons.General.SplitCenterH);
      splitCenterlabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      splitCenterlabel.setToolTipText(UIBundle.message("splitter.center.tooltip.text"));
      new ClickListener() {
        @Override
        public boolean onClick(@NotNull MouseEvent e, int clickCount) {
          center();
          return true;
        }
      }.installOn(splitCenterlabel);
      add(
          splitCenterlabel,
          new GridBagConstraints(
              3 * xMask,
              3 * yMask,
              1,
              1,
              0,
              0,
              GridBagConstraints.CENTER,
              GridBagConstraints.NONE,
              new Insets(0, 0, 0, 0),
              0,
              0));
      add(
          new JLabel(glueIcon),
          new GridBagConstraints(
              4 * xMask,
              4 * yMask,
              1,
              1,
              0,
              0,
              GridBagConstraints.CENTER,
              glueFill,
              new Insets(0, 0, 0, 0),
              0,
              0));
      //
      JLabel splitUpLabel =
          new JLabel(isVerticalSplit ? AllIcons.General.SplitUp : AllIcons.General.SplitLeft);
      splitUpLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      splitUpLabel.setToolTipText(
          isVerticalSplit
              ? UIBundle.message("splitter.up.tooltip.text")
              : UIBundle.message("splitter.left.tooltip.text"));
      new ClickListener() {
        @Override
        public boolean onClick(@NotNull MouseEvent e, int clickCount) {
          if (myInnerComponent != null) {
            final int income =
                myVerticalSplit ? myInnerComponent.getHeight() : myInnerComponent.getWidth();
            if (myIsFirst) {
              setFirstSize(myFirstSize + income);
            } else {
              setLastSize(myLastSize + income);
            }
          }
          return true;
        }
      }.installOn(splitUpLabel);

      add(
          splitUpLabel,
          new GridBagConstraints(
              isVerticalSplit ? 5 : 0,
              isVerticalSplit ? 0 : 1,
              1,
              1,
              0,
              0,
              GridBagConstraints.CENTER,
              GridBagConstraints.NONE,
              new Insets(0, 0, 0, 0),
              0,
              0));
      add(
          new JLabel(glueIcon),
          new GridBagConstraints(
              6 * xMask,
              6 * yMask,
              1,
              1,
              0,
              0,
              isVerticalSplit ? GridBagConstraints.WEST : GridBagConstraints.SOUTH,
              glueFill,
              new Insets(0, 0, 0, 0),
              0,
              0));
    }
 public HideAction() {
   copyFrom(ActionManager.getInstance().getAction(HIDE_ACTIVE_WINDOW_ACTION_ID));
   getTemplatePresentation().setText(UIBundle.message("tool.window.hide.action.name"));
 }
public abstract class StatusText {
  public static final SimpleTextAttributes DEFAULT_ATTRIBUTES =
      SimpleTextAttributes.GRAYED_ATTRIBUTES;
  public static final String DEFAULT_EMPTY_TEXT = UIBundle.message("message.nothingToShow");

  @Nullable private Component myOwner;
  private Component myMouseTarget;
  private final MouseMotionListener myMouseMotionListener;
  private final ClickListener myClickListener;

  private boolean myIsDefaultText;

  private String myText = "";
  protected final SimpleColoredComponent myComponent = new SimpleColoredComponent();
  private final List<ActionListener> myClickListeners = new ArrayList<ActionListener>();
  private boolean myHasActiveClickListeners; // calculated field for performance optimization

  protected StatusText(JComponent owner) {
    this();
    attachTo(owner);
  }

  public StatusText() {
    myClickListener =
        new ClickListener() {
          @Override
          public boolean onClick(@NotNull MouseEvent e, int clickCount) {
            if (e.getButton() == MouseEvent.BUTTON1 && clickCount == 1) {
              ActionListener actionListener = findActionListenerAt(e.getPoint());
              if (actionListener != null) {
                actionListener.actionPerformed(new ActionEvent(this, 0, ""));
                return true;
              }
            }
            return false;
          }
        };

    myMouseMotionListener =
        new MouseAdapter() {

          private Cursor myOriginalCursor;

          @Override
          public void mouseMoved(final MouseEvent e) {
            if (isStatusVisible()) {
              if (findActionListenerAt(e.getPoint()) != null) {
                if (myOriginalCursor == null) {
                  myOriginalCursor = myMouseTarget.getCursor();
                  myMouseTarget.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                }
              } else if (myOriginalCursor != null) {
                myMouseTarget.setCursor(myOriginalCursor);
                myOriginalCursor = null;
              }
            }
          }
        };

    myComponent.setOpaque(false);
    myComponent.setFont(UIUtil.getLabelFont());
    setText(DEFAULT_EMPTY_TEXT, DEFAULT_ATTRIBUTES);
    myIsDefaultText = true;
  }

  public void attachTo(@Nullable Component owner) {
    attachTo(owner, owner);
  }

  public void attachTo(@Nullable Component owner, @Nullable Component mouseTarget) {
    if (myMouseTarget != null) {
      myClickListener.uninstall(myMouseTarget);
      myMouseTarget.removeMouseMotionListener(myMouseMotionListener);
    }

    myOwner = owner;
    myMouseTarget = mouseTarget;

    if (myMouseTarget != null) {
      myClickListener.installOn(myMouseTarget);
      myMouseTarget.addMouseMotionListener(myMouseMotionListener);
    }
  }

  protected abstract boolean isStatusVisible();

  @Nullable
  private ActionListener findActionListenerAt(Point point) {
    if (!myHasActiveClickListeners || !isStatusVisible()) return null;

    point = SwingUtilities.convertPoint(myMouseTarget, point, myOwner);

    Rectangle b = getTextComponentBound();
    if (b.contains(point)) {
      int index = myComponent.findFragmentAt(point.x - b.x);
      if (index >= 0 && index < myClickListeners.size()) {
        return myClickListeners.get(index);
      }
    }
    return null;
  }

  protected Rectangle getTextComponentBound() {
    Rectangle ownerRec = myOwner == null ? new Rectangle(0, 0, 0, 0) : myOwner.getBounds();

    Dimension size = myComponent.getPreferredSize();
    int x = (ownerRec.width - size.width) / 2;
    int y = (ownerRec.height - size.height) / 3;
    return new Rectangle(x, y, size.width, size.height);
  }

  @NotNull
  public String getText() {
    return myText;
  }

  public StatusText setText(String text) {
    return setText(text, DEFAULT_ATTRIBUTES);
  }

  public StatusText setText(String text, SimpleTextAttributes attrs) {
    return clear().appendText(text, attrs);
  }

  public StatusText clear() {
    myText = "";
    myComponent.clear();
    myClickListeners.clear();
    myHasActiveClickListeners = false;
    if (myOwner != null && isStatusVisible()) myOwner.repaint();
    return this;
  }

  public StatusText appendText(String text) {
    return appendText(text, DEFAULT_ATTRIBUTES);
  }

  public StatusText appendText(String text, SimpleTextAttributes attrs) {
    return appendText(text, attrs, null);
  }

  public StatusText appendText(String text, SimpleTextAttributes attrs, ActionListener listener) {
    if (myIsDefaultText) {
      clear();
      myIsDefaultText = false;
    }

    myText += text;
    myComponent.append(text, attrs);
    myClickListeners.add(listener);
    if (listener != null) {
      myHasActiveClickListeners = true;
    }
    if (myOwner != null && isStatusVisible()) myOwner.repaint();
    return this;
  }

  public void paint(Component owner, Graphics g) {
    if (!isStatusVisible()) return;

    if (owner == myOwner) {
      doPaintStatusText(g, getTextComponentBound());
    } else {
      paintOnComponentUnderViewport(owner, g);
    }
  }

  private void paintOnComponentUnderViewport(Component component, Graphics g) {
    JBViewport viewport = ObjectUtils.tryCast(myOwner, JBViewport.class);
    if (viewport == null || viewport.getView() != component || viewport.isPaintingNow()) return;

    // We're painting a component which has a viewport as it's ancestor.
    // As the viewport paints status text, we'll erase it, so we need to schedule a repaint for the
    // viewport with status text's bounds.
    // But it causes flicker, so we paint status text over the component first and then schedule the
    // viewport repaint.

    Rectangle textBoundsInViewport = getTextComponentBound();

    int xInOwner = textBoundsInViewport.x - component.getX();
    int yInOwner = textBoundsInViewport.y - component.getY();
    Rectangle textBoundsInOwner =
        new Rectangle(xInOwner, yInOwner, textBoundsInViewport.width, textBoundsInViewport.height);
    doPaintStatusText(g, textBoundsInOwner);

    viewport.repaint(textBoundsInViewport);
  }

  private void doPaintStatusText(Graphics g, Rectangle textComponentBounds) {
    myComponent.setBounds(0, 0, textComponentBounds.width, textComponentBounds.height);
    Graphics2D g2 =
        (Graphics2D)
            g.create(
                textComponentBounds.x,
                textComponentBounds.y,
                textComponentBounds.width,
                textComponentBounds.height);
    myComponent.paint(g2);
    g2.dispose();
  }

  @NotNull
  public SimpleColoredComponent getComponent() {
    return myComponent;
  }

  public Dimension getPreferredSize() {
    return myComponent.getPreferredSize();
  }
}