Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

#include "EditWidget.h"
#include "SysFont.h"
#include "WidgetManager.h"
#include "SexyAppBase.h"
#include "EditListener.h"

using namespace Sexy;

static int gEditWidgetColors[][3] =
{{255, 255, 255},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{255, 255, 255}};

EditWidget::EditWidget(int theId, EditListener* theEditListener)
{              
        mId = theId;
        mEditListener = theEditListener;       
        mFont = NULL;

        mHadDoubleClick = false;
        mHilitePos = -1;
        mLastModifyIdx = -1;
        mLeftPos = 0;
        mUndoCursor = 0;
        mUndoHilitePos = 0;
        mLastModifyIdx = 0;
        mBlinkAcc = 0;
        mCursorPos = 0;
        mShowingCursor = false;
        mDrawSelOverride = false;
        mMaxChars = -1;
        mMaxPixels = -1;
        mPasswordChar = 0;
        mBlinkDelay = 40;

        SetColors(gEditWidgetColors, NUM_COLORS);
}

EditWidget::~EditWidget()
{
        delete mFont;
        ClearWidthCheckFonts();

}

void EditWidget::ClearWidthCheckFonts()
{
        for (WidthCheckList::iterator anItr = mWidthCheckList.begin(); anItr != mWidthCheckList.end(); ++anItr)
                delete anItr->mFont;

        mWidthCheckList.clear();
}

void EditWidget::AddWidthCheckFont(Font *theFont, int theMaxPixels)
{
        mWidthCheckList.push_back(WidthCheck());
        WidthCheck &aCheck = mWidthCheckList.back();
        aCheck.mWidth = theMaxPixels;
        aCheck.mFont = theFont->Duplicate();
}

void EditWidget::SetText(const SexyString& theText, bool leftPosToZero)
{
        mString = theText;
        mCursorPos = mString.length();
        mHilitePos = 0;
        if (leftPosToZero)
                mLeftPos = 0;
        else
                FocusCursor(true);
       
        MarkDirty();
}

SexyString& EditWidget::GetDisplayString()
{
        if (mPasswordChar==0)
                return mString;

        if (mPasswordDisplayString.size()!=mString.size())
        {
                mPasswordDisplayString = SexyString(mString.size(), mPasswordChar);
                //mPasswordDisplayString.resize(mString.size());
                //for (int i=0; i<(int)mPasswordDisplayString.length(); i++)
                //      mPasswordDisplayString[i] = mPasswordChar;
        }

        return mPasswordDisplayString;
}

bool EditWidget::WantsFocus()
{
        return true;
}

void EditWidget::Resize(int theX, int theY, int theWidth, int theHeight)
{
        Widget::Resize(theX, theY, theWidth, theHeight);

        FocusCursor(false);            
}

void EditWidget::SetFont(Font* theFont, Font* theWidthCheckFont)
{
        delete mFont;
        mFont = theFont->Duplicate();

        ClearWidthCheckFonts();
        if (theWidthCheckFont != NULL)
                AddWidthCheckFont(theWidthCheckFont);
}

void EditWidget::Draw(Graphics* g) // Already translated
{      
        if (mFont == NULL)
                mFont = new SysFont(mWidgetManager->mApp, "Arial Unicode MS", 10, false);

        SexyString &aString = GetDisplayString();

        g->SetColor(mColors[COLOR_BKG]);                       
        g->FillRect(0, 0, mWidth, mHeight);
       
        for (int i = 0; i < 2; i++)
        {
                Graphics* aClipG = g->Create();        
                aClipG->SetFont(mFont);
                               
                if (i == 1)
                {
                        int aCursorX = mFont->StringWidth(aString.substr(0, mCursorPos)) - mFont->StringWidth(aString.substr(0, mLeftPos));
                        int aHiliteX = aCursorX+2;
                        if ((mHilitePos != -1) && (mCursorPos != mHilitePos))
                                aHiliteX = mFont->StringWidth(aString.substr(0, mHilitePos)) - mFont->StringWidth(aString.substr(0, mLeftPos));
                       
                        if (!mShowingCursor)
                                aCursorX += 2;                                                         
                       
                        aCursorX = min(max(0, aCursorX), mWidth-8);
                        aHiliteX = min(max(0, aHiliteX), mWidth-8);
                       
                        aClipG->ClipRect(4 + min(aCursorX, aHiliteX), (mHeight - mFont->GetHeight())/2, abs(aHiliteX - aCursorX), mFont->GetHeight());
                }
                else
                        aClipG->ClipRect(4, 0, mWidth-8, mHeight);                     
               
                bool hasfocus = mHasFocus || mDrawSelOverride;
                if (i == 1 && hasfocus)
                {
                        aClipG->SetColor(mColors[COLOR_HILITE]);
                        aClipG->FillRect(0, 0, mWidth, mHeight);
                }
       
                if (i == 0 || !hasfocus)
                        aClipG->SetColor(mColors[COLOR_TEXT]);
                else
                        aClipG->SetColor(mColors[COLOR_HILITE_TEXT]);                  
                aClipG->DrawString(aString.substr(mLeftPos), 4, (mHeight - mFont->GetHeight())/2 + mFont->GetAscent());
               
                delete aClipG;
        }              
                       
        g->SetColor(mColors[COLOR_OUTLINE]);
        g->DrawRect(0, 0, mWidth-1, mHeight-1);                        
}

void EditWidget::UpdateCaretPos()
{
        SexyAppBase *anApp = mWidgetManager->mApp;

        Point aPoint = GetAbsPos();

        if (aPoint.mX<10) aPoint.mX = 10;
        else if (aPoint.mX>anApp->mWidth-10) aPoint.mX = anApp->mWidth-10;
        if (aPoint.mY<10) aPoint.mY = 10;
        else if (aPoint.mY>anApp->mHeight-10) aPoint.mY = anApp->mHeight-10;

        SetCaretPos(aPoint.mX,aPoint.mY);
}

void EditWidget::GotFocus()
{
        Widget::GotFocus();
        if (mWidgetManager && mWidgetManager->mApp->mTabletPC)
        {
                SexyAppBase *anApp = mWidgetManager->mApp;

                CreateCaret(anApp->mHWnd,NULL,0,0);
                UpdateCaretPos();
                ShowCaret(anApp->mHWnd);
        }
       
        mShowingCursor = true;
        mBlinkAcc = 0;
        MarkDirty();   
}

void EditWidget::LostFocus()
{
        Widget::LostFocus();

        if (mWidgetManager && mWidgetManager->mApp->mTabletPC)
        {
                HideCaret(mWidgetManager->mApp->mHWnd);
                DestroyCaret();
        }

        mShowingCursor = false;
        MarkDirty();
}

void EditWidget::Update()
{
        Widget::Update();

        if (mHasFocus)
        {
                if (mWidgetManager->mApp->mTabletPC)
                {
                        UpdateCaretPos();
                }

                if (++mBlinkAcc > mBlinkDelay)
                {
                        MarkDirty();
                        mBlinkAcc = 0;
                        mShowingCursor = !mShowingCursor;                      
                }              
        }      
}

void EditWidget::EnforceMaxPixels()
{
        if (mMaxPixels<=0 && mWidthCheckList.empty()) // no width checking in effect
                return;

        if (mWidthCheckList.empty())
        {
                while (mFont->StringWidth(mString) > mMaxPixels)
                        mString = mString.substr(0, mString.length()-1);

                return;
        }
               
        for (WidthCheckList::iterator anItr = mWidthCheckList.begin(); anItr != mWidthCheckList.end(); ++anItr)
        {
                int aWidth = anItr->mWidth;
                if (aWidth<=0)
                {
                        aWidth = mMaxPixels;
                        if (aWidth<=0)
                                continue;
                }

                while (anItr->mFont->StringWidth(mString) > aWidth)
                        mString = mString.substr(0,mString.length()-1);
        }
}

bool EditWidget::IsPartOfWord(SexyChar theChar)
{
        return (((theChar >= _S('A')) && (theChar <= _S('Z'))) ||
                        ((theChar >= _S('a')) && (theChar <= _S('z'))) ||
                        ((theChar >= _S('0')) && (theChar <= _S('9'))) ||
                        (((unsigned int)theChar >= (unsigned int)(L'À')) && ((unsigned int)theChar <= (unsigned int)(L'ÿ'))) ||
                        (theChar == _S('_')));
}

void EditWidget::ProcessKey(KeyCode theKey, SexyChar theChar)
{
        bool shiftDown = mWidgetManager->mKeyDown[KEYCODE_SHIFT];
        bool controlDown = mWidgetManager->mKeyDown[KEYCODE_CONTROL];

        if ((theKey == KEYCODE_SHIFT) || (theKey == KEYCODE_CONTROL))
                return;

        bool bigChange = false;
        bool removeHilite = !shiftDown;
       
        if (shiftDown && (mHilitePos == -1))
                mHilitePos = mCursorPos;
       
        SexyString anOldString = mString;
        int anOldCursorPos = mCursorPos;
        int anOldHilitePos = mHilitePos;
        if ((theChar == 3) || (theChar == 24))
        {
                // Copy selection
               
                if ((mHilitePos != -1) && (mHilitePos != mCursorPos))
                {
                        if (mCursorPos < mHilitePos)
                                mWidgetManager->mApp->CopyToClipboard(SexyStringToString(GetDisplayString().substr(mCursorPos, mHilitePos)));
                        else
                                mWidgetManager->mApp->CopyToClipboard(SexyStringToString(GetDisplayString().substr(mHilitePos, mCursorPos)));
               
                        if (theChar == 3)
                        {                              
                                removeHilite = false;
                        }
                        else
                        {
                                mString = mString.substr(0, min(mCursorPos, mHilitePos)) + mString.substr(max(mCursorPos, mHilitePos));
                                mCursorPos = min(mCursorPos, mHilitePos);
                                mHilitePos = -1;
                                bigChange = true;
                        }
                }                              
        }
        else if (theChar == 22)
        {
                // Paste selection
               
                SexyString aBaseString = StringToSexyString(mWidgetManager->mApp->GetClipboard());
               
                if (aBaseString.length() > 0)
                {      
                        SexyString aString;

                        for (ulong i = 0; i < aBaseString.length(); i++)
                        {
                                if ((aBaseString[i] == '\r') || (aBaseString[i] == '\n'))
                                        break;

                                if (mFont->CharWidth(aBaseString[i]) != 0 && mEditListener->AllowChar(mId, aBaseString[i]))
                                        aString += aBaseString[i];                                     
                        }                      

                        if (mHilitePos == -1)
                        {
                                // Insert string where cursor is
                                mString = mString.substr(0, mCursorPos) + aString + mString.substr(mCursorPos);
                        }
                        else
                        {
                                // Replace selection with new string
                                mString = mString.substr(0, min(mCursorPos, mHilitePos)) + aString + mString.substr(max(mCursorPos, mHilitePos));
                                mCursorPos = min(mCursorPos, mHilitePos);
                                mHilitePos = -1;
                        }
               
                        mCursorPos += aString.length();
               
                        bigChange = true;
                }
        }
        else if (theChar == 26)
        {
                // Undo
               
                mLastModifyIdx = -1;
               
                SexyString aSwapString = mString;
                int aSwapCursorPos = mCursorPos;
                int aSwapHilitePos = mHilitePos;                       
               
                mString = mUndoString;
                mCursorPos = mUndoCursor;
                mHilitePos = mUndoHilitePos;
                                       
                mUndoString = aSwapString;
                mUndoCursor = aSwapCursorPos;
                mUndoHilitePos = aSwapHilitePos;                       
               
                removeHilite = false;                                          
        }
        else if (theKey == KEYCODE_LEFT)
        {
                if (controlDown)
                {
                        // Get to a word
                        while ((mCursorPos > 0) && (!IsPartOfWord(mString[mCursorPos-1])))
                                   mCursorPos--;
                       
                        // Go beyond the word
                        while ((mCursorPos > 0) && (IsPartOfWord(mString[mCursorPos-1])))
                                   mCursorPos--;
                }
                else if (shiftDown || (mHilitePos == -1))
                        mCursorPos--;
                else
                        mCursorPos = min(mCursorPos, mHilitePos);
        }
        else if (theKey == KEYCODE_RIGHT)
        {
                if (controlDown)
                {
                        // Get to whitespace
                        while ((mCursorPos < (int) mString.length()-1) && (IsPartOfWord(mString[mCursorPos+1])))
                                   mCursorPos++;
                       
                        // Go beyond the whitespace
                        while ((mCursorPos < (int) mString.length()-1) && (!IsPartOfWord(mString[mCursorPos+1])))
                                   mCursorPos++;
                }
                if (shiftDown || (mHilitePos == -1))
                        mCursorPos++;
                else
                        mCursorPos = max(mCursorPos, mHilitePos);
        }
        else if (theKey == KEYCODE_BACK)
        {
                if (mString.length() > 0)
                {
                        if ((mHilitePos != -1) && (mHilitePos != mCursorPos))
                        {
                                // Delete selection
                                mString = mString.substr(0, min(mCursorPos, mHilitePos)) + mString.substr(max(mCursorPos, mHilitePos));
                                mCursorPos = min(mCursorPos, mHilitePos);
                                mHilitePos = -1;
                               
                                bigChange = true;
                        }
                        else
                        {
                                // Delete char behind cursor
                                if (mCursorPos > 0)
                                        mString = mString.substr(0, mCursorPos-1) + mString.substr(mCursorPos);
                                else
                                        mString = mString.substr(mCursorPos);
                                mCursorPos--;
                                mHilitePos = -1;
                               
                                if (mCursorPos != mLastModifyIdx)
                                        bigChange = true;
                                mLastModifyIdx = mCursorPos-1;
                        }
                }
        }
        else if (theKey == KEYCODE_DELETE)
        {
                if (mString.length() > 0)
                {
                        if ((mHilitePos != -1) && (mHilitePos != mCursorPos))
                        {
                                // Delete selection
                                mString = mString.substr(0, min(mCursorPos, mHilitePos)) + mString.substr(max(mCursorPos, mHilitePos));
                                mCursorPos = min(mCursorPos, mHilitePos);
                                mHilitePos = -1;
                               
                                bigChange = true;
                        }
                        else
                        {
                                // Delete char in front of cursor
                                if (mCursorPos < (int) mString.length())
                                        mString = mString.substr(0, mCursorPos) + mString.substr(mCursorPos+1);
                               
                                if (mCursorPos != mLastModifyIdx)
                                        bigChange = true;
                                mLastModifyIdx = mCursorPos;
                        }
                }      
        }
        else if (theKey == KEYCODE_HOME)
        {
                mCursorPos = 0;
        }
        else if (theKey == KEYCODE_END)
        {
                mCursorPos = mString.length(); 
        }
        else if (theKey == KEYCODE_RETURN)
        {
                mEditListener->EditWidgetText(mId, mString);           
        }
        else
        {
                SexyString aString = SexyString(1, theChar);
                unsigned int uTheChar = (unsigned int)theChar;
                unsigned int range = 127;
                if (gSexyAppBase->mbAllowExtendedChars)
                {
                        range = 255;
                }

                if ((uTheChar >= 32) && (uTheChar <= range) && (mFont->StringWidth(aString) > 0) && mEditListener->AllowChar(mId, theChar))
                {                              
                        if ((mHilitePos != -1) && (mHilitePos != mCursorPos))
                        {
                                // Replace selection with new character
                                mString = mString.substr(0, min(mCursorPos, mHilitePos)) + SexyString(1, theChar) + mString.substr(max(mCursorPos, mHilitePos));
                                mCursorPos = min(mCursorPos, mHilitePos);
                                mHilitePos = -1;
                               
                                bigChange = true;
                        }
                        else
                        {
                                // Insert character where cursor is
                                mString = mString.substr(0, mCursorPos) + SexyString(1, theChar) + mString.substr(mCursorPos);
                               
                                if (mCursorPos != mLastModifyIdx+1)
                                        bigChange = true;                                              
                                mLastModifyIdx = mCursorPos;
                                mHilitePos = -1;
                        }
                                                                                       
                        mCursorPos++;                          
                        FocusCursor(false);
                }
                else
                        removeHilite = false;
        }
       
        if ((mMaxChars != -1) && ((int) mString.length() > mMaxChars))
                mString = mString.substr(0, mMaxChars);

        EnforceMaxPixels();

        if (mCursorPos < 0)
                mCursorPos = 0;
        else if (mCursorPos > (int) mString.length())
                mCursorPos = mString.length();
       
        if (anOldCursorPos != mCursorPos)
        {
                mBlinkAcc = 0;
                mShowingCursor = true;
        }
       
        FocusCursor(true);
       
        if (removeHilite || mHilitePos==mCursorPos)
                mHilitePos = -1;
       
        if (!mEditListener->AllowText(mId, mString))
        {
                mString = anOldString;
                mCursorPos = anOldCursorPos;
                mHilitePos = anOldHilitePos;
        }
        else if (bigChange)
        {
                mUndoString = anOldString;
                mUndoCursor = anOldCursorPos;
                mUndoHilitePos = anOldHilitePos;
        }
       
        MarkDirty();
}

void EditWidget::KeyDown(KeyCode theKey)
{
        if (((theKey < 'A') || (theKey >= 'Z')) && mEditListener->AllowKey(mId, theKey))
                ProcessKey(theKey, 0);

        Widget::KeyDown(theKey);
}

void EditWidget::KeyChar(SexyChar theChar)
{
//      if (mEditListener->AllowChar(mId, theChar))
                ProcessKey(KEYCODE_UNKNOWN, theChar);

        Widget::KeyChar(theChar);
}

int EditWidget::GetCharAt(int x, int y)
{
        int aPos = 0;

        SexyString &aString = GetDisplayString();
                                       
        for (int i = mLeftPos; i < (int) aString.length(); i++)
        {
                SexyString aLoSubStr = aString.substr(mLeftPos, i-mLeftPos);
                SexyString aHiSubStr = aString.substr(mLeftPos, i-mLeftPos+1);
                       
                int aLoLen = mFont->StringWidth(aLoSubStr);
                int aHiLen = mFont->StringWidth(aHiSubStr);
                if (x >= (aLoLen+aHiLen)/2 + 5)                        
                        aPos = i+1;    
        }                                      
       
        return aPos;
}

void EditWidget::FocusCursor(bool bigJump)
{
        while (mCursorPos < mLeftPos)
        {
                if (bigJump)
                        mLeftPos = max(0, mLeftPos-10);
                else
                        mLeftPos = max(0, mLeftPos-1);
                MarkDirty();
        }                              
                                       
        if (mFont != NULL)
        {
                SexyString &aString = GetDisplayString();
                while ((mWidth-8 > 0) && (mFont->StringWidth(aString.substr(0, mCursorPos)) - mFont->StringWidth(aString.substr(0, mLeftPos)) >= mWidth-8))
                {
                        if (bigJump)
                                mLeftPos = min(mLeftPos + 10, (int) mString.length()-1);
                        else
                                mLeftPos = min(mLeftPos + 1, (int) mString.length()-1);

                        MarkDirty();
                }
        }
}

void EditWidget::MouseDown(int x, int y, int theBtnNum, int theClickCount)
{
        Widget::MouseDown(x, y, theBtnNum, theClickCount);

        mHilitePos = -1;
        mCursorPos = GetCharAt(x, y);
       
        if (theClickCount > 1)
        {
                mHadDoubleClick = true;
                HiliteWord();
        }
       
        MarkDirty();
       
        FocusCursor(false);
}

void EditWidget::MouseUp(int x, int y, int theBtnNum, int theClickCount)
{
        Widget::MouseUp(x,y,theBtnNum,theClickCount);
        if (mHilitePos==mCursorPos)
                mHilitePos = -1;
       
        if (mHadDoubleClick)
        {              
                mHilitePos = -1;
                mCursorPos = GetCharAt(x, y);          

                mHadDoubleClick = false;
                HiliteWord();
        }

        MarkDirty();
}

void EditWidget::HiliteWord()
{
        SexyString &aString = GetDisplayString();

        if (mCursorPos < (int) aString.length())
        {
                // Find first space before word
                mHilitePos = mCursorPos;
                while ((mHilitePos > 0) && (IsPartOfWord(aString[mHilitePos-1])))
                        mHilitePos--;

                // Find first space after word
                while ((mCursorPos < (int) aString.length()-1) && (IsPartOfWord(aString[mCursorPos+1])))
                        mCursorPos++;
                if (mCursorPos < (int) aString.length())
                        mCursorPos++;
        }
}

void EditWidget::MouseDrag(int x, int y)
{
        Widget::MouseDrag(x, y);

        if (mHilitePos == -1)
                mHilitePos = mCursorPos;
       
        mCursorPos = GetCharAt(x, y);
        MarkDirty();
       
        FocusCursor(false);
}

void EditWidget::MouseEnter()
{
        Widget::MouseEnter();

        mWidgetManager->mApp->SetCursor(CURSOR_TEXT);
}

void EditWidget::MouseLeave()
{      
        Widget::MouseLeave();

        mWidgetManager->mApp->SetCursor(CURSOR_POINTER);
}

void EditWidget::MarkDirty()
{
        if (mColors[COLOR_BKG].mAlpha != 255)
                Widget::MarkDirtyFull();
        else
                Widget::MarkDirty();
}