1

Hallo all. I want to paint a foreign component (i.e. belonging to a different frame's content pane), lets call it frame B, inside a component in frame A.

The problem is that when I paint the component it is painted also in the content pane of the frame A, also it flickers or all gets ugly when the frame is re-sized (i.e. painted few times inside the component, some blue squares are appearing, etc.). The issue becomes more visible if I try to, for example, scale or translate the foreign component before painting.

After a while I sorted it, I think. But I do not feel good with this solution, for some reason I believe there might be a better one, a more appropriate one. Here I need you. :)

This question is more a call for an explanation why the foreign component is painted incorrectly without manipulating it's double buffering feature before and after the paint inside the component. For example with use of either pair of setDoubleBuffered(false) and setDoubleBuffered(true) or disableDoubleBuffering(jP) and enableDoubleBuffering(jP)

respectively before and after the call to the foreign component's paint method.

Thank You in advance. The SSCCE showing the problem is presented below.


import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;

public class PaintForeignComponentSSCCE extends JFrame
{
    public static void main(String[] args) throws IOException
    {
        //foreign panel
        JPanel fp = new JPanel();
        fp.setBackground(Color.PINK);
        fp.setPreferredSize(new Dimension(200, 300));
        //component in which the foreign panel is painted
        ForeignComponentPainter fcp = new ForeignComponentPainter(fp);
        fcp.setPreferredSize(new Dimension(600, 600));
        //main frame's content
        JPanel contentPane = new JPanel();
        contentPane.setBackground(Color.BLUE);
        contentPane.add(fcp);
        //main frame
        JFrame f = new PaintForeignComponentSSCCE();
        f.setContentPane(contentPane);
        f.setSize(700, 500);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);             
        //foreign panel frame
        JFrame fpf = new JFrame();
        JPanel panelFrameContent = new JPanel();
        panelFrameContent.add(fp);
        fpf.setContentPane(panelFrameContent);
        fpf.setSize(400, 400);
        fpf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        fpf.setVisible(true);
    }
}

class ForeignComponentPainter extends JButton
{
    private static final long serialVersionUID = 1L;
    private JPanel jP;

    public ForeignComponentPainter(JPanel jP)
    {
        super();
        this.jP = jP;
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.drawString("OIOI", 50, 50);
        //g2.translate(100, 50);
        //g2.scale(.5, .5);
//      jP.setDoubleBuffered(false);
//      disableDoubleBuffering(jP);
        jP.paint(g2);
//      jP.setDoubleBuffered(true);
//      enableDoubleBuffering(jP);
        //g2.scale(1/.5, 1/.5);
    }   
    public static void disableDoubleBuffering(Component c)
    {
        RepaintManager currentManager = RepaintManager.currentManager(c);
        currentManager.setDoubleBufferingEnabled(false);
    }
    public static void enableDoubleBuffering(Component c)
    {
        RepaintManager currentManager = RepaintManager.currentManager(c);
        currentManager.setDoubleBufferingEnabled(true);
    }
}

Not related to the SSCCE example. The below is unrelated to the problem itself. This piece of code serves the purpose of presentation of how I am implementing Printable in the component which I also want to render in a print preview fashion. The print calls paint of the component (as shown below).


public int print(Graphics g, PageFormat pageFormat, int pageIndex)
{ 
   if(pageIndex >= pageHeights.size()) 
      return NO_SUCH_PAGE; 
   int savedPage = currentPageIndex; 
   currentPageIndex = pageIndex; 
   Graphics2D g2 = (Graphics2D) g; 
   paint(g2); 
   currentPageIndex = savedPage; 
   return PAGE_EXISTS; 
} 
Boro
  • 7,913
  • 4
  • 43
  • 85
  • 2
    I am suspicious of this design choice. It may help to explain why you must have one view update another, instead of having both views observe the same model. – trashgod Mar 11 '11 at 16:26
  • The reason for this choice is to show the user in one frame how the panel looks like in one frame, when in the other to show how its rendering looks like, like a print preview. – Boro Mar 11 '11 at 16:53
  • 1
    Retagged "Swing" to see if a better answer may be offered. – trashgod Mar 14 '11 at 17:05
  • 1
    Ah, now I see: you wanted a sort of "live" print preview, without having to invoke the print dialog. – trashgod Mar 17 '11 at 15:20
  • Yea. Totally that is what I wanted. And I still have the problems on my Win7 machine. – Boro Mar 18 '11 at 00:03

1 Answers1

2

Sorry, I can't address your question directly, but it may be possible to avoid it by having two different views of a common model. As shown in How to Write a Document Listener, it is possible to update more than one view of a Document. Your model and view(s) might be different, but the concept would still apply.

Addendum:

I think that is what I am doing at the moment, isn't it? I have one model i.e. the 'foreign' component, and two views one doing default paint and second custom paint.

No, you have one view updating another; you need two views responding to one model. This related example may offer some insight.

I am still interested in suggestions as to the reasons of this erroneous painting.

The attempt to interleave the updates of two components is, IMO, fundamentally flawed; it subverts the normal process of Painting in Swing. On my platform, I see only a very brief flicker and no adventitious painting. Although it may be possible to obtain satisfactory results on one system, the arrangement would be unreliable across implementations.

Is a simple call to repaint of the label, placed in the ModelObserver's update() method the appropriate solution?

Yes, repaint(), but it should be done in the ButtonHandler for View:

private class ButtonHandler implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        PieceButton pb = (PieceButton) e.getSource();
        icon.color = pb.piece.color;
        label.repaint();
        model.check(pb.piece);
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for trying, trashgod. It doesn't help me much though. I think that is what I am doing at the moment, isn't it? I have one model i.e. the 'foreign' component, and two views one doing default paint and second custom paint. I am still interested in suggestions as to the reasons of this erroneous painting. – Boro Mar 14 '11 at 09:44
  • Thanks trashgod. I see where you are going now. Which objects would you say would serve the role of the model and which of the views if I were to recode my example above? The model would be the panel to paint, i.e. the foreign component, right? What about the views? – Boro Mar 15 '11 at 13:27
  • PS: I have been playing with the example you pointed. And I have noticed an interesting behaviour. Everything works fine until you have a situation that you are selecting 2 wrong answers in a row then the label is not repainted into the second color of the wrong answer. I think it must have something to do with JLabel repaint optimization in case of entering the same text. How would you recommend to solve this? Is a simple call to repaint of the label, placed at the ModelObserver's update method, appropriate solution? – Boro Mar 15 '11 at 13:56
  • @Boro: Good catch! I've elaborated above and re-factored the [example](http://stackoverflow.com/questions/3072979). – trashgod Mar 15 '11 at 14:39
  • Thanks. Can you please comment on what would be the two views in my example? Would it be a view that takes the component to paint and the paints it in its paintComponent method. And second view which does similarly but applies additional options i.e. scaling, etc.? – Boro Mar 15 '11 at 17:05
  • Not exactly. Your example models a color and a string. One view might show both on a blue background; another might show the corresponding hexadecimal values. – trashgod Mar 15 '11 at 19:57
  • Thank you a tone, thrashgod, for your effort. I agree color and string could be variables of the model here. This could sort out this problem, but I am trying to develop a piece of API where you just give it a container (e.g. panel) and it will render a print preview. Therefore I am afraid that in fact I cannot expect having a model available. The most irritating is that the problem is OS dependent, e.g. on my mates Mac it was all fine (just as you said on your system), where on my Win it is constantly 'ugly', unless I disable/enable double buffering. – Boro Mar 15 '11 at 20:45
  • Why not just have the view implement `Printable`? – trashgod Mar 15 '11 at 23:43
  • I am implementing Printable in the component which I also want to render in a print preview fashion, the rendering is ugly, but the printout is perfect. If you are interested that is how I use it. The print calls paint of the component (as shown below). public int print(Graphics g, PageFormat pageFormat, int pageIndex){ if(pageIndex >= pageHeights.size()) return NO_SUCH_PAGE; int savedPage = currentPageIndex; currentPageIndex = pageIndex; Graphics2D g2 = (Graphics2D) g; paint(g2); currentPageIndex = savedPage; return PAGE_EXISTS; } – Boro Mar 16 '11 at 09:05
  • This code fragment would be more readable (and more visible) as an update to your question. – trashgod Mar 16 '11 at 11:09
  • I didn't find another solution to my problem then disable/enable double buffering as I described here. This appears to be only Win (maybe only 7) related problem. I any one can please test it on other machine then Win7 or Mac. Would be nice to know that. Thanks @trashgod for he effort and all interesting suggestion and examples. Considering I run out of possible implementation solutions - on this basis the answer was accepted. – Boro Apr 09 '11 at 12:20
  • @Boro: Note update to the [example cited](http://stackoverflow.com/a/3072979/230513). – trashgod Mar 26 '13 at 10:49