/* * CRchTxtPtr::ChangeCase(cch, Type, publdr) * * @mfunc * Change case of cch chars starting at this text ptr according to Type, * which has the possible values: * * tomSentenceCase = 0: capitalize first letter of each sentence * tomLowerCase = 1: change all letters to lower case * tomUpperCase = 2: change all letters to upper case * tomTitleCase = 3: capitalize the first letter of each word * tomToggleCase = 4: toggle the case of each letter * * @rdesc * TRUE iff a change occurred * * @devnote * Since this routine only changes the case of characters, it has no * effect on rich-text formatting. However it is part of the CRchTxtPtr * class in order to notify the display of changes. CTxtRanges are also * notified just in case the text blocks are modified. */ BOOL CRchTxtPtr::ChangeCase ( LONG cch, //@parm # chars to change case for LONG Type, //@parm Type of change case command IUndoBuilder *publdr) //@parm UndoBuilder to receive anti-event // for any replacements { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ChangeCase"); _TEST_INVARIANT_ #define BUFFERLEN 256 LONG cchChunk, cchFirst, cchGet, cchLast; BOOL fAlpha, fToUpper, fUpper; // Flags controling case change BOOL fChange = FALSE; // No change yet BOOL fStart = TRUE; // Start of Word/Sentence TCHAR * pch; // Ptr to walk rgCh with WORD * pType; // Ptr to walk rgType with TCHAR rgCh[BUFFERLEN]; // Char buffer to work in WORD rgType[BUFFERLEN]; // C1_TYPE array for rgCh if( GetCp() ) { if( Type == tomSentenceCase ) { fStart = _rpTX.IsAtBOSentence(); } else if( Type == tomTitleCase ) { // check to see if we are at the beginning of // a word. This is the case if the character preceeding // our current position is white space. fStart = IsWhiteSpace(_rpTX.PrevChar()); _rpTX.AdvanceCp(1); } } while(cch > 0) // Do 'em all (or as many as { // in story) cchChunk = min(BUFFERLEN, cch); // Get next bufferful cch -= cchChunk; // Decrement the count cchGet = _rpTX.GetText(cchChunk, rgCh); // Manipulate chars in buffer if(cchGet < cchChunk) // (for undo, need to use { // ReplaceRange()) cch = 0; // No more chars in story, if(!cchGet) // so we'll be done break; // We're done already cchChunk = cchGet; // Something in this chunk } GetStringTypeEx(0, CT_CTYPE1, rgCh, // Find out whether chars are cchChunk, rgType); // UC, LC, or neither cchLast = 0; // Default nothing to replace cchFirst = -1; for(pch = rgCh, pType = rgType; // Process buffered chars cchChunk; cchChunk--, pch++, pType++) { fAlpha = *pType & (C1_UPPER | C1_LOWER); // Nonzero if UC or LC fUpper = (*pType & C1_UPPER) != 0; // TRUE if UC fToUpper = fStart ? TRUE : fUpper; // capitalize first letter of a // sentence switch(Type) { // Decide whether to change case tomLowerCase: // case and determine start fToUpper = FALSE; // of word/sentence for title break; // and sentence cases case tomUpperCase: fToUpper = TRUE; break; case tomToggleCase: fToUpper = !fUpper; break; case tomSentenceCase: if(*pch == TEXT('.')) // If sentence terminator, fStart = TRUE; // capitalize next alpha if(fAlpha) // If this char is alpha, next fStart = FALSE; // char can't start a break; // sentence case tomTitleCase: // If this char is alpha, next fStart = (fAlpha == 0); // char can't start a word break; default: return FALSE; } if(fAlpha && (fToUpper ^ fUpper)) // Only change case if it { // makes a difference (saves if(fToUpper) // on system calls and undos) CharUpperBuff(pch, 1); else CharLowerBuff(pch, 1); fChange = TRUE; // Return value: change made if( cchFirst == -1 ) // Save cch of unchanged cchFirst = cchGet-cchChunk; // leading string cchLast = cchChunk - 1; // Save cch of unchanged } // trailing string } if( cchFirst == -1 ) { Assert(cchLast == 0); cchFirst = cchGet; } Advance(cchFirst); // Skip unchanged leading cchGet -= cchFirst + cchLast; // string. cchGet = cch of ReplaceRange(cchGet, cchGet, rgCh // changed span + cchFirst, publdr, tomUndefined); Advance(cchLast); // Skip unchanged trailing } // string return fChange; }
void CCharBufferText::OnChange (unsigned int start, unsigned int old_count, unsigned int new_count) { unsigned int c_start_line = GetLine (start); ASSERT (c_start_line < lines_count); unsigned int c_start_position = min (start - line_start [c_start_line], line_length [c_start_line]); unsigned int c_old_end_line = GetLine (start + old_count); ASSERT (c_old_end_line < lines_count); unsigned int c_old_end_position = min (start + old_count - line_start [c_old_end_line], line_length [c_old_end_line]); int delta = new_count - old_count; unsigned int before = GetLastLineEndingBefore (start); if (before > 0 && before < lines_count && line_length [before] == 0) before -= 1; unsigned int start_line = before >= lines_count ? 0 : before + 1; unsigned int start_pos = before >= lines_count ? 0 : line_start [before] + line_length [before]; unsigned int after = GetFirstLineStartingAfter (start + old_count); if (after < lines_count && line_length [after] == 0) after += 1; unsigned int end_line = after >= lines_count ? lines_count : after; unsigned int end_pos = after >= lines_count ? data.GetSize () : line_start [after] + delta; unsigned int count = end_pos - start_pos; vector <unsigned int> starts; vector <unsigned int> lengths; SplitIntoLines (start_pos, count, starts, lengths); int c = starts.size (); ASSERT (c > 0); if (c == 1 && before < lines_count && after < lines_count) // Join three lines { line_length [before] += lengths [0] + line_length [after]; line_length.erase (line_length.begin () + before + 1, line_length.begin () + after + 1); line_start.erase (line_start.begin () + before + 1, line_start.begin () + after + 1); if (delta != 0) { unsigned int size = line_length.size (); for (unsigned int i = before + 1; i < size; i++) line_start [i] += delta; } } else { unsigned int b = 0; unsigned int e = c; if (before < lines_count) // Append line { line_length [before] += lengths [0]; b = 1; } if (after < lines_count) // Prepend line { line_start [after] -= lengths [c - 1]; line_length [after] += lengths [c - 1]; e = c - 1; } if (end_line < start_line) end_line = start_line; ReplaceRange (line_length, start_line, end_line - start_line, lengths, b, e - b); ReplaceRange (line_start, start_line, end_line - start_line, starts, b, e - b); if (delta != 0) { unsigned int size = line_length.size (); for (unsigned int i = (before < lines_count ? before + 1 : 0) + e - b; i < size; i++) line_start [i] += delta; } } lines_count = line_length.size (); unsigned int c_new_end_line = GetLine (start + new_count); ASSERT (c_new_end_line < lines_count); unsigned int c_new_end_position = min (start + new_count - line_start [c_new_end_line], line_length [c_new_end_line]); FireOnChange (c_start_line, c_start_position, c_old_end_line, c_old_end_position, c_new_end_line, c_new_end_position); }