View Javadoc

1   /***
2    *     Ambient - A music player for the Android platform
3    Copyright (C) 2007 Martin Vysny
4    
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9    
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14  
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package sk.baka.ambient.views.gesturelist;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.List;
24  
25  import sk.baka.ambient.commons.Interval;
26  import android.view.View;
27  
28  /***
29   * <p>
30   * Manages the model for the {@link GesturesListView}.
31   * </p>
32   * <p>
33   * Handles special End Of Playlist item - this item allows us to insert tracks
34   * at the end of the playlist (and into an empty playlist) using keypad
35   * gestures. The special item is shown only when the list view:
36   * </p>
37   * <ul>
38   * <li>is modifiable</li>
39   * <li>has focus (this property however does not alter the return value of the
40   * function, i.e. the function can return <code>true</code> even if the list
41   * view does not have focus)</li>
42   * <li>is not in the {@link View#isInTouchMode() touch mode}</li>
43   * <li>the clipboard is not empty</li>
44   * </ul>
45   * 
46   * @author Martin Vysny
47   */
48  public final class ModelHolder {
49  	/***
50  	 * The owner.
51  	 */
52  	private final GesturesListView owner;
53  
54  	/***
55  	 * Creates new instance.
56  	 * 
57  	 * @param owner
58  	 *            owner listview.
59  	 */
60  	ModelHolder(final GesturesListView owner) {
61  		super();
62  		this.owner = owner;
63  		// We need to remember this adapter -
64  		// we cannot get it simply from the ListView for it will scroll to
65  		// the first item (WTF?).
66  		adapter = new MutableListAdapter(owner, this);
67  	}
68  
69  	/***
70  	 * The backing array of item data. In the MVC terminology, this is a model
71  	 * for all items. This list will never contain the EOP object - this object
72  	 * is factored by the {@link MutableListAdapter}.
73  	 */
74  	private final List<Object> model = new ArrayList<Object>();
75  
76  	/***
77  	 * The backing adapter.
78  	 */
79  	final MutableListAdapter adapter;
80  
81  	/***
82  	 * The model as string representation, converted by invoking
83  	 * {@link IGestureListViewListener#toString(Object)}.
84  	 */
85  	private final ArrayList<String> modelAsString = new ArrayList<String>();
86  
87  	/***
88  	 * <p>
89  	 * Returns a list of items. The backing array of item data. In the MVC
90  	 * terminology, this is a model for all items. If you plan to modify this
91  	 * list, do not forget to call {@link #notifyModified()} after all
92  	 * modifications are made.
93  	 * </p>
94  	 * <p>
95  	 * This model must never contain the
96  	 * {@link MutableListAdapter#EOP_MODEL_MARKER} object!
97  	 * </p>
98  	 * 
99  	 * @return a live list of item data.
100 	 */
101 	public List<Object> getModel() {
102 		return model;
103 	}
104 
105 	/***
106 	 * Returns model as a string representation. Updated automatically when
107 	 * {@link #notifyModified()} is invoked. Unmodifiable.
108 	 * 
109 	 * @return list representation of each item in a model, converted by
110 	 *         invoking {@link IGestureListViewListener#toString(Object)}.
111 	 */
112 	public List<String> getModelAsString() {
113 		return Collections.unmodifiableList(modelAsString);
114 	}
115 	
116 	/***
117 	 * Redraws items and reflects changes made to the {@link #getModel()} list.
118 	 */
119 	public void notifyModified() {
120 		// recompute the string model
121 		modelAsString.clear();
122 		modelAsString.ensureCapacity(model.size());
123 		for (final Object modelItem : model) {
124 			if (modelItem instanceof String) {
125 				modelAsString.add(((String) modelItem).toLowerCase());
126 			} else {
127 				modelAsString.add(owner.listener.toString(modelItem)
128 						.toLowerCase());
129 			}
130 		}
131 		adapter.notifyModified();
132 	}
133 
134 	/***
135 	 * These items are highlighted.
136 	 */
137 	private Interval highlight = Interval.EMPTY;
138 
139 	/***
140 	 * Highlight given items. Does not update the view - you need to call
141 	 * {@link #notifyModified()} to force the redraw.
142 	 * 
143 	 * @param highlight
144 	 *            items to highlight. May be <code>null</code> - equal to an
145 	 *            empty interval.
146 	 */
147 	public void highlight(final Interval highlight) {
148 		Interval newHighlight = highlight == null ? Interval.EMPTY : highlight;
149 		if (newHighlight.end >= model.size()) {
150 			newHighlight = new Interval(newHighlight.start, model.size());
151 		}
152 		if (newHighlight.equals(this.highlight)) {
153 			return;
154 		}
155 		this.highlight = newHighlight;
156 		owner.listener.highlightChanged(this.highlight);
157 	}
158 
159 	/***
160 	 * Returns current highlight.
161 	 * 
162 	 * @return current highlight, never <code>null</code>.
163 	 */
164 	public Interval getHighlight() {
165 		return highlight;
166 	}
167 
168 	/***
169 	 * Returns current highlight. If no items are highlighted, optionally return
170 	 * current selection.
171 	 * 
172 	 * @param returnSelectionOnEmptyHighlight
173 	 *            if <code>true</code> and no items are currently being
174 	 *            highlighted, return current selection.
175 	 * 
176 	 * @return current highlight, never <code>null</code>.
177 	 */
178 	public Interval getHighlight(final boolean returnSelectionOnEmptyHighlight) {
179 		if (highlight.isEmpty() && returnSelectionOnEmptyHighlight) {
180 			final int sel = owner.getSelectedItemPosition();
181 			return (sel < 0) || (sel >= model.size()) ? Interval.EMPTY
182 					: Interval.fromItem(sel);
183 		}
184 		return getHighlight();
185 	}
186 
187 	/***
188 	 * Returns current highlight. If no items are highlighted, optionally return
189 	 * given item.
190 	 * 
191 	 * @param returnSelectionOnEmptyHighlight
192 	 *            if not negative and no items are currently being highlighted,
193 	 *            return selection consisting of a single item.
194 	 * 
195 	 * @return current highlight, never <code>null</code>.
196 	 */
197 	public Interval getHighlight(final int returnSelectionOnEmptyHighlight) {
198 		if (highlight.isEmpty() && (returnSelectionOnEmptyHighlight >= 0)
199 				&& (returnSelectionOnEmptyHighlight < model.size())) {
200 			return Interval.fromItem(returnSelectionOnEmptyHighlight);
201 		}
202 		return getHighlight();
203 	}
204 
205 	/***
206 	 * Returns interval containing all items.
207 	 * 
208 	 * @return non-<code>null</code> interval.
209 	 */
210 	public Interval getAllItems() {
211 		return new Interval(0, model.size());
212 	}
213 }