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 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 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 map = (TwoWayHashMap)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 getToolPanels() { return mainGroup; } public JUserList getUserList() { return userList; } public void updateLayer() { Layer currentLayer = client.getConnection().getUser().getLayer(); Set> es = client.getCanvas().getLayerMap().entrySet(); int index = es.size() - 1; for(Map.Entry 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]))); } } }); } } }