Swing Notes Project
— print (last updated: Nov 13, 2009) print

Select font size:
Download the SwingNotes.zip archive. 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.

Create a new Library in NetBeans

The SwingNotes project requires JAR files available in NetBeans. For convenience, I have made a new group of JAR files which we will install as a Library in NetBeans. First download the zipped directory of JAR files:
HibernateSearch.zip
Extract it as the folder HibernateSearch into the location of your choice. Some suggestions are (create these folders if necessary, or make other choice):
(Windows XP):  My Documents\java\
(Mac OSX):     ~/Library/Java
(Linux):       ~/lib/java               (without root access)
               /usr/local/share/java    (with root access)
Create the HibernateSearch library in NetBeans as follows:
  1. Go to Tools Libraries and click the New Library button (bottom, left).
  2. In the Library Name, type HibernateSearch. Click OK.
  3. With the Classpath tab selected, click the Add Jar/Folder button and navigate to the location of the HibernateSearch folder.
  4. Select all the JAR files within the HibernateSearch folder and again click the Add Jar/Folder button.
  5. Back in the Library Manager, click OK.

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 in a scroll area whereas the JComboBox only keeps the "selected one" visible in a drop-down fashion. 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 guest swingnotes < table.sql
mysql -u guest swingnotes < table-data.sql
The first two are these:

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

table.sql
DROP TABLE IF EXISTS notes; CREATE TABLE notes ( id INTEGER AUTO_INCREMENT, subject VARCHAR(50) NOT NULL, content TEXT NOT NULL, created TIMESTAMP NOT NULL, PRIMARY KEY(id) );
The table-data.sql file has this format:
insert into notes (subject,content) values
 ( "ORMs",               "... a long content string ..." ),
 ( "NetBeans Designer",  "... a long content string ..." ),
 ( "MVC event handling", "... a long content string ..." );
Note the usage of the MySQL TIMESTAMP field, which usually automatically updates the time whenever the record is modified (unless the TIMESTAMP field itself is reset). Strangely enough, Hibernate does not do achieve this type of modification except when the inital record is created. function now() to give each entry the creation timestamp value. You can check the validity of the operations by running:
mysql -u guest swingnotes
mysql> show tables;
mysql> describe notes;
mysql> select id,subject,created from notes;
mysql> quit

NetBeans Installation

To install the entire source at once.
  1. move the SwingNotes project folder into your NetBeansProjects directory.
  2. create a Java project from existing sources.
  3. add the MySQL and the new HibernateSearch (note Hibernate) libraries.

Step-by-step Contruction

  1. Create the new Java Application project SwingNotes.
  2. Add the MySQL and HibernateSearch libraries.
  3. Create a JFrame Form as the class views.Frame.
  4. Set the layout of Frame to be BorderLayout.
  5. Drag a Split Pane container onto the Frame. Edit its properties (use the Inspector) and make the orientation be VERTICAL_SPLIT
  6. Drag a List into the top part
  7. Drag a TextArea into the bottom part
  8. Change the variable name of the List to be list.
  9. Change the variable name of the TextArea to be display.
  10. Edit list properties.
  11. Edit the display text area properties:
  12. Edit Frame in source mode. Add these imports:
    import javax.swing.*;
    import javax.swing.event.*;
    import java.awt.event.*;
    
    and add these member functions:
      public void setListModel(ListModel listmodel) {
        list.setModel(listmodel);
      }
    
      public void setSelectedIndex(int index) {
        list.setSelectedIndex(index);
      }
    
      public int getSelectedIndex() {
        return list.getSelectedIndex();
      }
    
      public Object getSelectedValue() {
        return list.getSelectedValue();
      }
    
      public void addListSelectionListener(ListSelectionListener listener) {
        list.addListSelectionListener(listener);
      }
    
      public void setDisplayText(String text) {
        display.setText(text);
        display.setCaretPosition(0);
      }
    
  13. Add this class to the views package:

    views/DisplayNote.java
    package views; import models.Note; import java.text.DateFormat; public class DisplayNote extends Note { public DisplayNote(Note note) { super(note); } @Override public String toString() { return "<html>" + DateFormat.getDateTimeInstance().format(getCreated()) + " | " + "<font color=red>" + getSubject() + "</font>"; } }
  14. Add these .xml files:

    hibernate.cfg.xml
    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url"> jdbc:mysql://localhost/swingnotes</property> <property name="hibernate.connection.username">guest</property> <property name="hibernate.search.default.indexBase">indexes</property> <mapping resource="tables.hbm.xml"/> <event type="post-update"> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-insert"> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-delete"> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> </session-factory> </hibernate-configuration>

    tables.hbm.xml
    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="models.Note" table="notes"> <id column="id" name="id" type="int"> <generator class="increment"/> </id> <property name="subject"/> <property name="content"/> <property name="created" type="timestamp"/> </class> </hibernate-mapping>
  15. Create the models package with these classes (models.DB is same as usual):

    models/Note.java
    package models; import java.sql.Timestamp; import org.hibernate.search.annotations.*; @Indexed public class Note { @DocumentId private int id; private String subject; @Field private String content; private Timestamp created; public int getId() { return id; } void setId(int id) { this.id = id; } public Timestamp getCreated() { return created; } void setCreated(Timestamp created) { this.created = created; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Note(String subject, String content) { this.subject = subject; this.content = content; } public Note() { } protected Note(Note n) { this.id = n.id; this.subject = n.subject; this.content = n.content; this.created = n.created; } @Override public String toString() { return "id: " + id + ", created: " + created + ", subject: " + subject + "\n-------------------------------------\n" + content; } }

    models/DB.java
    package models; import org.hibernate.*; import org.hibernate.cfg.Configuration; public class DB { public Session getSession() { return sessionFactory.openSession(); } private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (HibernateException x) { System.err.println("Initial SessionFactory creation failed. " + x); throw new ExceptionInInitializerError(x); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
  16. Modify the code of Main (a number of imports and features anticipate your future needs):

    swingnotes/Main.java
    package swingnotes; import javax.swing.*; import javax.swing.event.*; import java.awt.event.*; import java.util.List; import org.hibernate.*; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.hibernate.criterion.*; import org.apache.lucene.queryParser.*; import org.apache.lucene.analysis.standard.StandardAnalyzer; import views.*; import models.*; public class Main { private Frame frame = new Frame(); private DB db = new DB(); private DefaultListModel listmodel = new DefaultListModel(); private void error(String message) { JOptionPane.showMessageDialog(frame, message); } private int getSelectedId() { return ((Note) frame.getSelectedValue()).getId(); } private void loadList() { Session sess = db.getSession(); List<Note> notes = sess.createCriteria(Note.class).list(); listmodel.clear(); for (Note note : notes) { listmodel.addElement(new DisplayNote(note)); } } public Main() { loadList(); // initialize list frame.setListModel(listmodel); frame.setBounds(30, 30, 500, 500); frame.setVisible(true); frame.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent evt) { if (evt.getValueIsAdjusting()) { return; } Note n = (Note) frame.getSelectedValue(); frame.setDisplayText(n.getContent()); } }); } public static void main(String[] args) { //System.err.close(); new Main(); } }
    Do a test-run.


© Robert M. Kline