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
64
65
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
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 }