/*---------------------------------------------------------------------------------------------- The Ok button in the list chooser has been hit. Process the results from the list chooser. @param pplc Pointer to the dialog box being closed. ----------------------------------------------------------------------------------------------*/ void AfDeFeCliRef::ChooserApplied(PossChsrDlg * pplc) { // Get the output values. StrUni stu; ITsStringPtr qtss; ITsStrFactoryPtr qtsf; HVO pssId; qtsf.CreateInstance(CLSID_TsStrFactory); pplc->GetDialogValues(pssId); m_pss = pssId; m_fRecurse = true; if (pssId) { PossListInfoPtr qpli; AfLpInfo * plpi = GetLpInfo(); AssertPtr(plpi); plpi->LoadPossList(m_hvoPssl, m_wsMagic, &qpli); AssertPtr(qpli); int ipss = qpli->GetIndexFromId(pssId); PossItemInfo * ppii = qpli->GetPssFromIndex(ipss); if (m_fHier) ppii->GetHierName(qpli, stu, m_pnt); else ppii->GetName(stu, m_pnt); int ws = ppii->GetWs(); qtsf->MakeStringRgch(stu.Chars(), stu.Length(), ws, &qtss); m_qtss = qtss; ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)qtss.Ptr()); } else { m_qtss = NULL; ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, NULL); } // I (KenZ) don't fully understand this. But if a user enters the chooser, and opens a // list editor from there and adds a new item, closes the list editor, checks the new // item in the chooser, selects OK, then moves to the next record without moving from // the field, the added item is lost. We get ksyncPossList and ksyncAddPss sync messages // from the list editor, but for some reason we are getting an extra ksyncPossList // message after this method completes. That extra message is calling ListChanged which // calls UpdateField, which reloads our temporary cache from the main cache and wipes out // the change we just made. So until we can do something better, we'll save the changes // here to make sure the UpdateField doesn't wipe out our change. SaveEdit(); ::InvalidateRect(m_hwnd, NULL, true); }
/*---------------------------------------------------------------------------------------------- The edit box changed. We need to validate what was done. @param pedit @return ----------------------------------------------------------------------------------------------*/ bool AfDeFeCliRef::OnChange(AfDeFeEdBoxBut::DeEdit * pedit) { if (m_fRecurse) { m_fRecurse = false; return true; } if (!m_hwnd) return true; // We aren't completely set up yet, so ignore this. // Get the characters from the edit box. ITsStringPtr qtss; ::SendMessage(m_hwnd, FW_EM_GETTEXT, 0, (LPARAM)&qtss); int ichMin; int ichLim; ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&ichMin, (LPARAM)&ichLim); int cchTyped; // number of characters in the typed string // JohnT: use a StrUni rather than an StrUniBuf, because some user sometime will accidentally // paste something long here, and performance here is not critical. StrUni stuTyped; OLECHAR * pchBuf; qtss->get_Length(&cchTyped); if (cchTyped > kcchPossNameAbbrMax) { if (ichMin == cchTyped) ichMin = kcchPossNameAbbrMax; if (ichLim == cchTyped) ichLim = kcchPossNameAbbrMax; cchTyped = kcchPossNameAbbrMax; m_fRecurse = true; // Stop the recursion caused by the next instruction. // Note: This recursively calls this procedure. ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)qtss.Ptr()); ::SendMessage(m_hwnd, EM_SETSEL, ichMin, ichMin); } stuTyped.SetSize(cchTyped, &pchBuf); qtss->FetchChars(0, cchTyped, pchBuf); #ifdef DEBUG_THIS_FILE StrAnsi sta; sta.Format("AfDeFeCliRef::OnChange: pedit->m_ch=%d; ichMin=%d; ichLim=%d; pedit->m_cchMatched=%d; cchTyped=%d.\n", pedit->m_ch, ichMin, ichLim, pedit->m_cchMatched, cchTyped); OutputDebugString(sta.Chars()); #endif bool fTypeAhead = false; // allow type ahead only when adding characters at end of current item // or backspacing at end of current item. bool fNeedCompare; if (pedit->m_ch == 0) // (see kcidEditPaste special code) { // If we pasted something, force a compare. fNeedCompare = true; } else if (ichMin == pedit->m_cchMatched + 1 && pedit->m_ch != VK_BACK && pedit->m_ch != VK_DELETE) { // Need to compare if we typed a character and we are one greater than last match. fNeedCompare = true; if (cchTyped == ichMin) { fTypeAhead = true; #ifdef DEBUG_THIS_FILE sta.Format("OnChange: 1 - setting fTypeAhead to true.\n"); OutputDebugString(sta.Chars()); #endif } } else if (ichMin > pedit->m_cchMatched) { // Don't compare any other time we are past the last match. fNeedCompare = false; } // else if ((cchTyped == ichMin) && (ichMin == ichLim) && (pedit->m_ch != kscDelForward)) else if ((cchTyped == ichMin) && (ichMin == ichLim) && (pedit->m_ch != 46)) { // Need to compare if we typed a character and we are at the end of the item // Need to compare if we delete the last character in the non-type-ahead string fNeedCompare = true; fTypeAhead = true; #ifdef DEBUG_THIS_FILE sta.Format("OnChange: kscDelForward=%d.\n"); OutputDebugString(sta.Chars()); sta.Format("OnChange: 2 - setting fTypeAhead to true.\n"); OutputDebugString(sta.Chars()); #endif } else { // Always compare if we are not past the last match. fNeedCompare = true; } int cch; // Since the edit box deletes the selection on backspace, we need to use // some extra logic to make backspace actually move the selection back. if (pedit->m_cchMatched && pedit->m_ch == VK_BACK && m_pss) { // If we had a previous match and we got a backspace, we always decrement the matched // characters and start looking at that point. cch = --pedit->m_cchMatched; } else { // Otherwise we start looking at the cursor location. cch = ichMin; } AfLpInfo * plpi = GetLpInfo(); AssertPtr(plpi); PossListInfoPtr qpli; plpi->LoadPossList(m_hvoPssl, m_wsMagic, &qpli); AssertPtr(qpli); PossItemInfo * ppii = NULL; ComBool fExactMatch = false; fNeedCompare = true; if (cchTyped == 0) { // If nothing to match, get the first item in the possibility list. If we are // already at that item, remove the item. If we are already cleared, do nothing. if (!m_pss) return true; // If everything is highlighted, we want to clear the item with Del or Bsp. // But when we are backspacing, if there is only one character left and we backspace // over that, we want to switch to the first item in the list. ppii = qpli->GetPssFromIndex(0); if (ichMin != 1) { m_pss = 0; m_qtss = NULL; pedit->m_cchMatched = 0; // Keep cursor at beginning of item. m_fRecurse = true; ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)m_qtss.Ptr()); return true; } } else if (fNeedCompare) { // Try to find an item that matches what the user typed in the possibility list. StrUni stuMatch(stuTyped); ///// stuMatch.Replace(cch, stuMatch.Length(), L"");// Delete chars to right of cursor. Locale loc = GetLpInfo()->GetLocale(m_ws); if (m_fHier) { ppii = qpli->FindPssHier(stuMatch.Chars(), loc, m_pnt, fExactMatch); } else { ppii = qpli->FindPss(stuMatch.Chars(), loc, m_pnt); } if (ppii) { // found a match that starts with stuTyped int ipssTemp; // TODO TimP: check for hierarchy. If stuTyped contains hierarchy, // Was the match exact (rather than just starting with stuTyped) ? if (fExactMatch) // May have matched in the FindPssHier() call above. { #ifdef DEBUG_THIS_FILE sta.Format("OnChange: Exact match (hier).\n"); OutputDebugString(sta.Chars()); #endif } else { fExactMatch = ! qpli->PossUniqueName(-1, stuTyped, m_pnt, ipssTemp); if (fExactMatch) { #ifdef DEBUG_THIS_FILE sta.Format("OnChange: Exact match.\n"); OutputDebugString(sta.Chars()); #endif // in case FindPss() above matches "ABC" but "AB" is also in list. ppii = qpli->GetPssFromIndex(ipssTemp); } else { #ifdef DEBUG_THIS_FILE sta.Format("OnChange: Not exact match.\n"); OutputDebugString(sta.Chars()); #endif } } } } else ppii = NULL; StrUni stuFound; int ws = m_ws; if (ppii && (fTypeAhead || fExactMatch)) { // If found, process the new item. int pss = ppii->GetPssId(); m_pss = pss; if (m_fHier) ppii->GetHierName(qpli, stuFound, m_pnt); else ppii->GetName(stuFound, m_pnt); ws = ppii->GetWs(); // If the last character was a delimiter, we need to set cch accordingly. if (m_fHier && (stuTyped.Length() > 0) && (pedit->m_ch != VK_BACK) && (stuTyped.GetAt(stuTyped.Length() - 1) == kchHierDelim)) { // Need to set cch to the position of the last delimiter. cch = stuFound.FindCh(kchHierDelim, cch - 1) + 1; } pedit->m_cchMatched = cch; } else { // Something illegal was typed. Assume they are adding a new item. if (pedit->m_cchMatched + 1 == cch && pedit->m_ch != VK_BACK) ::MessageBeep(MB_ICONEXCLAMATION); // Beep on the first unmatched character. // Underline the string with a red squiggly. ITsIncStrBldrPtr qtisb; qtisb.CreateInstance(CLSID_TsIncStrBldr); qtisb->SetIntPropValues(ktptWs, ktpvDefault, m_ws); CheckHr(qtisb->SetIntPropValues(ktptUnderColor, ktpvDefault, kclrRed)); CheckHr(qtisb->SetIntPropValues(ktptUnderline, ktpvEnum, kuntSquiggle)); qtisb->AppendRgch(stuTyped.Chars(), stuTyped.Length()); qtisb->GetString(&m_qtss); m_fRecurse = true; // Stop the recursion caused by the next instruction. // Note: This recursively calls this procedure. ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)m_qtss.Ptr()); ::SendMessage(m_hwnd, EM_SETSEL, ichMin, ichMin); m_pss = 0; // We no longer have a matched HVO. return true; } // Update the edit box text and selection. ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); qtsf->MakeStringRgch(stuFound.Chars(), stuFound.Length(), ws, &qtss); m_qtss = qtss; #ifdef DEBUG_THIS_FILE sta.Format("AfDeFeCliRef::OnChange: pedit->m_cchMatched=%d; stuFound.Length()=%d; ichMin=%d; ichLim=%d.\n", pedit->m_cchMatched, stuFound.Length(), ichMin, ichLim); OutputDebugString(sta.Chars()); #endif m_fRecurse = true; // Shortcut the recursion caused by the next instruction. if (fTypeAhead) { // Note: This recursively calls this procedure. ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)qtss.Ptr()); ::SendMessage(m_hwnd, EM_SETSEL, pedit->m_cchMatched, stuFound.Length()); #ifdef DEBUG_THIS_FILE sta.Format("AfDeFeCliRef::OnChange: type ahead.\n"); OutputDebugString(sta.Chars()); #endif } else { // Note: This recursively calls this procedure. ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)qtss.Ptr()); ::SendMessage(m_hwnd, EM_SETSEL, ichMin, ichMin); #ifdef DEBUG_THIS_FILE sta.Format("AfDeFeCliRef::OnChange: NOT type ahead.\n"); OutputDebugString(sta.Chars()); #endif } return true; }