1#include "SkWidgetViews.h"
2
3#include "SkAnimator.h"
4#include "SkScrollBarView.h"
5
6extern void init_skin_anim(const char name[], SkAnimator*);
7
8struct SkListView::BindingRec {
9	SkString	fSlotName;
10	int			fFieldIndex;
11};
12
13SkListView::SkListView()
14{
15	fSource = NULL;				// our list-source
16	fScrollBar = NULL;
17	fAnims = NULL;				// array of animators[fVisibleRowCount]
18	fBindings = NULL;			// our fields->slot array
19	fBindingCount = 0;			// number of entries in fSlots array
20	fScrollIndex = 0;			// number of cells to skip before first visible cell
21	fCurrIndex = -1;			// index of "selected" cell
22	fVisibleRowCount = 0;		// number of cells that can fit in our bounds
23	fAnimContentDirty = true;	// true if fAnims[] have their correct content
24	fAnimFocusDirty = true;
25
26	fHeights[kNormal_Height] = SkIntToScalar(16);
27	fHeights[kSelected_Height] = SkIntToScalar(16);
28
29	this->setFlags(this->getFlags() | kFocusable_Mask);
30}
31
32SkListView::~SkListView()
33{
34	fScrollBar->safeUnref();
35	fSource->safeUnref();
36	delete[] fAnims;
37	delete[] fBindings;
38}
39
40void SkListView::setHasScrollBar(bool hasSB)
41{
42	if (hasSB != this->hasScrollBar())
43	{
44		if (hasSB)
45		{
46			SkASSERT(fScrollBar == NULL);
47			fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
48			fScrollBar->setVisibleP(true);
49			this->attachChildToFront(fScrollBar);
50			fScrollBar->setHeight(this->height());	// assume it auto-sets its width
51		//	fScrollBar->setLoc(this->getContentWidth(), 0);
52			fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
53		}
54		else
55		{
56			SkASSERT(fScrollBar);
57			fScrollBar->detachFromParent();
58			fScrollBar->unref();
59			fScrollBar = NULL;
60		}
61		this->dirtyCache(kAnimContent_DirtyFlag);
62	}
63}
64
65void SkListView::setSelection(int index)
66{
67	if (fCurrIndex != index)
68	{
69		fAnimFocusDirty = true;
70		this->inval(NULL);
71
72		this->invalSelection();
73		fCurrIndex = index;
74		this->invalSelection();
75		this->ensureSelectionIsVisible();
76	}
77}
78
79bool SkListView::moveSelectionUp()
80{
81	if (fSource)
82	{
83		int	index = fCurrIndex;
84		if (index < 0)	// no selection
85			index = fSource->countRecords() - 1;
86		else
87			index = SkMax32(index - 1, 0);
88
89		if (fCurrIndex != index)
90		{
91			this->setSelection(index);
92			return true;
93		}
94	}
95	return false;
96}
97
98bool SkListView::moveSelectionDown()
99{
100	if (fSource)
101	{
102		int	index = fCurrIndex;
103		if (index < 0)	// no selection
104			index = 0;
105		else
106			index = SkMin32(index + 1, fSource->countRecords() - 1);
107
108		if (fCurrIndex != index)
109		{
110			this->setSelection(index);
111			return true;
112		}
113	}
114	return false;
115}
116
117void SkListView::invalSelection()
118{
119	SkRect	r;
120	if (this->getRowRect(fCurrIndex, &r))
121		this->inval(&r);
122}
123
124void SkListView::ensureSelectionIsVisible()
125{
126	if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
127	{
128		int index = this->logicalToVisualIndex(fCurrIndex);
129
130		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
131		{
132			int newIndex;
133
134			if (index < 0)	// too high
135				newIndex = fCurrIndex;
136			else
137				newIndex = fCurrIndex - fVisibleRowCount + 1;
138			SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
139			this->inval(NULL);
140
141			if (fScrollIndex != newIndex)
142			{
143				fScrollIndex = newIndex;
144				if (fScrollBar)
145					fScrollBar->setStart(newIndex);
146				this->dirtyCache(kAnimContent_DirtyFlag);
147			}
148		}
149	}
150}
151
152SkScalar SkListView::getContentWidth() const
153{
154	SkScalar width = this->width();
155
156	if (fScrollBar)
157	{
158		width -= fScrollBar->width();
159		if (width < 0)
160			width = 0;
161	}
162	return width;
163}
164
165bool SkListView::getRowRect(int index, SkRect* r) const
166{
167	SkASSERT(r);
168
169	index = this->logicalToVisualIndex(index);
170	if (index >= 0)
171	{
172		int	selection = this->logicalToVisualIndex(fCurrIndex);
173
174		SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
175		SkScalar top = index * fHeights[kNormal_Height];
176
177		if (index > selection && selection >= 0)
178			top += fHeights[kSelected_Height] - fHeights[kNormal_Height];
179
180		if (top < this->height())
181		{
182			if (r)
183				r->set(0, top, this->getContentWidth(), top + height);
184			return true;
185		}
186	}
187	return false;
188}
189
190SkListSource* SkListView::setListSource(SkListSource* src)
191{
192	if (fSource != src)
193	{
194		SkRefCnt_SafeAssign(fSource, src);
195		this->ensureSelectionIsVisible();
196		this->inval(NULL);
197
198		if (fScrollBar)
199			fScrollBar->setTotal(fSource->countRecords());
200	}
201	return src;
202}
203
204void SkListView::dirtyCache(unsigned dirtyFlags)
205{
206	if (dirtyFlags & kAnimCount_DirtyFlag)
207	{
208		delete fAnims;
209		fAnims = NULL;
210		fAnimContentDirty = true;
211		fAnimFocusDirty = true;
212	}
213	if (dirtyFlags & kAnimContent_DirtyFlag)
214	{
215		if (!fAnimContentDirty)
216		{
217			this->inval(NULL);
218			fAnimContentDirty = true;
219		}
220		fAnimFocusDirty = true;
221	}
222}
223
224bool SkListView::ensureCache()
225{
226	if (fSkinName.size() == 0)
227		return false;
228
229	if (fAnims == NULL)
230	{
231		int n = SkMax32(1, fVisibleRowCount);
232
233		SkASSERT(fAnimContentDirty);
234		fAnims = new SkAnimator[n];
235		for (int i = 0; i < n; i++)
236		{
237			fAnims[i].setHostEventSink(this);
238			init_skin_anim(fSkinName.c_str(), &fAnims[i]);
239		}
240
241		fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
242		fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
243
244		fAnimFocusDirty = true;
245	}
246
247	if (fAnimContentDirty && fSource)
248	{
249		fAnimContentDirty = false;
250
251		SkString	str;
252		SkEvent		evt("user");
253		evt.setString("id", "setFields");
254		evt.setS32("rowCount", fVisibleRowCount);
255
256		SkEvent	dimEvt("user");
257		dimEvt.setString("id", "setDim");
258		dimEvt.setScalar("dimX", this->getContentWidth());
259		dimEvt.setScalar("dimY", this->height());
260
261		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
262		{
263			evt.setS32("relativeIndex", i - fScrollIndex);
264			for (int j = 0; j < fBindingCount; j++)
265			{
266				fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
267//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
268				evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
269			}
270			(void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
271			(void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
272		}
273		fAnimFocusDirty = true;
274	}
275
276	if (fAnimFocusDirty)
277	{
278//SkDEBUGF(("service fAnimFocusDirty\n"));
279		fAnimFocusDirty = false;
280
281		SkEvent		focusEvt("user");
282		focusEvt.setString("id", "setFocus");
283
284		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
285		{
286			focusEvt.setS32("FOCUS", i == fCurrIndex);
287			(void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
288		}
289	}
290
291	return true;
292}
293
294void SkListView::ensureVisibleRowCount()
295{
296	SkScalar	height = this->height();
297	int			n = 0;
298
299	if (height > 0)
300	{
301		n = 1;
302		height -= fHeights[kSelected_Height];
303		if (height > 0)
304		{
305			SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
306			n += SkScalarFloor(count);
307			if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
308				n += 1;
309
310		//	SkDebugf("count %g, n %d\n", count/65536., n);
311		}
312	}
313
314	if (fVisibleRowCount != n)
315	{
316		if (fScrollBar)
317			fScrollBar->setShown(n);
318
319		fVisibleRowCount = n;
320		this->ensureSelectionIsVisible();
321		this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
322	}
323}
324
325///////////////////////////////////////////////////////////////////////////////////////////////
326
327#include "SkSystemEventTypes.h"
328#include "SkTime.h"
329
330void SkListView::onSizeChange()
331{
332	this->INHERITED::onSizeChange();
333
334	if (fScrollBar)
335		fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
336
337	this->ensureVisibleRowCount();
338}
339
340void SkListView::onDraw(SkCanvas* canvas)
341{
342	this->INHERITED::onDraw(canvas);
343
344	this->ensureVisibleRowCount();
345
346	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
347	if (visibleCount == 0 || !this->ensureCache())
348		return;
349
350//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
351
352	SkAutoCanvasRestore	ar(canvas, true);
353	SkMSec				now = SkTime::GetMSecs();
354	SkRect				bounds;
355
356	bounds.fLeft	= 0;
357	bounds.fRight	= this->getContentWidth();
358	bounds.fBottom	= 0;
359	// assign bounds.fTop inside the loop
360
361	// hack to reveal our bounds for debugging
362	if (this->hasFocus())
363		canvas->drawARGB(0x11, 0, 0, 0xFF);
364	else
365		canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
366
367	for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
368	{
369		SkPaint	 paint;
370		SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
371
372		bounds.fTop = bounds.fBottom;
373		bounds.fBottom += height;
374
375		canvas->save();
376		if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
377			this->inval(&bounds);
378		canvas->restore();
379
380		canvas->translate(0, height);
381	}
382}
383
384bool SkListView::onEvent(const SkEvent& evt)
385{
386	if (evt.isType(SK_EventType_Key))
387	{
388		switch (evt.getFast32()) {
389		case kUp_SkKey:
390			return this->moveSelectionUp();
391		case kDown_SkKey:
392			return this->moveSelectionDown();
393		case kRight_SkKey:
394		case kOK_SkKey:
395			this->postWidgetEvent();
396			return true;
397		default:
398			break;
399		}
400	}
401	return this->INHERITED::onEvent(evt);
402}
403
404///////////////////////////////////////////////////////////////////////////////////////////////
405
406static const char gListViewEventSlot[] = "sk-listview-slot-name";
407
408/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
409{
410	if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
411		fSource->prepareWidgetEvent(evt, fCurrIndex))
412	{
413		evt->setS32(gListViewEventSlot, fCurrIndex);
414		return true;
415	}
416	return false;
417}
418
419int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
420{
421	int32_t	index;
422
423	return evt.findS32(gListViewEventSlot, &index) ? index : -1;
424}
425
426///////////////////////////////////////////////////////////////////////////////////////////////
427
428void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
429{
430	this->INHERITED::onInflate(dom, node);
431
432	{
433		bool hasScrollBar;
434		if (dom.findBool(node, "scrollBar", &hasScrollBar))
435			this->setHasScrollBar(hasScrollBar);
436	}
437
438	const SkDOM::Node*	child;
439
440	if ((child = dom.getFirstChild(node, "bindings")) != NULL)
441	{
442		delete[] fBindings;
443		fBindings = NULL;
444		fBindingCount = 0;
445
446		SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
447		SkASSERT(listSrc);
448		fSkinName.set(dom.findAttr(child, "skin-slots"));
449		SkASSERT(fSkinName.size());
450
451		this->setListSource(listSrc)->unref();
452
453		int count = dom.countChildren(child, "bind");
454		if (count > 0)
455		{
456			fBindings = new BindingRec[count];
457			count = 0;	// reuse this to count up to the number of valid bindings
458
459			child = dom.getFirstChild(child, "bind");
460			SkASSERT(child);
461			do {
462				const char* fieldName = dom.findAttr(child, "field");
463				const char* slotName = dom.findAttr(child, "slot");
464				if (fieldName && slotName)
465				{
466					fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
467					if (fBindings[count].fFieldIndex >= 0)
468						fBindings[count++].fSlotName.set(slotName);
469				}
470			} while ((child = dom.getNextSibling(child, "bind")) != NULL);
471
472			fBindingCount = SkToU16(count);
473			if (count == 0)
474			{
475				SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
476				delete[] fBindings;
477			}
478		}
479		this->dirtyCache(kAnimCount_DirtyFlag);
480		this->setSelection(0);
481	}
482}
483
484/////////////////////////////////////////////////////////////////////////////////////////////
485/////////////////////////////////////////////////////////////////////////////////////////////
486
487class SkXMLListSource : public SkListSource {
488public:
489	SkXMLListSource(const char doc[], size_t len);
490	virtual ~SkXMLListSource()
491	{
492		delete[] fFields;
493		delete[] fRecords;
494	}
495
496	virtual int countFields() { return fFieldCount; }
497	virtual void getFieldName(int index, SkString* field)
498	{
499		SkASSERT((unsigned)index < (unsigned)fFieldCount);
500		if (field)
501			*field = fFields[index];
502	}
503	virtual int findFieldIndex(const char field[])
504	{
505		for (int i = 0; i < fFieldCount; i++)
506			if (fFields[i].equals(field))
507				return i;
508		return -1;
509	}
510
511	virtual int	countRecords() { return fRecordCount; }
512	virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
513	{
514		SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
515		SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
516		if (data)
517			*data = fRecords[rowIndex * fFieldCount + fieldIndex];
518	}
519
520	virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
521	{
522		// hack, for testing right now. Need the xml to tell us what to jam in and where
523		SkString	data;
524
525		this->getRecord(rowIndex, 0, &data);
526		evt->setString("xml-listsource", data.c_str());
527		return true;
528	}
529
530private:
531	SkString*	fFields;	// [fFieldCount]
532	SkString*	fRecords;	// [fRecordCount][fFieldCount]
533	int			fFieldCount, fRecordCount;
534};
535
536#include "SkDOM.h"
537
538SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
539{
540	fFieldCount = fRecordCount = 0;
541	fFields = fRecords = NULL;
542
543	SkDOM	dom;
544
545	const SkDOM::Node* node = dom.build(doc, len);
546	SkASSERT(node);
547	const SkDOM::Node*	child;
548
549	child = dom.getFirstChild(node, "fields");
550	if (child)
551	{
552		fFieldCount = dom.countChildren(child, "field");
553		fFields = new SkString[fFieldCount];
554
555		int n = 0;
556		child = dom.getFirstChild(child, "field");
557		while (child)
558		{
559			fFields[n].set(dom.findAttr(child, "name"));
560			child = dom.getNextSibling(child, "field");
561			n += 1;
562		}
563		SkASSERT(n == fFieldCount);
564	}
565
566	child = dom.getFirstChild(node, "records");
567	if (child)
568	{
569		fRecordCount = dom.countChildren(child, "record");
570		fRecords = new SkString[fRecordCount * fFieldCount];
571
572		int n = 0;
573		child = dom.getFirstChild(child, "record");
574		while (child)
575		{
576			for (int i = 0; i < fFieldCount; i++)
577				fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
578			child = dom.getNextSibling(child, "record");
579			n += 1;
580		}
581		SkASSERT(n == fRecordCount);
582	}
583}
584
585/////////////////////////////////////////////////////////////////////////////////////////////
586
587SkListSource* SkListSource::Factory(const char name[])
588{
589	static const char gDoc[] =
590		"<db name='contacts.db'>"
591			"<fields>"
592				"<field name='name'/>"
593				"<field name='work-num'/>"
594				"<field name='home-num'/>"
595				"<field name='type'/>"
596			"</fields>"
597			"<records>"
598				"<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
599				"<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
600				"<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
601				"<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
602				"<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
603				"<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
604				"<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
605				"<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
606				"<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
607				"<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
608				"<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
609				"<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
610				"<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
611				"<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
612				"<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
613				"<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
614				"<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
615			"</records>"
616		"</db>";
617
618//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
619	return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
620}
621
622
623
624