Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

wrldwzrd89

macrumors G5
Original poster
Jun 6, 2003
12,110
77
Solon, OH
Argh, I'm having a frustrating problem getting my JScrollPane to work the way it is supposed to... I have a feeling some code would be helpful.

Code:
// Doodler.java
package com.puttysoftware.doodler;

import java.awt.Container;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JScrollPane;

public final class Doodler {

    // Fields
    private Doodle drawColorImage;
    private JFrame drawFrame;
    private JFrame colorFrame;
    private JFrame previewFrame;
    private JFrame toolFrame;
    private Doodle canvas;
    private JLabel canvasLabel;
    private JLabel currentColor;
    private JLabel previewLabel;
    private Container drawContainer;
    private Container colorContainer;
    private Container previewContainer;
    private Container imageContainer;
    private Container toolContainer;
    private JScrollPane scrollPane;
    private JComboBox zoomSelector;
    private int[] loc;
    private int drawMode;
    private int sizeX,  sizeY;
    private int zoomFactor;
    private Color drawColor;
    private MouseEventHandler mouseHandler;
    private ColorChangeEventHandler ccHandler;
    private WindowEventHandler windowHandler;
    private ZoomEventHandler zoomHandler;
    private Doodle preview;
    private boolean isActive;
    private static final int WINDOW_SPACING = 10;
    private static final int MIN_IMAGE_SIZE = 2;
    private static final int MAX_IMAGE_SIZE = 500;
    private static final int SCROLL_PANE_SIZE = 512;
    private static final int COLOR_SIZE = 64;
    private static final String PROGRAM_NAME = "Doodler";
    public static final int DRAW_MODE_VIEW = 0;
    public static final int DRAW_MODE_EDIT = 1;
    // Constructors
    public Doodler(int imageSizeX, int imageSizeY) {
        this.sizeX = imageSizeX;
        this.sizeY = imageSizeY;
        this.checkImageSize();
        this.drawMode = Doodler.DRAW_MODE_EDIT;
        this.createPreview();
        this.commonInit();
    }

    public Doodler(Doodle image) {
        this.sizeX = image.getWidth();
        this.sizeY = image.getHeight();
        this.checkImageSize();
        this.drawMode = Doodler.DRAW_MODE_VIEW;
        this.preview = image;
        this.commonInit();
    }

    public Doodler(Doodle image, boolean edit) {
        this.sizeX = image.getWidth();
        this.sizeY = image.getHeight();
        this.checkImageSize();
        if (edit) {
            this.drawMode = Doodler.DRAW_MODE_EDIT;
        } else {
            this.drawMode = Doodler.DRAW_MODE_VIEW;
        }
        this.preview = image;
        this.commonInit();
    }

    // Methods
    private void commonInit() {
        this.zoomFactor = 1;
        this.drawColor = Color.BLACK;
        this.initDrawFrame();
        this.initPreviewFrame();
        this.initColorFrame();
        this.initToolFrame();
    }

    private void initDrawFrame() {
        this.mouseHandler = new MouseEventHandler();
        this.windowHandler = new WindowEventHandler();
        this.drawFrame = new JFrame(Doodler.PROGRAM_NAME);
        this.drawFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.drawFrame.setResizable(false);
        this.drawContainer = new Container();
        this.drawContainer.setLayout(new FlowLayout());
        this.imageContainer = new Container();
        this.imageContainer.setLayout(new FlowLayout());
        this.createCanvas();
        this.canvasLabel = new JLabel(this.canvas);
        this.imageContainer.add(this.canvasLabel);
        this.scrollPane = new JScrollPane(this.imageContainer, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        this.scrollPane.setAutoscrolls(true);
        this.drawContainer.add(this.scrollPane);
        this.drawFrame.setContentPane(this.drawContainer);
        if (this.drawMode == Doodler.DRAW_MODE_EDIT) {
            this.scrollPane.addMouseListener(this.mouseHandler);
            this.scrollPane.addMouseMotionListener(this.mouseHandler);
        }
        this.scrollPane.setMaximumSize(new Dimension(Doodler.SCROLL_PANE_SIZE, Doodler.SCROLL_PANE_SIZE));
        this.loc = new int[2];
        this.loc[0] = 0;
        this.loc[1] = 0;
        this.drawFrame.pack();
        this.drawFrame.setLocation(0, 0);
        this.drawFrame.addWindowListener(this.windowHandler);
    }

    private void initColorFrame() {
        this.ccHandler = new ColorChangeEventHandler();
        this.colorFrame = new JFrame("Color");
        this.colorContainer = new Container();
        this.drawColorImage = this.createColorBox(this.drawColor);
        this.currentColor = new JLabel(this.drawColorImage);
        this.currentColor.addMouseListener(this.ccHandler);
        this.colorContainer.setLayout(new FlowLayout());
        this.colorContainer.add(this.currentColor);
        this.colorFrame.setContentPane(this.colorContainer);
        this.colorFrame.setResizable(false);
        this.colorFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.colorFrame.setFocusableWindowState(false);
        this.colorFrame.pack();
        this.colorFrame.setLocation(this.drawFrame.getWidth() + Doodler.WINDOW_SPACING, this.previewFrame.getHeight() + this.previewFrame.getInsets().top + Doodler.WINDOW_SPACING);
    }

    private void initPreviewFrame() {
        this.previewFrame = new JFrame("Preview");
        this.previewContainer = new Container();
        this.previewLabel = new JLabel(this.preview);
        this.previewContainer.setLayout(new FlowLayout());
        this.previewContainer.add(this.previewLabel);
        this.previewFrame.setContentPane(this.previewContainer);
        this.previewFrame.setResizable(false);
        this.previewFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.previewFrame.setFocusableWindowState(false);
        this.previewFrame.pack();
        this.previewFrame.setLocation(this.drawFrame.getWidth() + Doodler.WINDOW_SPACING, 0);
    }

    private void initToolFrame() {
        this.zoomHandler = new ZoomEventHandler();
        this.toolFrame = new JFrame("Tools");
        this.toolContainer = new Container();
        String[] zoomOptions = {"100%", "200%", "400%", "800%", "1600%", "3200%"};
        this.zoomSelector = new JComboBox(zoomOptions);
        this.zoomSelector.setEditable(false);
        this.zoomSelector.addItemListener(this.zoomHandler);
        this.toolContainer.setLayout(new FlowLayout());
        this.toolContainer.add(this.zoomSelector);
        this.toolFrame.setContentPane(this.toolContainer);
        this.toolFrame.setResizable(false);
        this.toolFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.toolFrame.setFocusableWindowState(false);
        this.toolFrame.pack();
        this.toolFrame.setLocation(this.drawFrame.getWidth() + Doodler.WINDOW_SPACING, this.previewFrame.getHeight() + this.previewFrame.getInsets().top + this.colorFrame.getHeight() + this.colorFrame.getInsets().top + Doodler.WINDOW_SPACING);
    }

    private void checkImageSize() {
        if (this.sizeX < Doodler.MIN_IMAGE_SIZE || this.sizeX > Doodler.MAX_IMAGE_SIZE || this.sizeY < Doodler.MIN_IMAGE_SIZE || this.sizeY > Doodler.MAX_IMAGE_SIZE) {
            throw new IllegalArgumentException("Image size must be between 2x2 and 100x100!");
        }
    }

    private void changeColor() {
        Color newColor = JColorChooser.showDialog(this.drawFrame, Doodler.PROGRAM_NAME, this.drawColor);
        if (newColor != null) {
            this.drawColor = newColor;
            this.drawColorImage = this.createColorBox(this.drawColor);
            this.currentColor.setIcon(this.drawColorImage);
        }
    }

    private void changeZoom(int newZoom) {
        this.zoomFactor = newZoom;
        this.createCanvas();
        this.canvasLabel.setIcon(this.canvas);
        this.rebuildGrid();
        this.drawFrame.pack();
    }

    public void draw() {
        this.isActive = true;
        this.rebuildGrid();
        this.showAllWindows();
        while (this.isActive) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
                // Ignore exception
            }
        }
    }

    private void showAllWindows() {
        this.drawFrame.setVisible(true);
        this.previewFrame.setVisible(true);
        this.toolFrame.setVisible(true);
        if (this.drawMode == Doodler.DRAW_MODE_EDIT) {
            this.colorFrame.setVisible(true);
        }
    }

    private void hideAllWindows() {
        this.drawFrame.setVisible(false);
        this.colorFrame.setVisible(false);
        this.previewFrame.setVisible(false);
        this.toolFrame.setVisible(false);
    }

    public Doodle getImage() {
        return this.preview;
    }

    private void rebuildGrid() {
        for (int x = 0; x < this.sizeX; x++) {
            for (int y = 0; y < this.sizeY; y++) {
                int pixel = this.preview.getRGB(x, y);
                for (int z = 0; z < this.zoomFactor; z++) {
                    for (int t = 0; t < this.zoomFactor; t++) {
                        this.canvas.setRGB((x * this.zoomFactor) + z, (y * this.zoomFactor) + t, pixel);
                    }
                }
            }
        }
    }

    private void updateGrid() {
        int x = this.loc[0];
        int y = this.loc[1];
        this.preview.setRGB(y, x, this.drawColor.getRGB());
        int pixel = this.drawColor.getRGB();
        for (int z = 0; z < this.zoomFactor; z++) {
            for (int t = 0; t < this.zoomFactor; t++) {
                this.canvas.setRGB((y * this.zoomFactor) + z, (x * this.zoomFactor) + t, pixel);
            }
        }
        this.previewLabel.repaint();
        this.canvasLabel.repaint();
    }

    private void createPreview() {
        Doodle image = new Doodle(this.sizeX, this.sizeY, Doodle.TYPE_INT_ARGB);
        int rgb = Color.WHITE.getRGB();
        for (int x = 0; x < this.sizeX; x++) {
            for (int y = 0; y < this.sizeY; y++) {
                image.setRGB(x, y, rgb);
            }
        }
        this.preview = image;
    }

    private void createCanvas() {
        Doodle image = new Doodle(this.sizeX * this.zoomFactor, this.sizeY * this.zoomFactor, Doodle.TYPE_INT_ARGB);
        int rgb = Color.WHITE.getRGB();
        for (int x = 0; x < this.sizeX * this.zoomFactor; x++) {
            for (int y = 0; y < this.sizeY * this.zoomFactor; y++) {
                image.setRGB(x, y, rgb);
            }
        }
        this.canvas = image;
    }

    private Doodle createColorBox(Color color) {
        Doodle image = new Doodle(Doodler.COLOR_SIZE, Doodler.COLOR_SIZE, Doodle.TYPE_INT_ARGB);
        int rgb = color.getRGB();
        for (int x = 0; x < Doodler.COLOR_SIZE; x++) {
            for (int y = 0; y < Doodler.COLOR_SIZE; y++) {
                image.setRGB(x, y, rgb);
            }
        }
        return image;
    }

    private class WindowEventHandler implements WindowListener {

        public void windowOpened(WindowEvent e) {
            // Do nothing
        }

        public void windowClosing(WindowEvent e) {
            Doodler.this.hideAllWindows();
            Doodler.this.isActive = false;
        }

        public void windowClosed(WindowEvent e) {
            // Do nothing
        }

        public void windowIconified(WindowEvent e) {
            Doodler.this.hideAllWindows();
        }

        public void windowDeiconified(WindowEvent e) {
            Doodler.this.showAllWindows();
        }

        public void windowActivated(WindowEvent e) {
            // Do nothing
        }

        public void windowDeactivated(WindowEvent e) {
            // Do nothing
        }
    }

    private class MouseEventHandler implements MouseListener, MouseMotionListener {

        public void mouseClicked(MouseEvent e) {
            this.handleClickAndDrag(e);
        }

        public void mousePressed(MouseEvent e) {
            // Do nothing
        }

        public void mouseReleased(MouseEvent e) {
            // Do nothing
        }

        public void mouseEntered(MouseEvent e) {
            // Do nothing
        }

        public void mouseExited(MouseEvent e) {
            // Do nothing
        }

        public void mouseDragged(MouseEvent e) {
            this.handleClickAndDrag(e);
        }

        public void mouseMoved(MouseEvent e) {
            // Do nothing
        }

        public void handleClickAndDrag(MouseEvent e) {
            Doodler d = Doodler.this;
            int x = e.getX();
            int y = e.getY();
            int xLoc = x / d.zoomFactor;
            int yLoc = y / d.zoomFactor;
            if (xLoc >= 0 && xLoc < d.sizeX && yLoc >= 0 && yLoc < d.sizeY) {
                d.loc[1] = xLoc;
                d.loc[0] = yLoc;
                d.updateGrid();
            }
        }
    }

    private class ColorChangeEventHandler implements MouseListener {

        public void mouseClicked(MouseEvent e) {
            Doodler d = Doodler.this;
            d.changeColor();
        }

        public void mousePressed(MouseEvent e) {
            // Do nothing
        }

        public void mouseReleased(MouseEvent e) {
            // Do nothing
        }

        public void mouseEntered(MouseEvent e) {
            // Do nothing
        }

        public void mouseExited(MouseEvent e) {
            // Do nothing
        }
    }

    private class ZoomEventHandler implements ItemListener {

        public void itemStateChanged(ItemEvent e) {
            String cmd = (String) e.getItem();
            int newZoom = 1;
            if (cmd.equals("100%")) {
                newZoom = 1;
            } else if (cmd.equals("200%")) {
                newZoom = 2;
            } else if (cmd.equals("400%")) {
                newZoom = 4;
            } else if (cmd.equals("800%")) {
                newZoom = 8;
            } else if (cmd.equals("1600%")) {
                newZoom = 16;
            } else if (cmd.equals("3200%")) {
                newZoom = 32;
            }
            Doodler.this.changeZoom(newZoom);
        }
    }
}
Code:
// Doodle.java
package com.puttysoftware.doodler;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.Icon;

/**
 *
 * @author wrldwzrd89
 */
public class Doodle extends BufferedImage implements Icon {
    // Constructor
    public Doodle(int w, int h, int type) {
        super(w, h, type);
    }

    public void paintIcon(Component c, Graphics g, int x, int y) {
        g.drawImage(this, x, y, c);
    }

    public int getIconWidth() {
        return this.getWidth();
    }

    public int getIconHeight() {
        return this.getHeight();
    }
}
Code:
// Main.java
package doodlertest;

import com.puttysoftware.doodler.Doodler;
import com.puttysoftware.doodler.Doodle;

/**
 *
 * @author wrldwzrd89
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Doodler d = new Doodler(128, 128);
        d.draw();
        Doodle doodle = d.getImage();
        if (doodle != null) {
            Doodler d2 = new Doodler(doodle);
            d2.draw();
        }
        System.exit(0);
    }
}
 
I've fiddled some more with the code, but for the life of me, don't understand why this behaves the way it does... I've attached a binary of the offending program. It only does it when the size of the image times the zoom factor exceeds the screen size - it won't show the scroll bars but it "leaks" outside its container.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.