views:

437

answers:

3

The following class implements a chatGUI. When it runs okay the screen looks like this:

Fine ChatGUI

The problem is very often when i enter text of large length ie. 50 - 100 chars the gui goes crazy. the chat history box shrinks as shown in this

image.

Any ideas regarding what is causing this?

Thank you.

PS: the attached class below is complete code. you can copy it and compile on your computer to see exactly what i mean.

NOTE: once the GUI goes crazy then if i hit the "Clear" button the history window clears and the GUI goes back to being correctly displayed again.

package Sartre.Connect4;

import javax.swing.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.text.StyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.BadLocationException;
import java.io.BufferedOutputStream;
import javax.swing.text.html.HTMLEditorKit;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.JFileChooser;


/**
 * Chat form class
 * @author iAmjad
 */
public class ChatGUI extends JDialog implements ActionListener {

/**
 * Used to hold chat history data
 */
private JTextPane textPaneHistory = new JTextPane();

/**
 * provides scrolling to chat history pane
 */
private JScrollPane scrollPaneHistory = new JScrollPane(textPaneHistory);

/**
 * used to input local message to chat history
 */
private JTextPane textPaneHome = new JTextPane();

/**
 * Provides scrolling to local chat pane
 */
private JScrollPane scrollPaneHomeText = new JScrollPane(textPaneHome);

/**
 * JLabel acting as a statusbar
 */
private JLabel statusBar = new JLabel("Ready");

/**
 * Button to clear chat history pane
 */
private JButton JBClear = new JButton("Clear");

/**
 * Button to save chat history pane
 */
private JButton JBSave = new JButton("Save");

/**
 * Holds contentPane
 */
private Container containerPane;

/**
 * Layout GridBagLayout manager
 */
private GridBagLayout gridBagLayout = new GridBagLayout();

/**
 * GridBagConstraints
 */
private GridBagConstraints constraints = new GridBagConstraints();

/**
 * Constructor for ChatGUI
 */
public ChatGUI(){

    setTitle("Chat");

    // set up dialog icon
    URL url = getClass().getResource("Resources/SartreIcon.jpg");
    ImageIcon imageIcon = new ImageIcon(url);
    Image image = imageIcon.getImage();
    this.setIconImage(image);

    this.setAlwaysOnTop(true);

    setLocationRelativeTo(this.getParent());
    //////////////// End icon and placement /////////////////////////

    // Get pane and set layout manager
    containerPane = getContentPane();
    containerPane.setLayout(gridBagLayout);
    /////////////////////////////////////////////////////////////

    //////////////// Begin Chat History //////////////////////////////

    textPaneHistory.setToolTipText("Chat History Window");
    textPaneHistory.setEditable(false);
    textPaneHistory.setPreferredSize(new Dimension(350,250));

    scrollPaneHistory.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    scrollPaneHistory.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

    // fill Chat History GridBagConstraints
    constraints.gridx = 0;
    constraints.gridy = 0;
    constraints.gridwidth = 10;
    constraints.gridheight = 10;
    constraints.weightx = 100;
    constraints.weighty = 100;
    constraints.fill = GridBagConstraints.BOTH;
    constraints.anchor = GridBagConstraints.CENTER;
    constraints.insets = new Insets(10,10,10,10);
    constraints.ipadx = 0;
    constraints.ipady = 0;

    gridBagLayout.setConstraints(scrollPaneHistory, constraints);

    // add to the pane
    containerPane.add(scrollPaneHistory);

    /////////////////////////////// End Chat History ///////////////////////

    ///////////////////////// Begin Home Chat //////////////////////////////

    textPaneHome.setToolTipText("Home Chat Message Window");
    textPaneHome.setPreferredSize(new Dimension(200,50));

    textPaneHome.addKeyListener(new MyKeyAdapter());

    scrollPaneHomeText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    scrollPaneHomeText.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

    // fill Chat History GridBagConstraints
    constraints.gridx = 0;
    constraints.gridy = 10;
    constraints.gridwidth = 6;
    constraints.gridheight = 1;
    constraints.weightx = 100;
    constraints.weighty = 100;
    constraints.fill = GridBagConstraints.BOTH;
    constraints.anchor = GridBagConstraints.CENTER;
    constraints.insets = new Insets(10,10,10,10);
    constraints.ipadx = 0;
    constraints.ipady = 0;

    gridBagLayout.setConstraints(scrollPaneHomeText, constraints);

    // add to the pane
    containerPane.add(scrollPaneHomeText);

    ////////////////////////// End Home Chat /////////////////////////

    ///////////////////////Begin Clear Chat History ////////////////////////

    JBClear.setToolTipText("Clear Chat History");

    // fill Chat History GridBagConstraints
    constraints.gridx = 6;
    constraints.gridy = 10;
    constraints.gridwidth = 2;
    constraints.gridheight = 1;
    constraints.weightx = 100;
    constraints.weighty = 100;
    constraints.fill = GridBagConstraints.BOTH;
    constraints.anchor = GridBagConstraints.CENTER;
    constraints.insets = new Insets(10,10,10,10);
    constraints.ipadx = 0;
    constraints.ipady = 0;

    gridBagLayout.setConstraints(JBClear, constraints);

    JBClear.addActionListener(this);

    // add to the pane
    containerPane.add(JBClear);

    ///////////////// End Clear Chat History ////////////////////////

    /////////////// Begin Save Chat History //////////////////////////

    JBSave.setToolTipText("Save Chat History");

    constraints.gridx = 8;
    constraints.gridy = 10;
    constraints.gridwidth = 2;
    constraints.gridheight = 1;
    constraints.weightx = 100;
    constraints.weighty = 100;
    constraints.fill = GridBagConstraints.BOTH;
    constraints.anchor = GridBagConstraints.CENTER;
    constraints.insets = new Insets(10,10,10,10);
    constraints.ipadx = 0;
    constraints.ipady = 0;

    gridBagLayout.setConstraints(JBSave, constraints);

    JBSave.addActionListener(this);

    // add to the pane
    containerPane.add(JBSave);

    ///////////////////// End Save Chat History /////////////////////

    /////////////////// Begin Status Bar /////////////////////////////
    constraints.gridx = 0;
    constraints.gridy = 11;
    constraints.gridwidth = 10;
    constraints.gridheight = 1;
    constraints.weightx = 100;
    constraints.weighty = 50;
    constraints.fill = GridBagConstraints.BOTH;
    constraints.anchor = GridBagConstraints.CENTER;
    constraints.insets = new Insets(0,10,5,0);
    constraints.ipadx = 0;
    constraints.ipady = 0;

    gridBagLayout.setConstraints(statusBar, constraints);

    // add to the pane
    containerPane.add(statusBar);

    ////////////// End Status Bar ////////////////////////////

    // set resizable to false
    this.setResizable(false);

    // pack the GUI
    pack();
}

/**
 * Deals with necessary menu click events
 * @param event
 */
public void actionPerformed(ActionEvent event) {

    Object source = event.getSource();

    // Process Clear button event
    if (source == JBClear){

        textPaneHistory.setText(null);
        statusBar.setText("Chat History Cleared");
    }

    // Process Save button event
    if (source == JBSave){

        // process only if there is data in history pane
        if (textPaneHistory.getText().length() > 0){

            // process location where to save the chat history file
            JFileChooser chooser = new JFileChooser();

            chooser.setMultiSelectionEnabled(false);

            chooser.setAcceptAllFileFilterUsed(false);

            FileNameExtensionFilter filter = new FileNameExtensionFilter("HTML Documents", "htm", "html");

            chooser.setFileFilter(filter);

            int option = chooser.showSaveDialog(ChatGUI.this);

            if (option == JFileChooser.APPROVE_OPTION) {

                // Set up document to be parsed as HTML
                StyledDocument doc = (StyledDocument)textPaneHistory.getDocument();

                HTMLEditorKit kit = new HTMLEditorKit();

                BufferedOutputStream out;

                try {

                    // add final file name and extension
                    String filePath = chooser.getSelectedFile().getAbsoluteFile() + ".html";

                    out = new BufferedOutputStream(new FileOutputStream(filePath));

                    // write out the HTML document
                    kit.write(out, doc, doc.getStartPosition().getOffset(), doc.getLength());

                } catch (FileNotFoundException e) {

                    JOptionPane.showMessageDialog(ChatGUI.this,
                    "Application will now close. \n A restart may cure the error!\n\n"
                    + e.getMessage(),
                    "Fatal Error",
                    JOptionPane.WARNING_MESSAGE, null);

                    System.exit(2);

                } catch (IOException e){

                    JOptionPane.showMessageDialog(ChatGUI.this,
                    "Application will now close. \n A restart may cure the error!\n\n"
                    + e.getMessage(),
                    "Fatal Error",
                    JOptionPane.WARNING_MESSAGE, null);

                    System.exit(3);

                } catch (BadLocationException e){

                    JOptionPane.showMessageDialog(ChatGUI.this,
                    "Application will now close. \n A restart may cure the error!\n\n"
                    + e.getMessage(),
                    "Fatal Error",
                    JOptionPane.WARNING_MESSAGE, null);

                    System.exit(4);
                }

                statusBar.setText("Chat History Saved");
            }
        }
    }
}

/**
 * Process return key for sending the message
 */
private class MyKeyAdapter extends KeyAdapter {

    @Override
    @SuppressWarnings("static-access")
    public void keyPressed(KeyEvent ke) {

        //DateTime dateTime = new DateTime();
        //String nowdateTime = dateTime.getDateTime();

        int kc = ke.getKeyCode();

        if (kc == ke.VK_ENTER) {

            try {
                // Process only if there is data
                if (textPaneHome.getText().length() > 0){

                    // Add message origin formatting
                    StyledDocument doc = (StyledDocument)textPaneHistory.getDocument();

                    Style style = doc.addStyle("HomeStyle", null);

                    StyleConstants.setBold(style, true);

                    String home = "Home [" + nowdateTime + "]: ";

                    doc.insertString(doc.getLength(), home, style);

                    StyleConstants.setBold(style, false);

                    doc.insertString(doc.getLength(), textPaneHome.getText() + "\n", style);

                    // update caret location
                    textPaneHistory.setCaretPosition(doc.getLength());

                    textPaneHome.setText(null);

                    statusBar.setText("Message Sent");
                }

            } catch (BadLocationException e) {

                JOptionPane.showMessageDialog(ChatGUI.this,
                        "Application will now close. \n A restart may cure the error!\n\n"
                        + e.getMessage(),
                        "Fatal Error",
                        JOptionPane.WARNING_MESSAGE, null);

                System.exit(1);
            }
            ke.consume();
        }
    }
}
}
A: 

Here's a simple example that you might try.

Edit: This answer doesn't address the problem in your code, but the example might be helpful anyway.

Addendum: One other thing that may explain @Oscar Reyes' disparate result is your missing main(). I suspect his looks something like this.

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            ChatGUI g = new ChatGUI();
            g.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            g.pack();
            g.setLocationRelativeTo(null);
            g.setVisible(true);
        }
    });
}
trashgod
thanks, i will be doing network soon :)
iEisenhower
@trashgod: let me know if your have seen this crazy behaviour im getting.. Oscar said my code was running flawlessly on his machines. thanks.
iEisenhower
@ikurtz: Yes, but I have attributed the effect to my limited experience with GriBagLayout. IIUC, @Oscar Reyes' result used the modification in his answer. @camickr's correction is also apropos.
trashgod
@trashgod: thanks for the feedback. the issue has been solved using camickr's help.
iEisenhower
+2  A: 

The problem might be the layout manager you're using.

You have a GridBagLayout for all the components. Try adding the bottom component in their own panel instead of align them with the history text area.

Something like:

JScrollPane history = new JScrollPane( new JTextPane() );

JPanel inputClearSavePane = new JPanel();
// layout the input, clear save button

getContentPane().add( history );
getContentPane().add( inputClearSavePane, BorderLayout.SOUTH );

And see if that helps. edit

BTW, it works on Mac

works on my machine

Linux

on linux

And windows:

alt text

OscarRyz
+1 I missed the essence of the question about layout.
trashgod
@Oscar: if you input lots of chars without any spaces between them what happens then?
iEisenhower
@Oscar: in your code you changed the inputclearsave.. what about the statusbar?
iEisenhower
@ikurtz, same thing, I have updated the image.
OscarRyz
@Oscar: should i put the statusbar in a panel also? sorry im new at this.
iEisenhower
@ikurtz mmhh yeap... .. let me try something...
OscarRyz
@Oscar: thanks. im waiting.
iEisenhower
Quizá quisiste decir "I'll wait" :P Ready, I've changed the code, run it and see if it does what you need.
OscarRyz
@Oscar: no i get the same results as before! it shrinks!
iEisenhower
+2  A: 

Lots of general comments first:

a) Post a SSCCE when you post code. If you don't know what a SSCCE is the search the forum or web. We only have limited time to look at code and 300 lines is way too much. For example:

  • the code is set the dialog icon is irrelevant to the problem and does not run since we don't have access to your resource file
  • the code to perform the save is irrelevant since that is not the proble you are trying to solve
  • as mentioned earlier the main() method is missing

b) use proper Java naming conventions. Variable names start with a lower case character. "JBSave" and "JBClear" are not standard names and it makes your code confusing to read.

c) I also agree the the Gridbaglayout is complicated and other layout managers (like the BorderLayout approach given earlier) are easier to use. In specific your understanding of the gridx and gridy is incorrect. They should be used to indicate "sequential" row and column positions. That is in your case you should use (0, 0), (0, 1), (1, 1), (2, 1). You jumped your gridy to 10. The 10 does not reflect a relative size. So you are missing rows, 1, 2, 3, 4, 5, 6, 7... Yes, it may work, but it is confusing to understand when reading the code.

d) Don't use a KeyListener to handle the Enter key in the textpane. Swing was designed to use "Key Bindings". Read the section from the Swing tutorial on the same topic for more information.

Finally the fix to your code is simply:

    textPaneHome.setToolTipText("Home Chat Message Window");
//    textPaneHome.setPreferredSize(new Dimension(200,50));
    textPaneHome.addKeyListener(new MyKeyAdapter());

    scrollPaneHomeText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    scrollPaneHomeText.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    scrollPaneHomeText.setPreferredSize(new Dimension(200,50));

In general, you should never set the preferred size of a component added to a scrollpane. In this case when you set the text to null the preferred size gets reset to 0 and the layout of all the components appears to be redone.

camickr
@camickr: you solved it! i find often enough that little thigs like these can hold the development for a length of time. you solution is very well recieved. i will be honouring your other comments in the future. i.e. trim down the code as much as possible so the reader is only presented wih issue at hand. i thought the code i attached did just that but it was later at night and i was not clear headed enough and also from browsing this site i have seen attachments often of lengthy code so i did not think it was a problem. yes keeping it in english is good practise except he did not understand.
iEisenhower
@camichr: regarding naming conventions, im still very new at this and without intension i tend to slip around a bit. hope to perfect over time.
iEisenhower
@camichr: regarding gridbaglayout layout design. i follow a different style of laying out the components and can be confusing to someone not used to my code. what i do is draw out the layout using a table of proportions and then code it. it has served me well.
iEisenhower
@camichr: and finally a Big Thanks for your time! and another Big thanks for your solution.
iEisenhower
@camichr: yes the gridbaglayout on this form is a bit dodgy after review. thanks for mentioning it.
iEisenhower