|
1 /* |
|
2 * Copyright (c) 2006 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: Chat client's editor. |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 // INCLUDE FILES |
|
20 #include "CCAMessageEditor.h" |
|
21 #include "ChatDefinitions.h" |
|
22 #include "CCAMessageExtensionsHandler.h" |
|
23 #include "IMUtils.h" |
|
24 #include "IMDialogUtils.h" |
|
25 #include "mcatapeventobserver.h" |
|
26 |
|
27 #include <eikenv.h> |
|
28 #include <aknutils.h> |
|
29 #include <txtrich.h> // CRichText |
|
30 #include <chatNG.rsg> |
|
31 #include <eikon.rsg> // R_EIK_EDWIN_CTRL_HOTKEYS & R_EIK_EDWIN_SHIFT_CTRL_HOTKEYS |
|
32 #include <AknsBasicBackgroundControlContext.h> |
|
33 #include "CCAEditIndicator.h" |
|
34 |
|
35 // The Settings have been moved to Cenrep (also retained in the Resource file), |
|
36 // so the enums for keys and central repository header is added here |
|
37 #include "VariantKeys.h" |
|
38 // CONSTANTS |
|
39 |
|
40 // TKeyEvent.iCode limit for hotkeys, see DoOfferKeyEvenL in eikedwin.cpp |
|
41 const TInt KChatKeyEventHotkeyLimit = 100; |
|
42 // Buffer descriptor size for |
|
43 // R_EIK_EDWIN_CTRL_HOTKEYS & R_EIK_EDWIN_SHIFT_CTRL_HOTKEYS |
|
44 // see DoOfferKeyEvenL in eikedwin.cpp |
|
45 const TInt KChatResourceBufferSize = 24; |
|
46 |
|
47 // Amount of extra characters every smiley takes in the editor |
|
48 const TInt KChatAddCharsPerSmiley = 2; |
|
49 |
|
50 // ================= MEMBER FUNCTIONS ======================= |
|
51 |
|
52 // C++ default constructor can NOT contain any code, that |
|
53 // might leave. |
|
54 // |
|
55 CCAMessageEditor::CCAMessageEditor( |
|
56 CCAMessageExtensionsHandler& aMessageHandler ) |
|
57 : iMessageHandler( aMessageHandler ) |
|
58 { |
|
59 } |
|
60 |
|
61 // Symbian OS default constructor can leave. |
|
62 void CCAMessageEditor::ConstructL( const CCoeControl* aParent, TInt |
|
63 aNumberOfLines, TInt aTextLimit, TInt aEdwinFlags, |
|
64 TInt aFontControlFlags, |
|
65 TInt aFontNameFlags ) |
|
66 { |
|
67 |
|
68 CEikRichTextEditor::ConstructL( aParent, aNumberOfLines, aTextLimit, |
|
69 aEdwinFlags, aFontControlFlags, aFontNameFlags ); |
|
70 } |
|
71 |
|
72 |
|
73 // Destructor |
|
74 CCAMessageEditor::~CCAMessageEditor() |
|
75 { |
|
76 delete iBgContext; |
|
77 delete iFgContext; |
|
78 delete iClipboardBuffer; |
|
79 } |
|
80 |
|
81 // --------------------------------------------------------- |
|
82 // CCAMessageEditor::NumberOfLines() |
|
83 // This method returns the number of lines if expanded |
|
84 // --------------------------------------------------------- |
|
85 // |
|
86 TInt CCAMessageEditor::NumberOfLines() const |
|
87 { |
|
88 return iNumberOfLines; |
|
89 } |
|
90 |
|
91 |
|
92 // --------------------------------------------------------- |
|
93 // CCAMessageEditor::ResetL() |
|
94 // This method clears the editor |
|
95 // --------------------------------------------------------- |
|
96 // |
|
97 void CCAMessageEditor::ResetL() |
|
98 { |
|
99 iTextView->EnablePictureFrameL( EFalse ); |
|
100 TBuf< KTwo > str; |
|
101 str.Zero(); |
|
102 SetCursorPosL( 0, EFalse ); |
|
103 SetTextL( &str ); |
|
104 HandleTextChangedL(); |
|
105 } |
|
106 |
|
107 // ----------------------------------------------------------------------------- |
|
108 // CCAMessageEditor::SetViewRect |
|
109 // Set view rect. |
|
110 // ----------------------------------------------------------------------------- |
|
111 // |
|
112 void CCAMessageEditor::SetViewRect( const TRect& aRect ) |
|
113 { |
|
114 iViewRect = aRect; |
|
115 } |
|
116 |
|
117 // ----------------------------------------------------------------------------- |
|
118 // CCAMessageEditor::OfferKeyEventL |
|
119 // ----------------------------------------------------------------------------- |
|
120 // |
|
121 TKeyResponse CCAMessageEditor::OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType ) |
|
122 { |
|
123 if ( aKeyEvent.iScanCode == EStdKeyYes && aType != EEventKey ) |
|
124 { |
|
125 return EKeyWasConsumed; |
|
126 } |
|
127 |
|
128 TInt oldLen = TextLength(); |
|
129 |
|
130 if ( TextLength() == MaxLength() ) |
|
131 { |
|
132 if ( ( aType == EEventKey && |
|
133 ( aKeyEvent.iCode != EKeyBackspace && |
|
134 aKeyEvent.iCode < EKeyDelete ) |
|
135 ) || |
|
136 ( aType == EEventKeyUp && |
|
137 aKeyEvent.iScanCode >= '0' && |
|
138 aKeyEvent.iScanCode <= '9' |
|
139 ) |
|
140 ) |
|
141 { |
|
142 HBufC* prompt = iEikonEnv->AllocReadResourceLC( |
|
143 R_QTN_CHAT_MAXIMUM_MESSAGE_LENGTH ); |
|
144 IMDialogUtils::DisplayInformationNoteL( |
|
145 *prompt ); |
|
146 CleanupStack::PopAndDestroy( prompt ); |
|
147 |
|
148 return EKeyWasConsumed; |
|
149 } |
|
150 } |
|
151 |
|
152 TKeyResponse res = EKeyWasNotConsumed; |
|
153 |
|
154 // -------------------- |
|
155 // This logic comes from CEikEdwin |
|
156 // We need to handle hot keys cut and copy |
|
157 TBool select = aKeyEvent.iModifiers & EModifierShift; |
|
158 TBool magnify = aKeyEvent.iModifiers & EModifierCtrl; |
|
159 if ( magnify && aKeyEvent.iCode < KChatKeyEventHotkeyLimit |
|
160 && aKeyEvent.iCode != ' ' ) |
|
161 { |
|
162 TBuf<KChatResourceBufferSize> buf; |
|
163 if ( select ) |
|
164 { |
|
165 iCoeEnv->ReadResourceL( buf, R_EIK_EDWIN_SHIFT_CTRL_HOTKEYS ); |
|
166 } |
|
167 else |
|
168 { |
|
169 iCoeEnv->ReadResourceL( buf, R_EIK_EDWIN_CTRL_HOTKEYS ); |
|
170 } |
|
171 |
|
172 const TInt ret = buf.Locate( TChar( aKeyEvent.iCode + 'a' - 1 ) ); |
|
173 switch ( ret ) |
|
174 { |
|
175 case EHotKeyCut: |
|
176 { |
|
177 CcpuCutL(); |
|
178 res = EKeyWasConsumed; |
|
179 break; |
|
180 } |
|
181 case EHotKeyCopy: |
|
182 { |
|
183 CcpuCopyL(); |
|
184 res = EKeyWasConsumed; |
|
185 break; |
|
186 } |
|
187 default: |
|
188 { |
|
189 // No need to handle other hot keys here |
|
190 break; |
|
191 } |
|
192 } |
|
193 } |
|
194 // -------------------- |
|
195 |
|
196 if ( res != EKeyWasConsumed ) |
|
197 { |
|
198 res = CEikRichTextEditor::OfferKeyEventL( aKeyEvent, aType ); |
|
199 } |
|
200 |
|
201 NotifyObserverIfNeededL( oldLen, TextLength() ); |
|
202 |
|
203 return res; |
|
204 } |
|
205 |
|
206 // ----------------------------------------------------------------------------- |
|
207 // CCAMessageEditor::HandlePointerEventL |
|
208 // ----------------------------------------------------------------------------- |
|
209 // |
|
210 void CCAMessageEditor::HandlePointerEventL( |
|
211 const TPointerEvent& aPointerEvent ) |
|
212 { |
|
213 TBool eventConsumed = EFalse; |
|
214 if ( aPointerEvent.iType == TPointerEvent::EButton1Down ) |
|
215 { |
|
216 // Select smiley if user taps on it |
|
217 TRect pictRect; |
|
218 TPoint pointerPos( aPointerEvent.iPosition ); |
|
219 if ( iTextView->GetPictureRectangleL( pointerPos, pictRect ) ) |
|
220 { |
|
221 eventConsumed = ETrue; |
|
222 TInt pos = iTextView->XyPosToDocPosL( pointerPos ); |
|
223 SetSelectionL( pos, pos + 1 ); |
|
224 IgnoreEventsUntilNextPointerUp(); |
|
225 } |
|
226 } |
|
227 |
|
228 // If user didn't start stylus down event on smiley, |
|
229 // forward all events to base class |
|
230 if ( !eventConsumed ) |
|
231 { |
|
232 CEikRichTextEditor::HandlePointerEventL( aPointerEvent ); |
|
233 } |
|
234 } |
|
235 |
|
236 // ----------------------------------------------------------------------------- |
|
237 // CCAMessageEditor::CcpuCutL |
|
238 // Handle cut |
|
239 // ----------------------------------------------------------------------------- |
|
240 // |
|
241 void CCAMessageEditor::CcpuCutL() |
|
242 { |
|
243 if ( Selection().Length() == 0 ) |
|
244 { |
|
245 return; |
|
246 } |
|
247 |
|
248 TInt oldLen = TextLength(); |
|
249 |
|
250 // Set up buffer |
|
251 ConstructAndSetUpClipboardBufferL(); |
|
252 |
|
253 // Perform cut in buffer |
|
254 iClipboardBuffer->CcpuCutL(); |
|
255 |
|
256 // Delete selection from UI |
|
257 TCursorSelection curSel = Selection(); |
|
258 TInt delLen = curSel.Length(); |
|
259 RichText()->DeleteL( curSel.LowerPos(), delLen ); |
|
260 // Create selection for HandleInsertDeleteL |
|
261 TInt delStart = Min( curSel.iAnchorPos, curSel.iCursorPos ); |
|
262 TCursorSelection delSel( delStart, delStart ); |
|
263 TextView()->HandleInsertDeleteL( delSel, delLen ); |
|
264 ClearSelectionL(); |
|
265 |
|
266 // Set limit to new. Because smileys take 2-3 characters in string format |
|
267 // so we have to multiply picture count with extra character count to get |
|
268 // possible limit. |
|
269 SetTextLimit( IMUtils::MaxMsgLength() - ( RichText()->PictureCount() * |
|
270 KChatAddCharsPerSmiley ) ); |
|
271 |
|
272 NotifyObserverIfNeededL( oldLen, TextLength() ); |
|
273 } |
|
274 |
|
275 // ----------------------------------------------------------------------------- |
|
276 // CCAMessageEditor::CcpuCopyL |
|
277 // Handle copy |
|
278 // ----------------------------------------------------------------------------- |
|
279 // |
|
280 void CCAMessageEditor::CcpuCopyL() |
|
281 { |
|
282 // Set up buffer |
|
283 ConstructAndSetUpClipboardBufferL(); |
|
284 |
|
285 // Perform copy in buffer |
|
286 iClipboardBuffer->CcpuCopyL(); |
|
287 } |
|
288 |
|
289 // ----------------------------------------------------------------------------- |
|
290 // CCAMessageEditor::CcpuPasteL |
|
291 // Handle paste |
|
292 // ----------------------------------------------------------------------------- |
|
293 // |
|
294 void CCAMessageEditor::CcpuPasteL() |
|
295 { |
|
296 TInt oldLen = TextLength(); |
|
297 |
|
298 // Take original cursorpos in memory |
|
299 TInt pos = CursorPos(); |
|
300 |
|
301 // Paste |
|
302 CEikRichTextEditor::CcpuPasteL(); |
|
303 |
|
304 // Select changed part so we can check it for extensions |
|
305 SetSelectionL( pos, CursorPos() ); |
|
306 |
|
307 // Convert strings to extensions |
|
308 iMessageHandler.ConvertSelectionToExtensionL( *this ); |
|
309 |
|
310 // Update cursor position. |
|
311 SetCursorPosL( Selection().HigherPos(), EFalse ); |
|
312 |
|
313 NotifyObserverIfNeededL( oldLen, TextLength() ); |
|
314 } |
|
315 |
|
316 // ----------------------------------------------------------------------------- |
|
317 // CCAMessageEditor::SetupSkinContextL |
|
318 // Enable skinning |
|
319 // ----------------------------------------------------------------------------- |
|
320 // |
|
321 void CCAMessageEditor::SetupSkinContextL( TBool aForceUpdate /*= EFalse*/ ) |
|
322 { |
|
323 TRect screen( 0, 0, 0, 0 ); |
|
324 |
|
325 AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EScreen, screen ); |
|
326 |
|
327 // If update forced, delete previous skin contexts |
|
328 if ( aForceUpdate ) |
|
329 { |
|
330 delete iBgContext; |
|
331 iBgContext = NULL; |
|
332 delete iFgContext; |
|
333 iFgContext = NULL; |
|
334 } |
|
335 |
|
336 if ( !iFgContext && !iBgContext ) |
|
337 { |
|
338 iBgContext = CAknsBasicBackgroundControlContext::NewL( |
|
339 KAknsIIDQsnBgAreaMain, screen, EFalse ); |
|
340 iFgContext = CAknsBasicBackgroundControlContext::NewL( |
|
341 KAknsIIDQsnFrInputCenter, screen, EFalse ); |
|
342 iFgContext->SetParentContext( iBgContext ); |
|
343 } |
|
344 else |
|
345 { |
|
346 iFgContext->SetRect( screen ); |
|
347 } |
|
348 |
|
349 SetSkinBackgroundControlContextL( iFgContext ); |
|
350 } |
|
351 |
|
352 TTypeUid::Ptr CCAMessageEditor::MopSupplyObject( TTypeUid aId ) |
|
353 { |
|
354 if ( aId.iUid == MAknsControlContext::ETypeId ) |
|
355 { |
|
356 return MAknsControlContext::SupplyMopObject( aId, iFgContext ); |
|
357 } |
|
358 return CEikRichTextEditor::MopSupplyObject( aId ); |
|
359 } |
|
360 |
|
361 // ----------------------------------------------------------------------------- |
|
362 // CCAMessageEditor::RefreshEditorL |
|
363 // Called to force refresh of editor contents after inserting a smiley |
|
364 // ----------------------------------------------------------------------------- |
|
365 // |
|
366 void CCAMessageEditor::RefreshEditorL() |
|
367 { |
|
368 iTextView->HandleCharEditL( CTextLayout::EFCharacterInsert ); |
|
369 } |
|
370 |
|
371 // ----------------------------------------------------------------------------- |
|
372 // CCAMessageEditor::SetTapObserver |
|
373 // (other items were commented in a header). |
|
374 // ----------------------------------------------------------------------------- |
|
375 // |
|
376 void CCAMessageEditor::SetTapObserver( MCATapEventObserver* aObserver, TUint aId ) |
|
377 { |
|
378 iTapObserver = aObserver; |
|
379 iTapControlId = aId; |
|
380 } |
|
381 |
|
382 // ----------------------------------------------------------------------------- |
|
383 // CCAMessageEditor::SetMessageEditorObserver |
|
384 // Called to force refresh of editor contents after inserting a smiley |
|
385 // ----------------------------------------------------------------------------- |
|
386 // |
|
387 void CCAMessageEditor::SetMessageEditorObserver( MCAMessageEditorObserver* aObserver ) |
|
388 { |
|
389 iEditorObserver = aObserver; |
|
390 } |
|
391 |
|
392 // ----------------------------------------------------------------------------- |
|
393 // CCAMessageEditor::NotifyObserverIfNeededL |
|
394 // Called to force refresh of editor contents after inserting a smiley |
|
395 // ----------------------------------------------------------------------------- |
|
396 // |
|
397 void CCAMessageEditor::NotifyObserverIfNeededL( TInt aOldLen, TInt aCurLen ) |
|
398 { |
|
399 if ( iEditorObserver ) |
|
400 { |
|
401 if ( !iEditIndicatorActiveObj ) |
|
402 { |
|
403 TInt editorFlags = 0; |
|
404 |
|
405 if ( !aCurLen ) |
|
406 { |
|
407 //dont show the edit indicator if editor is empty |
|
408 editorFlags = ( AknEdwinFlags() | EAknEditorFlagNoEditIndicators ); |
|
409 } |
|
410 else |
|
411 { |
|
412 //show the edit indicator if editor is non-empty |
|
413 TInt editorFlags = ( AknEdwinFlags() & ~EAknEditorFlagNoEditIndicators ); |
|
414 } |
|
415 |
|
416 SetAknEditorFlags( editorFlags ); |
|
417 } |
|
418 if ( aCurLen == 0 && aOldLen > 0 ) |
|
419 { |
|
420 iEditorObserver->HandleMessageEditorEventL( |
|
421 MCAMessageEditorObserver::EEditorContentCleared ); |
|
422 return; |
|
423 } |
|
424 if ( aOldLen == 0 && aCurLen > 0 ) |
|
425 { |
|
426 iEditorObserver->HandleMessageEditorEventL( |
|
427 MCAMessageEditorObserver::EEditorFirstContentAdded ); |
|
428 return; |
|
429 } |
|
430 } |
|
431 } |
|
432 |
|
433 // ----------------------------------------------------------------------------- |
|
434 // CCAMessageEditor::EditObserver |
|
435 // Ignored to prevent odd scrolling |
|
436 // ----------------------------------------------------------------------------- |
|
437 // |
|
438 void CCAMessageEditor::EditObserver( TInt /* aStartEdit */, TInt /* aEditLength */ ) |
|
439 { |
|
440 // ignored to prevent odd scrolling |
|
441 } |
|
442 |
|
443 // ----------------------------------------------------------------------------- |
|
444 // CCAMessageEditor::ConstructAndSetUpClipboardBufferL |
|
445 // Construct if needed and set up clipboard buffer |
|
446 // ----------------------------------------------------------------------------- |
|
447 // |
|
448 void CCAMessageEditor::ConstructAndSetUpClipboardBufferL() |
|
449 { |
|
450 if ( !iClipboardBuffer ) |
|
451 { |
|
452 // Not yet created, instantiate now |
|
453 iClipboardBuffer = new( ELeave ) CEikEdwin(); |
|
454 iClipboardBuffer->ConstructL( AknEdwinFlags() | CEikEdwin::EOwnsWindow, |
|
455 1, |
|
456 IMUtils::MaxMsgLength(), |
|
457 1 ); |
|
458 iClipboardBuffer->SetContainerWindowL( *Parent() ); |
|
459 //iClipboardBuffer->ActivateL(); |
|
460 iClipboardBuffer->CreateTextViewL(); |
|
461 iClipboardBuffer->MakeVisible( EFalse ); |
|
462 } |
|
463 else |
|
464 { |
|
465 // Clear buffer |
|
466 TInt textLen = iClipboardBuffer->TextLength(); |
|
467 iClipboardBuffer->Text()->DeleteL( 0, textLen ); |
|
468 TCursorSelection delSel( 0, 0 ); |
|
469 iClipboardBuffer->TextView()->HandleInsertDeleteL( delSel, textLen ); |
|
470 } |
|
471 |
|
472 // Get converted text |
|
473 TCursorSelection copySel; |
|
474 HBufC* plainText = HBufC::NewLC( IMUtils::MaxMsgLength() ); |
|
475 TPtr ptr( plainText->Des() ); |
|
476 iMessageHandler.ConvertSelectionToStringL( *this, ptr, copySel, EFalse ); |
|
477 |
|
478 // Insert only selected text to buffer |
|
479 HBufC* copiedText = |
|
480 plainText->Mid( copySel.LowerPos(), copySel.Length() ).AllocLC(); |
|
481 iClipboardBuffer->SetTextL( copiedText ); |
|
482 CleanupStack::PopAndDestroy( 2, plainText ); |
|
483 // Select whole buffer |
|
484 iClipboardBuffer->SelectAllL(); |
|
485 } |
|
486 |
|
487 |
|
488 // ----------------------------------------------------------------------------- |
|
489 // CCAMessageEditor::ShowIndicator |
|
490 // Construct if needed and set up clipboard buffer |
|
491 // ----------------------------------------------------------------------------- |
|
492 // |
|
493 void CCAMessageEditor::ShowIndicator() |
|
494 { |
|
495 TInt editorFlags = ( AknEdwinFlags() & ~EAknEditorFlagNoEditIndicators ); |
|
496 SetAknEditorFlags( editorFlags ); |
|
497 } |
|
498 |
|
499 |
|
500 |
|
501 // ----------------------------------------------------------------------------- |
|
502 // CCAMessageEditor::HideIndicator |
|
503 // Construct if needed and set up clipboard buffer |
|
504 // ----------------------------------------------------------------------------- |
|
505 // |
|
506 void CCAMessageEditor::HideIndicator() |
|
507 { |
|
508 if ( !TextLength() ) |
|
509 { |
|
510 TInt editorFlags = ( AknEdwinFlags() | EAknEditorFlagNoEditIndicators ); |
|
511 SetAknEditorFlags( editorFlags ); |
|
512 } |
|
513 } |
|
514 |
|
515 |
|
516 // ----------------------------------------------------------------------------- |
|
517 // CCAMessageEditor::SetActiveObject |
|
518 // Construct if needed and set up clipboard buffer |
|
519 // ----------------------------------------------------------------------------- |
|
520 // |
|
521 |
|
522 void CCAMessageEditor::SetActiveObject( CCAEditIndicator* aActiveObject ) |
|
523 { |
|
524 iEditIndicatorActiveObj = aActiveObject; |
|
525 } |
|
526 |
|
527 |
|
528 // ----------------------------------------------------------------------------- |
|
529 // CCAMessageEditor::ReSetActiveObject |
|
530 // Construct if needed and set up clipboard buffer |
|
531 // ----------------------------------------------------------------------------- |
|
532 // |
|
533 void CCAMessageEditor::ReSetActiveObject() |
|
534 { |
|
535 iEditIndicatorActiveObj = NULL; |
|
536 } |
|
537 |
|
538 // End of File |