public static void paintComponentDecoration(
      final GuiEditor editor, final RadComponent component, final Graphics g) {
    // Collect selected components and paint decoration for non selected components
    final ArrayList<RadComponent> selection = new ArrayList<RadComponent>();
    final Rectangle layeredPaneRect = editor.getLayeredPane().getVisibleRect();
    FormEditingUtil.iterate(
        component,
        new FormEditingUtil.ComponentVisitor<RadComponent>() {
          public boolean visit(final RadComponent component) {
            if (!component.getDelegee().isShowing()) { // Skip invisible components
              return true;
            }
            final Shape oldClip = g.getClip();
            final RadContainer parent = component.getParent();
            if (parent != null) {
              final Point p =
                  SwingUtilities.convertPoint(
                      component.getDelegee(), 0, 0, editor.getLayeredPane());
              final Rectangle visibleRect =
                  layeredPaneRect.intersection(
                      new Rectangle(p.x, p.y, parent.getWidth(), parent.getHeight()));
              g.setClip(visibleRect);
            }
            if (component.isSelected()) { // we will paint selection later
              selection.add(component);
            } else {
              paintComponentBoundsImpl(editor, component, g);
            }
            paintGridOutline(editor, component, g);
            if (parent != null) {
              g.setClip(oldClip);
            }
            return true;
          }
        });

    // Let's paint decoration for selected components
    for (int i = selection.size() - 1; i >= 0; i--) {
      final Shape oldClip = g.getClip();
      final RadComponent c = selection.get(i);
      final RadContainer parent = c.getParent();
      if (parent != null) {
        final Point p = SwingUtilities.convertPoint(c.getDelegee(), 0, 0, editor.getLayeredPane());
        final Rectangle visibleRect =
            layeredPaneRect.intersection(
                new Rectangle(p.x, p.y, parent.getWidth(), parent.getHeight()));
        g.setClip(visibleRect);
      }
      paintComponentBoundsImpl(editor, c, g);
      if (parent != null) {
        g.setClip(oldClip);
      }
    }
  }
  public void showPopup(final Action actionToSelect, final boolean ensureSelection) {
    if (myPopupIsShowing) return;

    myPopupIsShowing = true;
    final Point loc = getLocationOnScreen();
    final Rectangle screen = ScreenUtil.getScreenRectangle(loc);
    final Dimension popupSize = myUnderPopup.getPreferredSize();
    final Rectangle intersection =
        screen.intersection(new Rectangle(new Point(loc.x, loc.y + getHeight()), popupSize));
    final boolean above = intersection.height < popupSize.height;
    int y = above ? getY() - popupSize.height : getY() + getHeight();

    final JPopupMenu popup = above ? myAbovePopup : myUnderPopup;

    final Ref<PopupMenuListener> listener = new Ref<PopupMenuListener>();
    listener.set(
        new PopupMenuListener() {
          @Override
          public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}

          @Override
          public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            if (popup != null && listener.get() != null) {
              popup.removePopupMenuListener(listener.get());
            }
            SwingUtilities.invokeLater(
                new Runnable() {
                  @Override
                  public void run() {
                    myPopupIsShowing = false;
                  }
                });
          }

          @Override
          public void popupMenuCanceled(PopupMenuEvent e) {}
        });
    popup.addPopupMenuListener(listener.get());
    popup.show(this, 0, y);

    SwingUtilities.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            if (popup == null || !popup.isShowing() || !myPopupIsShowing) return;

            Action selection = actionToSelect;
            if (selection == null && myOptions.length > 0 && ensureSelection) {
              selection = getAction();
            }

            if (selection == null) return;

            final MenuElement[] elements = popup.getSubElements();
            for (MenuElement eachElement : elements) {
              if (eachElement instanceof JMenuItem) {
                JMenuItem eachItem = (JMenuItem) eachElement;
                if (selection.equals(eachItem.getAction())) {
                  final MenuSelectionManager mgr = MenuSelectionManager.defaultManager();
                  final MenuElement[] path = new MenuElement[2];
                  path[0] = popup;
                  path[1] = eachItem;
                  mgr.setSelectedPath(path);
                  break;
                }
              }
            }
          }
        });
  }
  protected void paintComponent(Graphics graphics) {
    if (justShown) {
      if (!paintTimer.isRunning()) paintTimer.start();
      return;
    }

    if (!isWidthInitialized) {
      // Only paint if the component size has been initialized.  Layout
      // jumps are typical the first time this component is displayed,
      // because the preferred height depends on the component width.
      return;
    }
    Graphics2D g = (Graphics2D) graphics;
    Rectangle clip = g.getClipBounds();

    // Figure out which ImageDatums fall within the clip bounds.
    List<ImageDatum> datums = getAllImageData();
    int[] indices = getIndices(datums.size(), clip);

    // Iterate backwards through indices, so repaints get enqueued
    // in a visually pleasing order.
    for (int i = indices.length - 1; i >= 0; i--) {
      int index = indices[i];
      if (index < 0) {
        continue;
      }
      ImageDatum datum = datums.get(index);
      if (datum == null) {
        // A race; the image disappeared during painting.
        continue;
      }
      RenderedImage image = datum.getImage(this);

      // This queue prevents GC of recently painted images:
      recentImages.add(image);

      Rectangle rect = getBounds(index);
      g.setClip(clip.intersection(rect));

      File file = datum.getFile();
      String label = file.getName();
      ImageDatumType type = datum.getType();
      String tag = type.toString();
      ImageMetadata meta = datum.getMetadata(true);
      int rating = meta.getRating();
      boolean selected = selection.isSelected(datum);
      renderer.paint(g, image, label, tag, rating, rect, selected);

      ImageGroup group = datum.getGroup();
      if (group.isNonTrivial()) {
        ImageGroupCountRenderer.paint(g, rect, datum);
      }
    }
    g.setClip(clip);

    // The control is drawn as an overlay.
    if (controller.isEnabled()) {
      Rectangle ctrlRect = controller.getRect();
      if (ctrlRect != null) {
        if (ctrlRect.intersects(clip)) {
          controller.paint(g);
        }
      }
    }
  }