From f09a779990339b0b34f9165424cb627a475abd4f Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Wed, 28 Oct 2015 19:46:27 +0300 Subject: [PATCH 01/38] i made some changes --- src/Connection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Connection.java b/src/Connection.java index e1dbd03..b584c7e 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -1,3 +1,3 @@ public class Connection { - //комментарий + //some changes made here } From 30e2948a4f1ee93f59c06cb51fe259cb667f111f Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 28 Oct 2015 19:35:25 +0200 Subject: [PATCH 02/38] Test commit --- src/Connection.java | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/Connection.java diff --git a/src/Connection.java b/src/Connection.java new file mode 100644 index 0000000..e1dbd03 --- /dev/null +++ b/src/Connection.java @@ -0,0 +1,3 @@ +public class Connection { + //комментарий +} From b0a1849340aea9faf4e5122020142507f0c9a0d9 Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Sat, 31 Oct 2015 15:25:18 +0300 Subject: [PATCH 03/38] Started something --- src/Connection.java | 77 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/Connection.java b/src/Connection.java index b584c7e..a9130a5 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -1,3 +1,78 @@ + + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; + public class Connection { - //some changes made here + private ServerSocket serverSocket; + private Socket socket; + private Connection user; + private String nick; + private String myNick; + + public Connection(Connection user) throws IOException { + serverSocket = new ServerSocket(28411); + socket = user.serverSocket.accept(); + + } + + public String getNick(){ + return nick; + } + + public String getMyNick() { + return myNick; + } + + public void sendNickHello(Connection user){ + sendMessage("ChatApp 2015 user "+ this.getMyNick()); + } + + public void sendNickBusy(Connection user){ + if (socket.isConnected()){ + sendMessage("Busy"); + } + + } + + void accept(){ + sendNickHello(nick); + } + + void reject(){ + sendNickBusy(nick); + disconnect(); + } + + public void sendMessage(String message){ + try{ + serverSocket = new ServerSocket(); + socket = serverSocket.accept(); + DataInputStream in = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + String instr; + while (true){ + out.writeUTF(message); + out.flush(); + System.out.println("sending.."); + instr= in.readUTF(); + System.out.println(instr); + } + }catch (Exception e){ + e.printStackTrace(); + } + } + + void disconnect(){ + try{ + socket.close(); + + }catch (Exception e){ + e.printStackTrace(); + } + } + + } From 96521bbcd0dda6211ee7583f4e6d5db650c6514f Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Sat, 31 Oct 2015 15:58:56 +0300 Subject: [PATCH 04/38] Started something --- src/Connection.java | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/Connection.java b/src/Connection.java index a9130a5..57932c2 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -4,51 +4,66 @@ import java.net.ServerSocket; import java.net.Socket; -public class Connection { +class Connection { private ServerSocket serverSocket; private Socket socket; - private Connection user; - private String nick; + private String userNick; private String myNick; - public Connection(Connection user) throws IOException { + private Connection(Connection user) throws IOException { serverSocket = new ServerSocket(28411); socket = user.serverSocket.accept(); - } - public String getNick(){ - return nick; + public String getUserNick(){ + return userNick; } - public String getMyNick() { + String getMyNick() { return myNick; } - public void sendNickHello(Connection user){ + void getNick(Connection newUser) { + this.userNick = newUser.getMyNick(); + } + void sendNick(Connection newUser){ + newUser.userNick=getMyNick(); + } + + void sendNickHello(String user){ sendMessage("ChatApp 2015 user "+ this.getMyNick()); } - public void sendNickBusy(Connection user){ + void sendNickBusy(String user){ if (socket.isConnected()){ sendMessage("Busy"); } } + + void setConnection(Connection newUser){ + try { + Connection user = new Connection(newUser); + getNick(newUser); + sendNick(newUser); + } catch (IOException e) { + e.printStackTrace(); + } + } + void accept(){ - sendNickHello(nick); + sendNickHello(userNick); } void reject(){ - sendNickBusy(nick); + sendNickBusy(userNick); disconnect(); } - public void sendMessage(String message){ + void sendMessage(String message){ try{ - serverSocket = new ServerSocket(); - socket = serverSocket.accept(); + Connection(newUser); DataInputStream in = new DataInputStream(socket.getInputStream()); DataOutputStream out = new DataOutputStream(socket.getOutputStream()); From a27a90db40df36b00de82a9589dd7e1cfb162905 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Sat, 31 Oct 2015 19:10:42 +0200 Subject: [PATCH 05/38] Useless commit I have to do because Git tells me to D: --- src/Connection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Connection.java b/src/Connection.java index e1dbd03..2e3188a 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -1,3 +1,3 @@ public class Connection { - //комментарий + //комментарифыв } From d3e93af10898672e25486e3354607014474a1f31 Mon Sep 17 00:00:00 2001 From: NotHappyDyadik Date: Sun, 8 Nov 2015 13:36:44 +0200 Subject: [PATCH 06/38] Create iteration1.1 --- iteration1.1 | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 iteration1.1 diff --git a/iteration1.1 b/iteration1.1 new file mode 100644 index 0000000..d98746d --- /dev/null +++ b/iteration1.1 @@ -0,0 +1,153 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/** + * + * @author User + */ +public class Wind extends javax.swing.JInternalFrame { + + /** + * Creates new form Wind + */ + public Wind() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jTextField1 = new javax.swing.JTextField(); + jButton1 = new javax.swing.JButton(); + jButton2 = new javax.swing.JButton(); + jButton3 = new javax.swing.JButton(); + jButton4 = new javax.swing.JButton(); + jTextField2 = new javax.swing.JTextField(); + jTextField3 = new javax.swing.JTextField(); + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jTextField4 = new javax.swing.JTextField(); + jTextField5 = new javax.swing.JTextField(); + + jTextField1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jTextField1ActionPerformed(evt); + } + }); + + jButton1.setText("Send"); + + jButton2.setText("Apply"); + + jButton3.setText("Connect"); + + jButton4.setText("Disconnect"); + + jTextField3.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jTextField3ActionPerformed(evt); + } + }); + + jLabel1.setText("local login"); + + jLabel2.setText("remote login"); + + jLabel3.setText("remore addr"); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jTextField5) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jTextField1) + .addGap(18, 18, 18) + .addComponent(jButton1)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 233, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 85, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton4))))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jButton4) + .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel1) + .addComponent(jLabel2) + .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jButton3) + .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jButton2) + .addComponent(jLabel3)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jTextField5, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jButton1) + .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextField1ActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_jTextField1ActionPerformed + + private void jTextField3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextField3ActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_jTextField3ActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; + private javax.swing.JButton jButton3; + private javax.swing.JButton jButton4; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JTextField jTextField1; + private javax.swing.JTextField jTextField2; + private javax.swing.JTextField jTextField3; + private javax.swing.JTextField jTextField4; + private javax.swing.JTextField jTextField5; + // End of variables declaration//GEN-END:variables +} From 7330d9a8906d4523344c8a52cb8111f6c99be833 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Thu, 12 Nov 2015 18:13:03 +0200 Subject: [PATCH 07/38] Added empty Caller class and fixed the other ones --- src/Caller.java | 3 +++ src/Command.java | 14 ++++++++------ src/Connection.java | 31 +++++++++++-------------------- src/MessageCommand.java | 12 ++++-------- src/NickCommand.java | 19 +++++++++++-------- src/Protocol.java | 8 ++++++++ 6 files changed, 45 insertions(+), 42 deletions(-) create mode 100644 src/Caller.java diff --git a/src/Caller.java b/src/Caller.java new file mode 100644 index 0000000..b175cbb --- /dev/null +++ b/src/Caller.java @@ -0,0 +1,3 @@ +public class Caller { + +} diff --git a/src/Command.java b/src/Command.java index 77b7df5..9e473be 100644 --- a/src/Command.java +++ b/src/Command.java @@ -1,19 +1,21 @@ +import java.util.Scanner; + public class Command { protected CommandType type ; - public Command(){ - } + public Command(){} public Command(CommandType type){ this.type=type; } - public static Command getCommand(String string){ - if (string.contains(Protocol.MESSAGE)) return new MessageCommand(CommandType.MESSAGE); + public static Command getCommand(Scanner in){ + String string = in.nextLine(); if (string.contains(Protocol.ACCEPTED)) return new Command(CommandType.ACCEPT); - if (string.contains(Protocol.REJECTED)) return new Command(CommandType.REJECT); - if (string.contains(Protocol.GREETING)) return new NickCommand(CommandType.NICK); if (string.contains(Protocol.DISCONNECT)) return new Command(CommandType.DISCONNECT); + if (string.contains(Protocol.REJECTED)) return new Command(CommandType.REJECT); + if (string.contains(Protocol.GREETING)) return new NickCommand(string); + if (string.contains(Protocol.MESSAGE)) return new MessageCommand(Protocol.decode(in.nextLine())); return null; } diff --git a/src/Connection.java b/src/Connection.java index 1ff2f25..3ead6ff 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -2,7 +2,7 @@ import java.net.Socket; import java.util.Scanner; -class Connection { +class Connection{ private Socket socket; private Scanner in; private DataOutputStream out; @@ -19,17 +19,17 @@ private Connection(Socket socket){ } - void sendNickHello(String nick){ + public void sendNickHello(String nick){ try { - out.writeUTF(new StringBuilder(Protocol.GREETING).append(nick).toString()); //"nick" - временно, потом заменим на непосредственно некую перемненную + out.writeUTF(new StringBuilder(Protocol.GREETING).append(nick).toString()); } catch (IOException e) { e.printStackTrace(); } } - void sendNickBusy(String nick){ + public void sendNickBusy(String nick){ try { - out.writeUTF(new StringBuilder(Protocol.GREETING).append(nick).append(" busy").toString()); //"nick" - временно, потом заменим на непосредственно некую перемненную + out.writeUTF(new StringBuilder(Protocol.GREETING).append(nick).append(" busy").toString()); } catch (IOException e) { e.printStackTrace(); } @@ -37,7 +37,7 @@ void sendNickBusy(String nick){ - void accept(){ + public void accept(){ try { out.writeUTF(Protocol.ACCEPTED); } catch (IOException e) { @@ -45,7 +45,7 @@ void accept(){ } } - void reject(){ + public void reject(){ try { out.writeUTF(Protocol.REJECTED); } catch (IOException e) { @@ -53,7 +53,7 @@ void reject(){ } } - void sendMessage(String message){ + public void sendMessage(String message){ try { out.writeUTF(new StringBuilder(Protocol.MESSAGE).append("\n").append(message).toString()); } catch (IOException e) { @@ -61,7 +61,7 @@ void sendMessage(String message){ } } - void disconnect(){ + public void disconnect(){ try{ out.writeUTF(Protocol.DISCONNECT); socket.close(); @@ -70,16 +70,7 @@ void disconnect(){ } } - Command recieve(){ - Command command; - String recievedString = in.nextLine(); - command = Command.getCommand(recievedString); - String temporary; - //если сообщение или ник, то еще читать его. - return command; + public Command recieve(){ + return Command.getCommand(in); } - - //Делалось совместно с Сергеем Александровичем через Skype - - } diff --git a/src/MessageCommand.java b/src/MessageCommand.java index 82e4f65..e33f78e 100644 --- a/src/MessageCommand.java +++ b/src/MessageCommand.java @@ -1,16 +1,12 @@ public class MessageCommand extends Command{ String message; - public MessageCommand(CommandType type){ - super(type); + public MessageCommand(){ + super(CommandType.MESSAGE); } - public MessageCommand(CommandType type, String message){ - super(type); - this.message=message; - } - - public void setMessage(String message){ + public MessageCommand(String message){ + super(CommandType.MESSAGE); this.message=message; } } diff --git a/src/NickCommand.java b/src/NickCommand.java index e317f94..1d51f97 100644 --- a/src/NickCommand.java +++ b/src/NickCommand.java @@ -1,16 +1,19 @@ public class NickCommand extends Command{ String nick; + Boolean busy; - public NickCommand(CommandType type){ - super(type); + public NickCommand(){ + super(CommandType.NICK); } - public NickCommand(CommandType type, String nick){ - super(type); - this.nick=nick; + public NickCommand(String string){ + super(CommandType.NICK); + if (string.contains(" busy")){ + busy = true; + string.replace(" busy",""); + } + string.replace(Protocol.GREETING,""); + nick = string; } - public void setNick(String nick){ - this.nick=nick; - } } diff --git a/src/Protocol.java b/src/Protocol.java index 68885a6..d7e9227 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -5,4 +5,12 @@ public class Protocol { public static final String REJECTED = "Rejected"; public static final String MESSAGE = "Message"; public static final String DISCONNECT = "Disconnect"; + + public static String encode(String string){ + return string.replace("\n",":&:"); + } + + public static String decode(String string){ + return string.replace(":&:","\n"); + } } From a84b1d4ecc9e1d1d9d084eacb411e6726f9d1d74 Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Sun, 15 Nov 2015 15:30:48 +0300 Subject: [PATCH 08/38] =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=20=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D1=82=D1=8C=20Caller=20+=20CallListener,=20?= =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20-=20=D0=BF=D1=80=D0=B0=D0=B2=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CallListener.java | 32 ++++++++++++++++++++++++++++++++ src/Caller.java | 18 ++++++++++++++++++ src/Connection.java | 2 +- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/CallListener.java diff --git a/src/CallListener.java b/src/CallListener.java new file mode 100644 index 0000000..181806d --- /dev/null +++ b/src/CallListener.java @@ -0,0 +1,32 @@ +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +/** + * Created by bresya on 15.11.2015. + */ +public class CallListener { + private String remoteNick; + private boolean isBusy; + private InetAddress remoteIP; + private int remotePort; + + public CallListener(){ + //посмотрим, что тут + } + + public Connection getConnection() throws IOException { + Connection connection = new Connection(new Socket(remoteIP,remotePort)); + Command command = connection.recieve(); + if (!isBusy){ + if (command.equals(new Command(CommandType.NICK))){ + connection.sendNickHello(remoteNick); + return connection; + } + else return null; + } + else connection.sendNickBusy(remoteNick); + return null; + + } +} diff --git a/src/Caller.java b/src/Caller.java index b175cbb..2260f40 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -1,3 +1,21 @@ +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + public class Caller { + private String localNick; + private InetAddress remoteIP; + private int remotePort; + private String remoteNick; + private Command lastCommand; + public Connection call() throws IOException { + CallListener callListener = new CallListener(); + Connection connection = callListener.getConnection(); + lastCommand = connection.recieve(); + if (lastCommand.equals(new Command(CommandType.ACCEPT))){ + return connection; + } + else return null; + } } diff --git a/src/Connection.java b/src/Connection.java index 3ead6ff..44088e5 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -7,7 +7,7 @@ class Connection{ private Scanner in; private DataOutputStream out; - private Connection(Socket socket){ + public Connection(Socket socket){ this.socket=socket; try { in = new Scanner(socket.getInputStream()); From df2762423dec8b55abf6d42f54b1492f624601ab Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Sun, 15 Nov 2015 16:33:00 +0300 Subject: [PATCH 09/38] =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=20=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D1=82=D1=8C=20Caller=20+=20CallListener,=20?= =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20-=20=D0=BF=D1=80=D0=B0=D0=B2=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Caller.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Caller.java b/src/Caller.java index 2260f40..9e1ac7f 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -10,11 +10,14 @@ public class Caller { private Command lastCommand; public Connection call() throws IOException { - CallListener callListener = new CallListener(); - Connection connection = callListener.getConnection(); + Connection connection = new Connection(new Socket(remoteIP,remotePort)); + connection.sendNickHello(localNick); lastCommand = connection.recieve(); - if (lastCommand.equals(new Command(CommandType.ACCEPT))){ - return connection; + if (lastCommand.equals(new Command(CommandType.NICK))){ + if (lastCommand.equals(new Command(CommandType.ACCEPT))){ + return connection; + } + else return null; } else return null; } From d4224234f26394daad54b915b986567ce965e173 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 18 Nov 2015 13:26:56 +0200 Subject: [PATCH 10/38] I don't know what is this D: --- src/CallListener.java | 43 +++++++++++++++++++++---------- src/CallListenerThread.java | 13 ++++++++++ src/Caller.java | 47 +++++++++++++++++++++++++++------- src/CommandListenerThread.java | 38 +++++++++++++++++++++++++++ src/CommandObserver.java | 5 ++++ src/ConnectionObserver.java | 3 +++ src/NickCommand.java | 7 +++++ 7 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 src/CallListenerThread.java create mode 100644 src/CommandListenerThread.java create mode 100644 src/CommandObserver.java create mode 100644 src/ConnectionObserver.java diff --git a/src/CallListener.java b/src/CallListener.java index 181806d..e9b2732 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -1,32 +1,47 @@ import java.io.IOException; import java.net.InetAddress; +import java.net.ServerSocket; import java.net.Socket; -/** - * Created by bresya on 15.11.2015. - */ public class CallListener { - private String remoteNick; + private String localNick,remoteNick; private boolean isBusy; - private InetAddress remoteIP; + private String remoteIP; private int remotePort; + private ServerSocket serverSocket; + private Command lastCommand; + private NickCommand nickCommand; - public CallListener(){ - //посмотрим, что тут + public CallListener(String localNick, boolean isBusy){ + try { + serverSocket = new ServerSocket(Protocol.PORT_NUMBER); + } catch (IOException e) { + //asd + } + this.localNick=localNick; + this.isBusy=isBusy; } public Connection getConnection() throws IOException { - Connection connection = new Connection(new Socket(remoteIP,remotePort)); - Command command = connection.recieve(); + Connection connection = new Connection(serverSocket.accept()); if (!isBusy){ - if (command.equals(new Command(CommandType.NICK))){ - connection.sendNickHello(remoteNick); + connection.sendNickHello(localNick); + lastCommand=connection.recieve(); + if (lastCommand.type==CommandType.NICK){ + nickCommand = (NickCommand) lastCommand; + remoteNick = nickCommand.getNick(); return connection; } - else return null; + else{ + return null; + } } - else connection.sendNickBusy(remoteNick); - return null; + else{ + connection.sendNickBusy(localNick); + connection.disconnect(); + return null; + } + } } diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java new file mode 100644 index 0000000..b24a5ad --- /dev/null +++ b/src/CallListenerThread.java @@ -0,0 +1,13 @@ + +public class CallListenerThread implements Runnable { + + private String localNick; + private boolean isBusy; + private String remoteIP; + private String lastAction; + + + public void run() { + + } +} diff --git a/src/Caller.java b/src/Caller.java index 9e1ac7f..c78df11 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -4,21 +4,50 @@ public class Caller { private String localNick; - private InetAddress remoteIP; - private int remotePort; + private String remoteIP; private String remoteNick; private Command lastCommand; + private NickCommand nickCommand; + private String lastError; + + public Caller(String localNick,String remoteIP){ + this.localNick=localNick; + this.remoteIP=remoteIP; + } public Connection call() throws IOException { - Connection connection = new Connection(new Socket(remoteIP,remotePort)); + Connection connection = new Connection(new Socket(remoteIP,Protocol.PORT_NUMBER)); + + //Проверка, к тому ли мы подключились. + lastCommand = connection.recieve(); + if (lastCommand.type==CommandType.NICK) { + nickCommand = (NickCommand) lastCommand; + } + else{ + connection.disconnect(); + lastError="Wrong IP"; + return null; + } + + //Проверка, занят ли тот пользователь + remoteNick = nickCommand.getNick(); + if (nickCommand.isBusy()){ + lastCommand = connection.recieve(); + connection.disconnect(); + lastError="User is busy"; + return null; + } + + //Если не занят, то отсылаем ник и ждём подтверждения connection.sendNickHello(localNick); lastCommand = connection.recieve(); - if (lastCommand.equals(new Command(CommandType.NICK))){ - if (lastCommand.equals(new Command(CommandType.ACCEPT))){ - return connection; - } - else return null; + + //Если Accept - вернуть connection + if (lastCommand.type==CommandType.ACCEPT) return connection; + else{ + lastError="User rejected your call"; + return null; } - else return null; + } } diff --git a/src/CommandListenerThread.java b/src/CommandListenerThread.java new file mode 100644 index 0000000..4817fc9 --- /dev/null +++ b/src/CommandListenerThread.java @@ -0,0 +1,38 @@ +import javafx.beans.InvalidationListener; + +import java.util.HashMap; +import java.util.Observable; +import java.util.List; + +public class CommandListenerThread implements Runnable { + private Command lastCommand; + private Connection connection; + private HashMap observers; + private boolean stop; + + public CommandListenerThread(Connection connection){ + this.connection=connection; + } + + public void run() { + while (!stop) { + lastCommand = connection.recieve(); + observers.get(lastCommand.type).update(lastCommand); + + } + } + + + public void addCommandObserver(CommandObserver commandObserver){ + observers.put(commandObserver.getType(),commandObserver); + } + + + public void removeCommandObserver(CommandObserver commandObserver){ + observers.remove(commandObserver.getType()); + } + + public void kill(){ + stop = true; + } +} diff --git a/src/CommandObserver.java b/src/CommandObserver.java new file mode 100644 index 0000000..efd28d5 --- /dev/null +++ b/src/CommandObserver.java @@ -0,0 +1,5 @@ +public interface CommandObserver{ + + void update(Command command); + CommandType getType(); +} diff --git a/src/ConnectionObserver.java b/src/ConnectionObserver.java new file mode 100644 index 0000000..c6cc5ce --- /dev/null +++ b/src/ConnectionObserver.java @@ -0,0 +1,3 @@ +public interface ConnectionObserver { + +} diff --git a/src/NickCommand.java b/src/NickCommand.java index 1d51f97..ed6de78 100644 --- a/src/NickCommand.java +++ b/src/NickCommand.java @@ -16,4 +16,11 @@ public NickCommand(String string){ nick = string; } + public boolean isBusy(){ + return busy; + } + + public String getNick() { + return nick; + } } From adec4cf18039d1e735ebb8008e465450239a04bd Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 18 Nov 2015 15:28:31 +0200 Subject: [PATCH 11/38] TOO LATE, U KNOW? --- src/CallListener.java | 4 +- src/CallListenerThread.java | 2 +- src/CommandListenerThread.java | 6 +-- src/CommandObserver.java | 13 ++++-- src/DisconnectCommandObserver.java | 17 ++++++++ src/Logic.java | 64 ++++++++++++++++++++++++++++++ src/MessageCommandObserver.java | 21 ++++++++++ 7 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 src/DisconnectCommandObserver.java create mode 100644 src/Logic.java create mode 100644 src/MessageCommandObserver.java diff --git a/src/CallListener.java b/src/CallListener.java index e9b2732..dec100e 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -41,7 +41,9 @@ public Connection getConnection() throws IOException { connection.disconnect(); return null; } + } - + public void setBusy(boolean isBusy) { + this.isBusy = isBusy; } } diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index b24a5ad..aef1eea 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -4,7 +4,7 @@ public class CallListenerThread implements Runnable { private String localNick; private boolean isBusy; private String remoteIP; - private String lastAction; + private String lastAction; public void run() { diff --git a/src/CommandListenerThread.java b/src/CommandListenerThread.java index 4817fc9..9065cdd 100644 --- a/src/CommandListenerThread.java +++ b/src/CommandListenerThread.java @@ -1,8 +1,5 @@ -import javafx.beans.InvalidationListener; - import java.util.HashMap; -import java.util.Observable; -import java.util.List; + public class CommandListenerThread implements Runnable { private Command lastCommand; @@ -18,7 +15,6 @@ public void run() { while (!stop) { lastCommand = connection.recieve(); observers.get(lastCommand.type).update(lastCommand); - } } diff --git a/src/CommandObserver.java b/src/CommandObserver.java index efd28d5..e0136cb 100644 --- a/src/CommandObserver.java +++ b/src/CommandObserver.java @@ -1,5 +1,12 @@ -public interface CommandObserver{ +public abstract class CommandObserver{ - void update(Command command); - CommandType getType(); + protected CommandListenerThread clt; + + public void update(Command command) { + + } + + CommandType getType() { + return null; + } } diff --git a/src/DisconnectCommandObserver.java b/src/DisconnectCommandObserver.java new file mode 100644 index 0000000..f917712 --- /dev/null +++ b/src/DisconnectCommandObserver.java @@ -0,0 +1,17 @@ +public class DisconnectCommandObserver extends CommandObserver { + private CommandType type = CommandType.DISCONNECT; + Connection connection; + + public DisconnectCommandObserver(CommandListenerThread clt, Connection connection){ + this.clt=clt; + this.connection=connection; + } + + public void update(Command command) { + //херня с окном "User disconnected"; + } + + public CommandType getType() { + return type; + } +} diff --git a/src/Logic.java b/src/Logic.java new file mode 100644 index 0000000..3c32d0c --- /dev/null +++ b/src/Logic.java @@ -0,0 +1,64 @@ +import java.io.IOException; + +public class Logic{ + //ссылка на GUI + private String localNick,remoteNick,remoteIP; + private boolean isBusy; + private Connection connection; + private CallListener callListener; + private Caller caller; + + public Logic(){ + //старт CallListenerThread + //старт GUI + // + } + + public void setLocalNick(){ + /* localNick = GUI.getLocalNick() */; + } + + public void setRemoteNick(String nick){ + remoteNick = nick; + } + + public void setRemoteIP(){ + // remoteIP = GUI.getRemoteIP(); + } + + public void setBusy(boolean isBusy) { + this.isBusy = isBusy; + callListener.setBusy(isBusy); + } + + public void call(){ + setRemoteIP(); + //блокировка кнопок Apply, Connect + caller = new Caller(localNick,remoteIP); + try { + connection = caller.call(); + } catch (IOException e) { + //не удалось дозвониться по какой-то причине + } + if (connection!=null){ + CommandListenerThread commandListenerThread = new CommandListenerThread(connection); + Thread comThread = new Thread(commandListenerThread); + comThread.start(); + setBusy(true); + } + } + + public void disconnect(){ + setBusy(false); + //разблокировка всякого + // + remoteNick=null; + remoteIP=null; + connection.disconnect(); + } + + + + + +} diff --git a/src/MessageCommandObserver.java b/src/MessageCommandObserver.java new file mode 100644 index 0000000..6946d02 --- /dev/null +++ b/src/MessageCommandObserver.java @@ -0,0 +1,21 @@ +public class MessageCommandObserver extends CommandObserver { + private CommandType type=CommandType.MESSAGE; + private MessageCommand messageCommand; + + //ссылка на форму с сообщениями или на её логическую часть + + public MessageCommandObserver(CommandListenerThread clt /*,непосредственно ссылка на ту форму*/){ + //задача формы + this.clt=clt; + clt.addCommandObserver(this); + } + + public void update(Command command) { + messageCommand = (MessageCommand) command; + //отправка на форму или лог. часть сообщения через ссылку + } + + public CommandType getType() { + return type; + } +} From af7ad1b7f0dd55c6eb08d8aba0881b84eff40ca3 Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Wed, 18 Nov 2015 16:45:37 +0300 Subject: [PATCH 12/38] I DON'T KNOW --- src/CallListener.java | 4 +++ src/CallListenerThread.java | 49 ++++++++++++++++++++++++++++++++++++- src/ConnectionObserver.java | 6 ++++- src/Logic.java | 23 ++++++++++++++--- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/CallListener.java b/src/CallListener.java index dec100e..a6f2c13 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -46,4 +46,8 @@ public Connection getConnection() throws IOException { public void setBusy(boolean isBusy) { this.isBusy = isBusy; } + + public boolean getBusy(){ + return isBusy; + } } diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index aef1eea..5edb851 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -1,13 +1,60 @@ +import java.io.IOException; public class CallListenerThread implements Runnable { private String localNick; - private boolean isBusy; + private CallListener callListener; + private boolean isBusy = callListener.getBusy();//тонко, типа я не знаю, зачем он здесь private String remoteIP; private String lastAction; + private boolean stop; + private Connection remoteConnection; + private boolean noConnection; + private CommandType buttonPressed; + public CallListenerThread(){ + callListener = new CallListener(localNick, false); + } + public void run() { + try { + while (!stop) { + + + while (noConnection = true) {//я знаю, что может подключиться второй, но давай хотя бы пока так + remoteConnection = callListener.getConnection(); + noConnection = false; + } + + //генерация кнопок АССЕРТ и РИЖЕКТ + if (buttonPressed.equals(CommandType.ACCEPT)) + { + remoteConnection.accept(); + callListener.setBusy(true); + }else if (buttonPressed.equals(CommandType.REJECT)) + { + remoteConnection.reject(); + remoteConnection.disconnect(); + } + + + + + } + } + catch(IOException e){ + e.printStackTrace(); + } + + + } + public void setButtonPressed(CommandType buttonPressed) { + this.buttonPressed = buttonPressed; + } + + public void kill() { + stop = true; } } diff --git a/src/ConnectionObserver.java b/src/ConnectionObserver.java index c6cc5ce..433868a 100644 --- a/src/ConnectionObserver.java +++ b/src/ConnectionObserver.java @@ -1,3 +1,7 @@ -public interface ConnectionObserver { +public abstract class ConnectionObserver { + + public void update(Connection connection){ + + } } diff --git a/src/Logic.java b/src/Logic.java index 3c32d0c..fb8617b 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -7,15 +7,18 @@ public class Logic{ private Connection connection; private CallListener callListener; private Caller caller; + CallListenerThread callListenerThread = new CallListenerThread(); + Thread callThread = new Thread(callListenerThread); public Logic(){ - //старт CallListenerThread - //старт GUI + + callThread.start(); + //GUI gui = new GUI(); // } public void setLocalNick(){ - /* localNick = GUI.getLocalNick() */; + /* localNick = GUI.getLocalNick() */ } public void setRemoteNick(String nick){ @@ -31,6 +34,16 @@ public void setBusy(boolean isBusy) { callListener.setBusy(isBusy); } + public void accept(){ + connection.accept(); + callListenerThread.setButtonPressed(CommandType.ACCEPT); + } + + public void reject(){ + connection.reject(); + callListenerThread.setButtonPressed(CommandType.REJECT); + } + public void call(){ setRemoteIP(); //блокировка кнопок Apply, Connect @@ -57,6 +70,10 @@ public void disconnect(){ connection.disconnect(); } + public void updateGUI(){ + // + } + From b14228b45de5e6b3d858d71ce9aaff6cc1b6eb3d Mon Sep 17 00:00:00 2001 From: Artem Dyadik Date: Wed, 18 Nov 2015 20:05:25 +0200 Subject: [PATCH 13/38] MainGui --- MainGui | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 MainGui diff --git a/MainGui b/MainGui new file mode 100644 index 0000000..63ed9ed --- /dev/null +++ b/MainGui @@ -0,0 +1,24 @@ +import javax.swing.*; +import java.awt.*; +public class MainGui { + + private static final int Height = 600; + private static final int Widht = 800; + + public static void main(String[] args) { + + LFrame frame = new LFrame(); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(Widht, Height); + frame.setResizable(false); + frame.setAlwaysOnTop(true); + frame.setLocationRelativeTo(null); + frame.setTitle("CHATAPP"); + frame.setVisible(true); + + + + } + +} From 5e938a65c6b6526aa7b8c0f0b8068eb9378f504e Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 18 Nov 2015 20:06:03 +0200 Subject: [PATCH 14/38] wip --- src/CallListenerThread.java | 38 +++++++++++------------------- src/Connection.java | 12 +++++----- src/DisconnectCommandObserver.java | 3 ++- src/Logic.java | 22 ++++++++--------- src/NickCommand.java | 6 ++--- 5 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index 5edb851..a4d537c 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -1,19 +1,18 @@ import java.io.IOException; +import java.sql.*; public class CallListenerThread implements Runnable { private String localNick; private CallListener callListener; - private boolean isBusy = callListener.getBusy();//тонко, типа я не знаю, зачем он здесь + private boolean isBusy; private String remoteIP; private String lastAction; private boolean stop; private Connection remoteConnection; - private boolean noConnection; - private CommandType buttonPressed; - public CallListenerThread(){ + public CallListenerThread(String localNick,Boolean isBusy){ callListener = new CallListener(localNick, false); } @@ -21,25 +20,9 @@ public void run() { try { while (!stop) { - - while (noConnection = true) {//я знаю, что может подключиться второй, но давай хотя бы пока так - remoteConnection = callListener.getConnection(); - noConnection = false; - } - + remoteConnection = callListener.getConnection(); + if (remoteConnection == null) continue; //генерация кнопок АССЕРТ и РИЖЕКТ - if (buttonPressed.equals(CommandType.ACCEPT)) - { - remoteConnection.accept(); - callListener.setBusy(true); - }else if (buttonPressed.equals(CommandType.REJECT)) - { - remoteConnection.reject(); - remoteConnection.disconnect(); - } - - - } } @@ -49,8 +32,15 @@ public void run() { } - public void setButtonPressed(CommandType buttonPressed) { - this.buttonPressed = buttonPressed; + + + public Connection getRemoteConnection(){ + return remoteConnection; + } + + public void setBusy(Boolean isBusy){ + this.isBusy=isBusy; + callListener.setBusy(isBusy); } diff --git a/src/Connection.java b/src/Connection.java index 44088e5..3a0ed23 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -21,7 +21,7 @@ public Connection(Socket socket){ public void sendNickHello(String nick){ try { - out.writeUTF(new StringBuilder(Protocol.GREETING).append(nick).toString()); + out.write(new StringBuilder(Protocol.GREETING).append(nick).append("\n").toString().getBytes("UTF-8")); } catch (IOException e) { e.printStackTrace(); } @@ -29,7 +29,7 @@ public void sendNickHello(String nick){ public void sendNickBusy(String nick){ try { - out.writeUTF(new StringBuilder(Protocol.GREETING).append(nick).append(" busy").toString()); + out.write(new StringBuilder(Protocol.GREETING).append(nick).append(" busy").append("\n").toString().getBytes("UTF-8")); } catch (IOException e) { e.printStackTrace(); } @@ -39,7 +39,7 @@ public void sendNickBusy(String nick){ public void accept(){ try { - out.writeUTF(Protocol.ACCEPTED); + out.write(new StringBuilder(Protocol.ACCEPTED).append("\n").toString().getBytes("UTF-8")); } catch (IOException e) { e.printStackTrace(); } @@ -47,7 +47,7 @@ public void accept(){ public void reject(){ try { - out.writeUTF(Protocol.REJECTED); + out.write(new StringBuilder(Protocol.REJECTED).append("\n").toString().getBytes("UTF-8")); } catch (IOException e) { e.printStackTrace(); } @@ -55,7 +55,7 @@ public void reject(){ public void sendMessage(String message){ try { - out.writeUTF(new StringBuilder(Protocol.MESSAGE).append("\n").append(message).toString()); + out.write(new StringBuilder(Protocol.MESSAGE).append("\n").append(message).append("\n").toString().getBytes("UTF-8")); } catch (IOException e) { e.printStackTrace(); } @@ -63,7 +63,7 @@ public void sendMessage(String message){ public void disconnect(){ try{ - out.writeUTF(Protocol.DISCONNECT); + out.write(new StringBuilder(Protocol.DISCONNECT).append("\n").toString().getBytes("UTF-8")); socket.close(); }catch (Exception e){ e.printStackTrace(); diff --git a/src/DisconnectCommandObserver.java b/src/DisconnectCommandObserver.java index f917712..40268fa 100644 --- a/src/DisconnectCommandObserver.java +++ b/src/DisconnectCommandObserver.java @@ -8,7 +8,8 @@ public DisconnectCommandObserver(CommandListenerThread clt, Connection connectio } public void update(Command command) { - //херня с окном "User disconnected"; + //вывод в чат "remoteNick disconnected; + // } public CommandType getType() { diff --git a/src/Logic.java b/src/Logic.java index fb8617b..ea9eec0 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -2,19 +2,18 @@ public class Logic{ //ссылка на GUI - private String localNick,remoteNick,remoteIP; + private String localNick = "default",remoteNick,remoteIP; private boolean isBusy; private Connection connection; - private CallListener callListener; private Caller caller; - CallListenerThread callListenerThread = new CallListenerThread(); - Thread callThread = new Thread(callListenerThread); - - public Logic(){ + private CallListenerThread callListenerThread; + private Thread callThread; + public Logic(/*ссылка на GUI*/){ + callListenerThread = new CallListenerThread(localNick,isBusy); + callThread = new Thread(callListenerThread); callThread.start(); - //GUI gui = new GUI(); - // + //this.GUI = GUI; } public void setLocalNick(){ @@ -31,17 +30,18 @@ public void setRemoteIP(){ public void setBusy(boolean isBusy) { this.isBusy = isBusy; - callListener.setBusy(isBusy); + callListenerThread.setBusy(isBusy); + } public void accept(){ connection.accept(); - callListenerThread.setButtonPressed(CommandType.ACCEPT); + // callListenerThread.setButtonPressed(CommandType.ACCEPT); } public void reject(){ connection.reject(); - callListenerThread.setButtonPressed(CommandType.REJECT); + // callListenerThread.setButtonPressed(CommandType.REJECT); } public void call(){ diff --git a/src/NickCommand.java b/src/NickCommand.java index ed6de78..0014e09 100644 --- a/src/NickCommand.java +++ b/src/NickCommand.java @@ -1,6 +1,6 @@ public class NickCommand extends Command{ String nick; - Boolean busy; + Boolean busy=false; public NickCommand(){ super(CommandType.NICK); @@ -10,9 +10,9 @@ public NickCommand(String string){ super(CommandType.NICK); if (string.contains(" busy")){ busy = true; - string.replace(" busy",""); + string=string.replace(" busy",""); } - string.replace(Protocol.GREETING,""); + string = string.replace(Protocol.GREETING,""); nick = string; } From 8e097398d8cd8182d0b0d794dad0c35042178b08 Mon Sep 17 00:00:00 2001 From: Artem Dyadik Date: Wed, 18 Nov 2015 20:06:07 +0200 Subject: [PATCH 15/38] LFrame --- LFrame | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 LFrame diff --git a/LFrame b/LFrame new file mode 100644 index 0000000..dc891e8 --- /dev/null +++ b/LFrame @@ -0,0 +1,136 @@ +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemListener; + +import java.util.Vector; + +import javax.swing.AbstractButton; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.LineBorder; +import javax.swing.border.SoftBevelBorder; + + +public class LFrame extends JFrame { + JPanel panel = new JPanel(); + + JButton Send; + JButton Apply; + JButton Disconnect; + JButton Connect; + + + JLabel login; + JLabel addr; + JLabel smth; + + JTextField textfieldlogin; + JTextField EnterIp; + + String name = ""; + + String name1; + String text; + + int adress; + + Font font = new Font("Verdana", Font.BOLD, 13); + + + + LineBorder linebord = new LineBorder(Color.BLACK, 1); + + LFrame(){ + + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setBackground(Color.white); + + login = new JLabel("login:"); + login.setFont(font); + login.setBounds(35, 40, 60, 30); + textfieldlogin = new JTextField(); + textfieldlogin.setBounds(80, 40, 115, 30); + textfieldlogin.setBorder(linebord); + + addr = new JLabel("remote addr"); + addr.setFont(font); + addr.setBounds(400,35,100,50); + EnterIp = new JTextField(); + EnterIp.setBounds(500,40,150,30); + EnterIp.setBorder(linebord); + + JTextField mass = new JTextField(); + mass.setHorizontalAlignment(JTextField.LEFT); + mass.setBorder(linebord); + JTextArea ForMass = new JTextArea(); + ForMass.setEditable(false); + ForMass.setLineWrap(true); + ForMass.setWrapStyleWord(true); + mass.setBounds(10, 480, 650, 75); + + smth = new JLabel(); + smth.setLocation(10, 140); + smth.setSize(740,300); + smth.setBorder(linebord); + + Apply = new JButton("Apply"); + Apply.setLocation(80, 80); + Apply.setSize(75,30); + Apply.setFont(font); + + Disconnect = new JButton("Disconnect"); + Disconnect.setLocation(600,80); + Disconnect.setSize(130,30); + Disconnect.setFont(font); + + Connect = new JButton("Connect"); + Connect.setLocation(450,80); + Connect.setSize(100, 30); + Connect.setFont(font); + Send = new JButton("Send"); + Send.setLocation(680, 530); + Send.setSize(100,30); + Send.addActionListener(new ActionListener( ) { + public void actionPerformed(ActionEvent ae) { + int h=0; + name1 = textfieldlogin.getText(); + + if(name.equals(name1)){ + text = mass.getText(); + smth.setText(name + text); + + } + + } + }); + + + panel.add(login); + panel.add(textfieldlogin); + panel.add(mass); + panel.add(smth); + panel.add(EnterIp); + panel.add(addr); + panel.add(Send); + panel.add(Apply); + panel.add(Disconnect); + panel.add(Connect); + + this.add(panel); + + + } + +} From 28cb790438ad6c3e86bd29bcce88e4df3415b644 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 18 Nov 2015 23:51:27 +0200 Subject: [PATCH 16/38] Kostiliiiii --- MainGui | 24 ---------- src/AccorDis.java | 83 ++++++++++++++++++++++++++++++++++ src/CallListener.java | 4 ++ src/CallListenerThread.java | 26 +++++++---- src/Caller.java | 3 ++ src/CommandListenerThread.java | 13 +++++- src/Connection.java | 13 ++++++ src/HistoryView.java | 19 ++++++++ src/HistoryViewModel.java | 56 +++++++++++++++++++++++ LFrame => src/LFrame.java | 40 +++++++++++----- src/Logic.java | 68 ++++++++++++++++++++++------ src/MainGui.java | 22 +++++++++ 12 files changed, 311 insertions(+), 60 deletions(-) delete mode 100644 MainGui create mode 100644 src/AccorDis.java create mode 100644 src/HistoryView.java create mode 100644 src/HistoryViewModel.java rename LFrame => src/LFrame.java (76%) create mode 100644 src/MainGui.java diff --git a/MainGui b/MainGui deleted file mode 100644 index 63ed9ed..0000000 --- a/MainGui +++ /dev/null @@ -1,24 +0,0 @@ -import javax.swing.*; -import java.awt.*; -public class MainGui { - - private static final int Height = 600; - private static final int Widht = 800; - - public static void main(String[] args) { - - LFrame frame = new LFrame(); - - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(Widht, Height); - frame.setResizable(false); - frame.setAlwaysOnTop(true); - frame.setLocationRelativeTo(null); - frame.setTitle("CHATAPP"); - frame.setVisible(true); - - - - } - -} diff --git a/src/AccorDis.java b/src/AccorDis.java new file mode 100644 index 0000000..c8c5eeb --- /dev/null +++ b/src/AccorDis.java @@ -0,0 +1,83 @@ + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextArea; +import javax.swing.border.LineBorder; +import javax.swing.border.SoftBevelBorder; + + +public class AccorDis extends JFrame { + + private static final int Height = 150; + private static final int Widht = 300; + private CallListenerThread callListenerThread; + + JPanel panel = new JPanel(); + + JButton Accept; + JButton Dismiss; + JLabel NickName; + + public void close(){ + dispose(); + } + + String username = "Artem"; + + AccorDis(final Connection connection){ + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(Widht, Height); + setResizable(false); + setAlwaysOnTop(true); + setLocationRelativeTo(null); + setTitle("hop-hey-la-lay"); + + + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setBackground(Color.pink); + + NickName = new JLabel("User wants " +username+ " to speak"); + NickName.setLocation(60, 10); + NickName.setSize(200,54); + + Accept = new JButton("Accept"); + Accept.setLocation(18, 80); + Accept.setSize(130,30); + Accept.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + connection.accept(); + close(); + } + }); + + Dismiss = new JButton("Dismiss"); + Dismiss.setLocation(150,80); + Dismiss.setSize(130,30); + Dismiss.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + connection.reject(); + close(); + } + }); + + panel.add(Accept); + panel.add(Dismiss); + panel.add(NickName); + + + + this.add(panel); + setVisible(true); + } + +} \ No newline at end of file diff --git a/src/CallListener.java b/src/CallListener.java index a6f2c13..90b6ae3 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -50,4 +50,8 @@ public void setBusy(boolean isBusy) { public boolean getBusy(){ return isBusy; } + + public String getRemoteNick(){ + return remoteNick; + } } diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index a4d537c..c1dbdd9 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -3,17 +3,22 @@ public class CallListenerThread implements Runnable { - private String localNick; - private CallListener callListener; - private boolean isBusy; - private String remoteIP; - private String lastAction; - private boolean stop; - private Connection remoteConnection; + private String localNick; + private CallListener callListener; + private boolean isBusy; + private String remoteIP; + private String lastAction; + private boolean stop; + private String remoteNick; + private Connection remoteConnection; + private AccorDis form; + Logic logic; - public CallListenerThread(String localNick,Boolean isBusy){ + + public CallListenerThread(String localNick,Boolean isBusy,Logic logic){ callListener = new CallListener(localNick, false); + this.logic = logic; } public void run() { @@ -22,7 +27,10 @@ public void run() { remoteConnection = callListener.getConnection(); if (remoteConnection == null) continue; - //генерация кнопок АССЕРТ и РИЖЕКТ + remoteNick=callListener.getRemoteNick(); + form = new AccorDis(remoteConnection); + if (remoteConnection.getLastCommand()==CommandType.ACCEPT) logic.accept(remoteConnection); + else continue; } } diff --git a/src/Caller.java b/src/Caller.java index c78df11..1efefea 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -50,4 +50,7 @@ public Connection call() throws IOException { } } + public String getRemoteNick(){ + return remoteNick; + } } diff --git a/src/CommandListenerThread.java b/src/CommandListenerThread.java index 9065cdd..1a75480 100644 --- a/src/CommandListenerThread.java +++ b/src/CommandListenerThread.java @@ -6,15 +6,24 @@ public class CommandListenerThread implements Runnable { private Connection connection; private HashMap observers; private boolean stop; + Logic logic; - public CommandListenerThread(Connection connection){ + public CommandListenerThread(Connection connection, Logic logic){ this.connection=connection; + this.logic=logic; } public void run() { while (!stop) { lastCommand = connection.recieve(); - observers.get(lastCommand.type).update(lastCommand); + if (lastCommand.type==CommandType.MESSAGE){ + MessageCommand mc = (MessageCommand) lastCommand; + logic.addMessage(mc.message); + } + + if (lastCommand.type==CommandType.DISCONNECT){ + logic.disconnect(); + } } } diff --git a/src/Connection.java b/src/Connection.java index 3a0ed23..c421032 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -6,6 +6,7 @@ class Connection{ private Socket socket; private Scanner in; private DataOutputStream out; + private CommandType lastCommand; public Connection(Socket socket){ this.socket=socket; @@ -22,6 +23,7 @@ public Connection(Socket socket){ public void sendNickHello(String nick){ try { out.write(new StringBuilder(Protocol.GREETING).append(nick).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.NICK; } catch (IOException e) { e.printStackTrace(); } @@ -30,6 +32,7 @@ public void sendNickHello(String nick){ public void sendNickBusy(String nick){ try { out.write(new StringBuilder(Protocol.GREETING).append(nick).append(" busy").append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.NICK; } catch (IOException e) { e.printStackTrace(); } @@ -40,6 +43,7 @@ public void sendNickBusy(String nick){ public void accept(){ try { out.write(new StringBuilder(Protocol.ACCEPTED).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.ACCEPT; } catch (IOException e) { e.printStackTrace(); } @@ -48,6 +52,7 @@ public void accept(){ public void reject(){ try { out.write(new StringBuilder(Protocol.REJECTED).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.REJECT; } catch (IOException e) { e.printStackTrace(); } @@ -56,6 +61,7 @@ public void reject(){ public void sendMessage(String message){ try { out.write(new StringBuilder(Protocol.MESSAGE).append("\n").append(message).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.MESSAGE; } catch (IOException e) { e.printStackTrace(); } @@ -65,6 +71,9 @@ public void disconnect(){ try{ out.write(new StringBuilder(Protocol.DISCONNECT).append("\n").toString().getBytes("UTF-8")); socket.close(); + in.close(); + out.close(); + lastCommand=CommandType.DISCONNECT; }catch (Exception e){ e.printStackTrace(); } @@ -73,4 +82,8 @@ public void disconnect(){ public Command recieve(){ return Command.getCommand(in); } + + public CommandType getLastCommand(){ + return lastCommand; + } } diff --git a/src/HistoryView.java b/src/HistoryView.java new file mode 100644 index 0000000..09557df --- /dev/null +++ b/src/HistoryView.java @@ -0,0 +1,19 @@ +import javax.swing.*; + +public class HistoryView extends JTextArea{ + HistoryViewModel historyViewModel; + int currentSize=0; + + public HistoryView(HistoryViewModel historyViewModel){ + this.historyViewModel=historyViewModel; + historyViewModel.setHistoryView(this); + } + + public void updateYourself(){ + int tmp = historyViewModel.getSize(); + for (int i=currentSize;i Messagelist = new ArrayList(); + HistoryView historyView; + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk:mm:ss dd.MM.YY]"); + + public HistoryViewModel(){ + + } + + public HistoryViewModel(Logic logic){ + this.logic = logic; + } + + private String composeString(String message){ + return new StringBuilder(simpleDateFormat.format(new Date(System.currentTimeMillis()))).append(" ").append(message).toString(); + } + + public void addRemoteMessage(String message){ + Messagelist.add(new StringBuilder(logic.getRemoteNick()).append(" ").append(composeString(message)).toString()); + notifyHistoryView(); + } + + public void addLocalMessage(String message){ + Messagelist.add(new StringBuilder(logic.getLocalNick()).append(" ").append(composeString(message)).toString()); + notifyHistoryView(); + } + + public void addSystemMessage(String message){ + Messagelist.add(composeString(message)); + notifyHistoryView(); + } + + public void notifyHistoryView(){ + historyView.updateYourself(); + } + + public int getSize(){ + return Messagelist.size(); + } + + public String get(int i){ + return Messagelist.get(i); + } + + public void setHistoryView(HistoryView historyView){ + this.historyView = historyView; + } + + +} diff --git a/LFrame b/src/LFrame.java similarity index 76% rename from LFrame rename to src/LFrame.java index dc891e8..2562d00 100644 --- a/LFrame +++ b/src/LFrame.java @@ -23,7 +23,8 @@ public class LFrame extends JFrame { - JPanel panel = new JPanel(); + JPanel panel = new JPanel(); + Logic logic; JButton Send; JButton Apply; @@ -33,10 +34,11 @@ public class LFrame extends JFrame { JLabel login; JLabel addr; - JLabel smth; + HistoryView smth; JTextField textfieldlogin; JTextField EnterIp; + JTextField mass; String name = ""; @@ -51,7 +53,9 @@ public class LFrame extends JFrame { LineBorder linebord = new LineBorder(Color.BLACK, 1); - LFrame(){ + LFrame(final Logic logic){ + this.logic=logic; + JPanel panel = new JPanel(); panel.setLayout(null); @@ -71,7 +75,7 @@ public class LFrame extends JFrame { EnterIp.setBounds(500,40,150,30); EnterIp.setBorder(linebord); - JTextField mass = new JTextField(); + mass = new JTextField(); mass.setHorizontalAlignment(JTextField.LEFT); mass.setBorder(linebord); JTextArea ForMass = new JTextArea(); @@ -80,7 +84,7 @@ public class LFrame extends JFrame { ForMass.setWrapStyleWord(true); mass.setBounds(10, 480, 650, 75); - smth = new JLabel(); + smth = new HistoryView(logic.getHistoryViewModel()); smth.setLocation(10, 140); smth.setSize(740,300); smth.setBorder(linebord); @@ -89,6 +93,12 @@ public class LFrame extends JFrame { Apply.setLocation(80, 80); Apply.setSize(75,30); Apply.setFont(font); + Apply.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + logic.setLocalNick(textfieldlogin.getText()); + } + }); Disconnect = new JButton("Disconnect"); Disconnect.setLocation(600,80); @@ -99,19 +109,26 @@ public class LFrame extends JFrame { Connect.setLocation(450,80); Connect.setSize(100, 30); Connect.setFont(font); + Connect.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + logic.setRemoteIP(EnterIp.getText()); + logic.call(); + } + }); + Send = new JButton("Send"); Send.setLocation(680, 530); Send.setSize(100,30); Send.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent ae) { int h=0; - name1 = textfieldlogin.getText(); - - if(name.equals(name1)){ - text = mass.getText(); - smth.setText(name + text); + + text = mass.getText(); + logic.sendMessage(text); + logic.getHistoryViewModel().addLocalMessage(text); - } + } }); @@ -127,6 +144,7 @@ public void actionPerformed(ActionEvent ae) { panel.add(Apply); panel.add(Disconnect); panel.add(Connect); + this.add(panel); diff --git a/src/Logic.java b/src/Logic.java index ea9eec0..c1b61f5 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -1,31 +1,37 @@ +import javax.swing.*; import java.io.IOException; public class Logic{ - //ссылка на GUI + private MainGui mainGui; private String localNick = "default",remoteNick,remoteIP; private boolean isBusy; private Connection connection; private Caller caller; private CallListenerThread callListenerThread; private Thread callThread; + private HistoryViewModel historyViewModel = new HistoryViewModel(this); + private CommandListenerThread commandListenerThread; - public Logic(/*ссылка на GUI*/){ - callListenerThread = new CallListenerThread(localNick,isBusy); + + + public Logic(){ + callListenerThread = new CallListenerThread(localNick,isBusy,this); callThread = new Thread(callListenerThread); callThread.start(); - //this.GUI = GUI; + mainGui = new MainGui(this); } - public void setLocalNick(){ - /* localNick = GUI.getLocalNick() */ + public void setLocalNick(String nick){ + localNick=nick; } + public void setRemoteNick(String nick){ remoteNick = nick; } - public void setRemoteIP(){ - // remoteIP = GUI.getRemoteIP(); + public void setRemoteIP(String IP){ + remoteIP = IP; } public void setBusy(boolean isBusy) { @@ -34,18 +40,24 @@ public void setBusy(boolean isBusy) { } - public void accept(){ - connection.accept(); - // callListenerThread.setButtonPressed(CommandType.ACCEPT); + public void accept(Connection connection){ + this.connection=connection; + commandListenerThread = new CommandListenerThread(this.connection,this); + Thread thread = new Thread(commandListenerThread); + thread.start(); + } public void reject(){ connection.reject(); - // callListenerThread.setButtonPressed(CommandType.REJECT); + + } + + public void sendMessage(String message){ + connection.sendMessage(message); } public void call(){ - setRemoteIP(); //блокировка кнопок Apply, Connect caller = new Caller(localNick,remoteIP); try { @@ -54,26 +66,54 @@ public void call(){ //не удалось дозвониться по какой-то причине } if (connection!=null){ - CommandListenerThread commandListenerThread = new CommandListenerThread(connection); + remoteNick=caller.getRemoteNick(); + commandListenerThread = new CommandListenerThread(connection,this); Thread comThread = new Thread(commandListenerThread); comThread.start(); setBusy(true); + System.out.println("CONNECTED, YEY"); } } + public void addMessage(String message){ + historyViewModel.addRemoteMessage(message); + } + public void disconnect(){ setBusy(false); //разблокировка всякого // remoteNick=null; remoteIP=null; + commandListenerThread.kill(); connection.disconnect(); + historyViewModel.addSystemMessage("Disconnected"); } public void updateGUI(){ // } + public String getLocalNick(){ + return localNick; + } + + public String getRemoteNick(){ + return remoteNick; + } + + public HistoryViewModel getHistoryViewModel(){ + return historyViewModel; + } + + public static void main(String[] args) { + Logic logic = new Logic(); + } + + + + + diff --git a/src/MainGui.java b/src/MainGui.java new file mode 100644 index 0000000..1ea7f11 --- /dev/null +++ b/src/MainGui.java @@ -0,0 +1,22 @@ +import javax.swing.*; +import java.awt.*; +public class MainGui { + + private static final int Height = 600; + private static final int Widht = 800; + + public MainGui(Logic logic){ + LFrame frame = new LFrame(logic); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(Widht, Height); + frame.setResizable(false); + frame.setAlwaysOnTop(true); + frame.setLocationRelativeTo(null); + frame.setTitle("CHATAPP"); + frame.setVisible(true); + + } + + +} From 13e35c0b6bc9ec3594d729a463e01981dc9a070d Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Thu, 19 Nov 2015 00:15:31 +0200 Subject: [PATCH 17/38] Work maybe? --- src/AccorDis.java | 3 ++- src/CallListenerThread.java | 8 +++++--- src/Logic.java | 3 +++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/AccorDis.java b/src/AccorDis.java index c8c5eeb..9c32438 100644 --- a/src/AccorDis.java +++ b/src/AccorDis.java @@ -31,7 +31,7 @@ public void close(){ String username = "Artem"; - AccorDis(final Connection connection){ + AccorDis(final Connection connection, final Logic logic){ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(Widht, Height); setResizable(false); @@ -55,6 +55,7 @@ public void close(){ @Override public void actionPerformed(ActionEvent e) { connection.accept(); + logic.accept(connection); close(); } }); diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index c1dbdd9..262aeab 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -28,9 +28,11 @@ public void run() { remoteConnection = callListener.getConnection(); if (remoteConnection == null) continue; remoteNick=callListener.getRemoteNick(); - form = new AccorDis(remoteConnection); - if (remoteConnection.getLastCommand()==CommandType.ACCEPT) logic.accept(remoteConnection); - else continue; + form = new AccorDis(remoteConnection,logic); + System.out.println("AZAZA"); + logic.setRemoteNick(remoteNick); + + } } diff --git a/src/Logic.java b/src/Logic.java index c1b61f5..34eeba3 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -41,10 +41,13 @@ public void setBusy(boolean isBusy) { } public void accept(Connection connection){ + System.out.println("logic got Connection!"); this.connection=connection; commandListenerThread = new CommandListenerThread(this.connection,this); Thread thread = new Thread(commandListenerThread); thread.start(); + setBusy(true); + System.out.println("GOT CONNECTION, YEY!"); } From 2f28afe45cd654090f120cfcad487274ed936a82 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Tue, 24 Nov 2015 21:15:54 +0200 Subject: [PATCH 18/38] Fixed some bugs, added some new ones :3 --- src/AccorDis.java | 10 ++-- src/CallListener.java | 4 ++ src/CallListenerThread.java | 10 ++-- src/Caller.java | 4 ++ src/Command.java | 8 ++- src/CommandListenerThread.java | 1 + src/HistoryView.java | 7 +++ src/HistoryViewModel.java | 26 +++++++++- src/LFrame.java | 90 +++++++++++++++++++++++++--------- src/Logic.java | 29 ++++++++--- src/MainGui.java | 22 --------- src/UltimateGUI.java | 14 ++++++ 12 files changed, 161 insertions(+), 64 deletions(-) delete mode 100644 src/MainGui.java create mode 100644 src/UltimateGUI.java diff --git a/src/AccorDis.java b/src/AccorDis.java index 9c32438..424661a 100644 --- a/src/AccorDis.java +++ b/src/AccorDis.java @@ -29,22 +29,22 @@ public void close(){ dispose(); } - String username = "Artem"; - AccorDis(final Connection connection, final Logic logic){ + + AccorDis(final Connection connection, final Logic logic, String username){ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(Widht, Height); setResizable(false); setAlwaysOnTop(true); setLocationRelativeTo(null); - setTitle("hop-hey-la-lay"); + setTitle("Incoming call"); JPanel panel = new JPanel(); panel.setLayout(null); panel.setBackground(Color.pink); - NickName = new JLabel("User wants " +username+ " to speak"); + NickName = new JLabel(username+ " is calling"); NickName.setLocation(60, 10); NickName.setSize(200,54); @@ -60,7 +60,7 @@ public void actionPerformed(ActionEvent e) { } }); - Dismiss = new JButton("Dismiss"); + Dismiss = new JButton("Reject"); Dismiss.setLocation(150,80); Dismiss.setSize(130,30); Dismiss.addActionListener(new ActionListener() { diff --git a/src/CallListener.java b/src/CallListener.java index 90b6ae3..61a50cc 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -46,6 +46,10 @@ public Connection getConnection() throws IOException { public void setBusy(boolean isBusy) { this.isBusy = isBusy; } + + public void setNick(String nick){ + localNick=nick; + } public boolean getBusy(){ return isBusy; diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index 262aeab..3e5d7f5 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -28,12 +28,9 @@ public void run() { remoteConnection = callListener.getConnection(); if (remoteConnection == null) continue; remoteNick=callListener.getRemoteNick(); - form = new AccorDis(remoteConnection,logic); + form = new AccorDis(remoteConnection,logic,remoteNick); System.out.println("AZAZA"); logic.setRemoteNick(remoteNick); - - - } } catch(IOException e){ @@ -52,6 +49,11 @@ public void setBusy(Boolean isBusy){ this.isBusy=isBusy; callListener.setBusy(isBusy); } + + public void setNick(String nick){ + localNick=nick; + callListener.setNick(localNick); + } public void kill() { diff --git a/src/Caller.java b/src/Caller.java index 1efefea..02276b2 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -34,7 +34,9 @@ public Connection call() throws IOException { if (nickCommand.isBusy()){ lastCommand = connection.recieve(); connection.disconnect(); + UltimateGUI busyGUI = new UltimateGUI(remoteNick); lastError="User is busy"; + remoteNick=null; return null; } @@ -45,7 +47,9 @@ public Connection call() throws IOException { //Если Accept - вернуть connection if (lastCommand.type==CommandType.ACCEPT) return connection; else{ + UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); lastError="User rejected your call"; + connection.disconnect(); return null; } diff --git a/src/Command.java b/src/Command.java index 9e473be..0100119 100644 --- a/src/Command.java +++ b/src/Command.java @@ -10,7 +10,13 @@ public Command(CommandType type){ } public static Command getCommand(Scanner in){ - String string = in.nextLine(); + String string =""; + if (in.hasNextLine()) { + string = in.nextLine(); + } + else{ + return null; + } if (string.contains(Protocol.ACCEPTED)) return new Command(CommandType.ACCEPT); if (string.contains(Protocol.DISCONNECT)) return new Command(CommandType.DISCONNECT); if (string.contains(Protocol.REJECTED)) return new Command(CommandType.REJECT); diff --git a/src/CommandListenerThread.java b/src/CommandListenerThread.java index 1a75480..86595e9 100644 --- a/src/CommandListenerThread.java +++ b/src/CommandListenerThread.java @@ -16,6 +16,7 @@ public CommandListenerThread(Connection connection, Logic logic){ public void run() { while (!stop) { lastCommand = connection.recieve(); + if (lastCommand==null) continue; if (lastCommand.type==CommandType.MESSAGE){ MessageCommand mc = (MessageCommand) lastCommand; logic.addMessage(mc.message); diff --git a/src/HistoryView.java b/src/HistoryView.java index 09557df..169979a 100644 --- a/src/HistoryView.java +++ b/src/HistoryView.java @@ -7,6 +7,8 @@ public class HistoryView extends JTextArea{ public HistoryView(HistoryViewModel historyViewModel){ this.historyViewModel=historyViewModel; historyViewModel.setHistoryView(this); + setEditable(false); + setAutoscrolls(true); } public void updateYourself(){ @@ -16,4 +18,9 @@ public void updateYourself(){ } currentSize=tmp; } + + public void clear(){ + currentSize=0; + setText(""); + } } diff --git a/src/HistoryViewModel.java b/src/HistoryViewModel.java index 04c781e..03f07b4 100644 --- a/src/HistoryViewModel.java +++ b/src/HistoryViewModel.java @@ -1,3 +1,5 @@ +import java.io.FileWriter; +import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -7,7 +9,7 @@ public class HistoryViewModel { Logic logic; List Messagelist = new ArrayList(); HistoryView historyView; - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk:mm:ss dd.MM.YY]"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk:mm:ss dd.MM.yy]"); public HistoryViewModel(){ @@ -52,5 +54,27 @@ public void setHistoryView(HistoryView historyView){ this.historyView = historyView; } + public void writeHistoryFile(){ + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk.mm.ss dd.MM.yy]"); + FileWriter out = null; + try { + out = new FileWriter(new StringBuilder(simpleDateFormat.format( + new Date(System.currentTimeMillis()))).append(logic.getRemoteNick()).append(".txt").toString()); + for (String string : Messagelist){ + out.write(string+"\n"); + out.flush(); + } + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + public void clearView(){ + Messagelist = new ArrayList(); + historyView.clear(); + } + } diff --git a/src/LFrame.java b/src/LFrame.java index 2562d00..9899ba2 100644 --- a/src/LFrame.java +++ b/src/LFrame.java @@ -1,30 +1,22 @@ import java.awt.Color; -import java.awt.Component; + import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.ItemListener; - -import java.util.Vector; - -import javax.swing.AbstractButton; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JTextArea; -import javax.swing.JTextField; + + +import javax.swing.*; + import javax.swing.border.LineBorder; -import javax.swing.border.SoftBevelBorder; + public class LFrame extends JFrame { JPanel panel = new JPanel(); Logic logic; + + private static final int Height = 600; + private static final int Widht = 800; JButton Send; JButton Apply; @@ -83,11 +75,16 @@ public class LFrame extends JFrame { ForMass.setLineWrap(true); ForMass.setWrapStyleWord(true); mass.setBounds(10, 480, 650, 75); - + + smth = new HistoryView(logic.getHistoryViewModel()); smth.setLocation(10, 140); smth.setSize(740,300); - smth.setBorder(linebord); + + JScrollPane scrollPane = new JScrollPane(smth); + scrollPane.setLocation(10, 140); + scrollPane.setSize(740,300); + scrollPane.setBorder(linebord); Apply = new JButton("Apply"); Apply.setLocation(80, 80); @@ -104,6 +101,12 @@ public void actionPerformed(ActionEvent e) { Disconnect.setLocation(600,80); Disconnect.setSize(130,30); Disconnect.setFont(font); + Disconnect.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + logic.disconnect(); + } + }); Connect = new JButton("Connect"); Connect.setLocation(450,80); @@ -113,7 +116,14 @@ public void actionPerformed(ActionEvent e) { @Override public void actionPerformed(ActionEvent e) { logic.setRemoteIP(EnterIp.getText()); - logic.call(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logic.getHistoryViewModel().clearView(); + logic.call(); + + } + }); } }); @@ -127,6 +137,7 @@ public void actionPerformed(ActionEvent ae) { text = mass.getText(); logic.sendMessage(text); logic.getHistoryViewModel().addLocalMessage(text); + mass.setText(""); @@ -137,18 +148,49 @@ public void actionPerformed(ActionEvent ae) { panel.add(login); panel.add(textfieldlogin); panel.add(mass); - panel.add(smth); + panel.add(scrollPane); panel.add(EnterIp); panel.add(addr); panel.add(Send); panel.add(Apply); panel.add(Disconnect); - panel.add(Connect); + panel.add(Connect); - - this.add(panel); + + this.add(panel); + + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(Widht, Height); + setResizable(false); + setLocationRelativeTo(null); + setTitle("ChatApp"); + setConnected(false); + setVisible(true); } + public void setBusy(Boolean busy){ + + } + + public void setConnected(boolean b){ + if (b){ + Connect.setEnabled(false); + Apply.setEnabled(false); + Send.setEnabled(true); + Disconnect.setEnabled(true); + textfieldlogin.setEditable(false); + EnterIp.setEditable(false); + } + else { + Connect.setEnabled(true); + Apply.setEnabled(true); + Send.setEnabled(false); + Disconnect.setEnabled(false); + textfieldlogin.setEditable(true); + EnterIp.setEditable(true); + } + } + } diff --git a/src/Logic.java b/src/Logic.java index 34eeba3..1d88097 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -2,7 +2,7 @@ import java.io.IOException; public class Logic{ - private MainGui mainGui; + private LFrame mainGui; private String localNick = "default",remoteNick,remoteIP; private boolean isBusy; private Connection connection; @@ -18,11 +18,12 @@ public Logic(){ callListenerThread = new CallListenerThread(localNick,isBusy,this); callThread = new Thread(callListenerThread); callThread.start(); - mainGui = new MainGui(this); + mainGui = new LFrame(this); } public void setLocalNick(String nick){ - localNick=nick; + localNick=nick; + callListenerThread.setNick(localNick); } @@ -37,7 +38,7 @@ public void setRemoteIP(String IP){ public void setBusy(boolean isBusy) { this.isBusy = isBusy; callListenerThread.setBusy(isBusy); - + mainGui.setBusy(isBusy); } public void accept(Connection connection){ @@ -47,7 +48,10 @@ public void accept(Connection connection){ Thread thread = new Thread(commandListenerThread); thread.start(); setBusy(true); + historyViewModel.addSystemMessage("Connected to "+remoteNick); System.out.println("GOT CONNECTION, YEY!"); + mainGui.setConnected(true); + } @@ -61,21 +65,28 @@ public void sendMessage(String message){ } public void call(){ + mainGui.setConnected(true); + setBusy(true); //блокировка кнопок Apply, Connect caller = new Caller(localNick,remoteIP); try { connection = caller.call(); } catch (IOException e) { - //не удалось дозвониться по какой-то причине + } if (connection!=null){ remoteNick=caller.getRemoteNick(); commandListenerThread = new CommandListenerThread(connection,this); Thread comThread = new Thread(commandListenerThread); comThread.start(); - setBusy(true); + historyViewModel.addSystemMessage("Connected to "+remoteNick); System.out.println("CONNECTED, YEY"); } + else{ + mainGui.setConnected(false); + setBusy(false); + UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); + } } public void addMessage(String message){ @@ -86,11 +97,15 @@ public void disconnect(){ setBusy(false); //разблокировка всякого // + historyViewModel.addSystemMessage("Disconnected"); + historyViewModel.writeHistoryFile(); remoteNick=null; remoteIP=null; commandListenerThread.kill(); connection.disconnect(); - historyViewModel.addSystemMessage("Disconnected"); + mainGui.setConnected(false); + setBusy(false); + } public void updateGUI(){ diff --git a/src/MainGui.java b/src/MainGui.java deleted file mode 100644 index 1ea7f11..0000000 --- a/src/MainGui.java +++ /dev/null @@ -1,22 +0,0 @@ -import javax.swing.*; -import java.awt.*; -public class MainGui { - - private static final int Height = 600; - private static final int Widht = 800; - - public MainGui(Logic logic){ - LFrame frame = new LFrame(logic); - - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(Widht, Height); - frame.setResizable(false); - frame.setAlwaysOnTop(true); - frame.setLocationRelativeTo(null); - frame.setTitle("CHATAPP"); - frame.setVisible(true); - - } - - -} diff --git a/src/UltimateGUI.java b/src/UltimateGUI.java new file mode 100644 index 0000000..a13190c --- /dev/null +++ b/src/UltimateGUI.java @@ -0,0 +1,14 @@ +import javax.swing.*; + +public class UltimateGUI extends JFrame{ + + public UltimateGUI(String nick){ + super(); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + //todo Универсальная GUI с кнопкой по середине и лейблом с извещением над ней, который будет передаваться в конструкторе; + } + + private void exit(){ + dispose(); + } +} From 49b85c4121d6a16dc8c3cd8a226f21aee3878adf Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 25 Nov 2015 19:58:26 +0200 Subject: [PATCH 19/38] wip I guess --- src/Caller.java | 12 ++++++--- src/LFrame.java | 4 +-- src/Logic.java | 63 +++++++++++++++++++++++++++++++++++--------- src/UltimateGUI.java | 47 ++++++++++++++++++++++++++++++--- 4 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/Caller.java b/src/Caller.java index 02276b2..03849b2 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -9,10 +9,12 @@ public class Caller { private Command lastCommand; private NickCommand nickCommand; private String lastError; + private Logic logic; - public Caller(String localNick,String remoteIP){ + public Caller(String localNick,String remoteIP, Logic logic){ this.localNick=localNick; this.remoteIP=remoteIP; + this.logic=logic; } public Connection call() throws IOException { @@ -22,10 +24,12 @@ public Connection call() throws IOException { lastCommand = connection.recieve(); if (lastCommand.type==CommandType.NICK) { nickCommand = (NickCommand) lastCommand; + logic.getMainGui().setConnected(true); } else{ connection.disconnect(); lastError="Wrong IP"; + UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); return null; } @@ -34,9 +38,10 @@ public Connection call() throws IOException { if (nickCommand.isBusy()){ lastCommand = connection.recieve(); connection.disconnect(); - UltimateGUI busyGUI = new UltimateGUI(remoteNick); + UltimateGUI busyGUI = new UltimateGUI(remoteNick+" is busy"); lastError="User is busy"; remoteNick=null; + logic.getMainGui().setConnected(false); return null; } @@ -47,8 +52,9 @@ public Connection call() throws IOException { //Если Accept - вернуть connection if (lastCommand.type==CommandType.ACCEPT) return connection; else{ - UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); + UltimateGUI ultimateGUI = new UltimateGUI(remoteNick+" rejected your call"); lastError="User rejected your call"; + logic.getMainGui().setConnected(false); connection.disconnect(); return null; } diff --git a/src/LFrame.java b/src/LFrame.java index 9899ba2..5edaf1a 100644 --- a/src/LFrame.java +++ b/src/LFrame.java @@ -56,14 +56,14 @@ public class LFrame extends JFrame { login = new JLabel("login:"); login.setFont(font); login.setBounds(35, 40, 60, 30); - textfieldlogin = new JTextField(); + textfieldlogin = new JTextField(logic.getLocalNick()); textfieldlogin.setBounds(80, 40, 115, 30); textfieldlogin.setBorder(linebord); addr = new JLabel("remote addr"); addr.setFont(font); addr.setBounds(400,35,100,50); - EnterIp = new JTextField(); + EnterIp = new JTextField("files.litvinov.in.ua"); EnterIp.setBounds(500,40,150,30); EnterIp.setBorder(linebord); diff --git a/src/Logic.java b/src/Logic.java index 1d88097..2025ad4 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -1,4 +1,6 @@ -import javax.swing.*; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; public class Logic{ @@ -12,21 +14,29 @@ public class Logic{ private HistoryViewModel historyViewModel = new HistoryViewModel(this); private CommandListenerThread commandListenerThread; - - public Logic(){ + String tmp = getNickFromFile(); + if (tmp!=null) localNick=tmp; + else localNick="default"; callListenerThread = new CallListenerThread(localNick,isBusy,this); callThread = new Thread(callListenerThread); callThread.start(); mainGui = new LFrame(this); + + try { + Class.forName("com.mysql.jdbc.Driver"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } public void setLocalNick(String nick){ localNick=nick; callListenerThread.setNick(localNick); + writeNickToFile(); } - public void setRemoteNick(String nick){ remoteNick = nick; } @@ -48,6 +58,7 @@ public void accept(Connection connection){ Thread thread = new Thread(commandListenerThread); thread.start(); setBusy(true); + historyViewModel.clearView(); historyViewModel.addSystemMessage("Connected to "+remoteNick); System.out.println("GOT CONNECTION, YEY!"); mainGui.setConnected(true); @@ -67,12 +78,11 @@ public void sendMessage(String message){ public void call(){ mainGui.setConnected(true); setBusy(true); - //блокировка кнопок Apply, Connect - caller = new Caller(localNick,remoteIP); + caller = new Caller(localNick,remoteIP,this); try { connection = caller.call(); } catch (IOException e) { - + UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); } if (connection!=null){ remoteNick=caller.getRemoteNick(); @@ -85,7 +95,7 @@ public void call(){ else{ mainGui.setConnected(false); setBusy(false); - UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); + } } @@ -124,16 +134,43 @@ public HistoryViewModel getHistoryViewModel(){ return historyViewModel; } - public static void main(String[] args) { - Logic logic = new Logic(); + public LFrame getMainGui(){ + return mainGui; } + public void writeNickToFile(){ + FileWriter out = null; + try { + out = new FileWriter("nick.txt"); + out.write(localNick); + out.close(); + } catch (IOException e) { + System.out.println("Could not create nick.txt"); + } + } + public String getNickFromFile(){ + BufferedReader in = null; + try{ + in = new BufferedReader(new FileReader("nick.txt")); + String nick = in.readLine(); + in.close(); + if (nick!=null){ + return nick; + } + else { + return null; + } + } catch (IOException e){ + System.out.println("Failed to find a file nick.txt"); + return null; + } + } - - - + public static void main(String[] args) { + Logic logic = new Logic(); + } } diff --git a/src/UltimateGUI.java b/src/UltimateGUI.java index a13190c..a2a4d6e 100644 --- a/src/UltimateGUI.java +++ b/src/UltimateGUI.java @@ -1,11 +1,52 @@ import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; public class UltimateGUI extends JFrame{ - public UltimateGUI(String nick){ + public UltimateGUI(String string){ super(); - setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - //todo Универсальная GUI с кнопкой по середине и лейблом с извещением над ней, который будет передаваться в конструкторе; + + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(300,120); + setResizable(false); + setAlwaysOnTop(true); + setLocationRelativeTo(null); + setTitle("Error"); //? + createGui(string); + setVisible(true); + + } + + public void createGui(String string){ + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setSize(300,150); + + JLabel label = new JLabel(string); + label.setLocation(0,0); + label.setSize(300,50); + label.setHorizontalAlignment(0); + + JButton okButton = new JButton("OK"); + okButton.setLocation(110,50); + okButton.setSize(75,25); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exit(); + } + }); + + ///panel.add(Box.createVerticalGlue()); + panel.add(label); + // panel.add(Box.createVerticalGlue()); + panel.add(okButton); + // panel.add(Box.createVerticalGlue()); + //add(Box.createHorizontalGlue()); + + add(panel); + //add(Box.createHorizontalGlue()); } private void exit(){ From e7241be11f192e183b24052ebef34b8767e29625 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Tue, 1 Dec 2015 19:45:42 +0200 Subject: [PATCH 20/38] Still fixing stuff. Some bugs should be fixed now, hopefully. --- src/AccorDis.java | 14 +++-- src/LFrame.java | 134 ++++++++++++++++++++++++++++++++++++---------- src/Logic.java | 49 +++++++++++------ src/Protocol.java | 14 +++++ 4 files changed, 162 insertions(+), 49 deletions(-) diff --git a/src/AccorDis.java b/src/AccorDis.java index 424661a..47ed385 100644 --- a/src/AccorDis.java +++ b/src/AccorDis.java @@ -2,15 +2,12 @@ import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; - +import javax.swing.JOptionPane; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JTextArea; -import javax.swing.border.LineBorder; -import javax.swing.border.SoftBevelBorder; + public class AccorDis extends JFrame { @@ -70,6 +67,13 @@ public void actionPerformed(ActionEvent e) { close(); } }); + + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + connection.reject(); + close(); + } + }); panel.add(Accept); panel.add(Dismiss); diff --git a/src/LFrame.java b/src/LFrame.java index 5edaf1a..1434636 100644 --- a/src/LFrame.java +++ b/src/LFrame.java @@ -1,16 +1,13 @@ import java.awt.Color; - import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; - - +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import javax.swing.*; - import javax.swing.border.LineBorder; - public class LFrame extends JFrame { JPanel panel = new JPanel(); Logic logic; @@ -22,7 +19,6 @@ public class LFrame extends JFrame { JButton Apply; JButton Disconnect; JButton Connect; - JLabel login; JLabel addr; @@ -31,17 +27,10 @@ public class LFrame extends JFrame { JTextField textfieldlogin; JTextField EnterIp; JTextField mass; - - String name = ""; - String name1; String text; - int adress; - Font font = new Font("Verdana", Font.BOLD, 13); - - LineBorder linebord = new LineBorder(Color.BLACK, 1); @@ -59,6 +48,25 @@ public class LFrame extends JFrame { textfieldlogin = new JTextField(logic.getLocalNick()); textfieldlogin.setBounds(80, 40, 115, 30); textfieldlogin.setBorder(linebord); + textfieldlogin.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode()==KeyEvent.VK_ENTER){ + applyNick(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); addr = new JLabel("remote addr"); addr.setFont(font); @@ -66,21 +74,62 @@ public class LFrame extends JFrame { EnterIp = new JTextField("files.litvinov.in.ua"); EnterIp.setBounds(500,40,150,30); EnterIp.setBorder(linebord); + EnterIp.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + logic.setRemoteIP(EnterIp.getText()); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logic.getHistoryViewModel().clearView(); + logic.call(); + } + }); + } + } + + + @Override + public void keyReleased(KeyEvent e) { + + } + }); mass = new JTextField(); mass.setHorizontalAlignment(JTextField.LEFT); mass.setBorder(linebord); - JTextArea ForMass = new JTextArea(); - ForMass.setEditable(false); - ForMass.setLineWrap(true); - ForMass.setWrapStyleWord(true); mass.setBounds(10, 480, 650, 75); + mass.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode()==KeyEvent.VK_ENTER){ + send(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); smth = new HistoryView(logic.getHistoryViewModel()); smth.setLocation(10, 140); smth.setSize(740,300); + JScrollPane scrollPane = new JScrollPane(smth); scrollPane.setLocation(10, 140); scrollPane.setSize(740,300); @@ -93,7 +142,7 @@ public class LFrame extends JFrame { Apply.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - logic.setLocalNick(textfieldlogin.getText()); + applyNick(); } }); @@ -132,16 +181,27 @@ public void run() { Send.setSize(100,30); Send.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent ae) { - int h=0; + send(); + } + }); - text = mass.getText(); - logic.sendMessage(text); - logic.getHistoryViewModel().addLocalMessage(text); - mass.setText(""); - + final JFrame frame = this; + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + if (logic.isConnected()){ + if (JOptionPane.showConfirmDialog(frame, + "You have an established connection with another user.\n Are you sure you want to close the program?", "Warning", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + exit(); + } + } + else{ + exit(); + } - - } + } }); @@ -159,7 +219,7 @@ public void actionPerformed(ActionEvent ae) { this.add(panel); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); setSize(Widht, Height); setResizable(false); setLocationRelativeTo(null); @@ -170,8 +230,26 @@ public void actionPerformed(ActionEvent ae) { } - public void setBusy(Boolean busy){ + public void exit(){ + logic.exit(); + System.exit(0); + } + public void send(){ + text = mass.getText(); + if (Protocol.isMessageValid(text)) { + logic.sendMessage(text); + logic.getHistoryViewModel().addLocalMessage(text); + mass.setText(""); + } + } + + public void applyNick(){ + text=textfieldlogin.getText(); + if (Protocol.isNickValid(text))logic.setLocalNick(text); + else { + UltimateGUI ultimateGUI = new UltimateGUI("Only latin, numbers and \"_\" \"-\" "); + } } public void setConnected(boolean b){ diff --git a/src/Logic.java b/src/Logic.java index 2025ad4..896ba3b 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -7,12 +7,13 @@ public class Logic{ private LFrame mainGui; private String localNick = "default",remoteNick,remoteIP; private boolean isBusy; - private Connection connection; + private Connection connection = null; private Caller caller; private CallListenerThread callListenerThread; private Thread callThread; private HistoryViewModel historyViewModel = new HistoryViewModel(this); private CommandListenerThread commandListenerThread; + private ServerConnection serverConnection = new ServerConnection(); public Logic(){ String tmp = getNickFromFile(); @@ -22,18 +23,20 @@ public Logic(){ callThread = new Thread(callListenerThread); callThread.start(); mainGui = new LFrame(this); - try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } - + serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); + serverConnection.connect(); + serverConnection.goOnline(); } public void setLocalNick(String nick){ localNick=nick; callListenerThread.setNick(localNick); + serverConnection.setLocalNick(localNick); writeNickToFile(); } @@ -48,7 +51,6 @@ public void setRemoteIP(String IP){ public void setBusy(boolean isBusy) { this.isBusy = isBusy; callListenerThread.setBusy(isBusy); - mainGui.setBusy(isBusy); } public void accept(Connection connection){ @@ -103,21 +105,25 @@ public void addMessage(String message){ historyViewModel.addRemoteMessage(message); } - public void disconnect(){ - setBusy(false); - //разблокировка всякого - // - historyViewModel.addSystemMessage("Disconnected"); - historyViewModel.writeHistoryFile(); - remoteNick=null; - remoteIP=null; - commandListenerThread.kill(); - connection.disconnect(); - mainGui.setConnected(false); - setBusy(false); + public void disconnect() { + if (isConnected()) { + setBusy(false); + historyViewModel.addSystemMessage("Disconnected"); + historyViewModel.writeHistoryFile(); + remoteNick = null; + remoteIP = null; + commandListenerThread.kill(); + connection.disconnect(); + connection = null; + mainGui.setConnected(false); + } } + public boolean isBusy(){ + return isBusy; + } + public void updateGUI(){ // } @@ -173,4 +179,15 @@ public String getNickFromFile(){ public static void main(String[] args) { Logic logic = new Logic(); } + + public boolean isConnected(){ + if (connection!=null) return true; + else return false; + } + + public void exit(){ + disconnect(); + serverConnection.goOffline(); + serverConnection.disconnect(); + } } diff --git a/src/Protocol.java b/src/Protocol.java index d7e9227..31747f5 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -10,6 +10,20 @@ public static String encode(String string){ return string.replace("\n",":&:"); } + public static boolean isNickValid(String nick){ + if (nick.matches(".*[a-bA-B0-9_\\-].*")) { + return true; + } + return false; + } + + public static boolean isMessageValid(String message){ + if (message.matches(".*\\S.*")) { + return true; + } + return false; + } + public static String decode(String string){ return string.replace(":&:","\n"); } From d1235c892d75bf32449b86edc9dd2abc58eafb5e Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Tue, 1 Dec 2015 20:18:20 +0200 Subject: [PATCH 21/38] +1 bugfix --- src/AccorDis.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AccorDis.java b/src/AccorDis.java index 47ed385..d07a6c3 100644 --- a/src/AccorDis.java +++ b/src/AccorDis.java @@ -29,7 +29,7 @@ public void close(){ AccorDis(final Connection connection, final Logic logic, String username){ - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setSize(Widht, Height); setResizable(false); setAlwaysOnTop(true); From e91216d4a7683604e32edccfa2ed057ff9447d61 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 2 Dec 2015 22:48:36 +0200 Subject: [PATCH 22/38] wip again. --- src/Caller.java | 3 +- src/Contact.java | 59 +++++++++++++++++++++++++++ src/ContactPanel.java | 58 ++++++++++++++++++++++++++ src/ContactsView.java | 81 +++++++++++++++++++++++++++++++++++++ src/ContactsViewModel.java | 40 ++++++++++++++++++ src/LFrame.java | 19 ++++++++- src/Logic.java | 6 +++ src/PopUp.java | 51 +++++++++++++++++++++++ src/Protocol.java | 2 +- src/UltimateGUI.java | 28 ++++++++++--- src/images/off.png | Bin 0 -> 3068 bytes src/images/on.png | Bin 0 -> 3076 bytes 12 files changed, 337 insertions(+), 10 deletions(-) create mode 100644 src/Contact.java create mode 100644 src/ContactPanel.java create mode 100644 src/ContactsView.java create mode 100644 src/ContactsViewModel.java create mode 100644 src/PopUp.java create mode 100644 src/images/off.png create mode 100644 src/images/on.png diff --git a/src/Caller.java b/src/Caller.java index 03849b2..58d9a0a 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -1,6 +1,8 @@ import java.io.IOException; import java.net.InetAddress; import java.net.Socket; +import java.util.Timer; +import java.util.TimerTask; public class Caller { private String localNick; @@ -19,7 +21,6 @@ public Caller(String localNick,String remoteIP, Logic logic){ public Connection call() throws IOException { Connection connection = new Connection(new Socket(remoteIP,Protocol.PORT_NUMBER)); - //Проверка, к тому ли мы подключились. lastCommand = connection.recieve(); if (lastCommand.type==CommandType.NICK) { diff --git a/src/Contact.java b/src/Contact.java new file mode 100644 index 0000000..496d143 --- /dev/null +++ b/src/Contact.java @@ -0,0 +1,59 @@ +public class Contact { + private boolean isFav,isOnline; + private String nick,IP; + private ContactsViewModel contactsViewModel; + + public Contact(ContactsViewModel cvm,String nick,String IP){ + contactsViewModel=cvm; + this.nick=nick; + this.IP=IP; + isFav=false; + isOnline=false; + } + + public boolean isFav() { + return isFav; + } + + public void setFav(boolean isFav) { + this.isFav = isFav; + } + + public boolean isOnline() { + return isOnline; + } + + public void setOnline(boolean isOnline) { + this.isOnline = isOnline; + } + + public String getNick() { + return nick; + } + + public void setNick(String nick) { + this.nick = nick; + } + + public String getIP() { + return IP; + } + + public void setIP(String IP) { + this.IP = IP; + } + + public void changeFav(){ + if (isFav) isFav=false; + else isFav=true; + contactsViewModel.updateView(); + } + + public void call(){ + contactsViewModel.call(this); + } + + public void remove(){ + contactsViewModel.removeContact(this); + } +} diff --git a/src/ContactPanel.java b/src/ContactPanel.java new file mode 100644 index 0000000..0b5d17b --- /dev/null +++ b/src/ContactPanel.java @@ -0,0 +1,58 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +public class ContactPanel extends JPanel{ + private Contact contact; + private JLabel label; + private ImageIcon imageIcon; + + public ContactPanel(final Contact contact){ + this.contact=contact; + imageIcon = new ImageIcon("src/images/off.png"); + label = new JLabel(contact.getNick()); + label.setIcon(imageIcon); + setBackground(Color.getHSBColor(188, 0, 97)); + addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + PopUp menu = new PopUp(contact); + menu.show(e.getComponent(), e.getX(), e.getY()); + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + setBackground(Color.getHSBColor(188,32,100)); + } + + @Override + public void mouseExited(MouseEvent e) { + setBackground(Color.getHSBColor(188,0,97)); + } + }); + setMinimumSize(new Dimension(200,50)); + add(label); + } + + public boolean isFav(){ + return contact.isFav(); + } + + public void update(){ + if (contact.getNick()!=label.getText()) label.setText(contact.getNick()); + if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); + else label.setIcon(new ImageIcon("src/images/off.png")); + } + +} diff --git a/src/ContactsView.java b/src/ContactsView.java new file mode 100644 index 0000000..c5de7de --- /dev/null +++ b/src/ContactsView.java @@ -0,0 +1,81 @@ +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; + +public class ContactsView extends JPanel{ + + private JPanel contacts,favourites; + private ContactsViewModel contactsViewModel; + private ArrayList list = new ArrayList(); + + public ContactsView(ContactsViewModel cvm){ + contactsViewModel=cvm; + contactsViewModel.setContactsView(this); + createGUI(); + } + + private void createGUI(){ + + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setPreferredSize(new Dimension(200, 600)); + JPanel top = new JPanel(); + top.setLayout(new BoxLayout(top,BoxLayout.Y_AXIS)); + JPanel bottom = new JPanel(); + bottom.setLayout(new BoxLayout(bottom,BoxLayout.Y_AXIS)); + + JLabel favouritesL = new JLabel("Favourites"); + JLabel contactsL = new JLabel("Contacts"); + Font font = new Font("Verdana",Font.BOLD,14); + favouritesL.setFont(font); + favouritesL.setSize(new Dimension(200, 50)); + contactsL.setFont(font); + contactsL.setSize(new Dimension(200, 50)); + + contacts = new JPanel(); + contacts.setLayout(new BoxLayout(contacts,BoxLayout.Y_AXIS)); + contacts.setMinimumSize(new Dimension(200, 250)); + + favourites = new JPanel(); + favourites.setLayout(new BoxLayout(favourites,BoxLayout.Y_AXIS)); + favourites.setMinimumSize(new Dimension(200, 250)); + + JScrollPane contactsS = new JScrollPane(contacts); + contactsS.setMinimumSize(new Dimension(200, 250)); + JScrollPane favouritesS = new JScrollPane(favourites); + favouritesS.setMinimumSize(new Dimension(200, 250)); + + bottom.setPreferredSize(new Dimension(200,300)); + top.setPreferredSize(new Dimension(200, 300)); + + top.add(favouritesL); + top.add(favouritesS); + bottom.add(contactsL); + bottom.add(contactsS); + + add(top); + add(bottom); + + setMinimumSize(new Dimension(200, 600)); + + } + + public void fullUpdate(){ + contacts.removeAll(); + favourites.removeAll(); + list.clear(); + ArrayList contactsList = (ArrayList) contactsViewModel.getList(); + for (Contact contact : contactsList){ + list.add(new ContactPanel(contact)); + } + for (ContactPanel cp : list){ + if (cp.isFav()) favourites.add(cp); + else contacts.add(cp); + } + } + + public void onlineUpdate(){ + updateUI(); + } + + +} diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java new file mode 100644 index 0000000..882228a --- /dev/null +++ b/src/ContactsViewModel.java @@ -0,0 +1,40 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.Timer; + +public class ContactsViewModel implements Runnable { + ContactsView contactsView; + ArrayList contactList = new ArrayList(); + Logic logic; + + public ContactsViewModel(Logic logic){ + this.logic=logic; + } + + public void setContactsView(ContactsView cv){ + contactsView=cv; + } + + public Collection getList(){ + return contactList; + } + + public void call(Contact contact){ + logic.getMainGui().changeEnterIp(contact.getIP()); + logic.call(); + } + + public void removeContact(Contact contact){ + contactList.remove(contact); + updateView(); + } + + public void updateView(){ + contactsView.fullUpdate(); + } + + + public void run() { + Timer timer = new Timer(); + } +} diff --git a/src/LFrame.java b/src/LFrame.java index 1434636..41025d2 100644 --- a/src/LFrame.java +++ b/src/LFrame.java @@ -28,6 +28,8 @@ public class LFrame extends JFrame { JTextField EnterIp; JTextField mass; + ContactsView contactsView; + String text; Font font = new Font("Verdana", Font.BOLD, 13); @@ -67,7 +69,12 @@ public void keyReleased(KeyEvent e) { } }); - + + contactsView = new ContactsView(logic.getContactsViewModel()); + // !!!!!!!!! + // ВОТ ТУТ ЕЁ ^ НОРМАЛЬНО РАСПОЛОЖИ И В НЕЙ САМОЙ МОЖЕШЬ МЕНЯТЬ РАЗМЕРЫ. НО ТОЛЬКО РАЗМЕРЫ ПО ДРУГОМУ ПОВОДУ - СПРАШИВАЙ + // !!!!!!!!! + addr = new JLabel("remote addr"); addr.setFont(font); addr.setBounds(400,35,100,50); @@ -156,6 +163,8 @@ public void actionPerformed(ActionEvent e) { logic.disconnect(); } }); + + Connect = new JButton("Connect"); Connect.setLocation(450,80); @@ -245,7 +254,9 @@ public void send(){ } public void applyNick(){ - text=textfieldlogin.getText(); + text=textfieldlogin.getText().replaceAll("\\s+", "_").replaceAll("\\-+","-").replaceAll("_+","_"); + if (text.endsWith("_")) text=text.substring(0,text.length()-1); + textfieldlogin.setText(text); if (Protocol.isNickValid(text))logic.setLocalNick(text); else { UltimateGUI ultimateGUI = new UltimateGUI("Only latin, numbers and \"_\" \"-\" "); @@ -271,4 +282,8 @@ public void setConnected(boolean b){ } } + public void changeEnterIp(String ip){ + EnterIp.setText(ip); + } + } diff --git a/src/Logic.java b/src/Logic.java index 896ba3b..a9c3ce0 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -14,6 +14,7 @@ public class Logic{ private HistoryViewModel historyViewModel = new HistoryViewModel(this); private CommandListenerThread commandListenerThread; private ServerConnection serverConnection = new ServerConnection(); + private ContactsViewModel contactsViewModel = new ContactsViewModel(this); public Logic(){ String tmp = getNickFromFile(); @@ -30,6 +31,7 @@ public Logic(){ } serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); serverConnection.connect(); + serverConnection.setLocalNick(localNick); serverConnection.goOnline(); } @@ -185,6 +187,10 @@ public boolean isConnected(){ else return false; } + public ContactsViewModel getContactsViewModel(){ + return contactsViewModel; + } + public void exit(){ disconnect(); serverConnection.goOffline(); diff --git a/src/PopUp.java b/src/PopUp.java new file mode 100644 index 0000000..187239c --- /dev/null +++ b/src/PopUp.java @@ -0,0 +1,51 @@ +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class PopUp extends JPopupMenu { + + JMenuItem delete,fav,call; + Contact contact; + + + public PopUp(Contact contact){ + this.contact = contact; + createGUI(); + + } + + private void createGUI(){ + delete = new JMenuItem("Delete"); + fav = new JMenuItem(); + if (contact.isFav()){ + fav.setText("Remove from favourites"); + } + else{ + fav.setText("Add to favourites"); + } + call = new JMenuItem("Call"); + + add(call); + add(fav); + add(delete); + + call.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + contact.call(); + } + }); + fav.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + contact.changeFav(); + } + }); + delete.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + contact.remove(); + } + }); + } +} \ No newline at end of file diff --git a/src/Protocol.java b/src/Protocol.java index 31747f5..a6fd570 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -11,7 +11,7 @@ public static String encode(String string){ } public static boolean isNickValid(String nick){ - if (nick.matches(".*[a-bA-B0-9_\\-].*")) { + if (nick.matches(".*[a-zA-Z0-9_\\-].*")) { return true; } return false; diff --git a/src/UltimateGUI.java b/src/UltimateGUI.java index a2a4d6e..4209c39 100644 --- a/src/UltimateGUI.java +++ b/src/UltimateGUI.java @@ -1,6 +1,8 @@ import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; public class UltimateGUI extends JFrame{ @@ -37,16 +39,30 @@ public void actionPerformed(ActionEvent e) { exit(); } }); + okButton.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode()==KeyEvent.VK_ENTER) + exit(); + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); - ///panel.add(Box.createVerticalGlue()); panel.add(label); - // panel.add(Box.createVerticalGlue()); panel.add(okButton); - // panel.add(Box.createVerticalGlue()); - //add(Box.createHorizontalGlue()); - add(panel); - //add(Box.createHorizontalGlue()); + + + } private void exit(){ diff --git a/src/images/off.png b/src/images/off.png new file mode 100644 index 0000000000000000000000000000000000000000..7420312f4f54b51ae15d589d0eed72711c42e7c4 GIT binary patch literal 3068 zcmVKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0003eNklhf(Hre2Z)!|K*+`Xu^%LO3bME!%m;W-M2L>Phs}f} zI$MV@>FJuDswvrQHgs?Rz5wAJ1{{HvFT|bASAgUpz_RI6{3k>-v(OQ09KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0003mNkl7xl5QM)yJ47J}C@Bz92%sQ0_>FlWBq|F90t$3VLBUUmfMZj{ zwrs;Og>~NBxxJYi84ibJFaurz;Tj6efo@XpGn*F&DS-f;xX+M$TtpRh9dbz~&x*v8 ze`48KV$^W<&h%EjTL--RvBnhR-k8bD1Z(VnmkOh|5&h3T;WW&)c1ML9kkiP}+0 zOzPINMD2*p^Cmalq$?Fxjg`|a6=hvr=X@~P1}oNO5&-+BJ&J>@qKMvpEm5Jit6|h! zlU0<~SBraSem8UfCQc5bLix8$un6$1m)<~B@T^C$2(E1Kka^5w?>*qANiKghbi6yw+N`a3o=WhU%>1}2c SNGC4<0000 Date: Thu, 3 Dec 2015 00:54:46 +0200 Subject: [PATCH 23/38] wip again. --- src/Contact.java | 7 ++++++ src/ContactPanel.java | 19 +++++++++++---- src/ContactsView.java | 39 ++++++++++++++++++++---------- src/ContactsViewModel.java | 26 +++++++++++++++++--- src/DisconnectCommandObserver.java | 18 -------------- src/LFrame.java | 8 +++--- src/Logic.java | 6 +++++ src/MessageCommandObserver.java | 21 ---------------- 8 files changed, 81 insertions(+), 63 deletions(-) delete mode 100644 src/DisconnectCommandObserver.java delete mode 100644 src/MessageCommandObserver.java diff --git a/src/Contact.java b/src/Contact.java index 496d143..9c5707f 100644 --- a/src/Contact.java +++ b/src/Contact.java @@ -3,6 +3,13 @@ public class Contact { private String nick,IP; private ContactsViewModel contactsViewModel; + public Contact(String nick,String IP){ + this.nick=nick; + this.IP=IP; + isFav=false; + isOnline=false; + } + public Contact(ContactsViewModel cvm,String nick,String IP){ contactsViewModel=cvm; this.nick=nick; diff --git a/src/ContactPanel.java b/src/ContactPanel.java index 0b5d17b..73a6647 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -8,12 +8,16 @@ public class ContactPanel extends JPanel{ private JLabel label; private ImageIcon imageIcon; - public ContactPanel(final Contact contact){ + public ContactPanel(Contact contact){ this.contact=contact; + createGUI(); + } + + private void createGUI(){ imageIcon = new ImageIcon("src/images/off.png"); label = new JLabel(contact.getNick()); label.setIcon(imageIcon); - setBackground(Color.getHSBColor(188, 0, 97)); + setBackground(Color.WHITE); addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { @@ -33,15 +37,16 @@ public void mouseReleased(MouseEvent e) { @Override public void mouseEntered(MouseEvent e) { - setBackground(Color.getHSBColor(188,32,100)); + setBackground(Color.CYAN); } @Override public void mouseExited(MouseEvent e) { - setBackground(Color.getHSBColor(188,0,97)); + setBackground(Color.WHITE); } }); - setMinimumSize(new Dimension(200,50)); + setMinimumSize(new Dimension(200,25)); + setMaximumSize(new Dimension(200,25)); add(label); } @@ -55,4 +60,8 @@ public void update(){ else label.setIcon(new ImageIcon("src/images/off.png")); } + public String getNick(){ + return contact.getNick(); + } + } diff --git a/src/ContactsView.java b/src/ContactsView.java index c5de7de..744df91 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -1,6 +1,7 @@ import javax.swing.*; import java.awt.*; -import java.util.ArrayList; +import java.util.*; +import java.util.Timer; public class ContactsView extends JPanel{ @@ -15,37 +16,42 @@ public ContactsView(ContactsViewModel cvm){ } private void createGUI(){ - + setBackground(Color.WHITE); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - setPreferredSize(new Dimension(200, 600)); + JPanel top = new JPanel(); + top.setBackground(Color.WHITE); top.setLayout(new BoxLayout(top,BoxLayout.Y_AXIS)); JPanel bottom = new JPanel(); + bottom.setBackground(Color.WHITE); bottom.setLayout(new BoxLayout(bottom,BoxLayout.Y_AXIS)); JLabel favouritesL = new JLabel("Favourites"); JLabel contactsL = new JLabel("Contacts"); Font font = new Font("Verdana",Font.BOLD,14); favouritesL.setFont(font); - favouritesL.setSize(new Dimension(200, 50)); + favouritesL.setSize(new Dimension(100, 50)); + favouritesL.setHorizontalTextPosition(0); contactsL.setFont(font); - contactsL.setSize(new Dimension(200, 50)); + contactsL.setSize(new Dimension(100, 50)); contacts = new JPanel(); + contacts.setBackground(Color.WHITE); contacts.setLayout(new BoxLayout(contacts,BoxLayout.Y_AXIS)); - contacts.setMinimumSize(new Dimension(200, 250)); + contacts.setMinimumSize(new Dimension(175, 150)); favourites = new JPanel(); favourites.setLayout(new BoxLayout(favourites,BoxLayout.Y_AXIS)); - favourites.setMinimumSize(new Dimension(200, 250)); + favourites.setMinimumSize(new Dimension(175, 150)); + favourites.setBackground(Color.WHITE); JScrollPane contactsS = new JScrollPane(contacts); - contactsS.setMinimumSize(new Dimension(200, 250)); + contactsS.setMinimumSize(new Dimension(175, 150)); JScrollPane favouritesS = new JScrollPane(favourites); - favouritesS.setMinimumSize(new Dimension(200, 250)); + favouritesS.setMinimumSize(new Dimension(175, 150)); - bottom.setPreferredSize(new Dimension(200,300)); - top.setPreferredSize(new Dimension(200, 300)); + bottom.setPreferredSize(new Dimension(175,200)); + top.setPreferredSize(new Dimension(175, 200)); top.add(favouritesL); top.add(favouritesS); @@ -55,7 +61,7 @@ private void createGUI(){ add(top); add(bottom); - setMinimumSize(new Dimension(200, 600)); + } @@ -66,15 +72,22 @@ public void fullUpdate(){ ArrayList contactsList = (ArrayList) contactsViewModel.getList(); for (Contact contact : contactsList){ list.add(new ContactPanel(contact)); + } for (ContactPanel cp : list){ + System.out.println(cp.getNick()); if (cp.isFav()) favourites.add(cp); else contacts.add(cp); } + updateUI(); + contactsViewModel.onlineUpdate(); + } public void onlineUpdate(){ - updateUI(); + for (ContactPanel cp : list){ + cp.update(); + } } diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index 882228a..3ddc2cb 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -1,8 +1,9 @@ +import javax.swing.*; import java.util.ArrayList; import java.util.Collection; import java.util.Timer; -public class ContactsViewModel implements Runnable { +public class ContactsViewModel{ ContactsView contactsView; ArrayList contactList = new ArrayList(); Logic logic; @@ -21,7 +22,9 @@ public Collection getList(){ public void call(Contact contact){ logic.getMainGui().changeEnterIp(contact.getIP()); + logic.setRemoteIP(contact.getIP()); logic.call(); + } public void removeContact(Contact contact){ @@ -33,8 +36,25 @@ public void updateView(){ contactsView.fullUpdate(); } + public void onlineUpdate(){ + ServerConnection serverConnection = logic.getServerConnection(); + for (Contact contact : contactList){ + if (serverConnection.isNickOnline(contact.getNick())){ + contact.setOnline(true); + } + } + contactsView.onlineUpdate(); + } - public void run() { - Timer timer = new Timer(); + public void getData(){ + ServerConnection serverConnection = logic.getServerConnection(); + String[] nicks = serverConnection.getAllNicks(); + for (String nick : nicks){ + if (nick.equals(logic.getLocalNick())) continue; + contactList.add(new Contact(this, nick, serverConnection.getIpForNick(nick))); + } + updateView(); } + + } diff --git a/src/DisconnectCommandObserver.java b/src/DisconnectCommandObserver.java deleted file mode 100644 index 40268fa..0000000 --- a/src/DisconnectCommandObserver.java +++ /dev/null @@ -1,18 +0,0 @@ -public class DisconnectCommandObserver extends CommandObserver { - private CommandType type = CommandType.DISCONNECT; - Connection connection; - - public DisconnectCommandObserver(CommandListenerThread clt, Connection connection){ - this.clt=clt; - this.connection=connection; - } - - public void update(Command command) { - //вывод в чат "remoteNick disconnected; - // - } - - public CommandType getType() { - return type; - } -} diff --git a/src/LFrame.java b/src/LFrame.java index 41025d2..44671cf 100644 --- a/src/LFrame.java +++ b/src/LFrame.java @@ -13,7 +13,7 @@ public class LFrame extends JFrame { Logic logic; private static final int Height = 600; - private static final int Widht = 800; + private static final int Widht = 1000; JButton Send; JButton Apply; @@ -71,6 +71,8 @@ public void keyReleased(KeyEvent e) { }); contactsView = new ContactsView(logic.getContactsViewModel()); + + contactsView.setBounds(0,20,175,400); // !!!!!!!!! // ВОТ ТУТ ЕЁ ^ НОРМАЛЬНО РАСПОЛОЖИ И В НЕЙ САМОЙ МОЖЕШЬ МЕНЯТЬ РАЗМЕРЫ. НО ТОЛЬКО РАЗМЕРЫ ПО ДРУГОМУ ПОВОДУ - СПРАШИВАЙ // !!!!!!!!! @@ -212,8 +214,8 @@ public void windowClosing(java.awt.event.WindowEvent windowEvent) { } }); - - + contactsView.setLocation(800,100); + panel.add(contactsView); panel.add(login); panel.add(textfieldlogin); panel.add(mass); diff --git a/src/Logic.java b/src/Logic.java index a9c3ce0..5ff330f 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -33,6 +33,8 @@ public Logic(){ serverConnection.connect(); serverConnection.setLocalNick(localNick); serverConnection.goOnline(); + + contactsViewModel.getData(); } public void setLocalNick(String nick){ @@ -196,4 +198,8 @@ public void exit(){ serverConnection.goOffline(); serverConnection.disconnect(); } + + public ServerConnection getServerConnection(){ + return serverConnection; + } } diff --git a/src/MessageCommandObserver.java b/src/MessageCommandObserver.java deleted file mode 100644 index 6946d02..0000000 --- a/src/MessageCommandObserver.java +++ /dev/null @@ -1,21 +0,0 @@ -public class MessageCommandObserver extends CommandObserver { - private CommandType type=CommandType.MESSAGE; - private MessageCommand messageCommand; - - //ссылка на форму с сообщениями или на её логическую часть - - public MessageCommandObserver(CommandListenerThread clt /*,непосредственно ссылка на ту форму*/){ - //задача формы - this.clt=clt; - clt.addCommandObserver(this); - } - - public void update(Command command) { - messageCommand = (MessageCommand) command; - //отправка на форму или лог. часть сообщения через ссылку - } - - public CommandType getType() { - return type; - } -} From 18582728f730aba5d8bf4c135aaa3a40858a3d27 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Thu, 3 Dec 2015 01:23:46 +0200 Subject: [PATCH 24/38] SERVER CONNECTION --- src/Logic.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Logic.java b/src/Logic.java index 5ff330f..1791667 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -195,8 +195,10 @@ public ContactsViewModel getContactsViewModel(){ public void exit(){ disconnect(); - serverConnection.goOffline(); - serverConnection.disconnect(); + if (serverConnection.isConnected()) { + serverConnection.goOffline(); + serverConnection.disconnect(); + } } public ServerConnection getServerConnection(){ From bcc7fc594d72953e9c33a51c45bc4a0e11668810 Mon Sep 17 00:00:00 2001 From: Artem Dyadik Date: Thu, 3 Dec 2015 07:14:34 +0200 Subject: [PATCH 25/38] Create LFrame --- LFrame | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 LFrame diff --git a/LFrame b/LFrame new file mode 100644 index 0000000..16319b8 --- /dev/null +++ b/LFrame @@ -0,0 +1,292 @@ +import java.awt.Color; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import javax.swing.*; +import javax.swing.border.LineBorder; + + +public class LFrame extends JFrame { + JPanel panel = new JPanel(); + Logic logic; + + private static final int Height = 600; + private static final int Widht = 1000; + + JButton Send; + JButton Apply; + JButton Disconnect; + JButton Connect; + + JLabel login; + JLabel addr; + HistoryView smth; + + JTextField textfieldlogin; + JTextField EnterIp; + JTextField mass; + + ContactsView contactsView; + + String text; + + Font font = new Font("Algerian", Font.BOLD, 13); + + LineBorder linebord = new LineBorder(Color.BLACK, 1); + + LFrame(final Logic logic){ + this.logic=logic; + + + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setBackground(Color.white); + + login = new JLabel("Login"); + login.setFont(font); + login.setBounds(10, 10, 50, 30); + textfieldlogin = new JTextField(logic.getLocalNick()); + textfieldlogin.setBounds(10, 40, 115, 20); + textfieldlogin.setBorder(linebord); + textfieldlogin.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode()==KeyEvent.VK_ENTER){ + applyNick(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); + + contactsView = new ContactsView(logic.getContactsViewModel()); + + contactsView.setBounds(0,20,175,400); + contactsView.setBorder(linebord); + + + addr = new JLabel("remote addr"); + addr.setFont(font); + addr.setBounds(600,10,100,30); + EnterIp = new JTextField("files.litvinov.in.ua"); + EnterIp.setBounds(600,40,150,20); + EnterIp.setBorder(linebord); + EnterIp.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + logic.setRemoteIP(EnterIp.getText()); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logic.getHistoryViewModel().clearView(); + logic.call(); + } + }); + } + } + + + @Override + public void keyReleased(KeyEvent e) { + + } + }); + + mass = new JTextField(); + mass.setHorizontalAlignment(JTextField.LEFT); + mass.setBorder(linebord); + mass.setBounds(10, 510, 636, 50); + mass.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode()==KeyEvent.VK_ENTER){ + send(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); + + + smth = new HistoryView(logic.getHistoryViewModel()); + smth.setLocation(48, 170); + smth.setSize(100,1000); + + + JScrollPane scrollPane = new JScrollPane(smth); + scrollPane.setLocation(10, 140); + scrollPane.setSize(740,350); + scrollPane.setBorder(linebord); + + Apply = new JButton("Apply"); + Apply.setLocation(10, 70); + Apply.setSize(115,25); + Apply.setFont(font); + Apply.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + applyNick(); + } + }); + + Disconnect = new JButton("Disconnect"); + Disconnect.setLocation(600,99); + Disconnect.setSize(150,25); + Disconnect.setFont(font); + Disconnect.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + logic.disconnect(); + } + }); + + + + Connect = new JButton("Connect"); + Connect.setLocation(600,70); + Connect.setSize(150, 25); + Connect.setFont(font); + Connect.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + logic.setRemoteIP(EnterIp.getText()); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logic.getHistoryViewModel().clearView(); + logic.call(); + + } + }); + } + }); + + Send = new JButton("Send"); + Send.setLocation(650, 510); + Send.setSize(100,50); + Send.addActionListener(new ActionListener( ) { + public void actionPerformed(ActionEvent ae) { + send(); + } + }); + + final JFrame frame = this; + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + if (logic.isConnected()){ + if (JOptionPane.showConfirmDialog(frame, + "You have an established connection with another user.\n Are you sure you want to close the program?", "Warning", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + exit(); + } + } + else{ + exit(); + } + + } + }); + contactsView.setLocation(765,20); + contactsView.setSize(220,540); + + panel.add(contactsView); + panel.add(login); + panel.add(textfieldlogin); + panel.add(mass); + panel.add(scrollPane); + panel.add(EnterIp); + panel.add(addr); + panel.add(Send); + panel.add(Apply); + panel.add(Disconnect); + panel.add(Connect); + + + this.add(panel); + + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + setSize(Widht, Height); + setResizable(false); + setLocationRelativeTo(null); + setTitle("ChatApp"); + setConnected(false); + setVisible(true); + + + } + + public void exit(){ + logic.exit(); + System.exit(0); + } + + public void send(){ + text = mass.getText(); + if (Protocol.isMessageValid(text)) { + logic.sendMessage(text); + logic.getHistoryViewModel().addLocalMessage(text); + mass.setText(""); + } + } + + public void applyNick(){ + text=textfieldlogin.getText().replaceAll("\\s+", "_").replaceAll("\\-+","-").replaceAll("_+","_"); + if (text.endsWith("_")) text=text.substring(0,text.length()-1); + textfieldlogin.setText(text); + if (Protocol.isNickValid(text))logic.setLocalNick(text); + else { + UltimateGUI ultimateGUI = new UltimateGUI("Only latin, numbers and \"_\" \"-\" "); + } + } + + public void setConnected(boolean b){ + if (b){ + Connect.setEnabled(false); + Apply.setEnabled(false); + Send.setEnabled(true); + Disconnect.setEnabled(true); + textfieldlogin.setEditable(false); + EnterIp.setEditable(false); + } + else { + Connect.setEnabled(true); + Apply.setEnabled(true); + Send.setEnabled(false); + Disconnect.setEnabled(false); + textfieldlogin.setEditable(true); + EnterIp.setEditable(true); + } + } + + public void changeEnterIp(String ip){ + EnterIp.setText(ip); + } + +} From 84e5448c5eaac68e6fe267a67f96c5a2c4df1385 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 9 Dec 2015 09:41:07 +0200 Subject: [PATCH 26/38] wip once again --- src/CommandListenerThread.java | 8 -------- src/CommandObserver.java | 12 ------------ src/Contact.java | 6 ++++-- src/ContactPanel.java | 10 +++++++++- src/ContactsView.java | 7 +++++++ src/ContactsViewModel.java | 4 ++-- src/PopUp.java | 8 +++++--- 7 files changed, 27 insertions(+), 28 deletions(-) delete mode 100644 src/CommandObserver.java diff --git a/src/CommandListenerThread.java b/src/CommandListenerThread.java index 86595e9..4ac5eb6 100644 --- a/src/CommandListenerThread.java +++ b/src/CommandListenerThread.java @@ -4,7 +4,6 @@ public class CommandListenerThread implements Runnable { private Command lastCommand; private Connection connection; - private HashMap observers; private boolean stop; Logic logic; @@ -29,14 +28,7 @@ public void run() { } - public void addCommandObserver(CommandObserver commandObserver){ - observers.put(commandObserver.getType(),commandObserver); - } - - public void removeCommandObserver(CommandObserver commandObserver){ - observers.remove(commandObserver.getType()); - } public void kill(){ stop = true; diff --git a/src/CommandObserver.java b/src/CommandObserver.java deleted file mode 100644 index e0136cb..0000000 --- a/src/CommandObserver.java +++ /dev/null @@ -1,12 +0,0 @@ -public abstract class CommandObserver{ - - protected CommandListenerThread clt; - - public void update(Command command) { - - } - - CommandType getType() { - return null; - } -} diff --git a/src/Contact.java b/src/Contact.java index 9c5707f..59c7237 100644 --- a/src/Contact.java +++ b/src/Contact.java @@ -60,7 +60,9 @@ public void call(){ contactsViewModel.call(this); } - public void remove(){ - contactsViewModel.removeContact(this); + public void remove(ContactPanel tmp){ + contactsViewModel.removeContact(this,tmp); + } + } diff --git a/src/ContactPanel.java b/src/ContactPanel.java index 73a6647..c727992 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -18,10 +18,10 @@ private void createGUI(){ label = new JLabel(contact.getNick()); label.setIcon(imageIcon); setBackground(Color.WHITE); + final PopUp menu = new PopUp(this); addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { - PopUp menu = new PopUp(contact); menu.show(e.getComponent(), e.getX(), e.getY()); } @@ -64,4 +64,12 @@ public String getNick(){ return contact.getNick(); } + public void remove(){ + contact.remove(this); + } + + public Contact getContact(){ + return contact; + } + } diff --git a/src/ContactsView.java b/src/ContactsView.java index 744df91..7b7f128 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -90,5 +90,12 @@ public void onlineUpdate(){ } } + public void delete(ContactPanel contact){ + if (contact.isFav()) favourites.remove(contact); + else contacts.remove(contact); + list.remove(contact); + updateUI(); + } + } diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index 3ddc2cb..3b2a6d1 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -27,9 +27,9 @@ public void call(Contact contact){ } - public void removeContact(Contact contact){ + public void removeContact(Contact contact, ContactPanel contactPanel){ contactList.remove(contact); - updateView(); + contactsView.delete(contactPanel); } public void updateView(){ diff --git a/src/PopUp.java b/src/PopUp.java index 187239c..1ff6041 100644 --- a/src/PopUp.java +++ b/src/PopUp.java @@ -6,10 +6,12 @@ public class PopUp extends JPopupMenu { JMenuItem delete,fav,call; Contact contact; + ContactPanel contactPanel; - public PopUp(Contact contact){ - this.contact = contact; + public PopUp(ContactPanel contact){ + contactPanel=contact; + this.contact = contact.getContact(); createGUI(); } @@ -44,7 +46,7 @@ public void actionPerformed(ActionEvent e) { delete.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - contact.remove(); + contact.remove(contactPanel); } }); } From 06a1dd0bfbdef39c86894a78565c062298fac176 Mon Sep 17 00:00:00 2001 From: Sergey Breus Date: Wed, 9 Dec 2015 13:20:31 +0300 Subject: [PATCH 27/38] I DON'T KNOW --- src/CallListener.java | 8 ++---- src/CallListenerThread.java | 3 +-- src/Contact.java | 26 ++++++++----------- src/ContactPanel.java | 4 --- src/ContactsView.java | 10 +++++-- src/ContactsViewModel.java | 25 +++++++++++------- src/HistoryViewModel.java | 4 --- src/LFrame.java | 14 +++------- src/Logic.java | 52 +++++++++++++++++++------------------ src/PopUp.java | 2 +- 10 files changed, 69 insertions(+), 79 deletions(-) diff --git a/src/CallListener.java b/src/CallListener.java index 61a50cc..60d0bb1 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -12,14 +12,14 @@ public class CallListener { private Command lastCommand; private NickCommand nickCommand; - public CallListener(String localNick, boolean isBusy){ + public CallListener(String localNick){ try { serverSocket = new ServerSocket(Protocol.PORT_NUMBER); } catch (IOException e) { //asd } this.localNick=localNick; - this.isBusy=isBusy; + this.isBusy= false; } public Connection getConnection() throws IOException { @@ -51,10 +51,6 @@ public void setNick(String nick){ localNick=nick; } - public boolean getBusy(){ - return isBusy; - } - public String getRemoteNick(){ return remoteNick; } diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index 3e5d7f5..8c705e0 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -1,5 +1,4 @@ import java.io.IOException; -import java.sql.*; public class CallListenerThread implements Runnable { @@ -17,7 +16,7 @@ public class CallListenerThread implements Runnable { public CallListenerThread(String localNick,Boolean isBusy,Logic logic){ - callListener = new CallListener(localNick, false); + callListener = new CallListener(localNick); this.logic = logic; } diff --git a/src/Contact.java b/src/Contact.java index 59c7237..b4f8618 100644 --- a/src/Contact.java +++ b/src/Contact.java @@ -3,13 +3,6 @@ public class Contact { private String nick,IP; private ContactsViewModel contactsViewModel; - public Contact(String nick,String IP){ - this.nick=nick; - this.IP=IP; - isFav=false; - isOnline=false; - } - public Contact(ContactsViewModel cvm,String nick,String IP){ contactsViewModel=cvm; this.nick=nick; @@ -22,10 +15,6 @@ public boolean isFav() { return isFav; } - public void setFav(boolean isFav) { - this.isFav = isFav; - } - public boolean isOnline() { return isOnline; } @@ -50,10 +39,17 @@ public void setIP(String IP) { this.IP = IP; } - public void changeFav(){ - if (isFav) isFav=false; - else isFav=true; - contactsViewModel.updateView(); + public void changeFav(ContactPanel contactPanel){ + if (isFav){ + contactsViewModel.removeContact(this,contactPanel); + isFav=false; + contactsViewModel.add(this); + } + else { + contactsViewModel.removeContact(this,contactPanel); + isFav=true; + contactsViewModel.add(this); + } } public void call(){ diff --git a/src/ContactPanel.java b/src/ContactPanel.java index c727992..ac2977d 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -64,10 +64,6 @@ public String getNick(){ return contact.getNick(); } - public void remove(){ - contact.remove(this); - } - public Contact getContact(){ return contact; } diff --git a/src/ContactsView.java b/src/ContactsView.java index 7b7f128..674f056 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -75,12 +75,11 @@ public void fullUpdate(){ } for (ContactPanel cp : list){ - System.out.println(cp.getNick()); if (cp.isFav()) favourites.add(cp); else contacts.add(cp); } updateUI(); - contactsViewModel.onlineUpdate(); + // contactsViewModel.onlineUpdate(); } @@ -97,5 +96,12 @@ public void delete(ContactPanel contact){ updateUI(); } + public void addContact(Contact contact){ + ContactPanel cp = new ContactPanel(contact); + list.add(cp); + if (cp.isFav()) favourites.add(cp); + else contacts.add(cp); + } + } diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index 3b2a6d1..5ffa201 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -21,9 +21,7 @@ public Collection getList(){ } public void call(Contact contact){ - logic.getMainGui().changeEnterIp(contact.getIP()); - logic.setRemoteIP(contact.getIP()); - logic.call(); + logic.call(contact.getIP()); } @@ -47,14 +45,23 @@ public void onlineUpdate(){ } public void getData(){ - ServerConnection serverConnection = logic.getServerConnection(); - String[] nicks = serverConnection.getAllNicks(); - for (String nick : nicks){ - if (nick.equals(logic.getLocalNick())) continue; - contactList.add(new Contact(this, nick, serverConnection.getIpForNick(nick))); - } + // ServerConnection serverConnection = logic.getServerConnection(); + // String[] nicks = serverConnection.getAllNicks(); + // for (String nick : nicks){ + // if (nick.equals(logic.getLocalNick())) continue; + // contactList.add(new Contact(this, nick, serverConnection.getIpForNick(nick))); + // } + contactList.add(new Contact(this,"Vasy","234")); + contactList.add(new Contact(this,"Vasert","2384")); + contactList.add(new Contact(this,"Vautr","2334")); + contactList.add(new Contact(this,"Vaityw","2234")); updateView(); } + public void add(Contact contact){ + contactList.add(contact); + contactsView.addContact(contact); + } + } diff --git a/src/HistoryViewModel.java b/src/HistoryViewModel.java index 03f07b4..f2df7b1 100644 --- a/src/HistoryViewModel.java +++ b/src/HistoryViewModel.java @@ -11,10 +11,6 @@ public class HistoryViewModel { HistoryView historyView; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk:mm:ss dd.MM.yy]"); - public HistoryViewModel(){ - - } - public HistoryViewModel(Logic logic){ this.logic = logic; } diff --git a/src/LFrame.java b/src/LFrame.java index 44671cf..f42eac7 100644 --- a/src/LFrame.java +++ b/src/LFrame.java @@ -92,12 +92,11 @@ public void keyTyped(KeyEvent e) { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { - logic.setRemoteIP(EnterIp.getText()); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { logic.getHistoryViewModel().clearView(); - logic.call(); + logic.call(EnterIp.getText()); } }); } @@ -175,15 +174,8 @@ public void actionPerformed(ActionEvent e) { Connect.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - logic.setRemoteIP(EnterIp.getText()); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - logic.getHistoryViewModel().clearView(); - logic.call(); - - } - }); + logic.getHistoryViewModel().clearView(); + logic.call(EnterIp.getText()); } }); diff --git a/src/Logic.java b/src/Logic.java index 1791667..2075261 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -1,3 +1,4 @@ +import javax.swing.*; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; @@ -22,6 +23,7 @@ public Logic(){ else localNick="default"; callListenerThread = new CallListenerThread(localNick,isBusy,this); callThread = new Thread(callListenerThread); + callThread.setDaemon(true); callThread.start(); mainGui = new LFrame(this); try { @@ -29,10 +31,10 @@ public Logic(){ } catch (ClassNotFoundException e) { e.printStackTrace(); } - serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); - serverConnection.connect(); - serverConnection.setLocalNick(localNick); - serverConnection.goOnline(); + // serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); + // serverConnection.connect(); + // serverConnection.setLocalNick(localNick); + // serverConnection.goOnline(); contactsViewModel.getData(); } @@ -41,7 +43,6 @@ public void setLocalNick(String nick){ localNick=nick; callListenerThread.setNick(localNick); serverConnection.setLocalNick(localNick); - writeNickToFile(); } public void setRemoteNick(String nick){ @@ -72,33 +73,41 @@ public void accept(Connection connection){ } - public void reject(){ - connection.reject(); - - } public void sendMessage(String message){ connection.sendMessage(message); } - public void call(){ + public void call(String IP) { + if (isConnected()){ + if (JOptionPane.showConfirmDialog(mainGui, + "You have an established connection with another user.\n If you will call this contact, current connection will be closed.\nAre you sure you want to do this?", "Warning", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + disconnect(); + } + + else{ + return; + }} + mainGui.changeEnterIp(IP); + setRemoteIP(IP); mainGui.setConnected(true); setBusy(true); - caller = new Caller(localNick,remoteIP,this); + caller = new Caller(localNick, remoteIP, this); try { connection = caller.call(); } catch (IOException e) { UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); } - if (connection!=null){ - remoteNick=caller.getRemoteNick(); - commandListenerThread = new CommandListenerThread(connection,this); + if (connection != null) { + remoteNick = caller.getRemoteNick(); + commandListenerThread = new CommandListenerThread(connection, this); Thread comThread = new Thread(commandListenerThread); comThread.start(); - historyViewModel.addSystemMessage("Connected to "+remoteNick); + historyViewModel.addSystemMessage("Connected to " + remoteNick); System.out.println("CONNECTED, YEY"); - } - else{ + } else { mainGui.setConnected(false); setBusy(false); @@ -124,14 +133,6 @@ public void disconnect() { } - public boolean isBusy(){ - return isBusy; - } - - public void updateGUI(){ - // - } - public String getLocalNick(){ return localNick; } @@ -199,6 +200,7 @@ public void exit(){ serverConnection.goOffline(); serverConnection.disconnect(); } + writeNickToFile(); } public ServerConnection getServerConnection(){ diff --git a/src/PopUp.java b/src/PopUp.java index 1ff6041..4b4f0aa 100644 --- a/src/PopUp.java +++ b/src/PopUp.java @@ -40,7 +40,7 @@ public void actionPerformed(ActionEvent e) { fav.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - contact.changeFav(); + contact.changeFav(contactPanel); } }); delete.addActionListener(new ActionListener() { From 1ee6b986bb1dd663c62fa2ab6568583eae1ade24 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Wed, 9 Dec 2015 23:44:28 +0200 Subject: [PATCH 28/38] Well, I still don't know how to do a jar with mySQL driver in it, so here's just the last commit without a new release. --- LFrame | 292 ------------------- iteration1.1 | 153 ---------- src/CallListener.java | 11 +- src/CallListenerThread.java | 12 +- src/Contact.java | 10 + src/ContactPanel.java | 8 +- src/ContactsView.java | 9 +- src/ContactsViewModel.java | 68 ++++- src/{AccorDis.java => IncomingCallForm.java} | 4 +- src/Logic.java | 82 +++++- src/{LFrame.java => MainGUI.java} | 117 ++++++-- src/NewContactFrame.java | 80 +++++ src/Options.java | 5 + src/OptionsFrame.java | 113 +++++++ 14 files changed, 457 insertions(+), 507 deletions(-) delete mode 100644 LFrame delete mode 100644 iteration1.1 rename src/{AccorDis.java => IncomingCallForm.java} (93%) rename src/{LFrame.java => MainGUI.java} (65%) create mode 100644 src/NewContactFrame.java create mode 100644 src/Options.java create mode 100644 src/OptionsFrame.java diff --git a/LFrame b/LFrame deleted file mode 100644 index 16319b8..0000000 --- a/LFrame +++ /dev/null @@ -1,292 +0,0 @@ -import java.awt.Color; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import javax.swing.*; -import javax.swing.border.LineBorder; - - -public class LFrame extends JFrame { - JPanel panel = new JPanel(); - Logic logic; - - private static final int Height = 600; - private static final int Widht = 1000; - - JButton Send; - JButton Apply; - JButton Disconnect; - JButton Connect; - - JLabel login; - JLabel addr; - HistoryView smth; - - JTextField textfieldlogin; - JTextField EnterIp; - JTextField mass; - - ContactsView contactsView; - - String text; - - Font font = new Font("Algerian", Font.BOLD, 13); - - LineBorder linebord = new LineBorder(Color.BLACK, 1); - - LFrame(final Logic logic){ - this.logic=logic; - - - JPanel panel = new JPanel(); - panel.setLayout(null); - panel.setBackground(Color.white); - - login = new JLabel("Login"); - login.setFont(font); - login.setBounds(10, 10, 50, 30); - textfieldlogin = new JTextField(logic.getLocalNick()); - textfieldlogin.setBounds(10, 40, 115, 20); - textfieldlogin.setBorder(linebord); - textfieldlogin.addKeyListener(new KeyListener() { - - @Override - public void keyTyped(KeyEvent e) { - - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode()==KeyEvent.VK_ENTER){ - applyNick(); - } - } - - @Override - public void keyReleased(KeyEvent e) { - - } - }); - - contactsView = new ContactsView(logic.getContactsViewModel()); - - contactsView.setBounds(0,20,175,400); - contactsView.setBorder(linebord); - - - addr = new JLabel("remote addr"); - addr.setFont(font); - addr.setBounds(600,10,100,30); - EnterIp = new JTextField("files.litvinov.in.ua"); - EnterIp.setBounds(600,40,150,20); - EnterIp.setBorder(linebord); - EnterIp.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { - - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - logic.setRemoteIP(EnterIp.getText()); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - logic.getHistoryViewModel().clearView(); - logic.call(); - } - }); - } - } - - - @Override - public void keyReleased(KeyEvent e) { - - } - }); - - mass = new JTextField(); - mass.setHorizontalAlignment(JTextField.LEFT); - mass.setBorder(linebord); - mass.setBounds(10, 510, 636, 50); - mass.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { - - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode()==KeyEvent.VK_ENTER){ - send(); - } - } - - @Override - public void keyReleased(KeyEvent e) { - - } - }); - - - smth = new HistoryView(logic.getHistoryViewModel()); - smth.setLocation(48, 170); - smth.setSize(100,1000); - - - JScrollPane scrollPane = new JScrollPane(smth); - scrollPane.setLocation(10, 140); - scrollPane.setSize(740,350); - scrollPane.setBorder(linebord); - - Apply = new JButton("Apply"); - Apply.setLocation(10, 70); - Apply.setSize(115,25); - Apply.setFont(font); - Apply.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - applyNick(); - } - }); - - Disconnect = new JButton("Disconnect"); - Disconnect.setLocation(600,99); - Disconnect.setSize(150,25); - Disconnect.setFont(font); - Disconnect.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - logic.disconnect(); - } - }); - - - - Connect = new JButton("Connect"); - Connect.setLocation(600,70); - Connect.setSize(150, 25); - Connect.setFont(font); - Connect.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - logic.setRemoteIP(EnterIp.getText()); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - logic.getHistoryViewModel().clearView(); - logic.call(); - - } - }); - } - }); - - Send = new JButton("Send"); - Send.setLocation(650, 510); - Send.setSize(100,50); - Send.addActionListener(new ActionListener( ) { - public void actionPerformed(ActionEvent ae) { - send(); - } - }); - - final JFrame frame = this; - addWindowListener(new java.awt.event.WindowAdapter() { - @Override - public void windowClosing(java.awt.event.WindowEvent windowEvent) { - if (logic.isConnected()){ - if (JOptionPane.showConfirmDialog(frame, - "You have an established connection with another user.\n Are you sure you want to close the program?", "Warning", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - exit(); - } - } - else{ - exit(); - } - - } - }); - contactsView.setLocation(765,20); - contactsView.setSize(220,540); - - panel.add(contactsView); - panel.add(login); - panel.add(textfieldlogin); - panel.add(mass); - panel.add(scrollPane); - panel.add(EnterIp); - panel.add(addr); - panel.add(Send); - panel.add(Apply); - panel.add(Disconnect); - panel.add(Connect); - - - this.add(panel); - - setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - setSize(Widht, Height); - setResizable(false); - setLocationRelativeTo(null); - setTitle("ChatApp"); - setConnected(false); - setVisible(true); - - - } - - public void exit(){ - logic.exit(); - System.exit(0); - } - - public void send(){ - text = mass.getText(); - if (Protocol.isMessageValid(text)) { - logic.sendMessage(text); - logic.getHistoryViewModel().addLocalMessage(text); - mass.setText(""); - } - } - - public void applyNick(){ - text=textfieldlogin.getText().replaceAll("\\s+", "_").replaceAll("\\-+","-").replaceAll("_+","_"); - if (text.endsWith("_")) text=text.substring(0,text.length()-1); - textfieldlogin.setText(text); - if (Protocol.isNickValid(text))logic.setLocalNick(text); - else { - UltimateGUI ultimateGUI = new UltimateGUI("Only latin, numbers and \"_\" \"-\" "); - } - } - - public void setConnected(boolean b){ - if (b){ - Connect.setEnabled(false); - Apply.setEnabled(false); - Send.setEnabled(true); - Disconnect.setEnabled(true); - textfieldlogin.setEditable(false); - EnterIp.setEditable(false); - } - else { - Connect.setEnabled(true); - Apply.setEnabled(true); - Send.setEnabled(false); - Disconnect.setEnabled(false); - textfieldlogin.setEditable(true); - EnterIp.setEditable(true); - } - } - - public void changeEnterIp(String ip){ - EnterIp.setText(ip); - } - -} diff --git a/iteration1.1 b/iteration1.1 deleted file mode 100644 index d98746d..0000000 --- a/iteration1.1 +++ /dev/null @@ -1,153 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -/** - * - * @author User - */ -public class Wind extends javax.swing.JInternalFrame { - - /** - * Creates new form Wind - */ - public Wind() { - initComponents(); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jTextField1 = new javax.swing.JTextField(); - jButton1 = new javax.swing.JButton(); - jButton2 = new javax.swing.JButton(); - jButton3 = new javax.swing.JButton(); - jButton4 = new javax.swing.JButton(); - jTextField2 = new javax.swing.JTextField(); - jTextField3 = new javax.swing.JTextField(); - jLabel1 = new javax.swing.JLabel(); - jLabel2 = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); - jTextField4 = new javax.swing.JTextField(); - jTextField5 = new javax.swing.JTextField(); - - jTextField1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jTextField1ActionPerformed(evt); - } - }); - - jButton1.setText("Send"); - - jButton2.setText("Apply"); - - jButton3.setText("Connect"); - - jButton4.setText("Disconnect"); - - jTextField3.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jTextField3ActionPerformed(evt); - } - }); - - jLabel1.setText("local login"); - - jLabel2.setText("remote login"); - - jLabel3.setText("remore addr"); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jTextField5) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(jTextField1) - .addGap(18, 18, 18) - .addComponent(jButton1)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 233, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 85, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton4))))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton4) - .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1) - .addComponent(jLabel2) - .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton3) - .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jButton2) - .addComponent(jLabel3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jTextField5, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton1) - .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextField1ActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_jTextField1ActionPerformed - - private void jTextField3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextField3ActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_jTextField3ActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton jButton1; - private javax.swing.JButton jButton2; - private javax.swing.JButton jButton3; - private javax.swing.JButton jButton4; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JTextField jTextField1; - private javax.swing.JTextField jTextField2; - private javax.swing.JTextField jTextField3; - private javax.swing.JTextField jTextField4; - private javax.swing.JTextField jTextField5; - // End of variables declaration//GEN-END:variables -} diff --git a/src/CallListener.java b/src/CallListener.java index 60d0bb1..caa3e4e 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -5,7 +5,7 @@ public class CallListener { private String localNick,remoteNick; - private boolean isBusy; + private boolean isBusy,isOnline; private String remoteIP; private int remotePort; private ServerSocket serverSocket; @@ -20,10 +20,15 @@ public CallListener(String localNick){ } this.localNick=localNick; this.isBusy= false; + this.isOnline=true; } public Connection getConnection() throws IOException { Connection connection = new Connection(serverSocket.accept()); + if (!isOnline){ + connection.disconnect(); + return null; + } if (!isBusy){ connection.sendNickHello(localNick); lastCommand=connection.recieve(); @@ -54,4 +59,8 @@ public void setNick(String nick){ public String getRemoteNick(){ return remoteNick; } + + public void setOnline(Boolean online){ + isOnline=online; + } } diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index 8c705e0..9c93993 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -4,13 +4,10 @@ public class CallListenerThread implements Runnable { private String localNick; private CallListener callListener; - private boolean isBusy; - private String remoteIP; - private String lastAction; private boolean stop; private String remoteNick; private Connection remoteConnection; - private AccorDis form; + private IncomingCallForm form; Logic logic; @@ -27,7 +24,7 @@ public void run() { remoteConnection = callListener.getConnection(); if (remoteConnection == null) continue; remoteNick=callListener.getRemoteNick(); - form = new AccorDis(remoteConnection,logic,remoteNick); + form = new IncomingCallForm(remoteConnection,logic,remoteNick); System.out.println("AZAZA"); logic.setRemoteNick(remoteNick); } @@ -45,9 +42,12 @@ public Connection getRemoteConnection(){ } public void setBusy(Boolean isBusy){ - this.isBusy=isBusy; callListener.setBusy(isBusy); } + + public void setOnline(Boolean online){ + callListener.setOnline(online); + } public void setNick(String nick){ localNick=nick; diff --git a/src/Contact.java b/src/Contact.java index b4f8618..15b79d9 100644 --- a/src/Contact.java +++ b/src/Contact.java @@ -1,3 +1,4 @@ + public class Contact { private boolean isFav,isOnline; private String nick,IP; @@ -58,7 +59,16 @@ public void call(){ public void remove(ContactPanel tmp){ contactsViewModel.removeContact(this,tmp); + } + + public boolean equals(Object object){ + Contact tmp = (Contact) object; + if (nick.equals(tmp.nick) && getIP().equals(tmp.getIP())) return true; + else return false; + } + public void setFav(Boolean b){ + isFav=b; } } diff --git a/src/ContactPanel.java b/src/ContactPanel.java index ac2977d..689c416 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -14,7 +14,13 @@ public ContactPanel(Contact contact){ } private void createGUI(){ - imageIcon = new ImageIcon("src/images/off.png"); + setToolTipText(contact.getIP()); + if (contact.isOnline()){ + imageIcon = new ImageIcon("src/images/on.png"); + } + else { + imageIcon = new ImageIcon("src/images/off.png"); + } label = new JLabel(contact.getNick()); label.setIcon(imageIcon); setBackground(Color.WHITE); diff --git a/src/ContactsView.java b/src/ContactsView.java index 674f056..5bb12bd 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -30,10 +30,10 @@ private void createGUI(){ JLabel contactsL = new JLabel("Contacts"); Font font = new Font("Verdana",Font.BOLD,14); favouritesL.setFont(font); - favouritesL.setSize(new Dimension(100, 50)); - favouritesL.setHorizontalTextPosition(0); + //favouritesL.setSize(new Dimension(50, 50)); + //favouritesL.setHorizontalTextPosition(0); contactsL.setFont(font); - contactsL.setSize(new Dimension(100, 50)); + //contactsL.setSize(new Dimension(50, 50)); contacts = new JPanel(); contacts.setBackground(Color.WHITE); @@ -79,7 +79,7 @@ public void fullUpdate(){ else contacts.add(cp); } updateUI(); - // contactsViewModel.onlineUpdate(); + contactsViewModel.onlineUpdate(); } @@ -101,6 +101,7 @@ public void addContact(Contact contact){ list.add(cp); if (cp.isFav()) favourites.add(cp); else contacts.add(cp); + updateUI(); } diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index 5ffa201..65e09db 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -1,6 +1,9 @@ import javax.swing.*; -import java.util.ArrayList; -import java.util.Collection; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; import java.util.Timer; public class ContactsViewModel{ @@ -10,6 +13,19 @@ public class ContactsViewModel{ public ContactsViewModel(Logic logic){ this.logic=logic; + Thread onlineChecker = new Thread(new Runnable() { + @Override + public void run() { + try { + while (true) { + wait(20000); + onlineUpdate(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); } public void setContactsView(ContactsView cv){ @@ -45,16 +61,14 @@ public void onlineUpdate(){ } public void getData(){ - // ServerConnection serverConnection = logic.getServerConnection(); - // String[] nicks = serverConnection.getAllNicks(); - // for (String nick : nicks){ - // if (nick.equals(logic.getLocalNick())) continue; - // contactList.add(new Contact(this, nick, serverConnection.getIpForNick(nick))); - // } - contactList.add(new Contact(this,"Vasy","234")); - contactList.add(new Contact(this,"Vasert","2384")); - contactList.add(new Contact(this,"Vautr","2334")); - contactList.add(new Contact(this,"Vaityw","2234")); + ServerConnection serverConnection = logic.getServerConnection(); + String[] nicks = serverConnection.getAllNicks(); + for (String nick : nicks){ + if (nick.equals(logic.getLocalNick())) continue; + Contact c = new Contact(this, nick, serverConnection.getIpForNick(nick)); + if (contactList.contains(c)) continue; + contactList.add(c); + } updateView(); } @@ -63,5 +77,35 @@ public void add(Contact contact){ contactsView.addContact(contact); } + public void writeToFile() { + FileWriter out = null; + try { + out = new FileWriter("Contacts.txt"); + for (Contact c : contactList){ + out.write(new StringBuilder(c.getNick()).append(" ").append(c.getIP()).append(" ").append(c.isFav()).append("\n").toString()); + out.flush(); + } + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void readFromFile(){ + Scanner in = null; + try{ + in = new Scanner(new FileReader("Contacts.txt")); + String[] tmp; + while (in.hasNextLine()){ + tmp = in.nextLine().split(" "); + Contact c = new Contact(this,tmp[0],tmp[1]); + if (tmp[2].equals("true")) c.setFav(true); + contactList.add(c); + } + in.close(); + } catch (IOException e){ + System.out.println("Failed to find a file Contacts.txt"); + } + } } diff --git a/src/AccorDis.java b/src/IncomingCallForm.java similarity index 93% rename from src/AccorDis.java rename to src/IncomingCallForm.java index d07a6c3..7a07c01 100644 --- a/src/AccorDis.java +++ b/src/IncomingCallForm.java @@ -10,7 +10,7 @@ -public class AccorDis extends JFrame { +public class IncomingCallForm extends JFrame { private static final int Height = 150; private static final int Widht = 300; @@ -28,7 +28,7 @@ public void close(){ - AccorDis(final Connection connection, final Logic logic, String username){ + IncomingCallForm(final Connection connection, final Logic logic, String username){ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setSize(Widht, Height); setResizable(false); diff --git a/src/Logic.java b/src/Logic.java index 2075261..ff9d1bc 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -3,11 +3,12 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.Scanner; public class Logic{ - private LFrame mainGui; + private MainGUI mainGui; private String localNick = "default",remoteNick,remoteIP; - private boolean isBusy; + private boolean isBusy,isOnline; private Connection connection = null; private Caller caller; private CallListenerThread callListenerThread; @@ -18,6 +19,7 @@ public class Logic{ private ContactsViewModel contactsViewModel = new ContactsViewModel(this); public Logic(){ + readOptions(); String tmp = getNickFromFile(); if (tmp!=null) localNick=tmp; else localNick="default"; @@ -25,18 +27,23 @@ public Logic(){ callThread = new Thread(callListenerThread); callThread.setDaemon(true); callThread.start(); - mainGui = new LFrame(this); + mainGui = new MainGUI(this); try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } - // serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); - // serverConnection.connect(); - // serverConnection.setLocalNick(localNick); - // serverConnection.goOnline(); + isOnline=true; + serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); + serverConnection.connect(); + serverConnection.setLocalNick(localNick); + serverConnection.goOnline(); + contactsViewModel.readFromFile(); contactsViewModel.getData(); + + + } public void setLocalNick(String nick){ @@ -79,6 +86,11 @@ public void sendMessage(String message){ } public void call(String IP) { + if (!isOnline){ + UltimateGUI ultimateGUI = new UltimateGUI("You cannot call anyone, you're Offline!"); + return; + } + if (isConnected()){ if (JOptionPane.showConfirmDialog(mainGui, "You have an established connection with another user.\n If you will call this contact, current connection will be closed.\nAre you sure you want to do this?", "Warning", @@ -122,7 +134,7 @@ public void disconnect() { if (isConnected()) { setBusy(false); historyViewModel.addSystemMessage("Disconnected"); - historyViewModel.writeHistoryFile(); + if (Options.saveHistory) historyViewModel.writeHistoryFile(); remoteNick = null; remoteIP = null; commandListenerThread.kill(); @@ -145,7 +157,7 @@ public HistoryViewModel getHistoryViewModel(){ return historyViewModel; } - public LFrame getMainGui(){ + public MainGUI getMainGui(){ return mainGui; } @@ -200,7 +212,57 @@ public void exit(){ serverConnection.goOffline(); serverConnection.disconnect(); } - writeNickToFile(); + if (Options.saveNick) writeNickToFile(); + if (Options.saveContacts) contactsViewModel.writeToFile(); + writeOptions(); + } + + public void setOnline(boolean online){ + isOnline=online; + if (isOnline){ + callListenerThread.setOnline(true); + mainGui.setConnected(false); + } + else{ + callListenerThread.setOnline(false); + mainGui.setOffline(); + } + } + + public boolean isOnline(){ + return isOnline; + } + + public void writeOptions(){ + FileWriter out; + try{ + out = new FileWriter("Options.txt"); + if (Options.saveNick) out.write("true "); + else out.write("false "); + if (Options.saveContacts) out.write("true "); + else out.write("false "); + if (Options.saveHistory) out.write("true"); + else out.write("false"); + out.close(); + } + catch (IOException e){ + e.printStackTrace(); + } + } + + public void readOptions(){ + Scanner in; + try{ + in = new Scanner(new FileReader("Options.txt")); + String[] tmp = in.nextLine().split(" "); + if (tmp[0].equals("false")) Options.saveNick=false; + if (tmp[1].equals("false")) Options.saveContacts=false; + if (tmp[2].equals("false")) Options.saveHistory=false; + in.close(); + } + catch (IOException e){ + e.printStackTrace(); + } } public ServerConnection getServerConnection(){ diff --git a/src/LFrame.java b/src/MainGUI.java similarity index 65% rename from src/LFrame.java rename to src/MainGUI.java index f42eac7..0136116 100644 --- a/src/LFrame.java +++ b/src/MainGUI.java @@ -1,5 +1,4 @@ -import java.awt.Color; -import java.awt.Font; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -8,7 +7,7 @@ import javax.swing.border.LineBorder; -public class LFrame extends JFrame { +public class MainGUI extends JFrame { JPanel panel = new JPanel(); Logic logic; @@ -19,14 +18,17 @@ public class LFrame extends JFrame { JButton Apply; JButton Disconnect; JButton Connect; + JButton AddNewContact; + JButton Options; + JToggleButton toggleOffOnline; JLabel login; - JLabel addr; - HistoryView smth; + JLabel remoteAdress; + HistoryView historyView; JTextField textfieldlogin; JTextField EnterIp; - JTextField mass; + JTextField messageArea; ContactsView contactsView; @@ -36,7 +38,7 @@ public class LFrame extends JFrame { LineBorder linebord = new LineBorder(Color.BLACK, 1); - LFrame(final Logic logic){ + MainGUI(final Logic logic){ this.logic=logic; @@ -73,13 +75,11 @@ public void keyReleased(KeyEvent e) { contactsView = new ContactsView(logic.getContactsViewModel()); contactsView.setBounds(0,20,175,400); - // !!!!!!!!! - // ВОТ ТУТ ЕЁ ^ НОРМАЛЬНО РАСПОЛОЖИ И В НЕЙ САМОЙ МОЖЕШЬ МЕНЯТЬ РАЗМЕРЫ. НО ТОЛЬКО РАЗМЕРЫ ПО ДРУГОМУ ПОВОДУ - СПРАШИВАЙ - // !!!!!!!!! - addr = new JLabel("remote addr"); - addr.setFont(font); - addr.setBounds(400,35,100,50); + + remoteAdress = new JLabel("remote remoteAdress"); + remoteAdress.setFont(font); + remoteAdress.setBounds(400,35,100,50); EnterIp = new JTextField("files.litvinov.in.ua"); EnterIp.setBounds(500,40,150,30); EnterIp.setBorder(linebord); @@ -108,12 +108,22 @@ public void keyReleased(KeyEvent e) { } }); + + Options = new JButton("Options"); + Options.setLocation(820,60); + Options.setSize(150,25); + Options.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + OptionsFrame opf = new OptionsFrame(); + } + }); - mass = new JTextField(); - mass.setHorizontalAlignment(JTextField.LEFT); - mass.setBorder(linebord); - mass.setBounds(10, 480, 650, 75); - mass.addKeyListener(new KeyListener() { + messageArea = new JTextField(); + messageArea.setHorizontalAlignment(JTextField.LEFT); + messageArea.setBorder(linebord); + messageArea.setBounds(10, 480, 650, 75); + messageArea.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { @@ -133,15 +143,54 @@ public void keyReleased(KeyEvent e) { }); - smth = new HistoryView(logic.getHistoryViewModel()); - smth.setLocation(10, 140); - smth.setSize(740,300); + historyView = new HistoryView(logic.getHistoryViewModel()); + historyView.setLocation(10, 140); + historyView.setSize(740,300); + toggleOffOnline = new JToggleButton(); + + toggleOffOnline.setLocation(200,48); + toggleOffOnline.setSize(17, 17); + toggleOffOnline.setBorderPainted(false); + toggleOffOnline.setFocusable(false); + toggleOffOnline.setBorder(null); + toggleOffOnline.setMargin(new Insets(0, 0, 0, 0)); + toggleOffOnline.setPressedIcon(new ImageIcon("src/images/off.png")); + toggleOffOnline.setDisabledIcon(new ImageIcon("src/images/off.png")); + toggleOffOnline.setContentAreaFilled(false); + toggleOffOnline.setFocusPainted(false); + toggleOffOnline.setIcon(new ImageIcon("src/images/on.png")); + + toggleOffOnline.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (logic.isOnline()) { + toggleOffOnline.setIcon(new ImageIcon("src/images/off.png")); + toggleOffOnline.setPressedIcon(new ImageIcon("src/images/on.png")); + logic.setOnline(false); + } + else{ + toggleOffOnline.setIcon(new ImageIcon("src/images/on.png")); + toggleOffOnline.setPressedIcon(new ImageIcon("src/images/off.png")); + logic.setOnline(true); + } + } + }); - JScrollPane scrollPane = new JScrollPane(smth); + JScrollPane scrollPane = new JScrollPane(historyView); scrollPane.setLocation(10, 140); scrollPane.setSize(740,300); scrollPane.setBorder(linebord); + + AddNewContact = new JButton("Add new Contact"); + AddNewContact.setLocation(820,30); + AddNewContact.setSize(150,25); + AddNewContact.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + NewContactFrame ncf = new NewContactFrame(logic.getContactsViewModel()); + } + }); Apply = new JButton("Apply"); Apply.setLocation(80, 80); @@ -210,14 +259,17 @@ public void windowClosing(java.awt.event.WindowEvent windowEvent) { panel.add(contactsView); panel.add(login); panel.add(textfieldlogin); - panel.add(mass); + panel.add(messageArea); panel.add(scrollPane); panel.add(EnterIp); - panel.add(addr); + panel.add(remoteAdress); + panel.add(toggleOffOnline); panel.add(Send); panel.add(Apply); panel.add(Disconnect); panel.add(Connect); + panel.add(AddNewContact); + panel.add(Options); this.add(panel); @@ -239,11 +291,11 @@ public void exit(){ } public void send(){ - text = mass.getText(); + text = messageArea.getText(); if (Protocol.isMessageValid(text)) { logic.sendMessage(text); logic.getHistoryViewModel().addLocalMessage(text); - mass.setText(""); + messageArea.setText(""); } } @@ -265,14 +317,18 @@ public void setConnected(boolean b){ Disconnect.setEnabled(true); textfieldlogin.setEditable(false); EnterIp.setEditable(false); + messageArea.setEnabled(true); + toggleOffOnline.setEnabled(false); } else { Connect.setEnabled(true); Apply.setEnabled(true); Send.setEnabled(false); Disconnect.setEnabled(false); + messageArea.setEnabled(false); textfieldlogin.setEditable(true); EnterIp.setEditable(true); + toggleOffOnline.setEnabled(true); } } @@ -280,4 +336,13 @@ public void changeEnterIp(String ip){ EnterIp.setText(ip); } + public void setOffline(){ + Connect.setEnabled(false); + Apply.setEnabled(true); + Send.setEnabled(false); + Disconnect.setEnabled(false); + textfieldlogin.setEditable(true); + EnterIp.setEditable(false); + } + } diff --git a/src/NewContactFrame.java b/src/NewContactFrame.java new file mode 100644 index 0000000..6b0bbb0 --- /dev/null +++ b/src/NewContactFrame.java @@ -0,0 +1,80 @@ +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class NewContactFrame extends JFrame{ + + JButton Cancel; + JButton Accept; + + JTextArea nickArea; + JTextArea ipArea; + + + public NewContactFrame(ContactsViewModel cvm){ + super("Create new contact"); + createGUI(cvm); + } + + private void createGUI(final ContactsViewModel cvm){ + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(300,150); + setResizable(false); + setAlwaysOnTop(true); + setLocationRelativeTo(null); + + + + + + JPanel panel = new JPanel(); + panel.setLayout(null); + + + nickArea = new JTextArea("Nick"); + nickArea.setLocation(18, 15); + nickArea.setSize(250,20); + ipArea = new JTextArea("IP"); + ipArea.setLocation(18, 46); + ipArea.setSize(250,20); + + + Accept = new JButton("Accept"); + Accept.setLocation(18, 80); + Accept.setSize(130,30); + Accept.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Contact c = new Contact(cvm,nickArea.getText(),ipArea.getText()); + cvm.add(c); + close(); + } + }); + + Cancel = new JButton("Cancel"); + Cancel.setLocation(150, 80); + Cancel.setSize(130, 30); + Cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + close(); + } + }); + + + panel.add(Accept); + panel.add(Cancel); + panel.add(nickArea); + panel.add(ipArea); + + + + this.add(panel); + setVisible(true); + } + + public void close(){ + dispose(); + } + +} diff --git a/src/Options.java b/src/Options.java new file mode 100644 index 0000000..e4f3275 --- /dev/null +++ b/src/Options.java @@ -0,0 +1,5 @@ +public class Options { + public static boolean saveNick = true; + public static boolean saveHistory = true; + public static boolean saveContacts = true; +} diff --git a/src/OptionsFrame.java b/src/OptionsFrame.java new file mode 100644 index 0000000..0c75cb3 --- /dev/null +++ b/src/OptionsFrame.java @@ -0,0 +1,113 @@ +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +public class OptionsFrame extends JFrame { + + JCheckBox contacts; + JCheckBox history; + JCheckBox nick; + + public OptionsFrame(){ + super("Options"); + createGUI(); + } + + private void createGUI(){ + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setSize(130, 100); + setResizable(false); + setAlwaysOnTop(true); + setLocationRelativeTo(null); + + JPanel panel = new JPanel(); + panel.setLayout(null); + + contacts = new JCheckBox("Save Contacts"); + history = new JCheckBox("Save History"); + nick = new JCheckBox("Save Nick"); + + contacts.setLocation(10,10); + contacts.setSize(120, 15); + if (Options.saveContacts) contacts.setSelected(true); + if (Options.saveHistory) history.setSelected(true); + if (Options.saveNick) nick.setSelected(true); + contacts.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (contacts.isSelected()) Options.saveContacts = true; + else Options.saveContacts = false; + } + }); + history.setLocation(10, 30); + history.setSize(120, 15); + history.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (history.isSelected()) Options.saveHistory=true; + else Options.saveHistory=false; + } + }); + nick.setLocation(10,50); + nick.setSize(120,15); + nick.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (nick.isSelected()) Options.saveNick=true; + else Options.saveNick=false; + } + }); + + addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + + } + + @Override + public void windowClosing(WindowEvent e) { + close(); + } + + @Override + public void windowClosed(WindowEvent e) { + + } + + @Override + public void windowIconified(WindowEvent e) { + + } + + @Override + public void windowDeiconified(WindowEvent e) { + + } + + @Override + public void windowActivated(WindowEvent e) { + + } + + @Override + public void windowDeactivated(WindowEvent e) { + + } + }); + + panel.add(contacts); + panel.add(history); + panel.add(nick); + + add(panel); + setVisible(true); + } + + public void close(){ + dispose(); + } + + +} From 9ca40588cbe30de03dedbfc2be2e60cd2b7bc7d1 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Thu, 10 Dec 2015 00:11:56 +0200 Subject: [PATCH 29/38] Last one --- src/Connection.java | 4 ++++ src/Logic.java | 7 ++++++- src/Options.java | 1 + src/OptionsFrame.java | 16 +++++++++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Connection.java b/src/Connection.java index c421032..a7e7653 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -79,6 +79,10 @@ public void disconnect(){ } } + public String getRemoteIP(){ + return socket.getInetAddress().getHostAddress(); + } + public Command recieve(){ return Command.getCommand(in); } diff --git a/src/Logic.java b/src/Logic.java index ff9d1bc..990f1e9 100644 --- a/src/Logic.java +++ b/src/Logic.java @@ -71,6 +71,7 @@ public void accept(Connection connection){ commandListenerThread = new CommandListenerThread(this.connection,this); Thread thread = new Thread(commandListenerThread); thread.start(); + if (Options.autoSaveContacts) contactsViewModel.add(new Contact(contactsViewModel,remoteNick,connection.getRemoteIP())); setBusy(true); historyViewModel.clearView(); historyViewModel.addSystemMessage("Connected to "+remoteNick); @@ -114,6 +115,7 @@ public void call(String IP) { } if (connection != null) { remoteNick = caller.getRemoteNick(); + if (Options.autoSaveContacts) contactsViewModel.add(new Contact(contactsViewModel,remoteNick,IP)); commandListenerThread = new CommandListenerThread(connection, this); Thread comThread = new Thread(commandListenerThread); comThread.start(); @@ -241,7 +243,9 @@ public void writeOptions(){ else out.write("false "); if (Options.saveContacts) out.write("true "); else out.write("false "); - if (Options.saveHistory) out.write("true"); + if (Options.saveHistory) out.write("true "); + else out.write("false "); + if (Options.autoSaveContacts) out.write("true"); else out.write("false"); out.close(); } @@ -258,6 +262,7 @@ public void readOptions(){ if (tmp[0].equals("false")) Options.saveNick=false; if (tmp[1].equals("false")) Options.saveContacts=false; if (tmp[2].equals("false")) Options.saveHistory=false; + if (tmp[3].equals("false")) Options.autoSaveContacts=false; in.close(); } catch (IOException e){ diff --git a/src/Options.java b/src/Options.java index e4f3275..c391a34 100644 --- a/src/Options.java +++ b/src/Options.java @@ -2,4 +2,5 @@ public class Options { public static boolean saveNick = true; public static boolean saveHistory = true; public static boolean saveContacts = true; + public static boolean autoSaveContacts = true; } diff --git a/src/OptionsFrame.java b/src/OptionsFrame.java index 0c75cb3..ac97088 100644 --- a/src/OptionsFrame.java +++ b/src/OptionsFrame.java @@ -6,6 +6,7 @@ public class OptionsFrame extends JFrame { + JCheckBox autoAddContacts; JCheckBox contacts; JCheckBox history; JCheckBox nick; @@ -17,7 +18,7 @@ public OptionsFrame(){ private void createGUI(){ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - setSize(130, 100); + setSize(200, 140); setResizable(false); setAlwaysOnTop(true); setLocationRelativeTo(null); @@ -25,6 +26,7 @@ private void createGUI(){ JPanel panel = new JPanel(); panel.setLayout(null); + autoAddContacts = new JCheckBox("Add Contacts automatically"); contacts = new JCheckBox("Save Contacts"); history = new JCheckBox("Save History"); nick = new JCheckBox("Save Nick"); @@ -34,6 +36,7 @@ private void createGUI(){ if (Options.saveContacts) contacts.setSelected(true); if (Options.saveHistory) history.setSelected(true); if (Options.saveNick) nick.setSelected(true); + if (Options.autoSaveContacts) autoAddContacts.setSelected(true); contacts.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -60,6 +63,16 @@ public void actionPerformed(ActionEvent e) { } }); + autoAddContacts.setLocation(10,70); + autoAddContacts.setSize(195,15); + autoAddContacts.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (autoAddContacts.isSelected()) Options.autoSaveContacts=true; + else Options.autoSaveContacts=false; + } + }); + addWindowListener(new WindowListener() { @Override public void windowOpened(WindowEvent e) { @@ -100,6 +113,7 @@ public void windowDeactivated(WindowEvent e) { panel.add(contacts); panel.add(history); panel.add(nick); + panel.add(autoAddContacts); add(panel); setVisible(true); From de3009c2f1d79fa7ccb42436fa010dc9ab0da7d5 Mon Sep 17 00:00:00 2001 From: Artem Dyadik Date: Thu, 10 Dec 2015 23:24:43 +0200 Subject: [PATCH 30/38] Update MainGUI.java --- src/MainGUI.java | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/MainGUI.java b/src/MainGUI.java index 0136116..0c22bb3 100644 --- a/src/MainGUI.java +++ b/src/MainGUI.java @@ -34,7 +34,7 @@ public class MainGUI extends JFrame { String text; - Font font = new Font("Verdana", Font.BOLD, 13); + Font font = new Font("Roboto", Font.BOLD, 13); LineBorder linebord = new LineBorder(Color.BLACK, 1); @@ -42,15 +42,15 @@ public class MainGUI extends JFrame { this.logic=logic; - JPanel panel = new JPanel(); + JPanel panel = new JPanel(); panel.setLayout(null); panel.setBackground(Color.white); - login = new JLabel("login:"); + login = new JLabel("Login"); login.setFont(font); - login.setBounds(35, 40, 60, 30); + login.setBounds(10, 10, 50, 30); textfieldlogin = new JTextField(logic.getLocalNick()); - textfieldlogin.setBounds(80, 40, 115, 30); + textfieldlogin.setBounds(10, 40, 115, 20); textfieldlogin.setBorder(linebord); textfieldlogin.addKeyListener(new KeyListener() { @@ -79,9 +79,9 @@ public void keyReleased(KeyEvent e) { remoteAdress = new JLabel("remote remoteAdress"); remoteAdress.setFont(font); - remoteAdress.setBounds(400,35,100,50); + remoteAdress.setBounds(600,10,100,30); EnterIp = new JTextField("files.litvinov.in.ua"); - EnterIp.setBounds(500,40,150,30); + EnterIp.setBounds(600,40,150,20); EnterIp.setBorder(linebord); EnterIp.addKeyListener(new KeyListener() { @Override @@ -110,7 +110,7 @@ public void keyReleased(KeyEvent e) { }); Options = new JButton("Options"); - Options.setLocation(820,60); + Options.setLocation(720,60); Options.setSize(150,25); Options.addActionListener(new ActionListener() { @Override @@ -122,7 +122,7 @@ public void actionPerformed(ActionEvent e) { messageArea = new JTextField(); messageArea.setHorizontalAlignment(JTextField.LEFT); messageArea.setBorder(linebord); - messageArea.setBounds(10, 480, 650, 75); + messageArea.setBounds(10, 510, 636, 50); messageArea.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { @@ -144,8 +144,8 @@ public void keyReleased(KeyEvent e) { historyView = new HistoryView(logic.getHistoryViewModel()); - historyView.setLocation(10, 140); - historyView.setSize(740,300); + historyView.setLocation(48, 170); + historyView.setSize(100,1000); toggleOffOnline = new JToggleButton(); @@ -179,11 +179,11 @@ public void actionPerformed(ActionEvent e) { JScrollPane scrollPane = new JScrollPane(historyView); scrollPane.setLocation(10, 140); - scrollPane.setSize(740,300); + scrollPane.setSize(740,350); scrollPane.setBorder(linebord); AddNewContact = new JButton("Add new Contact"); - AddNewContact.setLocation(820,30); + AddNewContact.setLocation(720,30); AddNewContact.setSize(150,25); AddNewContact.addActionListener(new ActionListener() { @Override @@ -193,8 +193,8 @@ public void actionPerformed(ActionEvent e) { }); Apply = new JButton("Apply"); - Apply.setLocation(80, 80); - Apply.setSize(75,30); + Apply.setLocation(10, 70); + Apply.setSize(115,25); Apply.setFont(font); Apply.addActionListener(new ActionListener() { @Override @@ -203,9 +203,9 @@ public void actionPerformed(ActionEvent e) { } }); - Disconnect = new JButton("Disconnect"); - Disconnect.setLocation(600,80); - Disconnect.setSize(130,30); + Disconnect = new JButton("Disconnect"); + Disconnect.setLocation(600,99); + Disconnect.setSize(150,25); Disconnect.setFont(font); Disconnect.addActionListener(new ActionListener() { @Override @@ -228,9 +228,10 @@ public void actionPerformed(ActionEvent e) { } }); - Send = new JButton("Send"); - Send.setLocation(680, 530); - Send.setSize(100,30); + Connect = new JButton("Connect"); + Connect.setLocation(600,70); + Connect.setSize(150, 25); + Connect.setFont(font); Send.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent ae) { send(); From e4a23b54ce4c8602c73d9fcdfec5b8d0aaed94f6 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Thu, 17 Dec 2015 00:47:27 +0200 Subject: [PATCH 31/38] wip, changing a lot of stuff --- src/CallListenerThread.java | 15 +- src/Caller.java | 15 +- src/Colors.java | 10 + src/CommandListenerThread.java | 10 +- src/Contact.java | 4 +- src/ContactPanel.java | 73 ++--- src/ContactsView.java | 122 +++----- src/ContactsViewModel.java | 20 +- src/HistoryView.java | 11 +- src/HistoryViewModel.java | 3 +- src/IncomingCallForm.java | 3 +- src/Logic.java | 276 ----------------- src/Main.java | 249 +++++++++++++++ src/MainGUI.java | 538 +++++++++++++++------------------ src/NewContactFrame.java | 2 + src/PopUp.java | 3 +- src/font/roboto-thin.ttf | Bin 0 -> 162636 bytes src/font/roboto-thin9.ttf | Bin 0 -> 122512 bytes src/images/disconD.png | Bin 0 -> 3922 bytes src/images/disconH.png | Bin 0 -> 3915 bytes src/images/disconN.png | Bin 0 -> 3917 bytes src/images/disconP.png | Bin 0 -> 3898 bytes src/images/logoutD.png | Bin 0 -> 3551 bytes src/images/logoutH.png | Bin 0 -> 3542 bytes src/images/logoutN.png | Bin 0 -> 3541 bytes src/images/logoutP.png | Bin 0 -> 3536 bytes src/images/off.png | Bin 3068 -> 2941 bytes src/images/on.png | Bin 3076 -> 2898 bytes src/images/optionsH.png | Bin 0 -> 705 bytes src/images/optionsN.png | Bin 0 -> 708 bytes src/images/optionsP.png | Bin 0 -> 748 bytes src/images/sendD.png | Bin 0 -> 4571 bytes src/images/sendH.png | Bin 0 -> 4508 bytes src/images/sendN.png | Bin 0 -> 4432 bytes src/images/sendP.png | Bin 0 -> 4608 bytes src/images/signingD.png | Bin 0 -> 3567 bytes src/images/signingH.png | Bin 0 -> 3546 bytes src/images/signingN.png | Bin 0 -> 3551 bytes src/images/signingP.png | Bin 0 -> 3546 bytes src/images/signuptD.png | Bin 0 -> 3696 bytes src/images/signuptH.png | Bin 0 -> 3720 bytes src/images/signuptN.png | Bin 0 -> 3717 bytes src/images/signuptP.png | Bin 0 -> 3706 bytes 43 files changed, 618 insertions(+), 736 deletions(-) create mode 100644 src/Colors.java delete mode 100644 src/Logic.java create mode 100644 src/Main.java create mode 100644 src/font/roboto-thin.ttf create mode 100644 src/font/roboto-thin9.ttf create mode 100644 src/images/disconD.png create mode 100644 src/images/disconH.png create mode 100644 src/images/disconN.png create mode 100644 src/images/disconP.png create mode 100644 src/images/logoutD.png create mode 100644 src/images/logoutH.png create mode 100644 src/images/logoutN.png create mode 100644 src/images/logoutP.png create mode 100644 src/images/optionsH.png create mode 100644 src/images/optionsN.png create mode 100644 src/images/optionsP.png create mode 100644 src/images/sendD.png create mode 100644 src/images/sendH.png create mode 100644 src/images/sendN.png create mode 100644 src/images/sendP.png create mode 100644 src/images/signingD.png create mode 100644 src/images/signingH.png create mode 100644 src/images/signingN.png create mode 100644 src/images/signingP.png create mode 100644 src/images/signuptD.png create mode 100644 src/images/signuptH.png create mode 100644 src/images/signuptN.png create mode 100644 src/images/signuptP.png diff --git a/src/CallListenerThread.java b/src/CallListenerThread.java index 9c93993..191b2b4 100644 --- a/src/CallListenerThread.java +++ b/src/CallListenerThread.java @@ -1,6 +1,7 @@ import java.io.IOException; -public class CallListenerThread implements Runnable { +public class CallListenerThread { + /* private String localNick; private CallListener callListener; @@ -8,13 +9,13 @@ public class CallListenerThread implements Runnable { private String remoteNick; private Connection remoteConnection; private IncomingCallForm form; - Logic logic; - public CallListenerThread(String localNick,Boolean isBusy,Logic logic){ + + public CallListenerThread(String localNick,Boolean isBusy){ callListener = new CallListener(localNick); - this.logic = logic; + } public void run() { @@ -24,9 +25,9 @@ public void run() { remoteConnection = callListener.getConnection(); if (remoteConnection == null) continue; remoteNick=callListener.getRemoteNick(); - form = new IncomingCallForm(remoteConnection,logic,remoteNick); + form = new IncomingCallForm(remoteConnection,remoteNick); System.out.println("AZAZA"); - logic.setRemoteNick(remoteNick); + } } catch(IOException e){ @@ -57,5 +58,5 @@ public void setNick(String nick){ public void kill() { stop = true; - } + }*/ } diff --git a/src/Caller.java b/src/Caller.java index 58d9a0a..503401d 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -1,8 +1,5 @@ import java.io.IOException; -import java.net.InetAddress; import java.net.Socket; -import java.util.Timer; -import java.util.TimerTask; public class Caller { private String localNick; @@ -11,12 +8,12 @@ public class Caller { private Command lastCommand; private NickCommand nickCommand; private String lastError; - private Logic logic; - public Caller(String localNick,String remoteIP, Logic logic){ + + public Caller(String localNick,String remoteIP){ this.localNick=localNick; this.remoteIP=remoteIP; - this.logic=logic; + } public Connection call() throws IOException { @@ -25,7 +22,7 @@ public Connection call() throws IOException { lastCommand = connection.recieve(); if (lastCommand.type==CommandType.NICK) { nickCommand = (NickCommand) lastCommand; - logic.getMainGui().setConnected(true); + } else{ connection.disconnect(); @@ -42,7 +39,7 @@ public Connection call() throws IOException { UltimateGUI busyGUI = new UltimateGUI(remoteNick+" is busy"); lastError="User is busy"; remoteNick=null; - logic.getMainGui().setConnected(false); + return null; } @@ -55,7 +52,7 @@ public Connection call() throws IOException { else{ UltimateGUI ultimateGUI = new UltimateGUI(remoteNick+" rejected your call"); lastError="User rejected your call"; - logic.getMainGui().setConnected(false); + connection.disconnect(); return null; } diff --git a/src/Colors.java b/src/Colors.java new file mode 100644 index 0000000..a8c3da5 --- /dev/null +++ b/src/Colors.java @@ -0,0 +1,10 @@ +import java.awt.*; + +public class Colors { + public static final Color mainGreen = new Color(139, 194, 73); + public static final Color darkGreen = new Color(54, 107, 52); + public static final Color midGreen = new Color(103, 159, 66); + public static final Color softGreen = new Color(242, 248, 233); + public static final Color darkGrey = new Color(67, 67, 67); + public static final Color lightGreen = new Color(0xDCECC8); +} diff --git a/src/CommandListenerThread.java b/src/CommandListenerThread.java index 4ac5eb6..c3fa08e 100644 --- a/src/CommandListenerThread.java +++ b/src/CommandListenerThread.java @@ -5,11 +5,11 @@ public class CommandListenerThread implements Runnable { private Command lastCommand; private Connection connection; private boolean stop; - Logic logic; - public CommandListenerThread(Connection connection, Logic logic){ + + public CommandListenerThread(Connection connection){ this.connection=connection; - this.logic=logic; + } public void run() { @@ -18,11 +18,11 @@ public void run() { if (lastCommand==null) continue; if (lastCommand.type==CommandType.MESSAGE){ MessageCommand mc = (MessageCommand) lastCommand; - logic.addMessage(mc.message); + } if (lastCommand.type==CommandType.DISCONNECT){ - logic.disconnect(); + } } } diff --git a/src/Contact.java b/src/Contact.java index 15b79d9..3983e44 100644 --- a/src/Contact.java +++ b/src/Contact.java @@ -1,6 +1,6 @@ public class Contact { - private boolean isFav,isOnline; + /* private boolean isFav,isOnline; private String nick,IP; private ContactsViewModel contactsViewModel; @@ -70,5 +70,5 @@ public boolean equals(Object object){ public void setFav(Boolean b){ isFav=b; } - +*/ } diff --git a/src/ContactPanel.java b/src/ContactPanel.java index 689c416..d6e5f38 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -2,11 +2,17 @@ import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; +import java.io.File; +import java.io.IOException; public class ContactPanel extends JPanel{ private Contact contact; - private JLabel label; - private ImageIcon imageIcon; + private JLabel label = new JLabel(); + private Font font; + + public ContactPanel(){ + createGUI(); + } public ContactPanel(Contact contact){ this.contact=contact; @@ -14,48 +20,25 @@ public ContactPanel(Contact contact){ } private void createGUI(){ - setToolTipText(contact.getIP()); - if (contact.isOnline()){ - imageIcon = new ImageIcon("src/images/on.png"); - } - else { - imageIcon = new ImageIcon("src/images/off.png"); + try { + font = Font.createFont(Font.TRUETYPE_FONT,new File("src/font/roboto-thin.ttf")).deriveFont(15f); + } catch (FontFormatException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } - label = new JLabel(contact.getNick()); - label.setIcon(imageIcon); - setBackground(Color.WHITE); - final PopUp menu = new PopUp(this); - addMouseListener(new MouseListener() { - @Override - public void mouseClicked(MouseEvent e) { - menu.show(e.getComponent(), e.getX(), e.getY()); - } - - @Override - public void mousePressed(MouseEvent e) { - - } - - @Override - public void mouseReleased(MouseEvent e) { - - } - - @Override - public void mouseEntered(MouseEvent e) { - setBackground(Color.CYAN); - } - - @Override - public void mouseExited(MouseEvent e) { - setBackground(Color.WHITE); - } - }); - setMinimumSize(new Dimension(200,25)); - setMaximumSize(new Dimension(200,25)); + setLayout(null); + setBackground(Colors.midGreen); + setMinimumSize(new Dimension(200, 25)); + setPreferredSize(new Dimension(200,25)); + setMaximumSize(new Dimension(200, 25)); + label.setBounds(5, 3, 200, 20); + label.setFont(font); + label.setText("Contact exz"); + label.setIcon(new ImageIcon("src/images/on.png")); add(label); } - +/* public boolean isFav(){ return contact.isFav(); } @@ -73,5 +56,13 @@ public String getNick(){ public Contact getContact(){ return contact; } +*/ + public static void main(String[] args) { + final JFrame test = new JFrame(); + test.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + test.add(new ContactPanel()); + test.setVisible(true); + + } } diff --git a/src/ContactsView.java b/src/ContactsView.java index 5bb12bd..db79a8a 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -5,104 +5,64 @@ public class ContactsView extends JPanel{ - private JPanel contacts,favourites; - private ContactsViewModel contactsViewModel; + private JPanel contactsP,favouritesP; + + JLabel contactsL = new JLabel("Contacts"); + JLabel favouritesL = new JLabel("Favourites"); + private ArrayList list = new ArrayList(); - public ContactsView(ContactsViewModel cvm){ - contactsViewModel=cvm; - contactsViewModel.setContactsView(this); + public ContactsView(){ createGUI(); } private void createGUI(){ - setBackground(Color.WHITE); - setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - - JPanel top = new JPanel(); - top.setBackground(Color.WHITE); - top.setLayout(new BoxLayout(top,BoxLayout.Y_AXIS)); - JPanel bottom = new JPanel(); - bottom.setBackground(Color.WHITE); - bottom.setLayout(new BoxLayout(bottom,BoxLayout.Y_AXIS)); - - JLabel favouritesL = new JLabel("Favourites"); - JLabel contactsL = new JLabel("Contacts"); - Font font = new Font("Verdana",Font.BOLD,14); - favouritesL.setFont(font); - //favouritesL.setSize(new Dimension(50, 50)); - //favouritesL.setHorizontalTextPosition(0); - contactsL.setFont(font); - //contactsL.setSize(new Dimension(50, 50)); - - contacts = new JPanel(); - contacts.setBackground(Color.WHITE); - contacts.setLayout(new BoxLayout(contacts,BoxLayout.Y_AXIS)); - contacts.setMinimumSize(new Dimension(175, 150)); - - favourites = new JPanel(); - favourites.setLayout(new BoxLayout(favourites,BoxLayout.Y_AXIS)); - favourites.setMinimumSize(new Dimension(175, 150)); - favourites.setBackground(Color.WHITE); - - JScrollPane contactsS = new JScrollPane(contacts); - contactsS.setMinimumSize(new Dimension(175, 150)); - JScrollPane favouritesS = new JScrollPane(favourites); - favouritesS.setMinimumSize(new Dimension(175, 150)); - - bottom.setPreferredSize(new Dimension(175,200)); - top.setPreferredSize(new Dimension(175, 200)); - - top.add(favouritesL); - top.add(favouritesS); - bottom.add(contactsL); - bottom.add(contactsS); + setBackground(Colors.softGreen); + setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); + + setAlignmentX(Component.LEFT_ALIGNMENT); + add(favouritesL); + add(Box.createVerticalStrut(10)); + + favouritesP = new JPanel(); + favouritesP.setLayout(new BoxLayout(favouritesP,BoxLayout.Y_AXIS)); + favouritesP.setBackground(Colors.softGreen); + favouritesP.setAlignmentX(Component.LEFT_ALIGNMENT); + favouritesP.setAutoscrolls(true); + add(favouritesP); + + + add(Box.createVerticalStrut(10)); + add(contactsL); + add(Box.createVerticalStrut(10)); + + contactsP = new JPanel(); + contactsP.setAutoscrolls(true); + contactsP.setLayout(new BoxLayout(contactsP,BoxLayout.Y_AXIS)); + contactsP.setBackground(Colors.softGreen); + contactsP.setAlignmentX(Component.LEFT_ALIGNMENT); + add(contactsP); + + favouritesP.add(new ContactPanel()); + for (int i=0;i<15;i++) { + contactsP.add(new ContactPanel()); + } - add(top); - add(bottom); + } + public void setLabelFont(Font font){ + contactsL.setFont(font); + favouritesL.setFont(font); } - public void fullUpdate(){ - contacts.removeAll(); - favourites.removeAll(); - list.clear(); - ArrayList contactsList = (ArrayList) contactsViewModel.getList(); - for (Contact contact : contactsList){ - list.add(new ContactPanel(contact)); - } - for (ContactPanel cp : list){ - if (cp.isFav()) favourites.add(cp); - else contacts.add(cp); - } - updateUI(); - contactsViewModel.onlineUpdate(); - } - public void onlineUpdate(){ - for (ContactPanel cp : list){ - cp.update(); - } - } - public void delete(ContactPanel contact){ - if (contact.isFav()) favourites.remove(contact); - else contacts.remove(contact); - list.remove(contact); - updateUI(); - } - public void addContact(Contact contact){ - ContactPanel cp = new ContactPanel(contact); - list.add(cp); - if (cp.isFav()) favourites.add(cp); - else contacts.add(cp); - updateUI(); - } + } diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index 65e09db..15f1216 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -7,7 +7,7 @@ import java.util.Timer; public class ContactsViewModel{ - ContactsView contactsView; +/* ContactsView contactsView; ArrayList contactList = new ArrayList(); Logic logic; @@ -43,13 +43,11 @@ public void call(Contact contact){ public void removeContact(Contact contact, ContactPanel contactPanel){ contactList.remove(contact); - contactsView.delete(contactPanel); - } - public void updateView(){ - contactsView.fullUpdate(); } + + public void onlineUpdate(){ ServerConnection serverConnection = logic.getServerConnection(); for (Contact contact : contactList){ @@ -57,24 +55,24 @@ public void onlineUpdate(){ contact.setOnline(true); } } - contactsView.onlineUpdate(); + } public void getData(){ - ServerConnection serverConnection = logic.getServerConnection(); - String[] nicks = serverConnection.getAllNicks(); + + //String[] nicks = serverConnection.getAllNicks(); for (String nick : nicks){ if (nick.equals(logic.getLocalNick())) continue; Contact c = new Contact(this, nick, serverConnection.getIpForNick(nick)); if (contactList.contains(c)) continue; contactList.add(c); } - updateView(); + } public void add(Contact contact){ contactList.add(contact); - contactsView.addContact(contact); + } public void writeToFile() { @@ -107,5 +105,5 @@ public void readFromFile(){ } catch (IOException e){ System.out.println("Failed to find a file Contacts.txt"); } - } + }*/ } diff --git a/src/HistoryView.java b/src/HistoryView.java index 169979a..c59c926 100644 --- a/src/HistoryView.java +++ b/src/HistoryView.java @@ -1,12 +1,12 @@ import javax.swing.*; -public class HistoryView extends JTextArea{ - HistoryViewModel historyViewModel; +public class HistoryView { + /* int currentSize=0; - public HistoryView(HistoryViewModel historyViewModel){ - this.historyViewModel=historyViewModel; - historyViewModel.setHistoryView(this); + public HistoryView(){ + + setEditable(false); setAutoscrolls(true); } @@ -23,4 +23,5 @@ public void clear(){ currentSize=0; setText(""); } + */ } diff --git a/src/HistoryViewModel.java b/src/HistoryViewModel.java index f2df7b1..9b03950 100644 --- a/src/HistoryViewModel.java +++ b/src/HistoryViewModel.java @@ -6,6 +6,7 @@ import java.util.List; public class HistoryViewModel { + /* Logic logic; List Messagelist = new ArrayList(); HistoryView historyView; @@ -72,5 +73,5 @@ public void clearView(){ historyView.clear(); } - +*/ } diff --git a/src/IncomingCallForm.java b/src/IncomingCallForm.java index 7a07c01..04332c1 100644 --- a/src/IncomingCallForm.java +++ b/src/IncomingCallForm.java @@ -11,6 +11,7 @@ public class IncomingCallForm extends JFrame { + /* private static final int Height = 150; private static final int Widht = 300; @@ -84,5 +85,5 @@ public void windowClosing(java.awt.event.WindowEvent windowEvent) { this.add(panel); setVisible(true); } - +*/ } \ No newline at end of file diff --git a/src/Logic.java b/src/Logic.java deleted file mode 100644 index 990f1e9..0000000 --- a/src/Logic.java +++ /dev/null @@ -1,276 +0,0 @@ -import javax.swing.*; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Scanner; - -public class Logic{ - private MainGUI mainGui; - private String localNick = "default",remoteNick,remoteIP; - private boolean isBusy,isOnline; - private Connection connection = null; - private Caller caller; - private CallListenerThread callListenerThread; - private Thread callThread; - private HistoryViewModel historyViewModel = new HistoryViewModel(this); - private CommandListenerThread commandListenerThread; - private ServerConnection serverConnection = new ServerConnection(); - private ContactsViewModel contactsViewModel = new ContactsViewModel(this); - - public Logic(){ - readOptions(); - String tmp = getNickFromFile(); - if (tmp!=null) localNick=tmp; - else localNick="default"; - callListenerThread = new CallListenerThread(localNick,isBusy,this); - callThread = new Thread(callListenerThread); - callThread.setDaemon(true); - callThread.start(); - mainGui = new MainGUI(this); - try { - Class.forName("com.mysql.jdbc.Driver"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - isOnline=true; - serverConnection.setServerAddress("jdbc:mysql://files.litvinov.in.ua/chatapp_server?characterEncoding=utf-8&useUnicode=true"); - serverConnection.connect(); - serverConnection.setLocalNick(localNick); - serverConnection.goOnline(); - - contactsViewModel.readFromFile(); - contactsViewModel.getData(); - - - - } - - public void setLocalNick(String nick){ - localNick=nick; - callListenerThread.setNick(localNick); - serverConnection.setLocalNick(localNick); - } - - public void setRemoteNick(String nick){ - remoteNick = nick; - } - - public void setRemoteIP(String IP){ - remoteIP = IP; - } - - public void setBusy(boolean isBusy) { - this.isBusy = isBusy; - callListenerThread.setBusy(isBusy); - } - - public void accept(Connection connection){ - System.out.println("logic got Connection!"); - this.connection=connection; - commandListenerThread = new CommandListenerThread(this.connection,this); - Thread thread = new Thread(commandListenerThread); - thread.start(); - if (Options.autoSaveContacts) contactsViewModel.add(new Contact(contactsViewModel,remoteNick,connection.getRemoteIP())); - setBusy(true); - historyViewModel.clearView(); - historyViewModel.addSystemMessage("Connected to "+remoteNick); - System.out.println("GOT CONNECTION, YEY!"); - mainGui.setConnected(true); - - - } - - - public void sendMessage(String message){ - connection.sendMessage(message); - } - - public void call(String IP) { - if (!isOnline){ - UltimateGUI ultimateGUI = new UltimateGUI("You cannot call anyone, you're Offline!"); - return; - } - - if (isConnected()){ - if (JOptionPane.showConfirmDialog(mainGui, - "You have an established connection with another user.\n If you will call this contact, current connection will be closed.\nAre you sure you want to do this?", "Warning", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - disconnect(); - } - - else{ - return; - }} - mainGui.changeEnterIp(IP); - setRemoteIP(IP); - mainGui.setConnected(true); - setBusy(true); - caller = new Caller(localNick, remoteIP, this); - try { - connection = caller.call(); - } catch (IOException e) { - UltimateGUI ultimateGUI = new UltimateGUI("Failed to connect"); - } - if (connection != null) { - remoteNick = caller.getRemoteNick(); - if (Options.autoSaveContacts) contactsViewModel.add(new Contact(contactsViewModel,remoteNick,IP)); - commandListenerThread = new CommandListenerThread(connection, this); - Thread comThread = new Thread(commandListenerThread); - comThread.start(); - historyViewModel.addSystemMessage("Connected to " + remoteNick); - System.out.println("CONNECTED, YEY"); - } else { - mainGui.setConnected(false); - setBusy(false); - - } - } - - public void addMessage(String message){ - historyViewModel.addRemoteMessage(message); - } - - public void disconnect() { - if (isConnected()) { - setBusy(false); - historyViewModel.addSystemMessage("Disconnected"); - if (Options.saveHistory) historyViewModel.writeHistoryFile(); - remoteNick = null; - remoteIP = null; - commandListenerThread.kill(); - connection.disconnect(); - connection = null; - mainGui.setConnected(false); - } - - } - - public String getLocalNick(){ - return localNick; - } - - public String getRemoteNick(){ - return remoteNick; - } - - public HistoryViewModel getHistoryViewModel(){ - return historyViewModel; - } - - public MainGUI getMainGui(){ - return mainGui; - } - - public void writeNickToFile(){ - FileWriter out = null; - try { - out = new FileWriter("nick.txt"); - out.write(localNick); - out.close(); - } catch (IOException e) { - System.out.println("Could not create nick.txt"); - } - - } - - public String getNickFromFile(){ - BufferedReader in = null; - try{ - in = new BufferedReader(new FileReader("nick.txt")); - String nick = in.readLine(); - in.close(); - if (nick!=null){ - return nick; - } - else { - return null; - } - - } catch (IOException e){ - System.out.println("Failed to find a file nick.txt"); - return null; - } - - } - - public static void main(String[] args) { - Logic logic = new Logic(); - } - - public boolean isConnected(){ - if (connection!=null) return true; - else return false; - } - - public ContactsViewModel getContactsViewModel(){ - return contactsViewModel; - } - - public void exit(){ - disconnect(); - if (serverConnection.isConnected()) { - serverConnection.goOffline(); - serverConnection.disconnect(); - } - if (Options.saveNick) writeNickToFile(); - if (Options.saveContacts) contactsViewModel.writeToFile(); - writeOptions(); - } - - public void setOnline(boolean online){ - isOnline=online; - if (isOnline){ - callListenerThread.setOnline(true); - mainGui.setConnected(false); - } - else{ - callListenerThread.setOnline(false); - mainGui.setOffline(); - } - } - - public boolean isOnline(){ - return isOnline; - } - - public void writeOptions(){ - FileWriter out; - try{ - out = new FileWriter("Options.txt"); - if (Options.saveNick) out.write("true "); - else out.write("false "); - if (Options.saveContacts) out.write("true "); - else out.write("false "); - if (Options.saveHistory) out.write("true "); - else out.write("false "); - if (Options.autoSaveContacts) out.write("true"); - else out.write("false"); - out.close(); - } - catch (IOException e){ - e.printStackTrace(); - } - } - - public void readOptions(){ - Scanner in; - try{ - in = new Scanner(new FileReader("Options.txt")); - String[] tmp = in.nextLine().split(" "); - if (tmp[0].equals("false")) Options.saveNick=false; - if (tmp[1].equals("false")) Options.saveContacts=false; - if (tmp[2].equals("false")) Options.saveHistory=false; - if (tmp[3].equals("false")) Options.autoSaveContacts=false; - in.close(); - } - catch (IOException e){ - e.printStackTrace(); - } - } - - public ServerConnection getServerConnection(){ - return serverConnection; - } -} diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..073c83d --- /dev/null +++ b/src/Main.java @@ -0,0 +1,249 @@ +import javax.swing.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +public class Main extends JFrame { + + MainGUI mainFrame; + + public Main() { + mainFrame = new MainGUI(); + + mainFrame.getDisconnectBtn().addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconP.png")); + } + }); + } + + @Override + public void mousePressed(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconP.png")); + } + }); + } + + @Override + public void mouseReleased(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconN.png")); + } + }); + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconH.png")); + } + }); + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconN.png")); + } + }); + } + }); + + mainFrame.getLogoutBtn().addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); + } + }); + } + + @Override + public void mousePressed(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); + } + }); + } + + @Override + public void mouseReleased(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutN.png")); + } + }); + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutH.png")); + } + }); + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutN.png")); + } + }); + } + }); + + mainFrame.getOptionsBtn().addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsP.png")); + } + }); + } + + @Override + public void mousePressed(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsP.png")); + } + }); + } + + @Override + public void mouseReleased(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsN.png")); + } + }); + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsH.png")); + } + }); + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsN.png")); + } + }); + } + }); + + mainFrame.getSendBtn().addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getSendBtn().setIcon(new ImageIcon("src/images/sendP.png")); + } + }); + } + + @Override + public void mousePressed(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getSendBtn().setIcon(new ImageIcon("src/images/sendP.png")); + } + }); + } + + @Override + public void mouseReleased(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getSendBtn().setIcon(new ImageIcon("src/images/sendN.png")); + } + }); + + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getSendBtn().setIcon(new ImageIcon("src/images/sendH.png")); + } + }); + + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainFrame.getSendBtn().setIcon(new ImageIcon("src/images/sendN.png")); + } + }); + + } + }); + + mainFrame.getMessageField().addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); + + mainFrame.setConnected(); + + + } + public static void main(String[] args) { + Main main = new Main(); + } +} \ No newline at end of file diff --git a/src/MainGUI.java b/src/MainGUI.java index 0c22bb3..b24c75d 100644 --- a/src/MainGUI.java +++ b/src/MainGUI.java @@ -1,349 +1,295 @@ -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; import javax.swing.*; -import javax.swing.border.LineBorder; - +import javax.swing.plaf.metal.MetalScrollBarUI; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowStateListener; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; public class MainGUI extends JFrame { - JPanel panel = new JPanel(); - Logic logic; - - private static final int Height = 600; - private static final int Widht = 1000; - - JButton Send; - JButton Apply; - JButton Disconnect; - JButton Connect; - JButton AddNewContact; - JButton Options; - JToggleButton toggleOffOnline; - - JLabel login; - JLabel remoteAdress; - HistoryView historyView; - - JTextField textfieldlogin; - JTextField EnterIp; - JTextField messageArea; - - ContactsView contactsView; - - String text; - - Font font = new Font("Roboto", Font.BOLD, 13); - - LineBorder linebord = new LineBorder(Color.BLACK, 1); - - MainGUI(final Logic logic){ - this.logic=logic; - - - JPanel panel = new JPanel(); - panel.setLayout(null); - panel.setBackground(Color.white); - - login = new JLabel("Login"); - login.setFont(font); - login.setBounds(10, 10, 50, 30); - textfieldlogin = new JTextField(logic.getLocalNick()); - textfieldlogin.setBounds(10, 40, 115, 20); - textfieldlogin.setBorder(linebord); - textfieldlogin.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { + final byte BTN_WIDTH = 97; + final byte BTN_HEIGHT = 27; - } + JLabel logged = new JLabel("Logged as"); + JLabel talking = new JLabel("Talking to"); + JLabel localNick = new JLabel("EvGe22"); + JLabel remoteNick = new JLabel(""); - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode()==KeyEvent.VK_ENTER){ - applyNick(); - } - } + JTextArea messageField = new JTextArea(); + JTextArea historyView = new JTextArea(); - @Override - public void keyReleased(KeyEvent e) { + JLabel logoutBtn = new JLabel(""); + JLabel disconnectBtn = new JLabel(""); + JLabel optionsBtn = new JLabel(""); + JLabel sendBtn = new JLabel(""); - } - }); + ContactsView contactsView = new ContactsView(); - contactsView = new ContactsView(logic.getContactsViewModel()); + JScrollPane contactsPane = new JScrollPane(contactsView); + JScrollPane historyPane = new JScrollPane(historyView); - contactsView.setBounds(0,20,175,400); + JPanel leftTopPanel = new JPanel(); + JPanel leftBottomPanel = new JPanel(); + Font bigFont,smallFont; - remoteAdress = new JLabel("remote remoteAdress"); - remoteAdress.setFont(font); - remoteAdress.setBounds(600,10,100,30); - EnterIp = new JTextField("files.litvinov.in.ua"); - EnterIp.setBounds(600,40,150,20); - EnterIp.setBorder(linebord); - EnterIp.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { - } + public MainGUI(){ + super("ChatApp 2015"); + createGUI(); + } - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - logic.getHistoryViewModel().clearView(); - logic.call(EnterIp.getText()); - } - }); - } - } + private void createGUI(){ + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setMinimumSize(new Dimension(860,550)); + setLayout(null); + getContentPane().setBackground(Color.white); - @Override - public void keyReleased(KeyEvent e) { + try { + smallFont = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + bigFont = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(18f); - } - }); + } catch (FontFormatException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } - Options = new JButton("Options"); - Options.setLocation(720,60); - Options.setSize(150,25); - Options.addActionListener(new ActionListener() { + leftTopPanel.setBackground(Colors.softGreen); + leftTopPanel.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Colors.midGreen)); + leftTopPanel.setBounds(-1,-1,280,123); + leftTopPanel.setLayout(null); + add(leftTopPanel); + + leftBottomPanel.setBackground(Colors.softGreen); + leftBottomPanel.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Colors.midGreen)); + leftBottomPanel.setBounds(-1,121,280,getHeight()-150); + leftBottomPanel.setLayout(null); + add(leftBottomPanel); + + logged.setBounds(25,10,150,40); + logged.setFont(bigFont); + leftTopPanel.add(logged); + + localNick.setBounds(25,32,150,40); + localNick.setFont(bigFont); + leftTopPanel.add(localNick); + + talking.setBounds(153,10,150,40); + talking.setFont(bigFont); + leftTopPanel.add(talking); + + remoteNick.setBounds(153,32,150,40); + remoteNick.setFont(bigFont); + leftTopPanel.add(remoteNick); + + logoutBtn.setIcon(new ImageIcon("src/images/logoutN.png")); + logoutBtn.setDisabledIcon(new ImageIcon("src/images/logoutD.png")); + logoutBtn.setBounds(20,80,BTN_WIDTH,BTN_HEIGHT); + leftTopPanel.add(logoutBtn); + + disconnectBtn.setIcon(new ImageIcon("src/images/disconN.png")); + disconnectBtn.setDisabledIcon(new ImageIcon("src/images/disconD.png")); + disconnectBtn.setEnabled(false); + disconnectBtn.setBounds(155,80,BTN_WIDTH,BTN_HEIGHT); + leftTopPanel.add(disconnectBtn); + + messageField.setBounds(getWidth()-544,getHeight()-122,430,50); + messageField.setFont(smallFont); + messageField.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Colors.lightGreen)); + messageField.setBackground(Colors.softGreen); + add(messageField); + messageField.setEnabled(false); + + sendBtn.setIcon(new ImageIcon("src/images/sendN.png")); + sendBtn.setDisabledIcon(new ImageIcon("src/images/sendD.png")); + sendBtn.setEnabled(false); + sendBtn.setBounds(getWidth()-96,getHeight()-124, 51,51); + add(sendBtn); + + optionsBtn.setIcon(new ImageIcon("src/images/optionsN.png")); + optionsBtn.setBounds(getWidth()-47,getHeight()-70,24,24); + add(optionsBtn); + + historyPane.getVerticalScrollBar().setPreferredSize(new Dimension(15, Integer.MAX_VALUE)); + historyPane.getVerticalScrollBar().setUI(new MyScrollbarUI()); + historyPane.setBackground(Color.white); + historyPane.setBorder(null); + + add(historyPane); + + contactsPane.getVerticalScrollBar().setPreferredSize(new Dimension(15,Integer.MAX_VALUE)); + contactsPane.getVerticalScrollBar().setUI(new MyScrollbarUI()); + contactsPane.setBounds(25, 18, 240, leftBottomPanel.getHeight() - 50); + contactsPane.setBorder(null); + contactsPane.setBackground(Colors.softGreen); + contactsView.setLabelFont(bigFont); + leftBottomPanel.add(contactsPane); + + + historyView.setFont(smallFont); + historyView.setEditable(false); + historyView.setText("\n\n\n\n\nasd\nafsdfsf\nafwasd\nasdfgd\naefasdf\nasdfsdrsetsd\naefsdfgste\nasfdsetsd\nsdfdgsersfg\nsdfgsgse\nserdfsd\naserfsdf\nsdfnn\n\n\n\\n\n\n\n\n\\n\n\n\n\n\n\n\n\n\n\n\n\nsdfsdf"); + + + resize(); + setVisible(true); + addWindowStateListener(new WindowStateListener() { @Override - public void actionPerformed(ActionEvent e) { - OptionsFrame opf = new OptionsFrame(); + public void windowStateChanged(WindowEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + resize(); + } + }); } }); - - messageArea = new JTextField(); - messageArea.setHorizontalAlignment(JTextField.LEFT); - messageArea.setBorder(linebord); - messageArea.setBounds(10, 510, 636, 50); - messageArea.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { - - } + addComponentListener(new ComponentAdapter() { @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode()==KeyEvent.VK_ENTER){ - send(); - } + public void componentResized(ComponentEvent e) { + super.componentResized(e); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + resize(); + } + }); } + }); + } - @Override - public void keyReleased(KeyEvent e) { - } - }); + /*public static void main(String[] args) { + NewGUI a = new NewGUI(); + }*/ - historyView = new HistoryView(logic.getHistoryViewModel()); - historyView.setLocation(48, 170); - historyView.setSize(100,1000); + public void resize(){ + leftBottomPanel.setBounds(-1,121,280,getHeight()-150); + contactsPane.setBounds(25, 18, 240, leftBottomPanel.getHeight() - 50); + historyPane.setBounds(321,40,getWidth()-370,getHeight()-195); + sendBtn.setBounds(getWidth()-96,getHeight()-124, 51,51); + optionsBtn.setBounds(getWidth()-47,getHeight()-70,24,24); + messageField.setBounds(320,getHeight()-122,getWidth()-430 /*430*/,50); - toggleOffOnline = new JToggleButton(); + } - toggleOffOnline.setLocation(200,48); - toggleOffOnline.setSize(17, 17); - toggleOffOnline.setBorderPainted(false); - toggleOffOnline.setFocusable(false); - toggleOffOnline.setBorder(null); - toggleOffOnline.setMargin(new Insets(0, 0, 0, 0)); - toggleOffOnline.setPressedIcon(new ImageIcon("src/images/off.png")); - toggleOffOnline.setDisabledIcon(new ImageIcon("src/images/off.png")); - toggleOffOnline.setContentAreaFilled(false); - toggleOffOnline.setFocusPainted(false); - toggleOffOnline.setIcon(new ImageIcon("src/images/on.png")); + public JLabel getOptionsBtn() { + return optionsBtn; + } - toggleOffOnline.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (logic.isOnline()) { - toggleOffOnline.setIcon(new ImageIcon("src/images/off.png")); - toggleOffOnline.setPressedIcon(new ImageIcon("src/images/on.png")); - logic.setOnline(false); - } - else{ - toggleOffOnline.setIcon(new ImageIcon("src/images/on.png")); - toggleOffOnline.setPressedIcon(new ImageIcon("src/images/off.png")); - logic.setOnline(true); - } - } - }); + public JLabel getDisconnectBtn() { + return disconnectBtn; + } - JScrollPane scrollPane = new JScrollPane(historyView); - scrollPane.setLocation(10, 140); - scrollPane.setSize(740,350); - scrollPane.setBorder(linebord); + public JLabel getLogoutBtn() { + return logoutBtn; + } - AddNewContact = new JButton("Add new Contact"); - AddNewContact.setLocation(720,30); - AddNewContact.setSize(150,25); - AddNewContact.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - NewContactFrame ncf = new NewContactFrame(logic.getContactsViewModel()); - } - }); - - Apply = new JButton("Apply"); - Apply.setLocation(10, 70); - Apply.setSize(115,25); - Apply.setFont(font); - Apply.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - applyNick(); - } - }); - - Disconnect = new JButton("Disconnect"); - Disconnect.setLocation(600,99); - Disconnect.setSize(150,25); - Disconnect.setFont(font); - Disconnect.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - logic.disconnect(); - } - }); + public JTextArea getHistoryView() { + return historyView; + } + public JTextArea getMessageField() { + return messageField; + } - - Connect = new JButton("Connect"); - Connect.setLocation(450,80); - Connect.setSize(100, 30); - Connect.setFont(font); - Connect.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - logic.getHistoryViewModel().clearView(); - logic.call(EnterIp.getText()); - } - }); + public JLabel getRemoteNick() { + return remoteNick; + } - Connect = new JButton("Connect"); - Connect.setLocation(600,70); - Connect.setSize(150, 25); - Connect.setFont(font); - Send.addActionListener(new ActionListener( ) { - public void actionPerformed(ActionEvent ae) { - send(); - } - }); + public JLabel getLocalNick() { + return localNick; + } - final JFrame frame = this; - addWindowListener(new java.awt.event.WindowAdapter() { - @Override - public void windowClosing(java.awt.event.WindowEvent windowEvent) { - if (logic.isConnected()){ - if (JOptionPane.showConfirmDialog(frame, - "You have an established connection with another user.\n Are you sure you want to close the program?", "Warning", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - exit(); - } - } - else{ - exit(); - } + public JLabel getSendBtn() { + return sendBtn; + } - } - }); - contactsView.setLocation(800,100); - panel.add(contactsView); - panel.add(login); - panel.add(textfieldlogin); - panel.add(messageArea); - panel.add(scrollPane); - panel.add(EnterIp); - panel.add(remoteAdress); - panel.add(toggleOffOnline); - panel.add(Send); - panel.add(Apply); - panel.add(Disconnect); - panel.add(Connect); - panel.add(AddNewContact); - panel.add(Options); - - - this.add(panel); - - setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - setSize(Widht, Height); - setResizable(false); - setLocationRelativeTo(null); - setTitle("ChatApp"); - setConnected(false); - setVisible(true); - - - } + public ContactsView getContactsView() { + return contactsView; + } - public void exit(){ - logic.exit(); - System.exit(0); + public void setConnected(){ + disconnectBtn.setEnabled(true); + sendBtn.setEnabled(true); + messageField.setEnabled(true); + logoutBtn.setEnabled(false); + messageField.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Colors.midGreen)); } - public void send(){ - text = messageArea.getText(); - if (Protocol.isMessageValid(text)) { - logic.sendMessage(text); - logic.getHistoryViewModel().addLocalMessage(text); - messageArea.setText(""); - } + public void setDisconnected(){ + disconnectBtn.setEnabled(false); + sendBtn.setEnabled(false); + messageField.setEnabled(false); + logoutBtn.setEnabled(true); + messageField.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Colors.lightGreen)); } - public void applyNick(){ - text=textfieldlogin.getText().replaceAll("\\s+", "_").replaceAll("\\-+","-").replaceAll("_+","_"); - if (text.endsWith("_")) text=text.substring(0,text.length()-1); - textfieldlogin.setText(text); - if (Protocol.isNickValid(text))logic.setLocalNick(text); - else { - UltimateGUI ultimateGUI = new UltimateGUI("Only latin, numbers and \"_\" \"-\" "); + private static class MyScrollbarUI extends MetalScrollBarUI { + + private Image imageThumb, imageTrack; + private JButton b = new JButton() { + + @Override + public Dimension getPreferredSize() { + return new Dimension(0, 0); } - } - public void setConnected(boolean b){ - if (b){ - Connect.setEnabled(false); - Apply.setEnabled(false); - Send.setEnabled(true); - Disconnect.setEnabled(true); - textfieldlogin.setEditable(false); - EnterIp.setEditable(false); - messageArea.setEnabled(true); - toggleOffOnline.setEnabled(false); + }; + + MyScrollbarUI() { + imageThumb = PlainImage.create(15, 15, Colors.mainGreen); + imageTrack = PlainImage.create(15, 15, Colors.darkGreen); } - else { - Connect.setEnabled(true); - Apply.setEnabled(true); - Send.setEnabled(false); - Disconnect.setEnabled(false); - messageArea.setEnabled(false); - textfieldlogin.setEditable(true); - EnterIp.setEditable(true); - toggleOffOnline.setEnabled(true); + + @Override + protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { + g.translate(thumbBounds.x, thumbBounds.y); + g.setColor( Color.red ); + g.drawRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 ); + AffineTransform transform = AffineTransform.getScaleInstance((double)thumbBounds.width/imageThumb.getWidth(null),(double)thumbBounds.height/imageThumb.getHeight(null)); + ((Graphics2D)g).drawImage(imageThumb, transform, null); + g.translate(-thumbBounds.x, -thumbBounds.y ); } - } - public void changeEnterIp(String ip){ - EnterIp.setText(ip); + @Override + protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { + g.translate(trackBounds.x, trackBounds.y); + ((Graphics2D)g).drawImage(imageTrack,AffineTransform.getScaleInstance(1,(double)trackBounds.height/imageTrack.getHeight(null)),null); + g.translate(-trackBounds.x, -trackBounds.y ); + } + + @Override + protected JButton createDecreaseButton(int orientation) { + return b; + } + + @Override + protected JButton createIncreaseButton(int orientation) { + return b; + } } - public void setOffline(){ - Connect.setEnabled(false); - Apply.setEnabled(true); - Send.setEnabled(false); - Disconnect.setEnabled(false); - textfieldlogin.setEditable(true); - EnterIp.setEditable(false); + private static class PlainImage { + + static public Image create(int w, int h, Color c) { + BufferedImage bi = new BufferedImage( + w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = bi.createGraphics(); + g2d.setPaint(c); + g2d.fillRect(0, 0, w, h); + g2d.dispose(); + return bi; + } } } diff --git a/src/NewContactFrame.java b/src/NewContactFrame.java index 6b0bbb0..0cfa299 100644 --- a/src/NewContactFrame.java +++ b/src/NewContactFrame.java @@ -3,6 +3,7 @@ import java.awt.event.ActionListener; public class NewContactFrame extends JFrame{ + /* JButton Cancel; JButton Accept; @@ -76,5 +77,6 @@ public void actionPerformed(ActionEvent e) { public void close(){ dispose(); } + */ } diff --git a/src/PopUp.java b/src/PopUp.java index 4b4f0aa..e053f2e 100644 --- a/src/PopUp.java +++ b/src/PopUp.java @@ -3,7 +3,7 @@ import java.awt.event.ActionListener; public class PopUp extends JPopupMenu { - +/* JMenuItem delete,fav,call; Contact contact; ContactPanel contactPanel; @@ -50,4 +50,5 @@ public void actionPerformed(ActionEvent e) { } }); } + */ } \ No newline at end of file diff --git a/src/font/roboto-thin.ttf b/src/font/roboto-thin.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d43e943312e0f2c653815dd791d93f94f0abd73f GIT binary patch literal 162636 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

v_-2rcF-PPXC&3E_Kh$ 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 XpLxoEqDV@O#}+R!3HI=xso?(riteo( literal 0 HcmV?d00001 diff --git a/src/font/roboto-thin9.ttf b/src/font/roboto-thin9.ttf new file mode 100644 index 0000000000000000000000000000000000000000..861d63adb6dee256c5d63879c323d27b5b0b047b GIT binary patch literal 122512 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 literal 0 HcmV?d00001 diff --git a/src/images/disconD.png b/src/images/disconD.png new file mode 100644 index 0000000000000000000000000000000000000000..3574afb74657d0a1471afcf77a7b1ba656bf612f GIT binary patch literal 3922 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 gpg=LOcqyPW_07*qoM6N<$g5PU&UjP6A literal 0 HcmV?d00001 diff --git a/src/images/disconH.png b/src/images/disconH.png new file mode 100644 index 0000000000000000000000000000000000000000..fea9b838afb938a919137092e1925ec1828ce5fd GIT binary patch literal 3915 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 literal 0 HcmV?d00001 diff --git a/src/images/disconN.png b/src/images/disconN.png new file mode 100644 index 0000000000000000000000000000000000000000..3c9f83b81933aa232c2a25112e06faeb76be16e2 GIT binary patch literal 3917 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 b;*S9Usc;!+eYL&Y00000NkvXXu0mjfjT2s} literal 0 HcmV?d00001 diff --git a/src/images/disconP.png b/src/images/disconP.png new file mode 100644 index 0000000000000000000000000000000000000000..ce0ce67cf124306701265fe5b4356595f9d61ee1 GIT binary patch literal 3898 zcmV-A55@3_P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000DKNklS_`Gc#rvGn+=Yusjx3cH0Hp35Omi zIF$W4&rSP%PtW`Hf@*>a)dbZ96;Im)zdMSU z$g0s@BubX@^SJIHm7lA;s;^2biq} z+7C2K^KH8u{+Ge*JUJ+f&ofC!H4WwLIpQQfmd5Xns$2RAp=m$V^uc0(e6J@=>#Np+ zd23?@zkKcGrFOftBPwlW7|nWenLIZy`sx1mK2ymYmp}LLIP)8IEoSNZp6>;2ocA%6 z%t@cK`GKpW{Csv;;$xvH?9C=LC99?Nw0aF!PJ0yB2n#9h{2HR~N&sPKg3;kPKYeiz z#|8_fYqS80I?dGvDs5#&b^6^=Zk+ewcTW%=Ns@?6%k(dMI#Pn!Y9Qnn6*+HzglX|H z2aj)-QdQTPyZGHv94-q3S4W9Q(hKrqLQ2M8Kj-DrsUg|X@A*NX$QDDdVx&jWDn>@enPgS5`eZ`AYYyEi#}Vk<_I zuBc9j%d#@*Xf_S`A zOv*a2*WX@`R~>o41-I17`GE=(+d^05Opz;F*xsGOb?2_I(YE48<0u z^O+Xr`7o7KZb_RrRdeao5ZBLnacs0;Z7i2{Q>pPBMw3oJ+dMd|!UnBcvgP@LE5HB#{o`V;hb1u$C; zY}?y}Rl={x|NcG?R9>g|gV^d;xz!$Jx%ku7*g{5y?Bv49rS2@nZPKK*nQ2~5iJ zW+IwqxHm$_QJ2((%thO-1}=Q!X8XP-*4m8(eMxS8BXIb{Rsc5dT*p@*^^r~Isa;<| z(3iy19iroy3xI8Vo4D|?o78xQMrRd4Uy|O-BXk^fEo(~XIO>w=d@vYe@cJVT9^Z`7 zq${d(_6e_qc_2%52qAM*?Tr578#%R__-AXQ#XY4_W@llbrbw;d><+4s@ zF0WMV!a|CmFR6I_-1YuS6#zz)j$9^>7|#}H4GSsRY072t1UwU@Vi{`JR}`GIVmie{ zmXVKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0009DNklPf6%FO)^OqwHA`s(ZbjnKepLD~?ILQKcamg8SCw`X z7-U`SPE8aqr0v2%GfPxTO2<^zqOGQmvBuW?DaD^T=Gm_5$^&ORt0Xpceb3$Zedm3@ zbKd9u`_4I0T8H8%2(}e_hzSS@1cU@a0s$d`kU$VF64sX2;iY`U%G*i83~*ep(xj|xm}dM7-v&EljVI5jO9g+&DcUO72t1OUXv#~|mB5Oa=2)<_nRPn-RH@(9_z7p%)s6 z#CbR-6=B+7MN_?;t|eZ|hsJt2Qqz)AQXv5V+?GAU$m@xqI`vu;o<8V8hLBIoTra;% z8+B#b#VX%?=NSMXJ#!y~IqBdFc*xBcB8AI|+(@uloS1lTK)dQ8Jy+XMg|-$YbehkQ zmWeT-8pTD~DcWnLWnuup#5)74PA}!dlg3VTs+*C-O=QTZpTEJ)`?5dY7hk{wwc*8v z#~acJ*b$LnnX=<*-MN7Iic7_`k*zZhh{Sn}b%AhyP@VJ6MU${k@Mb;B84~)&X|!t(27z64*d_e5^&)KgDbX9yi50Z@11768w>xmW+?%Cf4)C zruQds;_=n|_x*YbmxH&j##!snCSv1a;97P66VVYvLSaz>%wKHKYfTJey;P2%^2$lN zJ}5f+8QoHsToqJj5;qa2FC0ftYab)By5uU{YO0~r+p6DTthakGK-bvthp^ZoDwJQ= z^jcHURKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z00094Nkl2f(aHw6g2$tKvWWi5WJWu2M=D1 z7Y@eYO%EOk2M$EQ3kfk2Bhn}dqLCt1AP6nCP_!(qvD5+u=~kNPf$+?>TZNbwsPA{% zw>z`D^Sya9Z`-7n_t$=cvaRf)A)q2qs0dU93KfBhK#7+K%L@Snxe$%=c8V}QI18`K zzbgZ?ybyrP;iJpZcH;L1;Iz%hEIUW`fJ%!}x#`w_2*nSY=%SIvY)}yq$%i zRD(B9Cs9(JOJ=R1R09C`FyIvQ8RSA}yW5Y3wi?J%Bs`nGM-!;MQS{g6Vw7?O10is$ zYcb7$9gzu6+dL{R=SPeepFcu^%-X%shiEeR^Gf96zHa0&HL?)!@ZL3*9FO zAr#Q?*~cj5Tk@Hiy(`2It1{L7~WDnd94MvBRIbAtPe zjoB}ii7_|*ZPPyGsS=oahhS7xkn* z7~4m9-BMTcP*k6?6bT9|4x_tun3q{y%|ocYSxlC<@Xc6n$5@1~k(V=Zi9=E>e_6Ad z*{H2#WnIB~7s7?m+c8GkN2yGV@|rwe9SHZM(lKD|UR=Fh7WLky9vkeVZgPD3f*#tu z^xp;C?dJ)lJEnwv)XnqR*=T}?UBcNb2A=J%5+PPNCca|0&#|d?^S}Rg6bph}2*&mi zvI$EBM7!Z3I~z?{T@C^ORu(xJ$}}-u9^y?B{kH{OjwnKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z00093Nklxwoum{Ms*2>?saW7f=x>s0dU93Mv8>fnv8rNW@Y|C$rScdnv+O9s$jPxGyy}_4(Ha%8d46XE5ndosE@l7n zsS5zW7*(jQQ=?K}0i#(9wN75TnZSGF`0iZBeS0gJYwvErklh8(WDu4X118@t;6m3) z61A2V0|3B1u`J1%PG&Lq>@yy|XoX5E7ukG#?M3I^Gk<+9onDF5Mg}P%Q`8LD7n$Ja zVraWnpEqyos3lJJ&&FV~GNS9uaaBRimB2czmkmUKEm&F%7#r}>bAlA49GV+_I=ym7 zoQ0y-)_IJIAXz>(GLlU-x5VrONg*RSA6i(WBIH8DC@}l!r{wvfQeOc8P^!zp&#!Kq zQ>~L@YIsqy|7<~iLb{b6)~6YZ89z3zgX zDyfGCY;15YkL&A)z^?eAteAh+I5t#pD|xlM5jwpR z>0}mTgL7n0u2h%f;ti9i59IO~>$ULD#&Gk|xq@r64j<;H!(`liy$&Z%>&Q653wYN* zL&m494Cbf9BF-VZ3t@i(S8kg{wg*atVqxK11Yh0V9uv{_}wLDM}Bv2}4r)0_z6@h|^Kt-S^8h#G|aP%4KMPEP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0008}Nkl zyYroUXDXh|4*mvFS#^n&fIuK35C{Y!0)apvIxh&Ry$rHkj#Rmv5IoKpb|dKvS(wyb z20MX$Vn=0#z;XzCljm}+s{i4EcLMwT1FIMX%r;HIsPs5v006nN5(cvxiEygmyuZa* zy0wrgD*0nkf3hHc#j%a6gAMSx;t0A~u+c*RfT6V(^(K{c--P)o4_uCIm^v9)db9|- zSxk;O`CgLca+n-*Ld7W1aYGLPc=`AfzP=9^&FOQ+G5dTKbqy3B^Wwn=zNjgd6OEsJ zx(NVa3>36YHK27$7+PyV(F*C>(N16=Tgx%nN7_N_l%Ujd3=CVq7%6x@ACKZM?}E5? zvl(Wa1~Nq@ti5^+-M?HkXO_$1^|N){e%OV^Hd+v~^d@ks`=WJ9)EOzLHA>W*RG`#y z=|Tv5ljytKQn24P*nq9&V+av{5~fZ@aJUn)ic-%4C`eQ`4St7~M4 zen0(uouU<3oezojpA*TetB_9q8)(TuFxxZ;ZSBG5iVKRh{PqWWhMM{QurTcv%$6Jz z?&1*Ca&!$CF+b%IyjhM3H-<*7yuIZw#ujJ%1-ZU0M$V-U6{Y;M=5xi1P9+D1EueKu zWVsv`XZ-y1vARZv>$gmTK9HYB-%^W+KZ!e!x{6+V#j%aRT7uv1wl|@%jpn!6=%E0V zsgprqEg{G`H@<;HD24uet%BGK1))?5Zn9Ya7=eAHoqRhghq7D_3)5b7^yww@iKOZx z+~nN&296H00DyEd18c9or1n4{h)627BN;m)5Qqo_0)ePx`ZECE94DBv52fs&o!|zKIL2(cpJ9%5m-AOdyf(|eKgZVJ zT_oJ7>pEdkiBFP$l(4I`?+Le+_&=_Vh>tGkPn@Co1%2akDCMDrhiBL{Q4GJ0x0000DNk~Le0000F0000F2nGNE06W%|O0glH3V#77 zNklhf(Hre2Z)!|K*+`Xu^%LO3bME!%m;W-M2L>Phs}f}I$MV@ z>FJuDswvrQHgs?Rz5wAJ1{{HvFT|bASAgUpz_RI6{3k>-v(O2^Fg}}&bg3D0Nif3Fxkuy`TSZU!gM-CRo}^G#$vJHKg{Rz z-oG}*y|gEbr~m)}07*qo IM6N<$g5DISYybcN diff --git a/src/images/on.png b/src/images/on.png index 92b9f07098e3f2e119d5045dc394f0e15475b49c..8c8b95521b607b416d7c87a2f74089cd99066492 100644 GIT binary patch delta 191 zcmZpXxFn|78Q|y6%O%Cdz`(%k>ERLtq`5(ugAGWoTvM*LQE?tuJwvUhi(^Q|ttl55 z3LbJ0alN?tHse{A&1^>}lqIk;3bC|Q8962C7!>jxe)b~!-|vGn+`9e3m#yY5OoqlAcf!*;rx2(%9 ryVUtiFV5O+@LKb+g_!&gNd^(eovI=xsrAc&Zes9s^>bP0l+XkKx-LiV delta 370 zcmV-&0ge9B7K9ifiBL{Q4GJ0x0000DNk~Le0000F0000F2nGNE06W%|O0glH3V#7F zNkl7xl5QM)yJ47J}C@Bz92%sQ0_>FlWBq|F90t$3VLBUUmfMZj{wrs;O zg>~NBxxJYi84ibJFaurz;Tj6efo@XpGn*F&DS-f;xX+M$TtpRh9dbz~&x*v8e`48K zV$^W<&h%EjTL--RvBnhR-k8bD1b=Jnf0qiQw-No%KH)UXwsuW2RYf^I9EsXdN=)k3 zvqbHP&GRNV-J~lOR*jX@Efr;5UFUo-*#;}tWfB1Ur#*^;tfGkCel1an&rXnr?y|0YfjqC)w%Ot1*>te4(ERPd}vun4Yf@Z-pJl1exY8O#Q3%ST%dj|WPp z67^mEmYlgCXEm2FQK7o8Qu(RmG!}B+HO1@aG`WeU=(dR^xZX;Ek1XeJ0F>!%W)ny! QF8}}l07*qoM6N<$g1J1NbpQYW diff --git a/src/images/optionsH.png b/src/images/optionsH.png new file mode 100644 index 0000000000000000000000000000000000000000..3d12cd89da4a0df9e726d84cbca455b122b244d3 GIT binary patch literal 705 zcmV;y0zUnTP)~1AEyFL1vnKDomqdAR6tSzrUK#$a4Nu5fcdue$f*Fk0!#&%e@nx;dv!zx@X679c zO^67w_#Q-L75y6k+?<)eUn+TsT~+U5B@IAXmeabff3_2->R)DF)pfldjYbF9Pp8ww z7;~$t_ssmNBjzOloSbt@BKnvF?2R$LJ*xWF_mp`b$t}xr0$?KYw&3@}TI+f2;vgk+ zFCtSob+qqML>4iLa}GR2kTdenNG^&3q`gO5vMigVW0(#=Nc>4eP%)vBs;YKqm#S{9 zwfR{EVa_pz^I&)qk-2kj9+QL?!u}=r97W_RsUmIfqcP?Q7vxrX-oLJEe*5;(pzSFz#=HqYzXEZ7-c=Q&?0IPe1j4<4`t<_jd46!t zVJu&_gA#y7F8anC0F8*nK(uH!n{B(wPQf60S(fGKb&v1fPf;Kl1V+8_BnKN75CnQ9 zC8FPHRS~4BswFdj2tEsA%+iyb-YBW&2p;;>vM7pGlFZ9@%eiedv1hMP$$q n!yu>7zEty{MWpkcZeR2t<;{VP%EBN{00000NkvXXu0mjfgRn|^ literal 0 HcmV?d00001 diff --git a/src/images/optionsN.png b/src/images/optionsN.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0ea6a857afd373e4c9f5c0a14ca5c050b612b6 GIT binary patch literal 708 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 FV1j;|lcoRw literal 0 HcmV?d00001 diff --git a/src/images/sendH.png b/src/images/sendH.png new file mode 100644 index 0000000000000000000000000000000000000000..4bbb569fac60b43e93290d26a942d8a44f875905 GIT binary patch literal 4508 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 literal 0 HcmV?d00001 diff --git a/src/images/sendP.png b/src/images/sendP.png new file mode 100644 index 0000000000000000000000000000000000000000..7ca926af7afc90a0f9be52258207b86dfec9a5b8 GIT binary patch literal 4608 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 literal 0 HcmV?d00001 diff --git a/src/images/signuptP.png b/src/images/signuptP.png new file mode 100644 index 0000000000000000000000000000000000000000..4584f567084f49532c29ad33454edf5e718f2775 GIT binary patch literal 3706 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 Y0KgAplC3E(l>h($07*qoM6N<$g1fx Date: Sat, 19 Dec 2015 00:36:52 +0200 Subject: [PATCH 32/38] still wip --- src/LogInWindow.java | 188 +++++++++++++++++++++++++++++++++++ src/images/ButtonPattern.psd | Bin 0 -> 87408 bytes src/images/checked.png | Bin 0 -> 2989 bytes src/images/checkedH.png | Bin 0 -> 2982 bytes src/images/noH.png | Bin 0 -> 3248 bytes src/images/noN.png | Bin 0 -> 3248 bytes src/images/noP.png | Bin 0 -> 3245 bytes src/images/notchecked.png | Bin 0 -> 2898 bytes src/images/notcheckedH.png | Bin 0 -> 2898 bytes src/images/okH.png | Bin 0 -> 3238 bytes src/images/okN.png | Bin 0 -> 3237 bytes src/images/okP.png | Bin 0 -> 3243 bytes src/images/yesH.png | Bin 0 -> 3421 bytes src/images/yesN.png | Bin 0 -> 3420 bytes src/images/yesP.png | Bin 0 -> 3414 bytes 15 files changed, 188 insertions(+) create mode 100644 src/LogInWindow.java create mode 100644 src/images/ButtonPattern.psd create mode 100644 src/images/checked.png create mode 100644 src/images/checkedH.png create mode 100644 src/images/noH.png create mode 100644 src/images/noN.png create mode 100644 src/images/noP.png create mode 100644 src/images/notchecked.png create mode 100644 src/images/notcheckedH.png create mode 100644 src/images/okH.png create mode 100644 src/images/okN.png create mode 100644 src/images/okP.png create mode 100644 src/images/yesH.png create mode 100644 src/images/yesN.png create mode 100644 src/images/yesP.png diff --git a/src/LogInWindow.java b/src/LogInWindow.java new file mode 100644 index 0000000..e4aba3b --- /dev/null +++ b/src/LogInWindow.java @@ -0,0 +1,188 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.io.IOException; + +public class LogInWindow extends JFrame { + JTextArea login; + JPasswordField password; + + JLabel signupBtn,signinBtn; + + public LogInWindow(){ + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setLocationRelativeTo(null); + setSize(new Dimension(300,200)); + setLocation(getX()-150,getY()-100); + setAlwaysOnTop(true); + setResizable(false); + createGUI(); + setVisible(true); + } + + private void createGUI(){ + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setBackground(Colors.softGreen); + panel.setBorder(BorderFactory.createMatteBorder(2,2,2,2, Colors.midGreen)); + add(panel); + + Font font = null; + try { + font = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + } catch (FontFormatException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + login = new JTextArea(); + login.setFont(font); + login.setForeground(Color.lightGray); + login.setBorder(BorderFactory.createMatteBorder(1,1,1,1, Colors.midGreen)); + login.setBackground(Color.white); + login.setText("Login"); + login.setBounds(55, 30, 182, 30); + //login. + + login.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + if (login.getText().equals("Login")) { + login.setForeground(Color.black); + login.setText(""); + password.setForeground(Color.black); + password.setText(""); + signinBtn.setEnabled(true); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + login.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + if (login.getText().equals("Login")) { + login.setForeground(Color.black); + login.setText(""); + password.setForeground(Color.black); + password.setText(""); + signinBtn.setEnabled(true); + } + } + + @Override + public void keyPressed(KeyEvent e) { + + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); + + panel.add(login); + + password = new JPasswordField(); + password.setFont(font); + password.setBounds(55,65,182,30); + password.setForeground(Color.lightGray); + password.setBackground(Color.white); + password.setText("password"); + password.setBorder(BorderFactory.createMatteBorder(1,1,1,1, Colors.midGreen)); + panel.add(password); + + password.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + String tmp =""; + for ( char c : password.getPassword() ){ + tmp=tmp+c; + } + System.out.println(tmp); + if (tmp.equals("password")){ + password.setForeground(Color.black); + password.setText(""); + login.setForeground(Color.black); + login.setText(""); + signinBtn.setEnabled(true); + } + + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + + signinBtn = new JLabel(""); + signinBtn.setIcon(new ImageIcon("src/images/signingN.png")); + signinBtn.setDisabledIcon(new ImageIcon("src/images/signingD.png")); + signinBtn.setBounds(40,105,97,27); + signinBtn.setEnabled(false); + panel.add(signinBtn); + + signupBtn = new JLabel(""); + signupBtn.setIcon(new ImageIcon("src/images/signuptN.png")); + signupBtn.setDisabledIcon(new ImageIcon("src/images/signuptD.png")); + signupBtn.setBounds(157,105,97,27); + signupBtn.setEnabled(true); + panel.add(signupBtn); + + } + + public JLabel getSignupBtn() { + return signupBtn; + } + + public JLabel getSigninBtn() { + return signinBtn; + } + + public static void main(String[] args) { + LogInWindow asd = new LogInWindow(); + } + + + +} \ No newline at end of file diff --git a/src/images/ButtonPattern.psd b/src/images/ButtonPattern.psd new file mode 100644 index 0000000000000000000000000000000000000000..4d71212dbf6cc06dfb94a4898ca1bdf93da62890 GIT binary patch literal 87408 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 literal 0 HcmV?d00001 diff --git a/src/images/notchecked.png b/src/images/notchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..1441e3563c6ef2b3625cbe38f0fdf78187a052a7 GIT binary patch literal 2898 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 Y01e5Gj+-vs`v3p{07*qoM6N<$g262P?*IS* literal 0 HcmV?d00001 diff --git a/src/images/okN.png b/src/images/okN.png new file mode 100644 index 0000000000000000000000000000000000000000..a8f38c5ebe62377a48f7fed25a5f272fe421a4c5 GIT binary patch literal 3237 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 XXDyBl6s5BV00000NkvXXu0mjfwE_JC literal 0 HcmV?d00001 diff --git a/src/images/okP.png b/src/images/okP.png new file mode 100644 index 0000000000000000000000000000000000000000..cdae71dd69fc08af708b033cacd0d7c87da8f127 GIT binary patch literal 3243 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 literal 0 HcmV?d00001 diff --git a/src/images/yesH.png b/src/images/yesH.png new file mode 100644 index 0000000000000000000000000000000000000000..6018854c63d6cfd479cc7a5c03ce5dc18daad306 GIT binary patch literal 3421 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 literal 0 HcmV?d00001 From 71fbee2603ef723e9956b1e8ff001ba3d6fc59bf Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Sun, 20 Dec 2015 01:32:11 +0200 Subject: [PATCH 33/38] Starting to connect with the server --- src/CallListener.java | 22 +--- src/Caller.java | 3 +- src/Command.java | 2 + src/CommandType.java | 3 +- src/Connection.java | 59 +++++++-- src/Contact.java | 40 ++----- src/ContactPanel.java | 16 +-- src/ContactsView.java | 8 +- src/HistoryViewModel.java | 24 ++-- src/LogInWindow.java | 16 ++- src/Main.java | 246 ++++++++++++++++++++++++++++++-------- src/MainGUI.java | 25 ++-- src/OptionsFrame.java | 86 ++++++------- src/Protocol.java | 8 ++ src/ServerCaller.java | 17 +++ src/SignUpWindow.java | 246 ++++++++++++++++++++++++++++++++++++++ src/UltimateGUI.java | 81 ++++++++++--- 17 files changed, 683 insertions(+), 219 deletions(-) create mode 100644 src/ServerCaller.java create mode 100644 src/SignUpWindow.java diff --git a/src/CallListener.java b/src/CallListener.java index caa3e4e..c65a7b3 100644 --- a/src/CallListener.java +++ b/src/CallListener.java @@ -25,27 +25,7 @@ public CallListener(String localNick){ public Connection getConnection() throws IOException { Connection connection = new Connection(serverSocket.accept()); - if (!isOnline){ - connection.disconnect(); - return null; - } - if (!isBusy){ - connection.sendNickHello(localNick); - lastCommand=connection.recieve(); - if (lastCommand.type==CommandType.NICK){ - nickCommand = (NickCommand) lastCommand; - remoteNick = nickCommand.getNick(); - return connection; - } - else{ - return null; - } - } - else{ - connection.sendNickBusy(localNick); - connection.disconnect(); - return null; - } + return connection; } public void setBusy(boolean isBusy) { diff --git a/src/Caller.java b/src/Caller.java index 503401d..364e621 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -22,7 +22,6 @@ public Connection call() throws IOException { lastCommand = connection.recieve(); if (lastCommand.type==CommandType.NICK) { nickCommand = (NickCommand) lastCommand; - } else{ connection.disconnect(); @@ -44,7 +43,7 @@ public Connection call() throws IOException { } //Если не занят, то отсылаем ник и ждём подтверждения - connection.sendNickHello(localNick); + lastCommand = connection.recieve(); //Если Accept - вернуть connection diff --git a/src/Command.java b/src/Command.java index 0100119..8ef055c 100644 --- a/src/Command.java +++ b/src/Command.java @@ -22,6 +22,8 @@ public static Command getCommand(Scanner in){ if (string.contains(Protocol.REJECTED)) return new Command(CommandType.REJECT); if (string.contains(Protocol.GREETING)) return new NickCommand(string); if (string.contains(Protocol.MESSAGE)) return new MessageCommand(Protocol.decode(in.nextLine())); + if (string.contains(Protocol.HELLO_SERVER)) return new Command(CommandType.HELLO_SERVER); + if (string.contains(Protocol.HELLO_CLIENT)) return new Command(CommandType.HELLO_CLIENT); return null; } diff --git a/src/CommandType.java b/src/CommandType.java index ea2c45f..03d23e9 100644 --- a/src/CommandType.java +++ b/src/CommandType.java @@ -1,3 +1,4 @@ public enum CommandType { - ACCEPT,REJECT,DISCONNECT,NICK,MESSAGE + ACCEPT, REJECT, DISCONNECT, NICK, MESSAGE, HELLO_SERVER, HELLO_CLIENT, CALL, LOGIN, SIGNUP, DISCONNECT_FROM_USER, + CONTACTS, CONTACT, ONLINE_CONTACTS } diff --git a/src/Connection.java b/src/Connection.java index a7e7653..6a1b128 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -19,8 +19,44 @@ public Connection(Socket socket){ } } + public void sendServerHello(){ + try { + out.write(new StringBuilder(Protocol.HELLO_SERVER).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.HELLO_CLIENT; + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void sendCall(String nick){ + try { + out.write(new StringBuilder(Protocol.CALL).append(" ").append(nick).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.CALL; + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void sendLogin(String nick,String password){ + try { + out.write(new StringBuilder(Protocol.LOGIN).append(" ").append(nick).append(" ").append(password.hashCode()).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.LOGIN; + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void sendSignUp(String nick,String password){ + try { + out.write(new StringBuilder(Protocol.SIGNUP).append(" ").append(nick).append(" ").append(password.hashCode()).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.SIGNUP; + } catch (IOException e) { + e.printStackTrace(); + } + } - public void sendNickHello(String nick){ + + /*public void sendNickHello(String nick){ try { out.write(new StringBuilder(Protocol.GREETING).append(nick).append("\n").toString().getBytes("UTF-8")); lastCommand=CommandType.NICK; @@ -29,6 +65,8 @@ public void sendNickHello(String nick){ } } + + public void sendNickBusy(String nick){ try { out.write(new StringBuilder(Protocol.GREETING).append(nick).append(" busy").append("\n").toString().getBytes("UTF-8")); @@ -56,7 +94,7 @@ public void reject(){ } catch (IOException e) { e.printStackTrace(); } - } + } */ public void sendMessage(String message){ try { @@ -79,15 +117,20 @@ public void disconnect(){ } } - public String getRemoteIP(){ - return socket.getInetAddress().getHostAddress(); + public void disconnectFromUser(String nick){ + try{ + out.write(new StringBuilder(Protocol.DISCONNECT_FROM_USER).append(nick).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.DISCONNECT_FROM_USER; + }catch (Exception e){ + e.printStackTrace(); + } } - public Command recieve(){ - return Command.getCommand(in); + public void getContacts(){ + } - public CommandType getLastCommand(){ - return lastCommand; + public Command recieve(){ + return Command.getCommand(in); } } diff --git a/src/Contact.java b/src/Contact.java index 3983e44..b4cf3f2 100644 --- a/src/Contact.java +++ b/src/Contact.java @@ -1,13 +1,11 @@ public class Contact { - /* private boolean isFav,isOnline; - private String nick,IP; - private ContactsViewModel contactsViewModel; + private boolean isFav,isOnline; + private String nick; - public Contact(ContactsViewModel cvm,String nick,String IP){ - contactsViewModel=cvm; + + public Contact(String nick){ this.nick=nick; - this.IP=IP; isFav=false; isOnline=false; } @@ -32,43 +30,29 @@ public void setNick(String nick) { this.nick = nick; } - public String getIP() { - return IP; - } - - public void setIP(String IP) { - this.IP = IP; - } - public void changeFav(ContactPanel contactPanel){ + public void changeFav(){ if (isFav){ - contactsViewModel.removeContact(this,contactPanel); isFav=false; - contactsViewModel.add(this); } else { - contactsViewModel.removeContact(this,contactPanel); isFav=true; - contactsViewModel.add(this); } } - public void call(){ - contactsViewModel.call(this); - } - - public void remove(ContactPanel tmp){ - contactsViewModel.removeContact(this,tmp); - } - public boolean equals(Object object){ Contact tmp = (Contact) object; - if (nick.equals(tmp.nick) && getIP().equals(tmp.getIP())) return true; + if (nick.equals(tmp.nick)) return true; else return false; } + @Override + public int hashCode() { + return nick.hashCode(); + } + public void setFav(Boolean b){ isFav=b; } -*/ + } diff --git a/src/ContactPanel.java b/src/ContactPanel.java index d6e5f38..d43bc79 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -27,6 +27,7 @@ private void createGUI(){ } catch (IOException e) { e.printStackTrace(); } + setLayout(null); setBackground(Colors.midGreen); setMinimumSize(new Dimension(200, 25)); @@ -34,17 +35,17 @@ private void createGUI(){ setMaximumSize(new Dimension(200, 25)); label.setBounds(5, 3, 200, 20); label.setFont(font); - label.setText("Contact exz"); - label.setIcon(new ImageIcon("src/images/on.png")); + label.setText(contact.getNick()); + if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); + else label.setIcon(new ImageIcon("src/images/off.png")); add(label); } -/* + public boolean isFav(){ return contact.isFav(); } public void update(){ - if (contact.getNick()!=label.getText()) label.setText(contact.getNick()); if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); else label.setIcon(new ImageIcon("src/images/off.png")); } @@ -56,13 +57,6 @@ public String getNick(){ public Contact getContact(){ return contact; } -*/ - public static void main(String[] args) { - final JFrame test = new JFrame(); - test.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - test.add(new ContactPanel()); - test.setVisible(true); - } } diff --git a/src/ContactsView.java b/src/ContactsView.java index db79a8a..cf2ca67 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -42,15 +42,9 @@ private void createGUI(){ contactsP.setBackground(Colors.softGreen); contactsP.setAlignmentX(Component.LEFT_ALIGNMENT); add(contactsP); - - favouritesP.add(new ContactPanel()); - for (int i=0;i<15;i++) { - contactsP.add(new ContactPanel()); - } - + } - } public void setLabelFont(Font font){ contactsL.setFont(font); diff --git a/src/HistoryViewModel.java b/src/HistoryViewModel.java index 9b03950..a33a271 100644 --- a/src/HistoryViewModel.java +++ b/src/HistoryViewModel.java @@ -1,3 +1,4 @@ +import javax.swing.*; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; @@ -6,14 +7,13 @@ import java.util.List; public class HistoryViewModel { - /* - Logic logic; + String localNick,remoteNick; List Messagelist = new ArrayList(); - HistoryView historyView; + JTextArea historyView; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk:mm:ss dd.MM.yy]"); - public HistoryViewModel(Logic logic){ - this.logic = logic; + public HistoryViewModel(){ + } private String composeString(String message){ @@ -21,12 +21,12 @@ private String composeString(String message){ } public void addRemoteMessage(String message){ - Messagelist.add(new StringBuilder(logic.getRemoteNick()).append(" ").append(composeString(message)).toString()); + Messagelist.add(new StringBuilder(remoteNick).append(" ").append(composeString(message)).toString()); notifyHistoryView(); } public void addLocalMessage(String message){ - Messagelist.add(new StringBuilder(logic.getLocalNick()).append(" ").append(composeString(message)).toString()); + Messagelist.add(new StringBuilder(localNick).append(" ").append(composeString(message)).toString()); notifyHistoryView(); } @@ -36,7 +36,7 @@ public void addSystemMessage(String message){ } public void notifyHistoryView(){ - historyView.updateYourself(); + } public int getSize(){ @@ -47,7 +47,7 @@ public String get(int i){ return Messagelist.get(i); } - public void setHistoryView(HistoryView historyView){ + public void setHistoryView(JTextArea historyView){ this.historyView = historyView; } @@ -56,7 +56,7 @@ public void writeHistoryFile(){ FileWriter out = null; try { out = new FileWriter(new StringBuilder(simpleDateFormat.format( - new Date(System.currentTimeMillis()))).append(logic.getRemoteNick()).append(".txt").toString()); + new Date(System.currentTimeMillis()))).append(remoteNick).append(".txt").toString()); for (String string : Messagelist){ out.write(string+"\n"); out.flush(); @@ -70,8 +70,8 @@ public void writeHistoryFile(){ public void clearView(){ Messagelist = new ArrayList(); - historyView.clear(); + historyView.setText(""); } -*/ + } diff --git a/src/LogInWindow.java b/src/LogInWindow.java index e4aba3b..0b02928 100644 --- a/src/LogInWindow.java +++ b/src/LogInWindow.java @@ -11,10 +11,11 @@ public class LogInWindow extends JFrame { JTextArea login; JPasswordField password; - JLabel signupBtn,signinBtn; + JLabel signupBtn, signinBtn; public LogInWindow(){ - setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setLocationRelativeTo(null); setSize(new Dimension(300,200)); setLocation(getX()-150,getY()-100); @@ -47,7 +48,7 @@ private void createGUI(){ login.setBackground(Color.white); login.setText("Login"); login.setBounds(55, 30, 182, 30); - //login. + login.getDocument().putProperty("filterNewlines", Boolean.TRUE); login.addMouseListener(new MouseListener() { @Override @@ -179,6 +180,15 @@ public JLabel getSigninBtn() { return signinBtn; } + public JTextArea getLogin() { + return login; + } + + public JPasswordField getPassword() { + return password; + } + + public static void main(String[] args) { LogInWindow asd = new LogInWindow(); } diff --git a/src/Main.java b/src/Main.java index 073c83d..6a85bfb 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,75 +1,179 @@ import javax.swing.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; +import java.awt.*; +import java.awt.event.*; +import java.io.IOException; public class Main extends JFrame { MainGUI mainFrame; + LogInWindow logInWindow; + SignUpWindow signUpWindow; + Connection connection; + + boolean isConnected; public Main() { - mainFrame = new MainGUI(); + try { + connection = ServerCaller.call(); + } catch (IOException e) { + e.printStackTrace(); + } + + if (connection==null) { + UltimateGUI ultimateGUI = new UltimateGUI("Could not connect to main server"); + } + else { + createMainFrame(); + createLogInWindow(); + createSignUpWindow(); + } + } - mainFrame.getDisconnectBtn().addMouseListener(new MouseListener() { + + + private void createLogInWindow(){ + logInWindow = new LogInWindow(); + + logInWindow.getSigninBtn().addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconP.png")); - } - }); + } @Override public void mousePressed(MouseEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconP.png")); - } - }); + logInWindow.getSigninBtn().setIcon(new ImageIcon("src/images/signingP.png")); + String tmp = ""; + for (char c : logInWindow.getPassword().getPassword()){ + tmp+=c; + } + tmp = "" + tmp.hashCode(); + connection.sendLogin(logInWindow.getLogin().getText(),tmp); + Command command = connection.recieve(); + if (command.type==CommandType.ACCEPT) { + mainFrame.setLocalNick(logInWindow.getLogin().getText()); + logInWindow.setVisible(false); + signUpWindow.dispose(); + mainFrame.setVisible(true); + } + else{ + UltimateGUI ultimateGUI = new UltimateGUI("Wrong login\\password"); + } } @Override public void mouseReleased(MouseEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconN.png")); - } - }); + logInWindow.getSigninBtn().setIcon(new ImageIcon("src/images/signingN.png")); } @Override public void mouseEntered(MouseEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconH.png")); - } - }); + logInWindow.getSigninBtn().setIcon(new ImageIcon("src/images/signingH.png")); } @Override public void mouseExited(MouseEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconN.png")); + logInWindow.getSigninBtn().setIcon(new ImageIcon("src/images/signingN.png")); + } + }); + + logInWindow.getSignupBtn().addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + logInWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptP.png")); + logInWindow.dispose(); + signUpWindow.setVisible(true); + } + + @Override + public void mouseReleased(MouseEvent e) { + logInWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptN.png")); + } + + @Override + public void mouseEntered(MouseEvent e) { + logInWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptH.png")); + } + + @Override + public void mouseExited(MouseEvent e) { + logInWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptN.png")); + } + }); + } + + private void createSignUpWindow(){ + signUpWindow = new SignUpWindow(); + + signUpWindow.getSignupBtn().addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + signUpWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptP.png")); + String tmp = "",tmp1 =""; + for (char c : signUpWindow.getPassword().getPassword()){ + tmp+=c; + } + for (char c : signUpWindow.getPasswordC().getPassword()){ + tmp1+=c; + } + if (tmp.equals(tmp1)) { + tmp = "" + tmp.hashCode(); + connection.sendSignUp(signUpWindow.getLogin().getText(), tmp); + Command command = connection.recieve(); + System.out.println("Getting accept"); + if (command.type == CommandType.ACCEPT) { + System.out.println("Got accept"); + mainFrame.setLocalNick(signUpWindow.getLogin().getText()); + signUpWindow.setVisible(false); + signUpWindow.dispose(); + mainFrame.setVisible(true); + } else { + UltimateGUI ultimateGUI = new UltimateGUI("User with such login already exists"); } - }); + + } + else{ + UltimateGUI ultimateGUI = new UltimateGUI("Passwords must match!"); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + signUpWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptN.png")); + } + + @Override + public void mouseEntered(MouseEvent e) { + signUpWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptH.png")); + } + + @Override + public void mouseExited(MouseEvent e) { + signUpWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptN.png")); } }); + } - mainFrame.getLogoutBtn().addMouseListener(new MouseListener() { + private void createMainFrame(){ + mainFrame = new MainGUI(); + + mainFrame.getDisconnectBtn().addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconP.png")); } }); } @@ -79,7 +183,7 @@ public void mousePressed(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconP.png")); } }); } @@ -89,7 +193,7 @@ public void mouseReleased(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutN.png")); + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconN.png")); } }); } @@ -99,7 +203,7 @@ public void mouseEntered(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutH.png")); + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconH.png")); } }); } @@ -109,19 +213,19 @@ public void mouseExited(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutN.png")); + mainFrame.getDisconnectBtn().setIcon(new ImageIcon("src/images/disconN.png")); } }); } }); - mainFrame.getOptionsBtn().addMouseListener(new MouseListener() { + mainFrame.getLogoutBtn().addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsP.png")); + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); } }); } @@ -131,7 +235,11 @@ public void mousePressed(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsP.png")); + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); + mainFrame.dispose(); + createMainFrame(); + createLogInWindow(); + createSignUpWindow(); } }); } @@ -141,7 +249,7 @@ public void mouseReleased(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsN.png")); + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutN.png")); } }); } @@ -151,7 +259,7 @@ public void mouseEntered(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsH.png")); + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutH.png")); } }); } @@ -161,7 +269,7 @@ public void mouseExited(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - mainFrame.getOptionsBtn().setIcon(new ImageIcon("src/images/optionsN.png")); + mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutN.png")); } }); } @@ -239,10 +347,52 @@ public void keyReleased(KeyEvent e) { } }); - mainFrame.setConnected(); + mainFrame.setDisconnected(); + + mainFrame.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + } + @Override + public void windowClosing(WindowEvent e) { + if (isConnected) { + + } + else{ + mainFrame.dispose(); + System.exit(0); + } + } + + @Override + public void windowClosed(WindowEvent e) { + + } + + @Override + public void windowIconified(WindowEvent e) { + + } + + @Override + public void windowDeiconified(WindowEvent e) { + + } + + @Override + public void windowActivated(WindowEvent e) { + + } + + @Override + public void windowDeactivated(WindowEvent e) { + + } + }); } + public static void main(String[] args) { Main main = new Main(); } diff --git a/src/MainGUI.java b/src/MainGUI.java index b24c75d..758de78 100644 --- a/src/MainGUI.java +++ b/src/MainGUI.java @@ -25,7 +25,7 @@ public class MainGUI extends JFrame { JLabel logoutBtn = new JLabel(""); JLabel disconnectBtn = new JLabel(""); - JLabel optionsBtn = new JLabel(""); + //JLabel optionsBtn = new JLabel(""); JLabel sendBtn = new JLabel(""); ContactsView contactsView = new ContactsView(); @@ -46,7 +46,7 @@ public MainGUI(){ private void createGUI(){ - setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); setMinimumSize(new Dimension(860,550)); setLayout(null); getContentPane().setBackground(Color.white); @@ -113,9 +113,9 @@ private void createGUI(){ sendBtn.setBounds(getWidth()-96,getHeight()-124, 51,51); add(sendBtn); - optionsBtn.setIcon(new ImageIcon("src/images/optionsN.png")); + /*optionsBtn.setIcon(new ImageIcon("src/images/optionsN.png")); optionsBtn.setBounds(getWidth()-47,getHeight()-70,24,24); - add(optionsBtn); + add(optionsBtn);*/ historyPane.getVerticalScrollBar().setPreferredSize(new Dimension(15, Integer.MAX_VALUE)); historyPane.getVerticalScrollBar().setUI(new MyScrollbarUI()); @@ -139,7 +139,6 @@ private void createGUI(){ resize(); - setVisible(true); addWindowStateListener(new WindowStateListener() { @Override public void windowStateChanged(WindowEvent e) { @@ -166,25 +165,19 @@ public void run() { }); } - - - /*public static void main(String[] args) { - NewGUI a = new NewGUI(); - }*/ - public void resize(){ leftBottomPanel.setBounds(-1,121,280,getHeight()-150); contactsPane.setBounds(25, 18, 240, leftBottomPanel.getHeight() - 50); historyPane.setBounds(321,40,getWidth()-370,getHeight()-195); sendBtn.setBounds(getWidth()-96,getHeight()-124, 51,51); - optionsBtn.setBounds(getWidth()-47,getHeight()-70,24,24); + //optionsBtn.setBounds(getWidth()-47,getHeight()-70,24,24); messageField.setBounds(320,getHeight()-122,getWidth()-430 /*430*/,50); } - public JLabel getOptionsBtn() { + /* public JLabel getOptionsBtn() { return optionsBtn; - } + } */ public JLabel getDisconnectBtn() { return disconnectBtn; @@ -234,6 +227,10 @@ public void setDisconnected(){ messageField.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Colors.lightGreen)); } + public void setLocalNick(String nick){ + localNick.setText(nick); + } + private static class MyScrollbarUI extends MetalScrollBarUI { private Image imageThumb, imageTrack; diff --git a/src/OptionsFrame.java b/src/OptionsFrame.java index ac97088..44f0bde 100644 --- a/src/OptionsFrame.java +++ b/src/OptionsFrame.java @@ -1,8 +1,11 @@ import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.io.File; +import java.io.IOException; public class OptionsFrame extends JFrame { @@ -10,6 +13,17 @@ public class OptionsFrame extends JFrame { JCheckBox contacts; JCheckBox history; JCheckBox nick; + static Font font; + + static { + try { + font = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + } catch (FontFormatException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } public OptionsFrame(){ super("Options"); @@ -17,26 +31,32 @@ public OptionsFrame(){ } private void createGUI(){ - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - setSize(200, 140); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(300, 140); setResizable(false); setAlwaysOnTop(true); setLocationRelativeTo(null); JPanel panel = new JPanel(); panel.setLayout(null); + panel.setBackground(Colors.softGreen); autoAddContacts = new JCheckBox("Add Contacts automatically"); contacts = new JCheckBox("Save Contacts"); history = new JCheckBox("Save History"); nick = new JCheckBox("Save Nick"); - contacts.setLocation(10,10); - contacts.setSize(120, 15); + setStyle(autoAddContacts); + setStyle(contacts); + setStyle(history); + setStyle(nick); + if (Options.saveContacts) contacts.setSelected(true); if (Options.saveHistory) history.setSelected(true); if (Options.saveNick) nick.setSelected(true); if (Options.autoSaveContacts) autoAddContacts.setSelected(true); + + contacts.setLocation(10,10); contacts.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -44,8 +64,8 @@ public void actionPerformed(ActionEvent e) { else Options.saveContacts = false; } }); + history.setLocation(10, 30); - history.setSize(120, 15); history.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -53,8 +73,8 @@ public void actionPerformed(ActionEvent e) { else Options.saveHistory=false; } }); + nick.setLocation(10,50); - nick.setSize(120,15); nick.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -64,7 +84,6 @@ public void actionPerformed(ActionEvent e) { }); autoAddContacts.setLocation(10,70); - autoAddContacts.setSize(195,15); autoAddContacts.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -73,43 +92,6 @@ public void actionPerformed(ActionEvent e) { } }); - addWindowListener(new WindowListener() { - @Override - public void windowOpened(WindowEvent e) { - - } - - @Override - public void windowClosing(WindowEvent e) { - close(); - } - - @Override - public void windowClosed(WindowEvent e) { - - } - - @Override - public void windowIconified(WindowEvent e) { - - } - - @Override - public void windowDeiconified(WindowEvent e) { - - } - - @Override - public void windowActivated(WindowEvent e) { - - } - - @Override - public void windowDeactivated(WindowEvent e) { - - } - }); - panel.add(contacts); panel.add(history); panel.add(nick); @@ -119,9 +101,19 @@ public void windowDeactivated(WindowEvent e) { setVisible(true); } - public void close(){ - dispose(); + private static void setStyle(JCheckBox checkBox){ + checkBox.setIcon(new ImageIcon("src/images/notchecked.png")); + checkBox.setSelectedIcon(new ImageIcon("src/images/checked.png")); + checkBox.setRolloverIcon(new ImageIcon("src/images/notcheckedH.png")); + checkBox.setRolloverSelectedIcon(new ImageIcon("src/images/checkedH.png")); + checkBox.setFont(font.deriveFont(15f)); + checkBox.setBackground(Colors.softGreen); + checkBox.setIconTextGap(8); + checkBox.setSize(250,15); + checkBox.setBorderPainted(false); } - + public static void main(String[] args) { + OptionsFrame op = new OptionsFrame(); + } } diff --git a/src/Protocol.java b/src/Protocol.java index a6fd570..d5cd411 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -5,6 +5,14 @@ public class Protocol { public static final String REJECTED = "Rejected"; public static final String MESSAGE = "Message"; public static final String DISCONNECT = "Disconnect"; + public static final String DISCONNECT_FROM_USER = "Disconnect from "; + public static final String SERVER_IP = "178.151.143.46"; + public static final String HELLO_SERVER = "Hello server"; + public static final String HELLO_CLIENT = "Hello ChatApp2015"; + public static final String CALL = "Call"; + public static final String LOGIN = "Login"; + public static final String SIGNUP = "Signup"; + public static String encode(String string){ return string.replace("\n",":&:"); diff --git a/src/ServerCaller.java b/src/ServerCaller.java new file mode 100644 index 0000000..be1d384 --- /dev/null +++ b/src/ServerCaller.java @@ -0,0 +1,17 @@ +import java.io.IOException; +import java.net.Socket; + +public class ServerCaller { + + public static Connection call() throws IOException { + Command lastCommand; + Connection connection = new Connection(new Socket(Protocol.SERVER_IP,Protocol.PORT_NUMBER)); + //Проверка есть ли подключение и туда ли мы присоединились. + lastCommand = connection.recieve(); + if (lastCommand.type==CommandType.HELLO_CLIENT){ + connection.sendServerHello(); + return connection; + } + return null; + } +} diff --git a/src/SignUpWindow.java b/src/SignUpWindow.java new file mode 100644 index 0000000..cf73cc4 --- /dev/null +++ b/src/SignUpWindow.java @@ -0,0 +1,246 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.io.IOException; + +public class SignUpWindow extends JFrame { + JTextArea login; + JPasswordField password,passwordC; + + JLabel signupBtn; + + public SignUpWindow(){ + + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + setSize(new Dimension(300,225)); + setLocation(getX()-150,getY()-100); + setAlwaysOnTop(true); + setResizable(false); + createGUI(); + } + + private void createGUI(){ + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setBackground(Colors.softGreen); + panel.setBorder(BorderFactory.createMatteBorder(2,2,2,2, Colors.midGreen)); + add(panel); + + Font font = null; + try { + font = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + } catch (FontFormatException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + login = new JTextArea(); + login.setFont(font); + login.setForeground(Color.lightGray); + login.setBorder(BorderFactory.createMatteBorder(1,1,1,1, Colors.midGreen)); + login.setBackground(Color.white); + login.setText("Login"); + login.setBounds(55, 30, 182, 30); + login.getDocument().putProperty("filterNewlines", Boolean.TRUE); + + login.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + if (login.getText().equals("Login")) { + login.setForeground(Color.black); + login.setText(""); + password.setForeground(Color.black); + password.setText(""); + passwordC.setForeground(Color.black); + passwordC.setText(""); + signupBtn.setEnabled(true); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + login.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + if (login.getText().equals("Login")) { + login.setForeground(Color.black); + login.setText(""); + password.setForeground(Color.black); + password.setText(""); + signupBtn.setEnabled(true); + passwordC.setForeground(Color.black); + passwordC.setText(""); + } + } + + @Override + public void keyPressed(KeyEvent e) { + + } + + @Override + public void keyReleased(KeyEvent e) { + + } + }); + + panel.add(login); + + password = new JPasswordField(); + password.setFont(font); + password.setBounds(55,65,182,30); + password.setForeground(Color.lightGray); + password.setBackground(Color.white); + password.setText("password"); + password.setBorder(BorderFactory.createMatteBorder(1,1,1,1, Colors.midGreen)); + panel.add(password); + + password.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + String tmp =""; + for ( char c : password.getPassword() ){ + tmp=tmp+c; + } + System.out.println(tmp); + if (tmp.equals("password")){ + passwordC.setForeground(Color.black); + passwordC.setText(""); + password.setForeground(Color.black); + password.setText(""); + login.setForeground(Color.black); + login.setText(""); + signupBtn.setEnabled(true); + } + + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + + passwordC = new JPasswordField(); + passwordC.setFont(font); + passwordC.setBounds(55,100,182,30); + passwordC.setForeground(Color.lightGray); + passwordC.setBackground(Color.white); + passwordC.setText("password"); + passwordC.setBorder(BorderFactory.createMatteBorder(1,1,1,1, Colors.midGreen)); + panel.add(passwordC); + + passwordC.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + String tmp =""; + for ( char c : passwordC.getPassword() ){ + tmp=tmp+c; + } + System.out.println(tmp); + if (tmp.equals("password")){ + passwordC.setForeground(Color.black); + passwordC.setText(""); + password.setForeground(Color.black); + password.setText(""); + login.setForeground(Color.black); + login.setText(""); + signupBtn.setEnabled(true); + } + + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + + signupBtn = new JLabel(""); + signupBtn.setIcon(new ImageIcon("src/images/signuptN.png")); + signupBtn.setDisabledIcon(new ImageIcon("src/images/signuptD.png")); + signupBtn.setBounds(97,145,97,27); + signupBtn.setEnabled(false); + panel.add(signupBtn); + + } + + public JLabel getSignupBtn() { + return signupBtn; + } + + public JTextArea getLogin() { + return login; + } + + public JPasswordField getPassword() { + return password; + } + + public JPasswordField getPasswordC() { + return passwordC; + } + + public static void main(String[] args) { + SignUpWindow asd = new SignUpWindow(); + } + + + +} \ No newline at end of file diff --git a/src/UltimateGUI.java b/src/UltimateGUI.java index 4209c39..bd4cc60 100644 --- a/src/UltimateGUI.java +++ b/src/UltimateGUI.java @@ -1,45 +1,84 @@ import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.io.IOException; public class UltimateGUI extends JFrame{ + JLabel okButton; + Font font; + public UltimateGUI(String string){ - super(); + super("Error"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setSize(300,120); + setSize(300, 100); setResizable(false); setAlwaysOnTop(true); setLocationRelativeTo(null); - setTitle("Error"); //? createGui(string); + setUndecorated(true); setVisible(true); + } public void createGui(String string){ JPanel panel = new JPanel(); + panel.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Colors.midGreen)); + panel.setBackground(Colors.softGreen); panel.setLayout(null); panel.setSize(300,150); + try { + font = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + } catch (FontFormatException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + JLabel label = new JLabel(string); label.setLocation(0,0); label.setSize(300,50); label.setHorizontalAlignment(0); + label.setFont(font); + + + + okButton = new JLabel(""); + okButton.setIcon(new ImageIcon("src/images/okN.png")); + okButton.setLocation(100,50); + okButton.setSize(97,27); + okButton.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + okButton.setIcon(new ImageIcon("src/images/okP.png")); + } - JButton okButton = new JButton("OK"); - okButton.setLocation(110,50); - okButton.setSize(75,25); - okButton.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { - exit(); + public void mouseReleased(MouseEvent e) { + dispose(); + } + + @Override + public void mouseEntered(MouseEvent e) { + okButton.setIcon(new ImageIcon("src/images/okH.png")); + } + + @Override + public void mouseExited(MouseEvent e) { + okButton.setIcon(new ImageIcon("src/images/okN.png")); } }); - okButton.addKeyListener(new KeyListener() { + + addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { @@ -47,13 +86,17 @@ public void keyTyped(KeyEvent e) { @Override public void keyPressed(KeyEvent e) { - if (e.getKeyCode()==KeyEvent.VK_ENTER) - exit(); + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + okButton.setIcon(new ImageIcon("src/images/okP.png")); + + } } @Override public void keyReleased(KeyEvent e) { - + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + dispose(); + } } }); @@ -65,6 +108,10 @@ public void keyReleased(KeyEvent e) { } + public static void main(String[] args) { + UltimateGUI asd = new UltimateGUI("asd"); + } + private void exit(){ dispose(); } From 9c15b37805d7a308fb7b69767aa5ea5a7c42d82e Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Sun, 20 Dec 2015 23:33:31 +0200 Subject: [PATCH 34/38] Continuing to connect with the server --- src/Command.java | 21 +++++ src/CommandListenerThread.java | 36 --------- src/CommandType.java | 2 +- src/Connection.java | 23 ++++++ src/Contact.java | 18 ++++- src/ContactPanel.java | 36 ++++++++- src/ContactsCommand.java | 19 +++++ src/ContactsView.java | 13 +++ src/ContactsViewModel.java | 86 ++------------------ src/HistoryViewModel.java | 27 +++---- src/Main.java | 141 ++++++++++++++++++++++++++++++++- src/PopUp.java | 7 +- src/Protocol.java | 3 + 13 files changed, 292 insertions(+), 140 deletions(-) delete mode 100644 src/CommandListenerThread.java create mode 100644 src/ContactsCommand.java diff --git a/src/Command.java b/src/Command.java index 8ef055c..62b7f7c 100644 --- a/src/Command.java +++ b/src/Command.java @@ -1,3 +1,4 @@ +import java.util.ArrayList; import java.util.Scanner; public class Command { @@ -24,6 +25,26 @@ public static Command getCommand(Scanner in){ if (string.contains(Protocol.MESSAGE)) return new MessageCommand(Protocol.decode(in.nextLine())); if (string.contains(Protocol.HELLO_SERVER)) return new Command(CommandType.HELLO_SERVER); if (string.contains(Protocol.HELLO_CLIENT)) return new Command(CommandType.HELLO_CLIENT); + if (string.contains(Protocol.CONTACTS)){ + string = string.replace(Protocol.CONTACTS,""); + ArrayList result = new ArrayList(); + String[] tmp = string.split(" "); + for (int i=0;i arrayList; + + public ContactsCommand(){ + super(CommandType.CONTACTS); + } + + public ContactsCommand(ArrayList tmp){ + super(CommandType.CONTACTS); + arrayList=tmp; + } + + public ArrayList getArrayList() { + return arrayList; + } +} diff --git a/src/ContactsView.java b/src/ContactsView.java index cf2ca67..abcc263 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -44,6 +44,19 @@ private void createGUI(){ add(contactsP); } + public void addContact(Contact contact){ + ContactPanel tmp = new ContactPanel(contact); + if (contact.isFav()) favouritesP.add(tmp); + else contactsP.add(tmp); + } + + public void delete(ContactPanel contact){ + if (contact.isFav()) favouritesP.remove(contact); + else contactsP.remove(contact); + list.remove(contact); + updateUI(); + } + public void setLabelFont(Font font){ diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index 15f1216..092feb6 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -7,103 +7,33 @@ import java.util.Timer; public class ContactsViewModel{ -/* ContactsView contactsView; + ContactsView contactsView; ArrayList contactList = new ArrayList(); - Logic logic; - public ContactsViewModel(Logic logic){ - this.logic=logic; - Thread onlineChecker = new Thread(new Runnable() { - @Override - public void run() { - try { - while (true) { - wait(20000); - onlineUpdate(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); + + public ContactsViewModel(ContactsView contactsView){ + this.contactsView=contactsView; } public void setContactsView(ContactsView cv){ contactsView=cv; } - public Collection getList(){ - return contactList; - } - - public void call(Contact contact){ - logic.call(contact.getIP()); - - } - public void removeContact(Contact contact, ContactPanel contactPanel){ contactList.remove(contact); - + contactsView.delete(contactPanel); } - - - public void onlineUpdate(){ - ServerConnection serverConnection = logic.getServerConnection(); - for (Contact contact : contactList){ - if (serverConnection.isNickOnline(contact.getNick())){ - contact.setOnline(true); - } - } - + public Collection getList(){ + return contactList; } - public void getData(){ - - //String[] nicks = serverConnection.getAllNicks(); - for (String nick : nicks){ - if (nick.equals(logic.getLocalNick())) continue; - Contact c = new Contact(this, nick, serverConnection.getIpForNick(nick)); - if (contactList.contains(c)) continue; - contactList.add(c); - } - - } public void add(Contact contact){ contactList.add(contact); + contactsView.addContact(contact); } - public void writeToFile() { - FileWriter out = null; - try { - out = new FileWriter("Contacts.txt"); - for (Contact c : contactList){ - out.write(new StringBuilder(c.getNick()).append(" ").append(c.getIP()).append(" ").append(c.isFav()).append("\n").toString()); - out.flush(); - } - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void readFromFile(){ - Scanner in = null; - try{ - in = new Scanner(new FileReader("Contacts.txt")); - String[] tmp; - while (in.hasNextLine()){ - tmp = in.nextLine().split(" "); - Contact c = new Contact(this,tmp[0],tmp[1]); - if (tmp[2].equals("true")) c.setFav(true); - contactList.add(c); - } - in.close(); - } catch (IOException e){ - System.out.println("Failed to find a file Contacts.txt"); - } - }*/ } diff --git a/src/HistoryViewModel.java b/src/HistoryViewModel.java index a33a271..fce94aa 100644 --- a/src/HistoryViewModel.java +++ b/src/HistoryViewModel.java @@ -12,8 +12,9 @@ public class HistoryViewModel { JTextArea historyView; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk:mm:ss dd.MM.yy]"); - public HistoryViewModel(){ + public HistoryViewModel(JTextArea historyView){ + this.historyView = historyView; } private String composeString(String message){ @@ -21,36 +22,30 @@ private String composeString(String message){ } public void addRemoteMessage(String message){ - Messagelist.add(new StringBuilder(remoteNick).append(" ").append(composeString(message)).toString()); - notifyHistoryView(); + String tmp = new StringBuilder(remoteNick).append(" ").append(composeString(message)).toString(); + Messagelist.add(tmp); + historyView.append(tmp+"\n"); + } public void addLocalMessage(String message){ - Messagelist.add(new StringBuilder(localNick).append(" ").append(composeString(message)).toString()); - notifyHistoryView(); + String tmp = new StringBuilder(localNick).append(" ").append(composeString(message)).toString(); + Messagelist.add(tmp); + historyView.append(tmp+"\n"); + } public void addSystemMessage(String message){ Messagelist.add(composeString(message)); - notifyHistoryView(); - } - - public void notifyHistoryView(){ + historyView.append(composeString(message)+"\n"); } - public int getSize(){ - return Messagelist.size(); - } public String get(int i){ return Messagelist.get(i); } - public void setHistoryView(JTextArea historyView){ - this.historyView = historyView; - } - public void writeHistoryFile(){ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("[kk.mm.ss dd.MM.yy]"); FileWriter out = null; diff --git a/src/Main.java b/src/Main.java index 6a85bfb..4235582 100644 --- a/src/Main.java +++ b/src/Main.java @@ -2,13 +2,16 @@ import java.awt.*; import java.awt.event.*; import java.io.IOException; +import java.util.ArrayList; public class Main extends JFrame { MainGUI mainFrame; LogInWindow logInWindow; SignUpWindow signUpWindow; - Connection connection; + HistoryViewModel historyViewModel; + ContactsViewModel contactsViewModel; + public static Connection connection; boolean isConnected; @@ -26,6 +29,8 @@ public Main() { createMainFrame(); createLogInWindow(); createSignUpWindow(); + historyViewModel = new HistoryViewModel(mainFrame.getHistoryView()); + contactsViewModel = new ContactsViewModel(mainFrame.getContactsView()); } } @@ -55,6 +60,8 @@ public void mousePressed(MouseEvent e) { logInWindow.setVisible(false); signUpWindow.dispose(); mainFrame.setVisible(true); + CommandListenerThread clt = new CommandListenerThread(connection); + clt.start(); } else{ UltimateGUI ultimateGUI = new UltimateGUI("Wrong login\\password"); @@ -105,6 +112,44 @@ public void mouseExited(MouseEvent e) { logInWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptN.png")); } }); + + logInWindow.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + + } + + @Override + public void windowClosing(WindowEvent e) { + connection.disconnect(); + System.exit(0); + } + + @Override + public void windowClosed(WindowEvent e) { + + } + + @Override + public void windowIconified(WindowEvent e) { + + } + + @Override + public void windowDeiconified(WindowEvent e) { + + } + + @Override + public void windowActivated(WindowEvent e) { + + } + + @Override + public void windowDeactivated(WindowEvent e) { + + } + }); } private void createSignUpWindow(){ @@ -137,6 +182,8 @@ public void mousePressed(MouseEvent e) { signUpWindow.setVisible(false); signUpWindow.dispose(); mainFrame.setVisible(true); + CommandListenerThread clt = new CommandListenerThread(connection); + clt.start(); } else { UltimateGUI ultimateGUI = new UltimateGUI("User with such login already exists"); } @@ -162,6 +209,44 @@ public void mouseExited(MouseEvent e) { signUpWindow.getSignupBtn().setIcon(new ImageIcon("src/images/signuptN.png")); } }); + + signUpWindow.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + + } + + @Override + public void windowClosing(WindowEvent e) { + connection.disconnect(); + System.exit(0); + } + + @Override + public void windowClosed(WindowEvent e) { + + } + + @Override + public void windowIconified(WindowEvent e) { + + } + + @Override + public void windowDeiconified(WindowEvent e) { + + } + + @Override + public void windowActivated(WindowEvent e) { + + } + + @Override + public void windowDeactivated(WindowEvent e) { + + } + }); } private void createMainFrame(){ @@ -236,6 +321,7 @@ public void mousePressed(MouseEvent e) { @Override public void run() { mainFrame.getLogoutBtn().setIcon(new ImageIcon("src/images/logoutP.png")); + connection.logout(); mainFrame.dispose(); createMainFrame(); createLogInWindow(); @@ -361,8 +447,7 @@ public void windowClosing(WindowEvent e) { } else{ - mainFrame.dispose(); - System.exit(0); + exit(); } } @@ -393,6 +478,56 @@ public void windowDeactivated(WindowEvent e) { }); } + private void exit(){ + connection.disconnect(); + System.exit(0); + } + + + + private class CommandListenerThread extends Thread{ + + private Command lastCommand; + private Connection connection; + private boolean stop; + + + public CommandListenerThread(Connection connection){ + this.connection=connection; + + } + + public void run() { + + connection.getContacts(); + + while (!stop) { + lastCommand = connection.recieve(); + if (lastCommand==null) continue; + switch (lastCommand.type){ + case CONTACTS:{ + ContactsCommand tmp = (ContactsCommand) lastCommand; + ArrayList tmpArr = tmp.getArrayList(); + System.out.println("Got contacts"); + for (Contact contact : tmpArr){ + contact.setContactsViewModel(contactsViewModel); + contactsViewModel.add(contact); + } + break; + } + case LOGOUT:{ + return; + } + + } + } + } + + public void kill(){ + stop = true; + } + } + public static void main(String[] args) { Main main = new Main(); } diff --git a/src/PopUp.java b/src/PopUp.java index e053f2e..8e6c8e9 100644 --- a/src/PopUp.java +++ b/src/PopUp.java @@ -3,7 +3,7 @@ import java.awt.event.ActionListener; public class PopUp extends JPopupMenu { -/* + JMenuItem delete,fav,call; Contact contact; ContactPanel contactPanel; @@ -34,9 +34,10 @@ private void createGUI(){ call.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - contact.call(); + Main.connection.sendCall(contact.getNick()); } }); + fav.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -50,5 +51,5 @@ public void actionPerformed(ActionEvent e) { } }); } - */ + } \ No newline at end of file diff --git a/src/Protocol.java b/src/Protocol.java index d5cd411..1d1faf9 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -12,6 +12,9 @@ public class Protocol { public static final String CALL = "Call"; public static final String LOGIN = "Login"; public static final String SIGNUP = "Signup"; + public static final String LOGOUT = "Logout"; + public static final String CONTACTS = "Contacts "; + public static final String GET_CONTACTS = "Get Contacts"; public static String encode(String string){ From c9151cdef077bda6f0f8c0f65421cab25cf8a483 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Mon, 21 Dec 2015 23:58:59 +0200 Subject: [PATCH 35/38] Working on contacts --- src/Command.java | 31 +++++++++++++++++++++++------ src/CommandType.java | 2 +- src/Connection.java | 40 ++++++++++++-------------------------- src/ContactsViewModel.java | 7 +++++++ src/Main.java | 7 ++++--- src/MyContactsCommand.java | 19 ++++++++++++++++++ src/Protocol.java | 10 +++++++--- 7 files changed, 75 insertions(+), 41 deletions(-) create mode 100644 src/MyContactsCommand.java diff --git a/src/Command.java b/src/Command.java index 62b7f7c..26fbf80 100644 --- a/src/Command.java +++ b/src/Command.java @@ -25,26 +25,45 @@ public static Command getCommand(Scanner in){ if (string.contains(Protocol.MESSAGE)) return new MessageCommand(Protocol.decode(in.nextLine())); if (string.contains(Protocol.HELLO_SERVER)) return new Command(CommandType.HELLO_SERVER); if (string.contains(Protocol.HELLO_CLIENT)) return new Command(CommandType.HELLO_CLIENT); + + if (string.contains(Protocol.CONTACTS)){ - string = string.replace(Protocol.CONTACTS,""); + string = string.replace(Protocol.CONTACTS+" ",""); ArrayList result = new ArrayList(); String[] tmp = string.split(" "); - for (int i=0;i result = new ArrayList(); + String[] tmp = string.split(" "); + for (int i=0;i tmpArr = tmp.getArrayList(); System.out.println("Got contacts"); for (Contact contact : tmpArr){ diff --git a/src/MyContactsCommand.java b/src/MyContactsCommand.java new file mode 100644 index 0000000..0cad591 --- /dev/null +++ b/src/MyContactsCommand.java @@ -0,0 +1,19 @@ +import java.util.ArrayList; + +public class MyContactsCommand extends Command { + + ArrayList arrayList; + + public MyContactsCommand(){ + super(CommandType.MY_CONTACTS); + } + + public MyContactsCommand(ArrayList result) { + super(CommandType.MY_CONTACTS); + arrayList = result; + } + + public ArrayList getArrayList() { + return arrayList; + } +} diff --git a/src/Protocol.java b/src/Protocol.java index 1d1faf9..1469833 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -5,7 +5,7 @@ public class Protocol { public static final String REJECTED = "Rejected"; public static final String MESSAGE = "Message"; public static final String DISCONNECT = "Disconnect"; - public static final String DISCONNECT_FROM_USER = "Disconnect from "; + public static final String DISCONNECT_FROM_USER = "Disconnect from user"; public static final String SERVER_IP = "178.151.143.46"; public static final String HELLO_SERVER = "Hello server"; public static final String HELLO_CLIENT = "Hello ChatApp2015"; @@ -13,8 +13,12 @@ public class Protocol { public static final String LOGIN = "Login"; public static final String SIGNUP = "Signup"; public static final String LOGOUT = "Logout"; - public static final String CONTACTS = "Contacts "; - public static final String GET_CONTACTS = "Get Contacts"; + public static final String CONTACTS = "Contacts"; + public static final String GET_CONTACTS = "Get contacts "; + public static final String GET_MY_CONTACTS = "Get my contacts"; + public static final String MY_CONTACTS = "Mycntcts "; + public static final String EMPTYCONTACTS = "EmptyCntcts"; + public static final String EMPTYMYCONTACTS = "EmptyMyCntcts"; public static String encode(String string){ From 5015d1c6236bf8ba24cbcfed738d7f30de4c2e27 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Tue, 22 Dec 2015 23:19:19 +0200 Subject: [PATCH 36/38] Working on contacts --- src/CallCommand.java | 17 ++ src/Caller.java | 2 +- src/Command.java | 47 ++--- src/CommandType.java | 2 +- src/Connection.java | 2 + src/Contact.java | 9 +- src/ContactPanel.java | 2 - src/ContactsView.java | 1 + src/ContactsViewModel.java | 7 +- src/HistoryViewModel.java | 2 +- src/IncomingCallForm.java | 89 --------- src/Main.java | 364 ++++++++++++++++++++++++++++++++++++- src/MainGUI.java | 67 +------ src/MyScrollBarUI.java | 63 +++++++ src/NewContactFrame.java | 12 +- src/PopUp.java | 2 +- src/Protocol.java | 8 +- 17 files changed, 499 insertions(+), 197 deletions(-) create mode 100644 src/CallCommand.java delete mode 100644 src/IncomingCallForm.java create mode 100644 src/MyScrollBarUI.java diff --git a/src/CallCommand.java b/src/CallCommand.java new file mode 100644 index 0000000..0f8de45 --- /dev/null +++ b/src/CallCommand.java @@ -0,0 +1,17 @@ +public class CallCommand extends Command { + + String nick; + + public CallCommand(){ + super(CommandType.CALL); + } + + public CallCommand(String nick) { + super(CommandType.CALL); + this.nick = nick; + } + + public String getNick() { + return nick; + } +} diff --git a/src/Caller.java b/src/Caller.java index 364e621..7570c8c 100644 --- a/src/Caller.java +++ b/src/Caller.java @@ -46,7 +46,7 @@ public Connection call() throws IOException { lastCommand = connection.recieve(); - //Если Accept - вернуть connection + //Если accept - вернуть connection if (lastCommand.type==CommandType.ACCEPT) return connection; else{ UltimateGUI ultimateGUI = new UltimateGUI(remoteNick+" rejected your call"); diff --git a/src/Command.java b/src/Command.java index 26fbf80..beab068 100644 --- a/src/Command.java +++ b/src/Command.java @@ -18,51 +18,44 @@ public static Command getCommand(Scanner in){ else{ return null; } - if (string.contains(Protocol.ACCEPTED)) return new Command(CommandType.ACCEPT); - if (string.contains(Protocol.DISCONNECT)) return new Command(CommandType.DISCONNECT); - if (string.contains(Protocol.REJECTED)) return new Command(CommandType.REJECT); - if (string.contains(Protocol.GREETING)) return new NickCommand(string); - if (string.contains(Protocol.MESSAGE)) return new MessageCommand(Protocol.decode(in.nextLine())); - if (string.contains(Protocol.HELLO_SERVER)) return new Command(CommandType.HELLO_SERVER); - if (string.contains(Protocol.HELLO_CLIENT)) return new Command(CommandType.HELLO_CLIENT); + if (string.startsWith(Protocol.ACCEPTED)) return new Command(CommandType.ACCEPT); + if (string.startsWith(Protocol.DISCONNECT)) return new Command(CommandType.DISCONNECT); + if (string.startsWith(Protocol.REJECTED)) return new Command(CommandType.REJECT); + if (string.startsWith(Protocol.GREETING)) return new NickCommand(string); + if (string.startsWith(Protocol.MESSAGE)) return new MessageCommand(Protocol.decode(in.nextLine())); + if (string.startsWith(Protocol.HELLO_SERVER)) return new Command(CommandType.HELLO_SERVER); + if (string.startsWith(Protocol.HELLO_CLIENT)) return new Command(CommandType.HELLO_CLIENT); - - if (string.contains(Protocol.CONTACTS)){ + if (string.startsWith(Protocol.CONTACTS)){ string = string.replace(Protocol.CONTACTS+" ",""); ArrayList result = new ArrayList(); String[] tmp = string.split(" "); for (int i=0;i result = new ArrayList(); String[] tmp = string.split(" "); for (int i=0;i tmp = ((ContactsCommand) lastCommand).getArrayList(); + for (Contact contact : tmp){ + contact.setContactsViewModel(contactsViewModel); + contact.setMain(getThis()); + } + ContactsFindView contactsFindView = new ContactsFindView(tmp); + break; + } + case DISCONNECT:{ + System.out.println("Got disconnect"); + System.exit(0); + break; + } + case DISCONNECT_FROM_USER:{ + mainFrame.setDisconnected(); + historyViewModel.addSystemMessage("Disconnected"); + break; + } + case CALL:{ + CallCommand callCommand = (CallCommand) lastCommand; + IncomingCallForm incomingCallForm = new IncomingCallForm(callCommand.getNick()); + break; + } + case MESSAGE:{ + historyViewModel.addRemoteMessage(((MessageCommand) lastCommand).message); + break; + } + case ACCEPT:{ + mainFrame.setConnected(); + break; + } + case REJECT:{ + UltimateGUI ultimateGUI = new UltimateGUI("User rejected your call"); + break; + } + } } @@ -529,6 +591,304 @@ public void kill(){ } } + private class ContactsFindView extends JPanel{ + private JPanel contactsP; + + JLabel contactsL = new JLabel("Contacts"); + + private ArrayList list = new ArrayList(); + + public ContactsFindView(ArrayList contacts){ + JFrame frame = new JFrame(); + createGUI(); + for (Contact contact : contacts){ + addContact(contact); + } + + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setSize(290, 500); + frame.setResizable(false); + frame.setLocationRelativeTo(mainFrame); + frame.add(this); + frame.setVisible(true); + } + + private void createGUI(){ + setBackground(Colors.softGreen); + setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); + + setAlignmentX(Component.LEFT_ALIGNMENT); + add(Box.createVerticalStrut(10)); + add(contactsL); + contactsL.setFont(mainFrame.getBigFont()); + add(Box.createVerticalStrut(10)); + + contactsP = new JPanel(); + JScrollPane scrollPane = new JScrollPane(contactsP); + scrollPane.getVerticalScrollBar().setUI(new MyScrollBarUI()); + scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(15, Integer.MAX_VALUE)); + scrollPane.setBorder(null); + contactsP.setAutoscrolls(true); + contactsP.setLayout(new BoxLayout(contactsP,BoxLayout.Y_AXIS)); + contactsP.setBackground(Colors.softGreen); + contactsP.setAlignmentX(Component.LEFT_ALIGNMENT); + add(scrollPane); + } + + public void addContact(Contact contact){ + ContactFindPanel tmp = new ContactFindPanel(contact); + contactsP.add(tmp); + } + + public void delete(ContactFindPanel contact){ + contactsP.remove(contact); + list.remove(contact); + updateUI(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + + private class ContactFindPanel extends JPanel { + private Contact contact; + private JLabel label = new JLabel(); + + public ContactFindPanel(){ + createGUI(); + } + + public ContactFindPanel(Contact contact){ + this.contact=contact; + createGUI(); + } + + private void createGUI(){ + + + setLayout(null); + setBackground(Colors.softGreen); + setMinimumSize(new Dimension(200, 25)); + setPreferredSize(new Dimension(200,25)); + setMaximumSize(new Dimension(200, 25)); + label.setBounds(5, 3, 200, 20); + label.setFont(mainFrame.getSmallFont()); + label.setText(contact.getNick()); + if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); + else label.setIcon(new ImageIcon("src/images/off.png")); + add(label); + + addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + setBackground(Colors.midGreen); + delete(getThis()); + contactsViewModel.add(contact); + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setBackground(Colors.softGreen); + } + }); + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setBackground(Colors.mainGreen); + } + }); + + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setBackground(Colors.softGreen); + } + }); + } + }); + } + + public boolean isFav(){ + return contact.isFav(); + } + + public void update(){ + if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); + else label.setIcon(new ImageIcon("src/images/off.png")); + } + + public String getNick(){ + return contact.getNick(); + } + + public Contact getContact(){ + return contact; + } + + private ContactFindPanel getThis(){ + return this; + } + + } + + + } + + private class IncomingCallForm extends JFrame { + + + private static final int Height = 150; + private static final int Widht = 300; + + JPanel panel = new JPanel(); + + JLabel accept; + JLabel dismiss; + JLabel nickName; + + IncomingCallForm(final String username){ + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setSize(Widht, Height); + setResizable(false); + setUndecorated(true); + setAlwaysOnTop(true); + setLocationRelativeTo(null); + + JPanel panel = new JPanel(); + panel.setLayout(null); + panel.setBackground(Color.pink); + + nickName = new JLabel(username+ " is calling"); + nickName.setLocation(60, 10); + nickName.setFont(mainFrame.getSmallFont()); + nickName.setSize(200, 54); + + accept = new JLabel(""); + accept.setLocation(18, 80); + accept.setSize(97, 27); + accept.setIcon(new ImageIcon("src/images/acceptN.png")); + accept.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + connection.accept(); + System.out.println("Sending Accept!"); + mainFrame.setEnabled(true); + mainFrame.setConnected(); + mainFrame.setRemoteNick(username); + dispose(); + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + accept.setIcon(new ImageIcon("src/images/acceptH.png")); + } + }); + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + accept.setIcon(new ImageIcon("src/images/acceptN.png")); + } + }); + } + }); + + + dismiss = new JLabel(""); + dismiss.setLocation(150, 80); + dismiss.setSize(97, 27); + dismiss.setIcon(new ImageIcon("src/images/rejectN.png")); + dismiss.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + connection.reject(); + System.out.println("Sending Reject!"); + mainFrame.setEnabled(true); + dispose(); + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + + } + + @Override + public void mouseEntered(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + dismiss.setIcon(new ImageIcon("src/images/rejectH.png")); + } + }); + + } + + @Override + public void mouseExited(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + dismiss.setIcon(new ImageIcon("src/images/rejectN.png")); + } + }); + + } + }); + + + panel.add(accept); + panel.add(dismiss); + panel.add(nickName); + + + + this.add(panel); + setVisible(true); + } + + } + + + public static void main(String[] args) { Main main = new Main(); } diff --git a/src/MainGUI.java b/src/MainGUI.java index 758de78..91a6a6e 100644 --- a/src/MainGUI.java +++ b/src/MainGUI.java @@ -118,14 +118,14 @@ private void createGUI(){ add(optionsBtn);*/ historyPane.getVerticalScrollBar().setPreferredSize(new Dimension(15, Integer.MAX_VALUE)); - historyPane.getVerticalScrollBar().setUI(new MyScrollbarUI()); + historyPane.getVerticalScrollBar().setUI(new MyScrollBarUI()); historyPane.setBackground(Color.white); historyPane.setBorder(null); add(historyPane); contactsPane.getVerticalScrollBar().setPreferredSize(new Dimension(15,Integer.MAX_VALUE)); - contactsPane.getVerticalScrollBar().setUI(new MyScrollbarUI()); + contactsPane.getVerticalScrollBar().setUI(new MyScrollBarUI()); contactsPane.setBounds(25, 18, 240, leftBottomPanel.getHeight() - 50); contactsPane.setBorder(null); contactsPane.setBackground(Colors.softGreen); @@ -135,7 +135,7 @@ private void createGUI(){ historyView.setFont(smallFont); historyView.setEditable(false); - historyView.setText("\n\n\n\n\nasd\nafsdfsf\nafwasd\nasdfgd\naefasdf\nasdfsdrsetsd\naefsdfgste\nasfdsetsd\nsdfdgsersfg\nsdfgsgse\nserdfsd\naserfsdf\nsdfnn\n\n\n\\n\n\n\n\n\\n\n\n\n\n\n\n\n\n\n\n\n\nsdfsdf"); + historyView.setText(""); resize(); @@ -231,62 +231,15 @@ public void setLocalNick(String nick){ localNick.setText(nick); } - private static class MyScrollbarUI extends MetalScrollBarUI { - - private Image imageThumb, imageTrack; - private JButton b = new JButton() { - - @Override - public Dimension getPreferredSize() { - return new Dimension(0, 0); - } - - }; - - MyScrollbarUI() { - imageThumb = PlainImage.create(15, 15, Colors.mainGreen); - imageTrack = PlainImage.create(15, 15, Colors.darkGreen); - } - - @Override - protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - g.translate(thumbBounds.x, thumbBounds.y); - g.setColor( Color.red ); - g.drawRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 ); - AffineTransform transform = AffineTransform.getScaleInstance((double)thumbBounds.width/imageThumb.getWidth(null),(double)thumbBounds.height/imageThumb.getHeight(null)); - ((Graphics2D)g).drawImage(imageThumb, transform, null); - g.translate(-thumbBounds.x, -thumbBounds.y ); - } - - @Override - protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { - g.translate(trackBounds.x, trackBounds.y); - ((Graphics2D)g).drawImage(imageTrack,AffineTransform.getScaleInstance(1,(double)trackBounds.height/imageTrack.getHeight(null)),null); - g.translate(-trackBounds.x, -trackBounds.y ); - } - - @Override - protected JButton createDecreaseButton(int orientation) { - return b; - } - - @Override - protected JButton createIncreaseButton(int orientation) { - return b; - } + public Font getSmallFont() { + return smallFont; } - private static class PlainImage { - - static public Image create(int w, int h, Color c) { - BufferedImage bi = new BufferedImage( - w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2d = bi.createGraphics(); - g2d.setPaint(c); - g2d.fillRect(0, 0, w, h); - g2d.dispose(); - return bi; - } + public Font getBigFont(){ + return bigFont; } + public void setRemoteNick(String remoteNick) { + this.remoteNick.setText(remoteNick); + } } diff --git a/src/MyScrollBarUI.java b/src/MyScrollBarUI.java new file mode 100644 index 0000000..fce2c83 --- /dev/null +++ b/src/MyScrollBarUI.java @@ -0,0 +1,63 @@ +import javax.swing.*; +import javax.swing.plaf.metal.MetalScrollBarUI; +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; + +public class MyScrollBarUI extends MetalScrollBarUI { + + private Image imageThumb, imageTrack; + private JButton b = new JButton() { + + @Override + public Dimension getPreferredSize() { + return new Dimension(0, 0); + } + }; + + public MyScrollBarUI() { + imageThumb = PlainImage.create(15, 15, Colors.mainGreen); + imageTrack = PlainImage.create(15, 15, Colors.darkGreen); + } + + @Override + protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { + g.translate(thumbBounds.x, thumbBounds.y); + g.setColor( Color.red ); + g.drawRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 ); + AffineTransform transform = AffineTransform.getScaleInstance((double)thumbBounds.width/imageThumb.getWidth(null),(double)thumbBounds.height/imageThumb.getHeight(null)); + ((Graphics2D)g).drawImage(imageThumb, transform, null); + g.translate(-thumbBounds.x, -thumbBounds.y ); + } + + @Override + protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { + g.translate(trackBounds.x, trackBounds.y); + ((Graphics2D)g).drawImage(imageTrack,AffineTransform.getScaleInstance(1,(double)trackBounds.height/imageTrack.getHeight(null)),null); + g.translate(-trackBounds.x, -trackBounds.y ); + } + + @Override + protected JButton createDecreaseButton(int orientation) { + return b; + } + + @Override + protected JButton createIncreaseButton(int orientation) { + return b; + } + + + private static class PlainImage { + + static public Image create(int w, int h, Color c) { + BufferedImage bi = new BufferedImage( + w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = bi.createGraphics(); + g2d.setPaint(c); + g2d.fillRect(0, 0, w, h); + g2d.dispose(); + return bi; + } + } +} \ No newline at end of file diff --git a/src/NewContactFrame.java b/src/NewContactFrame.java index 0cfa299..ad212df 100644 --- a/src/NewContactFrame.java +++ b/src/NewContactFrame.java @@ -6,7 +6,7 @@ public class NewContactFrame extends JFrame{ /* JButton Cancel; - JButton Accept; + JButton accept; JTextArea nickArea; JTextArea ipArea; @@ -40,10 +40,10 @@ private void createGUI(final ContactsViewModel cvm){ ipArea.setSize(250,20); - Accept = new JButton("Accept"); - Accept.setLocation(18, 80); - Accept.setSize(130,30); - Accept.addActionListener(new ActionListener() { + accept = new JButton("accept"); + accept.setLocation(18, 80); + accept.setSize(130,30); + accept.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Contact c = new Contact(cvm,nickArea.getText(),ipArea.getText()); @@ -63,7 +63,7 @@ public void actionPerformed(ActionEvent e) { }); - panel.add(Accept); + panel.add(accept); panel.add(Cancel); panel.add(nickArea); panel.add(ipArea); diff --git a/src/PopUp.java b/src/PopUp.java index 8e6c8e9..d5c5e78 100644 --- a/src/PopUp.java +++ b/src/PopUp.java @@ -34,7 +34,7 @@ private void createGUI(){ call.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - Main.connection.sendCall(contact.getNick()); + contact.sendCall(); } }); diff --git a/src/Protocol.java b/src/Protocol.java index 1469833..613076c 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -5,7 +5,7 @@ public class Protocol { public static final String REJECTED = "Rejected"; public static final String MESSAGE = "Message"; public static final String DISCONNECT = "Disconnect"; - public static final String DISCONNECT_FROM_USER = "Disconnect from user"; + public static final String DISCONNECT_FROM_USER = "Dscnnct from user"; public static final String SERVER_IP = "178.151.143.46"; public static final String HELLO_SERVER = "Hello server"; public static final String HELLO_CLIENT = "Hello ChatApp2015"; @@ -14,11 +14,13 @@ public class Protocol { public static final String SIGNUP = "Signup"; public static final String LOGOUT = "Logout"; public static final String CONTACTS = "Contacts"; - public static final String GET_CONTACTS = "Get contacts "; + public static final String GET_CONTACTS = "Get contacts"; public static final String GET_MY_CONTACTS = "Get my contacts"; - public static final String MY_CONTACTS = "Mycntcts "; + public static final String MY_CONTACTS = "Mycntcts"; public static final String EMPTYCONTACTS = "EmptyCntcts"; public static final String EMPTYMYCONTACTS = "EmptyMyCntcts"; + public static final String BUSY = "Busy"; + public static final String OFFLINE = "Offline"; public static String encode(String string){ From d99902ac0adb0eed6b7c5ede80d11d63f14a4883 Mon Sep 17 00:00:00 2001 From: EvGe22 Date: Thu, 24 Dec 2015 01:00:00 +0200 Subject: [PATCH 37/38] Working on contacts --- src/Command.java | 10 ++ src/CommandType.java | 2 +- src/Connection.java | 23 +++- src/ContactPanel.java | 17 ++- src/ContactsView.java | 17 ++- src/ContactsViewModel.java | 16 +++ src/HistoryViewModel.java | 12 ++- src/LogInWindow.java | 12 +-- src/Main.java | 208 ++++++++++++++++++++++++++----------- src/MainGUI.java | 47 +++++++-- src/OnlineCommand.java | 19 ++++ src/PopUp.java | 1 + src/Protocol.java | 1 + src/SignUpWindow.java | 6 +- src/UltimateGUI.java | 20 ++-- src/images/acceptH.png | Bin 0 -> 3718 bytes src/images/acceptN.png | Bin 0 -> 3711 bytes src/images/acceptP.png | Bin 0 -> 3701 bytes src/images/rejectH.png | Bin 0 -> 3709 bytes src/images/rejectN.png | Bin 0 -> 3700 bytes src/images/rejectP.png | Bin 0 -> 3696 bytes 21 files changed, 297 insertions(+), 114 deletions(-) create mode 100644 src/OnlineCommand.java create mode 100644 src/images/acceptH.png create mode 100644 src/images/acceptN.png create mode 100644 src/images/acceptP.png create mode 100644 src/images/rejectH.png create mode 100644 src/images/rejectN.png create mode 100644 src/images/rejectP.png diff --git a/src/Command.java b/src/Command.java index beab068..4f34add 100644 --- a/src/Command.java +++ b/src/Command.java @@ -56,6 +56,16 @@ public static Command getCommand(Scanner in){ if (string.startsWith(Protocol.EMPTYCONTACTS)) return new Command(CommandType.EMPTYCONTACTS); if (string.startsWith(Protocol.EMPTYMYCONTACTS)) return new Command(CommandType.EMPTYMYCONTACTS); if (string.startsWith(Protocol.CALL)) return new CallCommand(string.replace(Protocol.CALL+" ","")); + if (string.startsWith(Protocol.DISCONNECT_FROM_USER)) return new Command(CommandType.DISCONNECT_FROM_USER); + if (string.startsWith(Protocol.ONLINE_CONTACTS)) { + string = string.replace(Protocol.ONLINE_CONTACTS+" ",""); + String[] tmp = string.split(" "); + ArrayList onlines = new ArrayList(); + for (String s : tmp){ + onlines.add(Boolean.parseBoolean(s)); + } + return new OnlineCommand(onlines); + } return null; } diff --git a/src/CommandType.java b/src/CommandType.java index 9476622..a6ca64e 100644 --- a/src/CommandType.java +++ b/src/CommandType.java @@ -1,4 +1,4 @@ public enum CommandType { ACCEPT, REJECT, DISCONNECT, NICK, MESSAGE, HELLO_SERVER, HELLO_CLIENT, CALL, LOGIN, SIGNUP, DISCONNECT_FROM_USER, - CONTACTS, CONTACT, ONLINE_CONTACTS, LOGOUT, EMPTYCONTACTS, EMPTYMYCONTACTS, GET_CONTACTS, BUSY, OFFLINE, MY_CONTACTS + CONTACTS, CONTACT, ONLINE_CONTACTS, LOGOUT, EMPTYCONTACTS, EMPTYMYCONTACTS, GET_CONTACTS, BUSY, OFFLINE, MY_CONTACTS, GET_MY_CONTACTS } diff --git a/src/Connection.java b/src/Connection.java index 0366eeb..1369bf0 100644 --- a/src/Connection.java +++ b/src/Connection.java @@ -57,7 +57,6 @@ public void sendSignUp(String nick,String password){ public void accept(){ try { - out.write(new StringBuilder(Protocol.ACCEPTED).append("\n").toString().getBytes("UTF-8")); lastCommand=CommandType.ACCEPT; } catch (IOException e) { @@ -105,9 +104,9 @@ public void logout(){ } } - public void disconnectFromUser(String nick){ + public void disconnectFromUser(){ try{ - out.write(new StringBuilder(Protocol.DISCONNECT_FROM_USER).append(nick).append("\n").toString().getBytes("UTF-8")); + out.write(new StringBuilder(Protocol.DISCONNECT_FROM_USER).append("\n").toString().getBytes("UTF-8")); lastCommand=CommandType.DISCONNECT_FROM_USER; }catch (Exception e){ e.printStackTrace(); @@ -123,6 +122,15 @@ public void getContacts(String regex){ } } + public void getMyContacts(){ + try { + out.write(new StringBuilder(Protocol.GET_MY_CONTACTS).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.GET_MY_CONTACTS; + } catch (IOException e) { + e.printStackTrace(); + } + } + public void sendContacts(String contacts){ try { out.write(new StringBuilder(Protocol.CONTACTS).append(contacts).append("\n").toString().getBytes("UTF-8")); @@ -132,8 +140,13 @@ public void sendContacts(String contacts){ } } - public void getOnline(){ - + public void getOnline(String s){ + try { + out.write(new StringBuilder(Protocol.ONLINE_CONTACTS).append(s).append("\n").toString().getBytes("UTF-8")); + lastCommand=CommandType.ONLINE_CONTACTS; + } catch (IOException e) { + e.printStackTrace(); + } } diff --git a/src/ContactPanel.java b/src/ContactPanel.java index df14e96..d12bd62 100644 --- a/src/ContactPanel.java +++ b/src/ContactPanel.java @@ -21,7 +21,7 @@ public ContactPanel(Contact contact){ private void createGUI(){ try { - font = Font.createFont(Font.TRUETYPE_FONT,new File("src/font/roboto-thin.ttf")).deriveFont(15f); + font = Font.createFont(Font.TRUETYPE_FONT,Main.class.getClassLoader().getResourceAsStream("roboto-thin.ttf")).deriveFont(15f); } catch (FontFormatException e) { e.printStackTrace(); } catch (IOException e) { @@ -36,8 +36,8 @@ private void createGUI(){ label.setBounds(5, 3, 200, 20); label.setFont(font); label.setText(contact.getNick()); - if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); - else label.setIcon(new ImageIcon("src/images/off.png")); + if (contact.isOnline()) label.setIcon(new ImageIcon(Main.class.getResource("/on.png"))); + else label.setIcon(new ImageIcon(Main.class.getResource("/off.png"))); add(label); addMouseListener(new MouseListener() { @@ -74,8 +74,15 @@ public boolean isFav(){ } public void update(){ - if (contact.isOnline()) label.setIcon(new ImageIcon("src/images/on.png")); - else label.setIcon(new ImageIcon("src/images/off.png")); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (contact.isOnline()) label.setIcon(new ImageIcon(Main.class.getResource("/on.png"))); + else label.setIcon(new ImageIcon(Main.class.getResource("/off.png"))); + label.updateUI(); + } + }); + } public String getNick(){ diff --git a/src/ContactsView.java b/src/ContactsView.java index 8611380..3168c0c 100644 --- a/src/ContactsView.java +++ b/src/ContactsView.java @@ -46,6 +46,7 @@ private void createGUI(){ public void addContact(Contact contact){ ContactPanel tmp = new ContactPanel(contact); + list.add(tmp); if (contact.isFav()) favouritesP.add(tmp); else contactsP.add(tmp); updateUI(); @@ -58,19 +59,15 @@ public void delete(ContactPanel contact){ updateUI(); } - - public void setLabelFont(Font font){ contactsL.setFont(font); favouritesL.setFont(font); } - - - - - - - - + public void onlineUpdate() { + for (ContactPanel cp : list){ + cp.update(); + } + updateUI(); + } } diff --git a/src/ContactsViewModel.java b/src/ContactsViewModel.java index b98ed82..968c094 100644 --- a/src/ContactsViewModel.java +++ b/src/ContactsViewModel.java @@ -25,6 +25,7 @@ public Collection getList(){ public void add(Contact contact){ + if (contactList.contains(contact)) return; contactList.add(contact); contactsView.addContact(contact); @@ -38,4 +39,19 @@ public String getContacts() { } return stringBuilder.toString(); } + + public String getOnlineCintacts(){ + StringBuilder stringBuilder = new StringBuilder(); + for (Contact contact : contactList){ + stringBuilder.append(" ").append(contact.getNick()); + } + return stringBuilder.toString(); + } + + public void updateOnline(ArrayList onlines){ + for (int i=0;i arrayList; + + public OnlineCommand() { + super(CommandType.ONLINE_CONTACTS); + } + + public OnlineCommand(ArrayList onlines) { + super(CommandType.ONLINE_CONTACTS); + arrayList = onlines; + } + + public ArrayList getArrayList() { + return arrayList; + } +} diff --git a/src/PopUp.java b/src/PopUp.java index d5c5e78..c70eb2d 100644 --- a/src/PopUp.java +++ b/src/PopUp.java @@ -26,6 +26,7 @@ private void createGUI(){ fav.setText("Add to favourites"); } call = new JMenuItem("Call"); + if (Main.isConnected) call.setEnabled(false); add(call); add(fav); diff --git a/src/Protocol.java b/src/Protocol.java index 613076c..0d7bc78 100644 --- a/src/Protocol.java +++ b/src/Protocol.java @@ -21,6 +21,7 @@ public class Protocol { public static final String EMPTYMYCONTACTS = "EmptyMyCntcts"; public static final String BUSY = "Busy"; public static final String OFFLINE = "Offline"; + public static final String ONLINE_CONTACTS = "Online Cntcts"; public static String encode(String string){ diff --git a/src/SignUpWindow.java b/src/SignUpWindow.java index cf73cc4..5487d45 100644 --- a/src/SignUpWindow.java +++ b/src/SignUpWindow.java @@ -33,7 +33,7 @@ private void createGUI(){ Font font = null; try { - font = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + font = Font.createFont(Font.TRUETYPE_FONT, Main.class.getClassLoader().getResourceAsStream("roboto-thin.ttf")).deriveFont(15f); } catch (FontFormatException e) { e.printStackTrace(); } catch (IOException e) { @@ -213,8 +213,8 @@ public void mouseExited(MouseEvent e) { }); signupBtn = new JLabel(""); - signupBtn.setIcon(new ImageIcon("src/images/signuptN.png")); - signupBtn.setDisabledIcon(new ImageIcon("src/images/signuptD.png")); + signupBtn.setIcon(new ImageIcon(Main.class.getResource("/signuptN.png"))); + signupBtn.setDisabledIcon(new ImageIcon(Main.class.getResource("/signuptD.png"))); signupBtn.setBounds(97,145,97,27); signupBtn.setEnabled(false); panel.add(signupBtn); diff --git a/src/UltimateGUI.java b/src/UltimateGUI.java index bd4cc60..eb6ac2d 100644 --- a/src/UltimateGUI.java +++ b/src/UltimateGUI.java @@ -13,7 +13,7 @@ public UltimateGUI(String string){ super("Error"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setSize(300, 100); + setSize(450, 100); setResizable(false); setAlwaysOnTop(true); setLocationRelativeTo(null); @@ -29,10 +29,10 @@ public void createGui(String string){ panel.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Colors.midGreen)); panel.setBackground(Colors.softGreen); panel.setLayout(null); - panel.setSize(300,150); + panel.setSize(450,150); try { - font = Font.createFont(Font.TRUETYPE_FONT, new File("src/font/roboto-thin.ttf")).deriveFont(15f); + font = Font.createFont(Font.TRUETYPE_FONT, Main.class.getClassLoader().getResourceAsStream("roboto-thin.ttf")).deriveFont(15f); } catch (FontFormatException e) { e.printStackTrace(); } catch (IOException e) { @@ -41,15 +41,15 @@ public void createGui(String string){ JLabel label = new JLabel(string); label.setLocation(0,0); - label.setSize(300,50); + label.setSize(450,50); label.setHorizontalAlignment(0); label.setFont(font); okButton = new JLabel(""); - okButton.setIcon(new ImageIcon("src/images/okN.png")); - okButton.setLocation(100,50); + okButton.setIcon(new ImageIcon(Main.class.getResource("/okN.png"))); + okButton.setLocation(175,50); okButton.setSize(97,27); okButton.addMouseListener(new MouseListener() { @Override @@ -59,7 +59,7 @@ public void mouseClicked(MouseEvent e) { @Override public void mousePressed(MouseEvent e) { - okButton.setIcon(new ImageIcon("src/images/okP.png")); + okButton.setIcon(new ImageIcon(Main.class.getResource("/okP.png"))); } @Override @@ -69,12 +69,12 @@ public void mouseReleased(MouseEvent e) { @Override public void mouseEntered(MouseEvent e) { - okButton.setIcon(new ImageIcon("src/images/okH.png")); + okButton.setIcon(new ImageIcon(Main.class.getResource("/okH.png"))); } @Override public void mouseExited(MouseEvent e) { - okButton.setIcon(new ImageIcon("src/images/okN.png")); + okButton.setIcon(new ImageIcon(Main.class.getResource("/okN.png"))); } }); @@ -87,7 +87,7 @@ public void keyTyped(KeyEvent e) { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { - okButton.setIcon(new ImageIcon("src/images/okP.png")); + okButton.setIcon(new ImageIcon(Main.class.getResource("/okP.png"))); } } diff --git a/src/images/acceptH.png b/src/images/acceptH.png new file mode 100644 index 0000000000000000000000000000000000000000..610342892012e6b713a21800b9bc3f55cd9162d8 GIT binary patch literal 3718 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 literal 0 HcmV?d00001 diff --git a/src/images/acceptN.png b/src/images/acceptN.png new file mode 100644 index 0000000000000000000000000000000000000000..1b5f03e28f97201dee17ba0f5106d7041dcbf452 GIT binary patch literal 3711 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 Trbz1?00000NkvXXu0mjf?JN%r literal 0 HcmV?d00001 diff --git a/src/images/rejectH.png b/src/images/rejectH.png new file mode 100644 index 0000000000000000000000000000000000000000..45e2a7371284c7d9029e59a4d4087d6b1c292082 GIT binary patch literal 3709 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 b;Lia7Q=eALxGh4S00000NkvXXu0mjf%H{_D literal 0 HcmV?d00001 diff --git a/src/images/rejectN.png b/src/images/rejectN.png new file mode 100644 index 0000000000000000000000000000000000000000..8dbb8765f6607b16b5bd5b50ca6bb2c42da87cdb GIT binary patch literal 3700 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 Date: Mon, 28 Dec 2015 01:40:43 +0200 Subject: [PATCH 38/38] fixing --- src/Command.java | 1 + src/HistoryViewModel.java | 4 ++++ src/Main.java | 20 +++++++++++++------- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Command.java b/src/Command.java index 4f34add..e40beff 100644 --- a/src/Command.java +++ b/src/Command.java @@ -66,6 +66,7 @@ public static Command getCommand(Scanner in){ } return new OnlineCommand(onlines); } + if (string.startsWith(Protocol.LOGOUT)) return new Command(CommandType.LOGOUT); return null; } diff --git a/src/HistoryViewModel.java b/src/HistoryViewModel.java index 967709a..c9d0079 100644 --- a/src/HistoryViewModel.java +++ b/src/HistoryViewModel.java @@ -65,6 +65,10 @@ public void writeHistoryFile(){ } + public void setHistoryView(JTextArea historyView) { + this.historyView = historyView; + } + public void clearView(){ Messagelist = new ArrayList(); historyView.setText(""); diff --git a/src/Main.java b/src/Main.java index 1d391e3..92fb873 100644 --- a/src/Main.java +++ b/src/Main.java @@ -32,9 +32,6 @@ public Main() { createMainFrame(); createLogInWindow(); createSignUpWindow(); - historyViewModel = new HistoryViewModel(mainFrame.getHistoryView()); - historyViewModel.setLocalNick(login); - contactsViewModel = new ContactsViewModel(mainFrame.getContactsView()); } } @@ -62,17 +59,21 @@ public void mousePressed(MouseEvent e) { tmp = "" + tmp.hashCode(); connection.sendLogin(logInWindow.getLogin().getText(),tmp); Command command = connection.recieve(); + System.out.println(command.type); if (command.type==CommandType.ACCEPT) { mainFrame.setLocalNick(logInWindow.getLogin().getText()); + historyViewModel = new HistoryViewModel(mainFrame.getHistoryView()); + contactsViewModel = new ContactsViewModel(mainFrame.getContactsView()); historyViewModel.setLocalNick(logInWindow.getLogin().getText()); logInWindow.setVisible(false); signUpWindow.dispose(); mainFrame.setVisible(true); CommandListenerThread clt = new CommandListenerThread(connection); clt.start(); + } else{ - UltimateGUI ultimateGUI = new UltimateGUI("Wrong login\\password or \nsuch user is already online"); + UltimateGUI ultimateGUI = new UltimateGUI("Wrong login\\password or such user is already online"); } } @@ -336,6 +337,7 @@ public void mousePressed(MouseEvent e) { public void run() { mainFrame.getLogoutBtn().setIcon(new ImageIcon(Main.class.getResource("/logoutP.png"))); connection.logout(); + connection.sendContacts(contactsViewModel.getContacts()); mainFrame.dispose(); createMainFrame(); createLogInWindow(); @@ -545,6 +547,7 @@ private void exit(){ public void call(String nick) { connection.sendCall(nick); remoteNick = nick; + isConnected=true; historyViewModel.setRemoteNick(nick); historyViewModel.clearView(); } @@ -576,6 +579,7 @@ public void run() { while (!stop) { + System.out.println("waiting for a new command"); lastCommand = connection.recieve(); if (lastCommand==null) continue; switch (lastCommand.type){ @@ -591,6 +595,8 @@ public void run() { break; } case LOGOUT:{ + System.out.println("got logout"); + onlineTimer.cancel(); return; } case BUSY:{ @@ -640,11 +646,11 @@ public void run() { case ACCEPT:{ mainFrame.setConnected(); mainFrame.setRemoteNick(remoteNick); - isConnected=true; break; } case REJECT:{ UltimateGUI ultimateGUI = new UltimateGUI("User rejected your call"); + isConnected=false; break; } case EMPTYCONTACTS:{ @@ -848,7 +854,7 @@ private class IncomingCallForm extends JFrame { panel.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Colors.midGreen)); nickName = new JLabel(username+ " is calling"); - nickName.setLocation(60, 10); + nickName.setLocation(80, 10); nickName.setFont(mainFrame.getSmallFont()); nickName.setSize(200, 54); @@ -915,7 +921,7 @@ public void run() { dismiss = new JLabel(""); - dismiss.setLocation(190, 80); + dismiss.setLocation(170, 80); dismiss.setSize(97, 27); dismiss.setIcon(new ImageIcon(Main.class.getResource("/rejectN.png"))); dismiss.addMouseListener(new MouseListener() {