Home | History | Annotate | Download | only in views
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "SkView.h"
      9 #include "SkCanvas.h"
     10 
     11 SK_DEFINE_INST_COUNT(SkView::Artist)
     12 SK_DEFINE_INST_COUNT(SkView::Layout)
     13 
     14 ////////////////////////////////////////////////////////////////////////
     15 
     16 SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
     17 {
     18     fWidth = fHeight = 0;
     19     fLoc.set(0, 0);
     20     fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
     21     fMatrix.setIdentity();
     22     fContainsFocus = 0;
     23 }
     24 
     25 SkView::~SkView()
     26 {
     27     this->detachAllChildren();
     28 }
     29 
     30 void SkView::setFlags(uint32_t flags)
     31 {
     32     SkASSERT((flags & ~kAllFlagMasks) == 0);
     33 
     34     uint32_t diff = fFlags ^ flags;
     35 
     36     if (diff & kVisible_Mask)
     37         this->inval(NULL);
     38 
     39     fFlags = SkToU8(flags);
     40 
     41     if (diff & kVisible_Mask)
     42     {
     43         this->inval(NULL);
     44     }
     45 }
     46 
     47 void SkView::setVisibleP(bool pred)
     48 {
     49     this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
     50 }
     51 
     52 void SkView::setEnabledP(bool pred)
     53 {
     54     this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
     55 }
     56 
     57 void SkView::setFocusableP(bool pred)
     58 {
     59     this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
     60 }
     61 
     62 void SkView::setClipToBounds(bool pred) {
     63     this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift));
     64 }
     65 
     66 void SkView::setSize(SkScalar width, SkScalar height)
     67 {
     68     width = SkMaxScalar(0, width);
     69     height = SkMaxScalar(0, height);
     70 
     71     if (fWidth != width || fHeight != height)
     72     {
     73         this->inval(NULL);
     74         fWidth = width;
     75         fHeight = height;
     76         this->inval(NULL);
     77         this->onSizeChange();
     78         this->invokeLayout();
     79     }
     80 }
     81 
     82 void SkView::setLoc(SkScalar x, SkScalar y)
     83 {
     84     if (fLoc.fX != x || fLoc.fY != y)
     85     {
     86         this->inval(NULL);
     87         fLoc.set(x, y);
     88         this->inval(NULL);
     89     }
     90 }
     91 
     92 void SkView::offset(SkScalar dx, SkScalar dy)
     93 {
     94     if (dx || dy)
     95         this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
     96 }
     97 
     98 void SkView::setLocalMatrix(const SkMatrix& matrix)
     99 {
    100     this->inval(NULL);
    101     fMatrix = matrix;
    102     this->inval(NULL);
    103 }
    104 
    105 void SkView::draw(SkCanvas* canvas)
    106 {
    107     if (fWidth && fHeight && this->isVisible())
    108     {
    109         SkRect    r;
    110         r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
    111         if (this->isClipToBounds() &&
    112             canvas->quickReject(r)) {
    113                 return;
    114         }
    115 
    116         SkAutoCanvasRestore    as(canvas, true);
    117 
    118         if (this->isClipToBounds()) {
    119             canvas->clipRect(r);
    120         }
    121 
    122         canvas->translate(fLoc.fX, fLoc.fY);
    123         canvas->concat(fMatrix);
    124 
    125         if (fParent) {
    126             fParent->beforeChild(this, canvas);
    127         }
    128 
    129         int sc = canvas->save();
    130         this->onDraw(canvas);
    131         canvas->restoreToCount(sc);
    132 
    133         if (fParent) {
    134             fParent->afterChild(this, canvas);
    135         }
    136 
    137         B2FIter    iter(this);
    138         SkView*    child;
    139 
    140         SkCanvas* childCanvas = this->beforeChildren(canvas);
    141 
    142         while ((child = iter.next()) != NULL)
    143             child->draw(childCanvas);
    144 
    145         this->afterChildren(canvas);
    146     }
    147 }
    148 
    149 void SkView::inval(SkRect* rect) {
    150     SkView*    view = this;
    151     SkRect storage;
    152 
    153     for (;;) {
    154         if (!view->isVisible()) {
    155             return;
    156         }
    157         if (view->isClipToBounds()) {
    158             SkRect bounds;
    159             view->getLocalBounds(&bounds);
    160             if (rect && !bounds.intersect(*rect)) {
    161                 return;
    162             }
    163             storage = bounds;
    164             rect = &storage;
    165         }
    166         if (view->handleInval(rect)) {
    167             return;
    168         }
    169 
    170         SkView* parent = view->fParent;
    171         if (parent == NULL) {
    172             return;
    173         }
    174 
    175         if (rect) {
    176             rect->offset(view->fLoc.fX, view->fLoc.fY);
    177         }
    178         view = parent;
    179     }
    180 }
    181 
    182 ////////////////////////////////////////////////////////////////////////////
    183 
    184 bool SkView::setFocusView(SkView* fv)
    185 {
    186     SkView* view = this;
    187 
    188     do {
    189         if (view->onSetFocusView(fv))
    190             return true;
    191     } while ((view = view->fParent) != NULL);
    192     return false;
    193 }
    194 
    195 SkView* SkView::getFocusView() const
    196 {
    197     SkView*            focus = NULL;
    198     const SkView*    view = this;
    199     do {
    200         if (view->onGetFocusView(&focus))
    201             break;
    202     } while ((view = view->fParent) != NULL);
    203     return focus;
    204 }
    205 
    206 bool SkView::hasFocus() const
    207 {
    208     return this == this->getFocusView();
    209 }
    210 
    211 bool SkView::acceptFocus()
    212 {
    213     return this->isFocusable() && this->setFocusView(this);
    214 }
    215 
    216 /*
    217     Try to give focus to this view, or its children
    218 */
    219 SkView* SkView::acceptFocus(FocusDirection dir)
    220 {
    221     if (dir == kNext_FocusDirection)
    222     {
    223         if (this->acceptFocus())
    224             return this;
    225 
    226         B2FIter    iter(this);
    227         SkView*    child, *focus;
    228         while ((child = iter.next()) != NULL)
    229             if ((focus = child->acceptFocus(dir)) != NULL)
    230                 return focus;
    231     }
    232     else // prev
    233     {
    234         F2BIter    iter(this);
    235         SkView*    child, *focus;
    236         while ((child = iter.next()) != NULL)
    237             if ((focus = child->acceptFocus(dir)) != NULL)
    238                 return focus;
    239 
    240         if (this->acceptFocus())
    241             return this;
    242     }
    243 
    244     return NULL;
    245 }
    246 
    247 SkView* SkView::moveFocus(FocusDirection dir)
    248 {
    249     SkView* focus = this->getFocusView();
    250 
    251     if (focus == NULL)
    252     {    // start with the root
    253         focus = this;
    254         while (focus->fParent)
    255             focus = focus->fParent;
    256     }
    257 
    258     SkView*    child, *parent;
    259 
    260     if (dir == kNext_FocusDirection)
    261     {
    262         parent = focus;
    263         child = focus->fFirstChild;
    264         if (child)
    265             goto FIRST_CHILD;
    266         else
    267             goto NEXT_SIB;
    268 
    269         do {
    270             while (child != parent->fFirstChild)
    271             {
    272     FIRST_CHILD:
    273                 if ((focus = child->acceptFocus(dir)) != NULL)
    274                     return focus;
    275                 child = child->fNextSibling;
    276             }
    277     NEXT_SIB:
    278             child = parent->fNextSibling;
    279             parent = parent->fParent;
    280         } while (parent != NULL);
    281     }
    282     else    // prevfocus
    283     {
    284         parent = focus->fParent;
    285         if (parent == NULL)    // we're the root
    286             return focus->acceptFocus(dir);
    287         else
    288         {
    289             child = focus;
    290             while (parent)
    291             {
    292                 while (child != parent->fFirstChild)
    293                 {
    294                     child = child->fPrevSibling;
    295                     if ((focus = child->acceptFocus(dir)) != NULL)
    296                         return focus;
    297                 }
    298                 if (parent->acceptFocus())
    299                     return parent;
    300 
    301                 child = parent;
    302                 parent = parent->fParent;
    303             }
    304         }
    305     }
    306     return NULL;
    307 }
    308 
    309 void SkView::onFocusChange(bool gainFocusP)
    310 {
    311     this->inval(NULL);
    312 }
    313 
    314 ////////////////////////////////////////////////////////////////////////////
    315 
    316 SkView::Click::Click(SkView* target)
    317 {
    318     SkASSERT(target);
    319     fTargetID = target->getSinkID();
    320     fType = NULL;
    321     fWeOwnTheType = false;
    322     fOwner = NULL;
    323 }
    324 
    325 SkView::Click::~Click()
    326 {
    327     this->resetType();
    328 }
    329 
    330 void SkView::Click::resetType()
    331 {
    332     if (fWeOwnTheType)
    333     {
    334         sk_free(fType);
    335         fWeOwnTheType = false;
    336     }
    337     fType = NULL;
    338 }
    339 
    340 bool SkView::Click::isType(const char type[]) const
    341 {
    342     const char* t = fType;
    343 
    344     if (type == t)
    345         return true;
    346 
    347     if (type == NULL)
    348         type = "";
    349     if (t == NULL)
    350         t = "";
    351     return !strcmp(t, type);
    352 }
    353 
    354 void SkView::Click::setType(const char type[])
    355 {
    356     this->resetType();
    357     fType = (char*)type;
    358 }
    359 
    360 void SkView::Click::copyType(const char type[])
    361 {
    362     if (fType != type)
    363     {
    364         this->resetType();
    365         if (type)
    366         {
    367             size_t    len = strlen(type) + 1;
    368             fType = (char*)sk_malloc_throw(len);
    369             memcpy(fType, type, len);
    370             fWeOwnTheType = true;
    371         }
    372     }
    373 }
    374 
    375 SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y, unsigned modi) {
    376     if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
    377         return NULL;
    378     }
    379 
    380     if (this->onSendClickToChildren(x, y, modi)) {
    381         F2BIter    iter(this);
    382         SkView*    child;
    383 
    384         while ((child = iter.next()) != NULL)
    385         {
    386             SkPoint p;
    387             if (!child->globalToLocal(x, y, &p)) {
    388                 continue;
    389             }
    390 
    391             Click* click = child->findClickHandler(p.fX, p.fY, modi);
    392 
    393             if (click) {
    394                 return click;
    395             }
    396         }
    397     }
    398 
    399     return this->onFindClickHandler(x, y, modi);
    400 }
    401 
    402 void SkView::DoClickDown(Click* click, int x, int y, unsigned modi)
    403 {
    404     SkASSERT(click);
    405 
    406     SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
    407     if (NULL == target) {
    408         return;
    409     }
    410 
    411     click->fIOrig.set(x, y);
    412     click->fICurr = click->fIPrev = click->fIOrig;
    413 
    414     click->fOrig.iset(x, y);
    415     if (!target->globalToLocal(&click->fOrig)) {
    416         // no history to let us recover from this failure
    417         return;
    418     }
    419     click->fPrev = click->fCurr = click->fOrig;
    420 
    421     click->fState = Click::kDown_State;
    422     click->fModifierKeys = modi;
    423     target->onClick(click);
    424 }
    425 
    426 void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi)
    427 {
    428     SkASSERT(click);
    429 
    430     SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
    431     if (NULL == target) {
    432         return;
    433     }
    434 
    435     click->fIPrev = click->fICurr;
    436     click->fICurr.set(x, y);
    437 
    438     click->fPrev = click->fCurr;
    439     click->fCurr.iset(x, y);
    440     if (!target->globalToLocal(&click->fCurr)) {
    441         // on failure pretend the mouse didn't move
    442         click->fCurr = click->fPrev;
    443     }
    444 
    445     click->fState = Click::kMoved_State;
    446     click->fModifierKeys = modi;
    447     target->onClick(click);
    448 }
    449 
    450 void SkView::DoClickUp(Click* click, int x, int y, unsigned modi)
    451 {
    452     SkASSERT(click);
    453 
    454     SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
    455     if (NULL == target) {
    456         return;
    457     }
    458 
    459     click->fIPrev = click->fICurr;
    460     click->fICurr.set(x, y);
    461 
    462     click->fPrev = click->fCurr;
    463     click->fCurr.iset(x, y);
    464     if (!target->globalToLocal(&click->fCurr)) {
    465         // on failure pretend the mouse didn't move
    466         click->fCurr = click->fPrev;
    467     }
    468 
    469     click->fState = Click::kUp_State;
    470     click->fModifierKeys = modi;
    471     target->onClick(click);
    472 }
    473 
    474 //////////////////////////////////////////////////////////////////////
    475 
    476 void SkView::invokeLayout() {
    477     SkView::Layout* layout = this->getLayout();
    478 
    479     if (layout) {
    480         layout->layoutChildren(this);
    481     }
    482 }
    483 
    484 void SkView::onDraw(SkCanvas* canvas) {
    485     Artist* artist = this->getArtist();
    486 
    487     if (artist) {
    488         artist->draw(this, canvas);
    489     }
    490 }
    491 
    492 void SkView::onSizeChange() {}
    493 
    494 bool SkView::onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) {
    495     return true;
    496 }
    497 
    498 SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) {
    499     return NULL;
    500 }
    501 
    502 bool SkView::onClick(Click*) {
    503     return false;
    504 }
    505 
    506 bool SkView::handleInval(const SkRect*) {
    507     return false;
    508 }
    509 
    510 //////////////////////////////////////////////////////////////////////
    511 
    512 void SkView::getLocalBounds(SkRect* bounds) const {
    513     if (bounds) {
    514         bounds->set(0, 0, fWidth, fHeight);
    515     }
    516 }
    517 
    518 //////////////////////////////////////////////////////////////////////
    519 //////////////////////////////////////////////////////////////////////
    520 
    521 void SkView::detachFromParent_NoLayout() {
    522     this->validate();
    523     if (fParent == NULL) {
    524         return;
    525     }
    526 
    527     if (fContainsFocus) {
    528         (void)this->setFocusView(NULL);
    529     }
    530 
    531     this->inval(NULL);
    532 
    533     SkView* next = NULL;
    534 
    535     if (fNextSibling != this) {   // do we have any siblings
    536         fNextSibling->fPrevSibling = fPrevSibling;
    537         fPrevSibling->fNextSibling = fNextSibling;
    538         next = fNextSibling;
    539     }
    540 
    541     if (fParent->fFirstChild == this) {
    542         fParent->fFirstChild = next;
    543     }
    544 
    545     fParent = fNextSibling = fPrevSibling = NULL;
    546 
    547     this->validate();
    548     this->unref();
    549 }
    550 
    551 void SkView::detachFromParent() {
    552     this->validate();
    553     SkView* parent = fParent;
    554 
    555     if (parent) {
    556         this->detachFromParent_NoLayout();
    557         parent->invokeLayout();
    558     }
    559 }
    560 
    561 SkView* SkView::attachChildToBack(SkView* child) {
    562     this->validate();
    563     SkASSERT(child != this);
    564 
    565     if (child == NULL || fFirstChild == child)
    566         goto DONE;
    567 
    568     child->ref();
    569     child->detachFromParent_NoLayout();
    570 
    571     if (fFirstChild == NULL) {
    572         child->fNextSibling = child;
    573         child->fPrevSibling = child;
    574     } else {
    575         child->fNextSibling = fFirstChild;
    576         child->fPrevSibling = fFirstChild->fPrevSibling;
    577         fFirstChild->fPrevSibling->fNextSibling = child;
    578         fFirstChild->fPrevSibling = child;
    579     }
    580 
    581     fFirstChild = child;
    582     child->fParent = this;
    583     child->inval(NULL);
    584 
    585     this->validate();
    586     this->invokeLayout();
    587 DONE:
    588     return child;
    589 }
    590 
    591 SkView* SkView::attachChildToFront(SkView* child) {
    592     this->validate();
    593     SkASSERT(child != this);
    594 
    595     if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
    596         goto DONE;
    597 
    598     child->ref();
    599     child->detachFromParent_NoLayout();
    600 
    601     if (fFirstChild == NULL) {
    602         fFirstChild = child;
    603         child->fNextSibling = child;
    604         child->fPrevSibling = child;
    605     } else {
    606         child->fNextSibling = fFirstChild;
    607         child->fPrevSibling = fFirstChild->fPrevSibling;
    608         fFirstChild->fPrevSibling->fNextSibling = child;
    609         fFirstChild->fPrevSibling = child;
    610     }
    611 
    612     child->fParent = this;
    613     child->inval(NULL);
    614 
    615     this->validate();
    616     this->invokeLayout();
    617 DONE:
    618     return child;
    619 }
    620 
    621 void SkView::detachAllChildren() {
    622     this->validate();
    623     while (fFirstChild)
    624         fFirstChild->detachFromParent_NoLayout();
    625 }
    626 
    627 void SkView::localToGlobal(SkMatrix* matrix) const {
    628     if (matrix) {
    629         matrix->reset();
    630         const SkView* view = this;
    631         while (view)
    632         {
    633             matrix->preConcat(view->getLocalMatrix());
    634             matrix->preTranslate(-view->fLoc.fX, -view->fLoc.fY);
    635             view = view->fParent;
    636         }
    637     }
    638 }
    639 bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
    640 {
    641     SkASSERT(this);
    642 
    643     if (NULL != local) {
    644         SkMatrix m;
    645         this->localToGlobal(&m);
    646         if (!m.invert(&m)) {
    647             return false;
    648         }
    649         SkPoint p;
    650         m.mapXY(x, y, &p);
    651         local->set(p.fX, p.fY);
    652     }
    653 
    654     return true;
    655 }
    656 
    657 //////////////////////////////////////////////////////////////////
    658 
    659 /*    Even if the subclass overrides onInflate, they should always be
    660     sure to call the inherited method, so that we get called.
    661 */
    662 void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node) {
    663     SkScalar x, y;
    664 
    665     x = this->locX();
    666     y = this->locY();
    667     (void)dom.findScalar(node, "x", &x);
    668     (void)dom.findScalar(node, "y", &y);
    669     this->setLoc(x, y);
    670 
    671     x = this->width();
    672     y = this->height();
    673     (void)dom.findScalar(node, "width", &x);
    674     (void)dom.findScalar(node, "height", &y);
    675     this->setSize(x, y);
    676 
    677     // inflate the flags
    678 
    679     static const char* gFlagNames[] = {
    680         "visible", "enabled", "focusable", "flexH", "flexV"
    681     };
    682     SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
    683 
    684     bool     b;
    685     uint32_t flags = this->getFlags();
    686     for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
    687         if (dom.findBool(node, gFlagNames[i], &b))
    688             flags = SkSetClearShift(flags, b, i);
    689     this->setFlags(flags);
    690 }
    691 
    692 void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node) {
    693     this->onInflate(dom, node);
    694 }
    695 
    696 void SkView::onPostInflate(const SkTDict<SkView*>&) {
    697     // override in subclass as needed
    698 }
    699 
    700 void SkView::postInflate(const SkTDict<SkView*>& dict) {
    701     this->onPostInflate(dict);
    702 
    703     B2FIter    iter(this);
    704     SkView*    child;
    705     while ((child = iter.next()) != NULL)
    706         child->postInflate(dict);
    707 }
    708 
    709 //////////////////////////////////////////////////////////////////
    710 
    711 SkView* SkView::sendEventToParents(const SkEvent& evt) {
    712     SkView* parent = fParent;
    713 
    714     while (parent) {
    715         if (parent->doEvent(evt)) {
    716             return parent;
    717         }
    718         parent = parent->fParent;
    719     }
    720     return NULL;
    721 }
    722 
    723 SkView* SkView::sendQueryToParents(SkEvent* evt) {
    724     SkView* parent = fParent;
    725 
    726     while (parent) {
    727         if (parent->doQuery(evt)) {
    728             return parent;
    729         }
    730         parent = parent->fParent;
    731     }
    732     return NULL;
    733 }
    734 
    735 //////////////////////////////////////////////////////////////////
    736 //////////////////////////////////////////////////////////////////
    737 
    738 SkView::F2BIter::F2BIter(const SkView* parent) {
    739     fFirstChild = parent ? parent->fFirstChild : NULL;
    740     fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
    741 }
    742 
    743 SkView* SkView::F2BIter::next() {
    744     SkView* curr = fChild;
    745 
    746     if (fChild) {
    747         if (fChild == fFirstChild) {
    748             fChild = NULL;
    749         } else {
    750             fChild = fChild->fPrevSibling;
    751         }
    752     }
    753     return curr;
    754 }
    755 
    756 SkView::B2FIter::B2FIter(const SkView* parent) {
    757     fFirstChild = parent ? parent->fFirstChild : NULL;
    758     fChild = fFirstChild;
    759 }
    760 
    761 SkView* SkView::B2FIter::next() {
    762     SkView* curr = fChild;
    763 
    764     if (fChild) {
    765         SkView* next = fChild->fNextSibling;
    766         if (next == fFirstChild)
    767             next = NULL;
    768         fChild = next;
    769     }
    770     return curr;
    771 }
    772 
    773 //////////////////////////////////////////////////////////////////
    774 //////////////////////////////////////////////////////////////////
    775 
    776 #ifdef SK_DEBUG
    777 
    778 void SkView::validate() const {
    779 //    SkASSERT(this->getRefCnt() > 0 && this->getRefCnt() < 100);
    780     if (fParent) {
    781         SkASSERT(fNextSibling);
    782         SkASSERT(fPrevSibling);
    783     } else {
    784         bool nextNull = NULL == fNextSibling;
    785         bool prevNull = NULL == fNextSibling;
    786         SkASSERT(nextNull == prevNull);
    787     }
    788 }
    789 
    790 static inline void show_if_nonzero(const char name[], SkScalar value)
    791 {
    792     if (value)
    793         SkDebugf("%s=\"%g\"", name, value/65536.);
    794 }
    795 
    796 static void tab(int level)
    797 {
    798     for (int i = 0; i < level; i++)
    799         SkDebugf("    ");
    800 }
    801 
    802 static void dumpview(const SkView* view, int level, bool recurse)
    803 {
    804     tab(level);
    805 
    806     SkDebugf("<view");
    807     show_if_nonzero(" x", view->locX());
    808     show_if_nonzero(" y", view->locY());
    809     show_if_nonzero(" width", view->width());
    810     show_if_nonzero(" height", view->height());
    811 
    812     if (recurse)
    813     {
    814         SkView::B2FIter    iter(view);
    815         SkView*            child;
    816         bool            noChildren = true;
    817 
    818         while ((child = iter.next()) != NULL)
    819         {
    820             if (noChildren)
    821                 SkDebugf(">\n");
    822             noChildren = false;
    823             dumpview(child, level + 1, true);
    824         }
    825 
    826         if (!noChildren)
    827         {
    828             tab(level);
    829             SkDebugf("</view>\n");
    830         }
    831         else
    832             goto ONELINER;
    833     }
    834     else
    835     {
    836     ONELINER:
    837         SkDebugf(" />\n");
    838     }
    839 }
    840 
    841 void SkView::dump(bool recurse) const
    842 {
    843     dumpview(this, 0, recurse);
    844 }
    845 
    846 #endif
    847