commit
74627c9c7a
178 changed files with 14428 additions and 0 deletions
@ -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> |
@ -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.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@ |
||||
proguard = proguard.ant.ProGuardTask |
||||
proguardconfiguration = proguard.ant.ConfigurationTask |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -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) { |
||||