Sketcher2 source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1723 lines
66 KiB

package com.jotuntech.sketcher.client;
import info.clearthought.layout.TableLayout;
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.BoundedRangeModel;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.ListModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.jotuntech.sketcher.client.command.CreateLayerCommand;
import com.jotuntech.sketcher.client.command.DeleteLayerCommand;
import com.jotuntech.sketcher.client.command.FilterCommand;
import com.jotuntech.sketcher.client.command.KickCommand;
import com.jotuntech.sketcher.client.command.MergeCommand;
import com.jotuntech.sketcher.client.command.SayCommand;
import com.jotuntech.sketcher.client.command.SetBrushCommand;
import com.jotuntech.sketcher.client.command.SetColorCommand;
import com.jotuntech.sketcher.client.command.SetLayerCommand;
import com.jotuntech.sketcher.client.command.VoiceCommand;
import com.jotuntech.sketcher.common.Brush;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.LayerType;
import com.jotuntech.sketcher.common.Log;
import com.jotuntech.sketcher.common.Pixels;
import com.jotuntech.sketcher.common.TwoWayHashMap;
import com.jotuntech.sketcher.common.User;
import com.jotuntech.sketcher.common.filter.AutoContrastFilter;
import com.jotuntech.sketcher.common.filter.BlurFilter;
public class UserInterface extends JPanel {
public final static Insets NO_INSETS = new Insets(0, 0, 0, 0);
protected Client client;
private JSplitPane splitPane;
private JScrollPane editorPane;
private Editor editor;
private JPanel rightPanel;
private JMenuBar menuBar;
private JMenu filterMenu;
private JCheckBoxMenuItem soundItem, tagsItem;
JCollapsiblePanelGroup mainGroup;
private JScrollPane chatPane;
private JTextArea chatLog;
private JTextField chatField;
private BrushButton[] brushButtons;
private JLabel zoomLabel;
private JSpinner zoomSpin;
JCheckBox smoothBox;
private JCheckBox opacityBox, flowBox, radiusBox, lockTransBox;
private JUserList userList;
private JList layerList;
private JSlider hardnessSlider, sizeSlider, opacitySlider, flowSlider, spacingSlider, jitterSlider, noiseSlider, waterSlider, waterAreaSlider;
private JSlider redSlider, greenSlider, blueSlider;
private JPanel rgbColor;
private JSlider hueSlider, satSlider, brightSlider;
private JPanel hsbColor;
private JSlider cyanSlider, magentaSlider, yellowSlider;
private JPanel cmyColor;
private JCollapsiblePanel brushPanel, brushSettingsPanel, colorPanel, inputPanel;
private JToggleButton selectButton, moveButton, freehandButton, lineButton, bezierButton, rectangleButton, ovalButton;
private boolean smoothZoom = true;
private boolean colorAdjusting = false;
protected boolean brushAdjusting = false;
private boolean layerAdjusting = false;
public static AudioClip
AUDIO_INTRO = Applet.newAudioClip(UserInterface.class.getResource("audio/intro.wav")),
AUDIO_SIGN_IN = Applet.newAudioClip(UserInterface.class.getResource("audio/signin.wav")),
AUDIO_SIGN_OUT = Applet.newAudioClip(UserInterface.class.getResource("audio/signout.wav")),
AUDIO_KICK = Applet.newAudioClip(UserInterface.class.getResource("audio/kick.wav")),
AUDIO_CHAT = Applet.newAudioClip(UserInterface.class.getResource("audio/chat.wav")),
AUDIO_OUTTRO = Applet.newAudioClip(UserInterface.class.getResource("audio/outtro.wav"));
private static Icon[] brushIcons = new Icon[] {
new ImageIcon(UserInterface.class.getResource("images/pen.png")),
new ImageIcon(UserInterface.class.getResource("images/pencil.png")),
new ImageIcon(UserInterface.class.getResource("images/water2.png")),
new ImageIcon(UserInterface.class.getResource("images/eraser.png")),
new ImageIcon(UserInterface.class.getResource("images/ink.png")),
new ImageIcon(UserInterface.class.getResource("images/wipe.png"))
};
private void saveFileToDisk(final boolean selectionOnly) {
try {
final JFileChooser fileChooser = new JFileChooser();
if(fileChooser.showSaveDialog(null) != JFileChooser.APPROVE_OPTION) {
return;
}
final File file = fileChooser.getSelectedFile();
if(file.exists()) {
if(JOptionPane.showConfirmDialog(null, "Overwrite existing file?", "Confirm", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
return;
}
}
Thread saveThread = new Thread("SaveThread") {
public void run() {
BufferedImage image;
if(selectionOnly) {
Rectangle s = editor.getSelect();
if(s == null) {
JOptionPane.showMessageDialog(UserInterface.this, "Cannot save empty selection!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
image = editor.getImage().getSubimage(s.x, s.y, s.width, s.height);
} else {
image = editor.getImage();
}
if(file.getName().toUpperCase().endsWith(".PNG")) {
println("Saving PNG file. Please wait...");
try {
ImageIO.write(image, "PNG", file);
} catch (Throwable e) {
JOptionPane.showMessageDialog(UserInterface.this, "Unable to save file! (" + e.getMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
println("Completed!");
} else if(file.getName().toUpperCase().endsWith(".PSD")) {
if(selectionOnly) {
JOptionPane.showMessageDialog(UserInterface.this, "Cannot save partial canvases in PSD format!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
println("Saving PSD file. Please wait...");
Canvas canvas = client.getCanvas();
TwoWayHashMap<Integer, Layer> lm = canvas.getLayerMap();
try {
RandomAccessFile raf = new RandomAccessFile(fileChooser.getSelectedFile(), "rw");
PSDEncoder psde = new PSDEncoder(raf, image, lm);
psde.encode();
raf.close();
System.gc();
} catch (Throwable e) {
JOptionPane.showMessageDialog(UserInterface.this, "Unable to save file! (" + e.getMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
println("Completed!");
} else {
JOptionPane.showMessageDialog(UserInterface.this, "You need to add an extension like .png or .psd to save a file!", "Error", JOptionPane.ERROR_MESSAGE);
}
}
};
saveThread.setPriority(Thread.MIN_PRIORITY);
saveThread.start();
} catch(SecurityException e) {
JOptionPane.showMessageDialog(UserInterface.this, "You cannot use the save feature without accepting the security certificate!", "Error", JOptionPane.ERROR_MESSAGE);
}
}
public UserInterface(final Client client) {
super(new BorderLayout());
this.client = client;
editor = new Editor(client);
editor.setSmoothZoom(true);
if(client.isAds()) {
try {
BufferedImage bi = ImageIO.read(UserInterface.class.getResource("images/ad1.png"));
editor.setAd(bi);
editor.setAdPosition(-468, -60);
} catch (IOException e1) {
e1.printStackTrace();
}
}
if(!LookAndFeel.NIMBUS.activate()) {
LookAndFeel.SYSTEM.activate();
}
editorPane = new JScrollPane(editor);
AdjustmentListener canvasScroller = new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent arg0) {
boolean adjusting = arg0.getValueIsAdjusting();
editor.setAdPosition(editorPane.getHorizontalScrollBar().getValue() + editorPane.getViewport().getWidth() - 478, editorPane.getVerticalScrollBar().getValue() + 10);
if(smoothZoom) {
if(adjusting) {
editor.setSmoothZoom(false);
} else {
editor.setSmoothZoom(true);
editor.repaint();
}
}
}
};
editorPane.getVerticalScrollBar().addAdjustmentListener(canvasScroller);
editorPane.getHorizontalScrollBar().addAdjustmentListener(canvasScroller);
/** Create panel for tools */
rightPanel = new JPanel();
rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
/** Make split pane with canvas on left, tools on right */
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, editorPane, rightPanel);
splitPane.setResizeWeight(1.0);
splitPane.setOneTouchExpandable(true);
add(splitPane, BorderLayout.CENTER);
menuBar = new JMenuBar();
menuBar.setAlignmentX(Component.LEFT_ALIGNMENT);
menuBar.setMinimumSize(new Dimension(0, 24));
menuBar.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
rightPanel.add(menuBar);
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
JMenuItem saveItem = new JMenuItem("Save Canvas As...");
saveItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
saveFileToDisk(false);
}
});
fileMenu.add(saveItem);
JMenuItem saveSelectItem = new JMenuItem("Save Selection As...");
saveSelectItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
saveFileToDisk(true);
}
});
fileMenu.add(saveSelectItem);
JMenuItem uploadSelectItem = new JMenuItem("Upload Selection...");
uploadSelectItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
final Rectangle s = editor.getSelect();
if(s == null) {
JOptionPane.showMessageDialog(UserInterface.this, "Cannot upload empty selection!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
final JDialog uploadDialog = new JDialog();
uploadDialog.setTitle("Upload Selection to ArtGrounds.com");
uploadDialog.setResizable(false);
uploadDialog.setModal(true);
Container contentPane = uploadDialog.getContentPane();
contentPane.setLayout(new BorderLayout());
JPanel northPanel = new JPanel();
northPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8));
northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.Y_AXIS));
northPanel.add(new JLabel("Format:"));
JPanel formatPanel = new JPanel();
formatPanel.setLayout(new BoxLayout(formatPanel, BoxLayout.X_AXIS));
ButtonGroup formatGroup = new ButtonGroup();
final JRadioButton jpegButton = new JRadioButton("JPEG (realism, paintings, soft shading)");
jpegButton.setSelected(true);
formatGroup.add(jpegButton);
formatPanel.add(jpegButton);
formatPanel.add(Box.createRigidArea(new Dimension(8, 0)));
final JRadioButton pngButton = new JRadioButton("PNG (cartoons, drawings, hard shading)");
formatGroup.add(pngButton);
formatPanel.add(pngButton);
northPanel.add(formatPanel);
formatPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
northPanel.add(new JLabel("Title:"));
final JTextField titleField = new JTextField();
titleField.setAlignmentX(Component.LEFT_ALIGNMENT);
northPanel.add(titleField);
northPanel.add(Box.createRigidArea(new Dimension(0, 8)));
northPanel.add(new JLabel("Description:"));
contentPane.add(northPanel, BorderLayout.NORTH);
contentPane.add(Box.createRigidArea(new Dimension(8, 0)), BorderLayout.WEST);
final JTextArea descArea = new JTextArea();
contentPane.add(descArea, BorderLayout.CENTER);
contentPane.add(Box.createRigidArea(new Dimension(8, 0)), BorderLayout.EAST);
JPanel southPanel = new JPanel();
southPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.X_AXIS));
final JProgressBar progressBar = new JProgressBar();
southPanel.add(progressBar);
southPanel.add(Box.createRigidArea(new Dimension(8, 0)));
final JCheckBox matureBox = new JCheckBox("Mature Content");
southPanel.add(matureBox);
southPanel.add(Box.createRigidArea(new Dimension(8, 0)));
final JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
uploadDialog.setVisible(false);
}
});
final JButton uploadButton = new JButton("Upload");
uploadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Thread saveThread = new Thread("SaveThread") {
public void run() {
try {
String title = titleField.getText().trim();
if(title.isEmpty()) {
JOptionPane.showMessageDialog(uploadDialog, "Title cannot be empty!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
String description = descArea.getText().trim();
if(description.isEmpty()) {
JOptionPane.showMessageDialog(uploadDialog, "Description cannot be empty!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
uploadButton.setEnabled(false);
jpegButton.setEnabled(false);
pngButton.setEnabled(false);
titleField.setEnabled(false);
descArea.setEnabled(false);
matureBox.setEnabled(false);
BufferedImage argbImage = editor.getImage().getSubimage(s.x, s.y, s.width, s.height);
BufferedImage rgbImage = new BufferedImage(s.width, s.height, BufferedImage.TYPE_INT_RGB);
Graphics g = rgbImage.getGraphics();
g.drawImage(argbImage, 0, 0, editor.getCanvasBackground(), null);
BufferedImage madeImage = ImageIO.read(UserInterface.class.getResource("images/madeinsketcher.png"));
g.drawImage(madeImage, s.width - madeImage.getWidth() - 2, 2, null);
ByteArrayOutputStream imgbaos = new ByteArrayOutputStream();
ImageIO.write(rgbImage, jpegButton.isSelected() ? "JPG" : "PNG", imgbaos);
byte[] imgarr = imgbaos.toByteArray();
progressBar.setMaximum(imgarr.length);
ByteArrayOutputStream hdrbaos = new ByteArrayOutputStream();
DataOutputStream hdrdos = new DataOutputStream(hdrbaos);
hdrdos.writeUTF(client.getLogin());
hdrdos.writeUTF(client.getPassword());
hdrdos.writeUTF(title);
hdrdos.writeUTF(description);
hdrdos.writeBoolean(matureBox.isSelected());
hdrdos.writeInt(imgarr.length);
byte[] hdrarr = hdrbaos.toByteArray();
int contentLength = hdrarr.length + imgarr.length;
URL url = new URL("http://www.artgrounds.com/sketcher-upload/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setFixedLengthStreamingMode(contentLength);
conn.setDoOutput(true);
OutputStream httpos = conn.getOutputStream();
httpos.write(hdrarr);
for(int ofs = 0; ofs < imgarr.length; ofs += 1458) {
progressBar.setValue(ofs);
httpos.write(imgarr, ofs, Math.min(1458, imgarr.length - ofs));
httpos.flush();
}
progressBar.setValue(imgarr.length);
httpos.flush();
httpos.close();
if(conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
progressBar.setValue(0);
uploadButton.setEnabled(true);
jpegButton.setEnabled(true);
pngButton.setEnabled(true);
titleField.setEnabled(true);
descArea.setEnabled(true);
matureBox.setEnabled(true);
JOptionPane.showMessageDialog(uploadDialog, "Unable to upload image! (" + conn.getResponseCode() + " " + conn.getResponseMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
cancelButton.setText("Close");
JOptionPane.showMessageDialog(uploadDialog, "Upload completed!", "Information", JOptionPane.INFORMATION_MESSAGE);
} catch (Throwable t) {
progressBar.setValue(0);
uploadButton.setEnabled(true);
jpegButton.setEnabled(true);
pngButton.setEnabled(true);
titleField.setEnabled(true);
descArea.setEnabled(true);
matureBox.setEnabled(true);
JOptionPane.showMessageDialog(uploadDialog, "Unable to upload image! (" + t.getClass().getSimpleName() + " " + t.getMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE);
Log.error(t);
}
}
};
saveThread.setPriority(Thread.MIN_PRIORITY);
saveThread.start();
}
});
southPanel.add(uploadButton);
southPanel.add(cancelButton);
contentPane.add(southPanel, BorderLayout.SOUTH);
uploadDialog.setPreferredSize(new Dimension(512, 512));
uploadDialog.pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
uploadDialog.setLocation((screenSize.width - uploadDialog.getWidth()) / 2, (screenSize.height - uploadDialog.getHeight()) / 2);
uploadDialog.setVisible(true);
}
});
fileMenu.add(uploadSelectItem);
JMenu optMenu = new JMenu("Options");
menuBar.add(optMenu);
soundItem = new JCheckBoxMenuItem("Sounds", client.isSoundEnabled());
soundItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
client.setSoundEnabled(((JCheckBoxMenuItem) e.getSource()).isSelected());
}
});
optMenu.add(soundItem);
tagsItem = new JCheckBoxMenuItem("Tags", editor.isTagsEnabled());
tagsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setTagsEnabled(((JCheckBoxMenuItem) e.getSource()).isSelected());
editor.repaint();
}
});
optMenu.add(tagsItem);
JCheckBoxMenuItem voiceItem = new JCheckBoxMenuItem("Voice", client.isVoiceEnabled());
voiceItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
client.getCommandQueue().offer(new CommandEntry(0, new VoiceCommand(((JCheckBoxMenuItem) e.getSource()).isSelected())));
}
});
optMenu.add(voiceItem);
JCheckBoxMenuItem swCursorItem = new JCheckBoxMenuItem("Software Cursor", editor.isSoftwareCursorEnabled());
swCursorItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setSoftwareCursorEnabled(((JCheckBoxMenuItem) e.getSource()).isSelected());
editor.setCursorType(Editor.CursorType.SOFTWARE);
}
});
optMenu.add(swCursorItem);
JMenuItem resetItem = new JMenuItem("Reset");
resetItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int confirm = JOptionPane.showConfirmDialog(UserInterface.this, "This will erase all your settings permanently. Continue?", "Confirm", JOptionPane.YES_NO_OPTION);
if(confirm != JOptionPane.OK_OPTION) {
return;
}
client.resetProps();
}
});
optMenu.add(resetItem);
filterMenu = new JMenu("Filters");
filterMenu.setEnabled(false);
menuBar.add(filterMenu);
JMenuItem blurItem = new JMenuItem("Blur");
blurItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final Rectangle select = editor.getSelect();
if(select == null) {
JOptionPane.showMessageDialog(UserInterface.this, "You must select an area before you can blur anything!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
final JDialog blurDialog = new JDialog();
blurDialog.setTitle("Blur");
blurDialog.setModal(true);
Container contentPane = blurDialog.getContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));
contentPane.add(new JLabel("Size: "));
final SpinnerNumberModel sizeModel = new SpinnerNumberModel(5, 0.5, 50, 0.5);
JSpinner sizeSpin = new JSpinner(sizeModel);
contentPane.add(sizeSpin);
JButton okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
blurDialog.setVisible(false);
editor.requestFocusInWindow();
client.getCommandQueue().offer(new CommandEntry(0, new FilterCommand(new BlurFilter(), select.x, select.y, select.width, select.height, ((Double) sizeModel.getValue()).floatValue(), 0f, 0f)));
client.getCommandQueue().offer(new CommandEntry(0, new MergeCommand()));
}
});
contentPane.add(okButton);
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.requestFocusInWindow();
blurDialog.setVisible(false);
}
});
contentPane.add(cancelButton);
blurDialog.pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
blurDialog.setLocation((screenSize.width - blurDialog.getWidth()) / 2, (screenSize.height - blurDialog.getHeight()) / 2);
blurDialog.setVisible(true);
}
});
filterMenu.add(blurItem);
JMenuItem autoContrastItem = new JMenuItem("Auto Contrast");
autoContrastItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final Rectangle select = editor.getSelect();
if(select == null) {
JOptionPane.showMessageDialog(UserInterface.this, "You must select an area before you can correct its contrast!", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
client.getCommandQueue().offer(new CommandEntry(0, new FilterCommand(new AutoContrastFilter(), select.x, select.y, select.width, select.height, 0.05f, 0.05f, 0.05f)));
client.getCommandQueue().offer(new CommandEntry(0, new MergeCommand()));
}
});
filterMenu.add(autoContrastItem);
menuBar.add(Box.createHorizontalGlue());
JMenu helpMenu = new JMenu("Help");
menuBar.add(helpMenu);
JMenuItem aboutItem = new JMenuItem("About");
aboutItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final JDialog aboutDialog = new JDialog();
aboutDialog.setTitle("About Sketcher\u2122");
aboutDialog.setResizable(false);
aboutDialog.setModal(true);
Container contentPane = aboutDialog.getContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
JLabel label = new JLabel(new ImageIcon(getClass().getResource("images/about.png")));
label.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
aboutDialog.setVisible(false);
}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
});
contentPane.add(label);
aboutDialog.pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
aboutDialog.setLocation((screenSize.width - aboutDialog.getWidth()) / 2, (screenSize.height - aboutDialog.getHeight()) / 2);
aboutDialog.setVisible(true);
}
});
helpMenu.add(aboutItem);
JLabel copyright = new JLabel(new ImageIcon(UserInterface.class.getResource("images/sketcherlogo.png")));
copyright.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
copyright.setAlignmentX(Component.LEFT_ALIGNMENT);
copyright.setHorizontalAlignment(SwingConstants.CENTER);
copyright.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
copyright.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
try {
Class<?> desktopClass = Class.forName("java.awt.Desktop");
if((Boolean) desktopClass.getMethod("isDesktopSupported").invoke(null)) { // Desktop.isDesktopSupported()
Object desktop = desktopClass.getMethod("getDesktop").invoke(null); // Desktop.getDesktop()
Class<?> actionClass = Class.forName("java.awt.Desktop$Action");
if((Boolean) desktopClass.getMethod("isSupported", actionClass).invoke(desktop, actionClass.getField("BROWSE").get(null))) { // desktop.isSupported(java.awt.Desktop.Action.BROWSE)
desktopClass.getMethod("browse", URI.class).invoke(desktop, new URI("http://sketcher.artgrounds.com/")); // desktop.browse(uri);
return;
}
}
} catch(Throwable t) { }
JOptionPane.showMessageDialog(UserInterface.this, "Visit sketcher.artgrounds.com for more information about Sketcher!");
}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
});
rightPanel.add(copyright);
mainGroup = new JCollapsiblePanelGroup();
/** Set up view panel */
JCollapsiblePanel viewPanel = new JCollapsiblePanel(100, "Canvas", false, false);
viewPanel.setGroup(mainGroup);
JPanel viewPanelContent = viewPanel.getContentPane();
viewPanelContent.setLayout(new BoxLayout(viewPanelContent, BoxLayout.Y_AXIS));
/* Set up zoom panel */
JPanel zoomPanel = new JPanel();
zoomPanel.setLayout(new BoxLayout(zoomPanel, BoxLayout.X_AXIS));
viewPanelContent.add(zoomPanel);
zoomPanel.add(Box.createHorizontalGlue());
zoomLabel = new JLabel(new ImageIcon(UserInterface.class.getResource("images/zoom.png")));
zoomLabel.setAlignmentY(Component.CENTER_ALIGNMENT);
zoomPanel.add(zoomLabel);
zoomSpin = new JSpinner(new SpinnerNumberModel(100, 10, 800, 25));
zoomSpin.setToolTipText("Zoom - grow or shrink the displayed canvas.");
zoomSpin.setAlignmentY(Component.CENTER_ALIGNMENT);
zoomPanel.add(zoomSpin);
zoomSpin.getModel().addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(client.getCanvas() != null) {
try {
int newZoom = (Integer)zoomSpin.getValue();
if(newZoom > 0 && newZoom <= 800) {
/* Fetch scrollbars */
JScrollBar v = editorPane.getHorizontalScrollBar();
JScrollBar h = editorPane.getVerticalScrollBar();
/* Fetch fractions */
float hf = (float)h.getValue() / (float)h.getMaximum();
float vf = (float)v.getValue() / (float)v.getMaximum();
float hvis = (float)h.getVisibleAmount() / (float)h.getMaximum();
float vvis = (float)v.getVisibleAmount() / (float)v.getMaximum();
/* Change zoom */
editor.setZoom(newZoom / 100f);
editor.repaint();
/* Apply old fractions to new viewport */
float nhvis = (float)h.getVisibleAmount() / (float)h.getMaximum();
float nvvis = (float)v.getVisibleAmount() / (float)v.getMaximum();
h.setValue((int)((hf + (hvis - nhvis) / 2) * h.getMaximum()));
v.setValue((int)((vf + (vvis - nvvis) / 2) * v.getMaximum()));
}
} catch(NumberFormatException e) {
/** Ignore bad numbers */
}
}
}
});
/** Smooth zoom box */
smoothBox = new JCheckBox("Smoothly");
smoothBox.setToolTipText("Display the canvas smoothly for zoom settings not divisble by 100. Somewhat computing intensive.");
smoothBox.setAlignmentY(Component.CENTER_ALIGNMENT);
smoothBox.setHorizontalAlignment(JCheckBox.CENTER);
smoothBox.setSelected(smoothZoom);
zoomPanel.add(smoothBox);
smoothBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
smoothZoom = smoothBox.isSelected();
editor.setSmoothZoom(smoothZoom);
editor.repaint();
}
});
zoomPanel.add(Box.createHorizontalGlue());
/* Background brightness */
final JSlider backgroundSlider = new JSlider(0, 255, 255);
backgroundSlider.setToolTipText("Background brightness - adjust the background brightness to see layer transparency.");
backgroundSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!backgroundSlider.getModel().getValueIsAdjusting()) {
int brightness = backgroundSlider.getValue();
editor.setCanvasBackground(new Color(brightness, brightness, brightness));
editor.repaint();
}
}
});
viewPanelContent.add(backgroundSlider);
rightPanel.add(viewPanel);
/** Create Tools panel, add to right panel */
JCollapsiblePanel toolsPanel = new JCollapsiblePanel(150, "Tools", true, true);
toolsPanel.setGroup(mainGroup);
rightPanel.add(toolsPanel);
JPanel toolsPane = toolsPanel.getContentPane();
toolsPane.setLayout(new BoxLayout(toolsPane, BoxLayout.X_AXIS));
/** Make group for buttons */
ButtonGroup toolButtonGroup = new ButtonGroup();
toolsPane.add(Box.createHorizontalGlue());
selectButton = new JToggleButton(loadImageIcon("select.png"));
selectButton.setToolTipText("Select");
selectButton.setMargin(UserInterface.NO_INSETS);
selectButton.setMinimumSize(new Dimension(32, 32));
selectButton.setPreferredSize(new Dimension(32, 32));
selectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.SELECT_HOVER);
updatePanels();
}
});
toolsPane.add(selectButton);
toolButtonGroup.add(selectButton);
moveButton = new JToggleButton(loadImageIcon("move.png"));
moveButton.setToolTipText("Duplicate");
moveButton.setMargin(UserInterface.NO_INSETS);
moveButton.setMinimumSize(new Dimension(32, 32));
moveButton.setPreferredSize(new Dimension(32, 32));
moveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.MOVE_HOVER);
updatePanels();
}
});
toolsPane.add(moveButton);
toolButtonGroup.add(moveButton);
freehandButton = new JToggleButton(loadImageIcon("freehand.png"));
freehandButton.setSelected(true);
freehandButton.setToolTipText("Freehand");
freehandButton.setMargin(UserInterface.NO_INSETS);
freehandButton.setMinimumSize(new Dimension(32, 32));
freehandButton.setPreferredSize(new Dimension(32, 32));
freehandButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.DRAW_HOVER);
updatePanels();
}
});
toolsPane.add(freehandButton);
toolButtonGroup.add(freehandButton);
lineButton = new JToggleButton(loadImageIcon("line.png"));
lineButton.setToolTipText("Line");
lineButton.setMargin(UserInterface.NO_INSETS);
lineButton.setMinimumSize(new Dimension(32, 32));
lineButton.setPreferredSize(new Dimension(32, 32));
lineButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.LINE_HOVER);
updatePanels();
}
});
toolsPane.add(lineButton);
toolButtonGroup.add(lineButton);
bezierButton = new JToggleButton(loadImageIcon("bezier.png"));
bezierButton.setToolTipText("Bezier");
bezierButton.setMargin(UserInterface.NO_INSETS);
bezierButton.setMinimumSize(new Dimension(32, 32));
bezierButton.setPreferredSize(new Dimension(32, 32));
bezierButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.BEZIER_HOVER);
updatePanels();
}
});
toolsPane.add(bezierButton);
toolButtonGroup.add(bezierButton);
rectangleButton = new JToggleButton(loadImageIcon("rectangle.png"));
rectangleButton.setToolTipText("Rectangle");
rectangleButton.setMargin(UserInterface.NO_INSETS);
rectangleButton.setMinimumSize(new Dimension(32, 32));
rectangleButton.setPreferredSize(new Dimension(32, 32));
rectangleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.RECT_HOVER);
updatePanels();
}
});
toolsPane.add(rectangleButton);
toolButtonGroup.add(rectangleButton);
ovalButton = new JToggleButton(loadImageIcon("oval.png"));
ovalButton.setToolTipText("Oval");
ovalButton.setMargin(UserInterface.NO_INSETS);
ovalButton.setMinimumSize(new Dimension(32, 32));
ovalButton.setPreferredSize(new Dimension(32, 32));
ovalButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
editor.setState(Editor.State.OVAL_HOVER);
updatePanels();
}
});
toolsPane.add(ovalButton);
toolButtonGroup.add(ovalButton);
toolsPane.add(Box.createHorizontalGlue());
/** Create Brushes panel, add to right panel */
brushPanel = new JCollapsiblePanel(200, "Brushes", true, true);
brushPanel.setGroup(mainGroup);
rightPanel.add(brushPanel);
JPanel brushesPane = brushPanel.getContentPane();
brushesPane.setLayout(new BoxLayout(brushesPane, BoxLayout.X_AXIS));
/** Make group for buttons */
ButtonGroup brushButtonGroup = new ButtonGroup();
/** Make, add and group brush buttons */
brushButtons = new BrushButton[client.brushes.length];
brushesPane.add(Box.createHorizontalGlue());
for(int i = 0; i < brushButtons.length; i++) {
BrushButton b = new BrushButton(this, client.brushes, brushIcons, i);
brushesPane.add(b);
brushButtonGroup.add(b);
brushButtons[i] = b;
}
brushesPane.add(Box.createHorizontalGlue());
/** BRUSH panel */
TableLayout brushTabLayout = new TableLayout(new double[][] {{0, -2, -1, -2, 0}, {-2, -2, -2, -2, -2, -2, -2, -2, -2}});
brushSettingsPanel = new JCollapsiblePanel(300, "Brush settings", false, false);
brushSettingsPanel.setGroup(mainGroup);
JPanel brushTab = brushSettingsPanel.getContentPane();
brushTab.setLayout(brushTabLayout);
brushTab.setAlignmentX(Component.LEFT_ALIGNMENT);
rightPanel.add(brushSettingsPanel);
/** HARDNESS */
brushTab.add(new JLabel(loadImageIcon("hardness.png")), "1 0 r c");
hardnessSlider = new JSlider(1, 12, 1);
hardnessSlider.setToolTipText("Hardness - controls the crispness of the brush.");
hardnessSlider.setSnapToTicks(true);
brushTab.add(hardnessSlider, "2 0 2 0");
hardnessSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!hardnessSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setHardness(hardnessSlider.getValue());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** SIZE */
brushTab.add(new JLabel(loadImageIcon("size.png")), "1 1 r c");
sizeSlider = new JSlider(1, 1008, 1);
sizeSlider.setToolTipText("Size - controls the diameter of the brush.");
brushTab.add(sizeSlider, "2 1 2 1");
sizeSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!sizeSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setRadius(sizeSlider.getValue() / 8f);
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** OPACITY */
brushTab.add(new JLabel(loadImageIcon("opacity.png")), "1 2 r c");
opacitySlider = new JSlider(-255, 255, 0);
opacitySlider.setMajorTickSpacing(255);
opacitySlider.setPaintTicks(true);
opacitySlider.setToolTipText("Opacity - controls the opacity of the brush.");
brushTab.add(opacitySlider, "2 2 2 2");
opacitySlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!opacitySlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
Brush b = client.getConnection().getUser().getBrush();
b.setOpacity(opacitySlider.getValue());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** FLOW */
brushTab.add(new JLabel(loadImageIcon("flow.png")), "1 3 r c");
flowSlider = new JSlider(0, 255, 0);
flowSlider.setToolTipText("Flow - controls the flow of the brush.");
brushTab.add(flowSlider, "2 3 2 3");
flowSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!flowSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setFlow(flowSlider.getValue());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** SPACING */
brushTab.add(new JLabel(loadImageIcon("spacing.png")), "1 4 r c");
spacingSlider = new JSlider(4, 100, 4);
spacingSlider.setToolTipText("Spacing - controls the density of the brush.");
brushTab.add(spacingSlider, "2 4 2 4");
spacingSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!spacingSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setSpacing(spacingSlider.getValue() / 100f);
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** JITTER */
brushTab.add(new JLabel(loadImageIcon("jitter.png")), "1 5 r c");
jitterSlider = new JSlider(0, 400, 0);
jitterSlider.setToolTipText("Jitter - adds unpredictability to the position of the brush.");
brushTab.add(jitterSlider, "2 5 2 5");
jitterSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!jitterSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setJitter(jitterSlider.getValue() / 100f);
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** NOISE */
brushTab.add(new JLabel(loadImageIcon("noise.png")), "1 6 r c");
noiseSlider = new JSlider(0, 255, 0);
noiseSlider.setToolTipText("Noise - adds grains to the brush.");
brushTab.add(noiseSlider, "2 6 2 6");
noiseSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!noiseSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setNoise(noiseSlider.getValue());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** WATER */
brushTab.add(new JLabel(loadImageIcon("water.png")), "1 7 r c");
waterSlider = new JSlider(0, 255, 0);
waterSlider.setToolTipText("Watercolor - amount of color that is sampled and mixed with the current color.");
brushTab.add(waterSlider, "2 7 2 7");
waterSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!waterSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setWater(waterSlider.getValue());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** WATER AREA */
brushTab.add(new JLabel(loadImageIcon("waterarea.png")), "1 8 r c");
waterAreaSlider = new JSlider(1, 100, 75);
waterAreaSlider.setToolTipText("Watercolor Pickup Area - size of color sampling area compared to the brush size.");
brushTab.add(waterAreaSlider, "2 8 2 8");
waterAreaSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!waterSlider.getModel().getValueIsAdjusting() && !brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setWaterArea(waterAreaSlider.getValue() / 100f);
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** Pressure-to-Radius box */
radiusBox = new JCheckBox();
radiusBox.setToolTipText("Pressure to Size - vary brush size with pen pressure");
radiusBox.setEnabled(false);
brushTab.add(radiusBox, "3 1 c c");
radiusBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setPressureToRadius(radiusBox.isSelected());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** Pressure-to-Opacity box */
opacityBox = new JCheckBox();
opacityBox.setToolTipText("Pressure to Opacity - vary brush opacity with pen pressure");
opacityBox.setEnabled(false);
brushTab.add(opacityBox, "3 2 c c");
opacityBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setPressureToOpacity(opacityBox.isSelected());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** Pressure-to-Flow box */
flowBox = new JCheckBox();
flowBox.setToolTipText("Pressure to Flow - vary brush flow with pen pressure");
opacityBox.setEnabled(false);
flowBox.setEnabled(false);
brushTab.add(flowBox, "3 3 c c");
flowBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
client.getConnection().getUser().getBrush().setPressureToFlow(flowBox.isSelected());
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/** Lock Transparency box */
lockTransBox = new JCheckBox();
lockTransBox.setToolTipText("Lock Transparency - alter colors while simultaneously preserving underlying shapes");
brushTab.add(lockTransBox, "3 0 c c");
lockTransBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!brushAdjusting && client.getConnection().getUser() != null && client.getConnection().getUser().getBrush() != null) {
Brush b = client.getConnection().getUser().getBrush();
b.setLockTransparency(lockTransBox.isSelected());
Layer l = client.getConnection().getUser().getPhantomLayer();
if(lockTransBox.isSelected()) {
l.setAlphaRule(AlphaComposite.SRC_ATOP);
} else if(b.getOpacity() >= 0) {
l.setAlphaRule(AlphaComposite.SRC_OVER);
} else {
l.setAlphaRule(AlphaComposite.DST_OUT);
}
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SetBrushCommand(client.getConnection().getUser().getBrush())));
}
}
});
/* Layers tab */
final JCollapsiblePanel layersPanel = new JCollapsiblePanel(400, "Layers", true, true);
layersPanel.setGroup(mainGroup);
JPanel layersTab = layersPanel.getContentPane();
TableLayout layersTabLayout = new TableLayout(new double[][] {{-1, -2}, {-2, -2, -1}});
layersTabLayout.setHGap(4);
layersTabLayout.setVGap(4);
layersTab.setLayout(layersTabLayout);
rightPanel.add(layersPanel);
layerList = new JList();
layersTab.add(layerList, "0 0 0 2");
JButton addLayerButton = new JButton("Add");
addLayerButton.setToolTipText("Adds a new layer to the canvas. Prompts you for a name first. Layers are visible to all users.");
addLayerButton.setMargin(NO_INSETS);
layersTab.add(addLayerButton, "1 0");
addLayerButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String layerName = JOptionPane.showInputDialog("Name of new layer:");
if(layerName == null) {
return;
}
layerName = layerName.trim();
if(layerName.length() == 0) {
return;
}
client.getConnection().getSendQueue().offer(new CommandEntry(0, new CreateLayerCommand(0, LayerType.BITMAP, layerName)));
}
});
JButton remLayerButton = new JButton("Remove");
remLayerButton.setToolTipText("Removes the currently selected layer from the canvas. Prompts you for confirmation first.");
remLayerButton.setMargin(NO_INSETS);
layersTab.add(remLayerButton, "1 1");
remLayerButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(client.getConnection().getUser() == null || client.getConnection().getUser().getLayer() == null) {
return;
}
Layer layer = client.getConnection().getUser().getLayer();
int confirm = JOptionPane.showConfirmDialog(UserInterface.this, "Really remove layer " + layer.getName() + "?", "Confirm", JOptionPane.YES_NO_OPTION);
if(confirm != JOptionPane.OK_OPTION) {
return;
}
if(!layer.isEmpty()) {
confirm = JOptionPane.showConfirmDialog(UserInterface.this, "Layer " + layer.getName() + " is not empty! Are you absolutely sure?", "Confirm", JOptionPane.YES_NO_OPTION);
if(confirm != JOptionPane.OK_OPTION) {
return;
}
}
client.getConnection().getSendQueue().offer(new CommandEntry(0, new DeleteLayerCommand(client.getCanvas().getLayerMap().getKeyForValue(layer))));
}
});
/** Color tab */
colorPanel = new JCollapsiblePanel(500, "Color", false, false);
colorPanel.setGroup(mainGroup);
JPanel colorTab = colorPanel.getContentPane();
colorTab.setLayout(new BorderLayout());
rightPanel.add(colorPanel);
/** Color space tabs */
JTabbedPane colorSpaceTabs = new JTabbedPane(JTabbedPane.TOP);
colorTab.add(colorSpaceTabs, BorderLayout.CENTER);
/** RGB tab */
JPanel rgbTab = new JPanel();
colorSpaceTabs.addTab(null, loadImageIcon("rgb.png"), rgbTab, "RGB - picks colors with red, green, and blue sliders.");
rgbTab.setLayout(new TableLayout(new double[][] {{TableLayout.PREFERRED, TableLayout.FILL}, {TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.FILL}}));
ChangeListener rgbChangeListener = new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!colorAdjusting) {
int color = Pixels.pack(redSlider.getValue(), greenSlider.getValue(), blueSlider.getValue()) & 0xFFFFFF;
if(!((JSlider) arg0.getSource()).getModel().getValueIsAdjusting()) {
client.getCommandQueue().offer(new CommandEntry(0, new SetColorCommand(color)));
setCMYSliders(color);
setHSBSliders(color);
}
rgbColor.setBackground(new Color(color));
}
}
};
rgbTab.add(new JLabel(loadImageIcon("red.png")), "0 0 r c");
redSlider = new JSlider(0, 255, 0);
redSlider.setToolTipText("Red");
rgbTab.add(redSlider, "1 0 1 0");
redSlider.addChangeListener(rgbChangeListener);
rgbTab.add(new JLabel(loadImageIcon("green.png")), "0 1 r c");
greenSlider = new JSlider(0, 255, 0);
greenSlider.setToolTipText("Green");
rgbTab.add(greenSlider, "1 1 1 1");
greenSlider.addChangeListener(rgbChangeListener);
rgbTab.add(new JLabel(loadImageIcon("blue.png")), "0 2 r c");
blueSlider = new JSlider(0, 255, 0);
blueSlider.setToolTipText("Blue");
rgbTab.add(blueSlider, "1 2 1 2");
blueSlider.addChangeListener(rgbChangeListener);
rgbColor = new JPanel();
rgbColor.setBackground(Color.BLACK);
rgbTab.add(rgbColor, "0 3 1 3");
/** CMY tab */
JPanel cmyTab = new JPanel();
cmyTab.setLayout(new TableLayout(new double[][] {{TableLayout.PREFERRED, TableLayout.FILL}, {TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.FILL}}));
colorSpaceTabs.addTab(null, loadImageIcon("cmy.png"), cmyTab, "CMY - picks colors with cyan, magenta and yellow sliders.");
ChangeListener cmyChangeListener = new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!colorAdjusting) {
int color = Pixels.pack(255 - cyanSlider.getValue(), 255 - magentaSlider.getValue(), 255 - yellowSlider.getValue()) & 0xFFFFFF;
if(!((JSlider) arg0.getSource()).getModel().getValueIsAdjusting()) {
client.getCommandQueue().offer(new CommandEntry(0, new SetColorCommand(color)));
setRGBSliders(color);
setHSBSliders(color);
}
cmyColor.setBackground(new Color(color));
}
}
};
cmyTab.add(new JLabel(loadImageIcon("cyan.png")), "0 0 r c");
cyanSlider = new JSlider(0, 255, 255);
cyanSlider.setToolTipText("Cyan");
cmyTab.add(cyanSlider, "1 0 1 0");
cyanSlider.addChangeListener(cmyChangeListener);
cmyTab.add(new JLabel(loadImageIcon("magenta.png")), "0 1 r c");
magentaSlider = new JSlider(0, 255, 255);
magentaSlider.setToolTipText("Magenta");
cmyTab.add(magentaSlider, "1 1 1 1");
magentaSlider.addChangeListener(cmyChangeListener);
cmyTab.add(new JLabel(loadImageIcon("yellow.png")), "0 2 r c");
yellowSlider = new JSlider(0, 255, 255);
yellowSlider.setToolTipText("Yellow");
cmyTab.add(yellowSlider, "1 2 1 2");
yellowSlider.addChangeListener(cmyChangeListener);
cmyColor = new JPanel();
cmyColor.setBackground(Color.BLACK);
cmyTab.add(cmyColor, "0 3 1 3");
/** HSB tab */
JPanel hsbTab = new JPanel();
hsbTab.setLayout(new TableLayout(new double[][] {{TableLayout.PREFERRED, TableLayout.FILL}, {TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.FILL}}));
colorSpaceTabs.addTab(null, loadImageIcon("hsb.png"), hsbTab, "HSB - picks colors with hue, saturation, and brightness sliders.");
/** HSB change listener */
ChangeListener hsbChangeListener = new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!colorAdjusting) {
int color = Color.HSBtoRGB(hueSlider.getValue() / 256f, satSlider.getValue() / 256f, brightSlider.getValue() / 256f) & 0xFFFFFF;
if(!((JSlider) arg0.getSource()).getModel().getValueIsAdjusting()) {
client.getCommandQueue().offer(new CommandEntry(0, new SetColorCommand(color)));
setRGBSliders(color);
setCMYSliders(color);
}
hsbColor.setBackground(new Color(color));
}
}
};
hsbTab.add(new JLabel(new ImageIcon(getClass().getResource("images/hue.png"))), "0 0 r c");
hueSlider = new JSlider(0, 256, 0);
hueSlider.setToolTipText("Hue - basic color");
hsbTab.add(hueSlider, "1 0 1 0");
hueSlider.addChangeListener(hsbChangeListener);
hsbTab.add(new JLabel(new ImageIcon(getClass().getResource(
"images/saturation.png"))), "0 1 r c");
satSlider = new JSlider(0, 256, 0);
satSlider.setToolTipText("Saturation - color amount");
hsbTab.add(satSlider, "1 1 1 1");
satSlider.addChangeListener(hsbChangeListener);
hsbTab.add(new JLabel(new ImageIcon(getClass().getResource(
"images/brightness.png"))), "0 2 r c");
brightSlider = new JSlider(0, 256, 0);
brightSlider.setToolTipText("Brightness");
hsbTab.add(brightSlider, "1 2 1 2");
brightSlider.addChangeListener(hsbChangeListener);
hsbColor = new JPanel();
hsbColor.setBackground(Color.BLACK);
hsbTab.add(hsbColor, "0 3 1 3");
inputPanel = new JCollapsiblePanel(600, "Input", false, false);
inputPanel.setGroup(mainGroup);
rightPanel.add(inputPanel);
JPanel smoothPanelContent = inputPanel.getContentPane();
smoothPanelContent.setLayout(new TableLayout(new double[][] {{TableLayout.PREFERRED, TableLayout.FILL}, {TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED}}));
JLabel jtabletLabel = new JLabel();
jtabletLabel.setBackground(Color.BLUE);
jtabletLabel.setHorizontalAlignment(SwingConstants.CENTER);
if(editor.hasJTablet()) {
jtabletLabel.setText("JTablet detected!");
jtabletLabel.setForeground(new Color(0, 160, 0));
} else {
jtabletLabel.setText("JTablet NOT detected!");
jtabletLabel.setForeground(Color.RED);
}
smoothPanelContent.add(jtabletLabel, "0 0 1 0");
smoothPanelContent.add(new JLabel(loadImageIcon("trailing.png")), "0 1 r c");
final JSlider smoothSlider = new JSlider(0, 100, 15);
smoothSlider.setToolTipText("Cursor Trailing - aids the unsteady hand in drawing sweeping curves.");
smoothPanelContent.add(smoothSlider, "1 1");
smoothSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
if(!smoothSlider.getModel().getValueIsAdjusting()) {
Smoother.setSmoothness(smoothSlider.getValue() / 10f);
}
}
});
final JCheckBox curveSmoothBox = new JCheckBox("Curve Interpolation");
curveSmoothBox.setToolTipText("Analyzes cursor motion and smooths segmented lines into curves.");
curveSmoothBox.setSelected(true);
smoothPanelContent.add(curveSmoothBox, "0 2 1 2");
curveSmoothBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Smoother.setEnabled(curveSmoothBox.isSelected());
}
});
/** Create user list panel */
JCollapsiblePanel userListPanel = new JCollapsiblePanel(700, "Users", true, true);
userListPanel.setGroup(mainGroup);
final JPanel userListContent = userListPanel.getContentPane();
userListContent.setLayout(new BorderLayout());
rightPanel.add(userListPanel);
userList = new JUserList(client.getUserMap());
userListContent.add(userList, BorderLayout.CENTER);
/** Create chat panel */
JCollapsiblePanel chatPanel = new JCollapsiblePanel(800, "Chat", true, true);
chatPanel.setGroup(mainGroup);
JPanel chatContent = chatPanel.getContentPane();
chatContent.setLayout(new BorderLayout());
rightPanel.add(chatPanel);
/** Create chat scroll pane, add to chat panel */
chatPane = new JScrollPane();
ComponentListener chatPaneListener = new ComponentListener() {
public void componentHidden(ComponentEvent arg0) {
}
public void componentMoved(ComponentEvent arg0) {
}
public void componentResized(ComponentEvent arg0) {
scroll();
}
public void componentShown(ComponentEvent arg0) {
}
};
chatPane.addComponentListener(chatPaneListener);
chatContent.add(chatPane, BorderLayout.CENTER);
/** Create chat log, add to chat pane */
chatLog = new JTextArea();
chatPane.setViewportView(chatLog);
chatLog.setCursor(new Cursor(Cursor.TEXT_CURSOR));
chatLog.setEditable(false);
chatLog.setLineWrap(true);
chatLog.setWrapStyleWord(true);
chatLog.getCaret().setVisible(false);
chatLog.setText("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
/** Create chat field, add to chat panel */
chatField = new JTextField();
chatContent.add(chatField, BorderLayout.SOUTH);
chatField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String input = chatField.getText();
if(client.getConnection().getUser() == null) {
return;
}
String username = client.getConnection().getUser() == null ? null : client.getConnection().getUser().getName();
if(input.startsWith("*") && input.endsWith("*")) {
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SayCommand(" " + input.substring(1, input.length() - 1), true)));
} else if(input.startsWith(username)) {
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SayCommand(input.substring(username.length()), true)));
} else if(input.startsWith("/me")) {
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SayCommand(input.substring(3), true)));
} else if(input.startsWith("/kick ")) {
String kickUsername = input.substring(6);
boolean kicked = false;
for(Map.Entry<Integer, User> e: client.getUserMap().entrySet()) {
if(kickUsername.equalsIgnoreCase(e.getValue().getName())) {
client.getConnection().getSendQueue().offer(new CommandEntry(0, new KickCommand(e.getKey(), "Kicked by " + username)));
kicked = true;
break;
}
}
if(!kicked) {
println("The user you are trying to kick doesn't exist.");
}
} else if("/brush".equals(input)) {
Brush brush = client.getConnection().getUser().getBrush();
if(brush != null) {
println("/*");
println(" Hardness: " + brush.getHardness());
println(" Size: " + brush.getRadius());
println(" Opacity: " + brush.getFlow());
println(" Spacing: " + brush.getSpacing());
println(" Jitter: " + brush.getJitter());
println(" Noise: " + brush.getNoise());
println(" Water: " + brush.getWater());
println(" WaterArea: " + brush.getWaterArea());
println(" PressuteToOpacity: " + brush.isPressureToOpacity());
println(" PressureToFlow: " + brush.isPressureToFlow());
println(" PressureToSize: " + brush.isPressureToRadius());
println("*/");
}
} else if(!input.trim().isEmpty()) {
client.getConnection().getSendQueue().offer(new CommandEntry(0, new SayCommand(input, false)));
}
chatField.setText(null);
}
});
/** Adjust size of editor */
editor.adjust();
if(editor.hasJTablet()) {
opacityBox.setEnabled(true);
flowBox.setEnabled(true);
radiusBox.setEnabled(true);
}
validate();
scroll();
}
ImageIcon loadImageIcon(String filename) {
return new ImageIcon(getClass().getResource("images/" + filename));
}
private void updatePanels() {
boolean hasBrush = freehandButton.isSelected() || lineButton.isSelected() || bezierButton.isSelected() || rectangleButton.isSelected() || ovalButton.isSelected();
brushPanel.setVisible(hasBrush);
brushSettingsPanel.setVisible(hasBrush);
colorPanel.setVisible(hasBrush);
inputPanel.setVisible(hasBrush);
filterMenu.setEnabled(selectButton.isSelected());
}
public void updateBrushSliders() {
if(client.getConnection() == null || client.getConnection().getUser() == null || client.getConnection().getUser().getBrush() == null) {
return;
}
Brush brush = client.getConnection().getUser().getBrush();
brushAdjusting = true;
for(int i = 0; i < client.brushes.length; i++) {
brushButtons[i].setSelected(brush == client.brushes[i]);
}
hardnessSlider.setValue(brush.getHardness());
sizeSlider.setValue(Math.round(brush.getRadius() * 8f));
opacitySlider.setValue(brush.getOpacity());
flowSlider.setValue(brush.getFlow());
spacingSlider.setValue(Math.round(brush.getSpacing() * 100f));
jitterSlider.setValue(Math.round(brush.getJitter() * 100f));
noiseSlider.setValue(brush.getNoise());
waterSlider.setValue(brush.getWater());
waterAreaSlider.setValue(Math.round(brush.getWaterArea() * 100f));
opacityBox.setSelected(brush.isPressureToOpacity());
flowBox.setSelected(brush.isPressureToFlow());
radiusBox.setSelected(brush.isPressureToRadius());
lockTransBox.setSelected(brush.isLockTransparency());
brushAdjusting = false;
}
public void setColorSliders(int color) {
setRGBSliders(color);
setCMYSliders(color);
setHSBSliders(color);
}
public void setRGBSliders(int color) {
rgbColor.setBackground(new Color(color));
colorAdjusting = true;
redSlider.setValue(Pixels.getChannel1(color));
greenSlider.setValue(Pixels.getChannel2(color));
blueSlider.setValue(Pixels.getChannel3(color));
colorAdjusting = false;
}
public void setHSBSliders(int color) {
hsbColor.setBackground(new Color(color));
float[] hsb = new float[3];
Color.RGBtoHSB(Pixels.getChannel1(color), Pixels.getChannel2(color), Pixels.getChannel3(color), hsb);
hsb[0] *= 256f;
hsb[1] *= 256f;
hsb[2] *= 256f;
colorAdjusting = true;
hueSlider.setValue(Math.round(hsb[0]));
satSlider.setValue(Math.round(hsb[1]));
brightSlider.setValue(Math.round(hsb[2]));
colorAdjusting = false;
}
public void setCMYSliders(int color) {
cmyColor.setBackground(new Color(color));
colorAdjusting = true;
cyanSlider.setValue(255 - Pixels.getChannel1(color));
magentaSlider.setValue(255 - Pixels.getChannel2(color));
yellowSlider.setValue(255 - Pixels.getChannel3(color));
colorAdjusting = false;
}
public void println(String str) {
print("\n" + str);
}
public void print(final String str) {
Runnable swingWork = new Runnable() {
public void run() {
JScrollBar scrollBar = chatPane.getVerticalScrollBar();
BoundedRangeModel scrollModel = scrollBar.getModel();
boolean scroll = !scrollModel.getValueIsAdjusting() && scrollModel.getValue() == scrollModel.getMaximum() - scrollModel.getExtent();
chatLog.append(str);
if(scroll) {
scroll();
}
}
};
SwingUtilities.invokeLater(swingWork);
}
public void scroll() {
chatPane.validate();
BoundedRangeModel model = chatPane.getVerticalScrollBar().getModel();
model.setValue(model.getMaximum() - model.getExtent());
}
public void setCanvas(final Canvas canvas) {
editor.setCanvas(canvas);
canvas.getLayerMap().addChangeListener(new ChangeListener() {
@SuppressWarnings("unchecked")
public void stateChanged(ChangeEvent e) {
TwoWayHashMap<Integer, Layer> map = (TwoWayHashMap<Integer, Layer>)e.getSource();
final Object[] array = map.values().toArray();
ListModel model = new AbstractListModel() {
public int getSize() {
return array.length;
}
public Object getElementAt(int index) {
Layer l = (Layer)array[array.length - 1 - index];
return l.getName();
}
};
layerList.setModel(model);
layerList.invalidate();
}
});
layerList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if(layerAdjusting) {
return;
}
JList source = (JList)e.getSource();
Object[] array = canvas.getLayerMap().keySet().toArray();
if(source.getSelectedIndex() != -1) {
client.getCommandQueue().offer(new CommandEntry(0, new SetLayerCommand((Integer)array[array.length - 1 - source.getSelectedIndex()])));
}
}
});
}
public Editor getEditor() {
return editor;
}
public JScrollPane getEditorPane() {
return editorPane;
}
public void setSmoothZoom(boolean smoothZoom) {
this.smoothZoom = smoothZoom;
smoothBox.setSelected(smoothZoom);
editor.setSmoothZoom(smoothZoom);
editor.repaint();
}
public boolean isSmoothZoom() {
return smoothZoom;
}
public void setSoundItem(JCheckBoxMenuItem soundItem) {
this.soundItem = soundItem;
}
public JCheckBoxMenuItem getSoundItem() {
return soundItem;
}
public void setTagsItem(JCheckBoxMenuItem tagsItem) {
this.tagsItem = tagsItem;
}
public JCheckBoxMenuItem getTagsItem() {
return tagsItem;
}
public Set<JCollapsiblePanel> getToolPanels() {
return mainGroup;
}
public JUserList getUserList() {
return userList;
}
public void updateLayer() {
Layer currentLayer = client.getConnection().getUser().getLayer();
Set<Map.Entry<Integer, Layer>> es = client.getCanvas().getLayerMap().entrySet();
int index = es.size() - 1;
for(Map.Entry<Integer, Layer> e : es) {
if(e.getValue() == currentLayer) {
break;
}
--index;
}
layerAdjusting = true;
layerList.setSelectedIndex(index);
layerAdjusting = false;
}
public BrushButton[] getBrushButtons() {
return brushButtons;
}
public class BrushButton extends JToggleButton {
public BrushButton(final UserInterface parent, final Brush[] brushArray, final Icon[] brushIconArray, final int brushIndex) {
super();
setIcon(brushIconArray[brushIndex]);
setToolTipText(brushArray[brushIndex].getName());
setMargin(UserInterface.NO_INSETS);
setMinimumSize(new Dimension(32, 32));
setPreferredSize(new Dimension(32, 32));
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!parent.brushAdjusting) {
parent.client.getCommandQueue().offer(new CommandEntry(0, new SetBrushCommand(brushArray[brushIndex])));
}
}
});
}
}
}