| BooksListModel.java |
1 /**
2 * Distribution License:
3 * BibleDesktop is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU General Public License, version 2 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/gpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: BooksListModel.java 2230 2012-02-08 00:00:10Z dmsmith $
21 */
22 package org.crosswire.bibledesktop.book;
23
24 import java.io.IOException;
25 import java.io.ObjectInputStream;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.List;
30
31 import javax.swing.AbstractListModel;
32 import javax.swing.event.ListDataListener;
33
34 import org.crosswire.common.util.Logger;
35 import org.crosswire.jsword.book.Book;
36 import org.crosswire.jsword.book.BookFilter;
37 import org.crosswire.jsword.book.BookList;
38 import org.crosswire.jsword.book.Books;
39 import org.crosswire.jsword.book.BooksEvent;
40 import org.crosswire.jsword.book.BooksListener;
41
42 /**
43 * BooksListModel creates a Swing ListModel from the available Bibles. I would
44 * normally implement BooksListener in an inner class however doing that would
45 * stop me calling fireInterval*() in AbstractListModel because that is a
46 * protected method and the inner class is neither in the same package or a sub
47 * class.
48 *
49 * @see gnu.gpl.License for license details.<br>
50 * The copyright to this program is held by it's authors.
51 * @author Joe Walker [joe at eireneh dot com]
52 */
53 public class BooksListModel extends AbstractListModel {
54 /**
55 * Basic constructor
56 */
57 public BooksListModel() {
58 this(null, null);
59 }
60
61 /**
62 * Basic constructor
63 */
64 public BooksListModel(BookFilter filter) {
65 this(filter, Books.installed(), null);
66 }
67
68 /**
69 * Basic constructor, redefining ordering.
70 */
71 public BooksListModel(BookFilter filter, Comparator<Book> comp) {
72 this(filter, Books.installed(), comp);
73 }
74
75 /**
76 * Basic constructor for a filtered list of books, ordered as requested.
77 */
78 public BooksListModel(BookFilter filter, BookList bookList, Comparator<Book> comparator) {
79 this.filter = filter;
80 this.bookList = bookList;
81 this.comparator = comparator;
82
83 cacheData();
84 }
85
86 /* (non-Javadoc)
87 * @see javax.swing.ListModel#getSize()
88 */
89 public synchronized int getSize() {
90 return books.size();
91 }
92
93 /* (non-Javadoc)
94 * @see javax.swing.ListModel#getElementAt(int)
95 */
96 public synchronized Object getElementAt(int index) {
97 // PARANOIA(joe): this check shouldn't be needed
98 if (index > books.size()) {
99 log.error("trying to get book at " + index + " when there are only " + books.size() + " known books.");
100 return null;
101 }
102
103 return books.get(index);
104 }
105
106 /**
107 * Returns the index-position of the specified object in the list.
108 *
109 * @param test
110 * the object to find
111 * @return an int representing the index position, where 0 is the first
112 * position
113 */
114 public synchronized int getIndexOf(Object test) {
115 return books.indexOf(test);
116 }
117
118 /**
119 * @param filter
120 */
121 public void setFilter(BookFilter filter) {
122 synchronized (this) {
123 this.filter = filter;
124 }
125 cacheData();
126
127 fireContentsChanged(this, 0, getSize());
128 }
129
130 /* (non-Javadoc)
131 * @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener)
132 */
133 @Override
134 public void addListDataListener(ListDataListener li) {
135 if (listenerList.getListenerCount() == 0) {
136 bookList.addBooksListener(listener);
137 }
138
139 super.addListDataListener(li);
140 }
141
142 /* (non-Javadoc)
143 * @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener)
144 */
145 @Override
146 public void removeListDataListener(ListDataListener li) {
147 super.removeListDataListener(li);
148
149 if (listenerList.getListenerCount() == 0) {
150 bookList.removeBooksListener(listener);
151 }
152 }
153
154 /**
155 * Setup the data-stores of the current Bibles and drivers
156 */
157 protected final synchronized void cacheData() {
158 books = new ArrayList<Book>();
159 books.addAll(bookList.getBooks(filter));
160 Collections.sort(books, comparator);
161 }
162
163 /**
164 * So we can get a handle on what Bibles there are
165 */
166 class CustomListDataListener implements BooksListener {
167 /* (non-Javadoc)
168 * @see org.crosswire.jsword.book.BooksListener#bookAdded(org.crosswire.jsword.book.BooksEvent)
169 */
170 public void bookAdded(BooksEvent ev) {
171 int oldsize = getSize();
172 cacheData();
173 fireContentsChanged(ev.getSource(), 0, oldsize);
174 }
175
176 /* (non-Javadoc)
177 * @see org.crosswire.jsword.book.BooksListener#bookRemoved(org.crosswire.jsword.book.BooksEvent)
178 */
179 public void bookRemoved(BooksEvent ev) {
180 int oldsize = getSize();
181 cacheData();
182 fireContentsChanged(ev.getSource(), 0, oldsize);
183 }
184 }
185
186 /* (non-Javadoc)
187 * @see javax.swing.AbstractListModel#fireContentsChanged(java.lang.Object, int, int)
188 */
189 @Override
190 protected void fireContentsChanged(Object source, int index0, int index1) {
191 super.fireContentsChanged(source, index0, index1);
192 }
193
194 /**
195 * Serialization support.
196 *
197 * @param is
198 * @throws IOException
199 * @throws ClassNotFoundException
200 */
201 private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
202 listener = new CustomListDataListener();
203 filter = null;
204 // This is not quite right. Probably should write out the Book initials
205 // and read them in here.
206 // But at this time we don't serialize views.
207 bookList = Books.installed();
208 books = new ArrayList<Book>();
209
210 is.defaultReadObject();
211 }
212
213 /**
214 * The list of books in this tree
215 */
216 private transient BookList bookList;
217
218 /**
219 * The filter used to choose Bibles
220 */
221 private transient BookFilter filter;
222
223 /**
224 * The listener
225 */
226 private transient CustomListDataListener listener = new CustomListDataListener();
227
228 /**
229 * The array of versions. All methods that access this variable have been
230 * marked synchronized to ensure that one thread can't update the list of
231 * books while another is trying to create a JList based on this class.
232 */
233 protected transient List<Book> books;
234
235 /**
236 * The sort algorithm to use.
237 */
238 protected transient Comparator<Book> comparator;
239
240 /**
241 * The log stream
242 */
243 private static final Logger log = Logger.getLogger(BooksListModel.class);
244
245 /**
246 * Serialization ID
247 */
248 private static final long serialVersionUID = 3257568408165036595L;
249 }
250