/*---------------------------------------------------------------------------------------------- Refresh the field from the data cache. We only need to do something if a new text has been edited. ----------------------------------------------------------------------------------------------*/ void AfDeFeSt::UpdateField() { // See if we have a dummy cache filling a missing text. if (!m_qvcdMissing) return; // If not, the view code updates things automatically from the real cache. HVO hvoText; CheckHr(m_qcvd->get_ObjectProp(m_hvoObj, m_flid, &hvoText)); if (!hvoText) return; // The main cache doesn't have anything yet. // We now have a text, but we still have a dummy data cache, so // we need to throw out the dummy data cache and use the real cache. m_hvoText = hvoText; m_qvcdMissing.Clear(); // Update the root object to point at the real text in the main cache. int frag = kfrText; IVwViewConstructor * pvvc = m_qstvc; CheckHr(m_qrootb->putref_DataAccess(m_qcvd)); CheckHr(m_qrootb->SetRootObjects(&m_hvoText, &pvvc, &frag, GetLpInfo()->GetAfStylesheet(), 1)); if (m_hwnd) { // If we have an editing window open, make it redraw. ::InvalidateRect(m_hwnd, NULL, true); } }
/*---------------------------------------------------------------------------------------------- This initializes the string based after the pssl and pss have been set. It takes into account whether the view is hierarchical or not. @param fHier @param pnt ----------------------------------------------------------------------------------------------*/ void AfDeFeCliRef::InitContents(bool fHier, PossNameType pnt) { Assert(m_hvoPssl); // This must be set prior to calling this method. Assert(pnt < kpntLim); SuperClass::Init(); // Initialize the superclass. m_fHier = fHier; m_pnt = pnt; if (!m_pss) return; // If the reference isn't set, we don't have anything to display. ITsStringPtr qtss; ITsStrFactoryPtr qtsf; StrUni stu; PossListInfoPtr qpli; PossItemInfo * ppii; int ipss; qtsf.CreateInstance(CLSID_TsStrFactory); GetLpInfo()->LoadPossList(m_hvoPssl, m_wsMagic, &qpli); AssertPtr(qpli); ipss = qpli->GetIndexFromId(m_pss); ppii = qpli->GetPssFromIndex(ipss); AssertPtr(ppii); 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; qpli->AddNotify(this); }
/*---------------------------------------------------------------------------------------------- Complete initialization of the property by storing the appropriate items, names, and lists. @param pssl The id for the People possibility list we are showing in this field. @param hvoOwner The owner of the RnRoledPartic we are displaying in this field. @param flid The flid in hvoOwner owning the RnRoledPartic we are displaying. @param pssRole The id of the CmPossibility from the Role list that we are using to display the label. (May be NULL for the unspecified item). @param fHier True if item names include parent items in the hierarchy. @param pnt Indicates whether we show name, abbr, or both for each item. ----------------------------------------------------------------------------------------------*/ void RnDeFeRoleParts::Init(HVO pssl, HVO hvoOwner, int flid, HVO pssRole, bool fHier, PossNameType pnt) { Assert(m_flid); // Don't call this until Initialize has been called. Assert(pssl); Assert(hvoOwner); Assert(flid); Assert((uint)pnt < (uint)kpntLim); // Save the list id. m_vpssl.Push(pssl); m_fHier = fHier; m_pnt = pnt; m_hvoOwner = hvoOwner; m_flidOwner = flid; m_pssRole = pssRole; // Set the proper m_wsMagic value for the People possibility list. m_wsMagic = kwsVernAnals; // Set up a notifier for the people list. PossListInfoPtr qpli; GetLpInfo()->LoadPossList(pssl, m_wsMagic, &qpli); AssertPtr(qpli); qpli->AddNotify(this); // Set up a notifier for the role list. if (pssRole) { GetLpInfo()->LoadPossListForItem(pssRole, m_wsMagic, &qpli); AssertPtr(qpli); qpli->AddNotify(this); m_psslRole = qpli->GetPsslId(); } // Create the display cache. m_qvcd.CreateInstance(CLSID_VwCacheDa); // Create the view constructor. m_qtgvc.Attach(NewObj TagsVc(m_qfsp->m_stuSty.Chars(), m_chrp.clrBack)); // Set the writing system info (especially for Right-To-Left handling). ILgWritingSystemFactoryPtr qwsf; GetLpInfo()->GetDbInfo()->GetLgWritingSystemFactory(&qwsf); m_qtgvc->SetWritingSystemInfo(m_ws, qwsf); // Add information to the display cache. FillDisplayCache(); }
/*---------------------------------------------------------------------------------------------- Relase all smart pointers. ----------------------------------------------------------------------------------------------*/ void AfDeFeCliRef::OnReleasePtr() { PossListInfoPtr qpli; if (GetLpInfo()->GetPossList(m_hvoPssl, m_wsMagic, &qpli)) { AssertPtr(qpli); qpli->RemoveNotify(this); } SuperClass::OnReleasePtr(); }
/*---------------------------------------------------------------------------------------------- Refresh the field from the data cache. ----------------------------------------------------------------------------------------------*/ void AfDeFeCliRef::UpdateField() { // Get the item info from the cache. CustViewDaPtr qcvd; GetDataAccess(&qcvd); AssertPtr(qcvd); HVO hvoPss; CheckHr(qcvd->get_ObjectProp(m_hvoObj, m_flid, &hvoPss)); m_pss = hvoPss; // Get the string from the poss cache. ITsStringPtr qtss; ITsStrFactoryPtr qtsf; StrUni stu; PossListInfoPtr qpli; PossItemInfo * ppii; int ipss; qtsf.CreateInstance(CLSID_TsStrFactory); int ws = m_ws; if (m_pss) { GetLpInfo()->LoadPossList(m_hvoPssl, m_wsMagic, &qpli); AssertPtr(qpli); ipss = qpli->GetIndexFromId(m_pss); if (ipss >= 0) { ppii = qpli->GetPssFromIndex(ipss); AssertPtr(ppii); if (m_fHier) ppii->GetHierName(qpli, stu, m_pnt); else ppii->GetName(stu, m_pnt); ws = ppii->GetWs(); } } qtsf->MakeStringRgch(stu.Chars(), stu.Length(), ws, &qtss); m_qtss = qtss; // If we have an edit box, update the contents. if (m_hwnd) { // Setting the property causes a recursive call to OnChange, so we want to block it // from making a further change. (e.g., when you backspace to set the string to null // then tab to the next field, we want it to stay null, not go to the first item in // the list.) m_fRecurse = true; ::SendMessage(m_hwnd, FW_EM_SETTEXT, 0, (LPARAM)m_qtss.Ptr()); } }
/*---------------------------------------------------------------------------------------------- Finish initialization. ----------------------------------------------------------------------------------------------*/ void AfDeFeSt::Init(CustViewDa * pcvd, HVO hvoText) { AssertPtr(pcvd); Assert(m_hvoObj); // Initialize should have been called prior to this. AssertPtr(m_qadsc); // Get the current default writing system for this field and convert any magic number. AfLpInfo * plpi = GetLpInfo(); AssertPtr(plpi); m_qcvd = pcvd; m_hvoText = hvoText; ILgWritingSystemFactoryPtr qwsf; plpi->GetDbInfo()->GetLgWritingSystemFactory(&qwsf); m_qstvc.Attach(NewObj StVc(m_qfsp->m_stuSty.Chars(), m_ws, m_chrp.clrBack, qwsf)); }
/*---------------------------------------------------------------------------------------------- Initialize font after superclass initialization is done. ----------------------------------------------------------------------------------------------*/ void AfDeFeEdBoxBut::Init() { Assert(m_hvoObj); // Initialize should have been called first. CreateFont(); ILgWritingSystemFactoryPtr qwsf; IWritingSystemPtr qws; GetLpInfo()->GetDbInfo()->GetLgWritingSystemFactory(&qwsf); AssertPtr(qwsf); ComBool fRTL = FALSE; CheckHr(qwsf->get_EngineOrNull(m_ws, &qws)); if (qws) CheckHr(qws->get_RightToLeft(&fRTL)); m_fRtl = bool(fRTL); }
/*---------------------------------------------------------------------------------------------- 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); }
void AfDeFeSt::MakeRoot(IVwGraphics * pvg, ILgWritingSystemFactory * pwsf, IVwRootBox ** pprootb) { AssertPtrN(pwsf); *pprootb = NULL; // Do we have a real text object? If not, make a fake one. int cpara = 0; ISilDataAccessPtr qsda = m_qcvd; if (m_hvoText) CheckHr(qsda->get_VecSize(m_hvoText, kflidStText_Paragraphs, &cpara)); if (!cpara) { // We make a dummy text that is complete enough to edit, in a separate data access // object. On loss of focus, if this data access is dirty, we make corresponding // real objects. if (!m_hvoText) m_hvoText = -1; HVO hvoPara = -2; m_qvcdMissing.CreateInstance(CLSID_VwCacheDa); CheckHr(m_qvcdMissing->QueryInterface(IID_ISilDataAccess, (void **)&qsda)); m_qvcdMissing->CacheVecProp(m_hvoText, kflidStText_Paragraphs, &hvoPara,1); ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); ITsStringPtr qtssMissing; CheckHr(qtsf->MakeStringRgch(L"", 0, m_ws, &qtssMissing)); m_qvcdMissing->CacheStringProp(hvoPara, kflidStTxtPara_Contents, qtssMissing); } IVwRootBoxPtr qrootb; qrootb.CreateInstance(CLSID_VwRootBox); CheckHr(qrootb->SetSite(this)); // pass root site int frag = kfrText; IVwViewConstructor * pvvc = m_qstvc; if (pwsf) CheckHr(qsda->putref_WritingSystemFactory(pwsf)); CheckHr(qrootb->putref_DataAccess(qsda)); CheckHr(qrootb->SetRootObjects(&m_hvoText, &pvvc, &frag, GetLpInfo()->GetAfStylesheet(), 1)); *pprootb = qrootb; (*pprootb)->AddRef(); RecMainWnd * prmw = dynamic_cast<RecMainWnd *>(m_qadsc->MainWindow()); if (prmw) prmw->RegisterRootBox(qrootb); }
/*---------------------------------------------------------------------------------------------- Release all smart pointers. ----------------------------------------------------------------------------------------------*/ void RnDeFeRoleParts::OnReleasePtr() { // Remove the notifier for the role list. if (m_psslRole) { PossListInfoPtr qpli; // Note: We need to use GetPossList here rather than LoadPossList since the // latter will try to reload the list if not present. During shutdown, the // possibility lists have already been cleared from AfLpInfo before we reach // this, so we want to quitely shut down. Using LoadPossList here produces a // massive memory leak. if (GetLpInfo()->GetPossList(m_psslRole, m_wsMagic, &qpli)) { AssertPtr(qpli); qpli->RemoveNotify(this); } m_psslRole = 0; } SuperClass::OnReleasePtr(); }
/*---------------------------------------------------------------------------------------------- Close the current editor, saving changes that were made. hwnd is the editor hwnd. @param fForce True if we want to force the editor closed without making any validity checks or saving any changes. ----------------------------------------------------------------------------------------------*/ void AfDeFeSt::EndEdit(bool fForce) { if (m_qvcdMissing && !fForce) { ComBool fDirty; ISilDataAccessPtr qsdaTemp; CheckHr(m_qvcdMissing->QueryInterface(IID_ISilDataAccess, (void **)&qsdaTemp)); qsdaTemp->IsDirty(&fDirty); if (fDirty) { // The text got edited. So, make a real text and copy the relevant info. // First get the paragraph list HvoVec vhvoParas; int chvoParas; // If we already had a text, we will notify it of new paragraphs. bool fNotifyText = m_hvoText > 0; CheckHr(qsdaTemp->get_VecSize(m_hvoText, kflidStText_Paragraphs, &chvoParas)); int ihvo; // used twice for (ihvo = 0; ihvo < chvoParas; ihvo++) { HVO hvoPara; CheckHr(qsdaTemp->get_VecItem(m_hvoText, kflidStText_Paragraphs, ihvo, &hvoPara)); vhvoParas.Push(hvoPara); } // Actually, we may have had a real text...if so, skip this step if (m_hvoText < 0) { m_qcvd->MakeNewObject(kclidStText, m_hvoObj, m_flid, -2, &m_hvoText); } else { // Check if the record has been edited by someone else since we first loaded // the data. HRESULT hrTemp; if ((hrTemp = m_qcvd->CheckTimeStamp(m_hvoText)) != S_OK) { // If it was changed and the user does not want to overwrite it, perform a // refresh so the displayed field will revert to it's original value. SuperClass::EndEdit(fForce); // REVIEW KenZ (PaulP): There may need to be a refresh call made here. // It's difficult to know, however, since I haven't tracked down when // this method actually gets called. m_fDirty = false; return; } } // Now make real paragraph objects and set their properties. for (ihvo = 0; ihvo < chvoParas; ihvo++) { HVO hvoPara = vhvoParas[ihvo]; ITsStringPtr qtss; ITsTextPropsPtr qttp; IUnknownPtr qunkTtp; CheckHr(qsdaTemp->get_UnknownProp(hvoPara, kflidStStyle_Rules, &qunkTtp)); CheckHr(qunkTtp->QueryInterface(IID_ITsTextProps, (void **) &qttp)); CheckHr(qsdaTemp->get_StringProp(hvoPara, kflidStTxtPara_Contents, &qtss)); CheckHr(m_qcvd->MakeNewObject(kclidStTxtPara, m_hvoText, kflidStText_Paragraphs, ihvo, &hvoPara)); if (qttp) CheckHr(m_qcvd->SetUnknown(hvoPara, kflidStPara_StyleRules, qttp)); CheckHr(m_qcvd->SetString(hvoPara, kflidStTxtPara_Contents, qtss)); } m_qvcdMissing.Clear(); // Update the root object to point at the real text we just made. int frag = kfrText; IVwViewConstructor * pvvc = m_qstvc; CheckHr(m_qrootb->putref_DataAccess(m_qcvd)); CheckHr(m_qrootb->SetRootObjects(&m_hvoText, &pvvc, &frag, GetLpInfo()->GetAfStylesheet(), 1)); m_qadsc->UpdateAllDEWindows(m_hvoObj, m_flid); if (fNotifyText) { CheckHr(m_qcvd->PropChanged(m_qrootb, kpctNotifyAllButMe, m_hvoText, kflidStText_Paragraphs, 0, chvoParas, chvoParas)); } else { CheckHr(m_qcvd->PropChanged(m_qrootb, kpctNotifyAllButMe, m_hvoObj, m_flid, 0, 1, 1)); } } } // Do this after the changes above, otherwise, our hwnd is no longer the child of a main // window, and we can't find the style sheet for the SetRootObjects call. SuperClass::EndEdit(fForce); m_fDirty = false; }
/*---------------------------------------------------------------------------------------------- Make an edit box to allow editing. hwnd is the parent hwnd. rc is the size of the child window. Store the hwnd and return true. @param hwnd @param rc @param dxpCursor @param fTopCursor @param tpte @return true if successful TODO: To handle extra long field contents (and RTL?) the button cannot be a child of the edit control, because they must occupy seperate rectangles inside the field proper. ----------------------------------------------------------------------------------------------*/ bool AfDeFeEdBoxBut::BeginEdit(HWND hwnd, Rect &rc, int dxpCursor, bool fTopCursor, TptEditable tpte) { if (!SuperClass::BeginEdit(hwnd, rc, dxpCursor, fTopCursor)) return false; DeEditPtr qde; qde.Create(); qde->SetEditable(tpte); qde->m_pdee = this; IActionHandler * pacth = BeginTempEdit(); ILgWritingSystemFactoryPtr qwsf; GetLpInfo()->GetDbInfo()->GetLgWritingSystemFactory(&qwsf); AssertPtr(qwsf); #if 1 int nRet = 0; ComBool fRTL = FALSE; IWritingSystemPtr qws; CheckHr(qwsf->get_EngineOrNull(m_ws, &qws)); if (qws) CheckHr(qws->get_RightToLeft(&fRTL)); if (fRTL) qde->Create(hwnd, kwidEditChild, WS_CHILD | ES_RIGHT | ES_AUTOHSCROLL, NULL, m_qtss, qwsf, m_ws, pacth); else #endif qde->Create(hwnd, kwidEditChild, WS_CHILD | ES_LEFT | ES_AUTOHSCROLL, NULL, m_qtss, qwsf, m_ws, pacth); m_hwnd = qde->Hwnd(); Rect rcT(rc.left + 2, rc.top + 1, rc.right, rc.bottom); nRet = ::MoveWindow(m_hwnd, rcT.left, rcT.top, rcT.Width(), rcT.Height(), true); #if 99-99 Rect rcParentClient; Rect rcParent; ::GetClientRect(hwnd, &rcParentClient); ::GetWindowRect(hwnd, &rcParent); Rect rcEdit; Rect rcEditClient; ::GetWindowRect(m_hwnd, &rcEdit); ::GetClientRect(m_hwnd, &rcEditClient); #endif Rect rcTb; ::GetClientRect(m_hwnd, &rcTb); rcTb.left = rcTb.right - 16; rcTb.bottom = rcTb.top + Min((int)rcTb.bottom - (int)rcTb.top, (int)kdxpButtonHeight); WndCreateStruct wcsButton; wcsButton.InitChild(_T("BUTTON"), m_hwnd, kwidEditChild); wcsButton.style |= WS_VISIBLE | BS_OWNERDRAW; wcsButton.SetRect(rcTb); DeButtonPtr qdb; qdb.Create(); qdb->CreateAndSubclassHwnd(wcsButton); qdb->m_pdee = this; m_hwndButton = qdb->Hwnd(); #if 1-1 // Resize the edit control window to exclude the button nRet = ::MoveWindow(m_hwnd, rcT.left, rcT.top, rcT.Width() - 18, rcT.Height(), true); #endif // Add text to the window. ::SendMessage(m_hwnd, FW_EM_SETSTYLE, m_chrp.clrBack, (LPARAM)&m_qfsp->m_stuSty); ::SendMessage(m_hwnd, EM_SETMARGINS, EC_RIGHTMARGIN | EC_LEFTMARGIN, MAKELPARAM(0, 18)); ::ShowWindow(m_hwnd, SW_SHOW); //::SendMessage(m_hwnd, WM_SETFONT, (WPARAM)::GetStockObject(DEFAULT_GUI_FONT), 0); // Foreground/background colors are set via WM_CTLCOLOREDIT in AfDeFeWnd. // Set cursor to desired offset. //int ich; //ich = LOWORD(::SendMessage(m_hwnd, EM_CHARFROMPOS, 0, dxpCursor)); // For some reason the above always comes back with -1 instead of the correct index. // Is this a bug in TssEdit or am I doing something wrong? //::SendMessage(m_hwnd, EM_SETSEL, ich, ich); //::mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0); // Send LButton to place cursor in edit ctrl. #if 99-99 Rect rcEditNew; Rect rcEditNewClient; Rect rcBut; Rect rcButClient; ::GetWindowRect(m_hwnd, &rcEditNew); ::GetClientRect(m_hwnd, &rcEditNewClient); ::GetWindowRect(m_hwndButton, &rcBut); ::GetClientRect(m_hwndButton, &rcButClient); #endif return 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; }
/*---------------------------------------------------------------------------------------------- Something has changed in the possibility list. ----------------------------------------------------------------------------------------------*/ void RnDeFeRoleParts::ListChanged(int nAction, HVO hvoPssl, HVO hvoSrc, HVO hvoDst, int ipssSrc, int ipssDst) { if (hvoPssl != m_psslRole) { // Process changes for people lists. SuperClass::ListChanged(nAction, hvoPssl, hvoSrc, hvoDst, ipssSrc, ipssDst); return; } // If someone deleted the role we are interested in we need to clean up. if (hvoSrc == m_pssRole && nAction == kplnaDelete) { // Remove the notifier for the role list. Assert(m_psslRole); PossListInfoPtr qpli; GetLpInfo()->LoadPossList(m_psslRole, m_wsMagic, &qpli); AssertPtr(qpli); qpli->RemoveNotify(this); m_psslRole = 0; m_pssRole = 0; } // At this point we are not getting kplnaMerged at all since we are just deleting all // fields and recreating new ones. But at some point down the road we may want to switch // to where we don't delete and recreate. This would be the right action to take then. if (hvoSrc == m_pssRole && nAction == kplnaMerged) { // Switch to the new role. m_pssRole = hvoDst; } // Now reset the label in case we changed. // Use Participants for the unspecified editor label. ITsStringPtr qtssName; int wsUser = GetLpInfo()->GetDbInfo()->UserWs(); AfUtil::GetResourceTss(kstidTlsOptParticipants, wsUser, &qtssName); ITsStringPtr qtssHelp = m_qfsp->m_qtssHelp; ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); if (m_pssRole) { PossItemInfo * ppii; GetLpInfo()->GetPossListAndItem(m_pssRole, wsUser, &ppii, NULL); StrUni stuName; // the Role's name ppii->GetName(stuName, m_pnt); qtsf->MakeStringRgch(stuName.Chars(), stuName.Length(), wsUser, &qtssName); ITsIncStrBldrPtr qtisb; qtisb.CreateInstance(CLSID_TsIncStrBldr); // Add the first substring. qtisb->AppendTsString(m_qfsp->m_qtssHelp); // Add the second substring. StrUni stuTemp; stuTemp.Load(kstidRnRoledPartic_HelpA); qtisb->AppendRgch(stuTemp.Chars(), stuTemp.Length()); // Turn bold on. qtisb->SetIntPropValues(ktptBold, ktpvEnum, kttvForceOn); // Add the Role's name qtisb->AppendRgch(stuName.Chars(), stuName.Length()); // Turn bold off. qtisb->SetIntPropValues(ktptBold, ktpvEnum, kttvOff); // Add the third substring. stuTemp.Load(kstidRnRoledPartic_HelpB); qtisb->AppendRgch(stuTemp.Chars(), stuTemp.Length()); // Get the completed TsString. qtisb->GetString(&qtssHelp); } m_qtssLabel = qtssName; m_qtssHelp = qtssHelp; // Force the label to redraw. Rect rcLabel; ::GetClientRect(m_qadsc->Hwnd(),&rcLabel); Rect rcEdit(m_rcClip); if (m_hwnd) { // We have an open edit box, so get the correct coordinates for the client window. ::GetClientRect(m_hwnd, &rcEdit); ::MapWindowPoints(m_hwnd, m_qadsc->Hwnd(), (POINT *)&rcEdit, 2); } rcLabel.top = rcEdit.top; rcLabel.bottom = rcEdit.bottom; rcLabel.right = m_qadsc->GetTreeWidth(); ::InvalidateRect(m_qadsc->Hwnd(), &rcLabel, false); }