// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "../../include/fxedit/fxet_stub.h" #include "../../include/fxedit/fxet_edit.h" #include "../../include/fxedit/fxet_list.h" /* ------------------------------- CFX_ListItem ---------------------------------- */ CFX_ListItem::CFX_ListItem() : m_pEdit(NULL), m_bSelected(FALSE), m_bCaret(FALSE), m_rcListItem(0.0f,0.0f,0.0f,0.0f) { m_pEdit = IFX_Edit::NewEdit(); ASSERT(m_pEdit != NULL); m_pEdit->SetAlignmentV(1); m_pEdit->Initialize(); } CFX_ListItem::~CFX_ListItem() { IFX_Edit::DelEdit(m_pEdit); } void CFX_ListItem::SetFontMap(IFX_Edit_FontMap * pFontMap) { if (m_pEdit) m_pEdit->SetFontMap(pFontMap); } IFX_Edit* CFX_ListItem::GetEdit() const { return m_pEdit; } IFX_Edit_Iterator* CFX_ListItem::GetIterator() const { if (m_pEdit) return m_pEdit->GetIterator(); return NULL; } void CFX_ListItem::SetRect(const CLST_Rect & rect) { m_rcListItem = rect; } CLST_Rect CFX_ListItem::GetRect() const { return m_rcListItem; } FX_BOOL CFX_ListItem::IsSelected() const { return m_bSelected; } void CFX_ListItem::SetSelect(FX_BOOL bSelected) { m_bSelected = bSelected; } FX_BOOL CFX_ListItem::IsCaret() const { return m_bCaret; } void CFX_ListItem::SetCaret(FX_BOOL bCaret) { m_bCaret = bCaret; } void CFX_ListItem::SetText(FX_LPCWSTR text) { if (m_pEdit) m_pEdit->SetText(text); } void CFX_ListItem::SetFontSize(FX_FLOAT fFontSize) { if (m_pEdit) m_pEdit->SetFontSize(fFontSize); } FX_FLOAT CFX_ListItem::GetItemHeight() const { if (m_pEdit) return m_pEdit->GetContentRect().Height(); return 0.0f; } FX_WORD CFX_ListItem::GetFirstChar() const { CPVT_Word word; if (IFX_Edit_Iterator* pIterator = GetIterator()) { pIterator->SetAt(1); pIterator->GetWord(word); } return word.Word; } CFX_WideString CFX_ListItem::GetText() const { if (m_pEdit) return m_pEdit->GetText(); return L""; } /* ------------------------------------ CFX_List --------------------------------- */ CFX_List::CFX_List() : m_pFontMap(NULL), m_fFontSize(0.0f), m_bMultiple(FALSE) { } CFX_List::~CFX_List() { Empty(); } void CFX_List::Empty() { for (FX_INT32 i=0,sz=m_aListItems.GetSize(); iSetFontMap(m_pFontMap); pListItem->SetFontSize(m_fFontSize); pListItem->SetText(str); m_aListItems.Add(pListItem); } } void CFX_List::ReArrange(FX_INT32 nItemIndex) { FX_FLOAT fPosY = 0.0f; if (CFX_ListItem * pPrevItem = m_aListItems.GetAt(nItemIndex - 1)) fPosY = pPrevItem->GetRect().bottom; for (FX_INT32 i=nItemIndex,sz=m_aListItems.GetSize(); iGetItemHeight(); pListItem->SetRect(CLST_Rect(0.0f,fPosY,0.0f,fPosY + fListItemHeight)); fPosY += fListItemHeight; } } SetContentRect(CLST_Rect(0.0f,0.0f,0.0f,fPosY)); } IFX_Edit * CFX_List::GetItemEdit(FX_INT32 nIndex) const { if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex)) { return pListItem->GetEdit(); } return NULL; } FX_INT32 CFX_List::GetCount() const { return m_aListItems.GetSize(); } CPDF_Rect CFX_List::GetPlateRect() const { return CFX_ListContainer::GetPlateRect(); } CPDF_Rect CFX_List::GetContentRect() const { return InnerToOuter(CFX_ListContainer::GetContentRect()); } FX_FLOAT CFX_List::GetFontSize() const { return m_fFontSize; } FX_INT32 CFX_List::GetItemIndex(const CPDF_Point & point) const { CPDF_Point pt = OuterToInner(point); FX_BOOL bFirst = TRUE; FX_BOOL bLast = TRUE; for (FX_INT32 i=0,sz=m_aListItems.GetSize(); iGetRect(); if (FX_EDIT_IsFloatBigger(pt.y, rcListItem.top)) { bFirst = FALSE; } if (FX_EDIT_IsFloatSmaller(pt.y, rcListItem.bottom)) { bLast = FALSE; } if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom) { return i; } } } if (bFirst) return 0; if (bLast) return m_aListItems.GetSize()-1; return -1; } FX_FLOAT CFX_List::GetFirstHeight() const { if (CFX_ListItem * pListItem = m_aListItems.GetAt(0)) { return pListItem->GetItemHeight(); } return 1.0f; } FX_INT32 CFX_List::GetFirstSelected() const { for (FX_INT32 i=0,sz=m_aListItems.GetSize(); iIsSelected()) return i; } } return -1; } FX_INT32 CFX_List::GetLastSelected() const { for (FX_INT32 i=m_aListItems.GetSize()-1; i>=0; i--) { if (CFX_ListItem * pListItem = m_aListItems.GetAt(i)) { if (pListItem->IsSelected()) return i; } } return -1; } FX_WCHAR CFX_List::Toupper(FX_WCHAR c) const { if ( (c >= 'a') && (c <= 'z') ) c = c - ('a' - 'A'); return c; } FX_INT32 CFX_List::FindNext(FX_INT32 nIndex,FX_WCHAR nChar) const { FX_INT32 nCircleIndex = nIndex; for (FX_INT32 i=0,sz=m_aListItems.GetSize(); i= sz) nCircleIndex = 0; if (CFX_ListItem * pListItem = m_aListItems.GetAt(nCircleIndex)) { if (Toupper(pListItem->GetFirstChar()) == Toupper(nChar)) return nCircleIndex; } } return nCircleIndex; } CPDF_Rect CFX_List::GetItemRect(FX_INT32 nIndex) const { if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex)) { CPDF_Rect rcItem = pListItem->GetRect(); rcItem.left = 0.0f; rcItem.right = GetPlateRect().Width(); return InnerToOuter(rcItem); } return CPDF_Rect(); } FX_BOOL CFX_List::IsItemSelected(FX_INT32 nIndex) const { if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex)) { return pListItem->IsSelected(); } return FALSE; } void CFX_List::SetItemSelect(FX_INT32 nItemIndex, FX_BOOL bSelected) { if (CFX_ListItem * pListItem = m_aListItems.GetAt(nItemIndex)) { pListItem->SetSelect(bSelected); } } void CFX_List::SetItemCaret(FX_INT32 nItemIndex, FX_BOOL bCaret) { if (CFX_ListItem * pListItem = m_aListItems.GetAt(nItemIndex)) { pListItem->SetCaret(bCaret); } } void CFX_List::SetMultipleSel(FX_BOOL bMultiple) { m_bMultiple = bMultiple; } FX_BOOL CFX_List::IsMultipleSel() const { return m_bMultiple; } FX_BOOL CFX_List::IsValid(FX_INT32 nItemIndex) const { return nItemIndex >= 0 && nItemIndex < m_aListItems.GetSize(); } CFX_WideString CFX_List::GetItemText(FX_INT32 nIndex) const { if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex)) { return pListItem->GetText(); } return L""; } /* ------------------------------------ CPLST_Select ---------------------------------- */ CPLST_Select::CPLST_Select() { } CPLST_Select::~CPLST_Select() { for (FX_INT32 i=0,sz=m_aItems.GetSize(); inState = 1; } } } void CPLST_Select::Add(FX_INT32 nBeginIndex, FX_INT32 nEndIndex) { if (nBeginIndex > nEndIndex) { FX_INT32 nTemp = nEndIndex; nEndIndex = nBeginIndex; nBeginIndex = nTemp; } for (FX_INT32 i=nBeginIndex; i<=nEndIndex; i++) Add(i); } void CPLST_Select::Sub(FX_INT32 nItemIndex) { for (FX_INT32 i=m_aItems.GetSize()-1; i>=0; i--) { if (CPLST_Select_Item * pItem = m_aItems.GetAt(i)) if (pItem->nItemIndex == nItemIndex) pItem->nState = -1; } } void CPLST_Select::Sub(FX_INT32 nBeginIndex, FX_INT32 nEndIndex) { if (nBeginIndex > nEndIndex) { FX_INT32 nTemp = nEndIndex; nEndIndex = nBeginIndex; nBeginIndex = nTemp; } for (FX_INT32 i=nBeginIndex; i<=nEndIndex; i++) Sub(i); } FX_INT32 CPLST_Select::Find(FX_INT32 nItemIndex) const { for (FX_INT32 i=0,sz=m_aItems.GetSize(); inItemIndex == nItemIndex) return i; } } return -1; } FX_BOOL CPLST_Select::IsExist(FX_INT32 nItemIndex) const { return Find(nItemIndex) >= 0; } FX_INT32 CPLST_Select::GetCount() const { return m_aItems.GetSize(); } FX_INT32 CPLST_Select::GetItemIndex(FX_INT32 nIndex) const { if (nIndex >= 0 && nIndex < m_aItems.GetSize()) if (CPLST_Select_Item * pItem = m_aItems.GetAt(nIndex)) return pItem->nItemIndex; return -1; } FX_INT32 CPLST_Select::GetState(FX_INT32 nIndex) const { if (nIndex >= 0 && nIndex < m_aItems.GetSize()) if (CPLST_Select_Item * pItem = m_aItems.GetAt(nIndex)) return pItem->nState; return 0; } void CPLST_Select::DeselectAll() { for (FX_INT32 i=0,sz=m_aItems.GetSize(); inState = -1; } } } void CPLST_Select::Done() { for (FX_INT32 i=m_aItems.GetSize()-1; i>=0; i--) { if (CPLST_Select_Item * pItem = m_aItems.GetAt(i)) { if (pItem->nState == -1) { delete pItem; m_aItems.RemoveAt(i); } else { pItem->nState = 0; } } } } /* ------------------------------------ CFX_ListCtrl --------------------------------- */ CFX_ListCtrl::CFX_ListCtrl() : m_pNotify(NULL), m_ptScrollPos(0.0f,0.0f), m_nSelItem(-1), m_nFootIndex(-1), m_bCtrlSel(FALSE), m_nCaretIndex(-1), m_bNotifyFlag(FALSE) { } CFX_ListCtrl::~CFX_ListCtrl() { } void CFX_ListCtrl::SetNotify(IFX_List_Notify * pNotify) { m_pNotify = pNotify; } CPDF_Point CFX_ListCtrl::InToOut(const CPDF_Point & point) const { CPDF_Rect rcPlate = GetPlateRect(); return CPDF_Point(point.x - (m_ptScrollPos.x - rcPlate.left), point.y - (m_ptScrollPos.y - rcPlate.top)); } CPDF_Point CFX_ListCtrl::OutToIn(const CPDF_Point & point) const { CPDF_Rect rcPlate = GetPlateRect(); return CPDF_Point(point.x + (m_ptScrollPos.x - rcPlate.left), point.y + (m_ptScrollPos.y - rcPlate.top)); } CPDF_Rect CFX_ListCtrl::InToOut(const CPDF_Rect & rect) const { CPDF_Point ptLeftBottom = InToOut(CPDF_Point(rect.left,rect.bottom)); CPDF_Point ptRightTop = InToOut(CPDF_Point(rect.right,rect.top)); return CPDF_Rect(ptLeftBottom.x,ptLeftBottom.y,ptRightTop.x,ptRightTop.y); } CPDF_Rect CFX_ListCtrl::OutToIn(const CPDF_Rect & rect) const { CPDF_Point ptLeftBottom = OutToIn(CPDF_Point(rect.left,rect.bottom)); CPDF_Point ptRightTop = OutToIn(CPDF_Point(rect.right,rect.top)); return CPDF_Rect(ptLeftBottom.x,ptLeftBottom.y,ptRightTop.x,ptRightTop.y); } void CFX_ListCtrl::OnMouseDown(const CPDF_Point & point,FX_BOOL bShift,FX_BOOL bCtrl) { FX_INT32 nHitIndex = this->GetItemIndex(point); if (IsMultipleSel()) { if (bCtrl) { if (IsItemSelected(nHitIndex)) { m_aSelItems.Sub(nHitIndex); SelectItems(); m_bCtrlSel = FALSE; } else { m_aSelItems.Add(nHitIndex); SelectItems(); m_bCtrlSel = TRUE; } m_nFootIndex = nHitIndex; } else if (bShift) { m_aSelItems.DeselectAll(); m_aSelItems.Add(m_nFootIndex,nHitIndex); SelectItems(); } else { m_aSelItems.DeselectAll(); m_aSelItems.Add(nHitIndex); SelectItems(); m_nFootIndex = nHitIndex; } SetCaret(nHitIndex); } else { SetSingleSelect(nHitIndex); } if (!this->IsItemVisible(nHitIndex)) this->ScrollToListItem(nHitIndex); } void CFX_ListCtrl::OnMouseMove(const CPDF_Point & point,FX_BOOL bShift,FX_BOOL bCtrl) { FX_INT32 nHitIndex = this->GetItemIndex(point); if (IsMultipleSel()) { if (bCtrl) { if (m_bCtrlSel) m_aSelItems.Add(m_nFootIndex,nHitIndex); else m_aSelItems.Sub(m_nFootIndex,nHitIndex); SelectItems(); } else { m_aSelItems.DeselectAll(); m_aSelItems.Add(m_nFootIndex,nHitIndex); SelectItems(); } SetCaret(nHitIndex); } else { SetSingleSelect(nHitIndex); } if (!this->IsItemVisible(nHitIndex)) this->ScrollToListItem(nHitIndex); } void CFX_ListCtrl::OnVK(FX_INT32 nItemIndex,FX_BOOL bShift,FX_BOOL bCtrl) { if (IsMultipleSel()) { if (nItemIndex >= 0 && nItemIndex < GetCount()) { if (bCtrl) { } else if (bShift) { m_aSelItems.DeselectAll(); m_aSelItems.Add(m_nFootIndex,nItemIndex); SelectItems(); } else { m_aSelItems.DeselectAll(); m_aSelItems.Add(nItemIndex); SelectItems(); m_nFootIndex = nItemIndex; } SetCaret(nItemIndex); } } else { SetSingleSelect(nItemIndex); } if (!this->IsItemVisible(nItemIndex)) this->ScrollToListItem(nItemIndex); } void CFX_ListCtrl::OnVK_UP(FX_BOOL bShift,FX_BOOL bCtrl) { OnVK(IsMultipleSel() ? GetCaret()-1 : GetSelect()-1, bShift, bCtrl); } void CFX_ListCtrl::OnVK_DOWN(FX_BOOL bShift,FX_BOOL bCtrl) { OnVK(IsMultipleSel() ? GetCaret()+1 : GetSelect()+1, bShift, bCtrl); } void CFX_ListCtrl::OnVK_LEFT(FX_BOOL bShift,FX_BOOL bCtrl) { OnVK(0, bShift, bCtrl); } void CFX_ListCtrl::OnVK_RIGHT(FX_BOOL bShift,FX_BOOL bCtrl) { OnVK(GetCount()-1, bShift, bCtrl); } void CFX_ListCtrl::OnVK_HOME(FX_BOOL bShift,FX_BOOL bCtrl) { OnVK(0, bShift, bCtrl); } void CFX_ListCtrl::OnVK_END(FX_BOOL bShift,FX_BOOL bCtrl) { OnVK(GetCount()-1, bShift, bCtrl); } FX_BOOL CFX_ListCtrl::OnChar(FX_WORD nChar,FX_BOOL bShift,FX_BOOL bCtrl) { FX_INT32 nIndex = GetLastSelected(); FX_INT32 nFindIndex = FindNext(nIndex,nChar); if (nFindIndex != nIndex) { OnVK(nFindIndex, bShift, bCtrl); return TRUE; } return FALSE; } /* -------- inner methods ------- */ void CFX_ListCtrl::SetPlateRect(const CPDF_Rect & rect) { CFX_ListContainer::SetPlateRect(rect); m_ptScrollPos.x = rect.left; SetScrollPos(CPDF_Point(rect.left,rect.top)); ReArrange(0); InvalidateItem(-1); } CPDF_Rect CFX_ListCtrl::GetItemRect(FX_INT32 nIndex) const { return InToOut(CFX_List::GetItemRect(nIndex)); } void CFX_ListCtrl::AddString(FX_LPCWSTR string) { AddItem(string); ReArrange(GetCount() - 1); } void CFX_ListCtrl::SetMultipleSelect(FX_INT32 nItemIndex, FX_BOOL bSelected) { if (!IsValid(nItemIndex)) return; if (bSelected != this->IsItemSelected(nItemIndex)) { if (bSelected) { SetItemSelect(nItemIndex,TRUE); InvalidateItem(nItemIndex); } else { SetItemSelect(nItemIndex,FALSE); InvalidateItem(nItemIndex); } } } void CFX_ListCtrl::SetSingleSelect(FX_INT32 nItemIndex) { if (!IsValid(nItemIndex)) return; if (m_nSelItem != nItemIndex) { if (m_nSelItem >= 0) { SetItemSelect(m_nSelItem,FALSE); InvalidateItem(m_nSelItem); } SetItemSelect(nItemIndex,TRUE); InvalidateItem(nItemIndex); m_nSelItem = nItemIndex; } } void CFX_ListCtrl::SetCaret(FX_INT32 nItemIndex) { if (!IsValid(nItemIndex)) return; if (this->IsMultipleSel()) { FX_INT32 nOldIndex = m_nCaretIndex; if (nOldIndex != nItemIndex) { m_nCaretIndex = nItemIndex; SetItemCaret(nOldIndex, FALSE); SetItemCaret(nItemIndex,TRUE); InvalidateItem(nOldIndex); InvalidateItem(nItemIndex); } } } void CFX_ListCtrl::InvalidateItem(FX_INT32 nItemIndex) { if (m_pNotify) { if (nItemIndex == -1) { if (!m_bNotifyFlag) { m_bNotifyFlag = TRUE; CPDF_Rect rcRefresh = GetPlateRect(); m_pNotify->IOnInvalidateRect(&rcRefresh); m_bNotifyFlag = FALSE; } } else { if (!m_bNotifyFlag) { m_bNotifyFlag = TRUE; CPDF_Rect rcRefresh = GetItemRect(nItemIndex); rcRefresh.left -= 1.0f; rcRefresh.right += 1.0f; rcRefresh.bottom -= 1.0f; rcRefresh.top += 1.0f; m_pNotify->IOnInvalidateRect(&rcRefresh); m_bNotifyFlag = FALSE; } } } } void CFX_ListCtrl::SelectItems() { for (FX_INT32 i=0,sz=m_aSelItems.GetCount(); iIsMultipleSel()) { m_aSelItems.Add(nItemIndex); SelectItems(); } else SetSingleSelect(nItemIndex); } FX_BOOL CFX_ListCtrl::IsItemVisible(FX_INT32 nItemIndex) const { CPDF_Rect rcPlate = this->GetPlateRect(); CPDF_Rect rcItem = this->GetItemRect(nItemIndex); return rcItem.bottom >= rcPlate.bottom && rcItem.top <= rcPlate.top; } void CFX_ListCtrl::ScrollToListItem(FX_INT32 nItemIndex) { if (!IsValid(nItemIndex)) return; CPDF_Rect rcPlate = this->GetPlateRect(); CPDF_Rect rcItem = CFX_List::GetItemRect(nItemIndex); CPDF_Rect rcItemCtrl = GetItemRect(nItemIndex); if (FX_EDIT_IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) { if (FX_EDIT_IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) { SetScrollPosY(rcItem.bottom + rcPlate.Height()); } } else if (FX_EDIT_IsFloatBigger(rcItemCtrl.top, rcPlate.top)) { if (FX_EDIT_IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) { SetScrollPosY(rcItem.top); } } } void CFX_ListCtrl::SetScrollInfo() { if (m_pNotify) { CPDF_Rect rcPlate = GetPlateRect(); CPDF_Rect rcContent = CFX_List::GetContentRect(); if (!m_bNotifyFlag) { m_bNotifyFlag = TRUE; m_pNotify->IOnSetScrollInfoY(rcPlate.bottom, rcPlate.top, rcContent.bottom, rcContent.top, GetFirstHeight(), rcPlate.Height()); m_bNotifyFlag = FALSE; } } } void CFX_ListCtrl::SetScrollPos(const CPDF_Point & point) { SetScrollPosY(point.y); } void CFX_ListCtrl::SetScrollPosY(FX_FLOAT fy) { if (!FX_EDIT_IsFloatEqual(m_ptScrollPos.y,fy)) { CPDF_Rect rcPlate = this->GetPlateRect(); CPDF_Rect rcContent = CFX_List::GetContentRect(); if (rcPlate.Height() > rcContent.Height()) { fy = rcPlate.top; } else { if (FX_EDIT_IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) { fy = rcContent.bottom + rcPlate.Height(); } else if (FX_EDIT_IsFloatBigger(fy, rcContent.top)) { fy = rcContent.top; } } m_ptScrollPos.y = fy; InvalidateItem(-1); if (m_pNotify) { if (!m_bNotifyFlag) { m_bNotifyFlag = TRUE; m_pNotify->IOnSetScrollPosY(fy); m_bNotifyFlag = FALSE; } } } } CPDF_Rect CFX_ListCtrl::GetContentRect() const { return InToOut(CFX_List::GetContentRect()); } void CFX_ListCtrl::ReArrange(FX_INT32 nItemIndex) { CFX_List::ReArrange(nItemIndex); SetScrollInfo(); } void CFX_ListCtrl::SetTopItem(FX_INT32 nIndex) { if (IsValid(nIndex)) { this->GetPlateRect(); CPDF_Rect rcItem = CFX_List::GetItemRect(nIndex); SetScrollPosY(rcItem.top); } } FX_INT32 CFX_ListCtrl::GetTopItem() const { FX_INT32 nItemIndex = this->GetItemIndex(this->GetBTPoint()); if (!IsItemVisible(nItemIndex) && IsItemVisible(nItemIndex + 1)) nItemIndex += 1; return nItemIndex; } void CFX_ListCtrl::Empty() { CFX_List::Empty(); InvalidateItem(-1); } void CFX_ListCtrl::Cancel() { m_aSelItems.DeselectAll(); } FX_INT32 CFX_ListCtrl::GetItemIndex(const CPDF_Point & point) const { return CFX_List::GetItemIndex(OutToIn(point)); } CFX_WideString CFX_ListCtrl::GetText() const { if (this->IsMultipleSel()) return this->GetItemText(this->m_nCaretIndex); else return this->GetItemText(this->m_nSelItem); }