From e7c4a39a75a9fa70b5cc95234494867851d4fbfb Mon Sep 17 00:00:00 2001 From: alartigu Date: Wed, 9 Dec 2020 12:30:38 +0100 Subject: [PATCH] Page de connexion et d'acceuil --- Implementation/src/ChatApp.java | 20 +- Implementation/src/ListUtilisateurs.java | 5 +- Implementation/src/Modification_Pseudo.java | 49 +++++ Implementation/src/View_Accueil.java | 14 +- Implementation/src/View_Clavardage.java | 53 ++++++ Implementation/src/View_Courante.java | 200 ++++++++++++++------ Implementation/src/images/Home.png | Bin 0 -> 6845 bytes 7 files changed, 270 insertions(+), 71 deletions(-) create mode 100644 Implementation/src/Modification_Pseudo.java create mode 100644 Implementation/src/View_Clavardage.java create mode 100644 Implementation/src/images/Home.png diff --git a/Implementation/src/ChatApp.java b/Implementation/src/ChatApp.java index 0d6cda1..47caf1c 100644 --- a/Implementation/src/ChatApp.java +++ b/Implementation/src/ChatApp.java @@ -64,13 +64,14 @@ public class ChatApp { * Envoie en broadcast ses informations utilisateurs et son nouveau pseudo * * @param nouveau correspond au nouveau pseudo + * @return False si modiferPseudo a echoue, True sinon */ - public void modifierPseudo(String nouveau) throws IOException { + public Boolean modifierPseudo(String nouveau) throws IOException { // Message que l'on envoie à tous les utilisateurs actifs String broadcastMessage = "Demande Modification Pseudo\n" + this.getMe().toString() + "\n" + nouveau + "\n"; UDPEchange.EnvoiBroadcast(broadcastMessage); try { - Thread.sleep(1000); + Thread.sleep(2000); /* L'utilisateur doit attendre la reponse de tous les utilisateurs connectes * pour savoir si son pseudo est accepte */ @@ -87,11 +88,12 @@ public class ChatApp { this.getMe().setPseudo(nouveau); System.out.println("Changement pseudo accepte, nouvelle liste des utilisateurs actifs:"); this.getActifUsers().afficherListeUtilisateurs(); + return true; } else { - System.out.println("Connexion echoue"); - System.exit(1) ; // A MODIFIER NORMALEMENT ON LUI DEMANDE DE CHOISIR UN NV MDP + System.out.println("Echec Modification pseudo"); + return false; } } @@ -99,24 +101,26 @@ public class ChatApp { /** * Methode appelee lors de la connexion d'un nouvel utilisateur. * Il va prevenir les utilisateurs du reseau de son arrivee. - * + * @return False si Connexion a echoue, True sinon */ - public void connexion() throws IOException { + public Boolean connexion() throws IOException { // Message que l'on envoie à tous les utilisateurs actifs String broadcastMessage = "Connexion\n" + this.getMe().toString() ; UDPEchange.EnvoiBroadcast(broadcastMessage); try { - Thread.sleep(1000); // L'utilisateur doit attendre la reponse de tous les utilisateurs connectes + Thread.sleep(2000); // L'utilisateur doit attendre la reponse de tous les utilisateurs connectes } catch (InterruptedException e) { e.printStackTrace(); } if (UDPEchange.getConnecte()) { System.out.println("Connexion reussie"); + return true; } else { System.out.println("Connexion echoue"); - System.exit(1) ; // A MODIFIER NORMALEMENT ON LUI DEMANDE DE CHOISIR UN NV MDP + UDPEchange.setConnecte(true); + return false ; } } diff --git a/Implementation/src/ListUtilisateurs.java b/Implementation/src/ListUtilisateurs.java index d3fc8db..bdd8efa 100644 --- a/Implementation/src/ListUtilisateurs.java +++ b/Implementation/src/ListUtilisateurs.java @@ -99,11 +99,14 @@ public class ListUtilisateurs { * Méthode affichant la liste des utilisateurs actifs * */ - public void afficherListeUtilisateurs() { + public String afficherListeUtilisateurs() { System.out.println ("Liste des utilisateurs actifs : "); + String Utilisateur = "" ; for(Utilisateur elem: this.actifUsers) { System.out.println (elem.toString()); + Utilisateur += (elem + "\n"); } + return Utilisateur; } } diff --git a/Implementation/src/Modification_Pseudo.java b/Implementation/src/Modification_Pseudo.java new file mode 100644 index 0000000..0ebc13e --- /dev/null +++ b/Implementation/src/Modification_Pseudo.java @@ -0,0 +1,49 @@ +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; + +public class Modification_Pseudo extends JPanel{ + ChatApp app; + public Modification_Pseudo(String name, ChatApp app) { + this.app = app ; + JLabel Text = new JLabel("Entrez un nouveau nom d'utilisateur!", SwingConstants.CENTER); + JTextField pseudofield = new JTextField(2); // Zone d'insertion de texte + JButton home = new JButton(new ImageIcon("/Users/auriane/Desktop/ChatApp-AL-NM/Implementation/src/images/Home.png")); + //Ajout d'un bouton Valider + JButton Valider = new JButton("Valider"); + this.getRootPane().setDefaultButton(Valider); + //Listen to events from the Valider button. + Valider.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent event) { + String nouveau = pseudofield.getText(); + try { + Boolean resultat = app.modifierPseudo(nouveau); + if(resultat) { + + } + else { + //JOptionPane.showMessageDialog(this, "Echec de modification de pseudo, " + nouveau +" deja pris", JOptionPane.WARNING_MESSAGE); + + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + this.add(home); + this.add(Text); + this.add(BorderLayout.CENTER,pseudofield); + this.add(BorderLayout.SOUTH,Valider); + +} +} diff --git a/Implementation/src/View_Accueil.java b/Implementation/src/View_Accueil.java index 612785a..bc5e066 100644 --- a/Implementation/src/View_Accueil.java +++ b/Implementation/src/View_Accueil.java @@ -79,14 +79,20 @@ public class View_Accueil implements ActionListener{ ChatApp app = new ChatApp(pseudo, 3000) ; ExecutorService execUDP = Executors.newFixedThreadPool(1000); execUDP.submit(new RunnerEcouteUDP(app)); + Boolean connexion = false ; try { - app.connexion(); + connexion = app.connexion(); } catch (IOException e) { e.printStackTrace(); } - //JOptionPane.showMessageDialog(frame, "Bonjour " + pseudo); - frame.dispose(); - View_Courante fenetreCourante= new View_Courante(app); + if(connexion) { + JOptionPane.showMessageDialog(frame, "Bonjour " + pseudo) ; + frame.dispose(); + View_Courante fenetreCourante= new View_Courante(app); + } + else { + JOptionPane.showMessageDialog(frame, "Echec de Connexion , ce pseudo est deja pris !"); + } } private static void createAndShowGUI() { //Make sure we have nice window decorations. diff --git a/Implementation/src/View_Clavardage.java b/Implementation/src/View_Clavardage.java new file mode 100644 index 0000000..e81aaad --- /dev/null +++ b/Implementation/src/View_Clavardage.java @@ -0,0 +1,53 @@ +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class View_Clavardage { + JFrame frame ; + ChatApp app; + Utilisateur destination ; + Utilisateur source; + WindowAdapter wa ; + JPanel panel ; + public View_Clavardage(ChatApp app, String User2) { + this.app = app ; + this.frame = new JFrame("ChatApp-AL-NM"); + this.destination = this.app.getMe(); + this.source = Utilisateur.stringToUtilisateur(User2); + this.frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // FERME TOUTE LES FENETRES ATTENTION + // fixer les dimensions de la fenetre + this.frame.setSize(new Dimension(394, 344)); + wa = new WindowAdapter(){ + public void windowClosing(WindowEvent e){ + int reponse = View_Courante.showConfirmDialog(); + if (reponse==0){ + try { + app.deconnexion(); + } catch (IOException e1) { + e1.printStackTrace(); + } + frame.dispose(); + } + }}; + panel = new JPanel(); + frame.addWindowListener( wa ) ; + //Add the widgets. + this.addWidgets(); + + //Add the panel to the window. + frame.getContentPane().add(this.panel, BorderLayout.CENTER); + + //Display the window. + //frame.pack(); + frame.setVisible(true); + } + private void addWidgets() {} + + +} diff --git a/Implementation/src/View_Courante.java b/Implementation/src/View_Courante.java index c9fbb7a..f082544 100644 --- a/Implementation/src/View_Courante.java +++ b/Implementation/src/View_Courante.java @@ -9,6 +9,7 @@ import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -17,6 +18,7 @@ import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; @@ -29,6 +31,8 @@ import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingConstants; + + import java.awt.Font; import java.awt.Color; import java.awt.SystemColor; @@ -63,10 +67,11 @@ public class View_Courante { } }}; frame.addWindowListener( wa ) ; - + menu = new JMenuBar(); + //Create and set up the panel. - panel = new JPanel(new GridLayout(3,1)); - panel.setForeground(SystemColor.menuText); + panel = new JPanel(new GridLayout(3,1)); + panel.setForeground(SystemColor.menuText); //Add the widgets. this.addWidgets(); @@ -91,67 +96,146 @@ public class View_Courante { */ private void addWidgets() { jlabel = new JLabel(new ImageIcon("/Users/auriane/Desktop/ChatApp-AL-NM/Implementation/src/images/Logo.png"), JLabel.CENTER); - menu = new JMenuBar(); - JMenu Actions = new JMenu("Actions"); - Actions.setForeground(Color.WHITE); - Actions.setHorizontalAlignment(SwingConstants.CENTER); - // Définir le sous-menu pour Actions - JMenuItem actifs = new JMenuItem("Utilisateurs actifs"); - JMenuItem session = new JMenuItem("Session de Clavardage"); - JMenuItem pseudo = new JMenuItem("Modifier Pseudo"); - JMenuItem deconnexion = new JMenuItem("Deconnexion"); - - actifs.addActionListener( new ActionListener(){ - public void actionPerformed(ActionEvent event) { - - } }); - session.addActionListener( new ActionListener(){ - public void actionPerformed(ActionEvent event) { - - } }); - pseudo.addActionListener( new ActionListener(){ - public void actionPerformed(ActionEvent event) { - panel.remove(jlabel); - panel.remove(Txt); - JLabel Text = new JLabel("Entrez un nouveau nom d'utilisateur!", SwingConstants.CENTER); - JTextField pseudofield = new JTextField(2); // Zone d'insertion de texte - panel.add(pseudofield); - panel.add(Text); - frame.getContentPane().add(panel, BorderLayout.CENTER); - frame.setVisible(true); - String pseudo = pseudofield.getText(); - try { - app.modifierPseudo(pseudo); - } catch (IOException e) { - e.printStackTrace(); - } - // IL FAUT RAJOUTER UN BOUTON ENTRRE - // IL FAUT MODIFIER "MODIFIER PSEUDO" pour que ca renvoit un boolean - //Selon le boolean on affiche un pop up ou on renvient au menu principal en faisant new view courante - } - }); - deconnexion.addActionListener( new ActionListener(){ - public void actionPerformed(ActionEvent event) { - frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); - } }); - - Actions.add(actifs); - Actions.add(session); - Actions.add(pseudo); - Actions.add(deconnexion); - - - menu.add(Actions); - + Txt = new JLabel("Menu principal de " + app.getMe().getPseudo()); Txt.setFont(new Font("Tamil MN", Font.PLAIN, 30)); Txt.setHorizontalAlignment(SwingConstants.CENTER); - panel.add(BorderLayout.NORTH , menu); - panel.add(BorderLayout.CENTER, jlabel); - panel.add(BorderLayout.SOUTH , Txt ); + JMenu Actions = new JMenu("Actions"); + Actions.setForeground(Color.WHITE); + Actions.setHorizontalAlignment(SwingConstants.CENTER); + // Définir le sous-menu pour Actions + JMenuItem actifs = new JMenuItem("Utilisateurs actifs"); + JMenuItem session = new JMenuItem("Session de Clavardage"); + JMenuItem pseudo = new JMenuItem("Modifier Pseudo"); + JMenuItem deconnexion = new JMenuItem("Deconnexion"); + Actions.add(actifs); + Actions.add(session); + Actions.add(pseudo); + Actions.add(deconnexion); + + menu.add(Actions); + + panel.add(BorderLayout.NORTH , menu); + panel.add(BorderLayout.CENTER, jlabel); + panel.add(BorderLayout.SOUTH , Txt ); + + //****************************************************************************************************************** + //**************************************** Actions lorsque l'on clique sur actifs ********************************** + //****************************************************************************************************************** + actifs.addActionListener( new ActionListener(){ + public void actionPerformed(ActionEvent event) { + JPanel panel1 = new JPanel(); + JButton home = new JButton(new ImageIcon("/Users/auriane/Desktop/ChatApp-AL-NM/Implementation/src/images/Home.png")); + panel1.add(BorderLayout.NORTH , home); + + JLabel Text = new JLabel(" Liste Utilisateurs Actifs
"); + panel1.add(Text); + + String utilisateurs = app.getActifUsers().afficherListeUtilisateurs(); + for(String elem : utilisateurs.split("\n")) { + JLabel Text1 = new JLabel("" + elem + "
"); + panel1.add(Text1); + } + + home.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent event) { + frame.dispose(); + new View_Courante(app); + } }); + + frame.getContentPane().removeAll(); + frame.getContentPane().add(panel1); + frame.setVisible(true); + }}); + + //****************************************************************************************************************** + //**************************************** Actions lorsque l'on clique sur Session ********************************* + //****************************************************************************************************************** + session.addActionListener( new ActionListener(){ + public void actionPerformed(ActionEvent event) { + JButton home = new JButton(new ImageIcon("/Users/auriane/Desktop/ChatApp-AL-NM/Implementation/src/images/Home.png")); + home.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent event) { + frame.dispose(); + new View_Courante(app); + } }); + String utilisateurs = app.getActifUsers().afficherListeUtilisateurs(); + Vector vector = new Vector(); + for(String elem : utilisateurs.split("\n")) { + vector.add(elem); + } + // Créer une liste déroulante + JComboBox cb = new JComboBox(vector); + cb.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + Object selected = cb.getSelectedItem(); + new View_Clavardage(app, selected.toString()); + + } + }); + JPanel panel1 = new JPanel(new GridLayout(2,1)); + panel1.add(home); + panel1.add(cb); + frame.getContentPane().removeAll(); + frame.getContentPane().add(panel1,BorderLayout.CENTER); + frame.setVisible(true); + } }); + + //****************************************************************************************************************** + //**************************************** Actions lorsque l'on clique sur Pseudo ********************************** + //****************************************************************************************************************** + pseudo.addActionListener( new ActionListener(){ + public void actionPerformed(ActionEvent event) { + JLabel Text = new JLabel("Entrez un nouveau nom d'utilisateur!", SwingConstants.CENTER); + JTextField pseudofield = new JTextField(2); // Zone d'insertion de texte + JButton home = new JButton(new ImageIcon("/Users/auriane/Desktop/ChatApp-AL-NM/Implementation/src/images/Home.png")); + home.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent event) { + frame.dispose(); + new View_Courante(app); + } }); + //Ajout d'un bouton Valider + JButton Valider = new JButton("Valider"); + frame.getRootPane().setDefaultButton(Valider); + //Listen to events from the Valider button. + Valider.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent event) { + String nouveau = pseudofield.getText(); + try { + Boolean resultat = app.modifierPseudo(nouveau); + if(resultat) { + JOptionPane.showMessageDialog(frame, "Modification pseudo Reussi"); + frame.dispose(); + new View_Courante(app); + } + else { + JOptionPane.showMessageDialog(frame, "Echec Modification pseudo "); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + JPanel panel1 = new JPanel(new GridLayout(4,1)); + panel1.add(home); + panel1.add(Text); + panel1.add(pseudofield); + panel1.add(Valider); + frame.getContentPane().removeAll(); + frame.getContentPane().add(panel1,BorderLayout.CENTER); + frame.setVisible(true); + }}); + //****************************************************************************************************************** + //**************************************** Actions lorsque l'on clique sur Deconnexion ***************************** + //****************************************************************************************************************** + deconnexion.addActionListener( new ActionListener(){ + public void actionPerformed(ActionEvent event) { + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } }); + } } diff --git a/Implementation/src/images/Home.png b/Implementation/src/images/Home.png new file mode 100644 index 0000000000000000000000000000000000000000..b0c3484f178597bd17a547166b34e1a066d1664b GIT binary patch literal 6845 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