|
1 /* |
|
2 * Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "TXTRICH.H" |
|
20 #include "TXTSTD.H" |
|
21 #include "ParseLst.h" |
|
22 |
|
23 |
|
24 // Install and activate a particular parser, app provides instance |
|
25 EXPORT_C void CRichText::ActivateParserL(MParser* aParser) |
|
26 { |
|
27 CParserList* activeParserList = (CParserList*)Dll::Tls(); |
|
28 if (!activeParserList) |
|
29 { |
|
30 CreateParserETextTLSL(); |
|
31 activeParserList = (CParserList*)Dll::Tls(); |
|
32 } |
|
33 activeParserList->ActivateParserL(aParser); |
|
34 } |
|
35 |
|
36 |
|
37 // Deactivate and deinstall a particular parser, identified by ptr to instance |
|
38 EXPORT_C void CRichText::DeactivateParser(MParser* aParser) |
|
39 { |
|
40 CParserList* activeParserList = (CParserList*)Dll::Tls(); |
|
41 __ASSERT_DEBUG(activeParserList, Panic(EParserListNotInitialized)); |
|
42 activeParserList->DeactivateParser(aParser); |
|
43 if ((activeParserList->iRefCount == 0) && (activeParserList->iNumberInList == 0)) |
|
44 { |
|
45 Dll::FreeTls(); |
|
46 delete activeParserList; |
|
47 } |
|
48 } |
|
49 |
|
50 |
|
51 // Install and activate a parser in the default set |
|
52 EXPORT_C void CRichText::ActivateDefaultParserL(MParser* aParser) |
|
53 { |
|
54 CParserList* activeParserList = (CParserList*)Dll::Tls(); |
|
55 if (!activeParserList) |
|
56 { |
|
57 CreateParserETextTLSL(); |
|
58 activeParserList = (CParserList*)Dll::Tls(); |
|
59 } |
|
60 activeParserList->ActivateDefaultParserL(aParser); |
|
61 } |
|
62 |
|
63 |
|
64 // Deactivate and deinstall the standard set of default parsers |
|
65 EXPORT_C void CRichText::DeactivateParserDefaults() |
|
66 { |
|
67 CParserList* activeParserList = (CParserList*)Dll::Tls(); |
|
68 if (activeParserList) |
|
69 { |
|
70 activeParserList->DeactivateParserDefaults(); |
|
71 if ((activeParserList->iRefCount == 0) && (activeParserList->iNumberInList == 0)) |
|
72 { |
|
73 Dll::FreeTls(); |
|
74 delete activeParserList; |
|
75 } |
|
76 } |
|
77 } |
|
78 |
|
79 |
|
80 // Create ParserLst instance and retain ownership of it but pass address to EText TLS |
|
81 void CRichText::CreateParserETextTLSL() |
|
82 { |
|
83 __ASSERT_DEBUG(Dll::Tls() == NULL, Panic(EParserListAlreadyExists)); |
|
84 CParserList* activeParserList = new (ELeave) CParserList; |
|
85 CleanupStack::PushL(activeParserList); |
|
86 TInt err = Dll::SetTls(activeParserList); |
|
87 User::LeaveIfError(err); |
|
88 CleanupStack::Pop(activeParserList); |
|
89 } |
|
90 |
|
91 |
|
92 // Set observer callback to tell whenever the object has been edited. |
|
93 // If set set then the parser system is active for this instance, otherwise not. |
|
94 |
|
95 |
|
96 |
|
97 EXPORT_C void CRichText::SetEditObserver(MEditObserver* aEditObserver) |
|
98 /** Sets the rich text object's edit observer. The observer's EditObserver() function |
|
99 is called by the rich text object each time the object's text content is edited |
|
100 (e.g. after a paste, insert, delete, reset etc.). |
|
101 |
|
102 @param aEditObserver Pointer to the edit observer. */ |
|
103 { |
|
104 iParserData->iEditObserver = aEditObserver; |
|
105 } |
|
106 |
|
107 |
|
108 EXPORT_C TBool CRichText::ParseText(TInt& aStartOfTags, TInt& aLength, TBool aForceScanAllText) |
|
109 { |
|
110 __ASSERT_ALWAYS(iIndex.IsPtr(),Panic(EParserListTextIndexNotInitialized)); |
|
111 TBool foundSomething = EFalse; |
|
112 if (iParserData->iActiveParserList && iParserData->iEditObserver) |
|
113 { |
|
114 if (aForceScanAllText) |
|
115 foundSomething = iParserData->iActiveParserList->ParseThisText(*this,0,DocumentLength(), |
|
116 aStartOfTags,aLength); |
|
117 else if (iParserData->HaveRange()) |
|
118 foundSomething = iParserData->iActiveParserList->ParseThisText(*this,iParserData->StartParse(), |
|
119 iParserData->EndParse() - iParserData->StartParse(), |
|
120 aStartOfTags,aLength); |
|
121 // All parsers have scanned the area |
|
122 iParserData->KillRange(); |
|
123 } |
|
124 return foundSomething; |
|
125 } |
|
126 |
|
127 |
|
128 // Given a cursor position, is there a tag under it and, if so, give me details |
|
129 TBool CRichText::DoCursorOverTag(TInt aPos, MParser*& aParser, TInt& aTagStart, TInt& aLength) const |
|
130 { |
|
131 TCharFormatX format; |
|
132 TCharFormatXMask varies; |
|
133 TBool success = EFalse; |
|
134 TBuf<1> buf; |
|
135 |
|
136 __ASSERT_DEBUG(iParserData->iActiveParserList, Panic(EParserListNotInitialized)); |
|
137 __ASSERT_DEBUG(iParserData->iEditObserver, Panic(EParserListNotActive)); |
|
138 GetExtendedCharFormat(format, varies, aPos, 1); |
|
139 Extract(buf, aPos, 1); |
|
140 if ((format.iParserTag) && (buf[0] != 0x2029)) |
|
141 { |
|
142 aParser = iParserData->iActiveParserList->ParserWithThisTag(format.iParserTag); |
|
143 if (aParser == NULL) |
|
144 { // Parser has been deactivated |
|
145 return EFalse; |
|
146 } |
|
147 // Get extent of tag (or set of contiguous tags for same parser) |
|
148 TInt pos = aPos; |
|
149 TInt startScan; |
|
150 TInt scanLength; |
|
151 // need to check backwards |
|
152 TUint tag = format.iParserTag; // Stash tag before overwriting it |
|
153 for (; pos > 0; pos--) |
|
154 { |
|
155 GetExtendedCharFormat(format, varies, pos - 1, 1); |
|
156 Extract(buf, aPos, 1); |
|
157 if ((format.iParserTag != tag) || (buf[0] == 0x2029)) |
|
158 break; |
|
159 } |
|
160 startScan = pos; |
|
161 // Now forwards |
|
162 TInt len = DocumentLength(); |
|
163 while (pos < len) |
|
164 { |
|
165 TPtrC ptr; |
|
166 GetTextAndExtendedFormat(ptr, format, pos); |
|
167 if (format.iParserTag != tag) |
|
168 break; |
|
169 pos += ptr.Length(); |
|
170 } |
|
171 if (pos > len) |
|
172 pos = len; |
|
173 scanLength = pos - startScan; |
|
174 // Now use the parser that found it originally to isolate the exact range |
|
175 // of the tag from the range that could contain several |
|
176 for (;;) |
|
177 { |
|
178 TInt result = aParser->ParseThisText(*this, EFalse, startScan, scanLength, aTagStart, aLength); |
|
179 // Check we haven't gone past it (failed to find it this time) |
|
180 if (!result || (aTagStart > aPos)) |
|
181 break; |
|
182 if ((aPos >= aTagStart) && (aPos < aTagStart + aLength)) |
|
183 { |
|
184 // We've found it |
|
185 success = ETrue; |
|
186 break; |
|
187 } |
|
188 // Not yet, skip over that one |
|
189 startScan += aLength; |
|
190 scanLength -= aLength; |
|
191 if (scanLength < 0) |
|
192 break; |
|
193 } |
|
194 |
|
195 } |
|
196 return success; |
|
197 } |
|
198 |
|
199 |
|
200 EXPORT_C TBool CRichText::CursorOverTag(TInt aPos, MParser*& aParser, TInt& aTagStart, TInt& aLength) const |
|
201 { |
|
202 TBool over = EFalse; |
|
203 |
|
204 if (iParserData->iActiveParserList && iParserData->iEditObserver) |
|
205 { |
|
206 iParserData->iLastKnownCursor = aPos; |
|
207 if (DoCursorOverTag(aPos, aParser, aTagStart, aLength)) |
|
208 { |
|
209 over = ETrue; |
|
210 } |
|
211 if (aPos && DoCursorOverTag(aPos - 1, aParser, aTagStart, aLength)) |
|
212 { |
|
213 over = ETrue; |
|
214 } |
|
215 } |
|
216 return over && |
|
217 aParser->ConfirmCursorOverTag(*this, aTagStart, aLength, aPos); |
|
218 } |
|
219 |
|
220 |
|
221 EXPORT_C TInt CRichText::PositionOfNextTag(TInt aPos, const MParser * aParser) const |
|
222 { |
|
223 if (iParserData->iActiveParserList && iParserData->iEditObserver) |
|
224 { |
|
225 MParser* parser; |
|
226 TInt tagStart; |
|
227 TInt length; |
|
228 TInt newPos = aPos; |
|
229 TUint tag = 0; |
|
230 if (aParser) |
|
231 tag = iParserData->iActiveParserList->TagForThisParser(aParser); |
|
232 if (DoCursorOverTag(newPos, parser, tagStart, length)) |
|
233 newPos = tagStart + length; // To get past the current tag |
|
234 TPtrC ptr; |
|
235 TCharFormatX format; |
|
236 while (newPos < DocumentLength()) |
|
237 { |
|
238 GetTextAndExtendedFormat(ptr, format, newPos); |
|
239 if (format.iParserTag &&(!aParser || format.iParserTag == tag)) |
|
240 return newPos; |
|
241 newPos += ptr.Length(); |
|
242 } |
|
243 } |
|
244 |
|
245 return KErrNotFound; |
|
246 } |
|
247 |
|
248 |
|
249 EXPORT_C TInt CRichText::PositionOfNextTag(TInt aPos) const |
|
250 { |
|
251 return PositionOfNextTag(aPos, NULL); |
|
252 } |
|
253 |
|
254 |
|
255 EXPORT_C TInt CRichText::PositionOfPrevTag(TInt aPos, const MParser * aParser) const |
|
256 { |
|
257 if (iParserData->iActiveParserList && iParserData->iEditObserver) |
|
258 { |
|
259 MParser* parser; |
|
260 TInt tagStart; |
|
261 TInt length; |
|
262 TInt newPos = aPos; |
|
263 TUint tag = 0; |
|
264 if (aParser) |
|
265 tag = iParserData->iActiveParserList->TagForThisParser(aParser); |
|
266 if (DoCursorOverTag(newPos, parser, tagStart, length)) |
|
267 newPos = tagStart; // To get past the current tag |
|
268 TCharFormatX format; |
|
269 TCharFormatXMask varies; |
|
270 |
|
271 for (; newPos > 0; newPos--) |
|
272 { |
|
273 GetExtendedCharFormat(format, varies, newPos - 1, 1); |
|
274 if (format.iParserTag &&(!aParser || format.iParserTag == tag)) |
|
275 { |
|
276 if (DoCursorOverTag(newPos - 1, parser, tagStart, length)) |
|
277 return tagStart; |
|
278 } |
|
279 } |
|
280 } |
|
281 |
|
282 return KErrNotFound; |
|
283 } |
|
284 |
|
285 |
|
286 EXPORT_C TInt CRichText::PositionOfPrevTag(TInt aPos) const |
|
287 { |
|
288 return PositionOfPrevTag(aPos, NULL); |
|
289 } |
|
290 |
|
291 |
|
292 void CRichText::OverrideFormatForParsersIfApplicable(TPtrC& aText, TCharFormatX& aFormat, TInt aStartPos) const |
|
293 { |
|
294 if (aFormat.iParserTag && iParserData->iActiveParserList && iParserData->iEditObserver) |
|
295 { |
|
296 // Replace format |
|
297 TInt start = -1; |
|
298 TInt length = 0; |
|
299 TInt curPos = iParserData->iLastKnownCursor; |
|
300 if (curPos > DocumentLength()) |
|
301 curPos = DocumentLength(); // This shouldn't be neccesary but it makes it more |
|
302 // bulletproof if the calls from outside are made in |
|
303 // the wrong order |
|
304 if (curPos != -1) |
|
305 { |
|
306 TCharFormatX format; |
|
307 TCharFormatXMask varies; |
|
308 MParser* parser; |
|
309 |
|
310 GetExtendedCharFormat(format, varies, curPos, 1); |
|
311 // If char at curpos has a tag then cursor is over that tag, get extents of tag |
|
312 if (CParserList::ReformatOnRollover(format.iParserTag)) |
|
313 DoCursorOverTag(curPos, parser, start, length); |
|
314 else if (curPos) |
|
315 { |
|
316 GetExtendedCharFormat(format, varies, curPos - 1, 1); |
|
317 // Try the char "before" curpos |
|
318 if (CParserList::ReformatOnRollover(format.iParserTag)) |
|
319 DoCursorOverTag(curPos - 1, parser, start, length); |
|
320 } |
|
321 } |
|
322 |
|
323 MParser* parser = iParserData->iActiveParserList->ParserWithThisTag(aFormat.iParserTag); |
|
324 |
|
325 if (length && (aStartPos >= start) && (aStartPos < start + length)) |
|
326 { |
|
327 if (start + length < aStartPos + aText.Length()) |
|
328 aText.Set(aText.Left(start + length - aStartPos)); |
|
329 if (parser != NULL) |
|
330 { |
|
331 // Only accept the rollover format if the parser agrees |
|
332 // with the framework match of a tag |
|
333 if (parser->ConfirmCursorOverTag(*this, start, length, curPos)) |
|
334 parser->GetRolloverFormat(aFormat.iCharFormat); |
|
335 else |
|
336 // Reset format to recognised format if parser disagrees |
|
337 // with the framework match as the tag is still in view |
|
338 // and must be formatted as in the else clause below. |
|
339 parser->GetRecogniseFormat(aFormat.iCharFormat); |
|
340 } |
|
341 } |
|
342 else |
|
343 { |
|
344 if (length && (start > aStartPos)) |
|
345 aText.Set(aText.Left(start - aStartPos)); |
|
346 if (parser != NULL) |
|
347 parser->GetRecogniseFormat(aFormat.iCharFormat); |
|
348 } |
|
349 } |
|
350 } |
|
351 |
|
352 |
|
353 void CRichText::CallEditObserver(TInt aStart, TInt aExtent) const |
|
354 { |
|
355 if (iParserData->iEditObserver) |
|
356 iParserData->iEditObserver->EditObserver(aStart, aExtent); |
|
357 } |