Forum Stats

  • 3,827,340 Users
  • 2,260,762 Discussions
  • 7,897,207 Comments

Discussions

adding log as a scrolling JList within aJDialog

RichF
RichF Member Posts: 214
edited Dec 3, 2010 3:39PM in Swing
I am designing the final item for the Help menu of the [url http://r0k.us/graphics/SIHwheel.html]Interactive Color Wheel. It will be a log of user actions, to replace and extend the System.out.println() info currently going to the Java Console.

Preliminary Design
1) implemented as a single-instance JDialog containing a scrolling JList
2) typically 2 rows per entry. background color for both rows equal to selected color
3) potential 3rd row if color name mismatch (a limitation of quantized color space)
4) potential 4th row when user has Quantization Error toggled on
5) created invisibly. records events whether visible or not

The 4 rows would look something like:
@Frangipani -- #ffd9b3, luma = 217, complement = #b3d9ff
   r,g,b = 255, 217, 179  --  h,s,b =  30°, 29.8%, 255
   Frangipani (FFDEB3) != NTC's Light Apricot (#FDD5B1), false
   hqe(#FDD5B1) = (2r, 4g, 2b) = 3%
Normally, only the first two lines would exist. The third and/or fourth lines are conditional.

Concerns
1) Is there a practical or actual limit to how many items in JList?
2) Whether or not limit, would it be beneficial to discard earliest rows when beyond some pre-determined limit?
3) Is there an easy way to enable copying JList rows (or dragged text) to clipboard?
.... If not, I'll add button to enable copying selected row(s) to clipboard.
4) Is a scrolling JList actually the best way to handle all this?

The clipboard stuff is based on user feedback. Some folks are looking for specific color(s), and they would like an easy way to capture that info.

Any feedback about my concerns, or even the general design, would be appreciated. Thank you.

-- Rich
«13

Answers

  • camickr
    camickr Member Posts: 24,931
    edited Nov 26, 2010 9:58PM
    How about using a JTable.

    You create a custom Object to store all the information, then you can display this information in the columns of the table.

    Its easy to control the number of rows in the table if you wish.

    A table already supports copying a row to the cliipboard.

    Or maybe a JTree. The main node would represent the log entry. Then multiple child nodes would represent the details. Not sure how copying to the clipboard would work.

    The key to each of these is that each object is represented at a high level so its easy to idenify and remove a complete object. Using the JList approach you don't know if you have 2, 3, 4 lines of data for each object.
  • RichF
    RichF Member Posts: 214
    edited Nov 28, 2010 8:42AM
    Nevermind, found the problem. See next post.

    I probably will go with the JTable approach. I was concerned about width, but then it occurred to me that all the columns need not be full width; most users will not be interested in the optional info anyways.

    But right now I am fighting a weird "two windows with one handle" problem. I've stripped my program down to a stand-alone executable that demonstrates the problem:
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class LogBox extends JApplet
    {
        public static SIHListPane colorList;
        public static HelpBox lBox;
    
        public void init()
        { // the static block above should have already executed
    	try {
    	    javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
    		public void run() {
    		    JPanel frame = makeContent();
    		    setContentPane(frame);
    		}
    	    });
    	} catch (Exception e) {
    	    System.err.println("makeContent() failed to complete: " + e);
    	    e.printStackTrace();
    	}
        }
    
        public JPanel makeContent()
        {
    	colorList = new SIHListPane();
    	lBox = new HelpBox("Log", new Dimension(800, 400));
    
    	JPanel outer = new JPanel();
    	outer.add(colorList);
    	return(outer);
        }
    
        // method expected by programs
        public static void main(String args[])
        {   // JFrame exists only when running as program, not applet
    	SwingUtilities.invokeLater(new Runnable()
    	{   // something to do with concurrency ...
    	    // @Override  // not supported in target Java 4 platform
    	    public void run()
    	    { // the static block at top should have initialized NTC already
    		JFrame f = new JFrame("LogBox Test");
    		LogBox logBox = new LogBox();
    
    		// in this paradigm, main() does not call init()
    		f.setContentPane(logBox.makeContent());
    
    		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		f.setResizable(false);
    		f.pack();
    		f.setLocationRelativeTo(null);  // center on screen
    	        f.setVisible(true);
    	    }
    	});
        }
    }
    
    // list-widget GUI
    class SIHListPane extends JPanel
    {
        private JTextField		hexEntry;
        private String		normalHexLabel = new String("Hex Color: ");
        private JLabel		hexLabel = new JLabel(normalHexLabel);
    
        public SIHListPane()
        {
    	this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    	JMenuBar menuBar = new JMenuBar();
    	menuBar.setBorderPainted(true);
    	menuBar.setPreferredSize(new Dimension(200, 40));
    	JMenu dictionary = new JMenu("Color Lists");
    	menuBar.add(dictionary);
    	menuBar.add(Box.createHorizontalGlue());
    
    	JMenu help = new JMenu("Help");
    	JMenuItem log = new JMenuItem("Show Log");
    	log.addActionListener( new ActionListener() {
    	    public void actionPerformed(ActionEvent e) {
    		LogBox.lBox.setVisible(true);
    	    }
    	});
    	help.add(log);
    	menuBar.add(help);
    	add(menuBar);
    
    	JPanel hexPanel = new JPanel();
    	hexPanel.setBorder(BorderFactory.createLineBorder(Color.black, 1));
    	hexPanel.setPreferredSize(new Dimension(200, 30));
    	hexPanel.add(hexLabel);
    	hexEntry = new JTextField("", 6);
            hexEntry.setForeground(Color.white);
    	hexEntry.setBackground(Color.darkGray);
    	hexEntry.setCaretColor(Color.lightGray);
    	hexEntry.setToolTipText("3-digit CSS or 6-digit HTML");
    	hexEntry.setActionCommand("hText");
    	hexPanel.add(hexEntry);
    	add(hexPanel);
        }
    }
    
    class HelpBox extends JDialog
    {
        private JDialog		jd;
    
        // window for log; instantiated once, in a runnable of SIHwheel
        HelpBox(String title, Dimension size)
        {
    	jd = new JDialog((Frame)null, title, false);
    	jd.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
    	jd.setPreferredSize(size);
    	jd.pack();
    	jd.setLocationRelativeTo(null);
    	jd.setVisible(true);
        }
    }
    I know it appears very convoluted, but I kept the structure of the 3454-line SIHwheel.java source. It seemed a good idea to keep the full environment in the test.

    The initial problem was that the JDialog shows from the menu uncentered, with no title and no size. As a debug step, I added the final "jd.setVisible(true);" line you see several centimeters above. Thus the dialog shows when you launch the program, and it has location, size, and title. Use the help menu to display it, and a second dialog appears, but again in ULC with no title or size. There are two dialogs!

    What is even weirder, is that they have the same handle. Obscure or partially obscure both, then use menu to make "it" visible". They both come to the front!

    If you make the titled dialog go away, you cannot get it back. There is only the untitled one left. You can make it go away and come back as many times as you want, and it remembers where you moved it to and how you resized it.

    If you got this far, you are probably wondering why the dialog is created in a separate class. The HelpBox class is actually bigger than I show. and will be handling creation and management of the JTable pane which will go into the dialog. It also has a second (actually first) constructor, to create an HTML help window. The creation of that dialog is handled within an embedded runnable. I didn't feel that the log window needed to be created directly within a runnable because it is launched from a runnable up top.

    I just tried extending the test program above to use a runnable in the creation of the log window, and it made no difference.
        HelpBox(String pTitle, Dimension pSize)
        {
    	final String title = pTitle;
    	final Dimension size = pSize;
    	SwingUtilities.invokeLater(new Runnable() {
    	    public void run() {
    		jd = new JDialog((Frame)null, title, false);
    		jd.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
    		jd.setPreferredSize(size);
    		jd.pack();
    		jd.setLocationRelativeTo(null);
    		jd.setVisible(true);
    	    }
    	});
        }
    The permanent log window made visible by the menu still has no title or size. The initial one displayed has these characteristics. And they both still respond to the menu setVisible() command.

    Edited by: RichF on Nov 28, 2010 8:41 AM
  • RichF
    RichF Member Posts: 214
    edited Nov 28, 2010 8:48AM
    I just extended the constructor to include adding a JTable. My hope was that if it had something inside of it, the dialog would behave itself. Nope.
        <<< moot >>>
    The initial, temporary one has everything, including the JTable. The permanent one is still completely empty, sizeless, and titleless.

    Edited by: RichF on Nov 28, 2010 8:42 AM
    Found the darn problem! An apple is not an orange.
    ----- in the menu constructor -----
    	JMenu help = new JMenu("Help");
    	JMenuItem log = new JMenuItem("Show Log");
    	log.addActionListener( new ActionListener() {
    	    public void actionPerformed(ActionEvent e) {
    		LogBox.lBox.getDialog().setVisible(true);
    	    }
    	});
    
    ----- the HelpBox class -----
    class HelpBox extends JDialog
    {
        private JDialog		jd;
    
        // window for log; instantiated once, in a runnable of SIHwheel
        HelpBox(String title, Dimension size)
        {
    	jd = new JDialog((Frame)null, title, false);
    	jd.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
    	jd.setPreferredSize(size);
    	jd.pack();
    	jd.setLocationRelativeTo(null);
    	JTable jt = new JTable(10, 8);
    	jd.getContentPane().add(jt);
        }
    
        public JDialog getDialog() {
    	return(jd);
        }
    }
    Sorry to waste your time.
  • RichF
    RichF Member Posts: 214
    Better yet, I stopped using jd altogether. The first line of the constructor is now:
    super((Frame)null, title, false);
    The menu callback has gone back to its original form, without the getDialog() segment.

    I didn't do this initially because it wouldn't work in the first constructor.
        // general window for display of HTML page
        HelpBox(String pTitle, String pUrlS, boolean pModal, Dimension pSize)
        {
    	final String	title = pTitle;
    	final String	urlS  = pUrlS;
    	final Dimension	size  = pSize;
    	final boolean	modal = pModal;
    
    	SwingUtilities.invokeLater(new Runnable() {
    	    public void run() {
    		jd = new JDialog((Frame)null, title, modal);
    		jd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    		JEditorPane ep = new JEditorPane();
    		ep.setEditable(false);
    		try {
    		    URL url = getClass().getResource(urlS);
    		    ep.setPage(url);
    		    JScrollPane eps = new JScrollPane(ep);
    		    eps.setPreferredSize(size);
    		    jd.getContentPane().add(eps, BorderLayout.CENTER);
    		} catch (IOException ioE) {
    		    System.err.println("Unable to display help pane");
    		    ioE.printStackTrace();
    		}
    		jd.pack();
    		jd.setLocationRelativeTo(null);
    		jd.setVisible(true);
    	    }
    	});
        }
    I could not find any way to stick a super() constructor in there that would let me also have a runnable. With a first constructor line of
    	super((Frame)null, pTitle, pModal);
    , how the heck do you reference that from the runnable doohickey below? The runnable compiles separately, "HelpBox$1.class".

    (a few minutes later). Nevermind. You don't reference it. Somehow it knows that a simple pack() within the runnable class means the thing created outside the runnable, in the super() constructor of the primary HelpBox class.

    Note to Rich: stop overthinking things. Try simple first.
  • RichF
    RichF Member Posts: 214
    camickr wrote:
    How about using a JTable.
    ...
    Its easy to control the number of rows in the table if you wish.
    I've started work on the JTable infrastructure. I cannot find a method to add (or delete) a row in either the [url http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/JTable.html]JTable docs or the [url http://download.oracle.com/javase/7/docs/api/javax/swing/table/TableModel.html]TableModel docs. The [url http://download.oracle.com/javase/tutorial/uiswing/components/table.html]tutorial does not mention it either. I'm likely missing something obvious, but what?

    Curiously, there is an addColumn() method, but not an addRow().
  • darrylburke
    darrylburke Member Posts: 18,007
    Scee the docs for DefaultTableModel.

    db
    darrylburke
  • camickr
    camickr Member Posts: 24,931
    I cannot find a method to add (or delete) a row in either the JTable docs or the TableModel docs.
    No this is not a method of the TableModel interface.

    However, add/removeRow methods are implemented on the DefaultTableModel. Or if you need a create a customized TableModel to hold your logging Object then you can try using the [url http://tips4java.wordpress.com/2008/11/27/bean-table-model/]Bean Table Model
    camickr
  • RichF
    RichF Member Posts: 214
    edited Nov 28, 2010 11:09AM
    There I go, thinking again. TableModel is obviously more flexible than DefaultTableModel, right? ;) ( answer: no! )
  • RichF
    RichF Member Posts: 214
    edited Nov 30, 2010 4:57AM
    I have the first pass up and running at my site:

    * http://r0k.us/graphics/SIHwheel.html

    The JTable is part of the Color Log, accessed via the Help --> Show Log menu item. It uses the DefaultTableModel unchanged. Even as is, it provides significantly more capability than the previous logging to System.out. And I don't have to explain to anyone how to access the Java Console!

    There are still some things I plan to do:

    1) gain access to the system clipboard so that the user can copy data there. Apparently I'll have to sign the applet and likely pay some 3rd party verifier to prevent a "do you really want to run this" requester whenever someone accesses the page. Sigh, I don't make any money from this, so paying someone just to allow copying to the clipboard seems pretty darn extravagant. (How come users can copy from the HTML page to the clipboard, but not from a Java applet on that page? I know there is a sandbox, but what would be the harm in access to the clipboard? You can even copy stuff from the Java Console for the applet! Only the poor applet itself is naked and alone. :( )
    2) Add a custom renderer so I can display rows in the various colors being logged.
    3) Pretty-up general formatting -- column widths, data alignment, etc.
    4) Probably begin auto-deleting early rows. I'm thinking whenever row count gets to 3500, delete the first 1000 rows. Is this a good idea?
    5) Probably eliminate cell-editing. The capability doesn't seem to be hurting anything, but it seems to interfere with row selection and <ctrl>c copies to the clipboard. (Tested when running as a program, not an applet.)
    6) I'd like the row(s) selection and copy feature of a default JTable, with no table model specified. That's actually on the back-burner until I decide whether to start signing and having it verified.

    I'll appreciate suggestions concerning presentation and/or implementation. Thanks.

    -- Rich
  • RichF
    RichF Member Posts: 214
    !!???!! One can copy to the clipboard either of the JTextField's, or any of the table cells. However, the button on the color log, which saves all the data below it, casts a security violation when executed as an applet. How can I bypass the violation and do the same thing those widgets are allowed to do?
This discussion has been closed.