aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/views
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
commit8a1c16ff38322f0210116fa7293eb8817c7e477e (patch)
treefe40e07f6c8983318a2f79032b9a706ede1090c1 /src/views
parent2559c629078f738ac37095d896d580b681ac6a30 (diff)
grab from latest android
git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/views')
-rw-r--r--src/views/SkBGViewArtist.cpp24
-rw-r--r--src/views/SkBorderView.cpp87
-rw-r--r--src/views/SkEvent.cpp565
-rw-r--r--src/views/SkEventSink.cpp345
-rw-r--r--src/views/SkImageView.cpp296
-rw-r--r--src/views/SkListView.cpp895
-rw-r--r--src/views/SkListWidget.cpp623
-rw-r--r--src/views/SkMetaData.cpp405
-rw-r--r--src/views/SkOSFile.cpp223
-rw-r--r--src/views/SkOSMenu.cpp53
-rw-r--r--src/views/SkOSSound.cpp343
-rw-r--r--src/views/SkParsePaint.cpp103
-rw-r--r--src/views/SkProgressBarView.cpp102
-rw-r--r--src/views/SkProgressView.cpp125
-rw-r--r--src/views/SkScrollBarView.cpp139
-rw-r--r--src/views/SkStackViewLayout.cpp262
-rw-r--r--src/views/SkTagList.cpp71
-rw-r--r--src/views/SkTagList.h51
-rw-r--r--src/views/SkTextBox.cpp216
-rw-r--r--src/views/SkView.cpp760
-rw-r--r--src/views/SkViewInflate.cpp139
-rw-r--r--src/views/SkViewPriv.cpp97
-rw-r--r--src/views/SkViewPriv.h38
-rw-r--r--src/views/SkWidget.cpp323
-rw-r--r--src/views/SkWidgetViews.cpp570
-rw-r--r--src/views/SkWidgets.cpp554
-rw-r--r--src/views/SkWindow.cpp367
27 files changed, 7776 insertions, 0 deletions
diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp
new file mode 100644
index 0000000000..07da123284
--- /dev/null
+++ b/src/views/SkBGViewArtist.cpp
@@ -0,0 +1,24 @@
+#include "SkBGViewArtist.h"
+#include "SkCanvas.h"
+#include "SkParsePaint.h"
+
+SkBGViewArtist::SkBGViewArtist(SkColor c)
+{
+ fPaint.setColor(c);
+}
+
+SkBGViewArtist::~SkBGViewArtist()
+{
+}
+
+void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas)
+{
+ // only works for views that are clipped their bounds.
+ canvas->drawPaint(fPaint);
+}
+
+void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp
new file mode 100644
index 0000000000..3356782326
--- /dev/null
+++ b/src/views/SkBorderView.cpp
@@ -0,0 +1,87 @@
+#include "SkBorderView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+#include "SkStackViewLayout.h"
+
+SkBorderView::SkBorderView() : fTop(SkIntToScalar(0)), fLeft(SkIntToScalar(0)),
+ fRight(SkIntToScalar(0)), fBottom(SkIntToScalar(0))
+{
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kBorder_SkinEnum, &fAnim);
+}
+
+SkBorderView::~SkBorderView()
+{
+
+}
+
+void SkBorderView::setSkin(const char skin[])
+{
+ init_skin_anim(skin, &fAnim);
+}
+
+/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+}
+
+/*virtual*/ void SkBorderView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(nil);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ evt.findScalar("leftMargin", &fLeft);
+ evt.findScalar("rightMargin", &fRight);
+ evt.findScalar("topMargin", &fTop);
+ evt.findScalar("bottomMargin", &fBottom);
+
+ //setup_views.cpp uses SkView::Layout instead of SkStackViewLayout
+ //but that gives me an error
+ SkStackViewLayout* layout;
+ fMargin.set(fLeft, fTop, fRight, fBottom);
+ if (this->getLayout())
+ {
+ layout = (SkStackViewLayout*)this->getLayout();
+ layout->setMargin(fMargin);
+ }
+ else
+ {
+ layout = new SkStackViewLayout;
+ layout->setMargin(fMargin);
+ this->setLayout(layout)->unref();
+ }
+ this->invokeLayout();
+ }
+ return this->INHERITED::onEvent(evt);
+}
diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp
new file mode 100644
index 0000000000..67549b48b5
--- /dev/null
+++ b/src/views/SkEvent.cpp
@@ -0,0 +1,565 @@
+/* libs/graphics/views/SkEvent.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkEvent.h"
+
+void SkEvent::initialize(const char* type, size_t typeLen) {
+ fType = NULL;
+ setType(type, typeLen);
+ f32 = 0;
+#ifdef SK_DEBUG
+ fTargetID = 0;
+ fTime = 0;
+ fNextEvent = NULL;
+#endif
+ SkDEBUGCODE(fDebugTrace = false;)
+}
+
+SkEvent::SkEvent()
+{
+ initialize("", 0);
+}
+
+SkEvent::SkEvent(const SkEvent& src)
+{
+ *this = src;
+ if (((size_t) fType & 1) == 0)
+ setType(src.fType);
+}
+
+SkEvent::SkEvent(const SkString& type)
+{
+ initialize(type.c_str(), type.size());
+}
+
+SkEvent::SkEvent(const char type[])
+{
+ SkASSERT(type);
+ initialize(type, strlen(type));
+}
+
+SkEvent::~SkEvent()
+{
+ if (((size_t) fType & 1) == 0)
+ sk_free((void*) fType);
+}
+
+static size_t makeCharArray(char* buffer, size_t compact)
+{
+ size_t bits = (size_t) compact >> 1;
+ memcpy(buffer, &bits, sizeof(compact));
+ buffer[sizeof(compact)] = 0;
+ return strlen(buffer);
+}
+
+#if 0
+const char* SkEvent::getType() const
+{
+ if ((size_t) fType & 1) { // not a pointer
+ char chars[sizeof(size_t) + 1];
+ size_t len = makeCharArray(chars, (size_t) fType);
+ fType = (char*) sk_malloc_throw(len);
+ SkASSERT(((size_t) fType & 1) == 0);
+ memcpy(fType, chars, len);
+ }
+ return fType;
+}
+#endif
+
+void SkEvent::getType(SkString* str) const
+{
+ if (str)
+ {
+ if ((size_t) fType & 1) // not a pointer
+ {
+ char chars[sizeof(size_t) + 1];
+ size_t len = makeCharArray(chars, (size_t) fType);
+ str->set(chars, len);
+ }
+ else
+ str->set(fType);
+ }
+}
+
+bool SkEvent::isType(const SkString& str) const
+{
+ return this->isType(str.c_str(), str.size());
+}
+
+bool SkEvent::isType(const char type[], size_t typeLen) const
+{
+ if (typeLen == 0)
+ typeLen = strlen(type);
+ if ((size_t) fType & 1) { // not a pointer
+ char chars[sizeof(size_t) + 1];
+ size_t len = makeCharArray(chars, (size_t) fType);
+ return len == typeLen && strncmp(chars, type, typeLen) == 0;
+ }
+ return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0;
+}
+
+void SkEvent::setType(const char type[], size_t typeLen)
+{
+ if (typeLen == 0)
+ typeLen = strlen(type);
+ if (typeLen <= sizeof(fType)) {
+ size_t slot = 0;
+ memcpy(&slot, type, typeLen);
+ if (slot << 1 >> 1 != slot)
+ goto useCharStar;
+ slot <<= 1;
+ slot |= 1;
+ fType = (char*) slot;
+ } else {
+useCharStar:
+ fType = (char*) sk_malloc_throw(typeLen + 1);
+ SkASSERT(((size_t) fType & 1) == 0);
+ memcpy(fType, type, typeLen);
+ fType[typeLen] = 0;
+ }
+}
+
+void SkEvent::setType(const SkString& type)
+{
+ setType(type.c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#include "SkParse.h"
+
+void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ const char* name = dom.findAttr(node, "type");
+ if (name)
+ this->setType(name);
+
+ const char* value;
+ if ((value = dom.findAttr(node, "fast32")) != NULL)
+ {
+ int32_t n;
+ if (SkParse::FindS32(value, &n))
+ this->setFast32(n);
+ }
+
+ for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node))
+ {
+ if (strcmp(dom.getName(node), "data"))
+ {
+ SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));)
+ continue;
+ }
+
+ name = dom.findAttr(node, "name");
+ if (name == NULL)
+ {
+ SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");)
+ continue;
+ }
+
+ if ((value = dom.findAttr(node, "s32")) != NULL)
+ {
+ int32_t n;
+ if (SkParse::FindS32(value, &n))
+ this->setS32(name, n);
+ }
+ else if ((value = dom.findAttr(node, "scalar")) != NULL)
+ {
+ SkScalar x;
+ if (SkParse::FindScalar(value, &x))
+ this->setScalar(name, x);
+ }
+ else if ((value = dom.findAttr(node, "string")) != NULL)
+ this->setString(name, value);
+#ifdef SK_DEBUG
+ else
+ {
+ SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name);
+ }
+#endif
+ }
+}
+
+#ifdef SK_DEBUG
+
+ #ifndef SkScalarToFloat
+ #define SkScalarToFloat(x) ((x) / 65536.f)
+ #endif
+
+ void SkEvent::dump(const char title[])
+ {
+ if (title)
+ SkDebugf("%s ", title);
+
+ SkString etype;
+ this->getType(&etype);
+ SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32());
+
+ const SkMetaData& md = this->getMetaData();
+ SkMetaData::Iter iter(md);
+ SkMetaData::Type mtype;
+ int count;
+ const char* name;
+
+ while ((name = iter.next(&mtype, &count)) != NULL)
+ {
+ SkASSERT(count > 0);
+
+ SkDebugf(" <%s>=", name);
+ switch (mtype) {
+ case SkMetaData::kS32_Type: // vector version???
+ {
+ int32_t value;
+ md.findS32(name, &value);
+ SkDebugf("%d ", value);
+ }
+ break;
+ case SkMetaData::kScalar_Type:
+ {
+ const SkScalar* values = md.findScalars(name, &count, NULL);
+ SkDebugf("%f", SkScalarToFloat(values[0]));
+ for (int i = 1; i < count; i++)
+ SkDebugf(", %f", SkScalarToFloat(values[i]));
+ SkDebugf(" ");
+ }
+ break;
+ case SkMetaData::kString_Type:
+ {
+ const char* value = md.findString(name);
+ SkASSERT(value);
+ SkDebugf("<%s> ", value);
+ }
+ break;
+ case SkMetaData::kPtr_Type: // vector version???
+ {
+ void* value;
+ md.findPtr(name, &value);
+ SkDebugf("%p ", value);
+ }
+ break;
+ case SkMetaData::kBool_Type: // vector version???
+ {
+ bool value;
+ md.findBool(name, &value);
+ SkDebugf("%s ", value ? "true" : "false");
+ }
+ break;
+ default:
+ SkASSERT(!"unknown metadata type returned from iterator");
+ break;
+ }
+ }
+ SkDebugf("\n");
+ }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+// #define SK_TRACE_EVENTSx
+#endif
+
+#ifdef SK_TRACE_EVENTS
+ static void event_log(const char s[])
+ {
+ SkDEBUGF(("%s\n", s));
+ }
+
+ #define EVENT_LOG(s) event_log(s)
+ #define EVENT_LOGN(s, n) do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0)
+#else
+ #define EVENT_LOG(s)
+ #define EVENT_LOGN(s, n)
+#endif
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+#include "SkTime.h"
+
+#define SK_Event_GlobalsTag SkSetFourByteTag('e', 'v', 'n', 't')
+
+class SkEvent_Globals : public SkGlobals::Rec {
+public:
+ SkMutex fEventMutex;
+ SkEvent* fEventQHead, *fEventQTail;
+ SkEvent* fDelayQHead;
+ SkDEBUGCODE(int fEventCounter;)
+};
+
+static SkGlobals::Rec* create_globals()
+{
+ SkEvent_Globals* rec = new SkEvent_Globals;
+ rec->fEventQHead = NULL;
+ rec->fEventQTail = NULL;
+ rec->fDelayQHead = NULL;
+ SkDEBUGCODE(rec->fEventCounter = 0;)
+ return rec;
+}
+
+bool SkEvent::Post(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay)
+{
+ if (delay)
+ return SkEvent::PostTime(evt, sinkID, SkTime::GetMSecs() + delay);
+
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ evt->fTargetID = sinkID;
+
+#ifdef SK_TRACE_EVENTS
+ {
+ SkString str("SkEvent::Post(");
+ str.append(evt->getType());
+ str.append(", 0x");
+ str.appendHex(sinkID);
+ str.append(", ");
+ str.appendS32(delay);
+ str.append(")");
+ event_log(str.c_str());
+ }
+#endif
+
+ globals.fEventMutex.acquire();
+ bool wasEmpty = SkEvent::Enqueue(evt);
+ globals.fEventMutex.release();
+
+ // call outside of us holding the mutex
+ if (wasEmpty)
+ SkEvent::SignalNonEmptyQueue();
+ return true;
+}
+
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS)
+SkMSec gMaxDrawTime;
+#endif
+
+bool SkEvent::PostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time)
+{
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS)
+ gMaxDrawTime = time;
+#endif
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ evt->fTargetID = sinkID;
+
+#ifdef SK_TRACE_EVENTS
+ {
+ SkString str("SkEvent::Post(");
+ str.append(evt->getType());
+ str.append(", 0x");
+ str.appendHex(sinkID);
+ str.append(", ");
+ str.appendS32(time);
+ str.append(")");
+ event_log(str.c_str());
+ }
+#endif
+
+ globals.fEventMutex.acquire();
+ SkMSec queueDelay = SkEvent::EnqueueTime(evt, time);
+ globals.fEventMutex.release();
+
+ // call outside of us holding the mutex
+ if ((int32_t)queueDelay != ~0)
+ SkEvent::SignalQueueTimer(queueDelay);
+ return true;
+}
+
+bool SkEvent::Enqueue(SkEvent* evt)
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ // gEventMutex acquired by caller
+
+ SkASSERT(evt);
+
+ bool wasEmpty = globals.fEventQHead == NULL;
+
+ if (globals.fEventQTail)
+ globals.fEventQTail->fNextEvent = evt;
+ globals.fEventQTail = evt;
+ if (globals.fEventQHead == NULL)
+ globals.fEventQHead = evt;
+ evt->fNextEvent = NULL;
+
+ SkDEBUGCODE(++globals.fEventCounter);
+// SkDebugf("Enqueue: count=%d\n", gEventCounter);
+
+ return wasEmpty;
+}
+
+SkEvent* SkEvent::Dequeue(SkEventSinkID* sinkID)
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ globals.fEventMutex.acquire();
+
+ SkEvent* evt = globals.fEventQHead;
+ if (evt)
+ {
+ SkDEBUGCODE(--globals.fEventCounter);
+
+ if (sinkID)
+ *sinkID = evt->fTargetID;
+
+ globals.fEventQHead = evt->fNextEvent;
+ if (globals.fEventQHead == NULL)
+ globals.fEventQTail = NULL;
+ }
+ globals.fEventMutex.release();
+
+// SkDebugf("Dequeue: count=%d\n", gEventCounter);
+
+ return evt;
+}
+
+bool SkEvent::QHasEvents()
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ // this is not thread accurate, need a semaphore for that
+ return globals.fEventQHead != NULL;
+}
+
+#ifdef SK_TRACE_EVENTS
+ static int gDelayDepth;
+#endif
+
+SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time)
+{
+#ifdef SK_TRACE_EVENTS
+ SkDebugf("enqueue-delay %s %d (%d)", evt->getType(), time, gDelayDepth);
+ const char* idStr = evt->findString("id");
+ if (idStr)
+ SkDebugf(" (%s)", idStr);
+ SkDebugf("\n");
+ ++gDelayDepth;
+#endif
+
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ // gEventMutex acquired by caller
+
+ SkEvent* curr = globals.fDelayQHead;
+ SkEvent* prev = NULL;
+
+ while (curr)
+ {
+ if (SkMSec_LT(time, curr->fTime))
+ break;
+ prev = curr;
+ curr = curr->fNextEvent;
+ }
+
+ evt->fTime = time;
+ evt->fNextEvent = curr;
+ if (prev == NULL)
+ globals.fDelayQHead = evt;
+ else
+ prev->fNextEvent = evt;
+
+ SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs();
+ if ((int32_t)delay <= 0)
+ delay = 1;
+ return delay;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkEventSink.h"
+
+bool SkEvent::ProcessEvent()
+{
+ SkEventSinkID sinkID;
+ SkEvent* evt = SkEvent::Dequeue(&sinkID);
+ SkAutoTDelete<SkEvent> autoDelete(evt);
+ bool again = false;
+
+ EVENT_LOGN("ProcessEvent", (int32_t)evt);
+
+ if (evt)
+ {
+ (void)SkEventSink::DoEvent(*evt, sinkID);
+ again = SkEvent::QHasEvents();
+ }
+ return again;
+}
+
+void SkEvent::ServiceQueueTimer()
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ globals.fEventMutex.acquire();
+
+ bool wasEmpty = false;
+ SkMSec now = SkTime::GetMSecs();
+ SkEvent* evt = globals.fDelayQHead;
+
+ while (evt)
+ {
+ if (SkMSec_LT(now, evt->fTime))
+ break;
+
+#ifdef SK_TRACE_EVENTS
+ --gDelayDepth;
+ SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth);
+ const char* idStr = evt->findString("id");
+ if (idStr)
+ SkDebugf(" (%s)", idStr);
+ SkDebugf("\n");
+#endif
+
+ SkEvent* next = evt->fNextEvent;
+ if (SkEvent::Enqueue(evt))
+ wasEmpty = true;
+ evt = next;
+ }
+ globals.fDelayQHead = evt;
+
+ SkMSec time = evt ? evt->fTime - now : 0;
+
+ globals.fEventMutex.release();
+
+ if (wasEmpty)
+ SkEvent::SignalNonEmptyQueue();
+
+ SkEvent::SignalQueueTimer(time);
+}
+
+////////////////////////////////////////////////////////////////
+
+void SkEvent::Init()
+{
+}
+
+void SkEvent::Term()
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ SkEvent* evt = globals.fEventQHead;
+ while (evt)
+ {
+ SkEvent* next = evt->fNextEvent;
+ delete evt;
+ evt = next;
+ }
+
+ evt = globals.fDelayQHead;
+ while (evt)
+ {
+ SkEvent* next = evt->fNextEvent;
+ delete evt;
+ evt = next;
+ }
+}
+
diff --git a/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp
new file mode 100644
index 0000000000..c8fe35ca18
--- /dev/null
+++ b/src/views/SkEventSink.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/views/SkEventSink.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkEventSink.h"
+#include "SkTagList.h"
+#include "SkThread.h"
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+#include "SkTime.h"
+
+#define SK_EventSink_GlobalsTag SkSetFourByteTag('e', 'v', 's', 'k')
+
+class SkEventSink_Globals : public SkGlobals::Rec {
+public:
+ SkMutex fSinkMutex;
+ SkEventSinkID fNextSinkID;
+ SkEventSink* fSinkHead;
+};
+
+static SkGlobals::Rec* create_globals()
+{
+ SkEventSink_Globals* rec = new SkEventSink_Globals;
+ rec->fNextSinkID = 0;
+ rec->fSinkHead = NULL;
+ return rec;
+}
+
+SkEventSink::SkEventSink() : fTagHead(NULL)
+{
+ SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+
+ globals.fSinkMutex.acquire();
+
+ fID = ++globals.fNextSinkID;
+ fNextSink = globals.fSinkHead;
+ globals.fSinkHead = this;
+
+ globals.fSinkMutex.release();
+}
+
+SkEventSink::~SkEventSink()
+{
+ SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+
+ if (fTagHead)
+ SkTagList::DeleteAll(fTagHead);
+
+ globals.fSinkMutex.acquire();
+
+ SkEventSink* sink = globals.fSinkHead;
+ SkEventSink* prev = NULL;
+
+ for (;;)
+ {
+ SkEventSink* next = sink->fNextSink;
+ if (sink == this)
+ {
+ if (prev)
+ prev->fNextSink = next;
+ else
+ globals.fSinkHead = next;
+ break;
+ }
+ prev = sink;
+ sink = next;
+ }
+ globals.fSinkMutex.release();
+}
+
+bool SkEventSink::doEvent(const SkEvent& evt)
+{
+ return this->onEvent(evt);
+}
+
+bool SkEventSink::doQuery(SkEvent* evt)
+{
+ SkASSERT(evt);
+ return this->onQuery(evt);
+}
+
+bool SkEventSink::onEvent(const SkEvent&)
+{
+ return false;
+}
+
+bool SkEventSink::onQuery(SkEvent*)
+{
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTagList* SkEventSink::findTagList(U8CPU tag) const
+{
+ return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL;
+}
+
+void SkEventSink::addTagList(SkTagList* rec)
+{
+ SkASSERT(rec);
+ SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL);
+
+ rec->fNext = fTagHead;
+ fTagHead = rec;
+}
+
+void SkEventSink::removeTagList(U8CPU tag)
+{
+ if (fTagHead)
+ SkTagList::DeleteTag(&fTagHead, tag);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkListenersTagList : SkTagList {
+ SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList)
+ {
+ fExtra16 = SkToU16(count);
+ fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID));
+ }
+ virtual ~SkListenersTagList()
+ {
+ sk_free(fIDs);
+ }
+
+ int countListners() const { return fExtra16; }
+
+ int find(SkEventSinkID id) const
+ {
+ const SkEventSinkID* idptr = fIDs;
+ for (int i = fExtra16 - 1; i >= 0; --i)
+ if (idptr[i] == id)
+ return i;
+ return -1;
+ }
+
+ SkEventSinkID* fIDs;
+};
+
+void SkEventSink::addListenerID(SkEventSinkID id)
+{
+ if (id == 0)
+ return;
+
+ SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+ int count = 0;
+
+ if (prev)
+ {
+ if (prev->find(id) >= 0)
+ return;
+ count = prev->countListners();
+ }
+
+ SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1));
+
+ if (prev)
+ {
+ memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID));
+ this->removeTagList(kListeners_SkTagList);
+ }
+ next->fIDs[count] = id;
+ this->addTagList(next);
+}
+
+void SkEventSink::copyListeners(const SkEventSink& sink)
+{
+ SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList);
+ if (sinkList == NULL)
+ return;
+ SkASSERT(sinkList->countListners() > 0);
+ const SkEventSinkID* iter = sinkList->fIDs;
+ const SkEventSinkID* stop = iter + sinkList->countListners();
+ while (iter < stop)
+ addListenerID(*iter++);
+}
+
+void SkEventSink::removeListenerID(SkEventSinkID id)
+{
+ if (id == 0)
+ return;
+
+ SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+
+ if (list == NULL)
+ return;
+
+ int index = list->find(id);
+ if (index >= 0)
+ {
+ int count = list->countListners();
+ SkASSERT(count > 0);
+ if (count == 1)
+ this->removeTagList(kListeners_SkTagList);
+ else
+ {
+ // overwrite without resize/reallocating our struct (for speed)
+ list->fIDs[index] = list->fIDs[count - 1];
+ list->fExtra16 = SkToU16(count - 1);
+ }
+ }
+}
+
+bool SkEventSink::hasListeners() const
+{
+ return this->findTagList(kListeners_SkTagList) != NULL;
+}
+
+void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay)
+{
+ SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+ if (list)
+ {
+ SkASSERT(list->countListners() > 0);
+ const SkEventSinkID* iter = list->fIDs;
+ const SkEventSinkID* stop = iter + list->countListners();
+ while (iter < stop)
+ (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID)
+{
+ SkEventSink* sink = SkEventSink::FindSink(sinkID);
+
+ if (sink)
+ {
+#ifdef SK_DEBUG
+ if (evt.isDebugTrace())
+ {
+ SkString etype;
+ evt.getType(&etype);
+ SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID);
+ const char* idStr = evt.findString("id");
+ if (idStr)
+ SkDebugf(" (%s)", idStr);
+ SkDebugf("\n");
+ }
+#endif
+ return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult;
+ }
+ else
+ {
+#ifdef SK_DEBUG
+ if (sinkID)
+ SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID);
+ else
+ SkDebugf("Event sent to 0 sinkID\n");
+
+ if (evt.isDebugTrace())
+ {
+ SkString etype;
+ evt.getType(&etype);
+ SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID);
+ }
+#endif
+ return kSinkNotFound_EventResult;
+ }
+}
+
+SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID)
+{
+ if (sinkID == 0)
+ return 0;
+
+ SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+ SkAutoMutexAcquire ac(globals.fSinkMutex);
+ SkEventSink* sink = globals.fSinkHead;
+
+ while (sink)
+ {
+ if (sink->getSinkID() == sinkID)
+ return sink;
+ sink = sink->fNextSink;
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0 // experimental, not tested
+
+#include "SkThread.h"
+#include "SkTDict.h"
+
+#define kMinStringBufferSize 128
+static SkMutex gNamedSinkMutex;
+static SkTDict<SkEventSinkID> gNamedSinkIDs(kMinStringBufferSize);
+
+/** Register a name/id pair with the system. If the name already exists,
+ replace its ID with the new id. This pair will persist until UnregisterNamedSink()
+ is called.
+*/
+void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id)
+{
+ if (id && name && *name)
+ {
+ SkAutoMutexAcquire ac(gNamedSinkMutex);
+ gNamedSinkIDs.set(name, id);
+ }
+}
+
+/** Return the id that matches the specified name (from a previous call to
+ RegisterNamedSinkID(). If no match is found, return 0
+*/
+SkEventSinkID SkEventSink::FindNamedSinkID(const char name[])
+{
+ SkEventSinkID id = 0;
+
+ if (name && *name)
+ {
+ SkAutoMutexAcquire ac(gNamedSinkMutex);
+ (void)gNamedSinkIDs.find(name, &id);
+ }
+ return id;
+}
+
+/** Remove all name/id pairs from the system. This is call internally
+ on shutdown, to ensure no memory leaks. It should not be called
+ before shutdown.
+*/
+void SkEventSink::RemoveAllNamedSinkIDs()
+{
+ SkAutoMutexAcquire ac(gNamedSinkMutex);
+ (void)gNamedSinkIDs.reset();
+}
+#endif
diff --git a/src/views/SkImageView.cpp b/src/views/SkImageView.cpp
new file mode 100644
index 0000000000..f19e913789
--- /dev/null
+++ b/src/views/SkImageView.cpp
@@ -0,0 +1,296 @@
+#include "SkImageView.h"
+#include "SkAnimator.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkMatrix.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+SkImageView::SkImageView()
+{
+ fMatrix = nil;
+ fScaleType = kMatrix_ScaleType;
+
+ fData.fAnim = nil; // handles initializing the other union values
+ fDataIsAnim = true;
+
+ fUriIsValid = false; // an empty string is not valid
+}
+
+SkImageView::~SkImageView()
+{
+ if (fMatrix)
+ sk_free(fMatrix);
+
+ this->freeData();
+}
+
+void SkImageView::getUri(SkString* uri) const
+{
+ if (uri)
+ *uri = fUri;
+}
+
+void SkImageView::setUri(const char uri[])
+{
+ if (!fUri.equals(uri))
+ {
+ fUri.set(uri);
+ this->onUriChange();
+ }
+}
+
+void SkImageView::setUri(const SkString& uri)
+{
+ if (fUri != uri)
+ {
+ fUri = uri;
+ this->onUriChange();
+ }
+}
+
+void SkImageView::setScaleType(ScaleType st)
+{
+ SkASSERT((unsigned)st <= kFitEnd_ScaleType);
+
+ if ((ScaleType)fScaleType != st)
+ {
+ fScaleType = SkToU8(st);
+ if (fUriIsValid)
+ this->inval(nil);
+ }
+}
+
+bool SkImageView::getImageMatrix(SkMatrix* matrix) const
+{
+ if (fMatrix)
+ {
+ SkASSERT(!fMatrix->isIdentity());
+ if (matrix)
+ *matrix = *fMatrix;
+ return true;
+ }
+ else
+ {
+ if (matrix)
+ matrix->reset();
+ return false;
+ }
+}
+
+void SkImageView::setImageMatrix(const SkMatrix* matrix)
+{
+ bool changed = false;
+
+ if (matrix && !matrix->isIdentity())
+ {
+ if (fMatrix == nil)
+ fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+ *fMatrix = *matrix;
+ changed = true;
+ }
+ else // set us to identity
+ {
+ if (fMatrix)
+ {
+ SkASSERT(!fMatrix->isIdentity());
+ sk_free(fMatrix);
+ fMatrix = nil;
+ changed = true;
+ }
+ }
+
+ // only redraw if we changed our matrix and we're not in scaleToFit mode
+ if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
+ this->inval(nil);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImageView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ if (fUriIsValid)
+ this->inval(nil);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
+{
+ SkASSERT(st != SkImageView::kMatrix_ScaleType);
+ SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
+
+ SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
+ SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
+ SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
+ SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
+
+ return (SkMatrix::ScaleToFit)(st - 1);
+}
+
+void SkImageView::onDraw(SkCanvas* canvas)
+{
+ SkRect src;
+ if (!this->getDataBounds(&src))
+ {
+ SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
+ return; // nothing to draw
+ }
+
+ SkAutoCanvasRestore restore(canvas, true);
+ SkMatrix matrix;
+
+ if (this->getScaleType() == kMatrix_ScaleType)
+ (void)this->getImageMatrix(&matrix);
+ else
+ {
+ SkRect dst;
+ dst.set(0, 0, this->width(), this->height());
+ matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
+ }
+ canvas->concat(matrix);
+
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+
+ if (fDataIsAnim)
+ {
+ SkMSec now = SkTime::GetMSecs();
+
+ SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
+
+SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fData.fAnim->getInvalBounds(&bounds);
+ matrix.mapRect(&bounds); // get the bounds into view coordinates
+ this->inval(&bounds);
+ }
+ }
+ else
+ canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
+}
+
+void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* src = dom.findAttr(node, "src");
+ if (src)
+ this->setUri(src);
+
+ int index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
+ if (index >= 0)
+ this->setScaleType((ScaleType)index);
+
+ // need inflate syntax/reader for matrix
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkImageView::onUriChange()
+{
+ if (this->freeData())
+ this->inval(nil);
+ fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri
+}
+
+bool SkImageView::freeData()
+{
+ if (fData.fAnim) // test is valid for all union values
+ {
+ if (fDataIsAnim)
+ delete fData.fAnim;
+ else
+ delete fData.fBitmap;
+
+ fData.fAnim = nil; // valid for all union values
+ return true;
+ }
+ return false;
+}
+
+bool SkImageView::getDataBounds(SkRect* bounds)
+{
+ SkASSERT(bounds);
+
+ if (this->ensureUriIsLoaded())
+ {
+ SkScalar width, height;
+
+ if (fDataIsAnim)
+ {
+ if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
+ SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
+ {
+ // cons up fake bounds
+ width = this->width();
+ height = this->height();
+ }
+ }
+ else
+ {
+ width = SkIntToScalar(fData.fBitmap->width());
+ height = SkIntToScalar(fData.fBitmap->height());
+ }
+ bounds->set(0, 0, width, height);
+ return true;
+ }
+ return false;
+}
+
+bool SkImageView::ensureUriIsLoaded()
+{
+ if (fData.fAnim) // test is valid for all union values
+ {
+ SkASSERT(fUriIsValid);
+ return true;
+ }
+ if (!fUriIsValid)
+ return false;
+
+ // try to load the url
+ if (fUri.endsWith(".xml")) // assume it is screenplay
+ {
+ SkAnimator* anim = new SkAnimator;
+
+ if (!anim->decodeURI(fUri.c_str()))
+ {
+ delete anim;
+ fUriIsValid = false;
+ return false;
+ }
+ anim->setHostEventSink(this);
+
+ fData.fAnim = anim;
+ fDataIsAnim = true;
+ }
+ else // assume it is an image format
+ {
+ #if 0
+ SkBitmap* bitmap = new SkBitmap;
+
+ if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
+ {
+ delete bitmap;
+ fUriIsValid = false;
+ return false;
+ }
+ fData.fBitmap = bitmap;
+ fDataIsAnim = false;
+ #else
+ return false;
+ #endif
+ }
+ return true;
+}
+
diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp
new file mode 100644
index 0000000000..27218a9d67
--- /dev/null
+++ b/src/views/SkListView.cpp
@@ -0,0 +1,895 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+
+#if 0
+
+SkEvent* SkListSource::getEvent(int index)
+{
+ return nil;
+}
+
+#include "SkOSFile.h"
+
+class SkDirListSource : public SkListSource {
+public:
+ SkDirListSource(const char path[], const char suffix[], const char target[])
+ : fPath(path), fSuffix(suffix), fTarget(target)
+ {
+ fCount = -1;
+ }
+ virtual int countRows()
+ {
+ if (fCount < 0)
+ {
+ fCount = 0;
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ while (fIter.next(nil))
+ fCount += 1;
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ fIndex = 0;
+ }
+ return fCount;
+ }
+ virtual void getRow(int index, SkString* left, SkString* right)
+ {
+ (void)this->countRows();
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (fIndex > index)
+ {
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ fIndex = 0;
+ }
+
+ while (fIndex < index)
+ {
+ fIter.next(nil);
+ fIndex += 1;
+ }
+
+ if (fIter.next(left))
+ {
+ if (left)
+ left->remove(left->size() - fSuffix.size(), fSuffix.size());
+ }
+ else
+ {
+ if (left)
+ left->reset();
+ }
+ if (right) // only set to ">" if we know we're on a sub-directory
+ right->reset();
+
+ fIndex += 1;
+ }
+ virtual SkEvent* getEvent(int index)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ SkEvent* evt = new SkEvent();
+ SkString label;
+
+ this->getRow(index, &label, nil);
+ evt->setString("name", label.c_str());
+
+ int c = fPath.c_str()[fPath.size() - 1];
+ if (c != '/' && c != '\\')
+ label.prepend("/");
+ label.prepend(fPath);
+ label.append(fSuffix);
+ evt->setString("path", label.c_str());
+ evt->setS32("index", index);
+ evt->setS32("duration", 22);
+ evt->setType(fTarget);
+ return evt;
+ }
+
+private:
+ SkString fPath, fSuffix;
+ SkString fTarget;
+ SkOSFile::Iter fIter;
+ int fCount;
+ int fIndex;
+};
+
+SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[])
+{
+ return new SkDirListSource(path, suffix, target);
+}
+
+//////////////////////////////////////////////////////////////////
+
+class SkDOMListSource : public SkListSource {
+public:
+ enum Type {
+ kUnknown_Type,
+ kDir_Type,
+ kToggle_Type
+ };
+ struct ItemRec {
+ SkString fLabel;
+ SkString fTail, fAltTail;
+ SkString fTarget;
+ Type fType;
+ };
+
+ SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">")
+ {
+ const SkDOM::Node* child = dom.getFirstChild(node, "item");
+ int count = 0;
+
+ while (child)
+ {
+ count += 1;
+ child = dom.getNextSibling(child, "item");
+ }
+
+ fCount = count;
+ fList = nil;
+ if (count)
+ {
+ ItemRec* rec = fList = new ItemRec[count];
+
+ child = dom.getFirstChild(node, "item");
+ while (child)
+ {
+ rec->fLabel.set(dom.findAttr(child, "label"));
+ rec->fTail.set(dom.findAttr(child, "tail"));
+ rec->fAltTail.set(dom.findAttr(child, "alt-tail"));
+ rec->fTarget.set(dom.findAttr(child, "target"));
+ rec->fType = kUnknown_Type;
+
+ int index = dom.findList(child, "type", "dir,toggle");
+ if (index >= 0)
+ rec->fType = (Type)(index + 1);
+
+ child = dom.getNextSibling(child, "item");
+ rec += 1;
+ }
+ }
+ }
+ virtual ~SkDOMListSource()
+ {
+ delete[] fList;
+ }
+ virtual int countRows()
+ {
+ return fCount;
+ }
+ virtual void getRow(int index, SkString* left, SkString* right)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (left)
+ *left = fList[index].fLabel;
+ if (right)
+ *right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail;
+ }
+ virtual SkEvent* getEvent(int index)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (fList[index].fType == kDir_Type)
+ {
+ SkEvent* evt = new SkEvent();
+ evt->setType(fList[index].fTarget);
+ evt->setFast32(index);
+ return evt;
+ }
+ if (fList[index].fType == kToggle_Type)
+ fList[index].fTail.swap(fList[index].fAltTail);
+
+ return nil;
+ }
+
+private:
+ int fCount;
+ ItemRec* fList;
+ SkString fDirTail;
+};
+
+SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node)
+{
+ return new SkDOMListSource(dom, node);
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkListView::SkListView(U32 flags) : SkWidgetView(flags)
+{
+ fSource = nil;
+ fScrollIndex = 0;
+ fCurrIndex = -1;
+ fRowHeight = SkIntToScalar(16);
+ fVisibleRowCount = 0;
+ fStrCache = nil;
+
+ fPaint[kBG_Attr].setColor(0);
+ fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14));
+ fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14));
+ fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE);
+ fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE);
+}
+
+SkListView::~SkListView()
+{
+ delete[] fStrCache;
+ fSource->safeUnref();
+}
+
+void SkListView::setRowHeight(SkScalar height)
+{
+ SkASSERT(height >= 0);
+
+ if (fRowHeight != height)
+ {
+ fRowHeight = height;
+ this->inval(nil);
+ this->onSizeChange();
+ }
+}
+
+void SkListView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+
+ {
+ SkEvent evt;
+ evt.setType("listview-selection");
+ evt.setFast32(index);
+ this->sendEventToParents(evt);
+ }
+ }
+}
+
+void SkListView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRows() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+ this->setSelection(index);
+ }
+}
+
+void SkListView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRows() - 1);
+ this->setSelection(index);
+ }
+}
+
+void SkListView::invalSelection()
+{
+ SkRect r;
+ if (this->getRowRect(fCurrIndex, &r))
+ this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+ if (fSource == nil)
+ return;
+
+ if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ if (index < 0) // too high
+ fScrollIndex = fCurrIndex;
+ else
+ fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+ this->dirtyStrCache();
+ this->inval(nil);
+ }
+ }
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+ SkASSERT(r);
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ SkScalar top = index * fRowHeight;
+
+ if (top < this->height())
+ {
+ if (r)
+ r->set(0, top, this->width(), top + fRowHeight);
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPaint& SkListView::paint(Attr attr)
+{
+ SkASSERT((unsigned)attr < kAttrCount);
+ return fPaint[attr];
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ this->dirtyStrCache();
+ this->ensureSelectionIsVisible();
+ this->inval(nil);
+ }
+ return src;
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ canvas->drawPaint(fPaint[kBG_Attr]);
+
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+ if (visibleCount == 0)
+ return;
+
+ this->ensureStrCache(visibleCount);
+ int currIndex = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)currIndex < (unsigned)visibleCount)
+ {
+ SkAutoCanvasRestore restore(canvas, true);
+ SkRect r;
+
+ canvas->translate(0, currIndex * fRowHeight);
+ (void)this->getRowRect(fScrollIndex, &r);
+ canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+ }
+
+ SkPaint* p;
+ SkScalar y, x = SkIntToScalar(6);
+ SkScalar rite = this->width() - x;
+
+ {
+ SkScalar ascent, descent;
+ fPaint[kNormalText_Attr].measureText(0, nil, &ascent, &descent);
+ y = SkScalarHalf(fRowHeight - descent + ascent) - ascent;
+ }
+
+ for (int i = 0; i < visibleCount; i++)
+ {
+ if (i == currIndex)
+ p = &fPaint[kHiliteText_Attr];
+ else
+ p = &fPaint[kNormalText_Attr];
+
+ p->setTextAlign(SkPaint::kLeft_Align);
+ canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p);
+ p->setTextAlign(SkPaint::kRight_Align);
+ canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p);
+ canvas->translate(0, fRowHeight);
+ }
+}
+
+void SkListView::onSizeChange()
+{
+ SkScalar count = SkScalarDiv(this->height(), fRowHeight);
+ int n = SkScalarFloor(count);
+
+ // only want to show rows that are mostly visible
+ if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100)
+ n += 1;
+
+ if (fVisibleRowCount != n)
+ {
+ fVisibleRowCount = n;
+ this->ensureSelectionIsVisible();
+ this->dirtyStrCache();
+ }
+}
+
+void SkListView::dirtyStrCache()
+{
+ if (fStrCache)
+ {
+ delete[] fStrCache;
+ fStrCache = nil;
+ }
+}
+
+void SkListView::ensureStrCache(int count)
+{
+ if (fStrCache == nil)
+ {
+ fStrCache = new SkString[count << 1];
+
+ if (fSource)
+ for (int i = 0; i < count; i++)
+ fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]);
+ }
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ this->moveSelectionUp();
+ return true;
+ case kDown_SkKey:
+ this->moveSelectionDown();
+ return true;
+ case kRight_SkKey:
+ case kOK_SkKey:
+ if (fSource && fCurrIndex >= 0)
+ {
+ SkEvent* evt = fSource->getEvent(fCurrIndex);
+ if (evt)
+ {
+ SkView* view = this->sendEventToParents(*evt);
+ delete evt;
+ return view != nil;
+ }
+ else // hack to make toggle work
+ {
+ this->dirtyStrCache();
+ this->inval(nil);
+ }
+ }
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ SkScalar x;
+ const SkDOM::Node* child;
+
+ if (dom.findScalar(node, "row-height", &x))
+ this->setRowHeight(x);
+
+ if ((child = dom.getFirstChild(node, "hilite-paint")) != nil)
+ SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+ // look for a listsource
+ {
+ SkListSource* src = nil;
+
+ if ((child = dom.getFirstChild(node, "file-listsource")) != nil)
+ {
+ const char* path = dom.findAttr(child, "path");
+ if (path)
+ src = SkListSource::CreateFromDir( path,
+ dom.findAttr(child, "filter"),
+ dom.findAttr(child, "target"));
+ }
+ else if ((child = dom.getFirstChild(node, "xml-listsource")) != nil)
+ {
+ src = SkListSource::CreateFromDOM(dom, child);
+ }
+
+ if (src)
+ {
+ this->setListSource(src)->unref();
+ this->setSelection(0);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+#include "SkShader.h"
+
+class SkScrollBarView : public SkView {
+public:
+ SkScrollBarView(const char bg[], const char fg[])
+ {
+ fBGRef = SkBitmapRef::Decode(bg, true);
+ fFGRef = SkBitmapRef::Decode(fg, true);
+
+ if (fBGRef)
+ this->setWidth(SkIntToScalar(fBGRef->bitmap().width()));
+ }
+ ~SkScrollBarView()
+ {
+ delete fBGRef;
+ delete fFGRef;
+ }
+protected:
+ virtual void onDraw(SkCanvas* canvas)
+ {
+ if (fBGRef == nil) return;
+
+ SkPaint paint;
+
+ SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+ paint.setShader(shader)->unref();
+
+ canvas->drawPaint(paint);
+ }
+private:
+ SkBitmapRef* fBGRef, *fFGRef;
+};
+
+SkGridView::SkGridView(U32 flags) : SkWidgetView(flags)
+{
+ fSource = nil;
+ fCurrIndex = -1;
+ fVisibleCount.set(0, 0);
+
+ fPaint[kBG_Attr].setColor(SK_ColorWHITE);
+ fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW);
+ fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style);
+ fPaint[kHiliteCell_Attr].setAntiAliasOn(true);
+ fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3);
+
+ fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg");
+ this->attachChildToFront(fScrollBar)->unref();
+ fScrollBar->setVisibleP(true);
+}
+
+SkGridView::~SkGridView()
+{
+ fSource->safeUnref();
+}
+
+void SkGridView::getCellSize(SkPoint* size) const
+{
+ if (size)
+ *size = fCellSize;
+}
+
+void SkGridView::setCellSize(SkScalar x, SkScalar y)
+{
+ SkASSERT(x >= 0 && y >= 0);
+
+ if (!fCellSize.equals(x, y))
+ {
+ fCellSize.set(x, y);
+ this->inval(nil);
+ }
+}
+
+void SkGridView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+
+ // this generates the click
+ {
+ SkEvent evt;
+ evt.setType("listview-selection");
+ evt.setFast32(index);
+ this->sendEventToParents(evt);
+ }
+ }
+}
+
+void SkGridView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRows() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+ this->setSelection(index);
+ }
+}
+
+void SkGridView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRows() - 1);
+ this->setSelection(index);
+ }
+}
+
+void SkGridView::invalSelection()
+{
+ SkRect r;
+ if (this->getCellRect(fCurrIndex, &r))
+ {
+ SkScalar inset = 0;
+ if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style)
+ inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2;
+ if (fPaint[kHiliteCell_Attr].isAntiAliasOn())
+ inset += SK_Scalar1;
+ r.inset(-inset, -inset);
+ this->inval(&r);
+ }
+}
+
+void SkGridView::ensureSelectionIsVisible()
+{
+ if (fSource == nil)
+ return;
+#if 0
+ if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ if (index < 0) // too high
+ fScrollIndex = fCurrIndex;
+ else
+ fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+ this->dirtyStrCache();
+ this->inval(nil);
+ }
+ }
+#endif
+}
+
+bool SkGridView::getCellRect(int index, SkRect* r) const
+{
+ if (fVisibleCount.fY == 0)
+ return false;
+
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ SkRect bounds;
+ int row = index / fVisibleCount.fY;
+ int col = index % fVisibleCount.fY;
+
+ bounds.set(0, 0, fCellSize.fX, fCellSize.fY);
+ bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)),
+ row * (fCellSize.fY + SkIntToScalar(row > 0)));
+
+ if (bounds.fTop < this->height())
+ {
+ if (r)
+ *r = bounds;
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPaint& SkGridView::paint(Attr attr)
+{
+ SkASSERT((unsigned)attr < kAttrCount);
+ return fPaint[attr];
+}
+
+SkListSource* SkGridView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ // this->dirtyStrCache();
+ this->ensureSelectionIsVisible();
+ this->inval(nil);
+ }
+ return src;
+}
+
+#include "SkShader.h"
+
+static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint)
+{
+ SkRect src;
+ SkMatrix matrix;
+
+ src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()));
+ if (matrix.setRectToRect(src, dst))
+ {
+ SkPaint p(paint);
+ SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+ p.setShader(shader)->unref();
+
+ shader->setLocalMatrix(matrix);
+ canvas->drawRect(dst, p);
+ }
+}
+
+#include "SkImageDecoder.h"
+
+void SkGridView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ canvas->drawPaint(fPaint[kBG_Attr]);
+
+ if (fSource == nil)
+ return;
+
+#if 0
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+ if (visibleCount == 0)
+ return;
+
+ this->ensureStrCache(visibleCount);
+ int currIndex = this->logicalToVisualIndex(fCurrIndex);
+#endif
+
+ SkPaint p;
+ for (int i = 0; i < fSource->countRows(); i++)
+ {
+ bool forced = false;
+ SkEvent* evt = fSource->getEvent(i);
+ SkASSERT(evt);
+ SkString path(evt->findString("path"));
+ delete evt;
+
+ SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false);
+ if (bmr == nil)
+ {
+ bmr = SkBitmapRef::Decode(path.c_str(), true);
+ if (bmr)
+ forced = true;
+ }
+
+ if (bmr)
+ {
+ SkAutoTDelete<SkBitmapRef> autoRef(bmr);
+ SkRect r;
+ if (!this->getCellRect(i, &r))
+ break;
+ copybits(canvas, bmr->bitmap(), r, p);
+ }
+ // only draw one forced bitmap at a time
+ if (forced)
+ {
+ this->inval(nil); // could inval only the remaining visible cells...
+ break;
+ }
+ }
+
+ // draw the hilite
+ {
+ SkRect r;
+ if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r))
+ canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+ }
+}
+
+static int check_count(int n, SkScalar s)
+{
+ // only want to show cells that are mostly visible
+ if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100)
+ n += 1;
+ return n;
+}
+
+void SkGridView::onSizeChange()
+{
+ fScrollBar->setHeight(this->height());
+ fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0);
+
+ if (fCellSize.equals(0, 0))
+ {
+ fVisibleCount.set(0, 0);
+ return;
+ }
+
+ SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY);
+ SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX);
+ int y = SkScalarFloor(rows);
+ int x = SkScalarFloor(cols);
+
+ y = check_count(y, rows);
+ x = check_count(x, cols);
+
+ if (!fVisibleCount.equals(x, y))
+ {
+ fVisibleCount.set(x, y);
+ this->ensureSelectionIsVisible();
+ // this->dirtyStrCache();
+ }
+}
+
+bool SkGridView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ this->moveSelectionUp();
+ return true;
+ case kDown_SkKey:
+ this->moveSelectionDown();
+ return true;
+ case kRight_SkKey:
+ case kOK_SkKey:
+ if (fSource && fCurrIndex >= 0)
+ {
+ SkEvent* evt = fSource->getEvent(fCurrIndex);
+ if (evt)
+ {
+ // augment the event with our local rect
+ (void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, nil));
+
+ SkView* view = this->sendEventToParents(*evt);
+ delete evt;
+ return view != nil;
+ }
+ }
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ SkScalar x[2];
+ const SkDOM::Node* child;
+
+ if (dom.findScalars(node, "cell-size", x, 2))
+ this->setCellSize(x[0], x[1]);
+
+ if ((child = dom.getFirstChild(node, "hilite-paint")) != nil)
+ SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+ // look for a listsource
+ {
+ SkListSource* src = nil;
+
+ if ((child = dom.getFirstChild(node, "file-listsource")) != nil)
+ {
+ const char* path = dom.findAttr(child, "path");
+ if (path)
+ src = SkListSource::CreateFromDir( path,
+ dom.findAttr(child, "filter"),
+ dom.findAttr(child, "target"));
+ }
+ else if ((child = dom.getFirstChild(node, "xml-listsource")) != nil)
+ {
+ src = SkListSource::CreateFromDOM(dom, child);
+ }
+
+ if (src)
+ {
+ this->setListSource(src)->unref();
+ this->setSelection(0);
+ }
+ }
+ this->onSizeChange();
+}
+
+#endif
diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp
new file mode 100644
index 0000000000..82e5d7813e
--- /dev/null
+++ b/src/views/SkListWidget.cpp
@@ -0,0 +1,623 @@
+#include "SkWidgetViews.h"
+
+#include "SkAnimator.h"
+#include "SkScrollBarView.h"
+
+extern void init_skin_anim(const char name[], SkAnimator*);
+
+struct SkListView::BindingRec {
+ SkString fSlotName;
+ int fFieldIndex;
+};
+
+SkListView::SkListView()
+{
+ fSource = nil; // our list-source
+ fScrollBar = nil;
+ fAnims = nil; // array of animators[fVisibleRowCount]
+ fBindings = nil; // our fields->slot array
+ fBindingCount = 0; // number of entries in fSlots array
+ fScrollIndex = 0; // number of cells to skip before first visible cell
+ fCurrIndex = -1; // index of "selected" cell
+ fVisibleRowCount = 0; // number of cells that can fit in our bounds
+ fAnimContentDirty = true; // true if fAnims[] have their correct content
+ fAnimFocusDirty = true;
+
+ fHeights[kNormal_Height] = SkIntToScalar(16);
+ fHeights[kSelected_Height] = SkIntToScalar(16);
+
+ this->setFlags(this->getFlags() | kFocusable_Mask);
+}
+
+SkListView::~SkListView()
+{
+ fScrollBar->safeUnref();
+ fSource->safeUnref();
+ delete[] fAnims;
+ delete[] fBindings;
+}
+
+void SkListView::setHasScrollBar(bool hasSB)
+{
+ if (hasSB != this->hasScrollBar())
+ {
+ if (hasSB)
+ {
+ SkASSERT(fScrollBar == nil);
+ fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
+ fScrollBar->setVisibleP(true);
+ this->attachChildToFront(fScrollBar);
+ fScrollBar->setHeight(this->height()); // assume it auto-sets its width
+ // fScrollBar->setLoc(this->getContentWidth(), 0);
+ fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+ }
+ else
+ {
+ SkASSERT(fScrollBar);
+ fScrollBar->detachFromParent();
+ fScrollBar->unref();
+ fScrollBar = nil;
+ }
+ this->dirtyCache(kAnimContent_DirtyFlag);
+ }
+}
+
+void SkListView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ fAnimFocusDirty = true;
+ this->inval(nil);
+
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+ }
+}
+
+bool SkListView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRecords() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+
+ if (fCurrIndex != index)
+ {
+ this->setSelection(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SkListView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRecords() - 1);
+
+ if (fCurrIndex != index)
+ {
+ this->setSelection(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SkListView::invalSelection()
+{
+ SkRect r;
+ if (this->getRowRect(fCurrIndex, &r))
+ this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+ if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ int newIndex;
+
+ if (index < 0) // too high
+ newIndex = fCurrIndex;
+ else
+ newIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
+ this->inval(nil);
+
+ if (fScrollIndex != newIndex)
+ {
+ fScrollIndex = newIndex;
+ if (fScrollBar)
+ fScrollBar->setStart(newIndex);
+ this->dirtyCache(kAnimContent_DirtyFlag);
+ }
+ }
+ }
+}
+
+SkScalar SkListView::getContentWidth() const
+{
+ SkScalar width = this->width();
+
+ if (fScrollBar)
+ {
+ width -= fScrollBar->width();
+ if (width < 0)
+ width = 0;
+ }
+ return width;
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+ SkASSERT(r);
+
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ int selection = this->logicalToVisualIndex(fCurrIndex);
+
+ SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
+ SkScalar top = index * fHeights[kNormal_Height];
+
+ if (index > selection && selection >= 0)
+ top += fHeights[kSelected_Height] - fHeights[kNormal_Height];
+
+ if (top < this->height())
+ {
+ if (r)
+ r->set(0, top, this->getContentWidth(), top + height);
+ return true;
+ }
+ }
+ return false;
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ this->ensureSelectionIsVisible();
+ this->inval(nil);
+
+ if (fScrollBar)
+ fScrollBar->setTotal(fSource->countRecords());
+ }
+ return src;
+}
+
+void SkListView::dirtyCache(unsigned dirtyFlags)
+{
+ if (dirtyFlags & kAnimCount_DirtyFlag)
+ {
+ delete fAnims;
+ fAnims = nil;
+ fAnimContentDirty = true;
+ fAnimFocusDirty = true;
+ }
+ if (dirtyFlags & kAnimContent_DirtyFlag)
+ {
+ if (!fAnimContentDirty)
+ {
+ this->inval(nil);
+ fAnimContentDirty = true;
+ }
+ fAnimFocusDirty = true;
+ }
+}
+
+bool SkListView::ensureCache()
+{
+ if (fSkinName.size() == 0)
+ return false;
+
+ if (fAnims == nil)
+ {
+ int n = SkMax32(1, fVisibleRowCount);
+
+ SkASSERT(fAnimContentDirty);
+ fAnims = new SkAnimator[n];
+ for (int i = 0; i < n; i++)
+ {
+ fAnims[i].setHostEventSink(this);
+ init_skin_anim(fSkinName.c_str(), &fAnims[i]);
+ }
+
+ fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
+ fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
+
+ fAnimFocusDirty = true;
+ }
+
+ if (fAnimContentDirty && fSource)
+ {
+ fAnimContentDirty = false;
+
+ SkString str;
+ SkEvent evt("user");
+ evt.setString("id", "setFields");
+ evt.setS32("rowCount", fVisibleRowCount);
+
+ SkEvent dimEvt("user");
+ dimEvt.setString("id", "setDim");
+ dimEvt.setScalar("dimX", this->getContentWidth());
+ dimEvt.setScalar("dimY", this->height());
+
+ for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+ {
+ evt.setS32("relativeIndex", i - fScrollIndex);
+ for (int j = 0; j < fBindingCount; j++)
+ {
+ fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
+//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
+ evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
+ }
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
+ }
+ fAnimFocusDirty = true;
+ }
+
+ if (fAnimFocusDirty)
+ {
+//SkDEBUGF(("service fAnimFocusDirty\n"));
+ fAnimFocusDirty = false;
+
+ SkEvent focusEvt("user");
+ focusEvt.setString("id", "setFocus");
+
+ for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+ {
+ focusEvt.setS32("FOCUS", i == fCurrIndex);
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
+ }
+ }
+
+ return true;
+}
+
+void SkListView::ensureVisibleRowCount()
+{
+ SkScalar height = this->height();
+ int n = 0;
+
+ if (height > 0)
+ {
+ n = 1;
+ height -= fHeights[kSelected_Height];
+ if (height > 0)
+ {
+ SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
+ n += SkScalarFloor(count);
+ if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
+ n += 1;
+
+ // SkDebugf("count %g, n %d\n", count/65536., n);
+ }
+ }
+
+ if (fVisibleRowCount != n)
+ {
+ if (fScrollBar)
+ fScrollBar->setShown(n);
+
+ fVisibleRowCount = n;
+ this->ensureSelectionIsVisible();
+ this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+void SkListView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+
+ if (fScrollBar)
+ fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+
+ this->ensureVisibleRowCount();
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ this->ensureVisibleRowCount();
+
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
+ if (visibleCount == 0 || !this->ensureCache())
+ return;
+
+//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
+
+ SkAutoCanvasRestore ar(canvas, true);
+ SkMSec now = SkTime::GetMSecs();
+ SkRect bounds;
+
+ bounds.fLeft = 0;
+ bounds.fRight = this->getContentWidth();
+ bounds.fBottom = 0;
+ // assign bounds.fTop inside the loop
+
+ // hack to reveal our bounds for debugging
+ if (this->hasFocus())
+ canvas->drawARGB(0x11, 0, 0, 0xFF);
+ else
+ canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
+
+ for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
+ {
+ SkPaint paint;
+ SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
+
+ bounds.fTop = bounds.fBottom;
+ bounds.fBottom += height;
+
+ canvas->save();
+ if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
+ this->inval(&bounds);
+ canvas->restore();
+
+ canvas->translate(0, height);
+ }
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ return this->moveSelectionUp();
+ case kDown_SkKey:
+ return this->moveSelectionDown();
+ case kRight_SkKey:
+ case kOK_SkKey:
+ this->postWidgetEvent();
+ return true;
+ default:
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const char gListViewEventSlot[] = "sk-listview-slot-name";
+
+/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
+ fSource->prepareWidgetEvent(evt, fCurrIndex))
+ {
+ evt->setS32(gListViewEventSlot, fCurrIndex);
+ return true;
+ }
+ return false;
+}
+
+int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
+{
+ int32_t index;
+
+ return evt.findS32(gListViewEventSlot, &index) ? index : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ {
+ bool hasScrollBar;
+ if (dom.findBool(node, "scrollBar", &hasScrollBar))
+ this->setHasScrollBar(hasScrollBar);
+ }
+
+ const SkDOM::Node* child;
+
+ if ((child = dom.getFirstChild(node, "bindings")) != nil)
+ {
+ delete[] fBindings;
+ fBindings = nil;
+ fBindingCount = 0;
+
+ SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
+ SkASSERT(listSrc);
+ fSkinName.set(dom.findAttr(child, "skin-slots"));
+ SkASSERT(fSkinName.size());
+
+ this->setListSource(listSrc)->unref();
+
+ int count = dom.countChildren(child, "bind");
+ if (count > 0)
+ {
+ fBindings = new BindingRec[count];
+ count = 0; // reuse this to count up to the number of valid bindings
+
+ child = dom.getFirstChild(child, "bind");
+ SkASSERT(child);
+ do {
+ const char* fieldName = dom.findAttr(child, "field");
+ const char* slotName = dom.findAttr(child, "slot");
+ if (fieldName && slotName)
+ {
+ fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
+ if (fBindings[count].fFieldIndex >= 0)
+ fBindings[count++].fSlotName.set(slotName);
+ }
+ } while ((child = dom.getNextSibling(child, "bind")) != nil);
+
+ fBindingCount = SkToU16(count);
+ if (count == 0)
+ {
+ SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
+ delete[] fBindings;
+ }
+ }
+ this->dirtyCache(kAnimCount_DirtyFlag);
+ this->setSelection(0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkXMLListSource : public SkListSource {
+public:
+ SkXMLListSource(const char doc[], size_t len);
+ virtual ~SkXMLListSource()
+ {
+ delete[] fFields;
+ delete[] fRecords;
+ }
+
+ virtual int countFields() { return fFieldCount; }
+ virtual void getFieldName(int index, SkString* field)
+ {
+ SkASSERT((unsigned)index < (unsigned)fFieldCount);
+ if (field)
+ *field = fFields[index];
+ }
+ virtual int findFieldIndex(const char field[])
+ {
+ for (int i = 0; i < fFieldCount; i++)
+ if (fFields[i].equals(field))
+ return i;
+ return -1;
+ }
+
+ virtual int countRecords() { return fRecordCount; }
+ virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
+ {
+ SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
+ SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
+ if (data)
+ *data = fRecords[rowIndex * fFieldCount + fieldIndex];
+ }
+
+ virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
+ {
+ // hack, for testing right now. Need the xml to tell us what to jam in and where
+ SkString data;
+
+ this->getRecord(rowIndex, 0, &data);
+ evt->setString("xml-listsource", data.c_str());
+ return true;
+ }
+
+private:
+ SkString* fFields; // [fFieldCount]
+ SkString* fRecords; // [fRecordCount][fFieldCount]
+ int fFieldCount, fRecordCount;
+};
+
+#include "SkDOM.h"
+
+SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
+{
+ fFieldCount = fRecordCount = 0;
+ fFields = fRecords = nil;
+
+ SkDOM dom;
+
+ const SkDOM::Node* node = dom.build(doc, len);
+ SkASSERT(node);
+ const SkDOM::Node* child;
+
+ child = dom.getFirstChild(node, "fields");
+ if (child)
+ {
+ fFieldCount = dom.countChildren(child, "field");
+ fFields = new SkString[fFieldCount];
+
+ int n = 0;
+ child = dom.getFirstChild(child, "field");
+ while (child)
+ {
+ fFields[n].set(dom.findAttr(child, "name"));
+ child = dom.getNextSibling(child, "field");
+ n += 1;
+ }
+ SkASSERT(n == fFieldCount);
+ }
+
+ child = dom.getFirstChild(node, "records");
+ if (child)
+ {
+ fRecordCount = dom.countChildren(child, "record");
+ fRecords = new SkString[fRecordCount * fFieldCount];
+
+ int n = 0;
+ child = dom.getFirstChild(child, "record");
+ while (child)
+ {
+ for (int i = 0; i < fFieldCount; i++)
+ fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
+ child = dom.getNextSibling(child, "record");
+ n += 1;
+ }
+ SkASSERT(n == fRecordCount);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+SkListSource* SkListSource::Factory(const char name[])
+{
+ static const char gDoc[] =
+ "<db name='contacts.db'>"
+ "<fields>"
+ "<field name='name'/>"
+ "<field name='work-num'/>"
+ "<field name='home-num'/>"
+ "<field name='type'/>"
+ "</fields>"
+ "<records>"
+ "<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
+ "<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
+ "<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
+ "<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
+ "<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
+ "<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
+ "<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "</records>"
+ "</db>";
+
+//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
+ return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
+}
+
+
+
diff --git a/src/views/SkMetaData.cpp b/src/views/SkMetaData.cpp
new file mode 100644
index 0000000000..c366bd3fee
--- /dev/null
+++ b/src/views/SkMetaData.cpp
@@ -0,0 +1,405 @@
+/* libs/graphics/views/SkMetaData.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMetaData.h"
+
+SkMetaData::SkMetaData() : fRec(NULL)
+{
+}
+
+SkMetaData::SkMetaData(const SkMetaData& src) : fRec(NULL)
+{
+ *this = src;
+}
+
+SkMetaData::~SkMetaData()
+{
+ this->reset();
+}
+
+void SkMetaData::reset()
+{
+ Rec* rec = fRec;
+ while (rec)
+ {
+ Rec* next = rec->fNext;
+ Rec::Free(rec);
+ rec = next;
+ }
+ fRec = NULL;
+}
+
+SkMetaData& SkMetaData::operator=(const SkMetaData& src)
+{
+ this->reset();
+
+ const Rec* rec = src.fRec;
+ while (rec)
+ {
+ this->set(rec->name(), rec->data(), rec->fDataLen, (Type)rec->fType, rec->fDataCount);
+ rec = rec->fNext;
+ }
+ return *this;
+}
+
+void SkMetaData::setS32(const char name[], int32_t value)
+{
+ (void)this->set(name, &value, sizeof(int32_t), kS32_Type, 1);
+}
+
+void SkMetaData::setScalar(const char name[], SkScalar value)
+{
+ (void)this->set(name, &value, sizeof(SkScalar), kScalar_Type, 1);
+}
+
+SkScalar* SkMetaData::setScalars(const char name[], int count, const SkScalar values[])
+{
+ SkASSERT(count > 0);
+ if (count > 0)
+ return (SkScalar*)this->set(name, values, sizeof(SkScalar), kScalar_Type, count);
+ return NULL;
+}
+
+void SkMetaData::setString(const char name[], const char value[])
+{
+ (void)this->set(name, value, sizeof(char), kString_Type, strlen(value) + 1);
+}
+
+void SkMetaData::setPtr(const char name[], void* ptr)
+{
+ (void)this->set(name, &ptr, sizeof(void*), kPtr_Type, 1);
+}
+
+void SkMetaData::setBool(const char name[], bool value)
+{
+ (void)this->set(name, &value, sizeof(bool), kBool_Type, 1);
+}
+
+void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count)
+{
+ SkASSERT(name);
+ SkASSERT(dataSize);
+ SkASSERT(count > 0);
+
+ (void)this->remove(name, type);
+
+ size_t len = strlen(name);
+ Rec* rec = Rec::Alloc(sizeof(Rec) + dataSize * count + len + 1);
+
+#ifndef SK_DEBUG
+ rec->fType = SkToU8(type);
+#else
+ rec->fType = type;
+#endif
+ rec->fDataLen = SkToU8(dataSize);
+ rec->fDataCount = SkToU16(count);
+ if (data)
+ memcpy(rec->data(), data, dataSize * count);
+ memcpy(rec->name(), name, len + 1);
+
+#ifdef SK_DEBUG
+ rec->fName = rec->name();
+ switch (type) {
+ case kS32_Type:
+ rec->fData.fS32 = *(const int32_t*)rec->data();
+ break;
+ case kScalar_Type:
+ rec->fData.fScalar = *(const SkScalar*)rec->data();
+ break;
+ case kString_Type:
+ rec->fData.fString = (const char*)rec->data();
+ break;
+ case kPtr_Type:
+ rec->fData.fPtr = *(void**)rec->data();
+ break;
+ case kBool_Type:
+ rec->fData.fBool = *(const bool*)rec->data();
+ break;
+ default:
+ SkASSERT(!"bad type");
+ break;
+ }
+#endif
+
+ rec->fNext = fRec;
+ fRec = rec;
+ return rec->data();
+}
+
+bool SkMetaData::findS32(const char name[], int32_t* value) const
+{
+ const Rec* rec = this->find(name, kS32_Type);
+ if (rec)
+ {
+ SkASSERT(rec->fDataCount == 1);
+ if (value)
+ *value = *(const int32_t*)rec->data();
+ return true;
+ }
+ return false;
+}
+
+bool SkMetaData::findScalar(const char name[], SkScalar* value) const
+{
+ const Rec* rec = this->find(name, kScalar_Type);
+ if (rec)
+ {
+ SkASSERT(rec->fDataCount == 1);
+ if (value)
+ *value = *(const SkScalar*)rec->data();
+ return true;
+ }
+ return false;
+}
+
+const SkScalar* SkMetaData::findScalars(const char name[], int* count, SkScalar values[]) const
+{
+ const Rec* rec = this->find(name, kScalar_Type);
+ if (rec)
+ {
+ if (count)
+ *count = rec->fDataCount;
+ if (values)
+ memcpy(values, rec->data(), rec->fDataCount * rec->fDataLen);
+ return (const SkScalar*)rec->data();
+ }
+ return NULL;
+}
+
+bool SkMetaData::findPtr(const char name[], void** value) const
+{
+ const Rec* rec = this->find(name, kPtr_Type);
+ if (rec)
+ {
+ SkASSERT(rec->fDataCount == 1);
+ if (value)
+ *value = *(void**)rec->data();
+ return true;
+ }
+ return false;
+}
+
+const char* SkMetaData::findString(const char name[]) const
+{
+ const Rec* rec = this->find(name, kString_Type);
+ SkASSERT(rec == NULL || rec->fDataLen == sizeof(char));
+ return rec ? (const char*)rec->data() : NULL;
+}
+
+bool SkMetaData::findBool(const char name[], bool* value) const
+{
+ const Rec* rec = this->find(name, kBool_Type);
+ if (rec)
+ {
+ SkASSERT(rec->fDataCount == 1);
+ if (value)
+ *value = *(const bool*)rec->data();
+ return true;
+ }
+ return false;
+}
+
+const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const
+{
+ const Rec* rec = fRec;
+ while (rec)
+ {
+ if (rec->fType == type && !strcmp(rec->name(), name))
+ return rec;
+ rec = rec->fNext;
+ }
+ return NULL;
+}
+
+bool SkMetaData::remove(const char name[], Type type)
+{
+ Rec* rec = fRec;
+ Rec* prev = NULL;
+ while (rec)
+ {
+ Rec* next = rec->fNext;
+ if (rec->fType == type && !strcmp(rec->name(), name))
+ {
+ if (prev)
+ prev->fNext = next;
+ else
+ fRec = next;
+ Rec::Free(rec);
+ return true;
+ }
+ prev = rec;
+ rec = next;
+ }
+ return false;
+}
+
+bool SkMetaData::removeS32(const char name[])
+{
+ return this->remove(name, kS32_Type);
+}
+
+bool SkMetaData::removeScalar(const char name[])
+{
+ return this->remove(name, kScalar_Type);
+}
+
+bool SkMetaData::removeString(const char name[])
+{
+ return this->remove(name, kString_Type);
+}
+
+bool SkMetaData::removePtr(const char name[])
+{
+ return this->remove(name, kPtr_Type);
+}
+
+bool SkMetaData::removeBool(const char name[])
+{
+ return this->remove(name, kBool_Type);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+SkMetaData::Iter::Iter(const SkMetaData& metadata)
+{
+ fRec = metadata.fRec;
+}
+
+void SkMetaData::Iter::reset(const SkMetaData& metadata)
+{
+ fRec = metadata.fRec;
+}
+
+const char* SkMetaData::Iter::next(SkMetaData::Type* t, int* count)
+{
+ const char* name = NULL;
+
+ if (fRec)
+ {
+ if (t)
+ *t = (SkMetaData::Type)fRec->fType;
+ if (count)
+ *count = fRec->fDataCount;
+ name = fRec->name();
+
+ fRec = fRec->fNext;
+ }
+ return name;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+SkMetaData::Rec* SkMetaData::Rec::Alloc(size_t size)
+{
+ return (Rec*)sk_malloc_throw(size);
+}
+
+void SkMetaData::Rec::Free(Rec* rec)
+{
+ sk_free(rec);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkMetaData::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+ SkMetaData m1;
+
+ SkASSERT(!m1.findS32("int"));
+ SkASSERT(!m1.findScalar("scalar"));
+ SkASSERT(!m1.findString("hello"));
+ SkASSERT(!m1.removeS32("int"));
+ SkASSERT(!m1.removeScalar("scalar"));
+ SkASSERT(!m1.removeString("hello"));
+ SkASSERT(!m1.removeString("true"));
+ SkASSERT(!m1.removeString("false"));
+
+ m1.setS32("int", 12345);
+ m1.setScalar("scalar", SK_Scalar1 * 42);
+ m1.setString("hello", "world");
+ m1.setPtr("ptr", &m1);
+ m1.setBool("true", true);
+ m1.setBool("false", false);
+
+ int32_t n;
+ SkScalar s;
+
+ m1.setScalar("scalar", SK_Scalar1/2);
+
+ SkASSERT(m1.findS32("int", &n) && n == 12345);
+ SkASSERT(m1.findScalar("scalar", &s) && s == SK_Scalar1/2);
+ SkASSERT(!strcmp(m1.findString("hello"), "world"));
+ SkASSERT(m1.hasBool("true", true));
+ SkASSERT(m1.hasBool("false", false));
+
+ Iter iter(m1);
+ const char* name;
+
+ static const struct {
+ const char* fName;
+ SkMetaData::Type fType;
+ int fCount;
+ } gElems[] = {
+ { "int", SkMetaData::kS32_Type, 1 },
+ { "scalar", SkMetaData::kScalar_Type, 1 },
+ { "ptr", SkMetaData::kPtr_Type, 1 },
+ { "hello", SkMetaData::kString_Type, sizeof("world") },
+ { "true", SkMetaData::kBool_Type, 1 },
+ { "false", SkMetaData::kBool_Type, 1 }
+ };
+
+ int loop = 0;
+ int count;
+ SkMetaData::Type t;
+ while ((name = iter.next(&t, &count)) != NULL)
+ {
+ int match = 0;
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++)
+ {
+ if (!strcmp(name, gElems[i].fName))
+ {
+ match += 1;
+ SkASSERT(gElems[i].fType == t);
+ SkASSERT(gElems[i].fCount == count);
+ }
+ }
+ SkASSERT(match == 1);
+ loop += 1;
+ }
+ SkASSERT(loop == SK_ARRAY_COUNT(gElems));
+
+ SkASSERT(m1.removeS32("int"));
+ SkASSERT(m1.removeScalar("scalar"));
+ SkASSERT(m1.removeString("hello"));
+ SkASSERT(m1.removeBool("true"));
+ SkASSERT(m1.removeBool("false"));
+
+ SkASSERT(!m1.findS32("int"));
+ SkASSERT(!m1.findScalar("scalar"));
+ SkASSERT(!m1.findString("hello"));
+ SkASSERT(!m1.findBool("true"));
+ SkASSERT(!m1.findBool("false"));
+#endif
+}
+
+#endif
+
+
diff --git a/src/views/SkOSFile.cpp b/src/views/SkOSFile.cpp
new file mode 100644
index 0000000000..c8eeeea405
--- /dev/null
+++ b/src/views/SkOSFile.cpp
@@ -0,0 +1,223 @@
+#include "SkOSFile.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+static U16* concat_to_16(const char src[], const char suffix[])
+{
+ size_t i, len = strlen(src);
+ size_t len2 = 3 + (suffix ? strlen(suffix) : 0);
+ U16* dst = (U16*)sk_malloc_throw((len + len2) * sizeof(U16));
+
+ for (i = 0; i < len; i++)
+ dst[i] = src[i];
+
+ if (i > 0 && dst[i-1] != '/')
+ dst[i++] = '/';
+ dst[i++] = '*';
+
+ if (suffix)
+ {
+ while (*suffix)
+ dst[i++] = *suffix++;
+ }
+ dst[i] = 0;
+ SkASSERT(i + 1 <= len + len2);
+
+ return dst;
+}
+
+SkUTF16_Str::SkUTF16_Str(const char src[])
+{
+ size_t len = strlen(src);
+
+ fStr = (U16*)sk_malloc_throw((len + 1) * sizeof(U16));
+ for (size_t i = 0; i < len; i++)
+ fStr[i] = src[i];
+ fStr[i] = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkOSFile::Iter::Iter() : fHandle(0), fPath16(nil)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fHandle(0), fPath16(nil)
+{
+ this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+ sk_free(fPath16);
+ if (fHandle)
+ ::FindClose(fHandle);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+ if (fHandle)
+ {
+ ::FindClose(fHandle);
+ fHandle = 0;
+ }
+ if (path == nil)
+ path = "";
+
+ sk_free(fPath16);
+ fPath16 = concat_to_16(path, suffix);
+}
+
+static bool is_magic_dir(const U16 dir[])
+{
+ // return true for "." and ".."
+ return dir[0] == '.' && (dir[1] == 0 || dir[1] == '.' && dir[2] == 0);
+}
+
+static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir)
+{
+ WIN32_FIND_DATAW data;
+
+ if (dataPtr == nil)
+ {
+ if (::FindNextFileW(handle, &data))
+ dataPtr = &data;
+ else
+ return false;
+ }
+
+ for (;;)
+ {
+ if (getDir)
+ {
+ if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is_magic_dir(dataPtr->cFileName))
+ break;
+ }
+ else
+ {
+ if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ break;
+ }
+ if (!::FindNextFileW(handle, dataPtr))
+ return false;
+ }
+ // if we get here, we've found a file/dir
+ if (name)
+ name->setUTF16(dataPtr->cFileName);
+ return true;
+}
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+ WIN32_FIND_DATAW data;
+ WIN32_FIND_DATAW* dataPtr = nil;
+
+ if (fHandle == 0) // our first time
+ {
+ if (fPath16 == nil || *fPath16 == 0) // check for no path
+ return false;
+
+ fHandle = ::FindFirstFileW(fPath16, &data);
+ if (fHandle != 0 && fHandle != (HANDLE)~0)
+ dataPtr = &data;
+ }
+ return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir);
+}
+
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
+
+#if 0
+OSStatus FSPathMakeRef (
+ const UInt8 * path,
+ FSRef * ref,
+ Boolean * isDirectory
+);
+#endif
+
+SkOSFile::Iter::Iter() : fDIR(0)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fDIR(0)
+{
+ this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+ if (fDIR)
+ ::closedir(fDIR);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+ if (fDIR)
+ {
+ ::closedir(fDIR);
+ fDIR = 0;
+ }
+
+ fPath.set(path);
+ if (path)
+ {
+ fDIR = ::opendir(path);
+ fSuffix.set(suffix);
+ }
+ else
+ fSuffix.reset();
+}
+
+// returns true if suffix is empty, or if str ends with suffix
+static bool issuffixfor(const SkString& suffix, const char str[])
+{
+ size_t suffixLen = suffix.size();
+ size_t strLen = strlen(str);
+
+ return strLen >= suffixLen &&
+ suffixLen == 0 ||
+ memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0;
+}
+
+#include <sys/stat.h>
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+ if (fDIR)
+ {
+ dirent* entry;
+
+ while ((entry = ::readdir(fDIR)) != NULL)
+ {
+ struct stat s;
+ SkString str(fPath);
+
+ if (!str.endsWith("/") && !str.endsWith("\\"))
+ str.append("/");
+ str.append(entry->d_name);
+
+ if (0 == stat(str.c_str(), &s))
+ {
+ if (getDir)
+ {
+ if (s.st_mode & S_IFDIR)
+ break;
+ }
+ else
+ {
+ if (!(s.st_mode & S_IFDIR) && issuffixfor(fSuffix, entry->d_name))
+ break;
+ }
+ }
+ }
+ if (entry) // we broke out with a file
+ {
+ if (name)
+ name->set(entry->d_name);
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
new file mode 100644
index 0000000000..3760ddd5cb
--- /dev/null
+++ b/src/views/SkOSMenu.cpp
@@ -0,0 +1,53 @@
+#include "SkOSMenu.h"
+
+static int gOSMenuCmd = 7000;
+
+SkOSMenu::SkOSMenu(const char title[])
+{
+ fTitle = title;
+}
+
+SkOSMenu::~SkOSMenu()
+{
+}
+
+int SkOSMenu::countItems() const
+{
+ return fItems.count();
+}
+
+void SkOSMenu::appendItem(const char title[], const char eventType[], int32_t eventData)
+{
+ Item* item = fItems.append();
+
+ item->fTitle = title;
+ item->fEventType = eventType;
+ item->fEventData = eventData;
+ item->fOSCmd = ++gOSMenuCmd;
+}
+
+SkEvent* SkOSMenu::createEvent(uint32_t os_cmd)
+{
+ const Item* iter = fItems.begin();
+ const Item* stop = fItems.end();
+
+ while (iter < stop)
+ {
+ if (iter->fOSCmd == os_cmd)
+ {
+ SkEvent* evt = new SkEvent(iter->fEventType);
+ evt->setFast32(iter->fEventData);
+ return evt;
+ }
+ iter++;
+ }
+ return NULL;
+}
+
+const char* SkOSMenu::getItem(int index, uint32_t* cmdID) const
+{
+ if (cmdID)
+ *cmdID = fItems[index].fOSCmd;
+ return fItems[index].fTitle;
+}
+
diff --git a/src/views/SkOSSound.cpp b/src/views/SkOSSound.cpp
new file mode 100644
index 0000000000..13cd037756
--- /dev/null
+++ b/src/views/SkOSSound.cpp
@@ -0,0 +1,343 @@
+#include "SkOSSound.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+#include <Mmreg.h>
+#if defined _WIN32 && _MSC_VER >= 1300 // disable nameless struct/union
+#pragma warning ( push )
+#pragma warning ( disable : 4201 )
+#endif
+#include <Mmsystem.h>
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+#include <stdio.h>
+
+class CWaveFile {
+public:
+ BOOL Open(const char path[]);
+ void Close();
+
+ long Read(char* pData, long nLength);
+
+ long GetLength() const {return m_nLength;}
+ WAVEFORMATEX* GetWaveFormat() {return (&m_Format);}
+
+protected:
+ FILE* m_pFile;
+ long m_nLength;
+ WAVEFORMATEX m_Format;
+
+private:
+ enum {
+ WF_OFFSET_FORMATTAG = 20,
+ WF_OFFSET_CHANNELS = 22,
+ WF_OFFSET_SAMPLESPERSEC = 24,
+ WF_OFFSET_AVGBYTESPERSEC = 28,
+ WF_OFFSET_BLOCKALIGN = 32,
+ WF_OFFSET_BITSPERSAMPLE = 34,
+ WF_OFFSET_DATASIZE = 40,
+ WF_OFFSET_DATA = 44,
+ WF_HEADER_SIZE = WF_OFFSET_DATA
+ };
+};
+
+BOOL CWaveFile::Open(const char path[])
+{
+ BYTE aHeader[WF_HEADER_SIZE];
+
+/* hResInfo = FindResource (hInst, lpName, "WAVE");
+
+ if (hResInfo == NULL)
+ return FALSE;
+
+ // Load the wave resource.
+ hRes = LoadResource (hInst, hResInfo);
+
+ if (hRes == NULL)
+ return FALSE;
+
+ // Lock the wave resource and play it.
+ lpRes = LockResource (0);
+*/
+
+
+ // open file
+// m_pFile = _tfopen(szFileName, TEXT("rb"));
+ m_pFile = fopen(path, "rb");
+ if (!m_pFile) {
+ return FALSE;
+ }
+
+ // set file length
+ fseek(m_pFile, 0, SEEK_END);
+ m_nLength = ftell(m_pFile) - WF_HEADER_SIZE;
+
+ // set the format attribute members
+ fseek(m_pFile, 0, SEEK_SET);
+ fread(aHeader, 1, WF_HEADER_SIZE, m_pFile);
+ m_Format.wFormatTag = *((WORD*) (aHeader + WF_OFFSET_FORMATTAG));
+ m_Format.nChannels = *((WORD*) (aHeader + WF_OFFSET_CHANNELS));
+ m_Format.nSamplesPerSec = *((DWORD*) (aHeader + WF_OFFSET_SAMPLESPERSEC));
+ m_Format.nAvgBytesPerSec = *((DWORD*) (aHeader + WF_OFFSET_AVGBYTESPERSEC));
+ m_Format.nBlockAlign = *((WORD*) (aHeader + WF_OFFSET_BLOCKALIGN));
+ m_Format.wBitsPerSample = *((WORD*) (aHeader + WF_OFFSET_BITSPERSAMPLE));
+
+ return TRUE;
+}
+
+void CWaveFile::Close()
+{
+ fclose(m_pFile);
+}
+
+long CWaveFile::Read(char* pData, long nLength)
+{
+ return fread(pData, 1, nLength, m_pFile);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+struct SkOSSoundWave {
+ HWAVEOUT hwo;
+ WAVEHDR whdr;
+ DWORD dwOldVolume;
+ CWaveFile waveFile;
+ HANDLE hDoneEvent;
+};
+
+static SkOSSoundWave gWave;
+static bool gWavePaused;
+static U8 gVolume;
+static bool gInited = false;
+
+static void init_wave()
+{
+ if (gInited == false)
+ {
+ gWave.hwo = nil;
+ gWavePaused = false;
+ gVolume = 0x80;
+ gInited = true;
+ }
+}
+
+MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol);
+MMRESULT EndWave(SkOSSoundWave* wave);
+
+#define MAX_ERRMSG 256
+
+//#include "SkOSFile.h" // for utf16
+
+void SkOSSound::Play(const char path[])
+{
+ init_wave();
+
+ if (gWave.hwo != nil)
+ SkOSSound::Stop();
+
+ U32 v32 = (gVolume << 8) | gVolume; // fill it out to 16bits
+ v32 |= v32 << 16; // set the left and right channels
+
+ StartWave(path, &gWave, v32);
+ gWavePaused = false;
+}
+
+bool SkOSSound::TogglePause()
+{
+ init_wave();
+
+ if (gWavePaused)
+ SkOSSound::Resume();
+ else
+ SkOSSound::Pause();
+ return !gWavePaused;
+}
+
+
+void SkOSSound::Pause()
+{
+ init_wave();
+
+ if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+ return;
+ waveOutPause(gWave.hwo);
+ gWavePaused = true;
+}
+
+void SkOSSound::Resume()
+{
+ init_wave();
+
+ if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+ return;
+ waveOutRestart(gWave.hwo);
+ gWavePaused = false;
+}
+
+void SkOSSound::Stop()
+{
+ init_wave();
+
+// if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+ if (gWave.hwo == nil)
+ return;
+ waveOutReset(gWave.hwo);
+ EndWave(&gWave);
+ gWavePaused = false;
+ gWave.hwo = nil;
+}
+
+U8 SkOSSound::GetVolume()
+{
+ init_wave();
+ return gVolume;
+}
+
+void SkOSSound::SetVolume(U8CPU vol)
+{
+ if ((int)vol < 0)
+ vol = 0;
+ else if (vol > 255)
+ vol = 255;
+
+ init_wave();
+ gVolume = SkToU8(vol);
+
+ if (gWave.hwo)
+ {
+ unsigned long v32 = (vol << 8) | vol; // fill it out to 16bits
+ v32 |= v32 << 16; // set the left and right channels
+ waveOutSetVolume(gWave.hwo, v32);
+ }
+}
+
+#if 0
+unsigned long SoundManager::GetPosition()
+{
+ if (fWave.hwo == nil)
+ return 0;
+ MMTIME time;
+ time.wType = TIME_MS;
+ if (waveOutGetPosition(fWave.hwo, &time, sizeof(time)) == MMSYSERR_NOERROR &&
+ time.wType == TIME_MS)
+ {
+ return time.u.ms;
+ }
+ return 0;
+}
+#endif
+
+MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol)
+{
+ HWAVEOUT hwo = nil;
+// WAVEHDR whdr;
+ MMRESULT mmres = 0;
+// CWaveFile waveFile;
+// HANDLE hDoneEvent = wave.hDoneEvent =
+// CreateEvent(NULL, FALSE, FALSE, TEXT("DONE_EVENT"));
+ UINT devId;
+// DWORD dwOldVolume;
+
+ // Open wave file
+ if (!wave->waveFile.Open(path)) {
+// TCHAR szErrMsg[MAX_ERRMSG];
+// _stprintf(szErrMsg, TEXT("Unable to open file: %s\n"), szWavFile);
+// MessageBox(NULL, szErrMsg, TEXT("File I/O Error"), MB_OK);
+ return MMSYSERR_NOERROR;
+ }
+
+ // Open audio device
+ for (devId = 0; devId < waveOutGetNumDevs(); devId++)
+ {
+ mmres = waveOutOpen(&hwo, devId, wave->waveFile.GetWaveFormat(), 0, 0, CALLBACK_NULL);
+ if (mmres == MMSYSERR_NOERROR)
+ {
+ wave->hwo = hwo;
+ break;
+ }
+ }
+ if (mmres != MMSYSERR_NOERROR)
+ {
+ SkDEBUGCODE(SkDebugf("waveOutOpen(%s) -> %d\n", path, mmres);)
+ return mmres;
+ }
+
+ // Set volume
+ mmres = waveOutGetVolume(hwo, &wave->dwOldVolume);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ waveOutSetVolume(hwo, vol);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ // Initialize wave header
+ ZeroMemory(&wave->whdr, sizeof(WAVEHDR));
+ wave->whdr.lpData = new char[wave->waveFile.GetLength()];
+ wave->whdr.dwBufferLength = wave->waveFile.GetLength();
+ wave->whdr.dwUser = 0;
+ wave->whdr.dwFlags = 0;
+ wave->whdr.dwLoops = 0;
+ wave->whdr.dwBytesRecorded = 0;
+ wave->whdr.lpNext = 0;
+ wave->whdr.reserved = 0;
+
+ // Play buffer
+ wave->waveFile.Read(wave->whdr.lpData, wave->whdr.dwBufferLength);
+
+ mmres = waveOutPrepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR));
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ mmres = waveOutWrite(hwo, &wave->whdr, sizeof(WAVEHDR));
+// if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+// }
+}
+
+#if 0
+void IdleWave(Wave& wave)
+{
+ // Wait for audio to finish playing
+ while (!(wave.whdr.dwFlags & WHDR_DONE)) {
+ WaitForSingleObject(wave.hDoneEvent, INFINITE);
+ }
+}
+#endif
+
+MMRESULT EndWave(SkOSSoundWave* wave)
+{
+ HWAVEOUT hwo = wave->hwo;
+ MMRESULT mmres;
+ // Clean up
+ mmres = waveOutUnprepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR));
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ waveOutSetVolume(hwo, wave->dwOldVolume);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ mmres = waveOutClose(hwo);
+ if (mmres != MMSYSERR_NOERROR) {
+ return mmres;
+ }
+
+ delete [] wave->whdr.lpData;
+ wave->waveFile.Close();
+
+ return MMSYSERR_NOERROR;
+}
+
+#endif /* SK_BUILD_FOR_WIN */
+
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000000..7bf02440f1
--- /dev/null
+++ b/src/views/SkParsePaint.cpp
@@ -0,0 +1,103 @@
+#include "SkParsePaint.h"
+#include "SkTSearch.h"
+#include "SkParse.h"
+#include "SkImageDecoder.h"
+#include "SkGradientShader.h"
+
+static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node)
+{
+ if ((node = dom.getFirstChild(node, "shader")) == nil)
+ return nil;
+
+ const char* str;
+
+ if (dom.hasAttr(node, "type", "linear-gradient"))
+ {
+ SkColor colors[2];
+ SkPoint pts[2];
+
+ colors[0] = colors[1] = SK_ColorBLACK; // need to initialized the alpha to opaque, since FindColor doesn't set it
+ if ((str = dom.findAttr(node, "c0")) != nil &&
+ SkParse::FindColor(str, &colors[0]) &&
+ (str = dom.findAttr(node, "c1")) != nil &&
+ SkParse::FindColor(str, &colors[1]) &&
+ dom.findScalars(node, "p0", &pts[0].fX, 2) &&
+ dom.findScalars(node, "p1", &pts[1].fX, 2))
+ {
+ SkShader::TileMode mode = SkShader::kClamp_TileMode;
+ int index;
+
+ if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+ mode = (SkShader::TileMode)index;
+ return SkGradientShader::CreateLinear(pts, colors, nil, 2, mode);
+ }
+ }
+ else if (dom.hasAttr(node, "type", "bitmap"))
+ {
+ if ((str = dom.findAttr(node, "src")) == nil)
+ return nil;
+
+ SkBitmap bm;
+
+ if (SkImageDecoder::DecodeFile(str, &bm))
+ {
+ SkShader::TileMode mode = SkShader::kRepeat_TileMode;
+ int index;
+
+ if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+ mode = (SkShader::TileMode)index;
+
+ return SkShader::CreateBitmapShader(bm, mode, mode);
+ }
+ }
+ return nil;
+}
+
+void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(paint);
+ SkASSERT(&dom);
+ SkASSERT(node);
+
+ SkScalar x;
+
+ if (dom.findScalar(node, "stroke-width", &x))
+ paint->setStrokeWidth(x);
+ if (dom.findScalar(node, "text-size", &x))
+ paint->setTextSize(x);
+
+ bool b;
+
+ SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b));
+
+ if (dom.findBool(node, "is-stroke", &b))
+ paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+ if (dom.findBool(node, "is-antialias", &b))
+ paint->setAntiAlias(b);
+ if (dom.findBool(node, "is-lineartext", &b))
+ paint->setLinearText(b);
+
+ const char* str = dom.findAttr(node, "color");
+ if (str)
+ {
+ SkColor c = paint->getColor();
+ if (SkParse::FindColor(str, &c))
+ paint->setColor(c);
+ }
+
+ // do this AFTER parsing for the color
+ if (dom.findScalar(node, "opacity", &x))
+ {
+ x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1));
+ paint->setAlpha(SkScalarRound(x * 255));
+ }
+
+ int index = dom.findList(node, "text-anchor", "left,center,right");
+ if (index >= 0)
+ paint->setTextAlign((SkPaint::Align)index);
+
+ SkShader* shader = inflate_shader(dom, node);
+ if (shader)
+ paint->setShader(shader)->unref();
+}
+
diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp
new file mode 100644
index 0000000000..21349a5500
--- /dev/null
+++ b/src/views/SkProgressBarView.cpp
@@ -0,0 +1,102 @@
+#include "SkProgressBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkTime.h"
+#include "SkSystemEventTypes.h"
+
+SkProgressBarView::SkProgressBarView()
+{
+ init_skin_anim(kProgress_SkinEnum, &fAnim);
+ fAnim.setHostEventSink(this);
+ fProgress = 0;
+ fMax = 100;
+
+}
+
+void SkProgressBarView::changeProgress(int diff)
+{
+ int newProg = fProgress + diff;
+ if (newProg > 0 && newProg < fMax)
+ this->setProgress(newProg);
+ //otherwise i'll just leave it as it is
+ //this implies that if a new max and progress are set, max must be set first
+}
+
+/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(nil);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar height;
+
+ if (evt.findScalar("y", &height))
+ this->setHeight(height);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+ int32_t temp;
+ if (dom.findS32(node, "max", &temp))
+ this->setMax(temp);
+ if (dom.findS32(node, "progress", &temp))
+ this->setProgress(temp);
+}
+
+/*virtual*/ void SkProgressBarView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+void SkProgressBarView::reset()
+{
+ fProgress = 0;
+ SkEvent e("user");
+ e.setString("id", "reset");
+ fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setMax(int max)
+{
+ fMax = max;
+ SkEvent e("user");
+ e.setString("id", "setMax");
+ e.setS32("newMax", max);
+ fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setProgress(int progress)
+{
+ fProgress = progress;
+ SkEvent e("user");
+ e.setString("id", "setProgress");
+ e.setS32("newProgress", progress);
+ fAnim.doUserEvent(e);
+}
diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp
new file mode 100644
index 0000000000..a34c284361
--- /dev/null
+++ b/src/views/SkProgressView.cpp
@@ -0,0 +1,125 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL)
+{
+ fValue = 0;
+ fMax = 0;
+ fInterp = NULL;
+ fDoInterp = false;
+}
+
+SkProgressView::~SkProgressView()
+{
+ delete fInterp;
+ fOnShader->safeUnref();
+ fOffShader->safeUnref();
+}
+
+void SkProgressView::setMax(U16CPU max)
+{
+ if (fMax != max)
+ {
+ fMax = SkToU16(max);
+ if (fValue > 0)
+ this->inval(NULL);
+ }
+}
+
+void SkProgressView::setValue(U16CPU value)
+{
+ if (fValue != value)
+ {
+ if (fDoInterp)
+ {
+ if (fInterp)
+ delete fInterp;
+ fInterp = new SkInterpolator(1, 2);
+ SkScalar x = (SkScalar)(fValue << 8);
+ fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0);
+ x = (SkScalar)(value << 8);
+ fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x);
+ }
+ fValue = SkToU16(value);
+ this->inval(NULL);
+ }
+}
+
+void SkProgressView::onDraw(SkCanvas* canvas)
+{
+ if (fMax == 0)
+ return;
+
+ SkFixed percent;
+
+ if (fInterp)
+ {
+ SkScalar x;
+ if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result)
+ {
+ delete fInterp;
+ fInterp = NULL;
+ }
+ percent = (SkFixed)x; // now its 16.8
+ percent = SkMax32(0, SkMin32(percent, fMax << 8)); // now its pinned
+ percent = SkFixedDiv(percent, fMax << 8); // now its 0.16
+ this->inval(NULL);
+ }
+ else
+ {
+ U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+ percent = SkFixedDiv(value, fMax);
+ }
+
+
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+ p.setAntiAlias(true);
+
+ r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+ p.setStyle(SkPaint::kFill_Style);
+
+ p.setColor(SK_ColorDKGRAY);
+ p.setShader(fOnShader);
+ canvas->drawRect(r, p);
+
+ p.setColor(SK_ColorWHITE);
+ p.setShader(fOffShader);
+ r.fLeft = r.fRight;
+ r.fRight = this->width() - SK_Scalar1;
+ if (r.width() > 0)
+ canvas->drawRect(r, p);
+}
+
+#include "SkImageDecoder.h"
+
+static SkShader* inflate_shader(const char file[])
+{
+ SkBitmap bm;
+
+ return SkImageDecoder::DecodeFile(file, &bm) ?
+ SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) :
+ NULL;
+}
+
+void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* s;
+
+ SkASSERT(fOnShader == NULL);
+ SkASSERT(fOffShader == NULL);
+
+ if ((s = dom.findAttr(node, "src-on")) != NULL)
+ fOnShader = inflate_shader(s);
+ if ((s = dom.findAttr(node, "src-off")) != NULL)
+ fOffShader = inflate_shader(s);
+ (void)dom.findBool(node, "do-interp", &fDoInterp);
+}
+
diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp
new file mode 100644
index 0000000000..2e087bd1ae
--- /dev/null
+++ b/src/views/SkScrollBarView.cpp
@@ -0,0 +1,139 @@
+#include "SkScrollBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+//see SkProgressBarView.cpp
+//#include "SkWidgetViews.cpp"
+
+SkScrollBarView::SkScrollBarView()
+{
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kScroll_SkinEnum, &fAnim);
+
+ fTotalLength = 0;
+ fStartPoint = 0;
+ fShownLength = 0;
+
+ this->adjust();
+}
+
+void SkScrollBarView::setStart(unsigned start)
+{
+ if ((int)start < 0)
+ start = 0;
+
+ if (fStartPoint != start)
+ {
+ fStartPoint = start;
+ this->adjust();
+ }
+}
+
+void SkScrollBarView::setShown(unsigned shown)
+{
+ if ((int)shown < 0)
+ shown = 0;
+
+ if (fShownLength != shown)
+ {
+ fShownLength = shown;
+ this->adjust();
+ }
+}
+
+void SkScrollBarView::setTotal(unsigned total)
+{
+ if ((int)total < 0)
+ total = 0;
+
+ if (fTotalLength != total)
+ {
+ fTotalLength = total;
+ this->adjust();
+ }
+}
+
+/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int32_t value;
+ if (dom.findS32(node, "total", &value))
+ this->setTotal(value);
+ if (dom.findS32(node, "shown", &value))
+ this->setShown(value);
+}
+
+/*virtual*/ void SkScrollBarView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(NULL);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar width;
+
+ if (evt.findScalar("x", &width))
+ this->setWidth(width);
+ return true;
+ }
+
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkScrollBarView::adjust()
+{
+ int total = fTotalLength;
+ int start = fStartPoint;
+ int shown = fShownLength;
+ int hideBar = 0;
+
+ if (total <= 0 || shown <= 0 || shown >= total) // no bar to show
+ {
+ total = 1; // avoid divide-by-zero. should be done by skin/script
+ hideBar = 1; // signal we don't want a thumb
+ }
+ else
+ {
+ if (start + shown > total)
+ start = total - shown;
+ }
+
+ SkEvent e("user");
+ e.setString("id", "adjustScrollBar");
+ e.setScalar("_totalLength", SkIntToScalar(total));
+ e.setScalar("_startPoint", SkIntToScalar(start));
+ e.setScalar("_shownLength", SkIntToScalar(shown));
+// e.setS32("hideBar", hideBar);
+ fAnim.doUserEvent(e);
+}
+
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
new file mode 100644
index 0000000000..ae2e9e81a5
--- /dev/null
+++ b/src/views/SkStackViewLayout.cpp
@@ -0,0 +1,262 @@
+#include "SkStackViewLayout.h"
+
+SkStackViewLayout::SkStackViewLayout()
+{
+ fMargin.set(0, 0, 0, 0);
+ fSpacer = 0;
+ fOrient = kHorizontal_Orient;
+ fPack = kStart_Pack;
+ fAlign = kStart_Align;
+ fRound = false;
+}
+
+void SkStackViewLayout::setOrient(Orient ori)
+{
+ SkASSERT((unsigned)ori < kOrientCount);
+ fOrient = SkToU8(ori);
+}
+
+void SkStackViewLayout::getMargin(SkRect* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStackViewLayout::setMargin(const SkRect& margin)
+{
+ fMargin = margin;
+}
+
+void SkStackViewLayout::setSpacer(SkScalar spacer)
+{
+ fSpacer = spacer;
+}
+
+void SkStackViewLayout::setPack(Pack pack)
+{
+ SkASSERT((unsigned)pack < kPackCount);
+ fPack = SkToU8(pack);
+}
+
+void SkStackViewLayout::setAlign(Align align)
+{
+ SkASSERT((unsigned)align < kAlignCount);
+ fAlign = SkToU8(align);
+}
+
+void SkStackViewLayout::setRound(bool r)
+{
+ fRound = SkToU8(r);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
+typedef SkScalar (SkView::*GetSizeProc)() const;
+typedef void (SkView::*SetLocProc)(SkScalar coord);
+typedef void (SkView::*SetSizeProc)(SkScalar coord);
+
+static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
+static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
+static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+
+/* Measure the main-dimension for all the children. If a child is marked flex in that direction
+ ignore its current value but increment the counter for flexChildren
+*/
+static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
+ uint32_t flexMask, int* flexCount)
+{
+ SkView::B2FIter iter(parent);
+ SkView* child;
+ SkScalar limit = 0;
+ int n = 0, flex = 0;
+
+ while ((child = iter.next()) != NULL)
+ {
+ n += 1;
+ if (child->getFlags() & flexMask)
+ flex += 1;
+ else
+ limit += (child->*sizeProc)();
+ }
+ if (count)
+ *count = n;
+ if (flexCount)
+ *flexCount = flex;
+ return limit;
+}
+
+void SkStackViewLayout::onLayoutChildren(SkView* parent)
+{
+ static AlignProc gAlignProcs[] = {
+ left_align_proc,
+ center_align_proc,
+ right_align_proc,
+ fill_align_proc
+ };
+
+ SkScalar startM, endM, crossStartM, crossLimit;
+ GetSizeProc mainGetSizeP, crossGetSizeP;
+ SetLocProc mainLocP, crossLocP;
+ SetSizeProc mainSetSizeP, crossSetSizeP;
+ SkView::Flag_Mask flexMask;
+
+ if (fOrient == kHorizontal_Orient)
+ {
+ startM = fMargin.fLeft;
+ endM = fMargin.fRight;
+ crossStartM = fMargin.fTop;
+ crossLimit = -fMargin.fTop - fMargin.fBottom;
+
+ mainGetSizeP = &SkView::width;
+ crossGetSizeP = &SkView::height;
+ mainLocP = &SkView::setLocX;
+ crossLocP = &SkView::setLocY;
+
+ mainSetSizeP = &SkView::setWidth;
+ crossSetSizeP = &SkView::setHeight;
+
+ flexMask = SkView::kFlexH_Mask;
+ }
+ else
+ {
+ startM = fMargin.fTop;
+ endM = fMargin.fBottom;
+ crossStartM = fMargin.fLeft;
+ crossLimit = -fMargin.fLeft - fMargin.fRight;
+
+ mainGetSizeP = &SkView::height;
+ crossGetSizeP = &SkView::width;
+ mainLocP = &SkView::setLocY;
+ crossLocP = &SkView::setLocX;
+
+ mainSetSizeP = &SkView::setHeight;
+ crossSetSizeP = &SkView::setWidth;
+
+ flexMask = SkView::kFlexV_Mask;
+ }
+ crossLimit += (parent->*crossGetSizeP)();
+ if (fAlign != kStretch_Align)
+ crossSetSizeP = NULL;
+
+ int childCount, flexCount;
+ SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
+
+ if (childCount == 0)
+ return;
+
+ childLimit += (childCount - 1) * fSpacer;
+
+ SkScalar parentLimit = (parent->*mainGetSizeP)() - startM - endM;
+ SkScalar pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
+ SkScalar flexAmount = 0;
+ SkView::B2FIter iter(parent);
+ SkView* child;
+
+ if (flexCount > 0 && parentLimit > childLimit)
+ flexAmount = (parentLimit - childLimit) / flexCount;
+
+ while ((child = iter.next()) != NULL)
+ {
+ if (fRound)
+ pos = SkIntToScalar(SkScalarRound(pos));
+ (child->*mainLocP)(pos);
+ SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
+ if (fRound)
+ crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
+ (child->*crossLocP)(crossLoc);
+
+ if (crossSetSizeP)
+ (child->*crossSetSizeP)(crossLimit);
+ if (child->getFlags() & flexMask)
+ (child->*mainSetSizeP)(flexAmount);
+ pos += (child->*mainGetSizeP)() + fSpacer;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ int index;
+ SkScalar value[4];
+
+ if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
+ this->setOrient((Orient)index);
+ else
+ assert_no_attr(dom, node, "orient");
+
+ if (dom.findScalars(node, "margin", value, 4))
+ {
+ SkRect margin;
+ margin.set(value[0], value[1], value[2], value[3]);
+ this->setMargin(margin);
+ }
+ else
+ assert_no_attr(dom, node, "margin");
+
+ if (dom.findScalar(node, "spacer", value))
+ this->setSpacer(value[0]);
+ else
+ assert_no_attr(dom, node, "spacer");
+
+ if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
+ this->setPack((Pack)index);
+ else
+ assert_no_attr(dom, node, "pack");
+
+ if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
+ this->setAlign((Align)index);
+ else
+ assert_no_attr(dom, node, "align");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+SkFillViewLayout::SkFillViewLayout()
+{
+ fMargin.setEmpty();
+}
+
+void SkFillViewLayout::getMargin(SkRect* r) const
+{
+ if (r)
+ *r = fMargin;
+}
+
+void SkFillViewLayout::setMargin(const SkRect& margin)
+{
+ fMargin = margin;
+}
+
+void SkFillViewLayout::onLayoutChildren(SkView* parent)
+{
+ SkView::B2FIter iter(parent);
+ SkView* child;
+
+ while ((child = iter.next()) != NULL)
+ {
+ child->setLoc(fMargin.fLeft, fMargin.fTop);
+ child->setSize( parent->width() - fMargin.fRight - fMargin.fLeft,
+ parent->height() - fMargin.fBottom - fMargin.fTop);
+ }
+}
+
+void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+ (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
+}
+
diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp
new file mode 100644
index 0000000000..4576ce67c0
--- /dev/null
+++ b/src/views/SkTagList.cpp
@@ -0,0 +1,71 @@
+/* libs/graphics/views/SkTagList.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTagList.h"
+
+SkTagList::~SkTagList()
+{
+}
+
+SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag)
+{
+ SkASSERT(tag < kSkTagListCount);
+
+ while (rec != NULL)
+ {
+ if (rec->fTag == tag)
+ break;
+ rec = rec->fNext;
+ }
+ return rec;
+}
+
+void SkTagList::DeleteTag(SkTagList** head, U8CPU tag)
+{
+ SkASSERT(tag < kSkTagListCount);
+
+ SkTagList* rec = *head;
+ SkTagList* prev = NULL;
+
+ while (rec != NULL)
+ {
+ SkTagList* next = rec->fNext;
+
+ if (rec->fTag == tag)
+ {
+ if (prev)
+ prev->fNext = next;
+ else
+ *head = next;
+ delete rec;
+ break;
+ }
+ prev = rec;
+ rec = next;
+ }
+}
+
+void SkTagList::DeleteAll(SkTagList* rec)
+{
+ while (rec)
+ {
+ SkTagList* next = rec->fNext;
+ delete rec;
+ rec = next;
+ }
+}
+
diff --git a/src/views/SkTagList.h b/src/views/SkTagList.h
new file mode 100644
index 0000000000..5f428e4022
--- /dev/null
+++ b/src/views/SkTagList.h
@@ -0,0 +1,51 @@
+/* libs/graphics/views/SkTagList.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkTagList_DEFINED
+#define SkTagList_DEFINED
+
+#include "SkTypes.h"
+
+enum SkTagListEnum {
+ kListeners_SkTagList,
+ kViewLayout_SkTagList,
+ kViewArtist_SkTagList,
+
+ kSkTagListCount
+};
+
+struct SkTagList {
+ SkTagList* fNext;
+ uint16_t fExtra16;
+ uint8_t fExtra8;
+ uint8_t fTag;
+
+ SkTagList(U8CPU tag) : fTag(SkToU8(tag))
+ {
+ SkASSERT(tag < kSkTagListCount);
+ fNext = NULL;
+ fExtra16 = 0;
+ fExtra8 = 0;
+ }
+ virtual ~SkTagList();
+
+ static SkTagList* Find(SkTagList* head, U8CPU tag);
+ static void DeleteTag(SkTagList** headptr, U8CPU tag);
+ static void DeleteAll(SkTagList* head);
+};
+
+#endif
diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp
new file mode 100644
index 0000000000..657f64805c
--- /dev/null
+++ b/src/views/SkTextBox.cpp
@@ -0,0 +1,216 @@
+/* libs/graphics/views/SkTextBox.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTextBox.h"
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+#include "SkAutoKern.h"
+
+static inline int is_ws(int c)
+{
+ return !((c - 1) >> 5);
+}
+
+static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin)
+{
+ const char* start = text;
+
+ SkAutoGlyphCache ac(paint, NULL);
+ SkGlyphCache* cache = ac.getCache();
+ SkFixed w = 0;
+ SkFixed limit = SkScalarToFixed(margin);
+ SkAutoKern autokern;
+
+ const char* word_start = text;
+ int prevWS = true;
+
+ while (text < stop)
+ {
+ const char* prevText = text;
+ SkUnichar uni = SkUTF8_NextUnichar(&text);
+ int currWS = is_ws(uni);
+ const SkGlyph& glyph = cache->getUnicharMetrics(uni);
+
+ if (!currWS && prevWS)
+ word_start = prevText;
+ prevWS = currWS;
+
+ w += autokern.adjust(glyph) + glyph.fAdvanceX;
+ if (w > limit)
+ {
+ if (currWS) // eat the rest of the whitespace
+ {
+ while (text < stop && is_ws(SkUTF8_ToUnichar(text)))
+ text += SkUTF8_CountUTF8Bytes(text);
+ }
+ else // backup until a whitespace (or 1 char)
+ {
+ if (word_start == start)
+ {
+ if (prevText > start)
+ text = prevText;
+ }
+ else
+ text = word_start;
+ }
+ break;
+ }
+ }
+ return text - start;
+}
+
+int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
+{
+ const char* stop = text + len;
+ int count = 0;
+
+ if (width > 0)
+ {
+ do {
+ count += 1;
+ text += linebreak(text, stop, paint, width);
+ } while (text < stop);
+ }
+ return count;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkTextBox::SkTextBox()
+{
+ fBox.setEmpty();
+ fSpacingMul = SK_Scalar1;
+ fSpacingAdd = 0;
+ fMode = kLineBreak_Mode;
+ fSpacingAlign = kStart_SpacingAlign;
+}
+
+void SkTextBox::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+ fMode = SkToU8(mode);
+}
+
+void SkTextBox::setSpacingAlign(SpacingAlign align)
+{
+ SkASSERT((unsigned)align < kSpacingAlignCount);
+ fSpacingAlign = SkToU8(align);
+}
+
+void SkTextBox::getBox(SkRect* box) const
+{
+ if (box)
+ *box = fBox;
+}
+
+void SkTextBox::setBox(const SkRect& box)
+{
+ fBox = box;
+}
+
+void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+ fBox.set(left, top, right, bottom);
+}
+
+void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
+{
+ if (mul)
+ *mul = fSpacingMul;
+ if (add)
+ *add = fSpacingAdd;
+}
+
+void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
+{
+ fSpacingMul = mul;
+ fSpacingAdd = add;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
+{
+ SkASSERT(canvas && &paint && (text || len == 0));
+
+ SkScalar marginWidth = fBox.width();
+
+ if (marginWidth <= 0 || len == 0)
+ return;
+
+ const char* textStop = text + len;
+
+ SkScalar x, y, scaledSpacing, height, fontHeight;
+ SkPaint::FontMetrics metrics;
+
+ switch (paint.getTextAlign()) {
+ case SkPaint::kLeft_Align:
+ x = 0;
+ break;
+ case SkPaint::kCenter_Align:
+ x = SkScalarHalf(marginWidth);
+ break;
+ default:
+ x = marginWidth;
+ break;
+ }
+ x += fBox.fLeft;
+
+ fontHeight = paint.getFontMetrics(&metrics);
+ scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
+ height = fBox.height();
+
+ // compute Y position for first line
+ {
+ SkScalar textHeight = fontHeight;
+
+ if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
+ {
+ int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
+ SkASSERT(count > 0);
+ textHeight += scaledSpacing * (count - 1);
+ }
+
+ switch (fSpacingAlign) {
+ case kStart_SpacingAlign:
+ y = 0;
+ break;
+ case kCenter_SpacingAlign:
+ y = SkScalarHalf(height - textHeight);
+ break;
+ default:
+ SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
+ y = height - textHeight;
+ break;
+ }
+ y += fBox.fTop - metrics.fAscent;
+ }
+
+ for (;;)
+ {
+ len = linebreak(text, textStop, paint, marginWidth);
+ if (y + metrics.fDescent + metrics.fLeading > 0)
+ canvas->drawText(text, len, x, y, paint);
+ text += len;
+ if (text >= textStop)
+ break;
+ y += scaledSpacing;
+ if (y + metrics.fAscent >= height)
+ break;
+ }
+}
+
diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp
new file mode 100644
index 0000000000..a027744c69
--- /dev/null
+++ b/src/views/SkView.cpp
@@ -0,0 +1,760 @@
+#include "SkView.h"
+#include "SkCanvas.h"
+
+////////////////////////////////////////////////////////////////////////
+
+SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
+{
+ fWidth = fHeight = 0;
+ fLoc.set(0, 0);
+ fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
+
+ fContainsFocus = 0;
+}
+
+SkView::~SkView()
+{
+ this->detachAllChildren();
+}
+
+void SkView::setFlags(uint32_t flags)
+{
+ SkASSERT((flags & ~kAllFlagMasks) == 0);
+
+ uint32_t diff = fFlags ^ flags;
+
+ if (diff & kVisible_Mask)
+ this->inval(NULL);
+
+ fFlags = SkToU8(flags);
+
+ if (diff & kVisible_Mask)
+ {
+ this->inval(NULL);
+ }
+}
+
+void SkView::setVisibleP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
+}
+
+void SkView::setEnabledP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
+}
+
+void SkView::setFocusableP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
+}
+
+void SkView::setSize(SkScalar width, SkScalar height)
+{
+ width = SkMaxScalar(0, width);
+ height = SkMaxScalar(0, height);
+
+ if (fWidth != width || fHeight != height)
+ {
+ this->inval(NULL);
+ fWidth = width;
+ fHeight = height;
+ this->inval(NULL);
+ this->onSizeChange();
+ this->invokeLayout();
+ }
+}
+
+void SkView::setLoc(SkScalar x, SkScalar y)
+{
+ if (fLoc.fX != x || fLoc.fY != y)
+ {
+ this->inval(NULL);
+ fLoc.set(x, y);
+ this->inval(NULL);
+ }
+}
+
+void SkView::offset(SkScalar dx, SkScalar dy)
+{
+ if (dx || dy)
+ this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
+}
+
+void SkView::draw(SkCanvas* canvas)
+{
+ if (fWidth && fHeight && this->isVisible())
+ {
+ SkRect r;
+ r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
+ if (canvas->quickReject(r, SkCanvas::kBW_EdgeType))
+ return;
+
+ SkAutoCanvasRestore as(canvas, true);
+
+ canvas->clipRect(r);
+ canvas->translate(fLoc.fX, fLoc.fY);
+
+ this->onDraw(canvas);
+
+ B2FIter iter(this);
+ SkView* child;
+
+ SkCanvas* childCanvas = this->beforeChildren(canvas);
+
+ while ((child = iter.next()) != NULL)
+ child->draw(childCanvas);
+
+ this->afterChildren(canvas);
+ }
+}
+
+void SkView::inval(SkRect* rect)
+{
+ if (!this->isVisible())
+ return;
+
+ SkRect bounds;
+
+ this->getLocalBounds(&bounds);
+ if (rect && !bounds.intersect(*rect))
+ return;
+
+ rect = &bounds;
+ SkView* view = this;
+
+ for (;;)
+ {
+ if (view->handleInval(bounds))
+ break;
+
+ SkRect parentR;
+ SkView* parent = view->fParent;
+
+ if (parent == NULL || !parent->isVisible())
+ break;
+
+ bounds.offset(view->fLoc.fX, view->fLoc.fY);
+ parent->getLocalBounds(&parentR);
+ if (!bounds.intersect(parentR))
+ return;
+
+ view = parent;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+bool SkView::setFocusView(SkView* fv)
+{
+ SkView* view = this;
+
+ do {
+ if (view->onSetFocusView(fv))
+ return true;
+ } while ((view = view->fParent) != NULL);
+ return false;
+}
+
+SkView* SkView::getFocusView() const
+{
+ SkView* focus = NULL;
+ const SkView* view = this;
+ do {
+ if (view->onGetFocusView(&focus))
+ break;
+ } while ((view = view->fParent) != NULL);
+ return focus;
+}
+
+bool SkView::hasFocus() const
+{
+ return this == this->getFocusView();
+}
+
+bool SkView::acceptFocus()
+{
+ return this->isFocusable() && this->setFocusView(this);
+}
+
+/*
+ Try to give focus to this view, or its children
+*/
+SkView* SkView::acceptFocus(FocusDirection dir)
+{
+ if (dir == kNext_FocusDirection)
+ {
+ if (this->acceptFocus())
+ return this;
+
+ B2FIter iter(this);
+ SkView* child, *focus;
+ while ((child = iter.next()) != NULL)
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ }
+ else // prev
+ {
+ F2BIter iter(this);
+ SkView* child, *focus;
+ while ((child = iter.next()) != NULL)
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+
+ if (this->acceptFocus())
+ return this;
+ }
+
+ return NULL;
+}
+
+SkView* SkView::moveFocus(FocusDirection dir)
+{
+ SkView* focus = this->getFocusView();
+
+ if (focus == NULL)
+ { // start with the root
+ focus = this;
+ while (focus->fParent)
+ focus = focus->fParent;
+ }
+
+ SkView* child, *parent;
+
+ if (dir == kNext_FocusDirection)
+ {
+ parent = focus;
+ child = focus->fFirstChild;
+ if (child)
+ goto FIRST_CHILD;
+ else
+ goto NEXT_SIB;
+
+ do {
+ while (child != parent->fFirstChild)
+ {
+ FIRST_CHILD:
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ child = child->fNextSibling;
+ }
+ NEXT_SIB:
+ child = parent->fNextSibling;
+ parent = parent->fParent;
+ } while (parent != NULL);
+ }
+ else // prevfocus
+ {
+ parent = focus->fParent;
+ if (parent == NULL) // we're the root
+ return focus->acceptFocus(dir);
+ else
+ {
+ child = focus;
+ while (parent)
+ {
+ while (child != parent->fFirstChild)
+ {
+ child = child->fPrevSibling;
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ }
+ if (parent->acceptFocus())
+ return parent;
+
+ child = parent;
+ parent = parent->fParent;
+ }
+ }
+ }
+ return NULL;
+}
+
+void SkView::onFocusChange(bool gainFocusP)
+{
+ this->inval(NULL);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkView::Click::Click(SkView* target)
+{
+ SkASSERT(target);
+ fTargetID = target->getSinkID();
+ fType = NULL;
+ fWeOwnTheType = false;
+}
+
+SkView::Click::~Click()
+{
+ this->resetType();
+}
+
+void SkView::Click::resetType()
+{
+ if (fWeOwnTheType)
+ {
+ sk_free(fType);
+ fWeOwnTheType = false;
+ }
+ fType = NULL;
+}
+
+bool SkView::Click::isType(const char type[]) const
+{
+ const char* t = fType;
+
+ if (type == t)
+ return true;
+
+ if (type == NULL)
+ type = "";
+ if (t == NULL)
+ t = "";
+ return !strcmp(t, type);
+}
+
+void SkView::Click::setType(const char type[])
+{
+ this->resetType();
+ fType = (char*)type;
+}
+
+void SkView::Click::copyType(const char type[])
+{
+ if (fType != type)
+ {
+ this->resetType();
+ if (type)
+ {
+ size_t len = strlen(type) + 1;
+ fType = (char*)sk_malloc_throw(len);
+ memcpy(fType, type, len);
+ fWeOwnTheType = true;
+ }
+ }
+}
+
+SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
+{
+ if (x < 0 || y < 0 || x >= fWidth || y >= fHeight)
+ return false;
+
+ F2BIter iter(this);
+ SkView* child;
+
+ while ((child = iter.next()) != NULL)
+ {
+ Click* click = child->findClickHandler(x - child->fLoc.fX, y - child->fLoc.fY);
+ if (click)
+ return click;
+ }
+ return this->onFindClickHandler(x, y);
+}
+
+void SkView::DoClickDown(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIOrig.set(x, y);
+ click->fICurr = click->fIPrev = click->fIOrig;
+
+ click->fOrig.iset(x, y);
+ target->globalToLocal(&click->fOrig);
+ click->fPrev = click->fCurr = click->fOrig;
+
+ click->fState = Click::kDown_State;
+ target->onClick(click);
+}
+
+void SkView::DoClickMoved(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIPrev = click->fICurr;
+ click->fICurr.set(x, y);
+
+ click->fPrev = click->fCurr;
+ click->fCurr.iset(x, y);
+ target->globalToLocal(&click->fCurr);
+
+ click->fState = Click::kMoved_State;
+ target->onClick(click);
+}
+
+void SkView::DoClickUp(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIPrev = click->fICurr;
+ click->fICurr.set(x, y);
+
+ click->fPrev = click->fCurr;
+ click->fCurr.iset(x, y);
+ target->globalToLocal(&click->fCurr);
+
+ click->fState = Click::kUp_State;
+ target->onClick(click);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::invokeLayout()
+{
+ SkView::Layout* layout = this->getLayout();
+
+ if (layout)
+ layout->layoutChildren(this);
+}
+
+void SkView::onDraw(SkCanvas* canvas)
+{
+ Artist* artist = this->getArtist();
+
+ if (artist)
+ artist->draw(this, canvas);
+}
+
+void SkView::onSizeChange()
+{
+}
+
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ return NULL;
+}
+
+bool SkView::onClick(Click*)
+{
+ return false;
+}
+
+bool SkView::handleInval(const SkRect& r)
+{
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::getLocalBounds(SkRect* bounds) const
+{
+ if (bounds)
+ bounds->set(0, 0, fWidth, fHeight);
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+void SkView::detachFromParent_NoLayout()
+{
+ if (fParent == NULL)
+ return;
+
+ if (fContainsFocus)
+ (void)this->setFocusView(NULL);
+
+ this->inval(NULL);
+
+ SkView* next = NULL;
+
+ if (fNextSibling != this) // do we have any siblings
+ {
+ fNextSibling->fPrevSibling = fPrevSibling;
+ fPrevSibling->fNextSibling = fNextSibling;
+ next = fNextSibling;
+ }
+
+ if (fParent->fFirstChild == this)
+ fParent->fFirstChild = next;
+
+ fParent = fNextSibling = fPrevSibling = NULL;
+
+ this->unref();
+}
+
+void SkView::detachFromParent()
+{
+ SkView* parent = fParent;
+
+ if (parent)
+ {
+ this->detachFromParent_NoLayout();
+ parent->invokeLayout();
+ }
+}
+
+SkView* SkView::attachChildToBack(SkView* child)
+{
+ SkASSERT(child != this);
+
+ if (child == NULL || fFirstChild == child)
+ goto DONE;
+
+ child->ref();
+ child->detachFromParent_NoLayout();
+
+ if (fFirstChild == NULL)
+ {
+ child->fNextSibling = child;
+ child->fPrevSibling = child;
+ }
+ else
+ {
+ child->fNextSibling = fFirstChild;
+ child->fPrevSibling = fFirstChild->fPrevSibling;
+ fFirstChild->fPrevSibling->fNextSibling = child;
+ fFirstChild->fPrevSibling = child;
+ }
+
+ fFirstChild = child;
+ child->fParent = this;
+ child->inval(NULL);
+
+ this->invokeLayout();
+DONE:
+ return child;
+}
+
+SkView* SkView::attachChildToFront(SkView* child)
+{
+ SkASSERT(child != this);
+
+ if (child == NULL || fFirstChild && fFirstChild->fPrevSibling == child)
+ goto DONE;
+
+ child->ref();
+ child->detachFromParent_NoLayout();
+
+ if (fFirstChild == NULL)
+ {
+ fFirstChild = child;
+ child->fNextSibling = child;
+ child->fPrevSibling = child;
+ }
+ else
+ {
+ child->fNextSibling = fFirstChild;
+ child->fPrevSibling = fFirstChild->fPrevSibling;
+ fFirstChild->fPrevSibling->fNextSibling = child;
+ fFirstChild->fPrevSibling = child;
+ }
+
+ child->fParent = this;
+ child->inval(NULL);
+
+ this->invokeLayout();
+DONE:
+ return child;
+}
+
+void SkView::detachAllChildren()
+{
+ while (fFirstChild)
+ fFirstChild->detachFromParent_NoLayout();
+}
+
+void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+{
+ SkASSERT(this);
+
+ if (local)
+ {
+ const SkView* view = this;
+ while (view)
+ {
+ x -= view->fLoc.fX;
+ y -= view->fLoc.fY;
+ view = view->fParent;
+ }
+ local->set(x, y);
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+
+/* Even if the subclass overrides onInflate, they should always be
+ sure to call the inherited method, so that we get called.
+*/
+void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkScalar x, y;
+
+ x = this->locX();
+ y = this->locY();
+ (void)dom.findScalar(node, "x", &x);
+ (void)dom.findScalar(node, "y", &y);
+ this->setLoc(x, y);
+
+ x = this->width();
+ y = this->height();
+ (void)dom.findScalar(node, "width", &x);
+ (void)dom.findScalar(node, "height", &y);
+ this->setSize(x, y);
+
+ // inflate the flags
+
+ static const char* gFlagNames[] = {
+ "visible", "enabled", "focusable", "flexH", "flexV"
+ };
+ SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
+
+ bool b;
+ uint32_t flags = this->getFlags();
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
+ if (dom.findBool(node, gFlagNames[i], &b))
+ flags = SkSetClearShift(flags, b, i);
+ this->setFlags(flags);
+}
+
+void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->onInflate(dom, node);
+}
+
+void SkView::onPostInflate(const SkTDict<SkView*>&)
+{
+ // override in subclass as needed
+}
+
+void SkView::postInflate(const SkTDict<SkView*>& dict)
+{
+ this->onPostInflate(dict);
+
+ B2FIter iter(this);
+ SkView* child;
+ while ((child = iter.next()) != NULL)
+ child->postInflate(dict);
+}
+
+//////////////////////////////////////////////////////////////////
+
+SkView* SkView::sendEventToParents(const SkEvent& evt)
+{
+ SkView* parent = fParent;
+
+ while (parent)
+ {
+ if (parent->doEvent(evt))
+ return parent;
+ parent = parent->fParent;
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkView::F2BIter::F2BIter(const SkView* parent)
+{
+ fFirstChild = parent ? parent->fFirstChild : NULL;
+ fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
+}
+
+SkView* SkView::F2BIter::next()
+{
+ SkView* curr = fChild;
+
+ if (fChild)
+ {
+ if (fChild == fFirstChild)
+ fChild = NULL;
+ else
+ fChild = fChild->fPrevSibling;
+ }
+ return curr;
+}
+
+SkView::B2FIter::B2FIter(const SkView* parent)
+{
+ fFirstChild = parent ? parent->fFirstChild : NULL;
+ fChild = fFirstChild;
+}
+
+SkView* SkView::B2FIter::next()
+{
+ SkView* curr = fChild;
+
+ if (fChild)
+ {
+ SkView* next = fChild->fNextSibling;
+ if (next == fFirstChild)
+ next = NULL;
+ fChild = next;
+ }
+ return curr;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static inline void show_if_nonzero(const char name[], SkScalar value)
+{
+ if (value)
+ SkDebugf("%s=\"%g\"", name, value/65536.);
+}
+
+static void tab(int level)
+{
+ for (int i = 0; i < level; i++)
+ SkDebugf(" ");
+}
+
+static void dumpview(const SkView* view, int level, bool recurse)
+{
+ tab(level);
+
+ SkDebugf("<view");
+ show_if_nonzero(" x", view->locX());
+ show_if_nonzero(" y", view->locY());
+ show_if_nonzero(" width", view->width());
+ show_if_nonzero(" height", view->height());
+
+ if (recurse)
+ {
+ SkView::B2FIter iter(view);
+ SkView* child;
+ bool noChildren = true;
+
+ while ((child = iter.next()) != NULL)
+ {
+ if (noChildren)
+ SkDebugf(">\n");
+ noChildren = false;
+ dumpview(child, level + 1, true);
+ }
+
+ if (!noChildren)
+ {
+ tab(level);
+ SkDebugf("</view>\n");
+ }
+ else
+ goto ONELINER;
+ }
+ else
+ {
+ ONELINER:
+ SkDebugf(" />\n");
+ }
+}
+
+void SkView::dump(bool recurse) const
+{
+ dumpview(this, 0, recurse);
+}
+
+#endif
diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp
new file mode 100644
index 0000000000..8f5489de54
--- /dev/null
+++ b/src/views/SkViewInflate.cpp
@@ -0,0 +1,139 @@
+#include "SkViewInflate.h"
+#include "SkView.h"
+#include <stdio.h>
+
+SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc)
+{
+}
+
+SkViewInflate::~SkViewInflate()
+{
+}
+
+void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent)
+{
+ const char* str = dom.findAttr(node, "id");
+ if (str)
+ fIDs.set(str, parent);
+
+ const SkDOM::Node* child = dom.getFirstChild(node);
+ while (child)
+ {
+ SkView* view = this->createView(dom, child);
+ if (view)
+ {
+ this->rInflate(dom, child, view);
+ parent->attachChildToFront(view)->unref();
+ }
+ else
+ {
+ const char* name = dom.getName(child);
+ const char* target;
+
+ if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL)
+ this->addIDStr(&fListenTo, parent, target);
+
+ if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL)
+ this->addIDStr(&fBroadcastTo, parent, target);
+ }
+ child = dom.getNextSibling(child);
+ }
+
+ parent->setVisibleP(true);
+ this->inflateView(parent, dom, node);
+}
+
+void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node)
+{
+ // called after all of view's children have been instantiated.
+ // this may be overridden by a subclass, to load in layout or other helpers
+ // they should call through to us (INHERITED) before or after their patch
+ view->inflate(dom, node);
+}
+
+SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root)
+{
+ fIDs.reset();
+
+ if (root == NULL)
+ {
+ root = this->createView(dom, node);
+ if (root == NULL)
+ {
+ printf("createView returned NULL on <%s>\n", dom.getName(node));
+ return NULL;
+ }
+ }
+ this->rInflate(dom, node, root);
+
+ // resolve listeners and broadcasters
+ {
+ SkView* target;
+ const IDStr* iter = fListenTo.begin();
+ const IDStr* stop = fListenTo.end();
+ for (; iter < stop; iter++)
+ {
+ if (fIDs.find(iter->fStr, &target))
+ target->addListenerID(iter->fView->getSinkID());
+ }
+
+ iter = fBroadcastTo.begin();
+ stop = fBroadcastTo.end();
+ for (; iter < stop; iter++)
+ {
+ if (fIDs.find(iter->fStr, &target))
+ iter->fView->addListenerID(target->getSinkID());
+ }
+ }
+
+ // now that the tree is built, give everyone a shot at the ID dict
+ root->postInflate(fIDs);
+ return root;
+}
+
+SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root)
+{
+ SkDOM dom;
+ const SkDOM::Node* node = dom.build(xml, len);
+
+ return node ? this->inflate(dom, node, root) : NULL;
+}
+
+SkView* SkViewInflate::findViewByID(const char id[]) const
+{
+ SkASSERT(id);
+ SkView* view;
+ return fIDs.find(id, &view) ? view : NULL;
+}
+
+SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node)
+{
+ if (!strcmp(dom.getName(node), "view"))
+ return new SkView;
+ return NULL;
+}
+
+void SkViewInflate::addIDStr(SkTDArray<IDStr>* list, SkView* view, const char* str)
+{
+ size_t len = strlen(str) + 1;
+ IDStr* pair = list->append();
+ pair->fView = view;
+ pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType);
+ memcpy(pair->fStr, str, len);
+}
+
+#ifdef SK_DEBUG
+void SkViewInflate::dump() const
+{
+ const IDStr* iter = fListenTo.begin();
+ const IDStr* stop = fListenTo.end();
+ for (; iter < stop; iter++)
+ SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr);
+
+ iter = fBroadcastTo.begin();
+ stop = fBroadcastTo.end();
+ for (; iter < stop; iter++)
+ SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
+}
+#endif
+
diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp
new file mode 100644
index 0000000000..b03ca8c101
--- /dev/null
+++ b/src/views/SkViewPriv.cpp
@@ -0,0 +1,97 @@
+#include "SkViewPriv.h"
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::Artist::draw(SkView* view, SkCanvas* canvas)
+{
+ SkASSERT(view && canvas);
+ this->onDraw(view, canvas);
+}
+
+void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(&dom && node);
+ this->onInflate(dom, node);
+}
+
+void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ // subclass should override this as needed
+}
+
+SkView::Artist* SkView::getArtist() const
+{
+ Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+ SkASSERT(rec == NULL || rec->fArtist != NULL);
+
+ return rec ? rec->fArtist : NULL;
+}
+
+SkView::Artist* SkView::setArtist(Artist* obj)
+{
+ if (obj == NULL)
+ {
+ this->removeTagList(kViewArtist_SkTagList);
+ }
+ else // add/replace
+ {
+ Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+
+ if (rec)
+ SkRefCnt_SafeAssign(rec->fArtist, obj);
+ else
+ this->addTagList(new Artist_SkTagList(obj));
+ }
+ return obj;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void SkView::Layout::layoutChildren(SkView* parent)
+{
+ SkASSERT(parent);
+ if (parent->width() > 0 && parent->height() > 0)
+ this->onLayoutChildren(parent);
+}
+
+void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(&dom && node);
+ this->onInflate(dom, node);
+}
+
+void SkView::Layout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ // subclass should override this as needed
+}
+
+SkView::Layout* SkView::getLayout() const
+{
+ Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+ SkASSERT(rec == NULL || rec->fLayout != NULL);
+
+ return rec ? rec->fLayout : NULL;
+}
+
+SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow)
+{
+ if (obj == NULL)
+ {
+ this->removeTagList(kViewLayout_SkTagList);
+ }
+ else // add/replace
+ {
+ Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+
+ if (rec)
+ SkRefCnt_SafeAssign(rec->fLayout, obj);
+ else
+ this->addTagList(new Layout_SkTagList(obj));
+ }
+
+ if (invokeLayoutNow)
+ this->invokeLayout();
+
+ return obj;
+}
+
diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h
new file mode 100644
index 0000000000..06ce59bec6
--- /dev/null
+++ b/src/views/SkViewPriv.h
@@ -0,0 +1,38 @@
+#ifndef SkViewPriv_DEFINED
+#define SkViewPriv_DEFINED
+
+#include "SkView.h"
+#include "SkTagList.h"
+
+struct Layout_SkTagList : SkTagList {
+ SkView::Layout* fLayout;
+
+ Layout_SkTagList(SkView::Layout* layout)
+ : SkTagList(kViewLayout_SkTagList), fLayout(layout)
+ {
+ SkASSERT(layout);
+ layout->ref();
+ }
+ virtual ~Layout_SkTagList()
+ {
+ fLayout->unref();
+ }
+};
+
+struct Artist_SkTagList : SkTagList {
+ SkView::Artist* fArtist;
+
+ Artist_SkTagList(SkView::Artist* artist)
+ : SkTagList(kViewArtist_SkTagList), fArtist(artist)
+ {
+ SkASSERT(artist);
+ artist->ref();
+ }
+ virtual ~Artist_SkTagList()
+ {
+ fArtist->unref();
+ }
+};
+
+#endif
+
diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp
new file mode 100644
index 0000000000..8d945f171a
--- /dev/null
+++ b/src/views/SkWidget.cpp
@@ -0,0 +1,323 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+#include "SkParsePaint.h"
+
+#if 0
+SkWidgetView::SkWidgetView(U32 flags) : SkView(flags)
+{
+}
+
+SkWidgetView::~SkWidgetView()
+{
+}
+
+const char* SkWidgetView::GetEventType()
+{
+ return "SkWidgetView";
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+class SkTextView::Interp {
+public:
+ Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2)
+ {
+ SkScalar x = 0;
+ fInterp.setKeyFrame(0, now, &x, 0);
+ x = SK_Scalar1;
+ if (dir == kBackward_AnimDir)
+ x = -x;
+ fInterp.setKeyFrame(1, now + dur, &x);
+ }
+ bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint)
+ {
+ SkScalar scale;
+
+ if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result)
+ {
+ canvas->drawText(newText.c_str(), newText.size(), x, y, paint);
+ return false;
+ }
+ else
+ {
+ U8 alpha = paint.getAlpha();
+ SkScalar above, below;
+ (void)paint.measureText(nil, 0, &above, &below);
+ SkScalar height = below - above;
+ SkScalar dy = SkScalarMul(height, scale);
+ if (scale < 0)
+ height = -height;
+
+ // draw the old
+ paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale)));
+ canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint);
+ // draw the new
+ paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale)));
+ canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint);
+ // restore the paint
+ paint.setAlpha(alpha);
+ return true;
+ }
+ }
+
+private:
+ SkString fOldText;
+ SkInterpolator fInterp;
+};
+
+SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(nil), fDoInterp(false)
+{
+ fMargin.set(0, 0);
+}
+
+SkTextView::~SkTextView()
+{
+ delete fInterp;
+}
+
+void SkTextView::getText(SkString* str) const
+{
+ if (str)
+ str->set(fText);
+}
+
+void SkTextView::setText(const char text[], AnimaDir dir)
+{
+ if (!fText.equals(text))
+ {
+ SkString tmp(text);
+ this->privSetText(tmp, dir);
+ }
+}
+
+void SkTextView::setText(const char text[], size_t len, AnimaDir dir)
+{
+ if (!fText.equals(text))
+ {
+ SkString tmp(text, len);
+ this->privSetText(tmp, dir);
+ }
+}
+
+void SkTextView::setText(const SkString& src, AnimaDir dir)
+{
+ if (fText != src)
+ this->privSetText(src, dir);
+}
+
+void SkTextView::privSetText(const SkString& src, AnimaDir dir)
+{
+ SkASSERT(fText != src);
+
+ if (fDoInterp)
+ {
+ if (fInterp)
+ delete fInterp;
+ fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir);
+ }
+ fText = src;
+ this->inval(nil);
+}
+
+/////////////////////////////////////////////////////////////////
+
+void SkTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkTextView::setMargin(const SkPoint& margin)
+{
+ if (fMargin != margin)
+ {
+ fMargin = margin;
+ this->inval(nil);
+ }
+}
+
+void SkTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.size() == 0)
+ return;
+
+ SkPaint::Align align = fPaint.getTextAlign();
+ SkScalar x, y;
+
+ switch (align) {
+ case SkPaint::kLeft_Align:
+ x = fMargin.fX;
+ break;
+ case SkPaint::kCenter_Align:
+ x = SkScalarHalf(this->width());
+ break;
+ default:
+ SkASSERT(align == SkPaint::kRight_Align);
+ x = this->width() - fMargin.fX;
+ break;
+ }
+
+ fPaint.measureText(nil, 0, &y, nil);
+ y = fMargin.fY - y;
+
+ if (fInterp)
+ {
+ if (fInterp->draw(canvas, fText, x, y, fPaint))
+ this->inval(nil);
+ else
+ {
+ delete fInterp;
+ fInterp = nil;
+ }
+ }
+ else
+ canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ SkPoint margin;
+ if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2))
+ this->setMargin(margin);
+ (void)dom.findBool(node, "do-interp", &fDoInterp);
+
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags)
+{
+ fValue = 0;
+ fMax = 0;
+}
+
+static U16 actual_value(U16CPU value, U16CPU max)
+{
+ return SkToU16(SkMax32(0, SkMin32(value, max)));
+}
+
+void SkSliderView::setMax(U16CPU max)
+{
+ if (fMax != max)
+ {
+ fMax = SkToU16(max);
+ if (fValue > 0)
+ this->inval(nil);
+ }
+}
+
+void SkSliderView::setValue(U16CPU value)
+{
+ if (fValue != value)
+ {
+ U16 prev = actual_value(fValue, fMax);
+ U16 next = actual_value(value, fMax);
+
+ fValue = SkToU16(value);
+ if (prev != next)
+ {
+ this->inval(nil);
+
+ if (this->hasListeners())
+ {
+ SkEvent evt;
+
+ evt.setType(SkWidgetView::GetEventType());
+ evt.setFast32(this->getSinkID());
+ evt.setS32("sliderValue", next);
+ this->postToListeners(evt);
+ }
+ }
+ }
+}
+
+#include "SkGradientShader.h"
+
+static void setgrad(SkPaint* paint, const SkRect& r)
+{
+ SkPoint pts[2];
+ SkColor colors[2];
+
+#if 0
+ pts[0].set(r.fLeft, r.fTop);
+ pts[1].set(r.fLeft + r.height(), r.fBottom);
+#else
+ pts[0].set(r.fRight, r.fBottom);
+ pts[1].set(r.fRight - r.height(), r.fTop);
+#endif
+ colors[0] = SK_ColorBLUE;
+ colors[1] = SK_ColorWHITE;
+
+ paint->setShader(SkGradientShader::CreateLinear(pts, colors, nil, 2, SkShader::kMirror_TileMode))->unref();
+}
+
+void SkSliderView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+
+ p.setAntiAliasOn(true);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ r.inset(SK_Scalar1/2, SK_Scalar1/2);
+ canvas->drawRect(r, p);
+
+ if (fMax)
+ {
+ SkFixed percent = SkFixedDiv(value, fMax);
+
+ r.inset(SK_Scalar1/2, SK_Scalar1/2);
+ r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+ p.setStyle(SkPaint::kFill_Style);
+ setgrad(&p, r);
+ canvas->drawRect(r, p);
+ }
+
+#if 0
+ r.set(0, 0, this->width(), this->height());
+ r.inset(SK_Scalar1, SK_Scalar1);
+ r.inset(r.width()/2, 0);
+ p.setColor(SK_ColorBLACK);
+ canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p);
+#endif
+}
+
+SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ return new Click(this);
+}
+
+bool SkSliderView::onClick(Click* click)
+{
+ if (fMax)
+ {
+ SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2);
+ percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1));
+ this->setValue(SkScalarRound(percent * fMax));
+ return true;
+ }
+ return false;
+}
+
+#endif
+
diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp
new file mode 100644
index 0000000000..e7eb772577
--- /dev/null
+++ b/src/views/SkWidgetViews.cpp
@@ -0,0 +1,570 @@
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkSystemEventTypes.h"
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+/*
+I have moved this to SkWidgetViews.h
+enum SkinEnum {
+ kButton_SkinEnum,
+ kProgress_SkinEnum,
+ kScroll_SkinEnum,
+ kStaticText_SkinEnum,
+
+ kSkinEnumCount
+};
+*/
+
+const char* get_skin_enum_path(SkinEnum se)
+{
+ SkASSERT((unsigned)se < kSkinEnumCount);
+
+ static const char* gSkinPaths[] = {
+ "common/default/default/skins/border3.xml",
+ "common/default/default/skins/button.xml",
+ "common/default/default/skins/progressBar.xml",
+ "common/default/default/skins/scrollBar.xml",
+ "common/default/default/skins/statictextpaint.xml"
+ };
+
+ return gSkinPaths[se];
+}
+
+void init_skin_anim(const char path[], SkAnimator* anim)
+{
+ SkASSERT(path && anim);
+
+ SkFILEStream stream(path);
+
+ if (!stream.isValid())
+ {
+ SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
+ sk_throw();
+ }
+
+ if (!anim->decodeStream(&stream))
+ {
+ SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
+ sk_throw();
+ }
+}
+
+void init_skin_anim(SkinEnum se, SkAnimator* anim)
+{
+ init_skin_anim(get_skin_enum_path(se), anim);
+}
+
+void init_skin_paint(SkinEnum se, SkPaint* paint)
+{
+ SkASSERT(paint);
+
+ SkAnimator anim;
+ SkCanvas canvas;
+
+ init_skin_anim(se, &anim);
+ anim.draw(&canvas, paint, 0);
+}
+
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
+{
+ SkASSERT(paint);
+
+ SkAnimator anim;
+ SkCanvas canvas;
+
+ if (!anim.decodeDOM(dom, node))
+ {
+ SkDEBUGF(("inflate_paint: decoding dom failed\n"));
+ SkDEBUGCODE(dom.dump(node);)
+ sk_throw();
+ }
+ anim.draw(&canvas, paint, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
+{
+}
+
+const char* SkWidgetView::getLabel() const
+{
+ return fLabel.c_str();
+}
+
+void SkWidgetView::getLabel(SkString* label) const
+{
+ if (label)
+ *label = fLabel;
+}
+
+void SkWidgetView::setLabel(const char label[])
+{
+ this->setLabel(label, label ? strlen(label) : 0);
+}
+
+void SkWidgetView::setLabel(const char label[], size_t len)
+{
+ if (label == nil && fLabel.size() != 0 || !fLabel.equals(label, len))
+ {
+ SkString tmp(label, len);
+
+ this->onLabelChange(fLabel.c_str(), tmp.c_str());
+ fLabel.swap(tmp);
+ }
+}
+
+void SkWidgetView::setLabel(const SkString& label)
+{
+ if (fLabel != label)
+ {
+ this->onLabelChange(fLabel.c_str(), label.c_str());
+ fLabel = label;
+ }
+}
+
+bool SkWidgetView::postWidgetEvent()
+{
+ if (!fEvent.isType(""))
+ {
+ SkEvent evt(fEvent); // make a copy since onPrepareWidgetEvent may edit the event
+
+ if (this->onPrepareWidgetEvent(&evt))
+ {
+ SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)
+
+ this->postToListeners(evt); // wonder if this should return true if there are > 0 listeners...
+ return true;
+ }
+ }
+ return false;
+}
+
+/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* label = dom.findAttr(node, "label");
+ if (label)
+ this->setLabel(label);
+
+ if ((node = dom.getFirstChild(node, "event")) != nil)
+ fEvent.inflate(dom, node);
+}
+
+/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
+{
+ this->inval(nil);
+}
+
+static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";
+
+/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
+ return true;
+}
+
+SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
+{
+ int32_t sinkID;
+
+ return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+ {
+ this->postWidgetEvent();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
+{
+}
+
+void SkCheckButtonView::setCheckState(CheckState state)
+{
+ SkASSERT((unsigned)state <= kUnknown_CheckState);
+
+ if (fCheckState != state)
+ {
+ this->onCheckStateChange(this->getCheckState(), state);
+ fCheckState = SkToU8(state);
+ }
+}
+
+/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
+{
+ this->inval(nil);
+}
+
+/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index = dom.findList(node, "check-state", "off,on,unknown");
+ if (index >= 0)
+ this->setCheckState((CheckState)index);
+}
+
+static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";
+
+/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ // could check if we're "disabled", and return false...
+
+ evt->setS32(gCheckStateSlotName, this->getCheckState());
+ return true;
+}
+
+bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
+{
+ int32_t state32;
+
+ if (evt.findS32(gCheckStateSlotName, &state32))
+ {
+ if (state)
+ *state = (CheckState)state32;
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+#include <stdio.h>
+
+class SkAnimButtonView : public SkButtonView {
+public:
+ SkAnimButtonView()
+ {
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kButton_SkinEnum, &fAnim);
+ }
+
+protected:
+ virtual void onLabelChange(const char oldLabel[], const char newLabel[])
+ {
+ this->INHERITED::onLabelChange(oldLabel, newLabel);
+
+ SkEvent evt("user");
+ evt.setString("id", "setLabel");
+ evt.setString("LABEL", newLabel);
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onFocusChange(bool gainFocus)
+ {
+ this->INHERITED::onFocusChange(gainFocus);
+
+ SkEvent evt("user");
+ evt.setString("id", "setFocus");
+ evt.setS32("FOCUS", gainFocus);
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onSizeChange()
+ {
+ this->INHERITED::onSizeChange();
+
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onDraw(SkCanvas* canvas)
+ {
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(nil);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+ }
+
+ virtual bool onEvent(const SkEvent& evt)
+ {
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(nil);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar height;
+
+ if (evt.findScalar("y", &height))
+ this->setHeight(height);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+ }
+
+ virtual bool onPrepareWidgetEvent(SkEvent* evt)
+ {
+ if (this->INHERITED::onPrepareWidgetEvent(evt))
+ {
+ SkEvent e("user");
+ e.setString("id", "handlePress");
+ (void)fAnim.doUserEvent(e);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SkAnimator fAnim;
+
+ typedef SkButtonView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTextBox.h"
+
+SkStaticTextView::SkStaticTextView()
+{
+ fMargin.set(0, 0);
+ fMode = kFixedSize_Mode;
+ fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+
+ init_skin_paint(kStaticText_SkinEnum, &fPaint);
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+ if (fMode == kAutoWidth_Mode)
+ {
+ SkScalar width = fPaint.measureText(fText.c_str(), fText.size());
+ this->setWidth(width + fMargin.fX * 2);
+ }
+ else if (fMode == kAutoHeight_Mode)
+ {
+ SkScalar width = this->width() - fMargin.fX * 2;
+ int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+ this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2);
+ }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+
+ if (fMode != mode)
+ {
+ fMode = SkToU8(mode);
+ this->computeSize();
+ }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+ fSpacingAlign = SkToU8(align);
+ this->inval(nil);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+ if (fMargin.fX != dx || fMargin.fY != dy)
+ {
+ fMargin.set(dx, dy);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+ if (text)
+ *text = fText;
+ return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+ if (text)
+ memcpy(text, fText.c_str(), fText.size());
+ return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+ this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+ if (text == nil)
+ text = "";
+ this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+ if (!fText.equals(text, len))
+ {
+ fText.set(text, len);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+ if (paint)
+ *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+ if (fPaint != paint)
+ {
+ fPaint = paint;
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.isEmpty())
+ return;
+
+ SkTextBox box;
+
+ box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+ box.setSpacingAlign(this->getSpacingAlign());
+ box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+ box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+ this->setMode((Mode)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+ this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+ else
+ assert_no_attr(dom, node, "spacing-align");
+
+ SkScalar s[2];
+ if (dom.findScalars(node, "margin", s, 2))
+ this->setMargin(s[0], s[1]);
+ else
+ assert_no_attr(dom, node, "margin");
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ if ((node = dom.getFirstChild(node, "paint")) != nil &&
+ (node = dom.getFirstChild(node, "screenplay")) != nil)
+ {
+ inflate_paint(dom, node, &fPaint);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkView* SkWidgetFactory(const char name[])
+{
+ if (name == nil)
+ return nil;
+
+ // must be in the same order as the SkSkinWidgetEnum is declared
+ static const char* gNames[] = {
+ "sk-border",
+ "sk-button",
+ "sk-image",
+ "sk-list",
+ "sk-progress",
+ "sk-scroll",
+ "sk-text"
+
+ };
+
+ for (int i = 0; i < SK_ARRAY_COUNT(gNames); i++)
+ if (!strcmp(gNames[i], name))
+ return SkWidgetFactory((SkWidgetEnum)i);
+
+ return nil;
+}
+
+#include "SkImageView.h"
+#include "SkProgressBarView.h"
+#include "SkScrollBarView.h"
+#include "SkBorderView.h"
+
+SkView* SkWidgetFactory(SkWidgetEnum sw)
+{
+ switch (sw) {
+ case kBorder_WidgetEnum:
+ return new SkBorderView;
+ case kButton_WidgetEnum:
+ return new SkAnimButtonView;
+ case kImage_WidgetEnum:
+ return new SkImageView;
+ case kList_WidgetEnum:
+ return new SkListView;
+ case kProgress_WidgetEnum:
+ return new SkProgressBarView;
+ case kScroll_WidgetEnum:
+ return new SkScrollBarView;
+ case kText_WidgetEnum:
+ return new SkStaticTextView;
+ default:
+ SkASSERT(!"unknown enum passed to SkWidgetFactory");
+ break;
+ }
+ return nil;
+}
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
new file mode 100644
index 0000000000..fe87cd6e19
--- /dev/null
+++ b/src/views/SkWidgets.cpp
@@ -0,0 +1,554 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+#include "SkTextBox.h"
+
+#if 0
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+#include "SkAnimator.h"
+#include "SkTime.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SkinType {
+ kPushButton_SkinType,
+ kStaticText_SkinType,
+
+ kSkinTypeCount
+};
+
+struct SkinSuite {
+ SkinSuite();
+ ~SkinSuite()
+ {
+ for (int i = 0; i < kSkinTypeCount; i++)
+ delete fAnimators[i];
+ }
+
+ SkAnimator* get(SkinType);
+
+private:
+ SkAnimator* fAnimators[kSkinTypeCount];
+};
+
+SkinSuite::SkinSuite()
+{
+ static const char kSkinPath[] = "skins/";
+
+ static const char* gSkinNames[] = {
+ "pushbutton_skin.xml",
+ "statictext_skin.xml"
+ };
+
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++)
+ {
+ size_t len = strlen(gSkinNames[i]);
+ SkString path(sizeof(kSkinPath) - 1 + len);
+
+ memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1);
+ memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len);
+
+ fAnimators[i] = new SkAnimator;
+ if (!fAnimators[i]->decodeURI(path.c_str()))
+ {
+ delete fAnimators[i];
+ fAnimators[i] = nil;
+ }
+ }
+}
+
+SkAnimator* SkinSuite::get(SkinType st)
+{
+ SkASSERT((unsigned)st < kSkinTypeCount);
+ return fAnimators[st];
+}
+
+static SkinSuite* gSkinSuite;
+
+static SkAnimator* get_skin_animator(SkinType st)
+{
+#if 0
+ if (gSkinSuite == nil)
+ gSkinSuite = new SkinSuite;
+ return gSkinSuite->get(st);
+#else
+ return nil;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkWidget::Init()
+{
+}
+
+void SkWidget::Term()
+{
+ delete gSkinSuite;
+}
+
+void SkWidget::onEnabledChange()
+{
+ this->inval(nil);
+}
+
+void SkWidget::postWidgetEvent()
+{
+ if (!fEvent.isType("") && this->hasListeners())
+ {
+ this->prepareWidgetEvent(&fEvent);
+ this->postToListeners(fEvent);
+ }
+}
+
+void SkWidget::prepareWidgetEvent(SkEvent*)
+{
+ // override in subclass to add any additional fields before posting
+}
+
+void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ if ((node = dom.getFirstChild(node, "event")) != nil)
+ fEvent.inflate(dom, node);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkHasLabelWidget::getLabel(SkString* str) const
+{
+ if (str)
+ *str = fLabel;
+ return fLabel.size();
+}
+
+size_t SkHasLabelWidget::getLabel(char buffer[]) const
+{
+ if (buffer)
+ memcpy(buffer, fLabel.c_str(), fLabel.size());
+ return fLabel.size();
+}
+
+void SkHasLabelWidget::setLabel(const SkString& str)
+{
+ this->setLabel(str.c_str(), str.size());
+}
+
+void SkHasLabelWidget::setLabel(const char label[])
+{
+ this->setLabel(label, strlen(label));
+}
+
+void SkHasLabelWidget::setLabel(const char label[], size_t len)
+{
+ if (!fLabel.equals(label, len))
+ {
+ fLabel.set(label, len);
+ this->onLabelChange();
+ }
+}
+
+void SkHasLabelWidget::onLabelChange()
+{
+ // override in subclass
+}
+
+void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* text = dom.findAttr(node, "label");
+ if (text)
+ this->setLabel(text);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkButtonWidget::setButtonState(State state)
+{
+ if (fState != state)
+ {
+ fState = state;
+ this->onButtonStateChange();
+ }
+}
+
+void SkButtonWidget::onButtonStateChange()
+{
+ this->inval(nil);
+}
+
+void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0)
+ this->setButtonState((State)index);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkPushButtonWidget::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+ {
+ this->postWidgetEvent();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state)
+{
+ if (!enabled)
+ return "disabled";
+ if (state == SkButtonWidget::kOn_State)
+ {
+ SkASSERT(focused);
+ return "enabled-pressed";
+ }
+ if (focused)
+ return "enabled-focused";
+ return "enabled";
+}
+
+#include "SkBlurMaskFilter.h"
+#include "SkEmbossMaskFilter.h"
+
+static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed)
+{
+ SkEmbossMaskFilter::Light light;
+
+ light.fDirection[0] = SK_Scalar1/2;
+ light.fDirection[1] = SK_Scalar1/2;
+ light.fDirection[2] = SK_Scalar1/3;
+ light.fAmbient = 0x48;
+ light.fSpecular = 0x80;
+
+ if (pressed)
+ {
+ light.fDirection[0] = -light.fDirection[0];
+ light.fDirection[1] = -light.fDirection[1];
+ }
+ if (focus)
+ light.fDirection[2] += SK_Scalar1/4;
+
+ paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref();
+}
+
+void SkPushButtonWidget::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ SkString label;
+ this->getLabel(&label);
+
+ SkAnimator* anim = get_skin_animator(kPushButton_SkinType);
+
+ if (anim)
+ {
+ SkEvent evt("user");
+
+ evt.setString("id", "prime");
+ evt.setScalar("prime-width", this->width());
+ evt.setScalar("prime-height", this->height());
+ evt.setString("prime-text", label);
+ evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState()));
+
+ (void)anim->doUserEvent(evt);
+ SkPaint paint;
+ anim->draw(canvas, &paint, SkTime::GetMSecs());
+ }
+ else
+ {
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+ p.setAntiAliasOn(true);
+ p.setColor(SK_ColorBLUE);
+ create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State);
+ canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p);
+ p.setMaskFilter(nil);
+
+ p.setTextAlign(SkPaint::kCenter_Align);
+
+ SkTextBox box;
+ box.setMode(SkTextBox::kOneLine_Mode);
+ box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign);
+ box.setBox(0, 0, this->width(), this->height());
+
+// if (this->getButtonState() == kOn_State)
+// p.setColor(SK_ColorRED);
+// else
+ p.setColor(SK_ColorWHITE);
+
+ box.draw(canvas, label.c_str(), label.size(), p);
+ }
+}
+
+SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ this->acceptFocus();
+ return new Click(this);
+}
+
+bool SkPushButtonWidget::onClick(Click* click)
+{
+ SkRect r;
+ State state = kOff_State;
+
+ this->getLocalBounds(&r);
+ if (r.contains(click->fCurr))
+ {
+ if (click->fState == Click::kUp_State)
+ this->postWidgetEvent();
+ else
+ state = kOn_State;
+ }
+ this->setButtonState(state);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags)
+{
+ fMargin.set(0, 0);
+ fMode = kFixedSize_Mode;
+ fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+ if (fMode == kAutoWidth_Mode)
+ {
+ SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), nil, nil);
+ this->setWidth(width + fMargin.fX * 2);
+ }
+ else if (fMode == kAutoHeight_Mode)
+ {
+ SkScalar width = this->width() - fMargin.fX * 2;
+ int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+ SkScalar before, after;
+ (void)fPaint.measureText(0, nil, &before, &after);
+
+ this->setHeight(lines * (after - before) + fMargin.fY * 2);
+ }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+
+ if (fMode != mode)
+ {
+ fMode = SkToU8(mode);
+ this->computeSize();
+ }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+ fSpacingAlign = SkToU8(align);
+ this->inval(nil);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+ if (fMargin.fX != dx || fMargin.fY != dy)
+ {
+ fMargin.set(dx, dy);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+ if (text)
+ *text = fText;
+ return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+ if (text)
+ memcpy(text, fText.c_str(), fText.size());
+ return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+ this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+ this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+ if (!fText.equals(text, len))
+ {
+ fText.set(text, len);
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+ if (paint)
+ *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+ if (fPaint != paint)
+ {
+ fPaint = paint;
+ this->computeSize();
+ this->inval(nil);
+ }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.isEmpty())
+ return;
+
+ SkTextBox box;
+
+ box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+ box.setSpacingAlign(this->getSpacingAlign());
+ box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+ box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+ this->setMode((Mode)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+ this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ SkScalar s[2];
+ if (dom.findScalars(node, "margin", s, 2))
+ this->setMargin(s[0], s[1]);
+ else
+ assert_no_attr(dom, node, "margin");
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ if ((node = dom.getFirstChild(node, "paint")) != nil)
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+
+SkBitmapView::SkBitmapView(U32 flags) : SkView(flags)
+{
+}
+
+SkBitmapView::~SkBitmapView()
+{
+}
+
+bool SkBitmapView::getBitmap(SkBitmap* bitmap) const
+{
+ if (bitmap)
+ *bitmap = fBitmap;
+ return fBitmap.getConfig() != SkBitmap::kNo_Config;
+}
+
+void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels)
+{
+ if (bitmap)
+ {
+ fBitmap = *bitmap;
+ fBitmap.setOwnsPixels(viewOwnsPixels);
+ }
+}
+
+bool SkBitmapView::loadBitmapFromFile(const char path[])
+{
+ SkBitmap bitmap;
+
+ if (SkImageDecoder::DecodeFile(path, &bitmap))
+ {
+ this->setBitmap(&bitmap, true);
+ bitmap.setOwnsPixels(false);
+ return true;
+ }
+ return false;
+}
+
+void SkBitmapView::onDraw(SkCanvas* canvas)
+{
+ if (fBitmap.getConfig() != SkBitmap::kNo_Config &&
+ fBitmap.width() && fBitmap.height())
+ {
+ SkAutoCanvasRestore restore(canvas, true);
+ SkPaint p;
+
+ p.setFilterType(SkPaint::kBilinear_FilterType);
+ canvas->scale( this->width() / fBitmap.width(),
+ this->height() / fBitmap.height(),
+ 0, 0);
+ canvas->drawBitmap(fBitmap, 0, 0, p);
+ }
+}
+
+void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* src = dom.findAttr(node, "src");
+ if (src)
+ (void)this->loadBitmapFromFile(src);
+}
+
+#endif
+
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
new file mode 100644
index 0000000000..5d00d4fb85
--- /dev/null
+++ b/src/views/SkWindow.cpp
@@ -0,0 +1,367 @@
+#include "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkOSMenu.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+#define SK_EventDelayInval "\xd" "n" "\xa" "l"
+
+#define TEST_BOUNDERx
+
+#include "SkBounder.h"
+class test_bounder : public SkBounder {
+public:
+ test_bounder(const SkBitmap& bm) : fCanvas(bm) {}
+protected:
+ virtual bool onIRect(const SkIRect& r)
+ {
+ SkRect rr;
+
+ rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop),
+ SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom));
+
+ SkPaint p;
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setColor(SK_ColorYELLOW);
+
+#if 0
+ rr.inset(SK_ScalarHalf, SK_ScalarHalf);
+#else
+ rr.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+#endif
+
+ fCanvas.drawRect(rr, p);
+ return true;
+ }
+private:
+ SkCanvas fCanvas;
+};
+
+SkWindow::SkWindow() : fFocusView(NULL)
+{
+ fClick = NULL;
+ fWaitingOnInval = false;
+
+#ifdef SK_BUILD_FOR_WINCE
+ fConfig = SkBitmap::kRGB_565_Config;
+#else
+ fConfig = SkBitmap::kARGB_8888_Config;
+#endif
+}
+
+SkWindow::~SkWindow()
+{
+ delete fClick;
+
+ fMenus.deleteAll();
+}
+
+void SkWindow::setConfig(SkBitmap::Config config)
+{
+ this->resize(fBitmap.width(), fBitmap.height(), config);
+}
+
+#include "SkImageDecoder.h"
+
+void SkWindow::resize(int width, int height, SkBitmap::Config config)
+{
+ if (config == SkBitmap::kNo_Config)
+ config = fConfig;
+
+ if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig)
+ {
+ fConfig = config;
+ fBitmap.setConfig(config, width, height);
+ fBitmap.allocPixels();
+
+ this->setSize(SkIntToScalar(width), SkIntToScalar(height));
+ this->inval(NULL);
+ }
+
+ SkImageDecoder::SetDeviceConfig(fConfig);
+}
+
+void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+ fBitmap.eraseARGB(a, r, g, b);
+}
+
+void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b)
+{
+ fBitmap.eraseRGB(r, g, b);
+}
+
+bool SkWindow::handleInval(const SkRect& r)
+{
+ SkIRect ir;
+
+ r.round(&ir);
+ fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+
+#ifdef SK_BUILD_FOR_WIN32
+ if (!fWaitingOnInval)
+ {
+ fWaitingOnInval = true;
+ (new SkEvent(SK_EventDelayInval))->post(this->getSinkID(), 10);
+ }
+#else
+ this->onHandleInval(ir);
+#endif
+ return true;
+}
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ #include <windows.h>
+ #include <gx.h>
+ extern GXDisplayProperties gDisplayProps;
+#endif
+
+#ifdef SK_SIMULATE_FAILED_MALLOC
+extern bool gEnableControlledThrow;
+#endif
+
+bool SkWindow::update(SkIRect* updateArea)
+{
+ if (!fDirtyRgn.isEmpty())
+ {
+ SkBitmap bm = this->getBitmap();
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ char* buffer = (char*)GXBeginDraw();
+ SkASSERT(buffer);
+
+ RECT rect;
+ GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+ buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+
+ bm.setPixels(buffer);
+#endif
+
+ SkCanvas canvas(bm);
+
+ canvas.clipRegion(fDirtyRgn);
+ if (updateArea)
+ *updateArea = fDirtyRgn.getBounds();
+
+ // empty this now, so we can correctly record any inval calls that
+ // might be made during the draw call.
+ fDirtyRgn.setEmpty();
+
+#ifdef TEST_BOUNDER
+ test_bounder b(bm);
+ canvas.setBounder(&b);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+ gEnableControlledThrow = true;
+#endif
+#ifdef SK_BUILD_FOR_WIN32
+ try {
+ this->draw(&canvas);
+ }
+ catch (...) {
+ }
+#else
+ this->draw(&canvas);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+ gEnableControlledThrow = false;
+#endif
+#ifdef TEST_BOUNDER
+ canvas.setBounder(NULL);
+#endif
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ GXEndDraw();
+#endif
+
+ return true;
+ }
+ return false;
+}
+
+bool SkWindow::handleChar(SkUnichar uni)
+{
+ if (this->onHandleChar(uni))
+ return true;
+
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ SkEvent evt(SK_EventType_Unichar);
+ evt.setFast32(uni);
+ return focus->doEvent(evt);
+}
+
+bool SkWindow::handleKey(SkKey key)
+{
+ if (key == kNONE_SkKey)
+ return false;
+
+ if (this->onHandleKey(key))
+ return true;
+
+ // send an event to the focus-view
+ {
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ SkEvent evt(SK_EventType_Key);
+ evt.setFast32(key);
+ if (focus->doEvent(evt))
+ return true;
+ }
+
+ if (key == kUp_SkKey || key == kDown_SkKey)
+ {
+ if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL)
+ this->onSetFocusView(NULL);
+ return true;
+ }
+ return false;
+}
+
+bool SkWindow::handleKeyUp(SkKey key)
+{
+ if (key == kNONE_SkKey)
+ return false;
+
+ if (this->onHandleKeyUp(key))
+ return true;
+
+ //send an event to the focus-view
+ {
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ //should this one be the same?
+ SkEvent evt(SK_EventType_KeyUp);
+ evt.setFast32(key);
+ if (focus->doEvent(evt))
+ return true;
+ }
+ return false;
+}
+
+void SkWindow::addMenu(SkOSMenu* menu)
+{
+ *fMenus.append() = menu;
+ this->onAddMenu(menu);
+}
+
+void SkWindow::setTitle(const char title[])
+{
+ if (NULL == title)
+ title = "";
+ this->onSetTitle(title);
+}
+
+bool SkWindow::handleMenu(uint32_t cmd)
+{
+ for (int i = 0; i < fMenus.count(); i++)
+ {
+ SkEvent* evt = fMenus[i]->createEvent(cmd);
+ if (evt)
+ {
+ evt->post(this->getSinkID());
+ return true;
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+bool SkWindow::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventDelayInval))
+ {
+ SkRegion::Iterator iter(fDirtyRgn);
+
+ for (; !iter.done(); iter.next())
+ this->onHandleInval(iter.rect());
+ fWaitingOnInval = false;
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+bool SkWindow::onGetFocusView(SkView** focus) const
+{
+ if (focus)
+ *focus = fFocusView;
+ return true;
+}
+
+bool SkWindow::onSetFocusView(SkView* focus)
+{
+ if (fFocusView != focus)
+ {
+ if (fFocusView)
+ fFocusView->onFocusChange(false);
+ fFocusView = focus;
+ if (focus)
+ focus->onFocusChange(true);
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkWindow::onHandleInval(const SkIRect&)
+{
+}
+
+bool SkWindow::onHandleChar(SkUnichar)
+{
+ return false;
+}
+
+bool SkWindow::onHandleKey(SkKey key)
+{
+ return false;
+}
+
+bool SkWindow::onHandleKeyUp(SkKey key)
+{
+ return false;
+}
+
+bool SkWindow::handleClick(int x, int y, Click::State state)
+{
+ bool handled = false;
+
+ switch (state) {
+ case Click::kDown_State:
+ if (fClick)
+ delete fClick;
+ fClick = this->findClickHandler(SkIntToScalar(x), SkIntToScalar(y));
+ if (fClick)
+ {
+ SkView::DoClickDown(fClick, x, y);
+ handled = true;
+ }
+ break;
+ case Click::kMoved_State:
+ if (fClick)
+ {
+ SkView::DoClickMoved(fClick, x, y);
+ handled = true;
+ }
+ break;
+ case Click::kUp_State:
+ if (fClick)
+ {
+ SkView::DoClickUp(fClick, x, y);
+ delete fClick;
+ fClick = NULL;
+ handled = true;
+ }
+ break;
+ }
+ return handled;
+}
+