MemoList
— print (last updated: Feb 14, 2009) print

Select font size:
Download the MemoList.ziparchive. This contains the project folder MemoList folders with two subfolders:
src/        source code for the project
setup/      database setup SQL files
For expediency you can install the project from existing sources, but it is recommended to go through the step-by-step approach to better appreciate the constructions.

Swing JList Component

Many of the complex swing components employ one or more models which control a portion of the total behavior of the component. The two primary models are the data and selection models. The data model usually governs how to control the contents of the component whereas the selection model governs the behavior of selected elements. Java provides default data and selection models which are often sufficient for most purposes, but allows a developer to create other non-standard models. This separation of component and its models gives the component an extra degree of generality and flexibility.

Both JList and JComboBox objects are used to maintain lists of objects. The difference is that the JList keeps a certain number visible whereas the JComboBox only keeps the "selected one" visible. In the case of a JList, we use a model object defined in the controller like this
DefaultListModel listmodel = new DefaultListModel();
This model is associated to the JList component, say, with name list, via the setModel operation by an interface function:
public void setListModel(ListModel lm) {
  list.setModel(lm);
}
In the controller, these are typical operations:
listmodel.addElement( some_element );    // add an element
listmodel.clear();                       // remove all elts
The JList object itself is responsible for determining the selection as well as generating an event when the selection changes. Use these operations for determining selection:
list.getSelectedValue()
list.getSelectedIndex();
Typical to our MVC approach, if we need to know the selected value without a selection change taking place, we would write an interface function:
public Object getListSelectedValue() {
  return list.getSelectedValue();
}
Most importantly a ListSelectionListener is used for activation when the selection is changed. Again, we will create an interface function to the container which employs the list
public void addListSelectionListener(ListSelectionListener lsl) {
  list.addListSelectionListener(lsl);
}
The controller then writes a handler operation:
view_container.addListSelectionListener(
  new ListSelectionListener() {
    public void itemStateChanged(ItemEvent evt) {
      ...
    }
  }
);
This function is called when a list selection is changed, both on deselection of the previous element with mouse down and selection of the new element with mouse up; the rationale is that this behavior is needed for various drag-and-drop operations. A special event-based getValueIsAdjusting() function is usually used to filter out the deselection event:
if (evt.getValueIsAdjusting()) return;

Database setup

The setup folder contains MySQL scripts which can be used to set up the database, table and provide initial population of data. Open a shell, using the cd shell command, navigate to the setup folder, then run these commands:
mysql -u root < database.sql
mysql -u root < table-create.sql
mysql -u root < table-data.sql
The first two are these:

database.sql
create database if not exists memolist; grant all on memolist.* to guest@localhost;

table-create.sql
use memolist; drop table if exists memos; create table memos ( id integer auto_increment not null, subject varchar(50) not null, content text not null, creation datetime not null, primary key(id), fulltext(content) ) type=myisam
The table-data.sql file has this format:
use memolist;
insert into memos (subject,content,creation) values
 ( "Dialogs", "... a very long content string ...", now() ),
 ( "JLists and JComboBoxes", "... a very long content string ...", now() );
Note the usage of the MySQL function now() to give each entry the creation timestamp value. We see, the database is memolist and the table is memos and so you can check the validity of the operations by running:
mysql -u guest memolist
mysql> show tables;
mysql> describe memos;
mysql> select id,subject,creation from memos;
mysql> quit
You should get positive results from these steps. If so, go ahead with the application.

NetBeans Installation

To install the entire source, move the MemoList project folder into your NetBeansProjects directory. Create a project from existing sources and add the MySQL library.

Step-by-step Contruction

  1. Create the new Java Application project MemoList.
  2. Add the MySQL library.
  3. Create a JFrame Form as the class views.Frame.
  4. Set the layout of Frame to be BorderLayout.
  5. Set the minimum size to be [400,500] in Properties.
  6. Drag a Split Pane container onto the Frame
  7. Open the properties of the SplitPane (use the Inspector) and make the orientation be VERTICAL_SPLIT
  8. Drag a List into the top part
  9. Drag a TextArea into the bottom part
  10. Change the variable name of the List to be list.
  11. Change the variable name of the TextArea to be display.
  12. Edit list properties. Remove the default value of the model property: [item1,item2,…] (remove all values).
  13. Edit the display text area properties:
  14. Edit Frame in source mode. Add these imports:
    import java.awt.BorderLayout;
    import javax.swing.event.*;
    import javax.swing.*;
    import java.awt.Color;
    import java.awt.Font;
    
    and add these member functions:
      public void setListModel(ListModel listmodel) {
        list.setModel(listmodel);
      }
    
      public void addListSelectionListener(ListSelectionListener s) {
        list.addListSelectionListener(s);
      }
    
      public void setText(String text) {
        display.setText(text);
        display.setCaretPosition(0);
      }
      
      //-------------------------------------
    
      public void setListFont(Font font) {
        list.setFont(font);
      }
    
      public void setListSelectionBackground(Color color) {
        list.setSelectionBackground(color);
      }
    
      public void setListSelectionForeground(Color color) {
        list.setSelectionForeground(color);
      }
    
      public void setListCellRenderer(ListCellRenderer rend) {
        list.setCellRenderer(rend);
      }
      
      public void addHeader(java.awt.Component c) {
        add(c, BorderLayout.PAGE_START);
      }
    
  15. Edit memolist.Main and create the usual starter code. Do a test-run to make sure it looks correct.
    package memolist;
    import views.*;
    
    public class Main {
      private Frame frame = new Frame();
      
      Main() {
        frame.setVisible(true);
      }
    
      public static void main(String[] args) { new Main(); }
    }
    
  16. Add these classes and .properties files (the Properties file generator can be found in the Other group).

    models/Memo.java
    package models; import java.sql.Timestamp; import java.text.DateFormat; public class Memo { public Integer id; public String subject; public String content; public Timestamp creation; public Memo(String subject, String content) { this(null,subject,content,null); } Memo(Integer id, String subject, String content, Timestamp creation) { this.id = id; this.subject = subject; this.content = content; this.creation = creation; } @Override public String toString() { return DateFormat.getDateTimeInstance().format(creation) + " | " + subject; } }

    models/Memos.java
    package models; import models.db.*; import java.sql.*; import java.util.*; public class Memos { private String table_name = getClass().getSimpleName().toLowerCase(); private Database db; public Memos() throws Exception { db = new Database(); } public Memos(Properties props) throws Exception { db = new Database(props); } public List<Memo> fetchAll() throws Exception { Connection cx = db.connect(); String sql_op = "SELECT * FROM " + table_name; Statement st = cx.createStatement(); ResultSet rs = st.executeQuery(sql_op); List<Memo> L = new LinkedList<Memo>(); while (rs.next()) { Integer id = rs.getInt("id"); String subject = rs.getString("subject"); String content = rs.getString("content"); Timestamp creation = rs.getTimestamp("creation"); L.add(new Memo(id, subject, content, creation)); } return L; } }

    models/db/Database.java
    package models.db; import java.sql.*; import java.util.*; public class Database { private String url; private String user; private String password; private Connection cx = null; public Database() throws Exception { ResourceBundle props = ResourceBundle.getBundle("models.db.database"); url = props.getString("url"); user = props.getString("user"); password = props.getString("password"); } public Database(Properties props) throws Exception { url = props.getProperty("url"); user = props.getProperty("user"); password = props.getProperty("password"); } public Connection connect() throws Exception { if (cx == null || cx.isClosed()) { cx = DriverManager.getConnection(url, user, password); } return cx; } }

    models/db/database.properties
    url=jdbc:mysql://localhost/memolist user=guest password=
  17. Modify the code of memolist.Main. Add these imports:
    import javax.swing.*;
    import java.util.List;
    import javax.swing.event.*;
    import models.*;
    
    Then rewrite the Main class:
    public class Main {
      private Frame frame = new Frame();
      private Memos memos = new Memos();
      private DefaultListModel listmodel = new DefaultListModel();
    
      private void initializeList() throws Exception {
        List<Memo> L = memos.fetchAll();
        for (Memo memo : L) {
          listmodel.addElement(memo);
        }
      }
    
      Main() throws Exception {
        initializeList();
    
        frame.setListModel(listmodel);
    
        frame.setVisible(true);
    
        frame.addListSelectionListener(
          new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent evt) {
              if (evt.getValueIsAdjusting()) {
                return;
              }
              JList list = (JList) evt.getSource();
              Memo n = (Memo) list.getSelectedValue();
              frame.setText(n.content);
            }
          });
      }
    
      public static void main(String[] args) {
        try {
          new Main();
        } catch (Exception x) {
          x.printStackTrace();
          JOptionPane.showMessageDialog(null, x);
          System.exit(1);
        }
      }
    }
    
    Do a test-run.

Visual enhancements

The remaining things offer visual enhancements achieved by adding cell rendering.
  1. Create the classes:

    memolist/MemoListCell.java
    package memolist; import javax.swing.*; import java.awt.*; public class MemoListCell extends JPanel { private Color fg, bg; private Font font; private String datePart, subjPart; private int subjStart = 200; public MemoListCell(Color fg, Color bg, Font font, String datePart, String subjPart) { this.fg = fg; this.bg = bg; this.font = font; this.datePart = datePart; this.subjPart = subjPart; } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setFont(font); g.setColor(bg); int width = getWidth(), height = getHeight(); g.fillRect(0, 0, width, height); g.setColor(fg); int asc = g.getFontMetrics().getAscent(); g.drawString(datePart, 0, asc); g.drawString(subjPart, subjStart, asc); g.drawLine(0, height - 1, width, height - 1); } @Override public Dimension getPreferredSize() { int asc = getGraphics().getFontMetrics().getAscent(); return new Dimension(getWidth(), 3 * asc /2); } }

    memolist/MemoRenderer.java
    package memolist; import javax.swing.*; import java.awt.*; import java.text.DateFormat; import models.Memo; public class MemoRenderer implements ListCellRenderer { @Override public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) { Memo memo = (Memo) value; Color fg = isSelected ? list.getSelectionForeground() : list.getForeground(); Color bg = isSelected ? list.getSelectionBackground() : list.getBackground(); Font font = list.getFont(); MemoListCell cell = new MemoListCell(fg, bg, font, DateFormat.getDateTimeInstance().format(memo.creation), memo.subject); return cell; } }
  2. Modify the code of memolist.Main. Add these extra imports:
    import java.awt.Color;
    import java.awt.Font;
    
    add this member function:
      private void enhance() {
        Font font = Font.decode("Arial Bold 12");
        Color selectfg, selectbg, headerfg, headerbg;
    
        selectbg = headerfg = Color.decode("#cc0000");
        selectfg = Color.decode("#ffffff");
        headerbg = Color.decode("#ccddee");
    
        frame.setListFont(font);
        frame.setListSelectionBackground(selectbg);
        frame.setListSelectionForeground(selectfg);
    
        frame.setListCellRenderer(new MemoRenderer());
    
        frame.addHeader(
          new MemoListCell(headerfg, headerbg, font, "Time", "Subject")
        );
      }
    
    and call enhance() in the Main constructor:
        frame.setListModel(listmodel);
    
        enhance();
    
        frame.setVisible(true);
    


© Robert M. Kline