Home | History | Annotate | Download | only in pdfwindow
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "fpdfsdk/include/pdfwindow/PWL_EditCtrl.h"
      8 
      9 #include "fpdfsdk/include/pdfwindow/PWL_Caret.h"
     10 #include "fpdfsdk/include/pdfwindow/PWL_FontMap.h"
     11 #include "fpdfsdk/include/pdfwindow/PWL_ScrollBar.h"
     12 #include "fpdfsdk/include/pdfwindow/PWL_Utils.h"
     13 #include "fpdfsdk/include/pdfwindow/PWL_Wnd.h"
     14 #include "public/fpdf_fwlevent.h"
     15 
     16 #define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001)
     17 #define IsFloatBigger(fa, fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb)))
     18 #define IsFloatSmaller(fa, fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb)))
     19 #define IsFloatEqual(fa, fb) IsFloatZero((fa) - (fb))
     20 
     21 CPWL_EditCtrl::CPWL_EditCtrl()
     22     : m_pEdit(NULL),
     23       m_pEditCaret(NULL),
     24       m_bMouseDown(FALSE),
     25       m_pEditNotify(NULL),
     26       m_nCharSet(DEFAULT_CHARSET),
     27       m_nCodePage(0) {
     28   m_pEdit = IFX_Edit::NewEdit();
     29   ASSERT(m_pEdit);
     30 }
     31 
     32 CPWL_EditCtrl::~CPWL_EditCtrl() {
     33   IFX_Edit::DelEdit(m_pEdit);
     34 }
     35 
     36 void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM& cp) {
     37   cp.eCursorType = FXCT_VBEAM;
     38 }
     39 
     40 void CPWL_EditCtrl::OnCreated() {
     41   SetFontSize(GetCreationParam().fFontSize);
     42 
     43   m_pEdit->SetFontMap(GetFontMap());
     44   m_pEdit->SetNotify(this);
     45   m_pEdit->Initialize();
     46 }
     47 
     48 FX_BOOL CPWL_EditCtrl::IsWndHorV() {
     49   CFX_Matrix mt = GetWindowMatrix();
     50   CPDF_Point point1(0, 1);
     51   CPDF_Point point2(1, 1);
     52 
     53   mt.Transform(point1.x, point1.y);
     54   mt.Transform(point2.x, point2.y);
     55 
     56   return point2.y == point1.y;
     57 }
     58 
     59 void CPWL_EditCtrl::SetCursor() {
     60   if (IsValid()) {
     61     if (IFX_SystemHandler* pSH = GetSystemHandler()) {
     62       if (IsWndHorV())
     63         pSH->SetCursor(FXCT_VBEAM);
     64       else
     65         pSH->SetCursor(FXCT_HBEAM);
     66     }
     67   }
     68 }
     69 
     70 void CPWL_EditCtrl::RePosChildWnd() {
     71   m_pEdit->SetPlateRect(GetClientRect());
     72 }
     73 
     74 void CPWL_EditCtrl::OnNotify(CPWL_Wnd* pWnd,
     75                              FX_DWORD msg,
     76                              intptr_t wParam,
     77                              intptr_t lParam) {
     78   CPWL_Wnd::OnNotify(pWnd, msg, wParam, lParam);
     79 
     80   switch (msg) {
     81     case PNM_SETSCROLLINFO:
     82       switch (wParam) {
     83         case SBT_VSCROLL:
     84           if (CPWL_Wnd* pChild = GetVScrollBar()) {
     85             pChild->OnNotify(pWnd, PNM_SETSCROLLINFO, wParam, lParam);
     86           }
     87           break;
     88       }
     89       break;
     90     case PNM_SETSCROLLPOS:
     91       switch (wParam) {
     92         case SBT_VSCROLL:
     93           if (CPWL_Wnd* pChild = GetVScrollBar()) {
     94             pChild->OnNotify(pWnd, PNM_SETSCROLLPOS, wParam, lParam);
     95           }
     96           break;
     97       }
     98       break;
     99     case PNM_SCROLLWINDOW: {
    100       FX_FLOAT fPos = *(FX_FLOAT*)lParam;
    101       switch (wParam) {
    102         case SBT_VSCROLL:
    103           m_pEdit->SetScrollPos(CPDF_Point(m_pEdit->GetScrollPos().x, fPos));
    104           break;
    105       }
    106     } break;
    107     case PNM_SETCARETINFO: {
    108       if (PWL_CARET_INFO* pCaretInfo = (PWL_CARET_INFO*)wParam) {
    109         SetCaret(pCaretInfo->bVisible, pCaretInfo->ptHead, pCaretInfo->ptFoot);
    110       }
    111     } break;
    112   }
    113 }
    114 
    115 void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM& cp) {
    116   if (!IsReadOnly())
    117     CreateEditCaret(cp);
    118 }
    119 
    120 void CPWL_EditCtrl::CreateEditCaret(const PWL_CREATEPARAM& cp) {
    121   if (!m_pEditCaret) {
    122     m_pEditCaret = new CPWL_Caret;
    123     m_pEditCaret->SetInvalidRect(GetClientRect());
    124 
    125     PWL_CREATEPARAM ecp = cp;
    126     ecp.pParentWnd = this;
    127     ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP;
    128     ecp.dwBorderWidth = 0;
    129     ecp.nBorderStyle = PBS_SOLID;
    130     ecp.rcRectWnd = CPDF_Rect(0, 0, 0, 0);
    131 
    132     m_pEditCaret->Create(ecp);
    133   }
    134 }
    135 
    136 void CPWL_EditCtrl::SetFontSize(FX_FLOAT fFontSize) {
    137   m_pEdit->SetFontSize(fFontSize);
    138 }
    139 
    140 FX_FLOAT CPWL_EditCtrl::GetFontSize() const {
    141   return m_pEdit->GetFontSize();
    142 }
    143 
    144 FX_BOOL CPWL_EditCtrl::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag) {
    145   if (m_bMouseDown)
    146     return TRUE;
    147 
    148   FX_BOOL bRet = CPWL_Wnd::OnKeyDown(nChar, nFlag);
    149 
    150   // FILTER
    151   switch (nChar) {
    152     default:
    153       return FALSE;
    154     case FWL_VKEY_Delete:
    155     case FWL_VKEY_Up:
    156     case FWL_VKEY_Down:
    157     case FWL_VKEY_Left:
    158     case FWL_VKEY_Right:
    159     case FWL_VKEY_Home:
    160     case FWL_VKEY_End:
    161     case FWL_VKEY_Insert:
    162     case 'C':
    163     case 'V':
    164     case 'X':
    165     case 'A':
    166     case 'Z':
    167     case 'c':
    168     case 'v':
    169     case 'x':
    170     case 'a':
    171     case 'z':
    172       break;
    173   }
    174 
    175   if (nChar == FWL_VKEY_Delete) {
    176     if (m_pEdit->IsSelected())
    177       nChar = FWL_VKEY_Unknown;
    178   }
    179 
    180   switch (nChar) {
    181     case FWL_VKEY_Delete:
    182       Delete();
    183       return TRUE;
    184     case FWL_VKEY_Insert:
    185       if (IsSHIFTpressed(nFlag))
    186         PasteText();
    187       return TRUE;
    188     case FWL_VKEY_Up:
    189       m_pEdit->OnVK_UP(IsSHIFTpressed(nFlag), FALSE);
    190       return TRUE;
    191     case FWL_VKEY_Down:
    192       m_pEdit->OnVK_DOWN(IsSHIFTpressed(nFlag), FALSE);
    193       return TRUE;
    194     case FWL_VKEY_Left:
    195       m_pEdit->OnVK_LEFT(IsSHIFTpressed(nFlag), FALSE);
    196       return TRUE;
    197     case FWL_VKEY_Right:
    198       m_pEdit->OnVK_RIGHT(IsSHIFTpressed(nFlag), FALSE);
    199       return TRUE;
    200     case FWL_VKEY_Home:
    201       m_pEdit->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
    202       return TRUE;
    203     case FWL_VKEY_End:
    204       m_pEdit->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
    205       return TRUE;
    206     case FWL_VKEY_Unknown:
    207       if (!IsSHIFTpressed(nFlag))
    208         Clear();
    209       else
    210         CutText();
    211       return TRUE;
    212     default:
    213       break;
    214   }
    215 
    216   return bRet;
    217 }
    218 
    219 FX_BOOL CPWL_EditCtrl::OnChar(FX_WORD nChar, FX_DWORD nFlag) {
    220   if (m_bMouseDown)
    221     return TRUE;
    222 
    223   CPWL_Wnd::OnChar(nChar, nFlag);
    224 
    225   // FILTER
    226   switch (nChar) {
    227     case 0x0A:
    228     case 0x1B:
    229       return FALSE;
    230     default:
    231       break;
    232   }
    233 
    234   FX_BOOL bCtrl = IsCTRLpressed(nFlag);
    235   FX_BOOL bAlt = IsALTpressed(nFlag);
    236   FX_BOOL bShift = IsSHIFTpressed(nFlag);
    237 
    238   FX_WORD word = nChar;
    239 
    240   if (bCtrl && !bAlt) {
    241     switch (nChar) {
    242       case 'C' - 'A' + 1:
    243         CopyText();
    244         return TRUE;
    245       case 'V' - 'A' + 1:
    246         PasteText();
    247         return TRUE;
    248       case 'X' - 'A' + 1:
    249         CutText();
    250         return TRUE;
    251       case 'A' - 'A' + 1:
    252         SelectAll();
    253         return TRUE;
    254       case 'Z' - 'A' + 1:
    255         if (bShift)
    256           Redo();
    257         else
    258           Undo();
    259         return TRUE;
    260       default:
    261         if (nChar < 32)
    262           return FALSE;
    263     }
    264   }
    265 
    266   if (IsReadOnly())
    267     return TRUE;
    268 
    269   if (m_pEdit->IsSelected() && word == FWL_VKEY_Back)
    270     word = FWL_VKEY_Unknown;
    271 
    272   Clear();
    273 
    274   switch (word) {
    275     case FWL_VKEY_Back:
    276       Backspace();
    277       break;
    278     case FWL_VKEY_Return:
    279       InsertReturn();
    280       break;
    281     case FWL_VKEY_Unknown:
    282       break;
    283     default:
    284       if (IsINSERTpressed(nFlag))
    285         Delete();
    286       InsertWord(word, GetCharSet());
    287       break;
    288   }
    289 
    290   return TRUE;
    291 }
    292 
    293 FX_BOOL CPWL_EditCtrl::OnLButtonDown(const CPDF_Point& point, FX_DWORD nFlag) {
    294   CPWL_Wnd::OnLButtonDown(point, nFlag);
    295 
    296   if (ClientHitTest(point)) {
    297     if (m_bMouseDown)
    298       InvalidateRect();
    299 
    300     m_bMouseDown = TRUE;
    301     SetCapture();
    302 
    303     m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
    304   }
    305 
    306   return TRUE;
    307 }
    308 
    309 FX_BOOL CPWL_EditCtrl::OnLButtonUp(const CPDF_Point& point, FX_DWORD nFlag) {
    310   CPWL_Wnd::OnLButtonUp(point, nFlag);
    311 
    312   if (m_bMouseDown) {
    313     // can receive keybord message
    314     if (ClientHitTest(point) && !IsFocused())
    315       SetFocus();
    316 
    317     ReleaseCapture();
    318     m_bMouseDown = FALSE;
    319   }
    320 
    321   return TRUE;
    322 }
    323 
    324 FX_BOOL CPWL_EditCtrl::OnMouseMove(const CPDF_Point& point, FX_DWORD nFlag) {
    325   CPWL_Wnd::OnMouseMove(point, nFlag);
    326 
    327   if (m_bMouseDown)
    328     m_pEdit->OnMouseMove(point, FALSE, FALSE);
    329 
    330   return TRUE;
    331 }
    332 
    333 CPDF_Rect CPWL_EditCtrl::GetContentRect() const {
    334   return m_pEdit->GetContentRect();
    335 }
    336 
    337 void CPWL_EditCtrl::SetEditCaret(FX_BOOL bVisible) {
    338   CPDF_Point ptHead(0, 0), ptFoot(0, 0);
    339 
    340   if (bVisible) {
    341     GetCaretInfo(ptHead, ptFoot);
    342   }
    343 
    344   CPVT_WordPlace wpTemp = m_pEdit->GetCaretWordPlace();
    345   IOnSetCaret(bVisible, ptHead, ptFoot, wpTemp);
    346 }
    347 
    348 void CPWL_EditCtrl::GetCaretInfo(CPDF_Point& ptHead, CPDF_Point& ptFoot) const {
    349   if (IFX_Edit_Iterator* pIterator = m_pEdit->GetIterator()) {
    350     pIterator->SetAt(m_pEdit->GetCaret());
    351     CPVT_Word word;
    352     CPVT_Line line;
    353     if (pIterator->GetWord(word)) {
    354       ptHead.x = word.ptWord.x + word.fWidth;
    355       ptHead.y = word.ptWord.y + word.fAscent;
    356       ptFoot.x = word.ptWord.x + word.fWidth;
    357       ptFoot.y = word.ptWord.y + word.fDescent;
    358     } else if (pIterator->GetLine(line)) {
    359       ptHead.x = line.ptLine.x;
    360       ptHead.y = line.ptLine.y + line.fLineAscent;
    361       ptFoot.x = line.ptLine.x;
    362       ptFoot.y = line.ptLine.y + line.fLineDescent;
    363     }
    364   }
    365 }
    366 
    367 void CPWL_EditCtrl::GetCaretPos(int32_t& x, int32_t& y) const {
    368   CPDF_Point ptHead(0, 0), ptFoot(0, 0);
    369 
    370   GetCaretInfo(ptHead, ptFoot);
    371 
    372   PWLtoWnd(ptHead, x, y);
    373 }
    374 
    375 void CPWL_EditCtrl::SetCaret(FX_BOOL bVisible,
    376                              const CPDF_Point& ptHead,
    377                              const CPDF_Point& ptFoot) {
    378   if (m_pEditCaret) {
    379     if (!IsFocused() || m_pEdit->IsSelected())
    380       bVisible = FALSE;
    381 
    382     m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot);
    383   }
    384 }
    385 
    386 CFX_WideString CPWL_EditCtrl::GetText() const {
    387   return m_pEdit->GetText();
    388 }
    389 
    390 void CPWL_EditCtrl::SetSel(int32_t nStartChar, int32_t nEndChar) {
    391   m_pEdit->SetSel(nStartChar, nEndChar);
    392 }
    393 
    394 void CPWL_EditCtrl::GetSel(int32_t& nStartChar, int32_t& nEndChar) const {
    395   m_pEdit->GetSel(nStartChar, nEndChar);
    396 }
    397 
    398 void CPWL_EditCtrl::Clear() {
    399   if (!IsReadOnly())
    400     m_pEdit->Clear();
    401 }
    402 
    403 void CPWL_EditCtrl::SelectAll() {
    404   m_pEdit->SelectAll();
    405 }
    406 
    407 void CPWL_EditCtrl::Paint() {
    408   if (m_pEdit)
    409     m_pEdit->Paint();
    410 }
    411 
    412 void CPWL_EditCtrl::EnableRefresh(FX_BOOL bRefresh) {
    413   if (m_pEdit)
    414     m_pEdit->EnableRefresh(bRefresh);
    415 }
    416 
    417 int32_t CPWL_EditCtrl::GetCaret() const {
    418   if (m_pEdit)
    419     return m_pEdit->GetCaret();
    420 
    421   return -1;
    422 }
    423 
    424 void CPWL_EditCtrl::SetCaret(int32_t nPos) {
    425   if (m_pEdit)
    426     m_pEdit->SetCaret(nPos);
    427 }
    428 
    429 int32_t CPWL_EditCtrl::GetTotalWords() const {
    430   if (m_pEdit)
    431     return m_pEdit->GetTotalWords();
    432 
    433   return 0;
    434 }
    435 
    436 void CPWL_EditCtrl::SetScrollPos(const CPDF_Point& point) {
    437   if (m_pEdit)
    438     m_pEdit->SetScrollPos(point);
    439 }
    440 
    441 CPDF_Point CPWL_EditCtrl::GetScrollPos() const {
    442   if (m_pEdit)
    443     return m_pEdit->GetScrollPos();
    444 
    445   return CPDF_Point(0.0f, 0.0f);
    446 }
    447 
    448 CPDF_Font* CPWL_EditCtrl::GetCaretFont() const {
    449   int32_t nFontIndex = 0;
    450 
    451   if (IFX_Edit_Iterator* pIterator = m_pEdit->GetIterator()) {
    452     pIterator->SetAt(m_pEdit->GetCaret());
    453     CPVT_Word word;
    454     CPVT_Section section;
    455     if (pIterator->GetWord(word)) {
    456       nFontIndex = word.nFontIndex;
    457     } else if (HasFlag(PES_RICH)) {
    458       if (pIterator->GetSection(section)) {
    459         nFontIndex = section.WordProps.nFontIndex;
    460       }
    461     }
    462   }
    463 
    464   if (IFX_Edit_FontMap* pFontMap = GetFontMap())
    465     return pFontMap->GetPDFFont(nFontIndex);
    466 
    467   return NULL;
    468 }
    469 
    470 FX_FLOAT CPWL_EditCtrl::GetCaretFontSize() const {
    471   FX_FLOAT fFontSize = GetFontSize();
    472 
    473   if (IFX_Edit_Iterator* pIterator = m_pEdit->GetIterator()) {
    474     pIterator->SetAt(m_pEdit->GetCaret());
    475     CPVT_Word word;
    476     CPVT_Section section;
    477     if (pIterator->GetWord(word)) {
    478       fFontSize = word.fFontSize;
    479     } else if (HasFlag(PES_RICH)) {
    480       if (pIterator->GetSection(section)) {
    481         fFontSize = section.WordProps.fFontSize;
    482       }
    483     }
    484   }
    485 
    486   return fFontSize;
    487 }
    488 
    489 void CPWL_EditCtrl::SetText(const FX_WCHAR* csText) {
    490   m_pEdit->SetText(csText);
    491 }
    492 
    493 void CPWL_EditCtrl::CopyText() {}
    494 
    495 void CPWL_EditCtrl::PasteText() {}
    496 
    497 void CPWL_EditCtrl::CutText() {}
    498 
    499 void CPWL_EditCtrl::ShowVScrollBar(FX_BOOL bShow) {}
    500 
    501 void CPWL_EditCtrl::InsertText(const FX_WCHAR* csText) {
    502   if (!IsReadOnly())
    503     m_pEdit->InsertText(csText);
    504 }
    505 
    506 void CPWL_EditCtrl::InsertWord(FX_WORD word, int32_t nCharset) {
    507   if (!IsReadOnly())
    508     m_pEdit->InsertWord(word, nCharset);
    509 }
    510 
    511 void CPWL_EditCtrl::InsertReturn() {
    512   if (!IsReadOnly())
    513     m_pEdit->InsertReturn();
    514 }
    515 
    516 void CPWL_EditCtrl::Delete() {
    517   if (!IsReadOnly())
    518     m_pEdit->Delete();
    519 }
    520 
    521 void CPWL_EditCtrl::Backspace() {
    522   if (!IsReadOnly())
    523     m_pEdit->Backspace();
    524 }
    525 
    526 FX_BOOL CPWL_EditCtrl::CanUndo() const {
    527   return !IsReadOnly() && m_pEdit->CanUndo();
    528 }
    529 
    530 FX_BOOL CPWL_EditCtrl::CanRedo() const {
    531   return !IsReadOnly() && m_pEdit->CanRedo();
    532 }
    533 
    534 void CPWL_EditCtrl::Redo() {
    535   if (CanRedo())
    536     m_pEdit->Redo();
    537 }
    538 
    539 void CPWL_EditCtrl::Undo() {
    540   if (CanUndo())
    541     m_pEdit->Undo();
    542 }
    543 
    544 void CPWL_EditCtrl::IOnSetScrollInfoY(FX_FLOAT fPlateMin,
    545                                       FX_FLOAT fPlateMax,
    546                                       FX_FLOAT fContentMin,
    547                                       FX_FLOAT fContentMax,
    548                                       FX_FLOAT fSmallStep,
    549                                       FX_FLOAT fBigStep) {
    550   PWL_SCROLL_INFO Info;
    551 
    552   Info.fPlateWidth = fPlateMax - fPlateMin;
    553   Info.fContentMin = fContentMin;
    554   Info.fContentMax = fContentMax;
    555   Info.fSmallStep = fSmallStep;
    556   Info.fBigStep = fBigStep;
    557 
    558   OnNotify(this, PNM_SETSCROLLINFO, SBT_VSCROLL, (intptr_t)&Info);
    559 
    560   if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) ||
    561       IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) {
    562     ShowVScrollBar(FALSE);
    563   } else {
    564     ShowVScrollBar(TRUE);
    565   }
    566 }
    567 
    568 void CPWL_EditCtrl::IOnSetScrollPosY(FX_FLOAT fy) {
    569   OnNotify(this, PNM_SETSCROLLPOS, SBT_VSCROLL, (intptr_t)&fy);
    570 }
    571 
    572 void CPWL_EditCtrl::IOnSetCaret(FX_BOOL bVisible,
    573                                 const CPDF_Point& ptHead,
    574                                 const CPDF_Point& ptFoot,
    575                                 const CPVT_WordPlace& place) {
    576   PWL_CARET_INFO cInfo;
    577   cInfo.bVisible = bVisible;
    578   cInfo.ptHead = ptHead;
    579   cInfo.ptFoot = ptFoot;
    580 
    581   OnNotify(this, PNM_SETCARETINFO, (intptr_t)&cInfo, (intptr_t)NULL);
    582 }
    583 
    584 void CPWL_EditCtrl::IOnCaretChange(const CPVT_SecProps& secProps,
    585                                    const CPVT_WordProps& wordProps) {}
    586 
    587 void CPWL_EditCtrl::IOnContentChange(const CPDF_Rect& rcContent) {
    588   if (IsValid()) {
    589     if (m_pEditNotify) {
    590       m_pEditNotify->OnContentChange(rcContent);
    591     }
    592   }
    593 }
    594 
    595 void CPWL_EditCtrl::IOnInvalidateRect(CPDF_Rect* pRect) {
    596   InvalidateRect(pRect);
    597 }
    598 
    599 int32_t CPWL_EditCtrl::GetCharSet() const {
    600   return m_nCharSet < 0 ? DEFAULT_CHARSET : m_nCharSet;
    601 }
    602 
    603 void CPWL_EditCtrl::GetTextRange(const CPDF_Rect& rect,
    604                                  int32_t& nStartChar,
    605                                  int32_t& nEndChar) const {
    606   nStartChar = m_pEdit->WordPlaceToWordIndex(
    607       m_pEdit->SearchWordPlace(CPDF_Point(rect.left, rect.top)));
    608   nEndChar = m_pEdit->WordPlaceToWordIndex(
    609       m_pEdit->SearchWordPlace(CPDF_Point(rect.right, rect.bottom)));
    610 }
    611 
    612 CFX_WideString CPWL_EditCtrl::GetText(int32_t& nStartChar,
    613                                       int32_t& nEndChar) const {
    614   CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStartChar);
    615   CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEndChar);
    616   return m_pEdit->GetRangeText(CPVT_WordRange(wpStart, wpEnd));
    617 }
    618 
    619 void CPWL_EditCtrl::SetReadyToInput() {
    620   if (m_bMouseDown) {
    621     ReleaseCapture();
    622     m_bMouseDown = FALSE;
    623   }
    624 }
    625