#include "SRNumberChooser.h" #include "SwordReaderResource.h" #include BOOL SRNumberChooser::s_fRegistered = false; SRNumberChooser::SRNumberChooser(ChooserType type, WCString wcsPrompt, WORD wNextMenuID) :m_nEndNumber(0) ,m_nSelectedNumber(0) ,m_nStartAt(1) ,m_wcsPrompt(wcsPrompt) ,m_wNextMenuID(wNextMenuID) ,m_type(type) { RECT rect = {0,0,0,0}; m_wcsClassName = "SRNumberChooser"; m_wcsWindowName = "Number Chooser"; // Calculate the button size. the 999 string should be a good representation of the largest string. // Future representations will reflect a preferred font. HDC hdc = ::GetDC(m_hWnd); INT h = ::DrawText(hdc, L"999", -1, &rect, DT_CALCRECT | DT_SINGLELINE | DT_CENTER | DT_VCENTER); m_nButtonHeight = 2*BUTTON_INTERNAL_PADDING + (rect.bottom - rect.top); m_nButtonWidth = 2*BUTTON_INTERNAL_PADDING + (rect.right - rect.left); ::ReleaseDC(m_hWnd, hdc); } BOOL SRNumberChooser::Create(SRWnd *pParentWnd, RECT bounds) { if(!Register()) return FALSE; if(!SRWnd::Create(m_wcsClassName,m_wcsWindowName,WS_CHILD | WS_VISIBLE, bounds, pParentWnd, NULL, m_hInstance)) return FALSE; return TRUE; } SRNumberChooser::~SRNumberChooser() { } BOOL SRNumberChooser::Register() { // Register window class... WNDCLASS wc; if(s_fRegistered) return TRUE; wc.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC; wc.lpfnWndProc = (WNDPROC) MessageRoute; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = m_hInstance; wc.hIcon = NULL; wc.hCursor = 0; wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = m_wcsClassName.w_str(); if(RegisterClass(&wc) == 0) return FALSE; s_fRegistered = TRUE; return TRUE; } INT SRNumberChooser::MaxCols() { RECT clientRect; ::GetClientRect(m_hWnd,&clientRect); return ( ((clientRect.right - BUTTON_PADDING_WIDTH) - clientRect.left)/(BUTTON_PADDING_WIDTH + m_nButtonWidth) ); } INT SRNumberChooser::MaxRows() { RECT clientRect; INT nMaxNumbers = 0; INT nMaxRows = 0; ::GetClientRect(m_hWnd,&clientRect); // 2 less rows due to the Prompt and the More Prev buttons. nMaxRows = ( ((clientRect.bottom - BUTTON_PADDING_HEIGHT) - clientRect.top)/(BUTTON_PADDING_HEIGHT + m_nButtonHeight) ) - 2; nMaxNumbers = MaxCols() * nMaxRows; if(m_nStartAt == 1 && m_nEndNumber <= nMaxNumbers + MaxCols()) nMaxRows++; // We can fit another row since there is no need for a More or Prev button. return nMaxRows; } INT SRNumberChooser::MaxNumbersPerScreen() { return MaxCols() * MaxRows(); } // Tries to center the buttons by calculating a left edge INT SRNumberChooser::LeftEdge() { RECT clientRect; GetClientRect(m_hWnd, &clientRect); return ((clientRect.right - clientRect.left)/2) - ((MaxCols()*m_nButtonWidth + (MaxCols() - 1)*BUTTON_PADDING_WIDTH)/2 ); } BOOL SRNumberChooser::OnPaint() { TCHAR buttonText[4]; RECT buttonRect; RECT clientRect; INT nCurrent = m_nStartAt; INT nColStart = 0; PAINTSTRUCT ps; INT nRow; INT nCol; INT nMaxCols = MaxCols(); INT nMaxRows = MaxRows(); if (m_nSelectedNumber < 1 || m_nSelectedNumber > m_nEndNumber) m_nSelectedNumber = 1; HDC hdc = BeginPaint(m_hWnd, &ps); HBRUSH brushBG = CreateSolidBrush((COLORREF)BUTTON_BACKGROUND); GetClientRect(m_hWnd, &clientRect); FillRect(hdc, &clientRect, (HBRUSH)GetStockObject(WHITE_BRUSH)); // Draw the prompt. buttonRect.top = BUTTON_PADDING_HEIGHT; buttonRect.left = LeftEdge(); buttonRect.right = clientRect.right - LeftEdge(); buttonRect.bottom = buttonRect.top + m_nButtonHeight; DrawText(hdc, m_wcsPrompt.w_str(), -1, &buttonRect, DT_CENTER | DT_VCENTER); // Init the first button's bounds buttonRect.top = 2*BUTTON_PADDING_HEIGHT + m_nButtonHeight; buttonRect.bottom = buttonRect.top + m_nButtonHeight; buttonRect.left = LeftEdge(); buttonRect.right = buttonRect.left + m_nButtonWidth; SetBkColor(hdc, BUTTON_BACKGROUND); for(nRow = 0; nRow < nMaxRows; nRow++){ for(nCol = 0; nCol < nMaxCols; nCol++){ nCurrent = m_nStartAt + (nRow * nMaxCols) + nCol; _itow(nCurrent, buttonText, 10); if(nCurrent == m_nSelectedNumber) { SetBkColor(hdc, BUTTON_SEL_BACKGROUND); FillRect(hdc, &buttonRect, brushBG); Rectangle(hdc, buttonRect.left, buttonRect.top, buttonRect.right, buttonRect.bottom); } else { SetBkColor(hdc, BUTTON_BACKGROUND); FillRect(hdc, &buttonRect, brushBG); } DrawText(hdc, buttonText, -1, &buttonRect, DT_CENTER | DT_VCENTER); if(nCurrent == m_nEndNumber) break; // Move the bounds right. buttonRect.left += m_nButtonWidth + BUTTON_PADDING_WIDTH; buttonRect.right = buttonRect.left + m_nButtonWidth; } // Move the bounds down and all the way back to the left. buttonRect.left = LeftEdge(); buttonRect.right = buttonRect.left + m_nButtonWidth; buttonRect.top += m_nButtonHeight + BUTTON_PADDING_HEIGHT; buttonRect.bottom = buttonRect.top + m_nButtonHeight; if(nCurrent == m_nEndNumber) break; } // If we didn't reach the end we need to draw the More button... if(nCurrent < m_nEndNumber){ buttonRect.left = clientRect.right - (BUTTON_WIDTH_MORE + LeftEdge()); buttonRect.right = buttonRect.left + BUTTON_WIDTH_MORE; buttonRect.top = clientRect.bottom - (m_nButtonHeight + BUTTON_PADDING_HEIGHT); buttonRect.bottom = clientRect.bottom - BUTTON_PADDING_HEIGHT; FillRect(hdc, &buttonRect, brushBG); DrawText(hdc, L"More >>", -1, &buttonRect, DT_CENTER | DT_VCENTER); } // We are not on the first page of numbers we need to draw a Prev button... if(m_nStartAt != 1){ buttonRect.left = LeftEdge(); buttonRect.right = buttonRect.left + BUTTON_WIDTH_MORE; buttonRect.top = clientRect.bottom - (m_nButtonHeight + BUTTON_PADDING_HEIGHT); buttonRect.bottom = clientRect.bottom - BUTTON_PADDING_HEIGHT; FillRect(hdc, &buttonRect, brushBG); DrawText(hdc, L"<< Prev", -1, &buttonRect, DT_CENTER | DT_VCENTER); } //Clean up. DeleteObject(brushBG); EndPaint(m_hWnd,&ps); return TRUE; } /* Return: * > 0 - the number found. * 0 - More button pressed. * -1 - Prev button pressed. * -2 - White space tapped. Ignore */ INT SRNumberChooser::NumberAt(int x, int y) { RECT clientRect; INT nCols = (x - BUTTON_PADDING_WIDTH) / (m_nButtonWidth + BUTTON_PADDING_WIDTH); INT nRows = (y - BUTTON_PADDING_HEIGHT)/ (m_nButtonHeight + BUTTON_PADDING_HEIGHT) - 1; GetClientRect(m_hWnd, &clientRect); // I ignore the minimal amount of white space between the buttons. INT nNumber = m_nStartAt + (nRows * MaxCols()) + nCols; if(y < m_nButtonHeight + 2*BUTTON_PADDING_HEIGHT) return -2; // Tapped the title area. // Check if the tap was on a button... if(y > clientRect.bottom - (m_nButtonHeight + BUTTON_PADDING_HEIGHT) ){ if(x > BUTTON_PADDING_WIDTH && x < BUTTON_PADDING_WIDTH + BUTTON_WIDTH_MORE){ return -1; }else if( (x > (MaxCols()*(m_nButtonWidth+BUTTON_PADDING_WIDTH) - BUTTON_WIDTH_MORE)) && (x < (MaxCols()*(m_nButtonWidth+BUTTON_PADDING_WIDTH))) ){ return 0; }else return -2; } if(nNumber > m_nEndNumber || nNumber >= m_nStartAt + MaxNumbersPerScreen()) return -2; return nNumber; } BOOL SRNumberChooser::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags){ if(nChar == VK_RIGHT){ m_nLastChar = VK_RIGHT; MoveRight(); RefreshWindow(); } else if(nChar == VK_DOWN){ m_nLastChar = VK_DOWN; MoveDown(); RefreshWindow(); } else if(nChar == VK_LEFT){ m_nLastChar = VK_LEFT; MoveLeft(); RefreshWindow(); } else if(nChar == VK_UP){ m_nLastChar = VK_UP; MoveUp(); RefreshWindow(); } else if (nChar == VK_ROCKER) { if (m_nLastChar == VK_UP) { MoveDown(); MoveLeft(); RefreshWindow(); } else if (m_nLastChar == VK_DOWN) { MoveUp(); MoveRight(); RefreshWindow(); } } else if(nChar == VK_RETURN){ ::SendMessage(m_pParentWnd->GetWindowHandle(),WM_COMMAND,(SR_MSRC_CHOOSER << 16) | (m_type == CHAPTER ? SR_SETCHAPTER : SR_SETVERSE), m_nSelectedNumber); ::SendMessage(m_pParentWnd->GetWindowHandle(),WM_COMMAND,m_wNextMenuID, m_nSelectedNumber); } return true; } void SRNumberChooser::MoveDown() { INT nMaxCols = MaxCols(); INT nMaxRows = MaxRows(); INT curRow = (m_nSelectedNumber - m_nStartAt + 1) / nMaxCols; INT curTrueRow = m_nSelectedNumber / nMaxCols; INT curCol = m_nSelectedNumber - (nMaxCols * curTrueRow); if (curCol != nMaxCols) curRow++; if ((m_nSelectedNumber + nMaxCols) > m_nEndNumber) { if ((curRow == nMaxRows) && (((nMaxCols - curCol) + m_nSelectedNumber) < m_nEndNumber)) m_nStartAt += nMaxCols; m_nSelectedNumber = m_nEndNumber; } else { if (curRow == nMaxRows) m_nStartAt += nMaxCols; m_nSelectedNumber += nMaxCols; } } void SRNumberChooser::MoveLeft() { if (m_nSelectedNumber == m_nStartAt) { if (m_nStartAt != 1) { m_nSelectedNumber--; m_nStartAt -= MaxCols(); } } else { m_nSelectedNumber--; } } void SRNumberChooser::MoveRight() { INT nMaxCols = MaxCols(); INT nMaxRows = MaxRows(); INT curRow = (m_nSelectedNumber - m_nStartAt + 1) / nMaxCols; INT curTrueRow = m_nSelectedNumber / nMaxCols; INT curCol = m_nSelectedNumber - (nMaxCols * (curTrueRow - 1)); if (m_nSelectedNumber != m_nEndNumber) { if ((curRow == nMaxRows) && (curCol == nMaxCols)) { m_nStartAt += nMaxCols; } m_nSelectedNumber++; } } void SRNumberChooser::MoveUp() { INT nMaxCols = MaxCols(); if ((m_nSelectedNumber - nMaxCols) < m_nStartAt) { if (!((m_nSelectedNumber - nMaxCols) < 1)) { m_nSelectedNumber -= nMaxCols; m_nStartAt -= nMaxCols; } } else { m_nSelectedNumber -= nMaxCols; } } BOOL SRNumberChooser::OnLButtonUp(WORD fwKeys, INT xPos, INT yPos) { TCHAR buf[16] = {0}; INT found = NumberAt(xPos, yPos); m_fLButtonDown = FALSE; if(found == 0 && (m_nStartAt + MaxNumbersPerScreen() <= m_nEndNumber) ){ m_nStartAt += MaxNumbersPerScreen(); RefreshWindow(); }else if(found == -1 && m_nStartAt != 1){ m_nStartAt -= MaxNumbersPerScreen(); RefreshWindow(); }else if(found == -2){ return TRUE; }else if(found != 0 && found != -1){ m_nSelectedNumber = found; // We also send the value found, thus when this messge is recv'd the new value can be processed. ::SendMessage(m_pParentWnd->GetWindowHandle(),WM_COMMAND,(SR_MSRC_CHOOSER << 16) | (m_type == CHAPTER ? SR_SETCHAPTER : SR_SETVERSE), found); ::SendMessage(m_pParentWnd->GetWindowHandle(),WM_COMMAND,m_wNextMenuID, found); } return TRUE; } BOOL SRNumberChooser::OnLButtonDown(WORD fwKeys, INT xPos, INT yPos) { INT found = NumberAt(xPos, yPos); m_fLButtonDown = TRUE; if(found > 0){ m_nSelectedNumber = found; RefreshWindow(); } return TRUE; } BOOL SRNumberChooser::OnMouseMove(WORD fwKeys, INT xPos, INT yPos) { INT found = NumberAt(xPos, yPos); if(found > 0 && m_nSelectedNumber != found && m_fLButtonDown){ m_nSelectedNumber = found; RefreshWindow(); } return TRUE; } void SRNumberChooser::SetSelectedNumber(INT nSelectedNumber) { // Adjust the starting page. INT nMaxCols = MaxCols(); INT nMaxRows = MaxRows(); INT nIndex = nSelectedNumber / (nMaxCols * nMaxRows); // Integer math makes this work, otherwise the (nMaxCols * nMaxRows) term would cancel. m_nStartAt = nIndex * (nMaxCols * nMaxRows) + 1; if(nSelectedNumber < 0 || nSelectedNumber > m_nEndNumber) m_nSelectedNumber = 0; else m_nSelectedNumber = nSelectedNumber; } void SRNumberChooser::SetEndNumber(INT nEndNumber) { m_nEndNumber = nEndNumber; }