Intial commit

master
Thor 3 years ago
commit 74627c9c7a
  1. 14
      .classpath
  2. 8
      .gitignore
  3. 42
      build.xml
  4. BIN
      lib/TableLayout-bin-jdk1.5-2009-08-26.jar
  5. BIN
      lib/jspeex.jar
  6. BIN
      lib/jtablet-2010-04-13-1.jar
  7. BIN
      lib/jtablet2-thin-2010-04-13-1.jar
  8. BIN
      lib/proguard.jar
  9. 2
      lib/proguard_task.properties
  10. BIN
      sketcher2-client-20120527-1225.jar
  11. 1215
      sketcher2-client-20120527-1225.map
  12. BIN
      sketcher2-client-resources-20120527-1225.jar
  13. BIN
      sketcher2-server-20120527-1225.jar
  14. 626
      src/com/jotuntech/sketcher/client/Client.java
  15. 54
      src/com/jotuntech/sketcher/client/ClientApplet.java
  16. 8
      src/com/jotuntech/sketcher/client/Command.java
  17. 24
      src/com/jotuntech/sketcher/client/CommandEntry.java
  18. 113
      src/com/jotuntech/sketcher/client/Connection.java
  19. 1278
      src/com/jotuntech/sketcher/client/Editor.java
  20. 154
      src/com/jotuntech/sketcher/client/JCollapsiblePanel.java
  21. 7
      src/com/jotuntech/sketcher/client/JCollapsiblePanelGroup.java
  22. 74
      src/com/jotuntech/sketcher/client/JUserEntry.java
  23. 57
      src/com/jotuntech/sketcher/client/JUserList.java
  24. 81
      src/com/jotuntech/sketcher/client/LookAndFeel.java
  25. 568
      src/com/jotuntech/sketcher/client/PSDEncoder.java
  26. 141
      src/com/jotuntech/sketcher/client/PackBitsOutputStream.java
  27. 150
      src/com/jotuntech/sketcher/client/Smoother.java
  28. 1723
      src/com/jotuntech/sketcher/client/UserInterface.java
  29. BIN
      src/com/jotuntech/sketcher/client/audio/chat.wav
  30. BIN
      src/com/jotuntech/sketcher/client/audio/intro.wav
  31. BIN
      src/com/jotuntech/sketcher/client/audio/kick.wav
  32. BIN
      src/com/jotuntech/sketcher/client/audio/outtro.wav
  33. BIN
      src/com/jotuntech/sketcher/client/audio/query.wav
  34. BIN
      src/com/jotuntech/sketcher/client/audio/signin.wav
  35. BIN
      src/com/jotuntech/sketcher/client/audio/signout.wav
  36. 56
      src/com/jotuntech/sketcher/client/command/CanvasCommand.java
  37. 56
      src/com/jotuntech/sketcher/client/command/CreateLayerCommand.java
  38. 56
      src/com/jotuntech/sketcher/client/command/CursorCommand.java
  39. 74
      src/com/jotuntech/sketcher/client/command/DeleteLayerCommand.java
  40. 82
      src/com/jotuntech/sketcher/client/command/FilterCommand.java
  41. 58
      src/com/jotuntech/sketcher/client/command/KickCommand.java
  42. 66
      src/com/jotuntech/sketcher/client/command/LayerDataCommand.java
  43. 73
      src/com/jotuntech/sketcher/client/command/LineCommand.java
  44. 45
      src/com/jotuntech/sketcher/client/command/MergeCommand.java
  45. 68
      src/com/jotuntech/sketcher/client/command/MoveCommand.java
  46. 32
      src/com/jotuntech/sketcher/client/command/PingCommand.java
  47. 62
      src/com/jotuntech/sketcher/client/command/SayCommand.java
  48. 41
      src/com/jotuntech/sketcher/client/command/ServerMessageCommand.java
  49. 57
      src/com/jotuntech/sketcher/client/command/SetBrushCommand.java
  50. 53
      src/com/jotuntech/sketcher/client/command/SetColorCommand.java
  51. 64
      src/com/jotuntech/sketcher/client/command/SetLayerCommand.java
  52. 96
      src/com/jotuntech/sketcher/client/command/SignInCommand.java
  53. 49
      src/com/jotuntech/sketcher/client/command/SignOutCommand.java
  54. 35
      src/com/jotuntech/sketcher/client/command/UndoCommand.java
  55. 45
      src/com/jotuntech/sketcher/client/command/UndoDataCommand.java
  56. 46
      src/com/jotuntech/sketcher/client/command/UndoEntryCommand.java
  57. 37
      src/com/jotuntech/sketcher/client/command/VoiceCommand.java
  58. BIN
      src/com/jotuntech/sketcher/client/images/about.png
  59. BIN
      src/com/jotuntech/sketcher/client/images/ad1.png
  60. BIN
      src/com/jotuntech/sketcher/client/images/bezier.png
  61. BIN
      src/com/jotuntech/sketcher/client/images/blue.png
  62. BIN
      src/com/jotuntech/sketcher/client/images/brightness.png
  63. BIN
      src/com/jotuntech/sketcher/client/images/brush.png
  64. BIN
      src/com/jotuntech/sketcher/client/images/cmy.png
  65. BIN
      src/com/jotuntech/sketcher/client/images/color.png
  66. BIN
      src/com/jotuntech/sketcher/client/images/crosshair.gif
  67. BIN
      src/com/jotuntech/sketcher/client/images/cyan.png
  68. BIN
      src/com/jotuntech/sketcher/client/images/dummybanner.png
  69. BIN
      src/com/jotuntech/sketcher/client/images/eraser.png
  70. BIN
      src/com/jotuntech/sketcher/client/images/eyedropper.gif
  71. BIN
      src/com/jotuntech/sketcher/client/images/flow.png
  72. BIN
      src/com/jotuntech/sketcher/client/images/freehand.png
  73. BIN
      src/com/jotuntech/sketcher/client/images/green.png
  74. BIN
      src/com/jotuntech/sketcher/client/images/hand.gif
  75. BIN
      src/com/jotuntech/sketcher/client/images/hardness.png
  76. BIN
      src/com/jotuntech/sketcher/client/images/hsb.png
  77. BIN
      src/com/jotuntech/sketcher/client/images/hue.png
  78. BIN
      src/com/jotuntech/sketcher/client/images/ink.png
  79. BIN
      src/com/jotuntech/sketcher/client/images/jitter.png
  80. BIN
      src/com/jotuntech/sketcher/client/images/layers.png
  81. BIN
      src/com/jotuntech/sketcher/client/images/line.png
  82. BIN
      src/com/jotuntech/sketcher/client/images/madeinsketcher.png
  83. BIN
      src/com/jotuntech/sketcher/client/images/magenta.png
  84. BIN
      src/com/jotuntech/sketcher/client/images/move.png
  85. BIN
      src/com/jotuntech/sketcher/client/images/noise.png
  86. BIN
      src/com/jotuntech/sketcher/client/images/opacity.png
  87. BIN
      src/com/jotuntech/sketcher/client/images/oval.png
  88. BIN
      src/com/jotuntech/sketcher/client/images/pen.png
  89. BIN
      src/com/jotuntech/sketcher/client/images/pencil.png
  90. BIN
      src/com/jotuntech/sketcher/client/images/pin_normal.png
  91. BIN
      src/com/jotuntech/sketcher/client/images/pin_stuck.png
  92. BIN
      src/com/jotuntech/sketcher/client/images/rectangle.png
  93. BIN
      src/com/jotuntech/sketcher/client/images/red.png
  94. BIN
      src/com/jotuntech/sketcher/client/images/rgb.png
  95. BIN
      src/com/jotuntech/sketcher/client/images/saturation.png
  96. BIN
      src/com/jotuntech/sketcher/client/images/select.png
  97. BIN
      src/com/jotuntech/sketcher/client/images/size.png
  98. BIN
      src/com/jotuntech/sketcher/client/images/sketcherlogo.png
  99. BIN
      src/com/jotuntech/sketcher/client/images/smoothing.png
  100. BIN
      src/com/jotuntech/sketcher/client/images/spacing.png
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/TableLayout-bin-jdk1.5-2009-08-26.jar"/>
<classpathentry kind="lib" path="lib/jspeex.jar">
<attributes>
<attribute name="javadoc_location" value="http://jspeex.sourceforge.net/doc/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/jtablet-2010-04-13-1.jar"/>
<classpathentry kind="lib" path="lib/jtablet2-thin-2010-04-13-1.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

8
.gitignore vendored

@ -0,0 +1,8 @@
**/.DS_Store
**/*.p12
.settings
.project
bin/
obfuscate-sketcher1.xml

@ -0,0 +1,42 @@
<project name="Sketcher2" default="dist" basedir=".">
<taskdef resource="proguard_task.properties" classpath="lib:lib/proguard.jar" />
<target name="dist">
<tstamp/>
<jar destfile="sketcher2-server-${DSTAMP}-${TSTAMP}.jar" basedir="bin" includes="com/jotuntech/sketcher/common/**,com/jotuntech/sketcher/server/**"/>
<jar destfile="sketcher2-client-internal-${DSTAMP}-${TSTAMP}.jar" basedir="bin" includes="com/jotuntech/sketcher/common/**,com/jotuntech/sketcher/client/**" excludes="com/jotuntech/sketcher/client/audio/**,com/jotuntech/sketcher/client/images/**"/>
<jar destfile="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" basedir="bin" includes="com/jotuntech/sketcher/client/audio/**,com/jotuntech/sketcher/client/images/**"/>
<proguard>
-libraryjars "${java.home}/lib/rt.jar"
-libraryjars lib/
-injars sketcher2-client-internal-${DSTAMP}-${TSTAMP}.jar
-outjars sketcher2-client-${DSTAMP}-${TSTAMP}.jar
-printmapping sketcher2-client-${DSTAMP}-${TSTAMP}.map
-keep public class * extends java.applet.Applet
-keep public class * extends com.jotuntech.sketcher.client.Command
-keep public class * extends com.jotuntech.sketcher.common.filter.Filter
</proguard>
<property name="certificate.keystore" location="jotun.p12"/>
<property name="certificate.storetype" value="pkcs12"/>
<property name="certificate.alias" value="le-20ab1c70-a64e-486b-ac42-a0f8ac0941f9"/>
<property name="certificate.password" value="rweysgib"/>
<signjar jar="sketcher2-client-${DSTAMP}-${TSTAMP}.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<!--
<signjar jar="lib/jtablet2-thin-2010-04-13-1.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/jtablet-2010-04-13-1.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/jspeex.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/mail.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
<signjar jar="lib/TableLayout-bin-jdk1.5-2009-08-26.jar" alias="${certificate.alias}" storepass="${certificate.password}" keystore="${certificate.keystore}" storetype="${certificate.storetype}" verbose="true"/>
-->
<delete file="sketcher2-client-internal-${DSTAMP}-${TSTAMP}.jar"/>
<copy file="sketcher2-client-${DSTAMP}-${TSTAMP}.jar" tofile="../ArtGrounds2/resources/sketcher2-client.jar"/>
<copy file="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" tofile="../ArtGrounds2/resources/sketcher2-client-resources.jar"/>
<copy file="sketcher2-client-resources-${DSTAMP}-${TSTAMP}.jar" tofile="sketcher2-client-resources.jar"/>
<copy file="sketcher2-server-${DSTAMP}-${TSTAMP}.jar" tofile="../ArtGrounds2/lib/sketcher2-server.jar"/>
<copy file="sketcher2-server-${DSTAMP}-${TSTAMP}.jar" tofile="../JotunWebsite/lib/sketcher2-server.jar"/>
</target>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,2 @@
proguard = proguard.ant.ProGuardTask
proguardconfiguration = proguard.ant.ConfigurationTask

File diff suppressed because it is too large Load Diff

@ -0,0 +1,626 @@
package com.jotuntech.sketcher.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.jotuntech.sketcher.client.command.PingCommand;
import com.jotuntech.sketcher.client.command.SignInCommand;
import com.jotuntech.sketcher.client.command.SignOutCommand;
import com.jotuntech.sketcher.client.voice.VoiceClient;
import com.jotuntech.sketcher.client.voice.VoiceEvent;
import com.jotuntech.sketcher.client.voice.VoiceListener;
import com.jotuntech.sketcher.common.Brush;
import com.jotuntech.sketcher.common.Canvas;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.Log;
import com.jotuntech.sketcher.common.StreamableUtils;
import com.jotuntech.sketcher.common.TwoWayHashMap;
import com.jotuntech.sketcher.common.User;
/**
* @author Thor Harald Johansen
*
*/
public class Client extends Thread {
private UserInterface userInterface;
private TwoWayHashMap<Integer, User> userMap;
private User[] userArray;
private Canvas canvas;
private long lastLayerClean;
private Connection connection;
private BlockingQueue<CommandEntry> commandQueue;
private ByteBuffer commandBuffer;
private String login;
private String password;
private String hostname;
private int port;
private VoiceClient voiceClient;
private boolean soundEnabled;
private boolean ads;
//private Random sendRandom = new Random(0x707733360A596E0AL);
//private Random recvRandom = new Random(0x59A70A401C8CD1EAL);
private Properties props;
private File propsFile;
Timer propsTimer = new Timer("PropsTimer", true);
private Brush defaultBrushes[] = new Brush[] {
new Brush("Pen", 255, 255, 3.375f, 1, 0.27f, true, false, true, 0, 0, 0, false, 0.5f),
new Brush("Pencil", 255, 255, 2f, 2, 0.45f, true, false, true, 1.5f, 0, 0, false, 0.5f),
new Brush("Water", 255, 255, 16f, 4, 0.22f, true, false, true, 0, 0, 128, false, 0.5f),
new Brush("Eraser", -255, 192, 16f, 4, 0.2f, false, true, false, 0, 0, 0, false, 0.5f),
new Brush("Ink", 255, 255, 4f, 1, 0.04f, false, false, true, 0, 0, 0, false, 0.5f),
new Brush("Wipe", -255, 255, 64f, 7, 0.2f, false, false, false, 0, 0, 0, false, 0.5f)
};
protected Brush[] brushes = new Brush[defaultBrushes.length];
public Client(String hostname, int port, String login, String password, boolean ads) {
super("ClientThread");
/** Save hostname and port */
this.hostname = hostname;
this.port = port;
/** Save login and password */
this.login = login;
this.password = password;
/** Save ad setting */
this.ads = ads;
/** Create user map & array */
userMap = new TwoWayHashMap<Integer, User>();
userMap.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
/** Populate user array in a thread-safe way */
User[] newUserArray = new User[0];
newUserArray = userMap.values().toArray(newUserArray);
userArray = newUserArray;
}
});
/** Create user array */
userArray = new User[0];
/** Create command queue */
commandQueue = new ArrayBlockingQueue<CommandEntry>(65536, true);
for(int i = 0; i < brushes.length; i++) {
brushes[i] = defaultBrushes[i].copy();
}
/** Create user interface */
userInterface = new UserInterface(this);
/** Load properties file */
try {
propsFile = new File(System.getProperty("user.home"), "sketcher.properties");
} catch(SecurityException e) {
propsFile = null;
}
loadProps();
/** Set last layer cleanup time */
lastLayerClean = System.currentTimeMillis();
propsTimer.schedule(new TimerTask() {
public void run() {
saveProps();
}
}, 60000, 60000);
}
synchronized public void resetProps() {
if(propsFile != null) {
propsFile.delete();
}
loadProps();
}
synchronized private void loadProps() {
props = new Properties();
if(propsFile != null) {
try {
if(propsFile.exists()) {
FileInputStream is = new FileInputStream(propsFile);
props.load(is);
is.close();
}
} catch(IOException e) {
} catch(SecurityException e) {
}
}
int propsVersion = Integer.valueOf(props.getProperty("file.version", "0"));
soundEnabled = "true".equals(props.getProperty("sound.enabled", "true"));
userInterface.getSoundItem().setSelected(soundEnabled);
userInterface.setSmoothZoom("true".equals(props.getProperty("zoom.smooth", "true")));
userInterface.getEditor().setTagsEnabled("true".equals(props.getProperty("tags.enabled", "true")));
userInterface.getEditor().setSoftwareCursorEnabled("true".equals(props.getProperty("cursor.software", "false")));
userInterface.getTagsItem().setSelected(userInterface.getEditor().isTagsEnabled());
for(JCollapsiblePanel panel : userInterface.getToolPanels()) {
panel.setExpanded("true".equals(props.getProperty("panel" + panel.getId() + ".expanded", panel.isExpandedByDefault() ? "true" : "false")), false);
panel.setStuck("true".equals(props.getProperty("panel" + panel.getId() + ".stuck", panel.isStuckByDefault() ? "true" : "false")));
}
for(int i = 0; i < brushes.length; i++) {
brushes[i] = defaultBrushes[i].copy();
}
for(int i = 0; i < defaultBrushes.length; i++) {
String name = props.getProperty("brush" + i + ".name");
String opacity = props.getProperty("brush" + i + ".opacity");
String flow = props.getProperty("brush" + i + ".flow");
String radius = props.getProperty("brush" + i + ".radius");
String hardness = props.getProperty("brush" + i + ".hardness");
String spacing = props.getProperty("brush" + i + ".spacing");
String pressureToFlow, pressureToOpacity;
if(propsVersion < 1) {
pressureToFlow = props.getProperty("brush" + i + ".pressureToOpacity");
pressureToOpacity = props.getProperty("brush" + i + ".realPressureToOpacity");
} else {
pressureToFlow = props.getProperty("brush" + i + ".pressureToFlow");
pressureToOpacity = props.getProperty("brush" + i + ".pressureToOpacity");
}
String pressureToRadius = props.getProperty("brush" + i + ".pressureToRadius");
String jitter = props.getProperty("brush" + i + ".jitter");
String water = props.getProperty("brush" + i + ".water");
String waterArea = props.getProperty("brush" + i + ".waterArea");
String lockTransparency = props.getProperty("brush" + i + ".lockTransparency");
Brush b = brushes[i];
if(name != null) {
b.setName(name);
}
if(opacity != null) {
b.setOpacity(Integer.valueOf(opacity));
}
if(flow != null) {
b.setFlow(Integer.valueOf(flow));
}
if(radius != null) {
b.setRadius(Float.valueOf(radius));
}
if(hardness != null) {
b.setHardness(Integer.valueOf(hardness));
}
if(spacing != null) {
b.setSpacing(Float.valueOf(spacing));
}
if(pressureToOpacity != null) {
b.setPressureToOpacity("true".equals(pressureToOpacity));
}
if(pressureToFlow != null) {
b.setPressureToFlow("true".equals(pressureToFlow));
}
if(pressureToRadius != null) {
b.setPressureToRadius("true".equals(pressureToRadius));
}
if(jitter != null) {
b.setJitter(Float.valueOf(jitter));
}
if(water != null) {
b.setWater(Integer.valueOf(water));
}
if(waterArea != null) {
b.setWaterArea(Float.valueOf(waterArea));
}
if(lockTransparency != null) {
b.setLockTransparency("true".equals(lockTransparency));
}
}
}
synchronized private void saveProps() {
if(propsFile == null) {
return;
}
props.setProperty("file.version", "1"); // Increase this when making format changes!
props.setProperty("sound.enabled", soundEnabled ? "true" : "false");
props.setProperty("zoom.smooth", userInterface.isSmoothZoom() ? "true" : "false");
props.setProperty("tags.enabled", userInterface.getEditor().isTagsEnabled() ? "true" : "false");
props.setProperty("cursor.software", userInterface.getEditor().isSoftwareCursorEnabled() ? "true" : "false");
for(JCollapsiblePanel panel : userInterface.getToolPanels()) {
props.setProperty("panel" + panel.getId() + ".expanded", panel.isExpanded() ? "true" : "false");
props.setProperty("panel" + panel.getId() + ".stuck", panel.isStuck() ? "true" : "false");
}
for(int i = 0; i < brushes.length; i++) {
props.setProperty("brush" + i + ".name", brushes[i].getName());
props.setProperty("brush" + i + ".opacity", String.valueOf(brushes[i].getOpacity()));
props.setProperty("brush" + i + ".flow", String.valueOf(brushes[i].getFlow()));
props.setProperty("brush" + i + ".radius", String.valueOf(brushes[i].getRadius()));
props.setProperty("brush" + i + ".hardness", String.valueOf(brushes[i].getHardness()));
props.setProperty("brush" + i + ".spacing", String.valueOf(brushes[i].getSpacing()));
props.setProperty("brush" + i + ".pressureToOpacity", brushes[i].isPressureToOpacity() ? "true" : "false");
props.setProperty("brush" + i + ".pressureToFlow", brushes[i].isPressureToFlow() ? "true" : "false");
props.setProperty("brush" + i + ".pressureToRadius", brushes[i].isPressureToRadius() ? "true" : "false");
props.setProperty("brush" + i + ".jitter", String.valueOf(brushes[i].getJitter()));
props.setProperty("brush" + i + ".noise", String.valueOf(brushes[i].getNoise()));
props.setProperty("brush" + i + ".water", String.valueOf(brushes[i].getWater()));
props.setProperty("brush" + i + ".waterArea", String.valueOf(brushes[i].getWaterArea()));
props.setProperty("brush" + i + ".lockTransparency", brushes[i].isLockTransparency() ? "true" : "false");
}
try {
FileOutputStream os = new FileOutputStream(propsFile);
props.store(os, "Sketcher Properties File");
os.close();
} catch(IOException e) {
} catch(SecurityException e) {
propsTimer.cancel();
}
}
public void close(String message) {
propsTimer.cancel();
saveProps();
/** Empty the send queue */
connection.getSendQueue().clear();
/** Send sign-out command */
connection.getSendQueue().offer(new CommandEntry(0, new SignOutCommand(message)));
/** Schedule connection death */
connection.setTimeOfDeath(System.currentTimeMillis() + 1000);
/** Play sound */
if("true".equals(props.getProperty("sound.enabled", "true"))) {
UserInterface.AUDIO_OUTTRO.play();
}
}
public TwoWayHashMap<Integer, User> getUserMap() {
return userMap;
}
public User[] getUserArray() {
return userArray;
}
public Canvas getCanvas() {
return canvas;
}
public void setCanvas(Canvas canvas) {
this.canvas = canvas;
userInterface.setCanvas(canvas);
}
public UserInterface getUserInterface() {
return userInterface;
}
public Connection getConnection() {
return connection;
}
public void run() {
try {
/** Create client connection */
connection = new Connection(SocketChannel.open());
userInterface.println("Connecting... ");
/** Enable blocking for connect */
connection.getChannel().configureBlocking(true);
/** Attempt to connect to server */
connection.getChannel().connect(new InetSocketAddress(hostname, port));
/** Disable blocking after connect */
connection.getChannel().configureBlocking(false);
userInterface.print("Success!");
/** Play audio */
if("true".equals(props.getProperty("sound.enabled", "true"))) {
UserInterface.AUDIO_INTRO.play();
}
/** Create command buffer */
commandBuffer = ByteBuffer.allocate(65538);
/** Send sign-in command */
connection.getSendQueue().offer(new CommandEntry(0, new SignInCommand(0, login, password, false)));
/** Set last ping time */
connection.setLastPing(0);
while(!Thread.interrupted()) {
if(!connection.getChannel().isConnected()) {
userInterface.println("Disconnected (Connection was closed)");
break;
} else if(connection.isTimeOfDeath()) {
try { connection.getChannel().close(); } catch(IOException e) { }
userInterface.println("Disconnected (Exit)");
break;
}
if(System.currentTimeMillis() - connection.getLastPing() >= Connection.PING_INTERVAL) {
long timestamp = System.currentTimeMillis();
connection.getSendQueue().offer(new CommandEntry(0, new PingCommand(timestamp)));
connection.setLastPing(timestamp);
}
for(CommandEntry e = commandQueue.poll(20, TimeUnit.MILLISECONDS); e != null; e = commandQueue.poll(20, TimeUnit.MILLISECONDS)) {
if(e.getCommand().perform(this, e.getSourceKey() == 0 ? connection.getUser() : userMap.get(e.getSourceKey())) == Connection.SEND_OTHERS) {
connection.getSendQueue().offer(new CommandEntry(e.getSourceKey(), e.getCommand()));
}
}
if(canvas != null && lastLayerClean + 30000 < System.currentTimeMillis()) {
lastLayerClean = System.currentTimeMillis();
for(Layer e : canvas.getLayerMap().values()) {
e.clean();
}
}
ByteBuffer inputBuffer = connection.getInputBuffer();
ByteBuffer outputBuffer = connection.getOutputBuffer();
ArrayBlockingQueue<CommandEntry> sendQueue = connection.getSendQueue();
//int startPosition = outputBuffer.position();
/** Only check queue if room for command length, peer key, name length, and one character of name */
while(outputBuffer.remaining() > 2 + 4 + 1 + 2 && sendQueue.size() > 0) {
CommandEntry e = sendQueue.peek();
Integer k = e.getSourceKey();
Command c = e.getCommand();
/** Clear command buffer */
commandBuffer.clear();
/** Write peer key */
commandBuffer.putInt(k);
/** Write command name */
String commandName = c.getClass().getSimpleName();
commandBuffer.put((byte) (commandName.length() - 1));
for(int i = 0; i < commandName.length(); i++) {
commandBuffer.putChar(commandName.charAt(i));
}
/** Write command */
c.encode(commandBuffer);
/** Flip command buffer before reading */
commandBuffer.flip();
/** Do not send this command yet if no room in output buffer */
if(commandBuffer.remaining() + 2 > outputBuffer.remaining()) {
break;
}
/** Write append command length and command data to output buffer */
outputBuffer.putShort((short) (commandBuffer.remaining() - 1));
outputBuffer.put(commandBuffer);
/** Remove command from queue */
sendQueue.remove();
}
// Primitive encryption
//for(int i = startPosition; i < outputBuffer.position(); i++) {
// outputBuffer.put(i, (byte) (outputBuffer.get(i) ^ sendRandom.nextInt(256)));
//}
/** Flip output buffer */
outputBuffer.flip();
if(outputBuffer.remaining() > 0) {
connection.getChannel().write(outputBuffer);
}
/** Compact buffer, preparing it for appending */
outputBuffer.compact();
if(canvas == null || !canvas.isDrawing()) {
try {
/** Append to input buffer */
//startPosition = inputBuffer.position();
int readResult = connection.getChannel().read(inputBuffer);
if(readResult == -1) {
try { connection.getChannel().close(); } catch(IOException e) { }
userInterface.println("Disconnected (Connection was closed)");
break;
} else if(readResult > 0) {
//for(int i = startPosition; i < inputBuffer.position(); i++) {
// inputBuffer.put(i, (byte) (inputBuffer.get(i) ^ recvRandom.nextInt(256)));
//}
/** Prepare buffer for reading */
inputBuffer.flip();
/** Long enough to peek at length? */
while((canvas == null || !canvas.isDrawing()) && inputBuffer.remaining() >= 2) {
/** Peek at length */
int commandLength = (inputBuffer.getShort(inputBuffer.position()) & 0xFFFF) + 1;
/** Don't read if not long enough to read entire command. */
if(inputBuffer.remaining() < commandLength + 2) {
break;
}
/** We already have the command length */
inputBuffer.getShort();
/** Read command buffer */
ByteBuffer commandBuffer = inputBuffer.slice();
commandBuffer.limit(commandLength);
inputBuffer.position(inputBuffer.position() + commandLength);
/** Read user key */
Integer userKey = commandBuffer.getInt();
/** Read command name */
int commandNameLength = (commandBuffer.get() & 0xFF) + 1;
StringBuffer commandNameBuffer = new StringBuffer();
for(int i = 0; i < commandNameLength; i++) {
commandNameBuffer.append(commandBuffer.getChar());
}
String commandName = commandNameBuffer.toString();
Command command = (Command) StreamableUtils.create("com.jotuntech.sketcher.client.command." + commandName, commandBuffer);
command.perform(this, userMap.get(userKey));
//++iterations;
}
/** Prepare input buffer for appending */
inputBuffer.compact();
}
} catch (IOException e) {
try { connection.getChannel().close(); } catch(IOException e2) { }
userInterface.println("Disconnected (Connection was broken: " + e.getMessage() + ")");
break;
} catch(Throwable t) {
sendQueue.clear();
sendQueue.offer(new CommandEntry(0, new SignOutCommand("Client crashed: " + t.getClass().getSimpleName())));
connection.setTimeOfDeath(System.currentTimeMillis() + 5000);
Log.error(t);
userInterface.println("Disconnected (Client crashed: " + t.getClass().getSimpleName() + ")");
}
}
}
} catch (InterruptedException e) {
Log.debug("Client interrupted.");
} catch (IOException e) {
userInterface.println("Disconnected (Connection was broken: " + e.getMessage() + ")");
Log.error(e);
}
try { connection.getChannel().close(); } catch(IOException e) { }
connection.setChannel(null);
connection.setUser(null);
if("true".equals(props.getProperty("sound.enabled", "true"))) {
UserInterface.AUDIO_OUTTRO.play();
}
}
public BlockingQueue<CommandEntry> getCommandQueue() {
return commandQueue;
}
public boolean isVoiceEnabled() {
return voiceClient != null;
}
public void setVoiceEnabled(boolean voiceEnabled) {
if(voiceEnabled) {
final Integer peerKey = getUserMap().getKeyForValue(getConnection().getUser());
voiceClient = new VoiceClient(new InetSocketAddress(hostname, port + 100), peerKey);
voiceClient.setListener(new VoiceListener() {
public void voiceEvent(VoiceEvent e) {
switch(e.getType()) {
case VoiceEvent.TYPE_CHANNEL_NEW:
Integer userKey = e.getChannel();
User user = userMap.get(userKey);
if(user == null) {
break;
}
break;
case VoiceEvent.TYPE_CHANNEL_DEAD: {
JUserEntry ue = userInterface.getUserList().getEntry(e.getChannel());
if(ue == null) {
break;
}
ue.clearVoice();
break;
}
case VoiceEvent.TYPE_PACKET_VOLUME: {
JUserEntry ue = userInterface.getUserList().getEntry(e.getChannel());
if(ue == null) {
break;
}
if(e.getChannel() == peerKey) {
ue.setVolume(e.getVolume(), 0);
} else {
ue.setVolume(e.getVolume(), 12);
}
break;
}
}
}
});
voiceClient.start();
} else {
voiceClient.interrupt();
try { voiceClient.join(); } catch (InterruptedException e) { }
voiceClient = null;
userInterface.getUserList().clearVoice();
}
}
public VoiceClient getVoiceClient() {
return voiceClient;
}
public Properties getProps() {
return props;
}
public void setSoundEnabled(boolean soundEnabled) {
this.soundEnabled = soundEnabled;
}
public boolean isSoundEnabled() {
return soundEnabled;
}
public void setAds(boolean ads) {
this.ads = ads;
}
public boolean isAds() {
return ads;
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
public String getHostname() {
return hostname;
}
}

@ -0,0 +1,54 @@
package com.jotuntech.sketcher.client;
import java.awt.HeadlessException;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import com.jotuntech.sketcher.common.Log;
public class ClientApplet extends JApplet {
private Client client;
String login, password, hostname;
int port;
boolean ads;
public ClientApplet() throws HeadlessException {
super();
}
public void init() {
login = getParameter("nick");
password = getParameter("secret");
hostname = getParameter("server");
port = Integer.valueOf(getParameter("port"));
ads = "1".equals(getParameter("ads"));
Log.info("Sketcher applet is initializing.");
}
public void start() {
System.gc();
Log.info("Sketcher applet is starting.");
if(Runtime.getRuntime().totalMemory() < 92274688) {
JOptionPane.showMessageDialog(this, "There is too little Java heap memory space for Sketcher to function.\nYour platform likely requires manual configuration. Please pass the\nfollowing run-time parameters to your Java Virtual Machine:\n\n -ms96M -mx384M\n\nContact the website administrator for further assistance.");
}
client = new Client(hostname, port, login, password, ads);
getContentPane().removeAll();
getContentPane().add(client.getUserInterface());
client.start();
}
public void stop() {
Log.info("Sketcher applet is stopping.");
client.close("Exit");
System.gc();
}
public void destroy() {
Log.info("Sketcher applet is being destroyed.");
getContentPane().removeAll();
client = null;
}
}

@ -0,0 +1,8 @@
package com.jotuntech.sketcher.client;
import com.jotuntech.sketcher.common.Streamable;
import com.jotuntech.sketcher.common.User;
public interface Command extends Streamable {
public int perform(Client client, User user);
}

@ -0,0 +1,24 @@
package com.jotuntech.sketcher.client;
public class CommandEntry {
private Integer sourceKey;
private Command command;
public CommandEntry(Integer sourceKey, Command command) {
if(sourceKey == null) {
throw new NullPointerException("Source key can't be null.");
} else if(command == null) {
throw new NullPointerException("Command can't be null.");
}
this.sourceKey = sourceKey;
this.command = command;
}
public Integer getSourceKey() {
return sourceKey;
}
public Command getCommand() {
return command;
}
}

@ -0,0 +1,113 @@
/**
*
*/
package com.jotuntech.sketcher.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ArrayBlockingQueue;
import com.jotuntech.sketcher.common.User;
/**
* Client connection
*
* @author Thor Harald Johansen
*/
public class Connection {
/** Return values for Command */
public final static int SEND_NONE = 0; /** Send to nobody */
public final static int SEND_SELF = 1; /** Send to self only */
public final static int SEND_OTHERS = 2; /** Send to others only */
public final static int SEND_ALL = 3; /** Send to self and others */
public final static long PING_INTERVAL = 10000;
/** Socket channel */
private SocketChannel channel;
/** User to which connection belongs. */
private User user;
/** Input buffer */
private ByteBuffer inputBuffer;
/** Output buffer */
private ByteBuffer outputBuffer;
/** Send queue */
private ArrayBlockingQueue<CommandEntry> sendQueue = new ArrayBlockingQueue<CommandEntry>(98304);
/** Time of death */
private long timeOfDeath;
/** Last ping */
private long lastPing;
public Connection() {
}
public Connection(SocketChannel channel) throws IOException {
/** Store socket channel. */
this.channel = channel;
/** Allocate transmission buffers */
inputBuffer = ByteBuffer.allocate(65538);
outputBuffer = ByteBuffer.allocate(65538);
/** Disable time of death */
timeOfDeath = Long.MAX_VALUE;
}
/** Set user for connection */
public void setUser(User user) {
this.user = user;
}
/** Get user for connection */
public User getUser() {
return user;
}
public void setChannel(SocketChannel channel) {
this.channel = channel;
}
public SocketChannel getChannel() {
return channel;
}
public ByteBuffer getInputBuffer() {
return inputBuffer;
}
public ByteBuffer getOutputBuffer() {
return outputBuffer;
}
public ArrayBlockingQueue<CommandEntry> getSendQueue() {
return sendQueue;
}
public void setTimeOfDeath(long timeOfDeath) {
this.timeOfDeath = timeOfDeath;
}
public boolean hasTimeOfDeath() {
return timeOfDeath != Long.MAX_VALUE;
}
public boolean isTimeOfDeath() {
return timeOfDeath != Long.MAX_VALUE && System.currentTimeMillis() >= timeOfDeath;
}
public void setLastPing(long lastPing) {
this.lastPing = lastPing;
}
public long getLastPing() {
return lastPing;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,154 @@
package com.jotuntech.sketcher.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
public class JCollapsiblePanel extends JPanel {
private final static Icon expandedIcon = UIManager.getIcon("Tree.expandedIcon");
private final static Icon collapsedIcon = UIManager.getIcon("Tree.collapsedIcon");
private final static Icon pinNormalIcon = new ImageIcon(JCollapsiblePanel.class.getResource("images/pin_normal.png"));
private final static Icon pinStuckIcon = new ImageIcon(JCollapsiblePanel.class.getResource("images/pin_stuck.png"));
private JLabel label;
private JLabel pin;
private JPanel contentPane;
private JCollapsiblePanelGroup group;
private boolean stuck;
private int id;
private boolean expandedByDefault, stuckByDefault;
public JCollapsiblePanel(int id, String title, boolean expanded, boolean stuckx) {
super();
this.id = id;
this.stuck = stuckx;
this.stuckByDefault = stuckx;
this.expandedByDefault = expanded;
setBorder(new EmptyBorder(0, 0, 1, 0));
setAlignmentX(Component.LEFT_ALIGNMENT);
setLayout(new BorderLayout());
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
add(topPanel, BorderLayout.NORTH);
label = new JLabel();
label.setIcon(expanded ? expandedIcon : collapsedIcon);
label.setText(title);
label.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
if(!stuck) {
setExpanded(!contentPane.isVisible(), true);
}
}
});
label.setAlignmentX(Component.LEFT_ALIGNMENT);
label.setBackground(new Color(172, 200, 230));
label.setOpaque(true);
topPanel.add(label, BorderLayout.CENTER);
pin = new JLabel();
pin.setIcon(stuck ? pinStuckIcon : pinNormalIcon);
pin.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
setStuck(!isStuck());
}
});
pin.setAlignmentX(Component.LEFT_ALIGNMENT);
pin.setBackground(new Color(172, 200, 230));
pin.setOpaque(true);
topPanel.add(pin, BorderLayout.EAST);
contentPane = new JPanel();
contentPane.setVisible(expanded);
contentPane.setBorder(new EmptyBorder(4, 4, 4, 4));
add(contentPane, BorderLayout.CENTER);
}
public JPanel getContentPane() {
return contentPane;
}
public void setExpanded(boolean expanded, boolean interactive) {
if(interactive && expanded && group != null) {
for(JCollapsiblePanel panel : group) {
if(panel == this || panel.isStuck()) {
continue;
}
panel.setExpanded(false, false);
}
}
contentPane.setVisible(expanded);
label.setIcon(expanded ? expandedIcon : collapsedIcon);
}
public boolean isExpanded() {
return contentPane.isVisible();
}
public void setGroup(JCollapsiblePanelGroup group) {
this.group = group;
group.add(this);
}
public void setStuck(boolean stuck) {
this.stuck = stuck;
pin.setIcon(stuck ? pinStuckIcon : pinNormalIcon);
}
public boolean isStuck() {
return stuck;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public boolean isExpandedByDefault() {
return expandedByDefault;
}
public boolean isStuckByDefault() {
return stuckByDefault;
}
}

@ -0,0 +1,7 @@
package com.jotuntech.sketcher.client;
import java.util.HashSet;
public class JCollapsiblePanelGroup extends HashSet<JCollapsiblePanel> {
}

@ -0,0 +1,74 @@
package com.jotuntech.sketcher.client;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Arrays;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class JUserEntry extends JPanel {
private JLabel nameLabel;
private JPanel infoPanel;
private JProgressBar volumeBar;
private double slowVolume;
private int[] delayLine;
public JUserEntry(String name) {
setLayout(new BorderLayout());
setOpaque(false);
nameLabel = new JLabel(name);
add(nameLabel, BorderLayout.CENTER);
infoPanel = new JPanel();
infoPanel.setOpaque(false);
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.X_AXIS));
add(infoPanel, BorderLayout.EAST);
volumeBar = new JProgressBar(0, 32768);
volumeBar.setPreferredSize(new Dimension(64, 16));
volumeBar.setOpaque(false);
volumeBar.setValue(32768);
volumeBar.setEnabled(false);
volumeBar.setToolTipText(name + " is not on voice.");
infoPanel.add(volumeBar);
}
public void setVolume(int volume) {
setVolume(volume, 0);
}
public void setVolume(int volume, int delay) {
if(volume <= 0) {
volume = 1;
}
if(delayLine == null || delayLine.length <= delay) {
delayLine = new int[delay + 1];
Arrays.fill(delayLine, 1);
}
delayLine[delay] = volume;
double newVolume = Math.log(delayLine[0]) * 32768d / Math.log(32768);
for(int i = 0; i < delayLine.length - 1; i++) {
delayLine[i] = delayLine[i + 1];
}
delayLine[delayLine.length - 1] = 1;
slowVolume += (newVolume - slowVolume) * 0.333d;
volumeBar.setValue((int) Math.round(slowVolume));
if(!volumeBar.isEnabled()) {
volumeBar.setEnabled(true);
volumeBar.setToolTipText(nameLabel.getText() + " is on voice.");
}
}
public void clearVoice() {
volumeBar.setValue(32768);
volumeBar.setEnabled(false);
volumeBar.setToolTipText(nameLabel.getText() + " is not on voice.");
}
}

@ -0,0 +1,57 @@
package com.jotuntech.sketcher.client;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.jotuntech.sketcher.common.TwoWayHashMap;
import com.jotuntech.sketcher.common.User;
public class JUserList extends JPanel {
TwoWayHashMap<Integer, User> userMap;
Map<Integer, JUserEntry> entryMap;
public JUserList(TwoWayHashMap<Integer, User> userMap) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.setBackground(Color.WHITE);
this.setOpaque(true);
entryMap = new HashMap<Integer, JUserEntry>();
this.userMap = userMap;
userMap.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
redo();
}
});
redo();
}
private void redo() {
entryMap.clear();
removeAll();
for(Map.Entry<Integer, User> e : userMap.entrySet()) {
JUserEntry ue = new JUserEntry(e.getValue().getName());
add(ue);
entryMap.put(e.getKey(), ue);
}
this.revalidate();
}
public JUserEntry getEntry(Integer key) {
return entryMap.get(key);
}
public void clearVoice() {
for(JUserEntry ue : entryMap.values()) {
ue.clearVoice();
}
}
}

@ -0,0 +1,81 @@
package com.jotuntech.sketcher.client;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
/**
* An collection of built-in Swing look and feels. This saves clients the
* trouble of manually setting the look and feel.
*
* @author Aaron Faanes
*
* @see javax.swing.LookAndFeel
*/
public enum LookAndFeel {
/**
* Represents the Nimbus look-and-feel that was added in Java 6 Update
* 10.
*/
NIMBUS() {
private volatile String name;
@Override
public String getName() {
if (this.name == null) {
for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if (info.getClassName().contains("nimbus")) {
this.name = info.getClassName();
}
}
}
return this.name;
}
},
/**
* Represents the look-and-feel that emulates the system
*
* @see UIManager#getSystemLookAndFeelClassName()
*/
SYSTEM() {
@Override
public String getName() {
return UIManager.getSystemLookAndFeelClassName();
}
};
/**
* Returns the name of the class that implements this look-and-feel. The
* returned name, if non-null, can be directly used in a
* {@link UIManager#setLookAndFeel(String)} invocation. If the returned
* value is null, then no class name could be found.
*
*
* @return the name of the class that implements this look-and-feel, or
* {@code null} if no class name could be found
*/
public abstract String getName();
/**
* Sets the global look and feel to this look and feel.
*
* @return {@code true} if setting the look-and-feel was successful,
* otherwise {@code false}
*/
public boolean activate() {
final String name = this.getName();
if (name == null) {
return false;
}
try {
UIManager.setLookAndFeel(name);
return true;
} catch (final Exception e) {
return false;
}
}
}

@ -0,0 +1,568 @@
package com.jotuntech.sketcher.client;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.DataOutput;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Map;
import com.jotuntech.sketcher.common.BitmapLayer;
import com.jotuntech.sketcher.common.BitmapTile;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.TwoWayHashMap;
public class PSDEncoder {
private RandomAccessFile raf;
private BufferedImage image;
private BitmapLayer[] layers;
private long[][] channelLengthPositions;
public PSDEncoder(RandomAccessFile raf, BufferedImage image, TwoWayHashMap<Integer, Layer> layers) throws IOException {
this.raf = raf;
this.image = image;
int layersLength = 0;
for(Layer l : layers.values()) {
if(l instanceof BitmapLayer) {
++layersLength;
}
}
this.layers = new BitmapLayer[layersLength];
int layerIndex = 0;
for(Layer l : layers.values()) {
if(l instanceof BitmapLayer) {