/*---------------------------------------------------------------------------------------------- Set the text of the control to be equal to ptss. If ptss is NULL, the edit box is cleared. Message: FW_EM_SETTEXT. ----------------------------------------------------------------------------------------------*/ void TssEdit::SetText(ITsString * ptss) { AssertPtrN(ptss); // NULL can be used to clear string. Assert(m_qcda); ITsStringPtr qtss = ptss; if (!ptss) { ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); CheckHr(qtsf->MakeStringRgch(L"", 0, m_wsBase, &qtss)); } ITsStringPtr qtssOld; CheckHr(m_qcda->get_StringProp(khvoString, ktagString, &qtssOld)); int cchOld; CheckHr(qtssOld->get_Length(&cchOld)); CheckHr(m_qcda->CacheStringProp(khvoString, ktagString, qtss)); int cchNew; CheckHr(qtss->get_Length(&cchNew)); // Pretend the whole length has been deleted and the whole new inserted. CheckHr(m_qcda->PropChanged(NULL, kpctNotifyAll, khvoString, ktagString, 0, cchNew, cchOld)); OnUpdate(); ::UpdateWindow(m_hwnd); OnChange(); }
/*---------------------------------------------------------------------------------------------- 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 AfDeFeCliRef::EndEdit(bool fForce) { if (!fForce) { if (!m_pss) { // The user added a new item. ITsStringPtr qtss; int cch; StrUni stu; // Name/Abbr string entered by user. OLECHAR * pchBuf; // Get the characters from the edit box. ::SendMessage(m_hwnd, FW_EM_GETTEXT, 0, (LPARAM)&qtss); qtss->get_Length(&cch); stu.SetSize(cch, &pchBuf); qtss->FetchChars(0, cch, pchBuf); if (cch) { // If a non-null string is present, create a new item. m_fSaving = true; // Disable updates while adding a new item. // The user created a new item, so add it to the possibility list. m_pss = CreatePss(m_hvoPssl, stu.Chars(), m_pnt, m_fHier); Assert(m_pss); // What should we do if we have an error? m_fSaving = false; } } } // When we close the edit window, the squiggley will automatically be removed. SuperClass::EndEdit(fForce); }
/*---------------------------------------------------------------------------------------------- Save the text to a file. ----------------------------------------------------------------------------------------------*/ bool WpDa::SaveToFile(StrAnsi staFileName) { IStreamPtr qstrm; FileStream::Create(staFileName.Chars(), kfstgmWrite | kfstgmCreate, &qstrm); // FileStream::Create("c:\\output.txt", kfstgmWrite | kfstgmCreate, &qstrm); ULONG cbWritten; // Write byte-order mark. byte b = 0xFF; CheckHr(qstrm->Write(&b, 1, &cbWritten)); b = 0xFE; CheckHr(qstrm->Write(&b, 1, &cbWritten)); int ctss; CheckHr(get_VecSize(1, kflidStText_Paragraphs, &ctss)); for (int itss = 0; itss < ctss; itss++) { ITsStringPtr qtss; CheckHr(get_StringProp(itss + 2, kflidStTxtPara_Contents, &qtss)); BSTR bstr = NULL; int cchw; CheckHr(qtss->get_Length(&cchw)); qtss->GetChars(0, cchw, &bstr); // Write the string to the file. if (cchw > 0) { Assert(bstr); CheckHr(qstrm->Write(bstr, cchw * isizeof(wchar), &cbWritten)); } if (itss < ctss - 1) { // CRLF--little-endian b = 13; CheckHr(qstrm->Write(&b, 1, &cbWritten)); b = 0; CheckHr(qstrm->Write(&b, 1, &cbWritten)); b = 10; CheckHr(qstrm->Write(&b, 1, &cbWritten)); b = 0; CheckHr(qstrm->Write(&b, 1, &cbWritten)); } ReleaseBstr(bstr); } return true; }
/*---------------------------------------------------------------------------------------------- Return true if the text buffer is empty--that is, containing only one empty string. ----------------------------------------------------------------------------------------------*/ bool WpDa::IsEmpty() { int ctss; CheckHr(get_VecSize(1, kflidStText_Paragraphs, &ctss)); if (ctss == 0) return true; if (ctss > 1) return false; ITsStringPtr qtss; CheckHr(get_StringProp(2, kflidStTxtPara_Contents, &qtss)); int cchw; CheckHr(qtss->get_Length(&cchw)); return (cchw == 0); }
/*---------------------------------------------------------------------------------------------- Display TsString in the control (may be explicitly called when created by ATL, because in this case TssEdit::Create() will not be called) ----------------------------------------------------------------------------------------------*/ void TssEdit::PostCreate(ITsString * ptss) { AssertPtrN(ptss); if (ptss) { int cch; ITsStringPtr qtss = ptss; CheckHr(qtss->get_Length(&cch)); // Treat as inserting all the characters, since the cache previously had nothing. CheckHr(m_qcda->PropChanged(NULL, kpctNotifyAll, khvoString, ktagString, 0, cch, 0)); OnUpdate(); ::UpdateWindow(m_hwnd); OnChange(); } }
/*---------------------------------------------------------------------------------------------- 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); }
/*---------------------------------------------------------------------------------------------- Check the requirments of the FldSpec, and verify that data in the field meets the requirement. It returns: kFTReqNotReq if the all requirements are met. kFTReqWs if data is missing, but it is encouraged. kFTReqReq if data is missing, but it is required. ----------------------------------------------------------------------------------------------*/ FldReq AfDeFeSt::HasRequiredData() { if (m_qfsp->m_fRequired == kFTReqNotReq) return kFTReqNotReq; CustViewDaPtr qcvd; GetDataAccess(&qcvd); AssertPtr(qcvd); bool fEmpty = true; ITsStringPtr qtss; int cch = 0; HVO hvoPara; int iPara; if (!m_hvoText) goto LExit; int cPara; CheckHr(qcvd->get_VecSize(m_hvoText, kflidStText_Paragraphs, &cPara)); if (!cPara) goto LExit; // Make sure at least some paragraph has data. for (iPara = 0; iPara < cPara; ++iPara) { CheckHr(qcvd->get_VecItem(m_hvoText, kflidStText_Paragraphs, iPara, &hvoPara)); // At some point we may need to handle something other than StTxtPara. CheckHr(qcvd->get_StringProp(hvoPara, kflidStTxtPara_Contents, &qtss)); if (qtss) CheckHr(qtss->get_Length(&cch)); if (cch) break; // Have a valid string } fEmpty = iPara == cPara; LExit: if (fEmpty) return m_qfsp->m_fRequired; else return kFTReqNotReq; }
/*---------------------------------------------------------------------------------------------- Selects a range of characters. Message: EM_SETSEL. ----------------------------------------------------------------------------------------------*/ void TssEdit::SetSel(int ichAnchor, int ichEnd) { Assert(m_qrootb); int cch = 0; ITsStringPtr qtss; CheckHr(m_qcda->get_StringProp(khvoString, ktagString, &qtss)); if (qtss) CheckHr(qtss->get_Length(&cch)); if (ichAnchor < 0) ichAnchor = cch; if (ichEnd < 0) ichEnd = cch; if (ichAnchor > cch) ichAnchor = cch; // This can happen; apparently when a tab brings the focus to this window, Windows passes // a large number rather than -1 to set the end of the range. if (ichEnd > cch) ichEnd = cch; CheckHr(m_qrootb->MakeTextSelection(0, 0, NULL, ktagString, 0, ichAnchor, ichEnd, 0, true, -1, NULL, true, NULL)); }
/*---------------------------------------------------------------------------------------------- Save data in the cache, to the database. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP VwRsOdbcDa::Save(SqlDb & sdb) { BEGIN_COM_METHOD const kcchErrMsgBuf = 1024; const kcbFmtBufMax = 1024; const kcchMaxColNameSize = 200; SQLINTEGER cbColVal; SDWORD cbClassName; long cbHvo; SDWORD cbFieldName; SDWORD cbFlid; int cbFmtBufSize = kcbFmtBufMax; int cbFmtSpaceTaken; SDWORD cbType; SQLUSMALLINT cParamsProcessed = 0; HRESULT hr; FieldMetaDataMap fmdm; // REVIEW PaulP: Would be nice to have this kept globally. FieldMetaDataRec fmdr; int nColVal; int nFlid; ITsStringPtr qtssColVal; RETCODE rc; byte * rgbFmt = NewObj byte[kcbFmtBufMax]; //[kcbFmtBufMax]; SmartBstr sbstr; SQLCHAR sqlchSqlState[5]; SQLCHAR sqlchMsgTxt[kcchErrMsgBuf]; SQLINTEGER sqlnNativeErrPtr; SQLSMALLINT sqlnTextLength; SQLRETURN sqlr; SqlStatement sstmt; StrUni suColumnName; StrUni suFieldMetaDataSql; StrUni suSql; StrUni suTableName; wchar wchClassName[kcchMaxColNameSize]; wchar wchFieldName[kcchMaxColNameSize]; Assert(sdb.IsOpen()); /*------------------------------------------------------------------------------------------ If a pointer to a HashMap of field$ metadata information was not passed in as a parameter, create the HashMap. The metaData includes all field names, field types, class names, and destination class name from the field$ and class$ tables. This information is needed since only store the "flid" value is stored in the cache (as the "tag") so when it comes time to do SQL "insert" and "update" statements, one need's to know what table and column needs to be affected. REVIEW PaulP: Should really cache this information globally so that it does not have to be reloaded each time the view cache is saved. The only time this info would have to be reloaded is when custom fields are added. Perhaps we could have a table with a single record which indicates the last date/time a change to the database was made. This could be compared with the currently loaded date each time the cache is saved to see if there is a need to reload the field$ meta information. Of course, if we require that only one user is allowed to be connected for a custom field addition, this would not be necessary. TOxDO PaulP: Cleanup on any error exit, including db rollback. CheckSqlRc(SQLEndTran(SQL_HANDLE_DBC, sdb.Hdbc(), SQL_ROLLBACK)); ------------------------------------------------------------------------------------------*/ try { // Set up SQL statment to obtain meta data. suFieldMetaDataSql.Format( L"select f.Id FLID, Type, f.Name FLD_NAME," L" c.Name CLS_NAME" L" from Field$ f" L" join Class$ c on c.id = f.Class" L" order by f.id"); sstmt.Init(sdb); CheckSqlRc(SQLExecDirectW(sstmt.Hstmt(), \ const_cast<wchar *>(suFieldMetaDataSql.Chars()), SQL_NTS)); // Bind Columns. rc = SQLSetStmtAttr(sstmt.Hstmt(), SQL_ATTR_PARAMS_PROCESSED_PTR, &cParamsProcessed, 0); CheckSqlRc(SQLBindCol(sstmt.Hstmt(), 1, SQL_C_SLONG, &nFlid, isizeof(int), &cbFlid)); CheckSqlRc(SQLBindCol(sstmt.Hstmt(), 2, SQL_C_SLONG, &fmdr.m_iType, isizeof(fmdr.m_iType), &cbType)); CheckSqlRc(SQLBindCol(sstmt.Hstmt(), 3, SQL_C_WCHAR, &wchFieldName, kcchMaxColNameSize, &cbFieldName)); CheckSqlRc(SQLBindCol(sstmt.Hstmt(), 4, SQL_C_WCHAR, &wchClassName, kcchMaxColNameSize, &cbClassName)); // Put info from the columns into a FieldMetaDataRec record and insert // this in the FieldMetaData HashMap. for (;;) { rc = SQLFetch(sstmt.Hstmt()); if (rc != SQL_SUCCESS) break; // TOxDO PaulP: Change this so it just gets a null terminated string!!! fmdr.m_suFieldName.Assign(wchFieldName, kcchMaxColNameSize); fmdr.m_suClassName.Assign(wchClassName, kcchMaxColNameSize); fmdm.Insert(nFlid, fmdr); } if (rc != SQL_NO_DATA) { // REVIEW PaulP (SteveMc): Handle possible error message? ThrowHr(WarnHr(E_UNEXPECTED)); } sstmt.Clear(); } catch (...) { sstmt.Clear(); delete rgbFmt; return E_UNEXPECTED; } /*------------------------------------------------------------------------------------------ For every key in the "m_soprMods" ObjPropSet set (ie. all the object properties that have been updated), look up that key in the appropriate HashMap and form an SQL "update" statement according to the field$ "Type". This begins a single database transaction. All the data in the application view cache must either be inserted/updated to the database successfully or the action should fail. This necessitates that the database connection is in manual-commit mode. REVIEW PaulP: It is probably faster to collect all the properties for a given object type and update the table in the database all with one SQL statement but this will do for now until the OLE DB stuff is done. ------------------------------------------------------------------------------------------*/ try { ObjPropSet::iterator itops; for (itops = m_soprMods.Begin(); itops != m_soprMods.End(); ++itops) { // Get the oprKey from the "update object property" Set. ObjPropRec & oprKey = itops.GetValue(); // Get the field$ metadata information based on the tag of that oprKey. fmdm.Retrieve(oprKey.m_tag, &fmdr); // Initialize and form the SQL statment based on the field "type". sstmt.Init(sdb); switch (fmdr.m_iType) { case kcptNil: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptBoolean: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptInteger: // Prepare the SQL statement. (Uses parameters). suSql.Format(L"update %s set %s=? where id=?", fmdr.m_suClassName.Chars(), fmdr.m_suFieldName.Chars()); rc = SQLPrepareW(sstmt.Hstmt(), const_cast<wchar *>(suSql.Chars()), SQL_NTS); // Get the integer value from the appropriate HashMap. if (m_hmoprn.Retrieve(oprKey, &nColVal)) { // Bind integer value as the first parameter. if (nColVal) { rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &nColVal, 0, &cbColVal); } else { // REVIEW JohnT: How do we indicate in the HashMap that the user // wants to set an integer to NULL? cbColVal = SQL_NULL_DATA; rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, NULL, 0, &cbColVal); } // Bind the HVO (Id) as the second parameter. rc = SQLBindParameter(sstmt.Hstmt(), 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &(oprKey.m_hvo), 0, &cbHvo); } else { ThrowHr(WarnHr(E_UNEXPECTED)); } break; case kcptNumeric: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptFloat: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptTime: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptGuid: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptImage: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptGenDate: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptBinary: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptString: case kcptBigString: // Prepare the SQL statement. (Uses parameters.) // REVIEW PaulP: Should I be hard-coding "_Fmt" in like this? suSql.Format(L"update %s set %s=?, %s_Fmt=? where id=?", fmdr.m_suClassName.Chars(), fmdr.m_suFieldName.Chars(), fmdr.m_suFieldName.Chars()); rc = SQLPrepareW(sstmt.Hstmt(), const_cast<wchar *>(suSql.Chars()), SQL_NTS); if (m_hmoprtss.Retrieve(oprKey, qtssColVal)) { // Obtain a SmartBstr from the COM smart pointer that points // to the TsString. CheckHr(qtssColVal->get_Text(&sbstr)); if (sbstr.Length()) { // Copy format information of the TsString to byte array rgbFmt. hr = qtssColVal->SerializeFmtRgb(rgbFmt, cbFmtBufSize, &cbFmtSpaceTaken); if (hr != S_OK) { if (hr == S_FALSE) { // If the supplied buffer is too small, try it again with // the value that cbFmtSpaceTaken was set to. If this // fails, throw error. delete rgbFmt; rgbFmt = NewObj byte[cbFmtSpaceTaken]; cbFmtBufSize = cbFmtSpaceTaken; CheckHr(qtssColVal->SerializeFmtRgb(rgbFmt, cbFmtBufSize, &cbFmtSpaceTaken)); } else { ThrowHr(WarnHr(E_UNEXPECTED)); } } // Bind the text and format parts of the string to the parameters. SQLINTEGER cbTextPart = sbstr.Length() * 2; rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, cbTextPart, 0, const_cast<wchar *>(sbstr.Chars()), cbTextPart, &cbTextPart); SQLINTEGER cbFmtPart = cbFmtSpaceTaken; rc = SQLBindParameter(sstmt.Hstmt(), 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, cbFmtPart, 0, rgbFmt, cbFmtPart, &cbFmtPart); } else { // Since the string had no length, bind NULL to the parameters. SQLINTEGER cbTextPart = SQL_NULL_DATA; rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 1, 0, NULL, 0, &cbTextPart); // REVIEW PaulP: Should we set the Fmt info to NULL for NULL // strings or should we use the Fmt info returned from the // TsString? SQLINTEGER cbFmtPart = SQL_NULL_DATA; rc = SQLBindParameter(sstmt.Hstmt(), 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, 1, 0, NULL, 0, &cbFmtPart); } } else { ThrowHr(WarnHr(E_UNEXPECTED)); } // Bind the HVO (ie. Id) value. rc = SQLBindParameter(sstmt.Hstmt(), 3, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &(oprKey.m_hvo), 0, &cbHvo); break; case kcptBigUnicode: case kcptUnicode: // Prepare the SQL statement. (Uses parameters.) suSql.Format(L"update %s set %s=? where id=?", fmdr.m_suClassName.Chars(), fmdr.m_suFieldName.Chars()); rc = SQLPrepareW(sstmt.Hstmt(), const_cast<wchar *>(suSql.Chars()), SQL_NTS); if (m_hmoprtss.Retrieve(oprKey, qtssColVal)) { // Obtain a SmartBstr from the COM smart pointer that // points to a TsString. CheckHr(qtssColVal->get_Text(&sbstr)); if (sbstr.Length()) { // Bind the string parameter. SQLINTEGER cbTextPart = sbstr.Length() * 2; rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, cbTextPart, 0, const_cast<wchar *>(sbstr.Chars()), cbTextPart, &cbTextPart); } else { // If the string had no length, bind NULL to the parameter. SQLINTEGER cbTextPart = SQL_NULL_DATA; rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 1, 0, NULL, 0, &cbTextPart); } } else { ThrowHr(WarnHr(E_UNEXPECTED)); } // Bind the HVO (ie. Id) value. rc = SQLBindParameter(sstmt.Hstmt(), 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &(oprKey.m_hvo), 0, &cbHvo); break; case kcptOwningAtom: // Update owning field. // Update Owner$ object of CmObject. ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptReferenceAtom: // This is basically the same as the Integer case except we obtain the // value from the m_hmoprobj HashMap rather than the m_hmoprn HashMap. ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptOwningCollection: ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptReferenceCollection: // Affect the joiner table. ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptOwningSequence: // Need to update. ThrowHr(WarnHr(E_NOTIMPL)); break; case kcptReferenceSequence: // Affect the joiner table. ThrowHr(WarnHr(E_NOTIMPL)); break; default: ThrowHr(WarnHr(E_UNEXPECTED)); break; } // Execute the SQL update command. //rc = SQLSetStmtAttr(sstmt.Hstmt(), SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0); rc = SQLExecute(sstmt.Hstmt()); if (rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO) { // TOxDO PaulP: Error information can be obtained from sqlchMsgTxt. sqlr = SQLGetDiagRec(SQL_HANDLE_STMT, sstmt.Hstmt(), 1, sqlchSqlState, &sqlnNativeErrPtr, sqlchMsgTxt, kcchErrMsgBuf, &sqlnTextLength); ThrowHr(WarnHr(E_UNEXPECTED)); } sstmt.Clear(); } /*-------------------------------------------------------------------------------------- Go through the m_soperMods ObjPropSet set and update columns in the MultiX tables. --------------------------------------------------------------------------------------*/ ObjPropEncSet::iterator itopes; for (itopes = m_soperMods.Begin(); itopes != m_soperMods.End(); ++itopes) { // Get the operKey from the "update MSA" Set. ObjPropEncRec & operKey = itopes.GetValue(); // Get the field$ metadata information based on the tag of that operKey. fmdm.Retrieve(operKey.m_tag, &fmdr); // Initialize and form the SQL statment. sstmt.Init(sdb); if (m_hmopertss.Retrieve(operKey, qtssColVal)) { // Obtain a SmartBstr from the COM smart pointer that points to the TsString. CheckHr(qtssColVal->get_Text(&sbstr)); if (sbstr.Length()) { // Get the format of the TsString hr = qtssColVal->SerializeFmtRgb(rgbFmt, cbFmtBufSize, &cbFmtSpaceTaken); if (hr != S_OK) { if (hr == S_FALSE) { // If the supplied buffer is too small, try it again with the value // that cbFmtSpaceTaken was set to. If this fails, signal error. delete rgbFmt; rgbFmt = NewObj byte[cbFmtSpaceTaken]; cbFmtBufSize = cbFmtSpaceTaken; CheckHr(qtssColVal->SerializeFmtRgb(rgbFmt, cbFmtBufSize, &cbFmtSpaceTaken)); } else { ThrowHr(WarnHr(E_UNEXPECTED)); } } // Execute stored procedure to set MSA value. // REVIEW PaulP: This technique isn't going to work for custom fields // since there will likely not be any "Set" stored procedure. suSql.Format(L"exec Set_%s_%s %d, %d, ?, ?", fmdr.m_suClassName.Chars(), fmdr.m_suFieldName.Chars(), operKey.m_hvo, operKey.m_ws); rc = SQLPrepareW(sstmt.Hstmt(), const_cast<wchar *>(suSql.Chars()), SQL_NTS); // Bind the text and format parts of the string to the parameters. SQLINTEGER cbTextPart = sbstr.Length() * 2; rc = SQLBindParameter(sstmt.Hstmt(), 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, cbTextPart, 0, const_cast<wchar *>(sbstr.Chars()), cbTextPart, &cbTextPart); SQLINTEGER cbFmtPart = cbFmtSpaceTaken; rc = SQLBindParameter(sstmt.Hstmt(), 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, cbFmtPart, 0, rgbFmt, cbFmtPart, &cbFmtPart); } else { // REVIEW PaulP: This technique isn't going to work for custom fields // since there will likely not be any "Set" stored procedure. suSql.Format(L"exec Set_%s_%s %d, %d, NULL, NULL", fmdr.m_suClassName.Chars(), fmdr.m_suFieldName.Chars(), operKey.m_hvo, operKey.m_ws); rc = SQLPrepareW(sstmt.Hstmt(), const_cast<wchar *>(suSql.Chars()), SQL_NTS); } // Execute the SQL update command. //rc = SQLSetStmtAttr(sstmt.Hstmt(), SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0); rc = SQLExecute(sstmt.Hstmt()); if (rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO) { // TOxDO PaulP: Error information can be obtained from sqlchMsgTxt. sqlr = SQLGetDiagRec(SQL_HANDLE_STMT, sstmt.Hstmt(), 1, sqlchSqlState, &sqlnNativeErrPtr, sqlchMsgTxt, kcchErrMsgBuf, &sqlnTextLength); ThrowHr(WarnHr(E_UNEXPECTED)); } sstmt.Clear(); } else { ThrowHr(WarnHr(E_UNEXPECTED)); } } /*-------------------------------------------------------------------------------------- Commit the database transaction. REVIEW PaulP: The DataSource MUST be in manual commit mode. --------------------------------------------------------------------------------------*/ CheckSqlRc(SQLEndTran(SQL_HANDLE_DBC, sdb.Hdbc(), SQL_COMMIT)); } catch (...) { sstmt.Clear(); delete rgbFmt; CheckSqlRc(SQLEndTran(SQL_HANDLE_DBC, sdb.Hdbc(), SQL_ROLLBACK)); return E_UNEXPECTED; } /*------------------------------------------------------------------------------------------ Clear the two Sets containing records of modified properties and MBA's. ------------------------------------------------------------------------------------------*/ m_soprMods.Clear(); m_soperMods.Clear(); delete rgbFmt; // REVIEW: Shouldn't this be delete[]? return S_OK; END_COM_METHOD(g_fact, IID_IVwCacheDa); }
/*---------------------------------------------------------------------------------------------- This processes Windows messages on the window. In general, it normally calls the appropriate method on the edit class. ----------------------------------------------------------------------------------------------*/ bool TssEdit::FWndProc(uint wm, WPARAM wp, LPARAM lp, long & lnRet) { bool fRet; switch (wm) { case WM_GETDLGCODE: // This is essential when embedded in a dialog to tell the dialog manager that it // wants to get key strokes. (We could try DLGC_WANTALLKEYS but I think we would then // get the Tab and Return keys...we may get them anyway with this combination...) // The last value tells Windows that when tabbing to this control we should use // EM_SETSEL to select all the text. lnRet = DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_HASSETSEL; return true; case EM_GETLINE: // Use FW_EM_GETLINE. case EM_REPLACESEL: // Use FW_EM_REPLACESEL. // We don't support these methods. Use the replacement TsString versions instead. Assert(false); lnRet = LB_ERR; return true; // NOTE: DO NOT send this message to a TssEdit if you want the actual text. Send the // FW_EM_GETTEXT message instead. This method is required for TssEdit controls on a // dialog because Windows will send the message to the control anytime the user hits a // key. case WM_GETTEXT: { ITsStringPtr qtss; GetText(&qtss); const wchar * pwrgch; int cch; HRESULT hr; IgnoreHr(hr = qtss->LockText(&pwrgch, &cch)); if (FAILED(hr)) return true; StrApp str(pwrgch, cch); qtss->UnlockText(pwrgch); lnRet = Min(cch + 1, (int)wp); achar * psz = reinterpret_cast<achar *>(lp); StrCpyN(psz, str.Chars(), lnRet); } return true; // NOTE: You should be sending an FW_EM_SETTEXT message instead of this. case WM_SETTEXT: { achar * psz = reinterpret_cast<achar *>(lp); StrUni stu(psz); ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); ITsStringPtr qtss; CheckHr(qtsf->MakeStringRgch(stu.Chars(), stu.Length(), m_wsBase, &qtss)); SetText(qtss); } return true; case EM_CANUNDO: case EM_CHARFROMPOS: case EM_EMPTYUNDOBUFFER: case EM_FMTLINES: case EM_GETFIRSTVISIBLELINE: case EM_GETHANDLE: case EM_GETMODIFY: case EM_GETPASSWORDCHAR: case EM_GETRECT: case EM_GETTHUMB: case EM_GETWORDBREAKPROC: case EM_POSFROMCHAR: case EM_SETHANDLE: case EM_SETMODIFY: case EM_SETPASSWORDCHAR: case EM_SETRECT: case EM_SETRECTNP: case EM_SETTABSTOPS: case EM_SETWORDBREAKPROC: case EM_UNDO: case WM_GETFONT: case WM_SETFONT: // We don't support these methods. Assert(false); lnRet = LB_ERR; return true; case EM_GETLIMITTEXT: lnRet = GetLimitText(); return true; case FW_EM_GETLINE: lnRet = GetLine(wp, (ITsString **)lp); return true; case EM_GETLINECOUNT: lnRet = GetLineCount(); return true; case EM_GETMARGINS: lnRet = GetMargins(); return true; case FW_EM_GETSTYLE: GetStyle((StrUni *)lp, (COLORREF *)wp); return true; case EM_GETSEL: lnRet = GetSel((int *)wp, (int *)lp); return true; case EM_LINEFROMCHAR: lnRet = LineFromChar(wp); return true; case EM_LINEINDEX: lnRet = LineIndex(wp); return true; case EM_LINELENGTH: lnRet = LineLength(wp); return true; case EM_LINESCROLL: LineScroll(lp, wp); return true; case FW_EM_REPLACESEL: ReplaceSel((ITsString *)lp); return true; case EM_SCROLL: lnRet = ::SendMessage(m_hwnd, WM_VSCROLL, LOWORD(wp), 0); return true; case EM_SCROLLCARET: ScrollCaret(); return true; case EM_SETLIMITTEXT: SetLimitText(wp); return true; case EM_SETMARGINS: SetMargins(wp, LOWORD(lp), HIWORD(lp)); return true; case EM_SETREADONLY: SetReadOnly(wp); return true; case EM_SETSEL: SetSel(wp, lp); return true; case FW_EM_SETSTYLE: SetStyle((StrUni *)lp, (COLORREF)wp); return true; case WM_GETTEXTLENGTH: lnRet = GetTextLength(); return true; case FW_EM_GETTEXT: GetText((ITsString **)lp); return true; case FW_EM_SETTEXT: SetText((ITsString *)lp); return true; case WM_COPY: Copy(); return true; case WM_CUT: Cut(); return true; case WM_PASTE: Paste(); return true; case WM_HSCROLL: if (!OnHScroll(LOWORD(wp), HIWORD(wp), (HWND)lp)) { ::SendMessage(::GetParent(m_hwnd), WM_COMMAND, MAKEWPARAM(::GetDlgCtrlID(m_hwnd), EN_HSCROLL), (LPARAM)m_hwnd); } return true; case WM_VSCROLL: if (!OnVScroll(LOWORD(wp), HIWORD(wp), (HWND)lp)) { ::SendMessage(::GetParent(m_hwnd), WM_COMMAND, MAKEWPARAM(::GetDlgCtrlID(m_hwnd), EN_VSCROLL), (LPARAM)m_hwnd); } return true; case WM_KILLFOCUS: if (!OnKillFocus((HWND)wp)) { ::SendMessage(::GetParent(m_hwnd), WM_COMMAND, MAKEWPARAM(::GetDlgCtrlID(m_hwnd), EN_KILLFOCUS), (LPARAM)m_hwnd); } return true; case WM_SETFOCUS: if (!OnSetFocus((HWND)wp)) { ::SendMessage(::GetParent(m_hwnd), WM_COMMAND, MAKEWPARAM(::GetDlgCtrlID(m_hwnd), EN_SETFOCUS), (LPARAM)m_hwnd); } return true; // Calling SuperClass here causes two OnSetFocus calls for each OnKillFocus. //return SuperClass::FWndProc(wm, wp, lp, lnRet); case WM_CHAR: fRet = false; if (wp == VK_TAB) // '\t' { fRet = OnCharTab(); } else if (wp == VK_RETURN) // '\r' { fRet = OnCharEnter(); } else if (wp == VK_ESCAPE) // '\33' { fRet = OnCharEscape(); } if (fRet) return fRet; else return SuperClass::FWndProc(wm, wp, lp, lnRet); case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MOUSEMOVE: if (HasToolTip()) { // Notify the tooltip belonging to the parent toolbar of the mouse message. Assert(m_hwndToolTip); MSG msg; msg.hwnd = m_hwnd; // ::GetParent(m_hwnd); msg.message = wm; msg.wParam = wp; msg.lParam = lp; ::SendMessage(m_hwndToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg); } break; default: break; } return SuperClass::FWndProc(wm, wp, lp, lnRet); }
/*---------------------------------------------------------------------------------------------- Load the data needed to display this view. In this case, we need to load the class, owner (so we can tell whether it is a subitem), the title, and create date. If all of these are already in the cache, don't reload it. @param pvwenv Pointer to the view environment. @param hvo The id of the object we are displaying. @param frag Identifies the part of the view we are currently displaying. @return HRESULT indicating success (S_OK), or failure (E_FAIL). ----------------------------------------------------------------------------------------------*/ STDMETHODIMP CleRecVc::LoadDataFor(IVwEnv * pvwenv, HVO hvo, int frag) { BEGIN_COM_METHOD; ChkComArgPtr(pvwenv); Assert(false); // TODO: rework StrUni stuSql; ISilDataAccessPtr qsda; CheckHr(pvwenv->get_DataAccess(&qsda)); bool fLoaded = false; int clid; CheckHr(qsda->get_IntProp(hvo, kflidCmObject_Class, &clid)); if (clid) { HVO hvoOwn; CheckHr(qsda->get_ObjectProp(hvo, kflidCmObject_Owner, &hvoOwn)); if (hvoOwn) { int64 tim; // REVIEW KenZ(RandyR) Whey are DN flids in this app? CheckHr(qsda->get_TimeProp(hvo, kflidRnGenericRec_DateCreated, &tim)); if (tim) { ITsStringPtr qtss; CheckHr(qsda->get_StringProp(hvo, kflidRnGenericRec_Title, &qtss)); if (qtss) { int cch; CheckHr(qtss->get_Length(&cch)); if (cch) fLoaded = true; } } } } if (!fLoaded) { // If any field is missing from the cache, load everything. IDbColSpecPtr qdcs; IVwOleDbDaPtr qda; CheckHr(qsda->QueryInterface(IID_IVwOleDbDa, (void**)&qda)); stuSql.Format(L"select id, Class$, Owner$, DateCreated, Title, Title_Fmt " L"from RnGenericRec_ " L"where id = %d", hvo); qdcs.CreateInstance(CLSID_DbColSpec); CheckHr(qdcs->Push(koctBaseId, 0, 0, 0)); CheckHr(qdcs->Push(koctInt, 1, kflidCmObject_Class, 0)); CheckHr(qdcs->Push(koctObj, 1, kflidCmObject_Owner, 0)); // REVIEW KenZ(RandyR) Whey are DN flids in this app? CheckHr(qdcs->Push(koctTime, 1, kflidRnGenericRec_DateCreated, 0)); CheckHr(qdcs->Push(koctString, 1, kflidRnGenericRec_Title, 0)); CheckHr(qdcs->Push(koctFmt, 1, kflidRnGenericRec_Title, 0)); AfMainWnd * pafw = AfApp::Papp()->GetCurMainWnd(); AssertPtr(pafw); AfStatusBar * pstbr = pafw->GetStatusBarWnd(); AssertPtr(pstbr); bool fProgBar = pstbr->IsProgressBarActive(); if (!fProgBar) { StrApp strMsg(kstidStBar_LoadingData); pstbr->StartProgressBar(strMsg.Chars(), 0, 70, 1); } // Execute the query and store results in the cache. CheckHr(qda->Load(stuSql.Bstr(), qdcs, hvo, 0, pstbr, NULL)); if (!fProgBar) pstbr->EndProgressBar(); } return S_OK; END_COM_METHOD(g_fact2, IID_IVwViewConstructor) }
/*---------------------------------------------------------------------------------------------- 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; }
/*---------------------------------------------------------------------------------------------- Close the current editor, saving changes that were made. This also updates the the cache if the name or abbr fields were edited, this then causing the treebar to be updated. @param fForce True if we want to force the editor closed without making any validity checks or saving any changes. ----------------------------------------------------------------------------------------------*/ void CleDeFeString::EndEdit(bool fForce) { if (fForce) { SuperClass::EndEdit(fForce); return; } CleMainWnd * pcmw = dynamic_cast<CleMainWnd *>(MainWindow()); Assert(pcmw); PossListInfoPtr qpli = pcmw->GetPossListInfoPtr(); int ipss = qpli->GetIndexFromId(m_hvoObj); if (ipss < 0) { SuperClass::EndEdit(fForce); // Closing an old editor after an item was removed. return; } PossItemInfo * ppii = qpli->GetPssFromIndex(ipss); AssertPtr(ppii); StrUni stu; const OLECHAR * prgwch; int cch; ITsStringPtr qtss; AssertPtr(m_qadsc->MainWindow()); AfLpInfo * plpi = m_qadsc->MainWindow()->GetLpInfo(); AssertPtr(plpi); AfDbInfo * pdbi; pdbi = plpi->GetDbInfo(); AssertPtr(pdbi); int wsMagic = qpli->GetWs(); int ws = plpi->ActualWs(wsMagic); // At this point, all strings in a PossList cache are assumed to have the same writing system. // However, if that writing system is missing, a non-null string from a different writing system is // loaded. Thus if FRN is the writing system for the PossList cache, we can't tell from the // cache whether the string we are getting is FRN or ENG, or some other writing system. When // we display strings in this editor, we show actual strings for each writing system shown. // Suppose we are showing FRN and ENG, but FRN is missing. If a person edits the ENG // string that is showing, the user would expect the tree to reflect this change, since // FRN is still missing. However we can't do this at this point because we don't know // which writing system is actually being substituted in the tree. It could actually be a GER // string because there also wasn't an ENG string. So until we cache encodings with each // string, the best we can do is only modify the PossList cache if the string for the // primary writing system changed. In that case, we now have a FRN string, so it should show // in the tree as well. CustViewDaPtr qcvd = pcmw->MainDa(); AssertPtr(qcvd); // Get the current primary string. CheckHr(qcvd->get_MultiStringAlt(m_hvoObj, m_flid, ws, &qtss)); if (qtss) { qtss->LockText(&prgwch, &cch); qtss->UnlockText(prgwch); } // Trim leading and trailing space characters. UnicodeString ust(prgwch, cch); ust.trim(); stu.Assign(ust.getBuffer(), ust.length()); // We don't expect to use this for anything except name or abbr. Assert(m_flid == kflidCmPossibility_Name || m_flid == kflidCmPossibility_Abbreviation); // We don't allow long strings. if (stu.Length() > kcchPossNameAbbrMax) // Need constant here and the line below. { ::MessageBeep(MB_ICONEXCLAMATION); // Beep if we truncated the length. stu.Replace(kcchPossNameAbbrMax, stu.Length(), L""); } ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); qtsf->MakeStringRgch(stu.Chars(), stu.Length(), ws, &qtss); // If we changed the length, store the trimmed string. if (cch != stu.Length()) { // Check if the record has been edited by someone else since we first loaded the data. HRESULT hrTemp; if ((hrTemp = qcvd->CheckTimeStamp(m_hvoObj)) != 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 its original value. CheckHr(qcvd->PropChanged(NULL, kpctNotifyAll, m_hvoObj, m_flid, 0, 1, 1)); return; } // Update the value in the cache and refresh views. CheckHr(qcvd->SetMultiStringAlt(m_hvoObj, m_flid, ws, qtss)); CheckHr(qcvd->PropChanged(NULL, kpctNotifyAll, m_hvoObj, m_flid, 0, 1, 1)); } SuperClass::EndEdit(fForce); // See if the primary string has changed. ComBool fEqual; CheckHr(qtss->Equals(m_qtssOld, &fEqual)); if (fEqual) return; // No change was made, so we don't need to do anything else. // A change was made. if (m_flid != kflidCmPossibility_Name && m_flid != kflidCmPossibility_Abbreviation) return; // Nothing more to do if it isn't a name or abbreviation. // We've changed the primary name or abbreviation, so update the PossList cache. ppii->SetName(stu, m_flid == kflidCmPossibility_Name ? kpntName : kpntAbbreviation); if (qpli->GetIsSorted()) { // The list is sorted, so we need to move the item to its new location. if (qpli->PutInSortedPosition(m_hvoObj, false)) { int ihvo; pcmw->LoadData(); HvoClsidVec & vhc = pcmw->Records(); for (ihvo = vhc.Size(); --ihvo >= 0; ) { if (vhc[ihvo].hvo == m_hvoObj) break; } Assert(ihvo >= 0); // Should have found item. pcmw->SetCurRecIndex(ihvo); } } // We can't do a sync in this method that opens new editors, so just flag // that it needs to be done. // We need to sync if either name or abbreviation has changed. Even though we may only // be showing one or the other in our lists, there may be overlays or something else // showing the other, so to be safe, we need to do a sync if either changed. dynamic_cast<CleDeSplitChild *>(m_qadsc.Ptr())->SetNeedSync(true); }
/*---------------------------------------------------------------------------------------------- Check to see if the edit box has valid data. if so return true. If not then put up a message to the user, then return false. ----------------------------------------------------------------------------------------------*/ bool CleDeFeString::IsOkToClose(bool fWarn) { CleMainWnd * pcmw = dynamic_cast<CleMainWnd *>(m_qadsc->MainWindow()); Assert(pcmw); IVwSelectionPtr qvwsel; CheckHr(m_qrootb->get_Selection(&qvwsel)); if (qvwsel) { ComBool fOk; CheckHr(qvwsel->Commit(&fOk)); } PossListInfoPtr qpli = pcmw->GetPossListInfoPtr(); int ipss = qpli->GetIndexFromId(m_hvoObj); StrUni stuNew; const OLECHAR * prgwch; int cch; ITsStringPtr qtss; CustViewDaPtr qcvd; GetDataAccess(&qcvd); AssertPtr(qcvd); int ws = m_qsvc->WritingSystems()[0]; CheckHr(qcvd->get_MultiStringAlt(m_hvoObj, m_flid, ws, &qtss)); Assert(qtss); qtss->LockText(&prgwch, &cch); qtss->UnlockText(prgwch); // Trim leading and trailing space characters. UnicodeString ust(prgwch, cch); ust.trim(); stuNew.Assign(ust.getBuffer(), ust.length()); // Obtain pointer to IOleDbEncap interface. IOleDbEncapPtr qode; IOleDbCommandPtr qodc; StrUni stuSql; ComBool fIsNull; ComBool fMoreRows; AssertPtr(m_qadsc->MainWindow()); AfLpInfo * plpi = m_qadsc->MainWindow()->GetLpInfo(); AssertPtr(plpi); AfDbInfo * pdbi = plpi->GetDbInfo(); AssertPtr(pdbi); pdbi->GetDbAccess(&qode); AssertPtr(qode); CheckHr(qode->CreateCommand(&qodc)); int cpii = qpli->GetCount(); if ((m_flid == kflidCmPossibility_Name) || (m_flid == kflidCmPossibility_Abbreviation)) { // Make sure it does not have a ":" or a " - " in the string int ich = stuNew.FindStr(L":"); StrUni stuTmp; bool fFixed = false; while (ich > 0) { stuNew.Replace(ich,ich + 1,"-"); fFixed = true; ich = stuNew.FindStr(L":"); } ich = stuNew.FindStr(L" - "); while (ich > 0) { stuNew.Replace(ich,ich + 3,"-"); fFixed = true; ich = stuNew.FindStr(L" - "); } if (fFixed) { if (fWarn) { ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); qtsf->MakeStringRgch(stuNew.Chars(), stuNew.Length(), pcmw->UserWs(), &qtss); CheckHr(qcvd->SetMultiStringAlt(m_hvoObj, m_flid, ws, qtss)); CheckHr(qcvd->PropChanged(NULL, kpctNotifyAll, m_hvoObj, m_flid, 0, 1, 1)); StrApp strMsg(kstidFixedStr); StrApp strTitle(kstidFixedStrTitle); ::MessageBox(m_hwnd, strMsg.Chars(), strTitle.Chars(), MB_OK | MB_ICONINFORMATION); } return false; } } if (qpli->GetAllowDup()) return true; ILgWritingSystemFactoryPtr qwsf; pdbi->GetLgWritingSystemFactory(&qwsf); AssertPtr(qwsf); switch (m_flid) { case kflidCmPossibility_Name: { for (int ipii = 0; ipii < cpii; ipii++) { if (ipii == ipss) continue; PossItemInfo * ppii = qpli->GetPssFromIndex(ipii); AssertPtr(ppii); StrUni stu; ppii->GetName(stu, kpntName); if (stu == stuNew) { stuSql.Format(L"select ws from CmPossibility_Name " L"where obj = %d and ws = %d", ppii->GetPssId(), ws); CheckHr(qode->CreateCommand(&qodc)); CheckHr(qodc->ExecCommand(stuSql.Bstr(), knSqlStmtSelectWithOneRowset)); CheckHr(qodc->GetRowset(0)); CheckHr(qodc->NextRow(&fMoreRows)); if (fMoreRows) { if (fWarn) { // this name already exists IWritingSystemPtr qws; CheckHr(qwsf->get_EngineOrNull(ws, &qws)); AssertPtr(qws); SmartBstr sbstr; qws->get_Name(ws, &sbstr); StrUni stu(kstidDupItemName); StrUni stuMsg; stuMsg.Format(stu,sbstr.Chars()); StrApp str(stuMsg); StrApp strTitle(kstidDupItemTitle); ::MessageBox(m_hwnd, str.Chars(), strTitle.Chars(), MB_OK | MB_ICONINFORMATION); } return false; } } } break; } case kflidCmPossibility_Abbreviation: { for (int ipii = 0; ipii < cpii; ipii++) { if (ipii == ipss) continue; PossItemInfo * ppii = qpli->GetPssFromIndex(ipii); AssertPtr(ppii); StrUni stu; ppii->GetName(stu, kpntAbbreviation); if (stu == stuNew) { stuSql.Format(L"select ws from CmPossibility_Abbreviation " L"where obj = %d and ws = %d", ppii->GetPssId(), ws); CheckHr(qode->CreateCommand(&qodc)); CheckHr(qodc->ExecCommand(stuSql.Bstr(), knSqlStmtSelectWithOneRowset)); CheckHr(qodc->GetRowset(0)); CheckHr(qodc->NextRow(&fMoreRows)); if (fMoreRows) { if (fWarn) { // this abbreviation already exists IWritingSystemPtr qws; CheckHr(qwsf->get_EngineOrNull(ws, &qws)); AssertPtr(qws); SmartBstr sbstr; qws->get_Name(ws, &sbstr); StrUni stu(kstidDupItemAbbr); StrUni stuMsg; stuMsg.Format(stu,sbstr.Chars()); StrApp str(stuMsg); StrApp strTitle(kstidDupItemTitle); ::MessageBox(m_hwnd, str.Chars(), strTitle.Chars(), MB_OK | MB_ICONINFORMATION); } return false; } } } break; } } return true; }
/*---------------------------------------------------------------------------------------------- Add a field editor for a given field at the location and indent specified. @param hvoRoot Id of the root object that holds the fields we want to display. @param clid Class of the root object. @param nLev Level (main/sub) of the root object in the window. @param pfsp The FldSpec that defines this field. @param pcvd Pointer to the CustViewDa specifying what fields to display. @param idfe Index where the new fields are to be inserted. On return it contains an index to the field following any inserted fields. @param nInd Indent of the fields to be added. @param fAlwaysVisible If true, show field regardless of pbsp->m_eVisibility ----------------------------------------------------------------------------------------------*/ void CleDeSplitChild::AddField(HVO hvoRoot, int clid, int nLev, FldSpec * pfsp, CustViewDa * pcvd, int & idfe, int nInd, bool fAlwaysVisible) { AssertPtr(pcvd); AssertPtr(pfsp); bool fCheckMissingData = fAlwaysVisible ? false : pfsp->m_eVisibility == kFTVisIfData; // If the user never wants to see this field, skip it. if (pfsp->m_eVisibility == kFTVisNever) return; ITsStringPtr qtss; AfLpInfo * plpi = pcvd->GetLpInfo(); AssertPtr(plpi); AfStatusBarPtr qstbr = MainWindow()->GetStatusBarWnd(); Assert(qstbr); switch(pfsp->m_ft) { case kftEnum: { AfDeFeComboBox * pdecb = NewObj AfDeFeComboBox(); ITsStrFactoryPtr qtsf; qtsf.CreateInstance(CLSID_TsStrFactory); int itss; CheckHr(pcvd->get_IntProp(hvoRoot, pfsp->m_flid, &itss)); pdecb->Initialize(hvoRoot, pfsp->m_flid, nInd, pfsp->m_qtssLabel, pfsp->m_qtssHelp, this, pfsp); pdecb->Init(pfsp->m_pnt); ComVector<ITsString> * pvtss; pvtss = pdecb->GetVec(); int stid; switch (pfsp->m_flid) { case kflidCmPerson_Gender: stid = kstidEnumGender; break; #ifdef ADD_LEXTEXT_LISTS case kflidCmAnnotationDefn_AllowsComment: // Fall through. case kflidCmAnnotationDefn_AllowsFeatureStructure: // Fall through. case kflidCmAnnotationDefn_AllowsInstanceOf: // Fall through. case kflidCmAnnotationDefn_UserCanCreate: // Fall through. case kflidCmAnnotationDefn_CanCreateOrphan: // Fall through. case kflidCmAnnotationDefn_PromptUser: // Fall through. case kflidCmAnnotationDefn_CopyCutPastable: // Fall through. case kflidCmAnnotationDefn_ZeroWidth: // Fall through. case kflidCmAnnotationDefn_Multi: // Fall through. #endif case kflidMoInflAffixSlot_Optional: // Fall through. case kflidCmPerson_IsResearcher: stid = kstidEnumNoYes; if (itss) itss = 1; break; default: Assert(false); // A list must be provided above. break; } StrUni stuEnum(stid); const wchar * pszEnum = stuEnum.Chars(); const wchar * pszEnumLim = stuEnum.Chars() + stuEnum.Length(); while (pszEnum < pszEnumLim) { const wchar * pszEnumNl = wcschr(pszEnum, '\n'); if (!pszEnumNl) pszEnumNl = pszEnumLim; AfDbInfo * pdbi = plpi->GetDbInfo(); AssertPtr(pdbi); int wsUser = pdbi->UserWs(); qtsf->MakeStringRgch(pszEnum, pszEnumNl - pszEnum, wsUser, &qtss); pvtss->Push(qtss); pszEnum = pszEnumNl + 1; } pdecb->SetIndex(itss); m_vdfe.Insert(idfe++, pdecb); } return; case kftMsa: // Fall through. case kftMta: { Vector<int> vwsT; Vector<int> & vws = vwsT; // We need a special editor for name and address to catch changes to update the // tree views. if ((pfsp->m_flid == kflidCmPossibility_Name) || (pfsp->m_flid == kflidCmPossibility_Abbreviation)) { CleMainWnd * pcmw = dynamic_cast<CleMainWnd *>(MainWindow()); Assert(pcmw); // PossListInfoPtr qpli = pcmw->GetPossListInfoPtr(); int wsPL= pcmw->GetWsPssl(); switch (wsPL) { case kwsAnals: vws = plpi->AnalWss(); break; case kwsVerns: vws = plpi->VernWss(); break; case kwsAnal: vwsT.Push(plpi->AnalWs()); break; case kwsVern: vwsT.Push(plpi->VernWs()); break; case kwsAnalVerns: vws = plpi->AnalVernWss(); break; case kwsVernAnals: vws = plpi->VernAnalWss(); break; default: vwsT.Push(wsPL); break; } if (fCheckMissingData) { ITsStringPtr qtss; int iws; for (iws = vws.Size(); --iws >= 0; ) { CheckHr(pcvd->get_MultiStringAlt(hvoRoot, pfsp->m_flid, vws[iws], &qtss)); int cch; CheckHr(qtss->get_Length(&cch)); if (cch) break; } if (iws < 0) return; // All writing systems are empty. } // An extra ref cnt is created here which is eventually assigned to the vector. CleDeFeStringPtr qdfs = NewObj CleDeFeString; qdfs->Initialize(hvoRoot, pfsp->m_flid, nInd, pfsp->m_qtssLabel, pfsp->m_qtssHelp, this, pfsp); if (pfsp->m_ft == kftMta) qdfs->Init(&vws, ktptSemiEditable); else qdfs->Init(&vws, ktptIsEditable); m_vdfe.Insert(idfe++, qdfs); } else { switch (pfsp->m_ws) { case kwsAnals: vws = plpi->AnalWss(); break; case kwsVerns: vws = plpi->VernWss(); break; case kwsAnal: vwsT.Push(plpi->AnalWs()); break; case kwsVern: vwsT.Push(plpi->VernWs()); break; case kwsAnalVerns: vwsT = plpi->AnalVernWss(); break; case kwsVernAnals: vwsT = plpi->VernAnalWss(); break; default: vwsT.Push(pfsp->m_ws); Assert(pfsp->m_ws); break; } if (fCheckMissingData) { ITsStringPtr qtss; int iws; for (iws = vws.Size(); --iws >= 0; ) { CheckHr(pcvd->get_MultiStringAlt(hvoRoot, pfsp->m_flid, vws[iws], &qtss)); int cch; CheckHr(qtss->get_Length(&cch)); if (cch) break; } if (iws < 0) return; // All writing systems are empty. } // An extra ref cnt is created here which is eventually assigned to the vector. AfDeFeStringPtr qdfs = NewObj AfDeFeString; qdfs->Initialize(hvoRoot, pfsp->m_flid, nInd, pfsp->m_qtssLabel, pfsp->m_qtssHelp, this, pfsp); if (pfsp->m_ft == kftMta) qdfs->Init(&vws, ktptSemiEditable); else qdfs->Init(&vws, ktptIsEditable); m_vdfe.Insert(idfe++, qdfs); } } return; case kftExpandable: // Fall through. case kftSubItems: return; case kftObjRefAtomic: // Fall through. case kftObjRefSeq: { bool fMultiRefs; if (pfsp->m_ft == kftObjRefAtomic) { HVO hvoRef; CheckHr(pcvd->get_ObjectProp(hvoRoot, pfsp->m_flid, &hvoRef)); fMultiRefs = false; if (fCheckMissingData && !hvoRef) return; } else { fMultiRefs = true; if (fCheckMissingData) // otherwise no need to check { int chvo; CheckHr(pcvd->get_VecSize(hvoRoot, pfsp->m_flid, &chvo)); if (!chvo) return; } } AfDeFeRefs * pdfr = NewObj AfDeFeRefs; pdfr->Initialize(hvoRoot, pfsp->m_flid, nInd, pfsp->m_qtssLabel, pfsp->m_qtssHelp, this, pfsp); switch (pfsp->m_flid) { default: // Standard kftObjRefAtomic & kftObjRefSeq fields are all handled in the superclass. SuperClass::AddField(hvoRoot, clid, nLev, pfsp, pcvd, idfe, nInd, fAlwaysVisible); return; } m_vdfe.Insert(idfe++, pdfr); } return; } qstbr->StepProgressBar(10); // Standard fields are all handled in the superclass. SuperClass::AddField(hvoRoot, clid, nLev, pfsp, pcvd, idfe, nInd, fAlwaysVisible); return; }