|
1 // lrtextview.cpp |
|
2 // |
|
3 // Copyright (c) 2009 - 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "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 // Accenture - Initial contribution |
|
11 // |
|
12 |
|
13 #include "lrtextview.h" |
|
14 #include <e32cons.h> |
|
15 #include "filebuffer.h" |
|
16 #include <fshell/common.mmh> |
|
17 #include <fshell/ltkutils.h> |
|
18 |
|
19 const TInt KLastCtrlChar = 0x1f; //Last control char in ASCII |
|
20 const TInt KCtrlHTab = 0x09; //Horizontal Tab |
|
21 |
|
22 CLRTextView* CLRTextView::NewL(MConsoleProvider& aConsoleProvider, CFedBufferBase& aBuffer) |
|
23 { |
|
24 CLRTextView* self = new (ELeave) CLRTextView(aConsoleProvider, aBuffer); |
|
25 CleanupStack::PushL(self); |
|
26 self->ConstructL(); |
|
27 CleanupStack::Pop(self); |
|
28 return self; |
|
29 } |
|
30 |
|
31 CLRTextView::~CLRTextView() |
|
32 { |
|
33 delete iLineData; |
|
34 delete iMarkFlasher; |
|
35 } |
|
36 |
|
37 CLRTextView::CLRTextView(MConsoleProvider& aConsoleProvider, CFedBufferBase& aBuffer) |
|
38 : CTextView(aConsoleProvider, aBuffer), iMarkDocPos(-1), iOldNextLine(-1) |
|
39 { |
|
40 } |
|
41 |
|
42 void CLRTextView::ConstructL() |
|
43 { |
|
44 iLineData = new(ELeave) CLineData; |
|
45 CTextView::ConstructL(); |
|
46 iMarkFlasher = CPeriodic::NewL(CActive::EPriorityStandard); |
|
47 } |
|
48 |
|
49 //MDeferredClose |
|
50 TBool CLRTextView::TryCloseL() |
|
51 { |
|
52 if (!iBuffer.Modified()) |
|
53 { |
|
54 return ETrue; // Ok to close now |
|
55 } |
|
56 else |
|
57 { |
|
58 TKeyCode ch = iConsoleProvider.Query(_L("Save changes to file? (yes/no/cancel) "), _L("ync\x1B")); |
|
59 if (ch == 'c' || ch == EKeyEscape) return EFalse; |
|
60 if (ch == 'y') |
|
61 { |
|
62 TInt err = Save(); |
|
63 if (err) return EFalse; // Don't allow exit if the save failed |
|
64 } |
|
65 return ETrue; |
|
66 } |
|
67 } |
|
68 |
|
69 TInt CLRTextView::Save() |
|
70 { |
|
71 if (!iBuffer.Editable() || iLineIsErrorMessage) return KErrNotSupported; |
|
72 |
|
73 CancelMark(); |
|
74 TBool wasOpen = iBuffer.IsOpen(); |
|
75 TInt err = KErrNone; |
|
76 const TDesC& name = iBuffer.Title(); |
|
77 if (name.Length()) |
|
78 { |
|
79 iConsoleProvider.InfoPrint(_L("Saving")); |
|
80 TRAP(err, iBuffer.SaveL(name, ETrue)); |
|
81 HandleSaveResult(err, wasOpen, iBuffer.Title()); |
|
82 return err; |
|
83 } |
|
84 else |
|
85 { |
|
86 return SaveAs(); |
|
87 } |
|
88 } |
|
89 |
|
90 TInt CLRTextView::SaveAs() |
|
91 { |
|
92 if (!iBuffer.Editable() || iLineIsErrorMessage) return KErrNotSupported; |
|
93 |
|
94 CancelMark(); |
|
95 TBool wasOpen = iBuffer.IsOpen(); |
|
96 TFileName name = iBuffer.Title(); |
|
97 TInt err = KErrCancel; |
|
98 TBool go = iConsoleProvider.QueryFilename(_L("Save as: "), name); |
|
99 if (go) |
|
100 { |
|
101 iConsoleProvider.InfoPrint(_L("Saving")); |
|
102 TRAP(err, iBuffer.SaveL(name, EFalse)); |
|
103 if (err == KErrAlreadyExists) |
|
104 { |
|
105 if (iConsoleProvider.Query(_L("File already exists. Replace? (yes/no) "), _L("yn")) == 'y') |
|
106 { |
|
107 iConsoleProvider.InfoPrint(_L("Saving")); |
|
108 TRAP(err, iBuffer.SaveL(name, ETrue)); |
|
109 } |
|
110 } |
|
111 HandleSaveResult(err, wasOpen, iBuffer.Title()); |
|
112 } |
|
113 return err; |
|
114 } |
|
115 |
|
116 void CLRTextView::HandleSaveResult(TInt aError, TBool aWasOpen, const TDesC& aName) |
|
117 { |
|
118 if (aError == KErrNone) |
|
119 { |
|
120 TRAP_IGNORE(DoRedrawL()); // Not only can QueryFilename mess up the screen, saving the file could change the line endings and thus change the character counts, meaning we need to recalculate & redraw |
|
121 iConsoleProvider.InfoPrint(_L("Save succeeded")); |
|
122 } |
|
123 else if (aWasOpen && !iBuffer.IsOpen()) |
|
124 { |
|
125 // An error occurred after the original file was closed. This is fatal so we need to put up a dialog to that effect |
|
126 iLine.Format(_L("Temp file @ %S"), &aName); |
|
127 iLineIsErrorMessage = ETrue; |
|
128 UpdateStatus(); |
|
129 iConsoleProvider.InfoPrint(_L("Failed to rename temp file (%d)"), aError); |
|
130 } |
|
131 else |
|
132 { |
|
133 // Failed to save, but we didn't mess with the original so we can carry on using it |
|
134 TRAP_IGNORE(DoRedrawL()); // Not only can QueryFilename mess up the screen, saving the file could change the line endings and thus change the character counts, meaning we need to recalculate & redraw |
|
135 switch (aError) |
|
136 { |
|
137 case KErrDiskFull: |
|
138 iConsoleProvider.InfoPrint(_L("Disk full, couldn't save file")); |
|
139 break; |
|
140 case KErrNoMemory: |
|
141 iConsoleProvider.InfoPrint(_L("Out of memory!")); |
|
142 break; |
|
143 case KErrPathNotFound: |
|
144 iConsoleProvider.InfoPrint(_L("KErrPathNotFound. Directory doesn't exist?")); |
|
145 break; |
|
146 case KErrGeneral: |
|
147 iConsoleProvider.InfoPrint(_L("Couldn't save file. No idea why")); |
|
148 break; |
|
149 case KErrBadName: |
|
150 iConsoleProvider.InfoPrint(_L("Bad file name")); |
|
151 break; |
|
152 case KErrAccessDenied: |
|
153 iConsoleProvider.InfoPrint(_L("Access denied. Read-only file or disk?")); |
|
154 break; |
|
155 default: |
|
156 iConsoleProvider.InfoPrint(_L("Unable to save file: %d"), aError); |
|
157 break; |
|
158 } |
|
159 } |
|
160 } |
|
161 |
|
162 //MKeyConsumer |
|
163 TBool CLRTextView::ConsumeKeyL(const TKeyCode& aCode) |
|
164 { |
|
165 TBool handled = ETrue; |
|
166 switch(aCode) |
|
167 { |
|
168 case EKeyUpArrow: |
|
169 MoveCursor(0, -1); break; |
|
170 case EKeyDownArrow: |
|
171 MoveCursor(0, 1); break; |
|
172 case EKeyLeftArrow: |
|
173 MoveCursor(-1, 0); break; |
|
174 case EKeyRightArrow: |
|
175 MoveCursor(1, 0); break; |
|
176 case EKeyPageUp: |
|
177 case CTRL('u'): |
|
178 MoveCursor(0, -iWindow.iHeight); break; |
|
179 case EKeyPageDown: |
|
180 case CTRL('d'): |
|
181 MoveCursor(0, iWindow.iHeight); break; |
|
182 case EKeyHome: |
|
183 UpdateCursor(TPoint(0, iCursor.iY)); |
|
184 break; |
|
185 case CTRL('t'): |
|
186 iCursor.SetXY(0, 0); |
|
187 RequestData(ETrue, 0); break; |
|
188 case CTRL('b'): |
|
189 GoToLine(KMaxTInt); break; |
|
190 case EKeyEnd: |
|
191 { |
|
192 TInt pos = iLineData->LastValidPosition(iCursor.iY); |
|
193 UpdateCursor(TPoint(pos, iCursor.iY)); |
|
194 break; |
|
195 } |
|
196 case EKeyBackspace: |
|
197 DeleteTextL(-1); |
|
198 break; |
|
199 case EKeyDelete: |
|
200 DeleteTextL(1); |
|
201 break; |
|
202 case CTRL('s'): |
|
203 Save(); |
|
204 break; |
|
205 case CTRL('a'): |
|
206 SaveAs(); |
|
207 break; |
|
208 case CTRL('k'): |
|
209 DeleteCurrentLineL(); |
|
210 break; |
|
211 case CTRL('g'): |
|
212 GoToLine(); |
|
213 break; |
|
214 case CTRL('f'): |
|
215 Find(); |
|
216 break; |
|
217 case CTRL('v'): |
|
218 PasteL(); |
|
219 break; |
|
220 case CTRL('c'): |
|
221 if (MarkActive()) CopyOrCutL(EFalse); |
|
222 else SetMark(); |
|
223 break; |
|
224 case CTRL('x'): |
|
225 if (MarkActive()) CopyOrCutL(ETrue); |
|
226 else SetMark(); |
|
227 break; |
|
228 default: |
|
229 handled = EFalse; |
|
230 break; |
|
231 } |
|
232 |
|
233 if (!handled && (aCode == EKeyEnter || aCode == EKeyTab || TChar(aCode).IsPrint())) |
|
234 { |
|
235 TBuf<1> buf; |
|
236 buf.Append(aCode); |
|
237 InsertTextL(buf); |
|
238 handled = ETrue; |
|
239 } |
|
240 return handled; |
|
241 } |
|
242 |
|
243 void CLRTextView::MoveCursor(TInt aX, TInt aY) |
|
244 { |
|
245 if (aX) ASSERT(aY == 0); |
|
246 if (aY) ASSERT(aX == 0); // Can't handle moving in both axes at once |
|
247 |
|
248 TInt line = iCursor.iY; |
|
249 TInt dir = (aX > 0) ? 1 : -1; |
|
250 TInt newX = iCursor.iX; |
|
251 while (aX) |
|
252 { |
|
253 newX += dir; |
|
254 if (newX < 0) |
|
255 { |
|
256 // Move to end of previous line |
|
257 newX = iWindow.iWidth; // Validate cursor will fix this, in the case where this causes us to scroll up a line |
|
258 aY--; |
|
259 break; // We don't really support seeking further than one line either way |
|
260 } |
|
261 else if (newX >= iWindow.iWidth-1) |
|
262 { |
|
263 // Move to start of next line |
|
264 newX = 0; |
|
265 aY++; |
|
266 break; // We don't really support seeking further than one line either way |
|
267 } |
|
268 |
|
269 if (iLineData->IsScreenPositionValid(line, newX)) |
|
270 { |
|
271 aX -= dir; |
|
272 } |
|
273 } |
|
274 |
|
275 if (line + aY < 0) |
|
276 { |
|
277 // Scroll up |
|
278 SeekData(iLineData->DocumentPosition(0), aY); |
|
279 } |
|
280 else if (line + aY >= iWindow.iHeight) |
|
281 { |
|
282 // Scroll down |
|
283 SeekData(iLineData->DocumentPosition(0), aY); |
|
284 } |
|
285 else |
|
286 { |
|
287 // Just update cursor |
|
288 if (iLineData->IsScreenPositionValid(line + aY, 0)) |
|
289 { |
|
290 // If the line len is -1 it means the doc doesn't extend as far as this line, therefore we shouldn't allow the cursor to be moved to it |
|
291 UpdateCursor(TPoint(newX, iCursor.iY + aY)); |
|
292 } |
|
293 } |
|
294 } |
|
295 |
|
296 void CLRTextView::DoResizeL(const TWindow& /*aOldWindow*/) |
|
297 { |
|
298 DoRedrawL(); |
|
299 } |
|
300 |
|
301 void CLRTextView::DoRedrawL() |
|
302 { |
|
303 iLineData->EnsureCapacityL(iWindow.iWidth, iWindow.iHeight); |
|
304 ValidateCursor(); |
|
305 RequestData(ETrue, iLineData->DocumentPosition(0)); |
|
306 } |
|
307 |
|
308 void CLRTextView::DoDrawL() |
|
309 { |
|
310 DoDrawL(iRecursiveUpdateFlag ? *iRecursiveUpdateFlag : ETrue); |
|
311 } |
|
312 |
|
313 void CLRTextView::DoDrawL(TBool aUpdateCursorAndStatus) |
|
314 { |
|
315 if (iDrawPoint == TPoint(iWindow.iX, iWindow.iY)) |
|
316 { |
|
317 iLineData->SetDocumentPositionForLine(0, iRange.iLocation); |
|
318 iLineData->SetFileLineNumberForLine(0, iRangeStartLineNumber); |
|
319 } |
|
320 |
|
321 ASSERT(iRange.iLength == iDes.Length()); |
|
322 HideCursor(); // Don't show the cursor while we're moving it around drawing - will be shown again by UpdateCursor |
|
323 for (iCurrentDrawDocIndex = 0; iCurrentDrawDocIndex < iDes.Length(); iCurrentDrawDocIndex++) |
|
324 { |
|
325 TUint16 c = iDes[iCurrentDrawDocIndex]; |
|
326 if(iDrawPoint.iY >= iWindow.NextY()) |
|
327 { |
|
328 break; |
|
329 } |
|
330 if(c <= KLastCtrlChar) |
|
331 HandleControlChar(); |
|
332 else |
|
333 AppendChar(c); |
|
334 } |
|
335 |
|
336 TBool moreData = !iBuffer.DocumentPositionIsEof(iRange.Max()); |
|
337 TBool filledScreen = iDrawPoint.iY >= iWindow.NextY(); |
|
338 if (!filledScreen) |
|
339 { |
|
340 WriteChars(); |
|
341 LineFinished(); |
|
342 } |
|
343 if (!moreData) |
|
344 { |
|
345 iDrawPoint.iY++; // Start from the line *after* the line we were on |
|
346 // In the case where we've finished writing but haven't filled the screen, we need to blank the rest of it (because to avoid flicker we don't do ClearScreens) |
|
347 while (iDrawPoint.iY < iWindow.NextY()) |
|
348 { |
|
349 iDrawPoint.iX = iWindow.iX; |
|
350 iLine.Copy(_L("^")); |
|
351 WriteChars(); |
|
352 TInt lineNum = iDrawPoint.iY - iWindow.iY; |
|
353 iLineData->SetDocumentPositionForLine(lineNum+1, iLineData->DocumentPosition(lineNum)); |
|
354 iLineData->SetFileLineNumberForLine(lineNum, -1); |
|
355 iLineData->LineFinishedAt(lineNum, -1); |
|
356 iDrawPoint.iY++; |
|
357 } |
|
358 } |
|
359 |
|
360 if (filledScreen || !moreData) |
|
361 { |
|
362 // Finished - null iDes and restore cursor |
|
363 iDes.Set(NULL, 0); |
|
364 if (aUpdateCursorAndStatus) |
|
365 { |
|
366 UpdateCursor(iCursor); |
|
367 } |
|
368 } |
|
369 else |
|
370 { |
|
371 TBool flagSet = EFalse; |
|
372 if (!iRecursiveUpdateFlag) |
|
373 { |
|
374 iRecursiveUpdateFlag = &aUpdateCursorAndStatus; |
|
375 flagSet = ETrue; |
|
376 } |
|
377 // More data needed to fill the screen |
|
378 RequestData(EFalse, iRange.Max()); |
|
379 if (flagSet) iRecursiveUpdateFlag = NULL; |
|
380 } |
|
381 } |
|
382 |
|
383 void CLRTextView::ValidateCursor() |
|
384 { |
|
385 CTextView::ValidateCursor(); |
|
386 TInt maxX = iWindow.iWidth - 1; |
|
387 TInt line = iCursor.iY; |
|
388 while (iCursor.iX > 0 && !iLineData->IsScreenPositionValid(line, iCursor.iX)) |
|
389 { |
|
390 // In case the cursor ended up in the dead space in the middle of a tab character |
|
391 iCursor.iX--; |
|
392 } |
|
393 while (line > 0 && !iLineData->IsScreenPositionValid(line, 0)) |
|
394 { |
|
395 // In case the cursor has ended up in dead space |
|
396 iCursor.iY--; |
|
397 line--; |
|
398 } |
|
399 maxX = iLineData->LastValidPosition(line); |
|
400 if(iCursor.iX > maxX) |
|
401 iCursor.iX = maxX; |
|
402 } |
|
403 |
|
404 void CLRTextView::HandleControlChar() |
|
405 { |
|
406 TUint8 c = iDes[iCurrentDrawDocIndex]; |
|
407 if (c == KCtrlHTab) |
|
408 { |
|
409 TInt pos = iDrawPoint.iX - iWindow.iX + iLine.Length(); |
|
410 TInt numToWrite = TabWidth() - (pos % TabWidth()); // Tabs align to 4-column boundaries rather than just outputting 4 spaces |
|
411 numToWrite = Min(numToWrite, iWindow.iWidth-1-pos); // In case the tab causes a line continuation, we want to make sure the tab ends at the end of the line so the following character will start at column zero |
|
412 for(TInt i=0; i<numToWrite; i++) |
|
413 { |
|
414 TInt line = iDrawPoint.iY - iWindow.iY; |
|
415 TInt col = iDrawPoint.iX - iWindow.iX + iLine.Length(); |
|
416 AppendChar(' '); |
|
417 if (i > 0) iLineData->SetPositionIsValid(line, col, EFalse); // i==0 (ie the first screen position of the tab) is a valid place to move the cursor to |
|
418 } |
|
419 } |
|
420 else if (c == '\r') |
|
421 { |
|
422 if (iCurrentDrawDocIndex+1 < iDes.Length() && iDes[iCurrentDrawDocIndex+1] == '\n') iCurrentDrawDocIndex++; // Skip over the CR to the LF |
|
423 WriteLine(); |
|
424 } |
|
425 else if (c == '\n') |
|
426 { |
|
427 WriteLine(); |
|
428 } |
|
429 else |
|
430 { |
|
431 // unknown control char - so escape it |
|
432 AppendChar('.'); |
|
433 } |
|
434 } |
|
435 |
|
436 void CLRTextView::AppendChar(TUint16 aChar) |
|
437 { |
|
438 iLine.Append(TChar(aChar)); |
|
439 iLineData->SetPositionIsValid(iDrawPoint.iY - iWindow.iY, iDrawPoint.iX - iWindow.iX + iLine.Length(), ETrue); |
|
440 if(iDrawPoint.iX + iLine.Length() == iWindow.NextX()-1) |
|
441 { |
|
442 WriteChars(); |
|
443 WriteLineContinue(); |
|
444 GoToNextLine(); |
|
445 } |
|
446 } |
|
447 |
|
448 //Writes the current line on the screen and moves to the next verse and line |
|
449 void CLRTextView::WriteLine() |
|
450 { |
|
451 TInt thisLine = iDrawPoint.iY-iWindow.iY; |
|
452 WriteChars(); |
|
453 GoToNextLine(); |
|
454 iLineData->SetFileLineNumberForLine(thisLine+1, iLineData->FileLineNumber(thisLine) + 1); |
|
455 if (iOldNextLine != -1 && thisLine+1 == iOldNextLine && iLineData->DocumentPosition(iOldNextLine) == iOldNextLineDocPos + iPredictedOldNextLineDocPosDelta) |
|
456 { |
|
457 // We have drawn everything we need to, and the drawing didn't cause the former next line to move |
|
458 iDrawPoint.iY = iWindow.NextY(); |
|
459 UpdateDocumentPositionsStartingAtLine(iOldNextLine+1, iPredictedOldNextLineDocPosDelta); |
|
460 } |
|
461 iOldNextLine = -1; |
|
462 } |
|
463 |
|
464 //Writes the current line on the screen |
|
465 void CLRTextView::WriteChars() |
|
466 { |
|
467 /* As an performance optimisation, we avoid calling ClearScreen when redrawing. Therefore we must ensure we |
|
468 * write to the entire line to prevent artifacts. |
|
469 * |
|
470 * There are 2 approaches to this - either using CConsoleBase::ClearToEndOfLine or padding the line with |
|
471 * spaces up to the window width. ClearToEndOfLine is really slow in WINSCW tshell which is why it's not |
|
472 * used by default. On target it's probably more efficient to use it. |
|
473 */ |
|
474 TPoint drawPoint(iDrawPoint); |
|
475 TPoint endPoint(drawPoint.iX + iLine.Length(), drawPoint.iY); |
|
476 iConsole.SetCursorPosAbs(drawPoint); |
|
477 #ifdef FSHELL_WSERV_SUPPORT |
|
478 // text windowserver is really slow at ClearToEndOfLine |
|
479 #define USE_CLEARTOENDOFLINE |
|
480 #endif |
|
481 #ifdef USE_CLEARTOENDOFLINE |
|
482 iConsole.Write(iLine); |
|
483 iConsole.ClearToEndOfLine(); |
|
484 #else |
|
485 iLine.AppendFill(' ', iWindow.iWidth - (iDrawPoint.iX-iWindow.iX) - iLine.Length()); |
|
486 iConsole.Write(iLine); |
|
487 #endif |
|
488 iLine.Zero(); |
|
489 // Remember to reset the console pos and the drawpoint to the end of the line proper in case the WriteChars |
|
490 // was to half a line and the rest of the line is in another block. |
|
491 iDrawPoint = endPoint; |
|
492 iConsole.SetCursorPosAbs(iDrawPoint); |
|
493 } |
|
494 |
|
495 // Moves to the next screen line but doesn't move to the next file line (used when a line is longer than the width of the window) |
|
496 void CLRTextView::GoToNextLine() |
|
497 { |
|
498 LineFinished(); |
|
499 iDrawPoint.iX = iWindow.iX; |
|
500 iDrawPoint.iY++; |
|
501 iLineData->SetPositionIsValid(iDrawPoint.iY-iWindow.iY, 0, ETrue); |
|
502 } |
|
503 |
|
504 void CLRTextView::LineFinished() |
|
505 { |
|
506 TInt currentLine = iDrawPoint.iY - iWindow.iY; |
|
507 if (currentLine == iWindow.iHeight) return; // This can happen (and is ok) when drawing the last line of the screen |
|
508 iLineData->LineFinishedAt(currentLine, iDrawPoint.iX - iWindow.iX); |
|
509 iLineData->SetDocumentPositionForLine(currentLine+1, iRange.iLocation+iCurrentDrawDocIndex+1); // Plus one cos we haven't drawn the first character of the next line yet |
|
510 } |
|
511 |
|
512 void CLRTextView::WriteLineContinue() |
|
513 { |
|
514 CColorConsoleBase* cons = iConsoleProvider.ColorConsole(); |
|
515 if (cons) cons->SetTextAttribute(ETextAttributeHighlight); |
|
516 _LIT(KContinuation, "\\"); |
|
517 iConsole.Write(KContinuation); |
|
518 if (cons) cons->SetTextAttribute(ETextAttributeNormal); |
|
519 |
|
520 TInt thisLine = iDrawPoint.iY-iWindow.iY; |
|
521 iLineData->SetFileLineNumberForLine(thisLine+1, iLineData->FileLineNumber(thisLine)); // The new line has the same file number as the current line, because it's a continuation line |
|
522 } |
|
523 |
|
524 TInt CLRTextView::DocumentPosition() const |
|
525 { |
|
526 TInt line = iCursor.iY; |
|
527 TInt pos = iLineData->DocumentPosition(line); |
|
528 for (TInt i = 0; i < iCursor.iX; i++) |
|
529 { |
|
530 if (iLineData->IsScreenPositionValid(line, i)) pos++; |
|
531 } |
|
532 return pos; |
|
533 } |
|
534 |
|
535 void CLRTextView::InsertTextL(const TDesC& aText) |
|
536 { |
|
537 if (!iBuffer.Editable() || iLineIsErrorMessage) return; |
|
538 |
|
539 CancelMark(); |
|
540 TInt pos = DocumentPosition(); |
|
541 iBuffer.InsertTextL(pos, aText); |
|
542 if (iCursor.iX == iWindow.iWidth-1) |
|
543 { |
|
544 // If the cursor is over a line continuation character (which it will be if it gets to this value) move cursor to the real insertion point at the start of the next line before doing anything - makes the logic much easier! |
|
545 MoveCursor(1, 0); |
|
546 } |
|
547 |
|
548 // If we can be sure it won't cause a reflow, shortcut the requesting and redrawing mechanism, for performance reasons |
|
549 TPoint cursorPos = WindowLocationForDocumentPosition(pos); |
|
550 TInt lineNum = cursorPos.iY; |
|
551 TBool posWasAtEndOfLine = cursorPos.iX == iLineData->LastValidPosition(lineNum) && cursorPos.iX + aText.Length() < iWindow.iWidth-1; |
|
552 if (posWasAtEndOfLine && aText.Locate('\t') == KErrNotFound && aText.Locate('\r') == KErrNotFound) |
|
553 { |
|
554 // Can't optimise if the text contains tabs or newlines, we need to do the full algorithm in that case |
|
555 iConsole.Write(aText); |
|
556 for (TInt i = 0; i < aText.Length(); i++) |
|
557 { |
|
558 iLineData->SetPositionIsValid(lineNum, cursorPos.iX + 1 + i, ETrue); // Each added char makes the character position one after that character valid |
|
559 } |
|
560 for (TInt l = lineNum+1; l <= iWindow.iHeight; l++) |
|
561 { |
|
562 iLineData->SetDocumentPositionForLine(l, iLineData->DocumentPosition(l) + aText.Length()); |
|
563 } |
|
564 MoveCursor(aText.Length(),0); |
|
565 } |
|
566 else |
|
567 { |
|
568 RedrawFromPositionToEndOfLine(pos, aText.Length()); |
|
569 CenterDocPosOnScreen(pos + aText.Length()); |
|
570 } |
|
571 } |
|
572 |
|
573 void CLRTextView::RedrawFromPositionToEndOfLine(TInt aDocumentPosition, TInt aNumCharsInserted) |
|
574 { |
|
575 TPoint point = WindowLocationForDocumentPosition(aDocumentPosition); |
|
576 ASSERT(point.iY >= 0); |
|
577 iDrawPoint = point + iWindow.Origin(); |
|
578 // Try and avoid redrawing the whole screen if the RequestData doesn't make the line longer (in terms of screen lines) |
|
579 TInt lastScreenLineForCurrentLine = point.iY; |
|
580 while (iLineData->FileLineNumber(lastScreenLineForCurrentLine+1) == iLineData->FileLineNumber(lastScreenLineForCurrentLine)) |
|
581 { |
|
582 // We have to allow for the current line wrapping onto additional screen lines |
|
583 lastScreenLineForCurrentLine++; |
|
584 } |
|
585 iOldNextLine = lastScreenLineForCurrentLine + 1; |
|
586 iOldNextLineDocPos = iLineData->DocumentPosition(iOldNextLine); |
|
587 iPredictedOldNextLineDocPosDelta = aNumCharsInserted; |
|
588 RequestData(EFalse, aDocumentPosition); |
|
589 } |
|
590 |
|
591 void CLRTextView::DeleteTextL(TInt aNumChars) |
|
592 { |
|
593 if (!iBuffer.Editable() || iLineIsErrorMessage) return; |
|
594 |
|
595 CancelMark(); |
|
596 ASSERT(aNumChars >= -1); // Can't backspace more than one character |
|
597 TInt pos = DocumentPosition(); |
|
598 TRange range; |
|
599 if (aNumChars < 0) |
|
600 range = TRange(pos + aNumChars, -aNumChars); |
|
601 else |
|
602 range = TRange(pos, aNumChars); |
|
603 range.Intersect(TRange(0, KMaxTInt)); // Clamp the range to zero |
|
604 |
|
605 if (range.iLength) |
|
606 { |
|
607 iBuffer.DeleteTextL(range); |
|
608 TInt line = iCursor.iY; |
|
609 if (aNumChars == -1) MoveCursor(-1, 0); |
|
610 // Seems to be an issue using RedrawFromPositionToEndOfLine when deleting from the start of a line - since we know it wouldn't be worth using it in that case, don't do it. |
|
611 if (iCursor.iY == line && aNumChars == -1) |
|
612 { |
|
613 RedrawFromPositionToEndOfLine(range.iLocation, -range.iLength); |
|
614 } |
|
615 else |
|
616 { |
|
617 iDrawPoint = iCursor + iWindow.Origin(); |
|
618 RequestData(EFalse, DocumentPosition()); |
|
619 } |
|
620 } |
|
621 } |
|
622 |
|
623 void CLRTextView::DeleteCurrentLineL() |
|
624 { |
|
625 if (!iBuffer.Editable() || iLineIsErrorMessage) return; |
|
626 |
|
627 CancelMark(); |
|
628 TInt currentLine = iCursor.iY; |
|
629 TInt lineStart = iLineData->DocumentPosition(currentLine); |
|
630 TInt lineLen = iLineData->DocumentPosition(currentLine+1) - lineStart; |
|
631 if (lineLen > 0) |
|
632 { |
|
633 TRange range(lineStart, lineLen); |
|
634 iBuffer.DeleteTextL(range); |
|
635 iDrawPoint = TPoint(iWindow.iX, iWindow.iY + iCursor.iY); |
|
636 RequestData(EFalse, lineStart); |
|
637 } |
|
638 } |
|
639 |
|
640 void CLRTextView::UpdateCursor(const TPoint& aNewPos) |
|
641 { |
|
642 CTextView::UpdateCursor(aNewPos); |
|
643 UpdateStatus(); |
|
644 } |
|
645 |
|
646 void CLRTextView::UpdateStatus() |
|
647 { |
|
648 if (iLineIsErrorMessage) |
|
649 { |
|
650 iConsoleProvider.WriteStatus(iLine, KNullDesC); |
|
651 return; |
|
652 } |
|
653 |
|
654 _LIT(KUnknownDelim, "CRLF"); // Since that's what it will get output as |
|
655 _LIT(KUnknownEnc, "UTF-8"); // ditto |
|
656 const TDesC* lineEnding = &KUnknownDelim; |
|
657 #define CASE_LIT(res, x, y) case x: { _LIT(KName, y); res = &KName; break; } |
|
658 switch(iBuffer.DelimiterType()) |
|
659 { |
|
660 CASE_LIT(lineEnding, EDelimLF, "LF") |
|
661 CASE_LIT(lineEnding, EDelimCR, "CR") |
|
662 CASE_LIT(lineEnding, EDelimCRLF, "CRLF") |
|
663 CASE_LIT(lineEnding, EDelimUnicode, "UniLF") |
|
664 default: |
|
665 break; |
|
666 } |
|
667 const TDesC* encoding = &KUnknownEnc; |
|
668 switch (iBuffer.Encoding()) |
|
669 { |
|
670 CASE_LIT(encoding, EEncodingNarrow, "8-bit") |
|
671 CASE_LIT(encoding, EEncodingUtf8, "UTF-8") |
|
672 CASE_LIT(encoding, EEncodingUtf16LE, "UTF-16LE") |
|
673 CASE_LIT(encoding, EEncodingUtf16BE, "UTF-16BE") |
|
674 default: |
|
675 break; |
|
676 } |
|
677 |
|
678 const TDesC* modified = &KNullDesC; |
|
679 if (iBuffer.Modified()) |
|
680 { |
|
681 _LIT(KMod, "(*)"); |
|
682 modified = &KMod; |
|
683 } |
|
684 |
|
685 TInt currentLine = iLineData->FileLineNumber(iCursor.iY) + 1; // Human-readable line numbers are always shown as 1-based |
|
686 TInt currentCol = iCursor.iX + 1; // Human-readable col numbers are always shown as 1-based, annoying as it is we will do the same |
|
687 TInt docPos = DocumentPosition(); |
|
688 _LIT(KStatus, "%S %S %S Ln:%d Col:%d Char:%d"); |
|
689 _LIT(KShortStatus, "%S %S %S Ln:%d"); |
|
690 _LIT(KMarkStatus, "Mark@%d. ^X/^C to complete. Ln:%d Ch:%d"); |
|
691 const TDesC* title = &iBuffer.Title(); |
|
692 _LIT(KUntitled, "untitled"); |
|
693 if (title->Length() == 0) title = &KUntitled; |
|
694 if (MarkActive()) |
|
695 { |
|
696 iConsoleProvider.WriteStatus(KNullDesC, KMarkStatus, iMarkDocPos, currentLine, docPos); |
|
697 } |
|
698 else if (iConsole.ScreenSize().iWidth < 50) |
|
699 { |
|
700 iConsoleProvider.WriteStatus(*title, KShortStatus, modified, lineEnding, encoding, currentLine); |
|
701 } |
|
702 else |
|
703 { |
|
704 iConsoleProvider.WriteStatus(*title, KStatus, modified, lineEnding, encoding, currentLine, currentCol, docPos); |
|
705 } |
|
706 } |
|
707 |
|
708 void CLRTextView::GoToLine() |
|
709 { |
|
710 TBuf<32> lineText; |
|
711 TBool go = iConsoleProvider.QueryText(_L("Go to line: "), lineText); |
|
712 if (go == EFalse) return; |
|
713 |
|
714 TLex lex(lineText); |
|
715 TBool gotoLine = ETrue; |
|
716 if (lex.Peek() == 'c') |
|
717 { |
|
718 // Go to character pos |
|
719 lex.Inc(); |
|
720 gotoLine = EFalse; |
|
721 } |
|
722 |
|
723 TInt pos; |
|
724 TInt err = lex.Val(pos); |
|
725 if (err || pos <= 0) |
|
726 { |
|
727 iConsoleProvider.InfoPrint(_L("Couldn't parse line number '%S'"), &lineText); |
|
728 return; |
|
729 } |
|
730 |
|
731 if (gotoLine && pos > 1) |
|
732 { |
|
733 GoToLine(pos-1); // -1 to change 1-based display number to internal 0-based. |
|
734 } |
|
735 else |
|
736 { |
|
737 CenterDocPosOnScreen(pos-1); |
|
738 } |
|
739 } |
|
740 |
|
741 void CLRTextView::GoToLine(TInt aLine) |
|
742 { |
|
743 TInt err = iBuffer.SeekFromOffset(*this, 0, aLine, KMaxTInt); |
|
744 if (err) |
|
745 { |
|
746 HandleDataLoadError(err); |
|
747 } |
|
748 else |
|
749 { |
|
750 CenterDocPosOnScreen(iRange.iLocation); |
|
751 } |
|
752 } |
|
753 |
|
754 void CLRTextView::Find() |
|
755 { |
|
756 TBool searchBackwards = EFalse; |
|
757 TBool proceed = iConsoleProvider.QueryText(_L("Find text: "), iFindText); |
|
758 while (proceed) |
|
759 { |
|
760 TInt nextLineStart = iLineData->DocumentPosition(iCursor.iY + 1); |
|
761 iConsoleProvider.InfoPrint(_L("Searching...")); |
|
762 TInt found = iBuffer.Find(nextLineStart, iFindText, searchBackwards); |
|
763 if (found == KErrNotFound) |
|
764 { |
|
765 iConsoleProvider.InfoPrint(_L("Cannot find '%S'"), &iFindText); |
|
766 proceed = EFalse; |
|
767 } |
|
768 else if (found < 0) |
|
769 { |
|
770 iConsoleProvider.InfoPrint(_L("Error during searching: %d"), found); |
|
771 proceed = EFalse; |
|
772 } |
|
773 else |
|
774 { |
|
775 CenterDocPosOnScreen(found); |
|
776 /*TKeyCode ch = iConsoleProvider.Query(_L("Search again? (next/previous/cancel) ")); |
|
777 if (ch == 'n') |
|
778 { |
|
779 searchBackwards = EFalse; |
|
780 continue; |
|
781 } |
|
782 else if (ch == 'p') |
|
783 { |
|
784 searchBackwards = ETrue; |
|
785 continue; |
|
786 } |
|
787 else*/ |
|
788 { |
|
789 proceed = EFalse; |
|
790 } |
|
791 } |
|
792 } |
|
793 } |
|
794 |
|
795 void CLRTextView::CenterDocPosOnScreen(TInt aDocumentPosition) |
|
796 { |
|
797 const TInt numScreenLines = iWindow.iHeight; |
|
798 TPoint point = WindowLocationForDocumentPosition(aDocumentPosition); |
|
799 if (point.iY != -1) |
|
800 { |
|
801 // pos is on screen already - just move the cursor |
|
802 UpdateCursor(point); |
|
803 return; |
|
804 } |
|
805 |
|
806 TInt err = iBuffer.SeekFromOffset(*this, aDocumentPosition, -(numScreenLines/2), iWindow.iWidth-1); |
|
807 if (err) |
|
808 { |
|
809 HandleDataLoadError(err); |
|
810 } |
|
811 else |
|
812 { |
|
813 iDrawPoint.SetXY(iWindow.iX, iWindow.iY); |
|
814 DoDrawL(EFalse); // EFalse to say we will update the cursor and status ourselves |
|
815 UpdateCursor(WindowLocationForDocumentPosition(aDocumentPosition)); |
|
816 } |
|
817 } |
|
818 |
|
819 void CLineData::EnsureCapacityL(TInt aWidth, TInt aHeight) |
|
820 { |
|
821 if (!iDocPosAndLine || (TUint)User::AllocLen(iDocPosAndLine) < sizeof(TInt)*2 * (aHeight+1)) |
|
822 { |
|
823 User::Free(iDocPosAndLine); |
|
824 iDocPosAndLine = NULL; |
|
825 iDocPosAndLine = (TInt*)User::AllocZL(sizeof(TInt)*2*(aHeight+1)); |
|
826 } |
|
827 iLineLen = aWidth-1; |
|
828 TInt lineWidthBytes = ((iLineLen+7)&~7)/8; |
|
829 TInt screenBytes = lineWidthBytes * (aHeight+1); |
|
830 if (!iScreenIndexes || User::AllocLen(iScreenIndexes) < screenBytes) |
|
831 { |
|
832 User::Free(iScreenIndexes); |
|
833 iScreenIndexes = NULL; |
|
834 iScreenIndexes = (TUint8*)User::AllocZL(screenBytes); |
|
835 } |
|
836 SetPositionIsValid(0, 0, ETrue); // This position is always valid even if the document is empty |
|
837 } |
|
838 |
|
839 TInt CLineData::DocumentPosition(TInt aLine) const |
|
840 { |
|
841 return iDocPosAndLine[aLine*2]; |
|
842 } |
|
843 |
|
844 TInt CLineData::FileLineNumber(TInt aLine) const |
|
845 { |
|
846 return iDocPosAndLine[aLine*2 + 1]; |
|
847 } |
|
848 |
|
849 const TUint8& CLineData::ByteForPos(TInt aLine, TInt aCol, TInt& aOffset) const |
|
850 { |
|
851 return const_cast<CLineData*>(this)->ByteForPos(aLine, aCol, aOffset); |
|
852 } |
|
853 |
|
854 TUint8& CLineData::ByteForPos(TInt aLine, TInt aCol, TInt& aOffset) |
|
855 { |
|
856 TInt lineWidthBytes = ((iLineLen+7)&~7)/8; |
|
857 TInt byteIndex = aLine*lineWidthBytes + aCol/8; |
|
858 aOffset = aCol%8; |
|
859 TUint8& b = iScreenIndexes[byteIndex]; |
|
860 return b; |
|
861 } |
|
862 |
|
863 TBool CLineData::IsScreenPositionValid(TInt aLine, TInt aCol) const |
|
864 { |
|
865 TInt offset; |
|
866 TUint8 b = ByteForPos(aLine, aCol, offset); |
|
867 return b & (1 << offset); |
|
868 } |
|
869 |
|
870 void CLineData::SetDocumentPositionForLine(TInt aLine, TInt aDocumentPosition) |
|
871 { |
|
872 ASSERT(iDocPosAndLine && aLine*2*sizeof(TInt) < (TUint)User::AllocLen(iDocPosAndLine)); |
|
873 iDocPosAndLine[aLine*2] = aDocumentPosition; |
|
874 } |
|
875 |
|
876 void CLineData::SetFileLineNumberForLine(TInt aLine, TInt aFileLineNumber) |
|
877 { |
|
878 ASSERT(iDocPosAndLine && (aLine*2+1)*sizeof(TInt) < (TUint)User::AllocLen(iDocPosAndLine)); |
|
879 iDocPosAndLine[aLine*2 + 1] = aFileLineNumber; |
|
880 } |
|
881 |
|
882 void CLineData::SetPositionIsValid(TInt aLine, TInt aCol, TBool aValid) |
|
883 { |
|
884 TInt offset; |
|
885 TUint8& b = ByteForPos(aLine, aCol, offset); |
|
886 if (aValid) |
|
887 { |
|
888 b |= (1<<offset); |
|
889 } |
|
890 else |
|
891 { |
|
892 b &= ~(1<<offset); |
|
893 } |
|
894 } |
|
895 |
|
896 void CLineData::LineFinishedAt(TInt aLine, TInt aCol) |
|
897 { |
|
898 if (aCol >=0 && aCol < iLineLen) SetPositionIsValid(aLine, aCol, ETrue); // The last position on the line is valid even though nothing is drawn there |
|
899 TInt offset; |
|
900 TUint8& b = ByteForPos(aLine, aCol+1, offset); // Plus one as we want to clear everything beyond the aCol position |
|
901 // Clear everything above the offset'th bit |
|
902 b &= (1 << offset) - 1; |
|
903 // And clear all bytes from b to the end of the line |
|
904 TUint8* endb = &ByteForPos(aLine, iLineLen-1, offset); |
|
905 // Armv5 doesn't like passing in a length of zero to memset, not sure why. Anyway, check there are actually some other bytes that need clearing |
|
906 if (endb > &b) |
|
907 { |
|
908 Mem::FillZ((&b)+1, endb - &b); |
|
909 } |
|
910 } |
|
911 |
|
912 CLineData::~CLineData() |
|
913 { |
|
914 User::Free(iDocPosAndLine); |
|
915 User::Free(iScreenIndexes); |
|
916 } |
|
917 |
|
918 TInt CLineData::LastValidPosition(TInt aLine) const |
|
919 { |
|
920 TInt offset; |
|
921 TInt pos = iLineLen-1; // Start comparing from the last possible valid position |
|
922 const TUint8* b = &ByteForPos(aLine, pos, offset); |
|
923 while(*b == 0) |
|
924 { |
|
925 b--; // Optimisation to skip over blanks |
|
926 pos -= offset+1; |
|
927 offset = 7; |
|
928 } |
|
929 |
|
930 while (offset >= 0 && ((*b) & (1<<offset)) == 0) |
|
931 { |
|
932 pos--; |
|
933 offset--; |
|
934 } |
|
935 ASSERT(IsScreenPositionValid(aLine, pos)); // Something's gone very wrong in the logic of this function if this assert is not true! |
|
936 return pos; |
|
937 } |
|
938 |
|
939 void CLRTextView::CopyToClipboardL(const TDesC& aBuf) |
|
940 { |
|
941 LtkUtils::CopyToClipboardL(aBuf, &iConsoleProvider.Fs()); |
|
942 } |
|
943 |
|
944 HBufC* CLRTextView::GetFromClipboardL() |
|
945 { |
|
946 return LtkUtils::GetFromClipboardL(&iConsoleProvider.Fs()); |
|
947 } |
|
948 |
|
949 void CLRTextView::PasteL() |
|
950 { |
|
951 HBufC* text = GetFromClipboardL(); |
|
952 CleanupStack::PushL(text); |
|
953 if (text->Length() == 0) |
|
954 { |
|
955 iConsoleProvider.InfoPrint(_L("Nothing to paste")); |
|
956 } |
|
957 else |
|
958 { |
|
959 InsertTextL(*text); |
|
960 } |
|
961 CleanupStack::PopAndDestroy(text); |
|
962 } |
|
963 |
|
964 TPoint CLRTextView::WindowLocationForDocumentPosition(TInt aDocumentPosition) const |
|
965 { |
|
966 TPoint result(-1,-1); // We return this if doc pos is not on screen |
|
967 const TInt numScreenLines = iWindow.iHeight; |
|
968 if (aDocumentPosition >= iLineData->DocumentPosition(0) && aDocumentPosition < iLineData->DocumentPosition(numScreenLines)) |
|
969 { |
|
970 // pos is on screen |
|
971 for (TInt line = 0; line < numScreenLines; line++) |
|
972 { |
|
973 if (aDocumentPosition < iLineData->DocumentPosition(line+1)) |
|
974 { |
|
975 result.iY = line; |
|
976 TInt docPos = iLineData->DocumentPosition(line); |
|
977 TInt column = 0; |
|
978 // Find the column screen position whose document position is what we want |
|
979 for (; docPos <= aDocumentPosition; column++) |
|
980 { |
|
981 if (iLineData->IsScreenPositionValid(line, column)) |
|
982 { |
|
983 docPos++; |
|
984 } |
|
985 } |
|
986 result.iX = column-1; |
|
987 break; |
|
988 } |
|
989 } |
|
990 } |
|
991 return result; |
|
992 } |
|
993 |
|
994 void CLRTextView::SetMark() |
|
995 { |
|
996 iMarkFlasher->Cancel(); |
|
997 iMarkDocPos = DocumentPosition(); |
|
998 iMarkShown = EFalse; |
|
999 const TInt KFlashInterval = 500000; |
|
1000 iMarkFlasher->Start(0, KFlashInterval, TCallBack(&FlashMark, this)); |
|
1001 UpdateStatus(); |
|
1002 iConsoleProvider.InfoPrint(_L("Mark set. Move cursor then press ^X/^C to cut/copy")); |
|
1003 } |
|
1004 |
|
1005 void CLRTextView::CancelMark() |
|
1006 { |
|
1007 if (MarkActive()) |
|
1008 { |
|
1009 iMarkFlasher->Cancel(); |
|
1010 if (WindowLocationForDocumentPosition(iMarkDocPos).iY != -1) |
|
1011 { |
|
1012 // If the mark pos is on screen, redraw it to get rid of the '*' |
|
1013 RedrawFromPositionToEndOfLine(iMarkDocPos, 0); |
|
1014 } |
|
1015 } |
|
1016 } |
|
1017 |
|
1018 TInt CLRTextView::FlashMark(TAny* aSelf) |
|
1019 { |
|
1020 static_cast<CLRTextView*>(aSelf)->DoFlashMark(); |
|
1021 return 1; |
|
1022 } |
|
1023 |
|
1024 void CLRTextView::DoFlashMark() |
|
1025 { |
|
1026 TPoint markPos = WindowLocationForDocumentPosition(iMarkDocPos); |
|
1027 if (markPos.iY != -1) |
|
1028 { |
|
1029 TPoint cursor = iConsole.CursorPos(); |
|
1030 iConsole.SetCursorPosAbs(markPos); |
|
1031 if (iMarkShown) |
|
1032 iConsole.Write(_L(" ")); |
|
1033 else |
|
1034 iConsole.Write(_L("*")); |
|
1035 iMarkShown = !iMarkShown; |
|
1036 iConsole.SetCursorPosAbs(cursor); |
|
1037 } |
|
1038 } |
|
1039 |
|
1040 TBool CLRTextView::MarkActive() const |
|
1041 { |
|
1042 return iMarkFlasher->IsActive(); |
|
1043 } |
|
1044 |
|
1045 void CLRTextView::CopyOrCutL(TBool aCut) |
|
1046 { |
|
1047 TInt docPos = DocumentPosition(); |
|
1048 const TInt start = Min(docPos, iMarkDocPos); |
|
1049 const TInt len = Abs(docPos - iMarkDocPos); |
|
1050 CancelMark(); |
|
1051 RBuf textCopy; |
|
1052 CleanupClosePushL(textCopy); |
|
1053 TInt dataPos = start; |
|
1054 while(ETrue) |
|
1055 { |
|
1056 TInt err = iBuffer.GetData(*this, dataPos); |
|
1057 if (err) |
|
1058 { |
|
1059 HandleDataLoadError(err); |
|
1060 CleanupStack::PopAndDestroy(&textCopy); |
|
1061 return; |
|
1062 } |
|
1063 else |
|
1064 { |
|
1065 TPtrC text = iDes.Left(len - textCopy.Length()); |
|
1066 if (textCopy.MaxLength() == 0 && len <= iRange.iLength) |
|
1067 { |
|
1068 // All fits in one buffer, no need to copy anything! |
|
1069 CopyToClipboardL(text); |
|
1070 break; |
|
1071 } |
|
1072 else |
|
1073 { |
|
1074 // Need to do some copying |
|
1075 if (textCopy.MaxLength() == 0) textCopy.CreateL(len); |
|
1076 textCopy.Append(text); |
|
1077 dataPos += text.Length(); |
|
1078 if (textCopy.Length() == len) |
|
1079 { |
|
1080 // Done |
|
1081 CopyToClipboardL(textCopy); |
|
1082 break; |
|
1083 } |
|
1084 } |
|
1085 } |
|
1086 } |
|
1087 CleanupStack::PopAndDestroy(&textCopy); |
|
1088 if (aCut) |
|
1089 { |
|
1090 TRange range(start, len); |
|
1091 iBuffer.DeleteTextL(range); |
|
1092 iCursor = WindowLocationForDocumentPosition(start); |
|
1093 if (iCursor.iY == -1) |
|
1094 { |
|
1095 // Start of the selection is offscreen - force a scroll |
|
1096 CenterDocPosOnScreen(start); |
|
1097 } |
|
1098 else |
|
1099 { |
|
1100 iDrawPoint = iCursor + iWindow.Origin(); |
|
1101 RequestData(EFalse, start); |
|
1102 } |
|
1103 iConsoleProvider.InfoPrint(_L("Text cut to clipboard")); |
|
1104 } |
|
1105 else |
|
1106 { |
|
1107 iConsoleProvider.InfoPrint(_L("Text copied to clipboard")); |
|
1108 } |
|
1109 } |
|
1110 |
|
1111 void CLRTextView::UpdateDocumentPositionsStartingAtLine(TInt aLine, TInt aDelta) |
|
1112 { |
|
1113 // This is important because even though we can optimise away the drawing, when a line has changed |
|
1114 // length but not caused wrapping, we still need to make sure the document positions of all the |
|
1115 // lines we didn't draw are correct to reflect the extra characters. |
|
1116 for (TInt i = aLine; i < iWindow.iHeight+1; i++) |
|
1117 { |
|
1118 iLineData->SetDocumentPositionForLine(i, iLineData->DocumentPosition(i) + aDelta); |
|
1119 } |
|
1120 } |